From 441a78d5e224e0d67f9b52fa9adc795c6944159b Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Fri, 8 Feb 2013 10:18:35 -0800 Subject: Implementing MediaDrm APIs Change-Id: I9ff8eeb7d0c383b5c0c68cd54eb54ce7d2d22fe6 --- drm/mediadrm/plugins/mock/Android.mk | 38 ++ drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp | 417 ++++++++++++++++ drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h | 122 +++++ include/media/IDrm.h | 89 ++++ include/media/IMediaPlayerService.h | 2 + media/libmedia/Android.mk | 1 + media/libmedia/IDrm.cpp | 551 +++++++++++++++++++++ media/libmedia/IMediaPlayerService.cpp | 15 + media/libmediaplayerservice/Android.mk | 2 + media/libmediaplayerservice/Drm.cpp | 423 ++++++++++++++++ media/libmediaplayerservice/Drm.h | 100 ++++ media/libmediaplayerservice/MediaPlayerService.cpp | 5 + media/libmediaplayerservice/MediaPlayerService.h | 1 + media/libmediaplayerservice/SharedLibrary.cpp | 49 ++ media/libmediaplayerservice/SharedLibrary.h | 39 ++ 15 files changed, 1854 insertions(+) create mode 100644 drm/mediadrm/plugins/mock/Android.mk create mode 100644 drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp create mode 100644 drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h create mode 100644 include/media/IDrm.h create mode 100644 media/libmedia/IDrm.cpp create mode 100644 media/libmediaplayerservice/Drm.cpp create mode 100644 media/libmediaplayerservice/Drm.h create mode 100644 media/libmediaplayerservice/SharedLibrary.cpp create mode 100644 media/libmediaplayerservice/SharedLibrary.h 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 + + +#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 &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 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 const &sessionId, + Vector const &initData, + String8 const &mimeType, LicenseType licenseType, + KeyedVector const &optionalParameters, + Vector &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 const &sessionId, + Vector 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 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 const &sessionId, + KeyedVector &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 &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 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 > &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 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 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 &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 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 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 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 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 const &lhs, Vector 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 + +#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 &sessionId); + status_t closeSession(Vector const &sessionId); + + status_t + getLicenseRequest(Vector const &sessionId, + Vector const &initData, + String8 const &mimeType, LicenseType licenseType, + KeyedVector const &optionalParameters, + Vector &request, String8 &defaultUrl); + + status_t provideLicenseResponse(Vector const &sessionId, + Vector const &response); + + status_t removeLicense(Vector const &sessionId); + + status_t + queryLicenseStatus(Vector const &sessionId, + KeyedVector &infoMap) const; + + status_t getProvisionRequest(Vector &request, + String8 &defaultUrl); + + status_t provideProvisionResponse(Vector const &response); + + status_t getSecureStops(List > &secureStops); + status_t releaseSecureStops(Vector const &ssRelease); + + status_t getPropertyString(String8 const &name, String8 &value ) const; + status_t getPropertyByteArray(String8 const &name, + Vector &value ) const; + + status_t setPropertyString(String8 const &name, + String8 const &value ); + status_t setPropertyByteArray(String8 const &name, + Vector const &value ); + + private: + String8 vectorToString(Vector const &vector) const; + String8 arrayToString(uint8_t const *array, size_t len) const; + String8 stringMapToString(KeyedVector map) const; + + SortedVector > mSessions; + + static const ssize_t kNotFound = -1; + ssize_t findSession(Vector const &sessionId) const; + + Mutex mLock; + KeyedVector mStringProperties; + KeyedVector > 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 +#include +#include + +#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 &sessionId) = 0; + + virtual status_t closeSession(Vector const &sessionId) = 0; + + virtual status_t + getLicenseRequest(Vector const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::LicenseType licenseType, + KeyedVector const &optionalParameters, + Vector &request, String8 &defaultUrl) = 0; + + virtual status_t provideLicenseResponse(Vector const &sessionId, + Vector const &response) = 0; + + virtual status_t removeLicense(Vector const &sessionId) = 0; + + virtual status_t queryLicenseStatus(Vector const &sessionId, + KeyedVector &infoMap) const = 0; + + virtual status_t getProvisionRequest(Vector &request, + String8 &defaulUrl) = 0; + + virtual status_t provideProvisionResponse(Vector const &response) = 0; + + virtual status_t getSecureStops(List > &secureStops) = 0; + + virtual status_t releaseSecureStops(Vector const &ssRelease) = 0; + + virtual status_t getPropertyString(String8 const &name, String8 &value ) const = 0; + virtual status_t getPropertyByteArray(String8 const &name, + Vector &value ) const = 0; + virtual status_t setPropertyString(String8 const &name, + String8 const &value ) const = 0; + virtual status_t setPropertyByteArray(String8 const &name, + Vector const &value ) const = 0; + +private: + DISALLOW_EVIL_CONSTRUCTORS(IDrm); +}; + +struct BnDrm : public BnInterface { + 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 decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) = 0; virtual sp getOMX() = 0; virtual sp makeCrypto() = 0; + virtual sp makeDrm() = 0; virtual sp 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 + +#include +#include +#include +#include +#include + +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 { + BpDrm(const sp &impl) + : BpInterface(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 &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 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 const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::LicenseType licenseType, + KeyedVector const &optionalParameters, + Vector &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 const &sessionId, + Vector 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 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 const &sessionId, + KeyedVector &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 &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 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 > &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 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 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 &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 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 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 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 sessionId; + uint32_t size = data.readInt32(); + sessionId.insertAt((size_t)0, size); + data.read(sessionId.editArray(), size); + + Vector 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 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 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 sessionId; + uint32_t size = data.readInt32(); + sessionId.insertAt((size_t)0, size); + data.read(sessionId.editArray(), size); + Vector 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 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 sessionId; + uint32_t size = data.readInt32(); + sessionId.insertAt((size_t)0, size); + data.read(sessionId.editArray(), size); + KeyedVector 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 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 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 > secureStops; + status_t result = getSecureStops(secureStops); + size_t count = secureStops.size(); + reply->writeInt32(count); + List >::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 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 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 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 #include #include +#include #include #include #include @@ -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(reply.readStrongBinder()); } + virtual sp makeDrm() { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + remote()->transact(MAKE_DRM, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + virtual sp 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 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 + +#include +#include + +#include "Drm.h" + +#include +#include +#include +#include +#include + +namespace android { + +KeyedVector, String8> Drm::mUUIDToLibraryPathMap; +KeyedVector > Drm::mLibraryPathToOpenLibraryMap; +Mutex Drm::mMapLock; + +static bool operator<(const Vector &lhs, const Vector &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 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 &sessionId) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->openSession(sessionId); +} + +status_t Drm::closeSession(Vector 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 const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::LicenseType licenseType, + KeyedVector const &optionalParameters, + Vector &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 const &sessionId, + Vector 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 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 const &sessionId, + KeyedVector &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 &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 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 > &secureStops) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->getSecureStops(secureStops); +} + +status_t Drm::releaseSecureStops(Vector 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 &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 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 +#include + +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 &sessionId); + + virtual status_t closeSession(Vector const &sessionId); + + virtual status_t + getLicenseRequest(Vector const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::LicenseType licenseType, + KeyedVector const &optionalParameters, + Vector &request, String8 &defaultUrl); + + virtual status_t provideLicenseResponse(Vector const &sessionId, + Vector const &response); + + virtual status_t removeLicense(Vector const &sessionId); + + virtual status_t queryLicenseStatus(Vector const &sessionId, + KeyedVector &infoMap) const; + + virtual status_t getProvisionRequest(Vector &request, + String8 &defaulUrl); + + virtual status_t provideProvisionResponse(Vector const &response); + + virtual status_t getSecureStops(List > &secureStops); + + virtual status_t releaseSecureStops(Vector const &ssRelease); + + virtual status_t getPropertyString(String8 const &name, String8 &value ) const; + virtual status_t getPropertyByteArray(String8 const &name, + Vector &value ) const; + virtual status_t setPropertyString(String8 const &name, String8 const &value ) const; + virtual status_t setPropertyByteArray(String8 const &name, + Vector const &value ) const; + +private: + mutable Mutex mLock; + + status_t mInitCheck; + sp mLibrary; + DrmFactory *mFactory; + DrmPlugin *mPlugin; + + static KeyedVector, String8> mUUIDToLibraryPathMap; + static KeyedVector > 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 #include "Crypto.h" +#include "Drm.h" #include "HDCP.h" #include "RemoteDisplay.h" @@ -285,6 +286,10 @@ sp MediaPlayerService::makeCrypto() { return new Crypto; } +sp MediaPlayerService::makeDrm() { + return new Drm; +} + sp 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 decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat); virtual sp getOMX(); virtual sp makeCrypto(); + virtual sp makeDrm(); virtual sp makeHDCP(bool createEncryptionModule); virtual sp listenForRemoteDisplay(const sp& 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 +#include + +#include + +#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 +#include +#include + +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_ -- cgit v1.1