diff options
| author | Jeff Tinker <jtinker@google.com> | 2013-02-08 10:18:35 -0800 | 
|---|---|---|
| committer | Jeff Tinker <jtinker@google.com> | 2013-03-13 13:39:48 -0700 | 
| commit | 441a78d5e224e0d67f9b52fa9adc795c6944159b (patch) | |
| tree | 855eed2e1ee757c6019b6da9137d659fdc20fdcf | |
| parent | c148e7a671c289f82f6ca40e29defcc43e1c6d0b (diff) | |
| download | frameworks_av-441a78d5e224e0d67f9b52fa9adc795c6944159b.zip frameworks_av-441a78d5e224e0d67f9b52fa9adc795c6944159b.tar.gz frameworks_av-441a78d5e224e0d67f9b52fa9adc795c6944159b.tar.bz2  | |
Implementing MediaDrm APIs
Change-Id: I9ff8eeb7d0c383b5c0c68cd54eb54ce7d2d22fe6
| -rw-r--r-- | drm/mediadrm/plugins/mock/Android.mk | 38 | ||||
| -rw-r--r-- | drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp | 417 | ||||
| -rw-r--r-- | drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h | 122 | ||||
| -rw-r--r-- | include/media/IDrm.h | 89 | ||||
| -rw-r--r-- | include/media/IMediaPlayerService.h | 2 | ||||
| -rw-r--r-- | media/libmedia/Android.mk | 1 | ||||
| -rw-r--r-- | media/libmedia/IDrm.cpp | 551 | ||||
| -rw-r--r-- | media/libmedia/IMediaPlayerService.cpp | 15 | ||||
| -rw-r--r-- | media/libmediaplayerservice/Android.mk | 2 | ||||
| -rw-r--r-- | media/libmediaplayerservice/Drm.cpp | 423 | ||||
| -rw-r--r-- | media/libmediaplayerservice/Drm.h | 100 | ||||
| -rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.cpp | 5 | ||||
| -rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.h | 1 | ||||
| -rw-r--r-- | media/libmediaplayerservice/SharedLibrary.cpp | 49 | ||||
| -rw-r--r-- | media/libmediaplayerservice/SharedLibrary.h | 39 | 
15 files changed, 1854 insertions, 0 deletions
diff --git a/drm/mediadrm/plugins/mock/Android.mk b/drm/mediadrm/plugins/mock/Android.mk new file mode 100644 index 0000000..a056cd8 --- /dev/null +++ b/drm/mediadrm/plugins/mock/Android.mk @@ -0,0 +1,38 @@ +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +#      http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ +    MockDrmCryptoPlugin.cpp + +LOCAL_MODULE := libmockdrmcryptoplugin + +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/mediadrm + +LOCAL_SHARED_LIBRARIES := \ +    libutils + +LOCAL_C_INCLUDES += \ +    $(TOP)/frameworks/av/include \ +    $(TOP)/frameworks/native/include/media + +# Set the following flag to enable the decryption passthru flow +#LOCAL_CFLAGS += -DENABLE_PASSTHRU_DECRYPTION + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp new file mode 100644 index 0000000..91f5c9c --- /dev/null +++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "MockDrmCryptoPlugin" +#include <utils/Log.h> + + +#include "drm/DrmAPI.h" +#include "MockDrmCryptoPlugin.h" + +using namespace android; + +// Shared library entry point +DrmFactory *createDrmFactory() +{ +    return new MockDrmFactory(); +} + +// Shared library entry point +CryptoFactory *createCryptoFactory() +{ +    return new MockCryptoFactory(); +} + +const uint8_t mock_uuid[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, +                               0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; + +namespace android { + +    // MockDrmFactory +    bool MockDrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) +    { +        return (!memcmp(uuid, mock_uuid, sizeof(uuid))); +    } + +    status_t MockDrmFactory::createDrmPlugin(const uint8_t uuid[16], DrmPlugin **plugin) +    { +        *plugin = new MockDrmPlugin(); +        return OK; +    } + +    // MockCryptoFactory +    bool MockCryptoFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const +    { +        return (!memcmp(uuid, mock_uuid, sizeof(uuid))); +    } + +    status_t MockCryptoFactory::createPlugin(const uint8_t uuid[16], const void *data, +                                             size_t size, CryptoPlugin **plugin) +    { +        *plugin = new MockCryptoPlugin(); +        return OK; +    } + + +    // MockDrmPlugin methods + +    status_t MockDrmPlugin::openSession(Vector<uint8_t> &sessionId) +    { +        const size_t kSessionIdSize = 8; + +        Mutex::Autolock lock(mLock); +        for (size_t i = 0; i < kSessionIdSize / sizeof(long); i++) { +            long r = random(); +            sessionId.appendArray((uint8_t *)&r, sizeof(long)); +        } +        mSessions.add(sessionId); + +        ALOGD("MockDrmPlugin::openSession() -> %s", vectorToString(sessionId).string()); +        return OK; +    } + +    status_t MockDrmPlugin::closeSession(Vector<uint8_t> const &sessionId) +    { +        Mutex::Autolock lock(mLock); +        ALOGD("MockDrmPlugin::closeSession(%s)", vectorToString(sessionId).string()); +        ssize_t index = findSession(sessionId); +        if (index == kNotFound) { +            ALOGD("Invalid sessionId"); +            return BAD_VALUE; +        } +        mSessions.removeAt(index); +        return OK; +    } + + +    status_t MockDrmPlugin::getLicenseRequest(Vector<uint8_t> const &sessionId, +                                              Vector<uint8_t> const &initData, +                                              String8 const &mimeType, LicenseType licenseType, +                                              KeyedVector<String8, String8> const &optionalParameters, +                                              Vector<uint8_t> &request, String8 &defaultUrl) +    { +        Mutex::Autolock lock(mLock); +        ALOGD("MockDrmPlugin::getLicenseRequest(sessionId=%s, initData=%s, mimeType=%s" +              ", licenseType=%d, optionalParameters=%s))", +              vectorToString(sessionId).string(), vectorToString(initData).string(), mimeType.string(), +              licenseType, stringMapToString(optionalParameters).string()); + +        ssize_t index = findSession(sessionId); +        if (index == kNotFound) { +            ALOGD("Invalid sessionId"); +            return BAD_VALUE; +        } + +        // Properties used in mock test, set by mock plugin and verifed cts test app +        //   byte[] initData           -> mock-initdata +        //   string mimeType           -> mock-mimetype +        //   string licenseType        -> mock-licensetype +        //   string optionalParameters -> mock-optparams formatted as {key1,value1},{key2,value2} + +        mByteArrayProperties.add(String8("mock-initdata"), initData); +        mStringProperties.add(String8("mock-mimetype"), mimeType); + +        String8 licenseTypeStr; +        licenseTypeStr.appendFormat("%d", (int)licenseType); +        mStringProperties.add(String8("mock-licensetype"), licenseTypeStr); + +        String8 params; +        for (size_t i = 0; i < optionalParameters.size(); i++) { +            params.appendFormat("%s{%s,%s}", i ? "," : "", +                                optionalParameters.keyAt(i).string(), +                                optionalParameters.valueAt(i).string()); +        } +        mStringProperties.add(String8("mock-optparams"), params); + +        // Properties used in mock test, set by cts test app returned from mock plugin +        //   byte[] mock-request       -> request +        //   string mock-default-url   -> defaultUrl + +        index = mByteArrayProperties.indexOfKey(String8("mock-request")); +        if (index < 0) { +            ALOGD("Missing 'mock-request' parameter for mock"); +            return BAD_VALUE; +        } else { +            request = mByteArrayProperties.valueAt(index); +        } + +        index = mStringProperties.indexOfKey(String8("mock-defaultUrl")); +        if (index < 0) { +            ALOGD("Missing 'mock-defaultUrl' parameter for mock"); +            return BAD_VALUE; +        } else { +            defaultUrl = mStringProperties.valueAt(index); +        } +        return OK; +    } + +    status_t MockDrmPlugin::provideLicenseResponse(Vector<uint8_t> const &sessionId, +                                                   Vector<uint8_t> const &response) +    { +        Mutex::Autolock lock(mLock); +        ALOGD("MockDrmPlugin::provideLicenseResponse(sessionId=%s, response=%s)", +              vectorToString(sessionId).string(), vectorToString(response).string()); +        ssize_t index = findSession(sessionId); +        if (index == kNotFound) { +            ALOGD("Invalid sessionId"); +            return BAD_VALUE; +        } +        if (response.size() == 0) { +            return BAD_VALUE; +        } + +        // Properties used in mock test, set by mock plugin and verifed cts test app +        //   byte[] response            -> mock-response + +        mByteArrayProperties.add(String8("mock-response"), response); + +        return OK; +    } + +    status_t MockDrmPlugin::removeLicense(Vector<uint8_t> const &sessionId) +    { +        Mutex::Autolock lock(mLock); +        ALOGD("MockDrmPlugin::removeLicense(sessionId=%s)", +              vectorToString(sessionId).string()); +        ssize_t index = findSession(sessionId); +        if (index == kNotFound) { +            ALOGD("Invalid sessionId"); +            return BAD_VALUE; +        } + +        return OK; +    } + +    status_t MockDrmPlugin::queryLicenseStatus(Vector<uint8_t> const &sessionId, +                                               KeyedVector<String8, String8> &infoMap) const +    { +        ALOGD("MockDrmPlugin::queryLicenseStatus(sessionId=%s)", +              vectorToString(sessionId).string()); + +        ssize_t index = findSession(sessionId); +        if (index == kNotFound) { +            ALOGD("Invalid sessionId"); +            return BAD_VALUE; +        } + +        infoMap.add(String8("purchaseDuration"), String8("1000")); +        infoMap.add(String8("licenseDuration"), String8("100")); +        return OK; +    } + +    status_t MockDrmPlugin::getProvisionRequest(Vector<uint8_t> &request, +                                                String8 &defaultUrl) +    { +        Mutex::Autolock lock(mLock); +        ALOGD("MockDrmPlugin::getProvisionRequest()"); + +        // Properties used in mock test, set by cts test app returned from mock plugin +        //   byte[] mock-request       -> request +        //   string mock-default-url   -> defaultUrl + +        ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-request")); +        if (index < 0) { +            ALOGD("Missing 'mock-request' parameter for mock"); +            return BAD_VALUE; +        } else { +            request = mByteArrayProperties.valueAt(index); +        } + +        index = mStringProperties.indexOfKey(String8("mock-defaultUrl")); +        if (index < 0) { +            ALOGD("Missing 'mock-defaultUrl' parameter for mock"); +            return BAD_VALUE; +        } else { +            defaultUrl = mStringProperties.valueAt(index); +        } +        return OK; +    } + +    status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response) +    { +        Mutex::Autolock lock(mLock); +        ALOGD("MockDrmPlugin::provideProvisionResponse(%s)", +              vectorToString(response).string()); + +        // Properties used in mock test, set by mock plugin and verifed cts test app +        //   byte[] response            -> mock-response + +        mByteArrayProperties.add(String8("mock-response"), response); +        return OK; +    } + +    status_t MockDrmPlugin::getSecureStops(List<Vector<uint8_t> > &secureStops) +    { +        Mutex::Autolock lock(mLock); +        ALOGD("MockDrmPlugin::getSecureStops()"); +        const uint8_t ss1[] = {0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89}; +        const uint8_t ss2[] = {0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99}; + +        Vector<uint8_t> vec; +        vec.appendArray(ss1, sizeof(ss1)); +        secureStops.push_back(vec); + +        vec.clear(); +        vec.appendArray(ss2, sizeof(ss2)); +        secureStops.push_back(vec); +        return OK; +    } + +    status_t MockDrmPlugin::releaseSecureStops(Vector<uint8_t> const &ssRelease) +    { +        Mutex::Autolock lock(mLock); +        ALOGD("MockDrmPlugin::releaseSecureStops(%s)", +              vectorToString(ssRelease).string()); +        return OK; +    } + +    status_t MockDrmPlugin::getPropertyString(String8 const &name, String8 &value) const +    { +        ALOGD("MockDrmPlugin::getPropertyString(name=%s)", name.string()); +        ssize_t index = mStringProperties.indexOfKey(name); +        if (index < 0) { +            ALOGD("no property for '%s'", name.string()); +            return BAD_VALUE; +        } +        value = mStringProperties.valueAt(index); +        return OK; +    } + +    status_t MockDrmPlugin::getPropertyByteArray(String8 const &name, +                                                 Vector<uint8_t> &value) const +    { +        ALOGD("MockDrmPlugin::getPropertyByteArray(name=%s)", name.string()); +        ssize_t index = mByteArrayProperties.indexOfKey(name); +        if (index < 0) { +            ALOGD("no property for '%s'", name.string()); +            return BAD_VALUE; +        } +        value = mByteArrayProperties.valueAt(index); +        return OK; +    } + +    status_t MockDrmPlugin::setPropertyString(String8 const &name, +                                              String8 const &value) +    { +        Mutex::Autolock lock(mLock); +        ALOGD("MockDrmPlugin::setPropertyString(name=%s, value=%s)", +              name.string(), value.string()); +        mStringProperties.add(name, value); +        return OK; +    } + +    status_t MockDrmPlugin::setPropertyByteArray(String8 const &name, +                                                 Vector<uint8_t> const &value) +    { +        Mutex::Autolock lock(mLock); +        ALOGD("MockDrmPlugin::setPropertyByteArray(name=%s, value=%s)", +              name.string(), vectorToString(value).string()); +        mByteArrayProperties.add(name, value); +        return OK; +    } + +    ssize_t MockDrmPlugin::findSession(Vector<uint8_t> const &sessionId) const +    { +        ALOGD("findSession: nsessions=%d, size=%d", mSessions.size(), sessionId.size()); +        for (size_t i = 0; i < mSessions.size(); ++i) { +            if (memcmp(mSessions[i].array(), sessionId.array(), sessionId.size()) == 0) { +                return i; +            } +        } +        return kNotFound; +    } + +    // Conversion utilities +    String8 MockDrmPlugin::vectorToString(Vector<uint8_t> const &vector) const +    { +        return arrayToString(vector.array(), vector.size()); +    } + +    String8 MockDrmPlugin::arrayToString(uint8_t const *array, size_t len) const +    { +        String8 result("{ "); +        for (size_t i = 0; i < len; i++) { +            result.appendFormat("0x%02x ", array[i]); +        } +        result += "}"; +        return result; +    } + +    String8 MockDrmPlugin::stringMapToString(KeyedVector<String8, String8> map) const +    { +        String8 result("{ "); +        for (size_t i = 0; i < map.size(); i++) { +            result.appendFormat("%s{name=%s, value=%s}", i > 0 ? ", " : "", +                                map.keyAt(i).string(), map.valueAt(i).string()); +        } +        return result + " }"; +    } + +    bool operator<(Vector<uint8_t> const &lhs, Vector<uint8_t> const &rhs) { +        return lhs.size() < rhs.size() || (memcmp(lhs.array(), rhs.array(), lhs.size()) < 0); +    } + +    // +    // Crypto Plugin +    // + +    bool MockCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const +    { +        ALOGD("MockCryptoPlugin::requiresSecureDecoderComponent(mime=%s)", mime); +        return false; +    } + +    ssize_t +    MockCryptoPlugin::decrypt(bool secure, const uint8_t key[16], const uint8_t iv[16], +                              Mode mode, const void *srcPtr, const SubSample *subSamples, +                              size_t numSubSamples, void *dstPtr, AString *errorDetailMsg) +    { +        ALOGD("MockCryptoPlugin::decrypt(secure=%d, key=%s, iv=%s, mode=%d, src=%p, " +              "subSamples=%s, dst=%p)", +              (int)secure, +              arrayToString(key, sizeof(key)).string(), +              arrayToString(iv, sizeof(iv)).string(), +              (int)mode, srcPtr, +              subSamplesToString(subSamples, numSubSamples).string(), +              dstPtr); +        return OK; +    } + +    // Conversion utilities +    String8 MockCryptoPlugin::arrayToString(uint8_t const *array, size_t len) const +    { +        String8 result("{ "); +        for (size_t i = 0; i < len; i++) { +            result.appendFormat("0x%02x ", array[i]); +        } +        result += "}"; +        return result; +    } + +    String8 MockCryptoPlugin::subSamplesToString(SubSample const *subSamples, +                                                 size_t numSubSamples) const +    { +        String8 result; +        for (size_t i = 0; i < numSubSamples; i++) { +            result.appendFormat("[%d] {clear:%d, encrypted:%d} ", i, +                                subSamples[i].mNumBytesOfClearData, +                                subSamples[i].mNumBytesOfEncryptedData); +        } +        return result; +    } + +}; diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h new file mode 100644 index 0000000..d46a127 --- /dev/null +++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utils/Mutex.h> + +#include "drm/DrmAPI.h" +#include "hardware/CryptoAPI.h" + +extern "C" { +      android::DrmFactory *createDrmFactory(); +      android::CryptoFactory *createCryptoFactory(); +} + +namespace android { + +    class MockDrmFactory : public DrmFactory { +    public: +        MockDrmFactory() {} +        virtual ~MockDrmFactory() {} + +        bool isCryptoSchemeSupported(const uint8_t uuid[16]); +        status_t createDrmPlugin(const uint8_t uuid[16], DrmPlugin **plugin); +    }; + +    class MockCryptoFactory : public CryptoFactory { +    public: +        MockCryptoFactory() {} +        virtual ~MockCryptoFactory() {} + +        bool isCryptoSchemeSupported(const uint8_t uuid[16]) const; +        status_t createPlugin( +            const uint8_t uuid[16], const void *data, size_t size, +            CryptoPlugin **plugin); +    }; + + + +    class MockDrmPlugin : public DrmPlugin { +    public: +        MockDrmPlugin() {} +        virtual ~MockDrmPlugin() {} + +        // from DrmPlugin +        status_t openSession(Vector<uint8_t> &sessionId); +        status_t closeSession(Vector<uint8_t> const &sessionId); + +        status_t +            getLicenseRequest(Vector<uint8_t> const &sessionId, +                              Vector<uint8_t> const &initData, +                              String8 const &mimeType, LicenseType licenseType, +                              KeyedVector<String8, String8> const &optionalParameters, +                              Vector<uint8_t> &request, String8 &defaultUrl); + +        status_t provideLicenseResponse(Vector<uint8_t> const &sessionId, +                                                Vector<uint8_t> const &response); + +        status_t removeLicense(Vector<uint8_t> const &sessionId); + +        status_t +            queryLicenseStatus(Vector<uint8_t> const &sessionId, +                               KeyedVector<String8, String8> &infoMap) const; + +        status_t getProvisionRequest(Vector<uint8_t> &request, +                                             String8 &defaultUrl); + +        status_t provideProvisionResponse(Vector<uint8_t> const &response); + +        status_t getSecureStops(List<Vector<uint8_t> > &secureStops); +        status_t releaseSecureStops(Vector<uint8_t> const &ssRelease); + +        status_t getPropertyString(String8 const &name, String8 &value ) const; +        status_t getPropertyByteArray(String8 const &name, +                                              Vector<uint8_t> &value ) const; + +        status_t setPropertyString(String8 const &name, +                                   String8 const &value ); +        status_t setPropertyByteArray(String8 const &name, +                                      Vector<uint8_t> const &value ); + +    private: +        String8 vectorToString(Vector<uint8_t> const &vector) const; +        String8 arrayToString(uint8_t const *array, size_t len) const; +        String8 stringMapToString(KeyedVector<String8, String8> map) const; + +        SortedVector<Vector<uint8_t> > mSessions; + +        static const ssize_t kNotFound = -1; +        ssize_t findSession(Vector<uint8_t> const &sessionId) const; + +        Mutex mLock; +        KeyedVector<String8, String8> mStringProperties; +        KeyedVector<String8, Vector<uint8_t> > mByteArrayProperties; +    }; + + +    class MockCryptoPlugin : public CryptoPlugin { + +        bool requiresSecureDecoderComponent(const char *mime) const; + +        ssize_t decrypt(bool secure, +            const uint8_t key[16], const uint8_t iv[16], +            Mode mode, const void *srcPtr, +            const SubSample *subSamples, size_t numSubSamples, +            void *dstPtr, AString *errorDetailMsg); +    private: +        String8 subSamplesToString(CryptoPlugin::SubSample const *subSamples, size_t numSubSamples) const; +        String8 arrayToString(uint8_t const *array, size_t len) const; +    }; +}; diff --git a/include/media/IDrm.h b/include/media/IDrm.h new file mode 100644 index 0000000..38e2378 --- /dev/null +++ b/include/media/IDrm.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/IInterface.h> +#include <media/stagefright/foundation/ABase.h> +#include <media/drm/DrmAPI.h> + +#ifndef ANDROID_IDRM_H_ + +#define ANDROID_IDRM_H_ + +namespace android { + +struct AString; + +struct IDrm : public IInterface { +    DECLARE_META_INTERFACE(Drm); + +    virtual status_t initCheck() const = 0; + +    virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) = 0; + +    virtual status_t createPlugin(const uint8_t uuid[16]) = 0; + +    virtual status_t destroyPlugin() = 0; + +    virtual status_t openSession(Vector<uint8_t> &sessionId) = 0; + +    virtual status_t closeSession(Vector<uint8_t> const &sessionId) = 0; + +    virtual status_t +        getLicenseRequest(Vector<uint8_t> const &sessionId, +                          Vector<uint8_t> const &initData, +                          String8 const &mimeType, DrmPlugin::LicenseType licenseType, +                          KeyedVector<String8, String8> const &optionalParameters, +                          Vector<uint8_t> &request, String8 &defaultUrl) = 0; + +    virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId, +                                            Vector<uint8_t> const &response) = 0; + +    virtual status_t removeLicense(Vector<uint8_t> const &sessionId) = 0; + +    virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId, +                                        KeyedVector<String8, String8> &infoMap) const = 0; + +    virtual status_t getProvisionRequest(Vector<uint8_t> &request, +                                         String8 &defaulUrl) = 0; + +    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response) = 0; + +    virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops) = 0; + +    virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) = 0; + +    virtual status_t getPropertyString(String8 const &name, String8 &value ) const = 0; +    virtual status_t getPropertyByteArray(String8 const &name, +                                          Vector<uint8_t> &value ) const = 0; +    virtual status_t setPropertyString(String8 const &name, +                                       String8 const &value ) const = 0; +    virtual status_t setPropertyByteArray(String8 const &name, +                                          Vector<uint8_t> const &value ) const = 0; + +private: +    DISALLOW_EVIL_CONSTRUCTORS(IDrm); +}; + +struct BnDrm : public BnInterface<IDrm> { +    virtual status_t onTransact( +            uint32_t code, const Parcel &data, Parcel *reply, +            uint32_t flags = 0); +}; + +}  // namespace android + +#endif // ANDROID_IDRM_H_ + diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h index b29d3c7..182d563 100644 --- a/include/media/IMediaPlayerService.h +++ b/include/media/IMediaPlayerService.h @@ -32,6 +32,7 @@  namespace android {  struct ICrypto; +struct IDrm;  struct IHDCP;  class IMediaRecorder;  class IOMX; @@ -52,6 +53,7 @@ public:      virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) = 0;      virtual sp<IOMX>            getOMX() = 0;      virtual sp<ICrypto>         makeCrypto() = 0; +    virtual sp<IDrm>            makeDrm() = 0;      virtual sp<IHDCP>           makeHDCP(bool createEncryptionModule) = 0;      // Connects to a remote display. diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 6b48991..1ada9c3 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -19,6 +19,7 @@ LOCAL_SRC_FILES:= \      IAudioTrack.cpp \      IAudioRecord.cpp \      ICrypto.cpp \ +    IDrm.cpp \      IHDCP.cpp \      AudioRecord.cpp \      AudioSystem.cpp \ diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp new file mode 100644 index 0000000..3b13ec6 --- /dev/null +++ b/media/libmedia/IDrm.cpp @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "IDrm" +#include <utils/Log.h> + +#include <binder/Parcel.h> +#include <media/IDrm.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AString.h> + +namespace android { + +enum { +    INIT_CHECK = IBinder::FIRST_CALL_TRANSACTION, +    IS_CRYPTO_SUPPORTED, +    CREATE_PLUGIN, +    DESTROY_PLUGIN, +    OPEN_SESSION, +    CLOSE_SESSION, +    GET_LICENSE_REQUEST, +    PROVIDE_LICENSE_RESPONSE, +    REMOVE_LICENSE, +    QUERY_LICENSE_STATUS, +    GET_PROVISION_REQUEST, +    PROVIDE_PROVISION_RESPONSE, +    GET_SECURE_STOPS, +    RELEASE_SECURE_STOPS, +    GET_PROPERTY_STRING, +    GET_PROPERTY_BYTE_ARRAY, +    SET_PROPERTY_STRING, +    SET_PROPERTY_BYTE_ARRAY +}; + +struct BpDrm : public BpInterface<IDrm> { +    BpDrm(const sp<IBinder> &impl) +        : BpInterface<IDrm>(impl) { +    } + +    virtual status_t initCheck() const { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); +        remote()->transact(INIT_CHECK, data, &reply); + +        return reply.readInt32(); +    } + +    virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); +        data.write(uuid, 16); +        remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply); + +        return reply.readInt32() != 0; +    } + +    virtual status_t createPlugin(const uint8_t uuid[16]) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); +        data.write(uuid, 16); + +        remote()->transact(CREATE_PLUGIN, data, &reply); + +        return reply.readInt32(); +    } + +    virtual status_t destroyPlugin() { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); +        remote()->transact(DESTROY_PLUGIN, data, &reply); + +        return reply.readInt32(); +    } + +    virtual status_t openSession(Vector<uint8_t> &sessionId) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        remote()->transact(OPEN_SESSION, data, &reply); +        uint32_t size = reply.readInt32(); +        sessionId.insertAt((size_t)0, size); +        reply.read(sessionId.editArray(), size); + +        return reply.readInt32(); +    } + +    virtual status_t closeSession(Vector<uint8_t> const &sessionId) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeInt32(sessionId.size()); +        data.write(sessionId.array(), sessionId.size()); +        remote()->transact(CLOSE_SESSION, data, &reply); + +        return reply.readInt32(); +    } + +    virtual status_t +        getLicenseRequest(Vector<uint8_t> const &sessionId, +                          Vector<uint8_t> const &initData, +                          String8 const &mimeType, DrmPlugin::LicenseType licenseType, +                          KeyedVector<String8, String8> const &optionalParameters, +                          Vector<uint8_t> &request, String8 &defaultUrl) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeInt32(sessionId.size()); +        data.write(sessionId.array(), sessionId.size()); + +        data.writeInt32(initData.size()); +        data.write(initData.array(), initData.size()); + +        data.writeString8(mimeType); +        data.writeInt32((uint32_t)licenseType); + +        data.writeInt32(optionalParameters.size()); +        for (size_t i = 0; i < optionalParameters.size(); ++i) { +            data.writeString8(optionalParameters.keyAt(i)); +            data.writeString8(optionalParameters.valueAt(i)); +        } +        remote()->transact(GET_LICENSE_REQUEST, data, &reply); + +        uint32_t len = reply.readInt32(); +        request.insertAt((size_t)0, len); +        reply.read(request.editArray(), len); +        defaultUrl = reply.readString8(); + +        return reply.readInt32(); +    } + +    virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId, +                                            Vector<uint8_t> const &response) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeInt32(sessionId.size()); +        data.write(sessionId.array(), sessionId.size()); +        data.writeInt32(response.size()); +        data.write(response.array(), response.size()); +        remote()->transact(PROVIDE_LICENSE_RESPONSE, data, &reply); + +        return reply.readInt32(); +    } + +    virtual status_t removeLicense(Vector<uint8_t> const &sessionId) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeInt32(sessionId.size()); +        data.write(sessionId.array(), sessionId.size()); +        remote()->transact(REMOVE_LICENSE, data, &reply); + +        return reply.readInt32(); +    } + +    virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId, +                                        KeyedVector<String8, String8> &infoMap) const { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeInt32(sessionId.size()); +        data.write(sessionId.array(), sessionId.size()); + +        remote()->transact(QUERY_LICENSE_STATUS, data, &reply); + +        infoMap.clear(); +        size_t count = reply.readInt32(); +        for (size_t i = 0; i < count; i++) { +            String8 key = reply.readString8(); +            String8 value = reply.readString8(); +            infoMap.add(key, value); +        } +        return reply.readInt32(); +    } + +    virtual status_t getProvisionRequest(Vector<uint8_t> &request, +                                         String8 &defaultUrl) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        remote()->transact(GET_PROVISION_REQUEST, data, &reply); + +        uint32_t len = reply.readInt32(); +        request.insertAt((size_t)0, len); +        reply.read(request.editArray(), len); +        defaultUrl = reply.readString8(); + +        return reply.readInt32(); +    } + +    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeInt32(response.size()); +        data.write(response.array(), response.size()); +        remote()->transact(PROVIDE_PROVISION_RESPONSE, data, &reply); + +        return reply.readInt32(); +    } + +    virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        remote()->transact(GET_SECURE_STOPS, data, &reply); + +        secureStops.clear(); +        uint32_t count = reply.readInt32(); +        for (size_t i = 0; i < count; i++) { +            Vector<uint8_t> secureStop; +            uint32_t len = reply.readInt32(); +            secureStop.insertAt((size_t)0, len); +            reply.read(secureStop.editArray(), len); +            secureStops.push_back(secureStop); +        } +        return reply.readInt32(); +    } + +    virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeInt32(ssRelease.size()); +        data.write(ssRelease.array(), ssRelease.size()); +        remote()->transact(RELEASE_SECURE_STOPS, data, &reply); + +        return reply.readInt32(); +    } + +    virtual status_t getPropertyString(String8 const &name, String8 &value) const { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeString8(name); +        remote()->transact(GET_PROPERTY_STRING, data, &reply); + +        value = reply.readString8(); +        return reply.readInt32(); +    } + +    virtual status_t getPropertyByteArray(String8 const &name, Vector<uint8_t> &value) const { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeString8(name); +        remote()->transact(GET_PROPERTY_BYTE_ARRAY, data, &reply); + +        uint32_t len = reply.readInt32(); +        value.insertAt((size_t)0, len); +        reply.read(value.editArray(), len); + +        return reply.readInt32(); +    } + +    virtual status_t setPropertyString(String8 const &name, String8 const &value) const { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeString8(name); +        data.writeString8(value); +        remote()->transact(SET_PROPERTY_STRING, data, &reply); + +        return reply.readInt32(); +    } + +    virtual status_t setPropertyByteArray(String8 const &name, +                                          Vector<uint8_t> const &value) const { +        Parcel data, reply; +        data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + +        data.writeString8(name); +        data.writeInt32(value.size()); +        data.write(value.array(), value.size()); +        remote()->transact(SET_PROPERTY_BYTE_ARRAY, data, &reply); + +        return reply.readInt32(); +    } + + +private: +    DISALLOW_EVIL_CONSTRUCTORS(BpDrm); +}; + +IMPLEMENT_META_INTERFACE(Drm, "android.drm.IDrm"); + +//////////////////////////////////////////////////////////////////////////////// + +status_t BnDrm::onTransact( +    uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { +    switch (code) { +        case INIT_CHECK: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            reply->writeInt32(initCheck()); +            return OK; +        } + +        case IS_CRYPTO_SUPPORTED: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            uint8_t uuid[16]; +            data.read(uuid, sizeof(uuid)); +            reply->writeInt32(isCryptoSchemeSupported(uuid)); +            return OK; +        } + +        case CREATE_PLUGIN: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            uint8_t uuid[16]; +            data.read(uuid, sizeof(uuid)); +            reply->writeInt32(createPlugin(uuid)); +            return OK; +        } + +        case DESTROY_PLUGIN: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            reply->writeInt32(destroyPlugin()); +            return OK; +        } + +        case OPEN_SESSION: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            Vector<uint8_t> sessionId; +            status_t result = openSession(sessionId); +            reply->writeInt32(sessionId.size()); +            reply->write(sessionId.array(), sessionId.size()); +            reply->writeInt32(result); +            return OK; +        } + +        case CLOSE_SESSION: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            Vector<uint8_t> sessionId; +            uint32_t size = data.readInt32(); +            sessionId.insertAt((size_t)0, size); +            data.read(sessionId.editArray(), size); +            reply->writeInt32(closeSession(sessionId)); +            return OK; +        } + +        case GET_LICENSE_REQUEST: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            Vector<uint8_t> sessionId; +            uint32_t size = data.readInt32(); +            sessionId.insertAt((size_t)0, size); +            data.read(sessionId.editArray(), size); + +            Vector<uint8_t> initData; +            size = data.readInt32(); +            initData.insertAt((size_t)0, size); +            data.read(initData.editArray(), size); + +            String8 mimeType = data.readString8(); +            DrmPlugin::LicenseType licenseType = (DrmPlugin::LicenseType)data.readInt32(); + +            KeyedVector<String8, String8> optionalParameters; +            uint32_t count = data.readInt32(); +            for (size_t i = 0; i < count; ++i) { +                String8 key, value; +                key = data.readString8(); +                value = data.readString8(); +                optionalParameters.add(key, value); +            } + +            Vector<uint8_t> request; +            String8 defaultUrl; + +            status_t result = getLicenseRequest(sessionId, initData, +                                                mimeType, licenseType, +                                                optionalParameters, +                                                request, defaultUrl); +            reply->writeInt32(request.size()); +            reply->write(request.array(), request.size()); +            reply->writeString8(defaultUrl); +            reply->writeInt32(result); +            return OK; +        } + +        case PROVIDE_LICENSE_RESPONSE: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            Vector<uint8_t> sessionId; +            uint32_t size = data.readInt32(); +            sessionId.insertAt((size_t)0, size); +            data.read(sessionId.editArray(), size); +            Vector<uint8_t> response; +            size = data.readInt32(); +            response.insertAt((size_t)0, size); +            data.read(response.editArray(), size); + +            reply->writeInt32(provideLicenseResponse(sessionId, response)); +            return OK; +        } + +        case REMOVE_LICENSE: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            Vector<uint8_t> sessionId; +            uint32_t size = data.readInt32(); +            sessionId.insertAt((size_t)0, size); +            data.read(sessionId.editArray(), size); +            reply->writeInt32(removeLicense(sessionId)); +            return OK; +        } + +        case QUERY_LICENSE_STATUS: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            Vector<uint8_t> sessionId; +            uint32_t size = data.readInt32(); +            sessionId.insertAt((size_t)0, size); +            data.read(sessionId.editArray(), size); +            KeyedVector<String8, String8> infoMap; + +            status_t result = queryLicenseStatus(sessionId, infoMap); + +            size_t count = infoMap.size(); +            reply->writeInt32(count); +            for (size_t i = 0; i < count; ++i) { +                reply->writeString8(infoMap.keyAt(i)); +                reply->writeString8(infoMap.valueAt(i)); +            } +            reply->writeInt32(result); +            return OK; +        } + +        case GET_PROVISION_REQUEST: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            Vector<uint8_t> request; +            String8 defaultUrl; +            status_t result = getProvisionRequest(request, defaultUrl); +            reply->writeInt32(request.size()); +            reply->write(request.array(), request.size()); +            reply->writeString8(defaultUrl); +            reply->writeInt32(result); +            return OK; +        } + +        case PROVIDE_PROVISION_RESPONSE: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            Vector<uint8_t> response; +            uint32_t size = data.readInt32(); +            response.insertAt((size_t)0, size); +            data.read(response.editArray(), size); +            reply->writeInt32(provideProvisionResponse(response)); + +            return OK; +        } + +        case GET_SECURE_STOPS: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            List<Vector<uint8_t> > secureStops; +            status_t result = getSecureStops(secureStops); +            size_t count = secureStops.size(); +            reply->writeInt32(count); +            List<Vector<uint8_t> >::iterator iter = secureStops.begin(); +            while(iter != secureStops.end()) { +                size_t size = iter->size(); +                reply->writeInt32(size); +                reply->write(iter->array(), iter->size()); +            } +            reply->writeInt32(result); +            return OK; +        } + +        case RELEASE_SECURE_STOPS: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            Vector<uint8_t> ssRelease; +            uint32_t size = data.readInt32(); +            ssRelease.insertAt((size_t)0, size); +            data.read(ssRelease.editArray(), size); +            reply->writeInt32(releaseSecureStops(ssRelease)); +            return OK; +        } + +        case GET_PROPERTY_STRING: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            String8 name = data.readString8(); +            String8 value; +            status_t result = getPropertyString(name, value); +            reply->writeString8(value); +            reply->writeInt32(result); +            return OK; +        } + +        case GET_PROPERTY_BYTE_ARRAY: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            String8 name = data.readString8(); +            Vector<uint8_t> value; +            status_t result = getPropertyByteArray(name, value); +            reply->writeInt32(value.size()); +            reply->write(value.array(), value.size()); +            reply->writeInt32(result); +            return OK; +        } + +        case SET_PROPERTY_STRING: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            String8 name = data.readString8(); +            String8 value = data.readString8(); +            reply->writeInt32(setPropertyString(name, value)); +            return OK; +        } + +        case SET_PROPERTY_BYTE_ARRAY: +        { +            CHECK_INTERFACE(IDrm, data, reply); +            String8 name = data.readString8(); +            Vector<uint8_t> value; +            size_t count = data.readInt32(); +            value.insertAt((size_t)0, count); +            data.read(value.editArray(), count); +            reply->writeInt32(setPropertyByteArray(name, value)); +            return OK; +        } + +        default: +            return BBinder::onTransact(code, data, reply, flags); +    } +} + +}  // namespace android + diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index a95f4c9..e1ce5a9 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -21,6 +21,7 @@  #include <binder/Parcel.h>  #include <binder/IMemory.h>  #include <media/ICrypto.h> +#include <media/IDrm.h>  #include <media/IHDCP.h>  #include <media/IMediaPlayerService.h>  #include <media/IMediaRecorder.h> @@ -42,6 +43,7 @@ enum {      CREATE_METADATA_RETRIEVER,      GET_OMX,      MAKE_CRYPTO, +    MAKE_DRM,      MAKE_HDCP,      ADD_BATTERY_DATA,      PULL_BATTERY_DATA, @@ -123,6 +125,13 @@ public:          return interface_cast<ICrypto>(reply.readStrongBinder());      } +    virtual sp<IDrm> makeDrm() { +        Parcel data, reply; +        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); +        remote()->transact(MAKE_DRM, data, &reply); +        return interface_cast<IDrm>(reply.readStrongBinder()); +    } +      virtual sp<IHDCP> makeHDCP(bool createEncryptionModule) {          Parcel data, reply;          data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); @@ -225,6 +234,12 @@ status_t BnMediaPlayerService::onTransact(              reply->writeStrongBinder(crypto->asBinder());              return NO_ERROR;          } break; +        case MAKE_DRM: { +            CHECK_INTERFACE(IMediaPlayerService, data, reply); +            sp<IDrm> drm = makeDrm(); +            reply->writeStrongBinder(drm->asBinder()); +            return NO_ERROR; +        } break;          case MAKE_HDCP: {              CHECK_INTERFACE(IMediaPlayerService, data, reply);              bool createEncryptionModule = data.readInt32(); diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 48f48e4..2a6f3c7 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -9,6 +9,7 @@ include $(CLEAR_VARS)  LOCAL_SRC_FILES:=               \      ActivityManager.cpp         \      Crypto.cpp                  \ +    Drm.cpp                     \      HDCP.cpp                    \      MediaPlayerFactory.cpp      \      MediaPlayerService.cpp      \ @@ -17,6 +18,7 @@ LOCAL_SRC_FILES:=               \      MidiFile.cpp                \      MidiMetadataRetriever.cpp   \      RemoteDisplay.cpp           \ +    SharedLibrary.cpp           \      StagefrightPlayer.cpp       \      StagefrightRecorder.cpp     \      TestPlayerStub.cpp          \ diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp new file mode 100644 index 0000000..6ac7530 --- /dev/null +++ b/media/libmediaplayerservice/Drm.cpp @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Drm" +#include <utils/Log.h> + +#include <dirent.h> +#include <dlfcn.h> + +#include "Drm.h" + +#include <media/drm/DrmAPI.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AString.h> +#include <media/stagefright/foundation/hexdump.h> +#include <media/stagefright/MediaErrors.h> + +namespace android { + +KeyedVector<Vector<uint8_t>, String8> Drm::mUUIDToLibraryPathMap; +KeyedVector<String8, wp<SharedLibrary> > Drm::mLibraryPathToOpenLibraryMap; +Mutex Drm::mMapLock; + +static bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs) { +    if (lhs.size() < rhs.size()) { +        return true; +    } else if (lhs.size() > rhs.size()) { +        return false; +    } + +    return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0; +} + +Drm::Drm() +    : mInitCheck(NO_INIT), +      mFactory(NULL), +      mPlugin(NULL) { +} + +Drm::~Drm() { +    delete mPlugin; +    mPlugin = NULL; +    closeFactory(); +} + +void Drm::closeFactory() { +    delete mFactory; +    mFactory = NULL; +    mLibrary.clear(); +} + +status_t Drm::initCheck() const { +    return mInitCheck; +} + + +/* + * Search the plugins directory for a plugin that supports the scheme + * specified by uuid + * + * If found: + *    mLibrary holds a strong pointer to the dlopen'd library + *    mFactory is set to the library's factory method + *    mInitCheck is set to OK + * + * If not found: + *    mLibrary is cleared and mFactory are set to NULL + *    mInitCheck is set to an error (!OK) + */ +void Drm::findFactoryForScheme(const uint8_t uuid[16]) { + +    closeFactory(); + +    // lock static maps +    Mutex::Autolock autoLock(mMapLock); + +    // first check cache +    Vector<uint8_t> uuidVector; +    uuidVector.appendArray(uuid, sizeof(uuid)); +    ssize_t index = mUUIDToLibraryPathMap.indexOfKey(uuidVector); +    if (index >= 0) { +        if (loadLibraryForScheme(mUUIDToLibraryPathMap[index], uuid)) { +            mInitCheck = OK; +            return; +        } else { +            ALOGE("Failed to load from cached library path!"); +            mInitCheck = ERROR_UNSUPPORTED; +            return; +        } +    } + +    // no luck, have to search +    String8 dirPath("/vendor/lib/mediadrm"); +    DIR* pDir = opendir(dirPath.string()); + +    if (pDir == NULL) { +        mInitCheck = ERROR_UNSUPPORTED; +        ALOGE("Failed to open plugin directory %s", dirPath.string()); +        return; +    } + + +    struct dirent* pEntry; +    while ((pEntry = readdir(pDir))) { + +        String8 pluginPath = dirPath + "/" + pEntry->d_name; + +        if (pluginPath.getPathExtension() == ".so") { + +            if (loadLibraryForScheme(pluginPath, uuid)) { +                mUUIDToLibraryPathMap.add(uuidVector, pluginPath); +                mInitCheck = OK; +                closedir(pDir); +                return; +            } +        } +    } + +    closedir(pDir); + +    ALOGE("Failed to find drm plugin"); +    mInitCheck = ERROR_UNSUPPORTED; +} + +bool Drm::loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]) { + +    // get strong pointer to open shared library +    ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path); +    if (index >= 0) { +        mLibrary = mLibraryPathToOpenLibraryMap[index].promote(); +    } else { +        index = mLibraryPathToOpenLibraryMap.add(path, NULL); +    } + +    if (!mLibrary.get()) { +        mLibrary = new SharedLibrary(path); +        if (!*mLibrary) { +            return false; +        } + +        mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary); +    } + +    typedef DrmFactory *(*CreateDrmFactoryFunc)(); + +    CreateDrmFactoryFunc createDrmFactory = +        (CreateDrmFactoryFunc)mLibrary->lookup("createDrmFactory"); + +    if (createDrmFactory == NULL || +        (mFactory = createDrmFactory()) == NULL || +        !mFactory->isCryptoSchemeSupported(uuid)) { +        closeFactory(); +        return false; +    } +    return true; +} + +bool Drm::isCryptoSchemeSupported(const uint8_t uuid[16]) { +    Mutex::Autolock autoLock(mLock); + +    if (mFactory && mFactory->isCryptoSchemeSupported(uuid)) { +        return true; +    } + +    findFactoryForScheme(uuid); +    return (mInitCheck == OK); +} + +status_t Drm::createPlugin(const uint8_t uuid[16]) { +    Mutex::Autolock autoLock(mLock); + +    if (mPlugin != NULL) { +        return -EINVAL; +    } + +    if (!mFactory || !mFactory->isCryptoSchemeSupported(uuid)) { +        findFactoryForScheme(uuid); +    } + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    return mFactory->createDrmPlugin(uuid, &mPlugin); +} + +status_t Drm::destroyPlugin() { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    delete mPlugin; +    mPlugin = NULL; + +    return OK; +} + +status_t Drm::openSession(Vector<uint8_t> &sessionId) { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->openSession(sessionId); +} + +status_t Drm::closeSession(Vector<uint8_t> const &sessionId) { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->closeSession(sessionId); +} + +status_t Drm::getLicenseRequest(Vector<uint8_t> const &sessionId, +                                Vector<uint8_t> const &initData, +                                String8 const &mimeType, DrmPlugin::LicenseType licenseType, +                                KeyedVector<String8, String8> const &optionalParameters, +                                Vector<uint8_t> &request, String8 &defaultUrl) { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->getLicenseRequest(sessionId, initData, mimeType, licenseType, +                                      optionalParameters, request, defaultUrl); +} + +status_t Drm::provideLicenseResponse(Vector<uint8_t> const &sessionId, +                                     Vector<uint8_t> const &response) { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->provideLicenseResponse(sessionId, response); +} + +status_t Drm::removeLicense(Vector<uint8_t> const &sessionId) { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->removeLicense(sessionId); +} + +status_t Drm::queryLicenseStatus(Vector<uint8_t> const &sessionId, +                                 KeyedVector<String8, String8> &infoMap) const { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->queryLicenseStatus(sessionId, infoMap); +} + +status_t Drm::getProvisionRequest(Vector<uint8_t> &request, String8 &defaultUrl) { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->getProvisionRequest(request, defaultUrl); +} + +status_t Drm::provideProvisionResponse(Vector<uint8_t> const &response) { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->provideProvisionResponse(response); +} + + +status_t Drm::getSecureStops(List<Vector<uint8_t> > &secureStops) { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->getSecureStops(secureStops); +} + +status_t Drm::releaseSecureStops(Vector<uint8_t> const &ssRelease) { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->releaseSecureStops(ssRelease); +} + +status_t Drm::getPropertyString(String8 const &name, String8 &value ) const { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->getPropertyString(name, value); +} + +status_t Drm::getPropertyByteArray(String8 const &name, Vector<uint8_t> &value ) const { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->getPropertyByteArray(name, value); +} + +status_t Drm::setPropertyString(String8 const &name, String8 const &value ) const { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->setPropertyString(name, value); +} + +status_t Drm::setPropertyByteArray(String8 const &name, +                                   Vector<uint8_t> const &value ) const { +    Mutex::Autolock autoLock(mLock); + +    if (mInitCheck != OK) { +        return mInitCheck; +    } + +    if (mPlugin == NULL) { +        return -EINVAL; +    } + +    return mPlugin->setPropertyByteArray(name, value); +} + +}  // namespace android diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h new file mode 100644 index 0000000..1b10958 --- /dev/null +++ b/media/libmediaplayerservice/Drm.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DRM_H_ + +#define DRM_H_ + +#include "SharedLibrary.h" + +#include <media/IDrm.h> +#include <utils/threads.h> + +namespace android { + +struct DrmFactory; +struct DrmPlugin; + +struct Drm : public BnDrm { +    Drm(); +    virtual ~Drm(); + +    virtual status_t initCheck() const; + +    virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]); + +    virtual status_t createPlugin(const uint8_t uuid[16]); + +    virtual status_t destroyPlugin(); + +    virtual status_t openSession(Vector<uint8_t> &sessionId); + +    virtual status_t closeSession(Vector<uint8_t> const &sessionId); + +    virtual status_t +        getLicenseRequest(Vector<uint8_t> const &sessionId, +                          Vector<uint8_t> const &initData, +                          String8 const &mimeType, DrmPlugin::LicenseType licenseType, +                          KeyedVector<String8, String8> const &optionalParameters, +                          Vector<uint8_t> &request, String8 &defaultUrl); + +    virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId, +                                            Vector<uint8_t> const &response); + +    virtual status_t removeLicense(Vector<uint8_t> const &sessionId); + +    virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId, +                                        KeyedVector<String8, String8> &infoMap) const; + +    virtual status_t getProvisionRequest(Vector<uint8_t> &request, +                                         String8 &defaulUrl); + +    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response); + +    virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops); + +    virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease); + +    virtual status_t getPropertyString(String8 const &name, String8 &value ) const; +    virtual status_t getPropertyByteArray(String8 const &name, +                                          Vector<uint8_t> &value ) const; +    virtual status_t setPropertyString(String8 const &name, String8 const &value ) const; +    virtual status_t setPropertyByteArray(String8 const &name, +                                          Vector<uint8_t> const &value ) const; + +private: +    mutable Mutex mLock; + +    status_t mInitCheck; +    sp<SharedLibrary> mLibrary; +    DrmFactory *mFactory; +    DrmPlugin *mPlugin; + +    static KeyedVector<Vector<uint8_t>, String8> mUUIDToLibraryPathMap; +    static KeyedVector<String8, wp<SharedLibrary> > mLibraryPathToOpenLibraryMap; +    static Mutex mMapLock; + +    void findFactoryForScheme(const uint8_t uuid[16]); +    bool loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]); +    void closeFactory(); + + +    DISALLOW_EVIL_CONSTRUCTORS(Drm); +}; + +}  // namespace android + +#endif  // CRYPTO_H_ diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 16f1317..ec6ace1 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -72,6 +72,7 @@  #include <OMX.h>  #include "Crypto.h" +#include "Drm.h"  #include "HDCP.h"  #include "RemoteDisplay.h" @@ -285,6 +286,10 @@ sp<ICrypto> MediaPlayerService::makeCrypto() {      return new Crypto;  } +sp<IDrm> MediaPlayerService::makeDrm() { +    return new Drm; +} +  sp<IHDCP> MediaPlayerService::makeHDCP(bool createEncryptionModule) {      return new HDCP(createEncryptionModule);  } diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 2d2a09d..82dc29b 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -249,6 +249,7 @@ public:      virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat);      virtual sp<IOMX>            getOMX();      virtual sp<ICrypto>         makeCrypto(); +    virtual sp<IDrm>            makeDrm();      virtual sp<IHDCP>           makeHDCP(bool createEncryptionModule);      virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client, diff --git a/media/libmediaplayerservice/SharedLibrary.cpp b/media/libmediaplayerservice/SharedLibrary.cpp new file mode 100644 index 0000000..178e15d --- /dev/null +++ b/media/libmediaplayerservice/SharedLibrary.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Drm" +#include <utils/Log.h> +#include <media/stagefright/foundation/ADebug.h> + +#include <dlfcn.h> + +#include "SharedLibrary.h" + +namespace android { + +    SharedLibrary::SharedLibrary(const String8 &path) { +        mLibHandle = dlopen(path.string(), RTLD_NOW); +    } + +    SharedLibrary::~SharedLibrary() { +        if (mLibHandle != NULL) { +            dlclose(mLibHandle); +            mLibHandle = NULL; +        } +    } + +    bool SharedLibrary::operator!() const { +        return mLibHandle == NULL; +    } + +    void *SharedLibrary::lookup(const char *symbol) const { +        if (!mLibHandle) { +            return NULL; +        } +        return dlsym(mLibHandle, symbol); +    } +}; diff --git a/media/libmediaplayerservice/SharedLibrary.h b/media/libmediaplayerservice/SharedLibrary.h new file mode 100644 index 0000000..5353642 --- /dev/null +++ b/media/libmediaplayerservice/SharedLibrary.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SHARED_LIBRARY_H_ +#define SHARED_LIBRARY_H_ + +#include <utils/RefBase.h> +#include <utils/String8.h> +#include <media/stagefright/foundation/ABase.h> + +namespace android { +    class SharedLibrary : public RefBase { +    public: +        SharedLibrary(const String8 &path); +        ~SharedLibrary(); + +        bool operator!() const; +        void *lookup(const char *symbol) const; + +    private: +        void *mLibHandle; +        DISALLOW_EVIL_CONSTRUCTORS(SharedLibrary); +    }; +}; + +#endif // SHARED_LIBRARY_H_  | 
