Modular DRM for MediaPlayer

Bug:  34559906
Test: Manual through the test app

Change-Id: I286f9ff199c34563b7b8643de725f8d1534ea06c
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index a4cc152..e5a98dd 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -24,7 +24,6 @@
 #include <system/audio.h>
 
 #include <media/IMediaSource.h>
-#include <media/drm/DrmAPI.h>   // for DrmPlugin::* enum
 #include <media/VolumeShaper.h>
 
 // Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
@@ -97,22 +96,10 @@
                                     const sp<VolumeShaper::Operation>& operation) = 0;
     virtual sp<VolumeShaper::State> getVolumeShaperState(int id) = 0;
 
-    // ModDrm
-    virtual status_t        prepareDrm(const uint8_t uuid[16], const int mode) = 0;
+    // Modular DRM
+    virtual status_t        prepareDrm(const uint8_t uuid[16],
+                                    const Vector<uint8_t>& drmSessionId) = 0;
     virtual status_t        releaseDrm() = 0;
-    virtual status_t        getKeyRequest(Vector<uint8_t> const& scope,
-                                    String8 const &mimeType,
-                                    DrmPlugin::KeyType keyType,
-                                    KeyedVector<String8, String8>& optionalParameters,
-                                    Vector<uint8_t>& request,
-                                    String8& defaultUrl,
-                                    DrmPlugin::KeyRequestType& keyRequestType) = 0;
-    virtual status_t        provideKeyResponse(Vector<uint8_t>& releaseKeySetId,
-                                    Vector<uint8_t>& response,
-                                    Vector<uint8_t>& keySetId) = 0;
-    virtual status_t        restoreKeys(Vector<uint8_t> const& keySetId) = 0;
-    virtual status_t        getDrmPropertyString(String8 const& name, String8& value) = 0;
-    virtual status_t        setDrmPropertyString(String8 const& name, String8 const& value) = 0;
 
     // Invoke a generic method on the player by using opaque parcels
     // for the request and reply.
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index b3e53fc..a01f7f2 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -285,33 +285,13 @@
         return INVALID_OPERATION;
     }
 
-    // ModDrm
-    virtual status_t prepareDrm(const uint8_t uuid[16], const int mode) {
+    // Modular DRM
+    virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId) {
         return INVALID_OPERATION;
     }
     virtual status_t releaseDrm() {
         return INVALID_OPERATION;
     }
-    virtual status_t getKeyRequest(Vector<uint8_t> const& scope, String8 const& mimeType,
-                             DrmPlugin::KeyType keyType,
-                             KeyedVector<String8, String8>& optionalParameters,
-                             Vector<uint8_t>& request, String8& defaultUrl,
-                             DrmPlugin::KeyRequestType& keyRequestType) {
-        return INVALID_OPERATION;
-    }
-    virtual status_t provideKeyResponse(Vector<uint8_t>& releaseKeySetId,
-                             Vector<uint8_t>& response, Vector<uint8_t>& keySetId) {
-        return INVALID_OPERATION;
-    }
-    virtual status_t restoreKeys(Vector<uint8_t> const& keySetId) {
-        return INVALID_OPERATION;
-    }
-    virtual status_t getDrmPropertyString(String8 const& name, String8& value) {
-        return INVALID_OPERATION;
-    }
-    virtual status_t setDrmPropertyString(String8 const& name, String8 const& value) {
-        return INVALID_OPERATION;
-    }
 
 private:
     friend class MediaPlayerService;
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index fbe3926..18d69a7 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -266,19 +266,9 @@
                                     const sp<VolumeShaper::Configuration>& configuration,
                                     const sp<VolumeShaper::Operation>& operation);
             sp<VolumeShaper::State> getVolumeShaperState(int id);
-            // ModDrm
-            status_t        prepareDrm(const uint8_t uuid[16], const int mode);
+            // Modular DRM
+            status_t        prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
             status_t        releaseDrm();
-            status_t        getKeyRequest(Vector<uint8_t> const& scope, String8 const& mimeType,
-                                    DrmPlugin::KeyType keyType,
-                                    KeyedVector<String8, String8>& optionalParameters,
-                                    Vector<uint8_t>& request, String8& defaultUrl,
-                                    DrmPlugin::KeyRequestType& keyRequestType);
-            status_t        provideKeyResponse(Vector<uint8_t>& releaseKeySetId,
-                                    Vector<uint8_t>& response, Vector<uint8_t>& keySetId);
-            status_t        restoreKeys(Vector<uint8_t> const& keySetId);
-            status_t        getDrmPropertyString(String8 const& name, String8& value);
-            status_t        setDrmPropertyString(String8 const& name, String8 const& value);
 
 private:
             void            clear_l();
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 699ae48..20b26e2 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -91,6 +91,8 @@
             const sp<ICrypto> &crypto,
             uint32_t flags);
 
+    status_t releaseCrypto();
+
     status_t setCallback(const sp<AMessage> &callback);
 
     status_t setOnFrameRenderedNotification(const sp<AMessage> &notify);
@@ -239,6 +241,7 @@
         kWhatSetParameters                  = 'setP',
         kWhatSetCallback                    = 'setC',
         kWhatSetNotification                = 'setN',
+        kWhatDrmReleaseCrypto               = 'rDrm',
     };
 
     enum {
@@ -416,6 +419,8 @@
         mStickyError = err;
     }
 
+    void onReleaseCrypto(const sp<AMessage>& msg);
+
     DISALLOW_EVIL_CONSTRUCTORS(MediaCodec);
 };
 
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index 5222a42..3996227 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -72,14 +72,9 @@
     SET_NEXT_PLAYER,
     APPLY_VOLUME_SHAPER,
     GET_VOLUME_SHAPER_STATE,
-    // ModDrm
+    // Modular DRM
     PREPARE_DRM,
     RELEASE_DRM,
-    GET_KEY_REQUEST,
-    PROVIDE_KEY_RESPONSE,
-    RESTORE_KEYS,
-    GET_DRM_PROPERTY_STRING,
-    SET_DRM_PROPERTY_STRING,
 };
 
 // ModDrm helpers
@@ -521,14 +516,14 @@
         return state;
     }
 
-    // ModDrm
-    status_t prepareDrm(const uint8_t uuid[16], const int mode)
+    // Modular DRM
+    status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
 
         data.write(uuid, 16);
-        data.writeInt32(mode);
+        writeVector(data, drmSessionId);
 
         status_t status = remote()->transact(PREPARE_DRM, data, &reply);
         if (status != OK) {
@@ -552,105 +547,6 @@
 
         return reply.readInt32();
     }
-
-    status_t getKeyRequest(Vector<uint8_t> const& scope, String8 const& mimeType,
-            DrmPlugin::KeyType keyType, KeyedVector<String8, String8>& optionalParameters,
-            Vector<uint8_t>& request, String8& defaultUrl,
-            DrmPlugin::KeyRequestType& keyRequestType)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
-        writeVector(data, scope);
-        data.writeString8(mimeType);
-        data.writeInt32((int32_t)keyType);
-
-        data.writeUint32(optionalParameters.size());
-        for (size_t i = 0; i < optionalParameters.size(); ++i) {
-            data.writeString8(optionalParameters.keyAt(i));
-            data.writeString8(optionalParameters.valueAt(i));
-        }
-
-        status_t status = remote()->transact(GET_KEY_REQUEST, data, &reply);
-        if (status != OK) {
-            ALOGE("getKeyRequest: binder call failed: %d", status);
-            return status;
-        }
-
-        readVector(reply, request);
-        defaultUrl = reply.readString8();
-        keyRequestType = (DrmPlugin::KeyRequestType)reply.readInt32();
-
-        return reply.readInt32();
-    }
-
-    status_t provideKeyResponse(Vector<uint8_t>& releaseKeySetId, Vector<uint8_t>& response,
-            Vector<uint8_t> &keySetId)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
-        writeVector(data, releaseKeySetId);
-        writeVector(data, response);
-
-        status_t status = remote()->transact(PROVIDE_KEY_RESPONSE, data, &reply);
-        if (status != OK) {
-            ALOGE("provideKeyResponse: binder call failed: %d", status);
-            return status;
-        }
-
-        readVector(reply, keySetId);
-
-        return reply.readInt32();
-    }
-
-    status_t restoreKeys(Vector<uint8_t> const& keySetId)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
-        writeVector(data, keySetId);
-
-        status_t status = remote()->transact(RESTORE_KEYS, data, &reply);
-        if (status != OK) {
-            ALOGE("restoreKeys: binder call failed: %d", status);
-            return status;
-        }
-
-        return reply.readInt32();
-    }
-
-    status_t getDrmPropertyString(String8 const& name, String8& value)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
-        data.writeString8(name);
-        status_t status = remote()->transact(GET_DRM_PROPERTY_STRING, data, &reply);
-        if (status != OK) {
-            ALOGE("getDrmPropertyString: binder call failed: %d", status);
-            return status;
-        }
-
-        value = reply.readString8();
-        return reply.readInt32();
-    }
-
-    status_t setDrmPropertyString(String8 const& name, String8 const& value)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
-        data.writeString8(name);
-        data.writeString8(value);
-        status_t status = remote()->transact(SET_DRM_PROPERTY_STRING, data, &reply);
-        if (status != OK) {
-            ALOGE("setDrmPropertyString: binder call failed: %d", status);
-            return status;
-        }
-
-        return reply.readInt32();
-    }
 };
 
 IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
@@ -983,15 +879,16 @@
             return NO_ERROR;
         } break;
 
-        // ModDrm
+        // Modular DRM
         case PREPARE_DRM: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
+
             uint8_t uuid[16];
             data.read(uuid, sizeof(uuid));
+            Vector<uint8_t> drmSessionId;
+            readVector(data, drmSessionId);
 
-            int mode = data.readInt32();
-
-            uint32_t result = prepareDrm(uuid, mode);
+            uint32_t result = prepareDrm(uuid, drmSessionId);
             reply->writeInt32(result);
             return OK;
         }
@@ -1002,73 +899,6 @@
             reply->writeInt32(result);
             return OK;
         }
-        case GET_KEY_REQUEST: {
-            CHECK_INTERFACE(IMediaPlayer, data, reply);
-
-            Vector<uint8_t> scope;
-            readVector(data, scope);
-            String8 mimeType = data.readString8();
-            DrmPlugin::KeyType keyType = (DrmPlugin::KeyType)data.readInt32();
-
-            KeyedVector<String8, String8> optionalParameters;
-            uint32_t count = data.readUint32();
-            for (size_t i = 0; i < count; ++i) {
-                String8 key, value;
-                key = data.readString8();
-                value = data.readString8();
-                optionalParameters.add(key, value);
-            }
-
-            Vector<uint8_t> request;
-            String8 defaultUrl;
-            DrmPlugin::KeyRequestType keyRequestType = DrmPlugin::kKeyRequestType_Unknown;
-
-            status_t result = getKeyRequest(scope, mimeType, keyType, optionalParameters,
-                                      request, defaultUrl, keyRequestType);
-
-            writeVector(*reply, request);
-            reply->writeString8(defaultUrl);
-            reply->writeInt32(keyRequestType);
-            reply->writeInt32(result);
-            return OK;
-        }
-        case PROVIDE_KEY_RESPONSE: {
-            CHECK_INTERFACE(IMediaPlayer, data, reply);
-            Vector<uint8_t> releaseKeySetId, response, keySetId;
-            readVector(data, releaseKeySetId);
-            readVector(data, response);
-            uint32_t result = provideKeyResponse(releaseKeySetId, response, keySetId);
-            writeVector(*reply, keySetId);
-            reply->writeInt32(result);
-            return OK;
-        }
-        case RESTORE_KEYS: {
-            CHECK_INTERFACE(IMediaPlayer, data, reply);
-
-            Vector<uint8_t> keySetId;
-            readVector(data, keySetId);
-            uint32_t result = restoreKeys(keySetId);
-            reply->writeInt32(result);
-            return OK;
-        }
-        case GET_DRM_PROPERTY_STRING: {
-            CHECK_INTERFACE(IMediaPlayer, data, reply);
-            String8 name, value;
-            name = data.readString8();
-            uint32_t result = getDrmPropertyString(name, value);
-            reply->writeString8(value);
-            reply->writeInt32(result);
-            return OK;
-        }
-        case SET_DRM_PROPERTY_STRING: {
-            CHECK_INTERFACE(IMediaPlayer, data, reply);
-            String8 name, value;
-            name = data.readString8();
-            value = data.readString8();
-            uint32_t result = setDrmPropertyString(name, value);
-            reply->writeInt32(result);
-            return OK;
-        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index dfc2e1b..685065a 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -16,7 +16,7 @@
 */
 
 //#define LOG_NDEBUG 0
-#define LOG_TAG "MediaPlayer"
+#define LOG_TAG "MediaPlayerNative"
 
 #include <fcntl.h>
 #include <inttypes.h>
@@ -1012,9 +1012,12 @@
     return mPlayer->getVolumeShaperState(id);
 }
 
-// ModDrm
-status_t MediaPlayer::prepareDrm(const uint8_t uuid[16], const int mode)
+// Modular DRM
+status_t MediaPlayer::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId)
 {
+    // TODO change to ALOGV
+    ALOGD("prepareDrm: uuid: %p  drmSessionId: %p(%zu)", uuid,
+            drmSessionId.array(), drmSessionId.size());
     Mutex::Autolock _l(mLock);
     if (mPlayer == NULL) {
         return NO_INIT;
@@ -1026,10 +1029,19 @@
         return INVALID_OPERATION;
     }
 
-    status_t ret = mPlayer->prepareDrm(uuid, mode);
-    ALOGV("prepareDrm: ret=%d", ret);
+    if (drmSessionId.isEmpty()) {
+        ALOGE("prepareDrm: Unexpected. Can't proceed with crypto. Empty drmSessionId.");
+        return INVALID_OPERATION;
+    }
 
-    return ret;
+    // Passing down to mediaserver mainly for creating the crypto
+    status_t status = mPlayer->prepareDrm(uuid, drmSessionId);
+    ALOGE_IF(status != OK, "prepareDrm: Failed at mediaserver with ret: %d", status);
+
+    // TODO change to ALOGV
+    ALOGD("prepareDrm: mediaserver::prepareDrm ret=%d", status);
+
+    return status;
 }
 
 status_t MediaPlayer::releaseDrm()
@@ -1039,96 +1051,26 @@
         return NO_INIT;
     }
 
-    // Not allowing releaseDrm in an active state
-    if (mCurrentState & (MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED)) {
-        ALOGE("releaseDrm can not be called in the started/paused state.");
+    // Not allowing releaseDrm in an active/resumable state
+    if (mCurrentState & (MEDIA_PLAYER_STARTED |
+                         MEDIA_PLAYER_PAUSED |
+                         MEDIA_PLAYER_PLAYBACK_COMPLETE |
+                         MEDIA_PLAYER_STATE_ERROR)) {
+        ALOGE("releaseDrm Unexpected state %d. Can only be called in stopped/idle.", mCurrentState);
         return INVALID_OPERATION;
     }
 
-    status_t ret = mPlayer->releaseDrm();
-    ALOGV("releaseDrm: ret=%d", ret);
-
-    return ret;
-}
-
-status_t MediaPlayer::getKeyRequest(Vector<uint8_t> const& scope, String8 const& mimeType,
-                              DrmPlugin::KeyType keyType,
-                              KeyedVector<String8, String8>& optionalParameters,
-                              Vector<uint8_t>& request, String8& defaultUrl,
-                              DrmPlugin::KeyRequestType& keyRequestType)
-{
-    Mutex::Autolock _l(mLock);
-    if (mPlayer == NULL) {
-        return NO_INIT;
+    status_t status = mPlayer->releaseDrm();
+    // TODO change to ALOGV
+    ALOGD("releaseDrm: mediaserver::releaseDrm ret: %d", status);
+    if (status != OK) {
+        ALOGE("releaseDrm: Failed at mediaserver with ret: %d", status);
+        // Overriding to OK so the client proceed with its own cleanup
+        // Client can't do more cleanup. mediaserver release its crypto at end of session anyway.
+        status = OK;
     }
 
-    // Not enforcing a particular state beyond the checks enforced by the Java layer
-    // Key exchange can happen after the start.
-    status_t ret = mPlayer->getKeyRequest(scope, mimeType, keyType, optionalParameters,
-                                     request, defaultUrl, keyRequestType);
-    ALOGV("getKeyRequest ret=%d  %d %s %d ", ret,
-          (int)request.size(), defaultUrl.string(), (int)keyRequestType);
-
-    return ret;
-}
-
-status_t MediaPlayer::provideKeyResponse(Vector<uint8_t>& releaseKeySetId,
-                              Vector<uint8_t>& response, Vector<uint8_t>& keySetId)
-{
-    Mutex::Autolock _l(mLock);
-    if (mPlayer == NULL) {
-        return NO_INIT;
-    }
-
-    // Not enforcing a particular state beyond the checks enforced by the Java layer
-    // Key exchange can happen after the start.
-    status_t ret = mPlayer->provideKeyResponse(releaseKeySetId, response, keySetId);
-    ALOGV("provideKeyResponse: ret=%d", ret);
-
-    return ret;
-}
-
-status_t MediaPlayer::restoreKeys(Vector<uint8_t> const& keySetId)
-{
-    Mutex::Autolock _l(mLock);
-    if (mPlayer == NULL) {
-        return NO_INIT;
-    }
-
-    // Not enforcing a particular state beyond the checks enforced by the Java layer
-    // Key exchange can happen after the start.
-    status_t ret = mPlayer->restoreKeys(keySetId);
-    ALOGV("restoreKeys: ret=%d", ret);
-
-    return ret;
-}
-
-status_t MediaPlayer::getDrmPropertyString(String8 const& name, String8& value)
-{
-    Mutex::Autolock _l(mLock);
-    if (mPlayer == NULL) {
-        return NO_INIT;
-    }
-
-    // Not enforcing a particular state beyond the checks enforced by the Java layer
-    status_t ret = mPlayer->getDrmPropertyString(name, value);
-    ALOGV("getDrmPropertyString: ret=%d", ret);
-
-    return ret;
-}
-
-status_t MediaPlayer::setDrmPropertyString(String8 const& name, String8 const& value)
-{
-    Mutex::Autolock _l(mLock);
-    if (mPlayer == NULL) {
-        return NO_INIT;
-    }
-
-    // Not enforcing a particular state beyond the checks enforced by the Java layer
-    status_t ret = mPlayer->setDrmPropertyString(name, value);
-    ALOGV("setDrmPropertyString: ret=%d", ret);
-
-    return ret;
+    return status;
 }
 
 } // namespace android
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 1786e6b..64627fb 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -25,8 +25,9 @@
     liblog                      \
     libdl                       \
     libgui                      \
-    libmedia                    \
     libaudioclient              \
+    libmedia                    \
+    libmediadrm                 \
     libmediautils               \
     libmemunreachable           \
     libstagefright              \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index cdae456..62e5ec8 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1428,6 +1428,32 @@
     }
 }
 
+// Modular DRM
+status_t MediaPlayerService::Client::prepareDrm(const uint8_t uuid[16],
+        const Vector<uint8_t>& drmSessionId)
+{
+    ALOGV("[%d] prepareDrm", mConnId);
+    sp<MediaPlayerBase> p = getPlayer();
+    if (p == 0) return UNKNOWN_ERROR;
+
+    status_t ret = p->prepareDrm(uuid, drmSessionId);
+    ALOGV("prepareDrm ret: %d", ret);
+
+    return ret;
+}
+
+status_t MediaPlayerService::Client::releaseDrm()
+{
+    ALOGV("[%d] releaseDrm", mConnId);
+    sp<MediaPlayerBase> p = getPlayer();
+    if (p == 0) return UNKNOWN_ERROR;
+
+    status_t ret = p->releaseDrm();
+    ALOGV("releaseDrm ret: %d", ret);
+
+    return ret;
+}
+
 #if CALLBACK_ANTAGONIZER
 const int Antagonizer::interval = 10000; // 10 msecs
 
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index cbaf21c..f5a540b 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -357,32 +357,9 @@
         virtual status_t        dump(int fd, const Vector<String16>& args);
 
                 audio_session_t getAudioSessionId() { return mAudioSessionId; }
-        // ModDrm
-        virtual status_t prepareDrm(const uint8_t /*uuid*/[16], const int /*mode*/)
-                            { return INVALID_OPERATION; }
-        virtual status_t releaseDrm()
-                            { return INVALID_OPERATION; }
-        virtual status_t getKeyRequest(Vector<uint8_t> const& /*scope*/,
-                                 String8 const& /*mimeType*/,
-                                 DrmPlugin::KeyType /*keyType*/,
-                                 KeyedVector<String8, String8>& /*optionalParameters*/,
-                                 Vector<uint8_t>& /*request*/,
-                                 String8& /*defaultUrl*/,
-                                 DrmPlugin::KeyRequestType& /*keyRequestType*/)
-                            { return INVALID_OPERATION; }
-        virtual status_t provideKeyResponse(Vector<uint8_t>& /*releaseKeySetId*/,
-                                 Vector<uint8_t>& /*response*/,
-                                 Vector<uint8_t>& /*keySetId*/)
-                            { return INVALID_OPERATION; }
-        virtual status_t restoreKeys(Vector<uint8_t> const& /*keySetId*/)
-                            { return INVALID_OPERATION; }
-        virtual status_t getDrmPropertyString(String8 const& /*name*/,
-                                              String8& /*value*/)
-                            { return INVALID_OPERATION; }
-        virtual status_t setDrmPropertyString(String8 const& /*name*/,
-                                              String8 const& /*value*/)
-                            { return INVALID_OPERATION; }
-
+        // Modular DRM
+        virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
+        virtual status_t releaseDrm();
 
     private:
         class ServiceDeathNotifier: public IBinder::DeathRecipient
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index a0e633c..8686560 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -10,6 +10,7 @@
         NuPlayerDecoderBase.cpp         \
         NuPlayerDecoderPassThrough.cpp  \
         NuPlayerDriver.cpp              \
+        NuPlayerDrm.cpp                 \
         NuPlayerRenderer.cpp            \
         NuPlayerStreamListener.cpp      \
         RTSPSource.cpp                  \
@@ -32,7 +33,10 @@
 LOCAL_CFLAGS += -DENABLE_STAGEFRIGHT_EXPERIMENTS
 endif
 
-LOCAL_SHARED_LIBRARIES := libmedia
+LOCAL_SHARED_LIBRARIES :=       \
+    libbinder                   \
+    libmedia                    \
+    libmediadrm                 \
 
 LOCAL_MODULE:= libstagefright_nuplayer
 
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 91a2b7b..7986856 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "GenericSource"
 
 #include "GenericSource.h"
+#include "NuPlayerDrm.h"
 
 #include "AnotherPacketSource.h"
 
@@ -63,14 +64,17 @@
       mUIDValid(uidValid),
       mUID(uid),
       mFd(-1),
-      mDrmManagerClient(NULL),
       mBitrate(-1ll),
       mPendingReadBufferTypes(0) {
+    ALOGV("GenericSource");
+
     mBufferingMonitor = new BufferingMonitor(notify);
     resetDataSource();
 }
 
 void NuPlayer::GenericSource::resetDataSource() {
+    ALOGV("resetDataSource");
+
     mHTTPService.clear();
     mHttpSource.clear();
     mUri.clear();
@@ -81,9 +85,6 @@
     }
     mOffset = 0;
     mLength = 0;
-    setDrmPlaybackStatusIfNeeded(Playback::STOP, 0);
-    mDecryptHandle = NULL;
-    mDrmManagerClient = NULL;
     mStarted = false;
     mStopRead = true;
 
@@ -93,12 +94,18 @@
         mBufferingMonitorLooper = NULL;
     }
     mBufferingMonitor->stop();
+
+    mIsDrmProtected = false;
+    mIsSecure = false;
+    mMimes.clear();
 }
 
 status_t NuPlayer::GenericSource::setDataSource(
         const sp<IMediaHTTPService> &httpService,
         const char *url,
         const KeyedVector<String8, String8> *headers) {
+    ALOGV("setDataSource url: %s", url);
+
     resetDataSource();
 
     mHTTPService = httpService;
@@ -115,6 +122,8 @@
 
 status_t NuPlayer::GenericSource::setDataSource(
         int fd, int64_t offset, int64_t length) {
+    ALOGV("setDataSource %d/%lld/%lld", fd, (long long)offset, (long long)length);
+
     resetDataSource();
 
     mFd = dup(fd);
@@ -127,6 +136,8 @@
 }
 
 status_t NuPlayer::GenericSource::setDataSource(const sp<DataSource>& source) {
+    ALOGV("setDataSource (source: %p)", source.get());
+
     resetDataSource();
     mDataSource = source;
     return OK;
@@ -161,6 +172,8 @@
         return UNKNOWN_ERROR;
     }
 
+    mMimes.clear();
+
     for (size_t i = 0; i < numtracks; ++i) {
         sp<IMediaSource> track = extractor->getTrack(i);
         if (track == NULL) {
@@ -176,6 +189,8 @@
         const char *mime;
         CHECK(meta->findCString(kKeyMIMEType, &mime));
 
+        ALOGV("initFromDataSource track[%d]: %s", i, mime);
+
         // Do the string compare immediately with "mime",
         // we can't assume "mime" would stay valid after another
         // extractor operation, some extractors might modify meta
@@ -192,6 +207,8 @@
                 } else {
                     mAudioIsVorbis = false;
                 }
+
+                mMimes.add(String8(mime));
             }
         } else if (!strncasecmp(mime, "video/", 6)) {
             if (mVideoTrack.mSource == NULL) {
@@ -200,15 +217,8 @@
                 mVideoTrack.mPackets =
                     new AnotherPacketSource(mVideoTrack.mSource->getFormat());
 
-                // check if the source requires secure buffers
-                int32_t secure;
-                if (meta->findInt32(kKeyRequiresSecureBuffers, &secure)
-                        && secure) {
-                    mIsSecure = true;
-                    if (mUIDValid) {
-                        extractor->setUID(mUID);
-                    }
-                }
+                // video always at the beginning
+                mMimes.insertAt(String8(mime), 0);
             }
         }
 
@@ -228,11 +238,17 @@
         }
     }
 
+    ALOGV("initFromDataSource mSources.size(): %zu  mIsSecure: %d  mime[0]: %s", mSources.size(),
+            mIsSecure, (mMimes.isEmpty() ? "NONE" : mMimes[0].string()));
+
     if (mSources.size() == 0) {
         ALOGE("b/23705695");
         return UNKNOWN_ERROR;
     }
 
+    // Modular DRM: The return value doesn't affect source initialization.
+    (void)checkDrmInfo();
+
     mBitrate = totalBitrate;
 
     return OK;
@@ -296,6 +312,7 @@
 }
 
 NuPlayer::GenericSource::~GenericSource() {
+    ALOGV("~GenericSource");
     if (mLooper != NULL) {
         mLooper->unregisterHandler(id());
         mLooper->stop();
@@ -304,6 +321,8 @@
 }
 
 void NuPlayer::GenericSource::prepareAsync() {
+    ALOGV("prepareAsync: (looper: %d)", (mLooper != NULL));
+
     if (mLooper == NULL) {
         mLooper = new ALooper;
         mLooper->setName("generic");
@@ -317,6 +336,8 @@
 }
 
 void NuPlayer::GenericSource::onPrepareAsync() {
+    ALOGV("onPrepareAsync: mDataSource: %d", (mDataSource != NULL));
+
     // delayed data source creation
     if (mDataSource == NULL) {
         // set to false first, if the extractor
@@ -380,35 +401,21 @@
     }
 
     notifyFlagsChanged(
-            (mIsSecure ? FLAG_SECURE : 0)
-            | (mDecryptHandle != NULL ? FLAG_PROTECTED : 0)
-            | FLAG_CAN_PAUSE
-            | FLAG_CAN_SEEK_BACKWARD
-            | FLAG_CAN_SEEK_FORWARD
-            | FLAG_CAN_SEEK);
+            // FLAG_SECURE will be known if/when prepareDrm is called by the app
+            // FLAG_PROTECTED will be known if/when prepareDrm is called by the app
+            FLAG_CAN_PAUSE |
+            FLAG_CAN_SEEK_BACKWARD |
+            FLAG_CAN_SEEK_FORWARD |
+            FLAG_CAN_SEEK);
 
-    if (mIsSecure) {
-        // secure decoders must be instantiated before starting widevine source
-        //
-        // TODO: mIsSecure and FLAG_SECURE may be obsolete, revisit after
-        // removing widevine
-        sp<AMessage> reply = new AMessage(kWhatSecureDecodersInstantiated, this);
-        notifyInstantiateSecureDecoders(reply);
-    } else {
-        finishPrepareAsync();
-    }
-}
-
-void NuPlayer::GenericSource::onSecureDecodersInstantiated(status_t err) {
-    if (err != OK) {
-        ALOGE("Failed to instantiate secure decoders!");
-        notifyPreparedAndCleanup(err);
-        return;
-    }
     finishPrepareAsync();
+
+    ALOGV("onPrepareAsync: Done");
 }
 
 void NuPlayer::GenericSource::finishPrepareAsync() {
+    ALOGV("finishPrepareAsync");
+
     status_t err = startSources();
     if (err != OK) {
         ALOGE("Failed to init start data source!");
@@ -443,8 +450,6 @@
             {
                 Mutex::Autolock _l(mDisconnectLock);
                 mDataSource.clear();
-                mDecryptHandle = NULL;
-                mDrmManagerClient = NULL;
                 mCachedSource.clear();
                 mHttpSource.clear();
             }
@@ -468,27 +473,20 @@
         postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
     }
 
-    setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
     mStarted = true;
 
     (new AMessage(kWhatStart, this))->post();
 }
 
 void NuPlayer::GenericSource::stop() {
-    // nothing to do, just account for DRM playback status
-    setDrmPlaybackStatusIfNeeded(Playback::STOP, 0);
     mStarted = false;
 }
 
 void NuPlayer::GenericSource::pause() {
-    // nothing to do, just account for DRM playback status
-    setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0);
     mStarted = false;
 }
 
 void NuPlayer::GenericSource::resume() {
-    // nothing to do, just account for DRM playback status
-    setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
     mStarted = true;
 
     (new AMessage(kWhatResume, this))->post();
@@ -512,14 +510,6 @@
     }
 }
 
-void NuPlayer::GenericSource::setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position) {
-    if (mDecryptHandle != NULL) {
-        mDrmManagerClient->setPlaybackStatus(mDecryptHandle, playbackStatus, position);
-    }
-    mSubtitleTrack.mPackets = new AnotherPacketSource(NULL);
-    mTimedTextTrack.mPackets = new AnotherPacketSource(NULL);
-}
-
 status_t NuPlayer::GenericSource::feedMoreTSData() {
     return OK;
 }
@@ -653,11 +643,14 @@
           break;
       }
 
-      case kWhatSecureDecodersInstantiated:
+      case kWhatPrepareDrm:
       {
-          int32_t err;
-          CHECK(msg->findInt32("err", &err));
-          onSecureDecodersInstantiated(err);
+          status_t status = onPrepareDrm(msg);
+          sp<AMessage> response = new AMessage;
+          response->setInt32("status", status);
+          sp<AReplyToken> replyID;
+          CHECK(msg->senderAwaitsResponse(&replyID));
+          response->postReply(replyID);
           break;
       }
 
@@ -1194,11 +1187,6 @@
         mAudioLastDequeueTimeUs = seekTimeUs;
     }
 
-    setDrmPlaybackStatusIfNeeded(Playback::START, seekTimeUs / 1000);
-    if (!mStarted) {
-        setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0);
-    }
-
     // If currently buffering, post kWhatBufferingEnd first, so that
     // NuPlayer resumes. Otherwise, if cache hits high watermark
     // before new polling happens, no one will resume the playback.
@@ -1219,11 +1207,26 @@
     }
 
     sp<ABuffer> ab;
-    if (mIsSecure && !audio) {
+
+    if (mIsDrmProtected)   {
+        // Modular DRM
+        // Enabled for both video/audio so 1) media buffer is reused without extra copying
+        // 2) meta data can be retrieved in onInputBufferFetched for calling queueSecureInputBuffer.
+
         // data is already provided in the buffer
         ab = new ABuffer(NULL, mb->range_length());
         mb->add_ref();
         ab->setMediaBufferBase(mb);
+
+        // Modular DRM: Required b/c of the above add_ref.
+        // If ref>0, there must be an observer, or it'll crash at release().
+        // TODO: MediaBuffer might need to be revised to ease such need.
+        mb->setObserver(this);
+        // setMediaBufferBase() interestingly doesn't increment the ref count on its own.
+        // Extra increment (since we want to keep mb alive and attached to ab beyond this function
+        // call. This is to counter the effect of mb->release() towards the end.
+        mb->add_ref();
+
     } else {
         ab = new ABuffer(outLength);
         memcpy(ab->data(),
@@ -1828,4 +1831,128 @@
     }
 }
 
+// Modular DRM
+status_t NuPlayer::GenericSource::prepareDrm(
+        const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId, sp<ICrypto> *crypto)
+{
+    ALOGV("prepareDrm");
+
+    sp<AMessage> msg = new AMessage(kWhatPrepareDrm, this);
+    // synchronous call so just passing the address but with local copies of "const" args
+    uint8_t UUID[16];
+    memcpy(UUID, uuid, sizeof(UUID));
+    Vector<uint8_t> sessionId = drmSessionId;
+    msg->setPointer("uuid", (void*)UUID);
+    msg->setPointer("drmSessionId", (void*)&sessionId);
+    msg->setPointer("crypto", (void*)crypto);
+
+    sp<AMessage> response;
+    status_t status = msg->postAndAwaitResponse(&response);
+
+    if (status == OK && response != NULL) {
+        CHECK(response->findInt32("status", &status));
+        ALOGV_IF(status == OK, "prepareDrm: mCrypto: %p (%d)", crypto->get(),
+                (*crypto != NULL ? (*crypto)->getStrongCount() : 0));
+        ALOGD("prepareDrm ret: %d ", status);
+    } else {
+        ALOGE("prepareDrm err: %d", status);
+    }
+
+    return status;
+}
+
+status_t NuPlayer::GenericSource::onPrepareDrm(const sp<AMessage> &msg)
+{
+    ALOGV("onPrepareDrm ");
+
+    mIsDrmProtected = false;
+    mIsSecure = false;
+
+    uint8_t *uuid;
+    Vector<uint8_t> *drmSessionId;
+    sp<ICrypto> *outCrypto;
+    CHECK(msg->findPointer("uuid", (void**)&uuid));
+    CHECK(msg->findPointer("drmSessionId", (void**)&drmSessionId));
+    CHECK(msg->findPointer("crypto", (void**)&outCrypto));
+
+    status_t status = OK;
+    sp<ICrypto> crypto = NuPlayerDrm::createCryptoAndPlugin(uuid, *drmSessionId, status);
+    if (crypto == NULL) {
+        ALOGE("onPrepareDrm: createCrypto failed. status: %d", status);
+        return status;
+    }
+    ALOGV("onPrepareDrm: createCryptoAndPlugin succeeded for uuid: %s",
+            DrmUUID::toHexString(uuid).string());
+
+    *outCrypto = crypto;
+    // as long a there is an active crypto
+    mIsDrmProtected = true;
+
+    if (mMimes.size() == 0) {
+        status = UNKNOWN_ERROR;
+        ALOGE("onPrepareDrm: Unexpected. Must have at least one track. status: %d", status);
+        return status;
+    }
+
+    // first mime in this list is either the video track, or the first audio track
+    const char *mime = mMimes[0].string();
+    mIsSecure = crypto->requiresSecureDecoderComponent(mime);
+    ALOGV("onPrepareDrm: requiresSecureDecoderComponent mime: %s  isSecure: %d",
+            mime, mIsSecure);
+
+    // Checking the member flags while in the looper to send out the notification.
+    // The legacy mDecryptHandle!=NULL check (for FLAG_PROTECTED) is equivalent to mIsDrmProtected.
+    notifyFlagsChanged(
+            (mIsSecure ? FLAG_SECURE : 0) |
+            (mIsDrmProtected ? FLAG_PROTECTED : 0) |
+            FLAG_CAN_PAUSE |
+            FLAG_CAN_SEEK_BACKWARD |
+            FLAG_CAN_SEEK_FORWARD |
+            FLAG_CAN_SEEK);
+
+    return status;
+}
+
+status_t NuPlayer::GenericSource::checkDrmInfo()
+{
+    if (mFileMeta == NULL) {
+        ALOGE("checkDrmInfo: No metadata");
+        return OK; // letting the caller responds accordingly
+    }
+
+    uint32_t type;
+    const void *pssh;
+    size_t psshsize;
+
+    if (!mFileMeta->findData(kKeyPssh, &type, &pssh, &psshsize)) {
+        ALOGE("checkDrmInfo: No PSSH");
+        return OK; // source without DRM info
+    }
+
+    Parcel parcel;
+    NuPlayerDrm::retrieveDrmInfo(pssh, psshsize, mMimes, &parcel);
+    ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH size: %d  Parcel size: %d  objects#: %d",
+          psshsize, (int)parcel.dataSize(), (int)parcel.objectsCount());
+
+    if (parcel.dataSize() == 0) {
+        ALOGE("checkDrmInfo: Unexpected parcel size: 0");
+        return UNKNOWN_ERROR;
+    }
+
+    // Can't pass parcel as a message to the player. Converting Parcel->ABuffer to pass it
+    // to the Player's onSourceNotify then back to Parcel for calling driver's notifyListener.
+    sp<ABuffer> drmInfoBuffer = ABuffer::CreateAsCopy(parcel.data(), parcel.dataSize());
+    notifyDrmInfo(drmInfoBuffer);
+
+    return OK;
+}
+
+void NuPlayer::GenericSource::signalBufferReturned(MediaBuffer *buffer)
+{
+    //ALOGV("signalBufferReturned %p  refCount: %d", buffer, buffer->localRefcount());
+
+    buffer->setObserver(NULL);
+    buffer->release(); // this leads to delete since that there is no observor
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index e1949f3..64f21a6 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -28,7 +28,6 @@
 namespace android {
 
 class DecryptHandle;
-class DrmManagerClient;
 struct AnotherPacketSource;
 struct ARTSPController;
 class DataSource;
@@ -38,7 +37,9 @@
 class MediaBuffer;
 struct NuCachedSource2;
 
-struct NuPlayer::GenericSource : public NuPlayer::Source {
+struct NuPlayer::GenericSource : public NuPlayer::Source,
+                                 public MediaBufferObserver // Modular DRM
+{
     GenericSource(const sp<AMessage> &notify, bool uidValid, uid_t uid);
 
     status_t setDataSource(
@@ -84,6 +85,13 @@
 
     virtual void setOffloadAudio(bool offload);
 
+    // Modular DRM
+    virtual void signalBufferReturned(MediaBuffer *buffer);
+
+    virtual status_t prepareDrm(
+            const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId, sp<ICrypto> *crypto);
+
+
 protected:
     virtual ~GenericSource();
 
@@ -109,6 +117,8 @@
         kWhatStart,
         kWhatResume,
         kWhatSecureDecodersInstantiated,
+        // Modular DRM
+        kWhatPrepareDrm,
     };
 
     struct Track {
@@ -224,8 +234,6 @@
     sp<NuCachedSource2> mCachedSource;
     sp<DataSource> mHttpSource;
     sp<MetaData> mFileMeta;
-    DrmManagerClient *mDrmManagerClient;
-    sp<DecryptHandle> mDecryptHandle;
     bool mStarted;
     bool mStopRead;
     int64_t mBitrate;
@@ -243,7 +251,6 @@
 
     status_t initFromDataSource();
     int64_t getLastReadPosition();
-    void setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position);
 
     void notifyPreparedAndCleanup(status_t err);
     void onSecureDecodersInstantiated(status_t err);
@@ -299,6 +306,13 @@
     void queueDiscontinuityIfNeeded(
             bool seeking, bool formatChange, media_track_type trackType, Track *track);
 
+    // Modular DRM
+    bool mIsDrmProtected;
+    Vector<String8> mMimes;
+
+    status_t checkDrmInfo();
+    status_t onPrepareDrm(const sp<AMessage> &msg);
+
     DISALLOW_EVIL_CONSTRUCTORS(GenericSource);
 };
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 6593fcd..e800d13 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -254,16 +254,21 @@
     sp<Source> source;
     if (IsHTTPLiveURL(url)) {
         source = new HTTPLiveSource(notify, httpService, url, headers);
+        ALOGV("setDataSourceAsync HTTPLiveSource %s", url);
     } else if (!strncasecmp(url, "rtsp://", 7)) {
         source = new RTSPSource(
                 notify, httpService, url, headers, mUIDValid, mUID);
+        ALOGV("setDataSourceAsync RTSPSource %s", url);
     } else if ((!strncasecmp(url, "http://", 7)
                 || !strncasecmp(url, "https://", 8))
                     && ((len >= 4 && !strcasecmp(".sdp", &url[len - 4]))
                     || strstr(url, ".sdp?"))) {
         source = new RTSPSource(
                 notify, httpService, url, headers, mUIDValid, mUID, true);
+        ALOGV("setDataSourceAsync RTSPSource http/https/.sdp %s", url);
     } else {
+        ALOGV("setDataSourceAsync GenericSource %s", url);
+
         sp<GenericSource> genericSource =
                 new GenericSource(notify, mUIDValid, mUID);
 
@@ -287,6 +292,9 @@
     sp<GenericSource> source =
             new GenericSource(notify, mUIDValid, mUID);
 
+    ALOGV("setDataSourceAsync fd %d/%lld/%lld source: %p",
+            fd, (long long)offset, (long long)length, source.get());
+
     status_t err = source->setDataSource(fd, offset, length);
 
     if (err != OK) {
@@ -340,6 +348,8 @@
 }
 
 void NuPlayer::prepareAsync() {
+    ALOGV("prepareAsync");
+
     (new AMessage(kWhatPrepare, this))->post();
 }
 
@@ -577,6 +587,8 @@
 
         case kWhatPrepare:
         {
+            ALOGV("onMessageReceived kWhatPrepare");
+
             mSource->prepareAsync();
             break;
         }
@@ -1133,8 +1145,9 @@
                     case SHUTTING_DOWN_DECODER:
                         break; // Wait for shutdown to complete.
                     case FLUSHED:
+                        // Both secure audio/video now. Legacy Widevine did it for secure video.
                         // Widevine source reads must stop before releasing the video decoder.
-                        if (!audio && mSource != NULL && mSourceFlags & Source::FLAG_SECURE) {
+                        if (mSource != NULL && mIsDrmProtected) {
                             mSource->stop();
                             mSourceStarted = false;
                         }
@@ -1330,6 +1343,30 @@
             break;
         }
 
+        case kWhatPrepareDrm:
+        {
+            status_t status = onPrepareDrm(msg);
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("status", status);
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatReleaseDrm:
+        {
+            status_t status = onReleaseDrm();
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("status", status);
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            response->postReply(replyID);
+            break;
+        }
+
         default:
             TRESPASS();
             break;
@@ -1391,6 +1428,9 @@
 }
 
 void NuPlayer::onStart(int64_t startPositionUs, MediaPlayerSeekMode mode) {
+    ALOGV("onStart: mCrypto: %p (%d)", mCrypto.get(),
+            (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+
     if (!mSourceStarted) {
         mSourceStarted = true;
         mSource->start();
@@ -1435,6 +1475,13 @@
     mOffloadAudio =
         canOffloadStream(audioMeta, hasVideo, mSource->isStreaming(), streamType)
                 && (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f);
+
+    // Modular DRM: Disabling audio offload if the source is protected
+    if (mOffloadAudio && mIsDrmProtected) {
+        mOffloadAudio = false;
+        ALOGV("onStart: Disabling mOffloadAudio now that the source is protected.");
+    }
+
     if (mOffloadAudio) {
         flags |= Renderer::FLAG_OFFLOAD_AUDIO;
     }
@@ -1527,12 +1574,11 @@
             *state = SHUTTING_DOWN_DECODER;
 
             ALOGV("initiating %s decoder shutdown", audio ? "audio" : "video");
-            if (!audio) {
-                // Widevine source reads must stop before releasing the video decoder.
-                if (mSource != NULL && mSourceFlags & Source::FLAG_SECURE) {
-                    mSource->stop();
-                    mSourceStarted = false;
-                }
+            // Both secure audio/video now. Legacy Widevine did it for secure video only.
+            // Widevine source reads must stop before releasing the video decoder.
+            if (mSource != NULL && mIsDrmProtected) {
+                mSource->stop();
+                mSourceStarted = false;
             }
             getDecoder(audio)->initiateShutdown();
             break;
@@ -1650,9 +1696,16 @@
     sp<AMessage> videoFormat = mSource->getFormat(false /* audio */);
     audio_stream_type_t streamType = mAudioSink->getAudioStreamType();
     const bool hasVideo = (videoFormat != NULL);
-    const bool canOffload = canOffloadStream(
+    bool canOffload = canOffloadStream(
             audioMeta, hasVideo, mSource->isStreaming(), streamType)
                     && (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f);
+
+    // Modular DRM: Disabling audio offload if the source is protected
+    if (canOffload && mIsDrmProtected) {
+        canOffload = false;
+        ALOGV("determineAudioModeChange: Disabling mOffloadAudio b/c the source is protected.");
+    }
+
     if (canOffload) {
         if (!mOffloadAudio) {
             mRenderer->signalEnableOffloadAudio();
@@ -1725,10 +1778,12 @@
             const bool hasVideo = (mSource->getFormat(false /*audio */) != NULL);
             format->setInt32("has-video", hasVideo);
             *decoder = new DecoderPassThrough(notify, mSource, mRenderer);
+            ALOGV("instantiateDecoder audio DecoderPassThrough  hasVideo: %d", hasVideo);
         } else {
             mSource->setOffloadAudio(false /* offload */);
 
             *decoder = new Decoder(notify, mSource, mPID, mUID, mRenderer);
+            ALOGV("instantiateDecoder audio Decoder");
         }
     } else {
         sp<AMessage> notify = new AMessage(kWhatVideoNotify, this);
@@ -1748,6 +1803,15 @@
         }
     }
     (*decoder)->init();
+
+    // Modular DRM
+    if (mIsDrmProtected) {
+        format->setPointer("crypto", mCrypto.get());
+        ALOGV("instantiateDecoder: mCrypto: %p (%d) isSecure: %d", mCrypto.get(),
+                (mCrypto != NULL ? mCrypto->getStrongCount() : 0),
+                (mSourceFlags & Source::FLAG_SECURE) != 0);
+    }
+
     (*decoder)->configure(format);
 
     if (!audio) {
@@ -2142,6 +2206,16 @@
     mPrepared = false;
     mResetting = false;
     mSourceStarted = false;
+
+    // Modular DRM
+    if (mCrypto != NULL) {
+        // decoders will be flushed before this so their mCrypto would go away on their own
+        // TODO change to ALOGV
+        ALOGD("performReset mCrypto: %p (%d)", mCrypto.get(),
+                (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+        mCrypto.clear();
+    }
+    mIsDrmProtected = false;
 }
 
 void NuPlayer::performScanSources() {
@@ -2236,6 +2310,7 @@
 
         case Source::kWhatPrepared:
         {
+            ALOGV("NuPlayer::onSourceNotify Source::kWhatPrepared source: %p", mSource.get());
             if (mSource == NULL) {
                 // This is a stale notification from a source that was
                 // asynchronously preparing when the client called reset().
@@ -2270,6 +2345,22 @@
             break;
         }
 
+        // Modular DRM
+        case Source::kWhatDrmInfo:
+        {
+            Parcel parcel;
+            sp<ABuffer> drmInfo;
+            CHECK(msg->findBuffer("drmInfo", &drmInfo));
+            parcel.setData(drmInfo->data(), drmInfo->size());
+
+            ALOGV("onSourceNotify() kWhatDrmInfo MEDIA_DRM_INFO drmInfo: %p  parcel size: %zu",
+                    drmInfo.get(), parcel.dataSize());
+
+            notifyListener(MEDIA_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel);
+
+            break;
+        }
+
         case Source::kWhatFlagsChanged:
         {
             uint32_t flags;
@@ -2277,6 +2368,19 @@
 
             sp<NuPlayerDriver> driver = mDriver.promote();
             if (driver != NULL) {
+
+                ALOGV("onSourceNotify() kWhatFlagsChanged  FLAG_CAN_PAUSE: %d  "
+                        "FLAG_CAN_SEEK_BACKWARD: %d \n\t\t\t\t FLAG_CAN_SEEK_FORWARD: %d  "
+                        "FLAG_CAN_SEEK: %d  FLAG_DYNAMIC_DURATION: %d \n"
+                        "\t\t\t\t FLAG_SECURE: %d  FLAG_PROTECTED: %d",
+                        (flags & Source::FLAG_CAN_PAUSE) != 0,
+                        (flags & Source::FLAG_CAN_SEEK_BACKWARD) != 0,
+                        (flags & Source::FLAG_CAN_SEEK_FORWARD) != 0,
+                        (flags & Source::FLAG_CAN_SEEK) != 0,
+                        (flags & Source::FLAG_DYNAMIC_DURATION) != 0,
+                        (flags & Source::FLAG_SECURE) != 0,
+                        (flags & Source::FLAG_PROTECTED) != 0);
+
                 if ((flags & NuPlayer::Source::FLAG_CAN_SEEK) == 0) {
                     driver->notifyListener(
                             MEDIA_INFO, MEDIA_INFO_NOT_SEEKABLE, 0);
@@ -2527,6 +2631,132 @@
         notifyListener(MEDIA_TIMED_TEXT, 0, 0);
     }
 }
+
+// Modular DRM begin
+status_t NuPlayer::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId)
+{
+    ALOGV("prepareDrm ");
+
+    // Passing to the looper anyway; called in a pre-config prepared state so no race on mCrypto
+    sp<AMessage> msg = new AMessage(kWhatPrepareDrm, this);
+    // synchronous call so just passing the address but with local copies of "const" args
+    uint8_t UUID[16];
+    memcpy(UUID, uuid, sizeof(UUID));
+    Vector<uint8_t> sessionId = drmSessionId;
+    msg->setPointer("uuid", (void*)UUID);
+    msg->setPointer("drmSessionId", (void*)&sessionId);
+
+    sp<AMessage> response;
+    status_t status = msg->postAndAwaitResponse(&response);
+
+    if (status == OK && response != NULL) {
+        CHECK(response->findInt32("status", &status));
+        ALOGV("prepareDrm ret: %d ", status);
+    } else {
+        ALOGE("prepareDrm err: %d", status);
+    }
+
+    return status;
+}
+
+status_t NuPlayer::releaseDrm()
+{
+    ALOGV("releaseDrm ");
+
+    sp<AMessage> msg = new AMessage(kWhatReleaseDrm, this);
+
+    sp<AMessage> response;
+    status_t status = msg->postAndAwaitResponse(&response);
+
+    if (status == OK && response != NULL) {
+        CHECK(response->findInt32("status", &status));
+        ALOGV("releaseDrm ret: %d ", status);
+    } else {
+        ALOGE("releaseDrm err: %d", status);
+    }
+
+    return status;
+}
+
+status_t NuPlayer::onPrepareDrm(const sp<AMessage> &msg)
+{
+    // TODO change to ALOGV
+    ALOGD("onPrepareDrm ");
+
+    status_t status = INVALID_OPERATION;
+    if (mSource == NULL) {
+        ALOGE("onPrepareDrm: No source. onPrepareDrm failed with %d.", status);
+        return status;
+    }
+
+    uint8_t *uuid;
+    Vector<uint8_t> *drmSessionId;
+    CHECK(msg->findPointer("uuid", (void**)&uuid));
+    CHECK(msg->findPointer("drmSessionId", (void**)&drmSessionId));
+
+    status = OK;
+    sp<ICrypto> crypto = NULL;
+
+    status = mSource->prepareDrm(uuid, *drmSessionId, &crypto);
+    if (crypto == NULL) {
+        ALOGE("onPrepareDrm: mSource->prepareDrm failed. status: %d", status);
+        return status;
+    }
+    ALOGV("onPrepareDrm: mSource->prepareDrm succeeded");
+
+    if (mCrypto != NULL) {
+        ALOGE("onPrepareDrm: Unexpected. Already having mCrypto: %p (%d)",
+                mCrypto.get(), mCrypto->getStrongCount());
+        mCrypto.clear();
+    }
+
+    mCrypto = crypto;
+    mIsDrmProtected = true;
+    // TODO change to ALOGV
+    ALOGD("onPrepareDrm: mCrypto: %p (%d)", mCrypto.get(),
+            (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+
+    return status;
+}
+
+status_t NuPlayer::onReleaseDrm()
+{
+    // TODO change to ALOGV
+    ALOGD("onReleaseDrm ");
+
+    mIsDrmProtected = true;
+
+    status_t status;
+    if (mCrypto != NULL) {
+        status=OK;
+        // first making sure the codecs have released their crypto reference
+        const sp<DecoderBase> &videoDecoder = getDecoder(false/*audio*/);
+        if (videoDecoder != NULL) {
+            status = videoDecoder->releaseCrypto();
+            ALOGV("onReleaseDrm: video decoder ret: %d", status);
+        }
+
+        const sp<DecoderBase> &audioDecoder = getDecoder(true/*audio*/);
+        if (audioDecoder != NULL) {
+            status_t status_audio = audioDecoder->releaseCrypto();
+            if (status == OK) {   // otherwise, returning the first error
+                status = status_audio;
+            }
+            ALOGV("onReleaseDrm: audio decoder ret: %d", status_audio);
+        }
+
+        // TODO change to ALOGV
+        ALOGD("onReleaseDrm: mCrypto: %p (%d)", mCrypto.get(),
+                (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+        mCrypto.clear();
+    } else {   // mCrypto == NULL
+        ALOGE("onReleaseDrm: Unexpected. There is no crypto.");
+        status = INVALID_OPERATION;
+    }
+
+    return status;
+}
+// Modular DRM end
 ////////////////////////////////////////////////////////////////////////////////
 
 sp<AMessage> NuPlayer::Source::getFormat(bool audio) {
@@ -2559,12 +2789,24 @@
 }
 
 void NuPlayer::Source::notifyPrepared(status_t err) {
+    ALOGV("Source::notifyPrepared %d", err);
     sp<AMessage> notify = dupNotify();
     notify->setInt32("what", kWhatPrepared);
     notify->setInt32("err", err);
     notify->post();
 }
 
+void NuPlayer::Source::notifyDrmInfo(const sp<ABuffer> &drmInfoBuffer)
+{
+    ALOGV("Source::notifyDrmInfo");
+
+    sp<AMessage> notify = dupNotify();
+    notify->setInt32("what", kWhatDrmInfo);
+    notify->setBuffer("drmInfo", drmInfoBuffer);
+
+    notify->post();
+}
+
 void NuPlayer::Source::notifyInstantiateSecureDecoders(const sp<AMessage> &reply) {
     sp<AMessage> notify = dupNotify();
     notify->setInt32("what", kWhatInstantiateSecureDecoders);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index cc8c97a..d3cb7c1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -19,6 +19,7 @@
 #define NU_PLAYER_H_
 
 #include <media/AudioResamplerPublic.h>
+#include <media/ICrypto.h>
 #include <media/MediaPlayerInterface.h>
 #include <media/stagefright/foundation/AHandler.h>
 
@@ -88,6 +89,10 @@
     sp<MetaData> getFileMeta();
     float getFrameRate();
 
+    // Modular DRM
+    status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId);
+    status_t releaseDrm();
+
 protected:
     virtual ~NuPlayer();
 
@@ -142,6 +147,8 @@
         kWhatSelectTrack                = 'selT',
         kWhatGetDefaultBufferingSettings = 'gDBS',
         kWhatSetBufferingSettings       = 'sBuS',
+        kWhatPrepareDrm                 = 'pDrm',
+        kWhatReleaseDrm                 = 'rDrm',
     };
 
     wp<NuPlayerDriver> mDriver;
@@ -223,6 +230,10 @@
     // Pause state as requested by source (internally) due to buffering
     bool mPausedForBuffering;
 
+    // Modular DRM
+    sp<ICrypto> mCrypto;
+    bool mIsDrmProtected;
+
     inline const sp<DecoderBase> &getDecoder(bool audio) {
         return audio ? mAudioDecoder : mVideoDecoder;
     }
@@ -294,6 +305,9 @@
 
     void writeTrackInfo(Parcel* reply, const sp<AMessage>& format) const;
 
+    status_t onPrepareDrm(const sp<AMessage> &msg);
+    status_t onReleaseDrm();
+
     DISALLOW_EVIL_CONSTRUCTORS(NuPlayer);
 };
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 0a0a8aa..5689e95 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -23,6 +23,7 @@
 
 #include "NuPlayerCCDecoder.h"
 #include "NuPlayerDecoder.h"
+#include "NuPlayerDrm.h"
 #include "NuPlayerRenderer.h"
 #include "NuPlayerSource.h"
 
@@ -255,6 +256,13 @@
             break;
         }
 
+        case kWhatDrmReleaseCrypto:
+        {
+            ALOGV("kWhatDrmReleaseCrypto");
+            onReleaseCrypto(msg);
+            break;
+        }
+
         default:
             DecoderBase::onMessageReceived(msg);
             break;
@@ -312,8 +320,19 @@
         // any error signaling will occur.
         ALOGW_IF(err != OK, "failed to disconnect from surface: %d", err);
     }
+
+    // Modular DRM
+    void *pCrypto;
+    if (!format->findPointer("crypto", &pCrypto)) {
+        pCrypto = NULL;
+    }
+    sp<ICrypto> crypto = (ICrypto*)pCrypto;
+    ALOGV("onConfigure mCrypto: %p (%d)  mIsSecure: %d",
+            crypto.get(), (crypto != NULL ? crypto->getStrongCount() : 0), mIsSecure);
+
     err = mCodec->configure(
-            format, mSurface, NULL /* crypto */, 0 /* flags */);
+            format, mSurface, crypto, 0 /* flags */);
+
     if (err != OK) {
         ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err);
         mCodec->release();
@@ -559,6 +578,43 @@
     notify->post();
 }
 
+status_t NuPlayer::Decoder::releaseCrypto()
+{
+    ALOGV("releaseCrypto");
+
+    sp<AMessage> msg = new AMessage(kWhatDrmReleaseCrypto, this);
+
+    sp<AMessage> response;
+    status_t status = msg->postAndAwaitResponse(&response);
+    if (status == OK && response != NULL) {
+        CHECK(response->findInt32("status", &status));
+        ALOGV("releaseCrypto ret: %d ", status);
+    } else {
+        ALOGE("releaseCrypto err: %d", status);
+    }
+
+    return status;
+}
+
+void NuPlayer::Decoder::onReleaseCrypto(const sp<AMessage>& msg)
+{
+    status_t status = INVALID_OPERATION;
+    if (mCodec != NULL) {
+        status = mCodec->releaseCrypto();
+    } else {
+        // returning OK if the codec has been already released
+        status = OK;
+        ALOGE("onReleaseCrypto No mCodec. err: %d", status);
+    }
+
+    sp<AMessage> response = new AMessage;
+    response->setInt32("status", status);
+
+    sp<AReplyToken> replyID;
+    CHECK(msg->senderAwaitsResponse(&replyID));
+    response->postReply(replyID);
+}
+
 bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) {
     if (isDiscontinuityPending()) {
         return false;
@@ -929,6 +985,10 @@
             flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
         }
 
+        // Modular DRM
+        MediaBuffer *mediaBuf = NULL;
+        NuPlayerDrm::CryptoInfo *cryptInfo = NULL;
+
         // copy into codec buffer
         if (needsCopy) {
             if (buffer->size() > codecBuffer->capacity()) {
@@ -936,24 +996,68 @@
                 mDequeuedInputBuffers.push_back(bufferIx);
                 return false;
             }
-            codecBuffer->setRange(0, buffer->size());
-            memcpy(codecBuffer->data(), buffer->data(), buffer->size());
-        }
 
-        status_t err = mCodec->queueInputBuffer(
-                        bufferIx,
-                        codecBuffer->offset(),
-                        codecBuffer->size(),
-                        timeUs,
-                        flags);
+            if (buffer->data() != NULL) {
+                codecBuffer->setRange(0, buffer->size());
+                memcpy(codecBuffer->data(), buffer->data(), buffer->size());
+            } else { // No buffer->data()
+                //Modular DRM
+                mediaBuf = (MediaBuffer*)buffer->getMediaBufferBase();
+                if (mediaBuf != NULL) {
+                    codecBuffer->setRange(0, mediaBuf->size());
+                    memcpy(codecBuffer->data(), mediaBuf->data(), mediaBuf->size());
+
+                    sp<MetaData> meta_data = mediaBuf->meta_data();
+                    cryptInfo = NuPlayerDrm::getSampleCryptoInfo(meta_data);
+
+                    // since getMediaBuffer() has incremented the refCount
+                    mediaBuf->release();
+                } else { // No mediaBuf
+                    ALOGE("onInputBufferFetched: buffer->data()/mediaBuf are NULL for %p",
+                            buffer.get());
+                    handleError(UNKNOWN_ERROR);
+                    return false;
+                }
+            } // buffer->data()
+        } // needsCopy
+
+        status_t err;
+        AString errorDetailMsg;
+        if (cryptInfo != NULL) {
+            err = mCodec->queueSecureInputBuffer(
+                    bufferIx,
+                    codecBuffer->offset(),
+                    cryptInfo->subSamples,
+                    cryptInfo->numSubSamples,
+                    cryptInfo->key,
+                    cryptInfo->iv,
+                    cryptInfo->mode,
+                    cryptInfo->pattern,
+                    timeUs,
+                    flags,
+                    &errorDetailMsg);
+            // synchronous call so done with cryptInfo here
+            free(cryptInfo);
+        } else {
+            err = mCodec->queueInputBuffer(
+                    bufferIx,
+                    codecBuffer->offset(),
+                    codecBuffer->size(),
+                    timeUs,
+                    flags,
+                    &errorDetailMsg);
+        } // no cryptInfo
+
         if (err != OK) {
-            ALOGE("Failed to queue input buffer for %s (err=%d)",
-                    mComponentName.c_str(), err);
+            ALOGE("onInputBufferFetched: queue%sInputBuffer failed for %s (err=%d, %s)",
+                    (cryptInfo != NULL ? "Secure" : ""),
+                    mComponentName.c_str(), err, errorDetailMsg.c_str());
             handleError(err);
         } else {
             mInputBufferIsDequeued.editItemAt(bufferIx) = false;
         }
-    }
+
+    }   // buffer != NULL
     return true;
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 82db59c..de21379 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -39,6 +39,8 @@
     // sets the output surface of video decoders.
     virtual status_t setVideoSurface(const sp<Surface> &surface);
 
+    virtual status_t releaseCrypto();
+
 protected:
     virtual ~Decoder();
 
@@ -57,7 +59,8 @@
         kWhatCodecNotify         = 'cdcN',
         kWhatRenderBuffer        = 'rndr',
         kWhatSetVideoSurface     = 'sSur',
-        kWhatAudioOutputFormatChanged = 'aofc'
+        kWhatAudioOutputFormatChanged = 'aofc',
+        kWhatDrmReleaseCrypto    = 'rDrm',
     };
 
     enum {
@@ -135,6 +138,8 @@
 
     void notifyResumeCompleteIfNecessary();
 
+    void onReleaseCrypto(const sp<AMessage>& msg);
+
     DISALLOW_EVIL_CONSTRUCTORS(Decoder);
 };
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index 6811903..dcdfcaf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -51,6 +51,10 @@
         return mStats;
     }
 
+    virtual status_t releaseCrypto() {
+        return INVALID_OPERATION;
+    }
+
     enum {
         kWhatInputDiscontinuity  = 'inDi',
         kWhatVideoSizeChanged    = 'viSC',
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 0ddbd63..abea5bc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -884,8 +884,8 @@
 
 void NuPlayerDriver::notifyListener_l(
         int msg, int ext1, int ext2, const Parcel *in) {
-    ALOGD("notifyListener_l(%p), (%d, %d, %d), loop setting(%d, %d)",
-            this, msg, ext1, ext2, mAutoLoop, mLooping);
+    ALOGD("notifyListener_l(%p), (%d, %d, %d, %d), loop setting(%d, %d)",
+            this, msg, ext1, ext2, (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
     switch (msg) {
         case MEDIA_PLAYBACK_COMPLETE:
         {
@@ -943,6 +943,8 @@
 }
 
 void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
+    ALOGV("notifyPrepareCompleted %d", err);
+
     Mutex::Autolock autoLock(mLock);
 
     if (mState != STATE_PREPARING) {
@@ -987,4 +989,33 @@
     mPlayerFlags = flags;
 }
 
+// Modular DRM
+status_t NuPlayerDriver::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId)
+{
+    ALOGV("prepareDrm(%p) state: %d", this, mState);
+
+    Mutex::Autolock autoLock(mLock);
+
+    // leaving the state verification for mediaplayer.cpp
+    status_t ret = mPlayer->prepareDrm(uuid, drmSessionId);
+
+    ALOGV("prepareDrm ret: %d", ret);
+
+    return ret;
+}
+
+status_t NuPlayerDriver::releaseDrm()
+{
+    ALOGV("releaseDrm(%p) state: %d", this, mState);
+
+    Mutex::Autolock autoLock(mLock);
+
+    // leaving the state verification for mediaplayer.cpp
+    status_t ret = mPlayer->releaseDrm();
+
+    ALOGV("releaseDrm ret: %d", ret);
+
+    return ret;
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 5bfc539..972a348 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -87,6 +87,10 @@
     void notifyListener(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
     void notifyFlagsChanged(uint32_t flags);
 
+    // Modular DRM
+    virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId);
+    virtual status_t releaseDrm();
+
 protected:
     virtual ~NuPlayerDriver();
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
new file mode 100644
index 0000000..cb9a5f8
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2017 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 "NuPlayerDrm"
+
+#include "NuPlayerDrm.h"
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaDrmService.h>
+#include <utils/Log.h>
+
+
+namespace android {
+
+// static helpers - internal
+
+sp<IDrm> NuPlayerDrm::CreateDrm(status_t *pstatus)
+{
+    status_t &status = *pstatus;
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> binder = sm->getService(String16("media.drm"));
+    ALOGV("CreateDrm binder %p", (binder != NULL ? binder.get() : 0));
+
+    sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
+    if (service == NULL) {
+        ALOGE("CreateDrm failed at IMediaDrmService");
+        return NULL;
+    }
+
+    sp<IDrm> drm = service->makeDrm();
+    if (drm == NULL) {
+        ALOGE("CreateDrm failed at makeDrm");
+        return NULL;
+    }
+
+    // this is before plugin creation so NO_INIT is fine
+    status = drm->initCheck();
+    if (status != OK && status != NO_INIT) {
+        ALOGE("CreateDrm failed drm->initCheck(): %d", status);
+        return NULL;
+    }
+    return drm;
+}
+
+sp<ICrypto> NuPlayerDrm::createCrypto(status_t *pstatus)
+{
+    status_t &status = *pstatus;
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> binder = sm->getService(String16("media.drm"));
+
+    sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
+    if (service == NULL) {
+        status = UNKNOWN_ERROR;
+        ALOGE("CreateCrypto failed at IMediaDrmService");
+        return NULL;
+    }
+
+    sp<ICrypto> crypto = service->makeCrypto();
+    if (crypto == NULL) {
+        status = UNKNOWN_ERROR;
+        ALOGE("createCrypto failed");
+        return NULL;
+    }
+
+    // this is before plugin creation so NO_INIT is fine
+    status = crypto->initCheck();
+    if (status != OK && status != NO_INIT) {
+        ALOGE("createCrypto failed crypto->initCheck(): %d", status);
+        return NULL;
+    }
+
+    return crypto;
+}
+
+Vector<DrmUUID> NuPlayerDrm::parsePSSH(const void *pssh, size_t psshsize)
+{
+    Vector<DrmUUID> drmSchemes, empty;
+    const int DATALEN_SIZE = 4;
+
+    // the format of the buffer is 1 or more of:
+    //    {
+    //        16 byte uuid
+    //        4 byte data length N
+    //        N bytes of data
+    //    }
+    // Determine the number of entries in the source data.
+    // Since we got the data from stagefright, we trust it is valid and properly formatted.
+
+    const uint8_t *data = (const uint8_t*)pssh;
+    size_t len = psshsize;
+    size_t numentries = 0;
+    while (len > 0) {
+        if (len < DrmUUID::UUID_SIZE) {
+            ALOGE("ParsePSSH: invalid PSSH data");
+            return empty;
+        }
+
+        const uint8_t *uuidPtr = data;
+
+        // skip uuid
+        data += DrmUUID::UUID_SIZE;
+        len -= DrmUUID::UUID_SIZE;
+
+        // get data length
+        if (len < DATALEN_SIZE) {
+            ALOGE("ParsePSSH: invalid PSSH data");
+            return empty;
+        }
+
+        uint32_t datalen = *((uint32_t*)data);
+        data += DATALEN_SIZE;
+        len -= DATALEN_SIZE;
+
+        if (len < datalen) {
+            ALOGE("ParsePSSH: invalid PSSH data");
+            return empty;
+        }
+
+        // skip the data
+        data += datalen;
+        len -= datalen;
+
+        DrmUUID _uuid(uuidPtr);
+        drmSchemes.add(_uuid);
+
+        ALOGV("ParsePSSH[%zu]: %s: %s", numentries,
+                _uuid.toHexString().string(),
+                DrmUUID::arrayToHex(data, datalen).string()
+             );
+
+        numentries++;
+    }
+
+    return drmSchemes;
+}
+
+Vector<DrmUUID> NuPlayerDrm::getSupportedDrmSchemes(const void *pssh, size_t psshsize)
+{
+    Vector<DrmUUID> psshDRMs = parsePSSH(pssh, psshsize);
+
+    Vector<DrmUUID> supportedDRMs;
+     // temporary DRM object for crypto Scheme enquiry (without creating a plugin)
+    status_t status = OK;
+    sp<IDrm> drm = CreateDrm(&status);
+    if (drm != NULL) {
+        for (size_t i = 0; i < psshDRMs.size(); i++) {
+            DrmUUID uuid = psshDRMs[i];
+            if (drm->isCryptoSchemeSupported(uuid.ptr(), String8()))
+                supportedDRMs.add(uuid);
+        }
+
+        drm.clear();
+    } else {
+        ALOGE("getSupportedDrmSchemes: Can't create Drm obj: %d", status);
+    }
+
+    ALOGV("getSupportedDrmSchemes: psshDRMs: %zu supportedDRMs: %zu",
+            psshDRMs.size(), supportedDRMs.size());
+
+    return supportedDRMs;
+}
+
+// static helpers - public
+
+sp<ICrypto> NuPlayerDrm::createCryptoAndPlugin(const uint8_t uuid[16],
+        const Vector<uint8_t> &drmSessionId, status_t &status)
+{
+    // Extra check
+    if (drmSessionId.isEmpty()) {
+        status = INVALID_OPERATION;
+        ALOGE("createCryptoAndPlugin: Failed. Empty drmSessionId. status: %d", status);
+        return NULL;
+    }
+
+    status = OK;
+    sp<ICrypto> crypto = createCrypto(&status);
+    if (crypto == NULL) {
+        ALOGE("createCryptoAndPlugin: createCrypto failed. status: %d", status);
+        return NULL;
+    }
+    ALOGV("createCryptoAndPlugin: createCrypto succeeded");
+
+    status = crypto->createPlugin(uuid, drmSessionId.array(), drmSessionId.size());
+    if (status != OK) {
+        ALOGE("createCryptoAndPlugin: createCryptoPlugin failed. status: %d", status);
+        // crypto will clean itself when leaving the current scope
+        return NULL;
+    }
+
+    return crypto;
+}
+
+// Parcel has only private copy constructor so passing it in rather than returning
+void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize,
+        const Vector<String8> &mimes_in, Parcel *parcel)
+{
+    // 0) Make mimes a vector of unique items while keeping the original order; video first
+    Vector<String8> mimes;
+    for (size_t j = 0; j < mimes_in.size(); j++) {
+        String8 mime = mimes_in[j];
+        bool exists = false;
+        for (size_t i = 0; i < mimes.size() && !exists; i++) {
+            if (mimes[i] == mime) {
+                exists = true;
+            }
+        } // for i
+
+        if (!exists) {
+            mimes.add(mime);
+        }
+    } // for j
+
+
+    // 1) PSSH bytes
+    parcel->writeUint32(psshsize);
+    parcel->writeByteArray(psshsize, (const uint8_t*)pssh);
+
+    ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  PSSH: size: %zu %s", psshsize,
+            DrmUUID::arrayToHex((uint8_t*)pssh, psshsize).string());
+
+    // 2) supportedDRMs
+    Vector<DrmUUID> supportedDRMs = getSupportedDrmSchemes(pssh, psshsize);
+    parcel->writeUint32(supportedDRMs.size());
+    for (size_t i = 0; i < supportedDRMs.size(); i++) {
+        DrmUUID uuid = supportedDRMs[i];
+        parcel->writeByteArray(DrmUUID::UUID_SIZE, uuid.ptr());
+
+        ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  supportedScheme[%zu] %s", i,
+                uuid.toHexString().string());
+    }
+
+    // TODO: remove mimes after it's removed from Java DrmInfo
+    // 3) mimes
+    parcel->writeUint32(mimes.size());
+    for (size_t i = 0; i < mimes.size(); i++) {
+        // writing as String16 so the Java framework side can unpack it to Java String
+        String16 mime(mimes[i]);
+        parcel->writeString16(mime);
+
+        ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  MIME[%zu] %s",
+                i, mimes[i].string());
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/// Helpers for NuPlayerDecoder
+////////////////////////////////////////////////////////////////////////////////////////////
+
+NuPlayerDrm::CryptoInfo *NuPlayerDrm::makeCryptoInfo(
+        int numSubSamples,
+        uint8_t key[kBlockSize],
+        uint8_t iv[kBlockSize],
+        CryptoPlugin::Mode mode,
+        size_t *clearbytes,
+        size_t *encryptedbytes)
+{
+    // size needed to store all the crypto data
+    size_t cryptosize = sizeof(CryptoInfo) +
+                        sizeof(CryptoPlugin::SubSample) * numSubSamples;
+    CryptoInfo *ret = (CryptoInfo*) malloc(cryptosize);
+    if (ret == NULL) {
+        ALOGE("couldn't allocate %zu bytes", cryptosize);
+        return NULL;
+    }
+    ret->numSubSamples = numSubSamples;
+    memcpy(ret->key, key, kBlockSize);
+    memcpy(ret->iv, iv, kBlockSize);
+    ret->mode = mode;
+    ret->pattern.mEncryptBlocks = 0;
+    ret->pattern.mSkipBlocks = 0;
+    ret->subSamples = (CryptoPlugin::SubSample*)(ret + 1);
+    CryptoPlugin::SubSample *subSamples = ret->subSamples;
+
+    for (int i = 0; i < numSubSamples; i++) {
+        subSamples[i].mNumBytesOfClearData = (clearbytes == NULL) ? 0 : clearbytes[i];
+        subSamples[i].mNumBytesOfEncryptedData = (encryptedbytes == NULL) ?
+                                                  0 :
+                                                  encryptedbytes[i];
+    }
+
+    return ret;
+}
+
+NuPlayerDrm::CryptoInfo *NuPlayerDrm::getSampleCryptoInfo(sp<MetaData> meta)
+{
+    uint32_t type;
+    const void *crypteddata;
+    size_t cryptedsize;
+
+    if (meta == NULL) {
+        ALOGE("getSampleCryptoInfo: Unexpected. No meta data for sample.");
+        return NULL;
+    }
+
+    if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
+        return NULL;
+    }
+    size_t numSubSamples = cryptedsize / sizeof(size_t);
+
+    if (numSubSamples <= 0) {
+        ALOGE("getSampleCryptoInfo INVALID numSubSamples: %zu", numSubSamples);
+        return NULL;
+    }
+
+    const void *cleardata;
+    size_t clearsize;
+    if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
+        if (clearsize != cryptedsize) {
+            // The two must be of the same length.
+            ALOGE("getSampleCryptoInfo mismatch cryptedsize: %zu != clearsize: %zu",
+                    cryptedsize, clearsize);
+            return NULL;
+        }
+    }
+
+    const void *key;
+    size_t keysize;
+    if (meta->findData(kKeyCryptoKey, &type, &key, &keysize)) {
+        if (keysize != kBlockSize) {
+            ALOGE("getSampleCryptoInfo Keys must be %zu bytes in length: %zu",
+                    kBlockSize, keysize);
+            // Keys must be 16 bytes in length.
+            return NULL;
+        }
+    }
+
+    const void *iv;
+    size_t ivsize;
+    if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
+        if (ivsize != kBlockSize) {
+            ALOGE("getSampleCryptoInfo IV must be %zu bytes in length: %zu",
+                    kBlockSize, ivsize);
+            // IVs must be 16 bytes in length.
+            return NULL;
+        }
+    }
+
+    int32_t mode;
+    if (!meta->findInt32(kKeyCryptoMode, &mode)) {
+        mode = CryptoPlugin::kMode_AES_CTR;
+    }
+
+    return makeCryptoInfo(numSubSamples,
+            (uint8_t*) key,
+            (uint8_t*) iv,
+            (CryptoPlugin::Mode)mode,
+            (size_t*) cleardata,
+            (size_t*) crypteddata);
+}
+
+}   // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
new file mode 100644
index 0000000..6704bd1
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 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 NUPLAYER_DRM_H_
+#define NUPLAYER_DRM_H_
+
+#include <binder/Parcel.h>
+#include <media/ICrypto.h>
+#include <media/IDrm.h>
+#include <media/stagefright/MetaData.h> // for CryptInfo
+
+
+namespace android {
+
+    struct DrmUUID {
+        static const int UUID_SIZE = 16;
+
+        DrmUUID() {
+            memset(this->uuid, 0, sizeof(uuid));
+        }
+
+        // to allow defining Vector/KeyedVector of UUID type
+        DrmUUID(const DrmUUID &a) {
+            memcpy(this->uuid, a.uuid, sizeof(uuid));
+        }
+
+        // to allow defining Vector/KeyedVector of UUID type
+        DrmUUID(const uint8_t uuid_in[UUID_SIZE]) {
+            memcpy(this->uuid, uuid_in, sizeof(uuid));
+        }
+
+        const uint8_t *ptr() const {
+            return uuid;
+        }
+
+        String8 toHexString() const {
+            return arrayToHex(uuid, UUID_SIZE);
+        }
+
+        static String8 toHexString(const uint8_t uuid_in[UUID_SIZE]) {
+            return arrayToHex(uuid_in, UUID_SIZE);
+        }
+
+        static String8 arrayToHex(const uint8_t *array, int bytes) {
+            String8 result;
+            for (int i = 0; i < bytes; i++) {
+                result.appendFormat("%02x", array[i]);
+            }
+
+            return result;
+        }
+
+    protected:
+        uint8_t uuid[UUID_SIZE];
+    };
+
+
+    struct NuPlayerDrm {
+
+        // static helpers - internal
+
+    protected:
+        static sp<IDrm> CreateDrm(status_t *pstatus);
+        static sp<ICrypto> createCrypto(status_t *pstatus);
+        static Vector<DrmUUID> parsePSSH(const void *pssh, size_t psshsize);
+        static Vector<DrmUUID> getSupportedDrmSchemes(const void *pssh, size_t psshsize);
+
+        // static helpers - public
+
+    public:
+        static sp<ICrypto> createCryptoAndPlugin(const uint8_t uuid[16],
+                const Vector<uint8_t> &drmSessionId, status_t &status);
+        // Parcel has only private copy constructor so passing it in rather than returning
+        static void retrieveDrmInfo(const void *pssh, size_t psshsize,
+                const Vector<String8> &mimes_in, Parcel *parcel);
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        /// Helpers for NuPlayerDecoder
+        ////////////////////////////////////////////////////////////////////////////////////////////
+
+        static const uint8_t kBlockSize = 16; // AES_BLOCK_SIZE
+
+        struct CryptoInfo {
+            int numSubSamples;
+            uint8_t key[kBlockSize];
+            uint8_t iv[kBlockSize];
+            CryptoPlugin::Mode mode;
+            CryptoPlugin::Pattern pattern;
+            CryptoPlugin::SubSample *subSamples;
+        };
+
+        static CryptoInfo *makeCryptoInfo(
+                int numSubSamples,
+                uint8_t key[kBlockSize],
+                uint8_t iv[kBlockSize],
+                CryptoPlugin::Mode mode,
+                size_t *clearbytes,
+                size_t *encryptedbytes);
+
+        static CryptoInfo *getSampleCryptoInfo(sp<MetaData> meta);
+
+    };  // NuPlayerDrm
+
+}   // android
+
+#endif     //NUPLAYER_DRM_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 0429ef1..e7cca27 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -20,9 +20,10 @@
 
 #include "NuPlayer.h"
 
+#include <media/ICrypto.h>
+#include <media/mediaplayer.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MetaData.h>
-#include <media/mediaplayer.h>
 #include <utils/Vector.h>
 
 namespace android {
@@ -55,6 +56,8 @@
         kWhatQueueDecoderShutdown,
         kWhatDrmNoLicense,
         kWhatInstantiateSecureDecoders,
+        // Modular DRM
+        kWhatDrmInfo,
     };
 
     // The provides message is used to notify the player about various
@@ -132,6 +135,17 @@
 
     virtual void setOffloadAudio(bool /* offload */) {}
 
+    // Modular DRM
+    virtual status_t prepareDrm(
+            const uint8_t /*uuid*/[16], const Vector<uint8_t> &/*drmSessionId*/,
+            sp<ICrypto> */*crypto*/) {
+        return INVALID_OPERATION;
+    }
+
+    virtual status_t releaseDrm() {
+        return INVALID_OPERATION;
+    }
+
 protected:
     virtual ~Source() {}
 
@@ -143,6 +157,8 @@
     void notifyVideoSizeChanged(const sp<AMessage> &format = NULL);
     void notifyInstantiateSecureDecoders(const sp<AMessage> &reply);
     void notifyPrepared(status_t err = OK);
+    // Modular DRM
+    void notifyDrmInfo(const sp<ABuffer> &buffer);
 
 private:
     sp<AMessage> mNotify;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index a332cce..cbd5802 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -758,6 +758,51 @@
     return err;
 }
 
+status_t MediaCodec::releaseCrypto()
+{
+    ALOGV("releaseCrypto");
+
+    sp<AMessage> msg = new AMessage(kWhatDrmReleaseCrypto, this);
+
+    sp<AMessage> response;
+    status_t status = msg->postAndAwaitResponse(&response);
+
+    if (status == OK && response != NULL) {
+        CHECK(response->findInt32("status", &status));
+        ALOGV("releaseCrypto ret: %d ", status);
+    }
+    else {
+        ALOGE("releaseCrypto err: %d", status);
+    }
+
+    return status;
+}
+
+void MediaCodec::onReleaseCrypto(const sp<AMessage>& msg)
+{
+    status_t status = INVALID_OPERATION;
+    if (mCrypto != NULL) {
+        ALOGV("onReleaseCrypto: mCrypto: %p (%d)", mCrypto.get(), mCrypto->getStrongCount());
+        mBufferChannel->setCrypto(NULL);
+        // TODO change to ALOGV
+        ALOGD("onReleaseCrypto: [before clear]  mCrypto: %p (%d)",
+                mCrypto.get(), mCrypto->getStrongCount());
+        mCrypto.clear();
+
+        status = OK;
+    }
+    else {
+        ALOGW("onReleaseCrypto: No mCrypto. err: %d", status);
+    }
+
+    sp<AMessage> response = new AMessage;
+    response->setInt32("status", status);
+
+    sp<AReplyToken> replyID;
+    CHECK(msg->senderAwaitsResponse(&replyID));
+    response->postReply(replyID);
+}
+
 status_t MediaCodec::setInputSurface(
         const sp<PersistentSurface> &surface) {
     sp<AMessage> msg = new AMessage(kWhatSetInputSurface, this);
@@ -1938,9 +1983,15 @@
                 crypto = NULL;
             }
 
+            ALOGV("kWhatConfigure: Old mCrypto: %p (%d)",
+                    mCrypto.get(), (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+
             mCrypto = static_cast<ICrypto *>(crypto);
             mBufferChannel->setCrypto(mCrypto);
 
+            ALOGV("kWhatConfigure: New mCrypto: %p (%d)",
+                    mCrypto.get(), (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+
             uint32_t flags;
             CHECK(msg->findInt32("flags", (int32_t *)&flags));
 
@@ -2471,6 +2522,12 @@
             break;
         }
 
+        case kWhatDrmReleaseCrypto:
+        {
+            onReleaseCrypto(msg);
+            break;
+        }
+
         default:
             TRESPASS();
     }
@@ -2530,6 +2587,10 @@
         delete mSoftRenderer;
         mSoftRenderer = NULL;
 
+        if ( mCrypto != NULL ) {
+            ALOGV("setState: ~mCrypto: %p (%d)",
+                    mCrypto.get(), (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+        }
         mCrypto.clear();
         handleSetSurface(NULL);