diff options
Diffstat (limited to 'drm')
84 files changed, 15722 insertions, 0 deletions
diff --git a/drm/common/Android.mk b/drm/common/Android.mk new file mode 100644 index 0000000..c79a91a --- /dev/null +++ b/drm/common/Android.mk @@ -0,0 +1,44 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + DrmConstraints.cpp \ + DrmMetadata.cpp \ + DrmConvertedStatus.cpp \ + DrmEngineBase.cpp \ + DrmInfo.cpp \ + DrmInfoRequest.cpp \ + DrmInfoStatus.cpp \ + DrmRights.cpp \ + DrmSupportInfo.cpp \ + IDrmIOService.cpp \ + IDrmManagerService.cpp \ + IDrmServiceListener.cpp \ + DrmInfoEvent.cpp \ + ReadWriteUtils.cpp + +LOCAL_C_INCLUDES := \ + $(TOP)/frameworks/base/include \ + $(TOP)/frameworks/base/drm/libdrmframework/include \ + $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include + +LOCAL_MODULE:= libdrmframeworkcommon + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) diff --git a/drm/common/DrmConstraints.cpp b/drm/common/DrmConstraints.cpp new file mode 100644 index 0000000..4a4d798 --- /dev/null +++ b/drm/common/DrmConstraints.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <drm/DrmConstraints.h> + +using namespace android; + +const String8 DrmConstraints::MAX_REPEAT_COUNT("max_repeat_count"); +const String8 DrmConstraints::REMAINING_REPEAT_COUNT("remaining_repeat_count"); +const String8 DrmConstraints::LICENSE_START_TIME("license_start_time"); +const String8 DrmConstraints::LICENSE_EXPIRY_TIME("license_expiry_time"); +const String8 DrmConstraints::LICENSE_AVAILABLE_TIME("license_available_time"); +const String8 DrmConstraints::EXTENDED_METADATA("extended_metadata"); + +int DrmConstraints::getCount(void) const { + return mConstraintMap.size(); +} + +status_t DrmConstraints::put(const String8* key, const char* value) { + int length = strlen(value); + char* charValue = new char[length + 1]; + if (NULL != charValue) { + strncpy(charValue, value, length); + charValue[length] = '\0'; + mConstraintMap.add(*key, charValue); + } + return DRM_NO_ERROR; +} + +String8 DrmConstraints::get(const String8& key) const { + if (NULL != getValue(&key)) { + return String8(getValue(&key)); + } + return String8(""); +} + +const char* DrmConstraints::getValue(const String8* key) const { + if (NAME_NOT_FOUND != mConstraintMap.indexOfKey(*key)) { + return mConstraintMap.valueFor(*key); + } + return NULL; +} + +const char* DrmConstraints::getAsByteArray(const String8* key) const { + return getValue(key); +} + +bool DrmConstraints::KeyIterator::hasNext() { + return mIndex < mDrmConstraints->mConstraintMap.size(); +} + +const String8& DrmConstraints::KeyIterator::next() { + const String8& key = mDrmConstraints->mConstraintMap.keyAt(mIndex); + mIndex++; + return key; +} + +DrmConstraints::KeyIterator DrmConstraints::keyIterator() { + return KeyIterator(this); +} + +DrmConstraints::KeyIterator::KeyIterator(const DrmConstraints::KeyIterator& keyIterator) + : mDrmConstraints(keyIterator.mDrmConstraints), + mIndex(keyIterator.mIndex) { +} + +DrmConstraints::KeyIterator& DrmConstraints::KeyIterator::operator=( + const DrmConstraints::KeyIterator& keyIterator) { + mDrmConstraints = keyIterator.mDrmConstraints; + mIndex = keyIterator.mIndex; + return *this; +} + + +DrmConstraints::Iterator DrmConstraints::iterator() { + return Iterator(this); +} + +DrmConstraints::Iterator::Iterator(const DrmConstraints::Iterator& iterator) : + mDrmConstraints(iterator.mDrmConstraints), + mIndex(iterator.mIndex) { +} + +DrmConstraints::Iterator& DrmConstraints::Iterator::operator=( + const DrmConstraints::Iterator& iterator) { + mDrmConstraints = iterator.mDrmConstraints; + mIndex = iterator.mIndex; + return *this; +} + +bool DrmConstraints::Iterator::hasNext() { + return mIndex < mDrmConstraints->mConstraintMap.size(); +} + +String8 DrmConstraints::Iterator::next() { + String8 value = String8(mDrmConstraints->mConstraintMap.editValueAt(mIndex)); + mIndex++; + return value; +} + diff --git a/drm/common/DrmConvertedStatus.cpp b/drm/common/DrmConvertedStatus.cpp new file mode 100644 index 0000000..5d035f5 --- /dev/null +++ b/drm/common/DrmConvertedStatus.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <drm/DrmConvertedStatus.h> + +using namespace android; + +DrmConvertedStatus::DrmConvertedStatus( + int _statusCode, const DrmBuffer* _convertedData, int _offset) : + statusCode(_statusCode), + convertedData(_convertedData), + offset(_offset) { + +} + diff --git a/drm/common/DrmEngineBase.cpp b/drm/common/DrmEngineBase.cpp new file mode 100644 index 0000000..ac360eb --- /dev/null +++ b/drm/common/DrmEngineBase.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DrmEngineBase.h" + +using namespace android; + +DrmEngineBase::DrmEngineBase() { + +} + +DrmEngineBase::~DrmEngineBase() { + +} + +DrmConstraints* DrmEngineBase::getConstraints( + int uniqueId, const String8* path, int action) { + return onGetConstraints(uniqueId, path, action); +} + +DrmMetadata* DrmEngineBase::getMetadata(int uniqueId, const String8* path) { + return onGetMetadata(uniqueId, path); +} + +status_t DrmEngineBase::initialize(int uniqueId) { + return onInitialize(uniqueId); +} + +status_t DrmEngineBase::setOnInfoListener( + int uniqueId, const IDrmEngine::OnInfoListener* infoListener) { + return onSetOnInfoListener(uniqueId, infoListener); +} + +status_t DrmEngineBase::terminate(int uniqueId) { + return onTerminate(uniqueId); +} + +bool DrmEngineBase::canHandle(int uniqueId, const String8& path) { + return onCanHandle(uniqueId, path); +} + +DrmInfoStatus* DrmEngineBase::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) { + return onProcessDrmInfo(uniqueId, drmInfo); +} + +status_t DrmEngineBase::saveRights( + int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + return onSaveRights(uniqueId, drmRights, rightsPath, contentPath); +} + +DrmInfo* DrmEngineBase::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) { + return onAcquireDrmInfo(uniqueId, drmInfoRequest); +} + +String8 DrmEngineBase::getOriginalMimeType(int uniqueId, const String8& path) { + return onGetOriginalMimeType(uniqueId, path); +} + +int DrmEngineBase::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) { + return onGetDrmObjectType(uniqueId, path, mimeType); +} + +int DrmEngineBase::checkRightsStatus(int uniqueId, const String8& path, int action) { + return onCheckRightsStatus(uniqueId, path, action); +} + +status_t DrmEngineBase::consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) { + return onConsumeRights(uniqueId, decryptHandle, action, reserve); +} + +status_t DrmEngineBase::setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) { + return onSetPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position); +} + +bool DrmEngineBase::validateAction( + int uniqueId, const String8& path, + int action, const ActionDescription& description) { + return onValidateAction(uniqueId, path, action, description); +} + +status_t DrmEngineBase::removeRights(int uniqueId, const String8& path) { + return onRemoveRights(uniqueId, path); +} + +status_t DrmEngineBase::removeAllRights(int uniqueId) { + return onRemoveAllRights(uniqueId); +} + +status_t DrmEngineBase::openConvertSession(int uniqueId, int convertId) { + return onOpenConvertSession(uniqueId, convertId); +} + +DrmConvertedStatus* DrmEngineBase::convertData( + int uniqueId, int convertId, const DrmBuffer* inputData) { + return onConvertData(uniqueId, convertId, inputData); +} + +DrmConvertedStatus* DrmEngineBase::closeConvertSession(int uniqueId, int convertId) { + return onCloseConvertSession(uniqueId, convertId); +} + +DrmSupportInfo* DrmEngineBase::getSupportInfo(int uniqueId) { + return onGetSupportInfo(uniqueId); +} + +status_t DrmEngineBase::openDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) { + return onOpenDecryptSession(uniqueId, decryptHandle, fd, offset, length); +} + +status_t DrmEngineBase::openDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, const char* uri) { + return onOpenDecryptSession(uniqueId, decryptHandle, uri); +} + +status_t DrmEngineBase::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + return onCloseDecryptSession(uniqueId, decryptHandle); +} + +status_t DrmEngineBase::initializeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) { + return onInitializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo); +} + +status_t DrmEngineBase::decrypt( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) { + return onDecrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV); +} + +status_t DrmEngineBase::finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + return onFinalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId); +} + +ssize_t DrmEngineBase::pread( + int uniqueId, DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) { + return onPread(uniqueId, decryptHandle, buffer, numBytes, offset); +} + diff --git a/drm/common/DrmInfo.cpp b/drm/common/DrmInfo.cpp new file mode 100644 index 0000000..ddcab33 --- /dev/null +++ b/drm/common/DrmInfo.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <drm/DrmInfo.h> + +using namespace android; + +DrmInfo::DrmInfo(int infoType, const DrmBuffer& drmBuffer, const String8& mimeType) : + mInfoType(infoType), + mData(drmBuffer), + mMimeType(mimeType) { + +} + +int DrmInfo::getInfoType(void) const { + return mInfoType; +} + +String8 DrmInfo::getMimeType(void) const { + return mMimeType; +} + +const DrmBuffer& DrmInfo::getData(void) const { + return mData; +} + +int DrmInfo::getCount(void) const { + return mAttributes.size(); +} + +status_t DrmInfo::put(const String8& key, const String8& value) { + mAttributes.add(key, value); + return DRM_NO_ERROR; +} + +String8 DrmInfo::get(const String8& key) const { + if (NAME_NOT_FOUND != mAttributes.indexOfKey(key)) { + return mAttributes.valueFor(key); + } + return String8(""); +} + +int DrmInfo::indexOfKey(const String8& key) const { + return mAttributes.indexOfKey(key); +} + +DrmInfo::KeyIterator DrmInfo::keyIterator() const { + return KeyIterator(this); +} + +DrmInfo::Iterator DrmInfo::iterator() const { + return Iterator(this); +} + +// KeyIterator implementation +DrmInfo::KeyIterator::KeyIterator(const DrmInfo::KeyIterator& keyIterator) : + mDrmInfo(keyIterator.mDrmInfo), mIndex(keyIterator.mIndex) { + +} + +bool DrmInfo::KeyIterator::hasNext() { + return (mIndex < mDrmInfo->mAttributes.size()); +} + +const String8& DrmInfo::KeyIterator::next() { + const String8& key = mDrmInfo->mAttributes.keyAt(mIndex); + mIndex++; + return key; +} + +DrmInfo::KeyIterator& DrmInfo::KeyIterator::operator=(const DrmInfo::KeyIterator& keyIterator) { + mDrmInfo = keyIterator.mDrmInfo; + mIndex = keyIterator.mIndex; + return *this; +} + +// Iterator implementation +DrmInfo::Iterator::Iterator(const DrmInfo::Iterator& iterator) + : mDrmInfo(iterator.mDrmInfo), mIndex(iterator.mIndex) { + +} + +DrmInfo::Iterator& DrmInfo::Iterator::operator=(const DrmInfo::Iterator& iterator) { + mDrmInfo = iterator.mDrmInfo; + mIndex = iterator.mIndex; + return *this; +} + +bool DrmInfo::Iterator::hasNext() { + return mIndex < mDrmInfo->mAttributes.size(); +} + +String8& DrmInfo::Iterator::next() { + String8& value = mDrmInfo->mAttributes.editValueAt(mIndex); + mIndex++; + return value; +} + diff --git a/drm/common/DrmInfoEvent.cpp b/drm/common/DrmInfoEvent.cpp new file mode 100644 index 0000000..8d115a8 --- /dev/null +++ b/drm/common/DrmInfoEvent.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utils/String8.h> +#include <drm/DrmInfoEvent.h> + +using namespace android; + +DrmInfoEvent::DrmInfoEvent(int uniqueId, int infoType, const String8& message) + : mUniqueId(uniqueId), + mInfoType(infoType), + mMessage(message) { + +} + +int DrmInfoEvent::getUniqueId() const { + return mUniqueId; +} + +int DrmInfoEvent::getType() const { + return mInfoType; +} + +const String8& DrmInfoEvent::getMessage() const { + return mMessage; +} + diff --git a/drm/common/DrmInfoRequest.cpp b/drm/common/DrmInfoRequest.cpp new file mode 100644 index 0000000..a646859 --- /dev/null +++ b/drm/common/DrmInfoRequest.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <drm/DrmInfoRequest.h> + +using namespace android; + +const String8 DrmInfoRequest::ACCOUNT_ID("account_id"); +const String8 DrmInfoRequest::SUBSCRIPTION_ID("subscription_id"); + +DrmInfoRequest::DrmInfoRequest(int infoType, const String8& mimeType) : + mInfoType(infoType), mMimeType(mimeType) { + +} + +String8 DrmInfoRequest::getMimeType(void) const { + return mMimeType; +} + +int DrmInfoRequest::getInfoType(void) const { + return mInfoType; +} + +int DrmInfoRequest::getCount(void) const { + return mRequestInformationMap.size(); +} + +status_t DrmInfoRequest::put(const String8& key, const String8& value) { + mRequestInformationMap.add(key, value); + return DRM_NO_ERROR; +} + +String8 DrmInfoRequest::get(const String8& key) const { + if (NAME_NOT_FOUND != mRequestInformationMap.indexOfKey(key)) { + return mRequestInformationMap.valueFor(key); + } + return String8(""); +} + +DrmInfoRequest::KeyIterator DrmInfoRequest::keyIterator() const { + return KeyIterator(this); +} + +DrmInfoRequest::Iterator DrmInfoRequest::iterator() const { + return Iterator(this); +} + +// KeyIterator implementation +DrmInfoRequest::KeyIterator::KeyIterator(const DrmInfoRequest::KeyIterator& keyIterator) + : mDrmInfoRequest(keyIterator.mDrmInfoRequest), + mIndex(keyIterator.mIndex) { + +} + +bool DrmInfoRequest::KeyIterator::hasNext() { + return (mIndex < mDrmInfoRequest->mRequestInformationMap.size()); +} + +const String8& DrmInfoRequest::KeyIterator::next() { + const String8& key = mDrmInfoRequest->mRequestInformationMap.keyAt(mIndex); + mIndex++; + return key; +} + +DrmInfoRequest::KeyIterator& DrmInfoRequest::KeyIterator::operator=( + const DrmInfoRequest::KeyIterator& keyIterator) { + mDrmInfoRequest = keyIterator.mDrmInfoRequest; + mIndex = keyIterator.mIndex; + return *this; +} + +// Iterator implementation +DrmInfoRequest::Iterator::Iterator(const DrmInfoRequest::Iterator& iterator) : + mDrmInfoRequest(iterator.mDrmInfoRequest), mIndex(iterator.mIndex) { +} + +DrmInfoRequest::Iterator& DrmInfoRequest::Iterator::operator=( + const DrmInfoRequest::Iterator& iterator) { + mDrmInfoRequest = iterator.mDrmInfoRequest; + mIndex = iterator.mIndex; + return *this; +} + +bool DrmInfoRequest::Iterator::hasNext() { + return mIndex < mDrmInfoRequest->mRequestInformationMap.size(); +} + +String8& DrmInfoRequest::Iterator::next() { + String8& value = mDrmInfoRequest->mRequestInformationMap.editValueAt(mIndex); + mIndex++; + return value; +} + diff --git a/drm/common/DrmInfoStatus.cpp b/drm/common/DrmInfoStatus.cpp new file mode 100644 index 0000000..8ec7311 --- /dev/null +++ b/drm/common/DrmInfoStatus.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <drm/DrmInfoStatus.h> + +using namespace android; + +DrmInfoStatus::DrmInfoStatus( + int _statusCode, int _infoType, const DrmBuffer* _drmBuffer, const String8& _mimeType) : + statusCode(_statusCode), + infoType(_infoType), + drmBuffer(_drmBuffer), + mimeType(_mimeType) { + +} + diff --git a/drm/common/DrmMetadata.cpp b/drm/common/DrmMetadata.cpp new file mode 100644 index 0000000..6cc5ec1 --- /dev/null +++ b/drm/common/DrmMetadata.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <drm/DrmMetadata.h> + +using namespace android; + +int DrmMetadata::getCount(void) const { + return mMetadataMap.size(); +} + +status_t DrmMetadata::put(const String8* key, + const char* value) { + if((value != NULL) && (key != NULL)) { + int length = strlen(value); + char* charValue = new char[length + 1]; + + memcpy(charValue, value, length); + charValue[length] = '\0'; + mMetadataMap.add(*key, charValue); + } + return NO_ERROR; +} + +String8 DrmMetadata::get(const String8& key) const { + if (NULL != getValue(&key)) { + return String8(getValue(&key)); + } + else { + return String8(""); + } +} + +const char* DrmMetadata::getValue(const String8* key) const { + if(key != NULL) { + if (NAME_NOT_FOUND != mMetadataMap.indexOfKey(*key)) { + return mMetadataMap.valueFor(*key); + } + else { + return NULL; + } + } else { + return NULL; + } +} + +const char* DrmMetadata::getAsByteArray(const String8* key) const { + return getValue(key); +} + +bool DrmMetadata::KeyIterator::hasNext() { + return mIndex < mDrmMetadata->mMetadataMap.size(); +} + +const String8& DrmMetadata::KeyIterator::next() { + const String8& key = mDrmMetadata->mMetadataMap.keyAt(mIndex); + mIndex++; + return key; +} + +DrmMetadata::KeyIterator DrmMetadata::keyIterator() { + return KeyIterator(this); +} + +DrmMetadata::KeyIterator::KeyIterator(const DrmMetadata::KeyIterator& keyIterator) : + mDrmMetadata(keyIterator.mDrmMetadata), + mIndex(keyIterator.mIndex) { + LOGV("DrmMetadata::KeyIterator::KeyIterator"); +} + +DrmMetadata::KeyIterator& DrmMetadata::KeyIterator::operator=(const DrmMetadata::KeyIterator& keyIterator) { + LOGV("DrmMetadata::KeyIterator::operator="); + mDrmMetadata = keyIterator.mDrmMetadata; + mIndex = keyIterator.mIndex; + return *this; +} + + +DrmMetadata::Iterator DrmMetadata::iterator() { + return Iterator(this); +} + +DrmMetadata::Iterator::Iterator(const DrmMetadata::Iterator& iterator) : + mDrmMetadata(iterator.mDrmMetadata), + mIndex(iterator.mIndex) { + LOGV("DrmMetadata::Iterator::Iterator"); +} + +DrmMetadata::Iterator& DrmMetadata::Iterator::operator=(const DrmMetadata::Iterator& iterator) { + LOGV("DrmMetadata::Iterator::operator="); + mDrmMetadata = iterator.mDrmMetadata; + mIndex = iterator.mIndex; + return *this; +} + +bool DrmMetadata::Iterator::hasNext() { + return mIndex < mDrmMetadata->mMetadataMap.size(); +} + +String8 DrmMetadata::Iterator::next() { + String8 value = String8(mDrmMetadata->mMetadataMap.editValueAt(mIndex)); + mIndex++; + return value; +} diff --git a/drm/common/DrmRights.cpp b/drm/common/DrmRights.cpp new file mode 100644 index 0000000..3aecb3d --- /dev/null +++ b/drm/common/DrmRights.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <drm/DrmRights.h> +#include <ReadWriteUtils.h> + +using namespace android; + +DrmRights::DrmRights(const String8& rightsFilePath, const String8& mimeType, + const String8& accountId, const String8& subscriptionId) : + mMimeType(mimeType), + mAccountId(accountId), + mSubscriptionId(subscriptionId), + mRightsFromFile(NULL) { + int rightsLength = 0; + if (String8("") != rightsFilePath) { + rightsLength = ReadWriteUtils::readBytes(rightsFilePath, &mRightsFromFile); + } + mData = DrmBuffer(mRightsFromFile, rightsLength); +} + +DrmRights::DrmRights(const DrmBuffer& rightsData, const String8& mimeType, + const String8& accountId, const String8& subscriptionId) : + mData(rightsData), + mMimeType(mimeType), + mAccountId(accountId), + mSubscriptionId(subscriptionId), + mRightsFromFile(NULL) { +} + +DrmRights::~DrmRights() { + delete[] mRightsFromFile; mRightsFromFile = NULL; +} + +const DrmBuffer& DrmRights::getData(void) const { + return mData; +} + +String8 DrmRights::getMimeType(void) const { + return mMimeType; +} + +String8 DrmRights::getAccountId(void) const { + return mAccountId; +} + +String8 DrmRights::getSubscriptionId(void) const { + return mSubscriptionId; +} + diff --git a/drm/common/DrmSupportInfo.cpp b/drm/common/DrmSupportInfo.cpp new file mode 100644 index 0000000..ffc8953 --- /dev/null +++ b/drm/common/DrmSupportInfo.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <drm/DrmSupportInfo.h> + +using namespace android; + +DrmSupportInfo::DrmSupportInfo() { + +} + +DrmSupportInfo::DrmSupportInfo(const DrmSupportInfo& drmSupportInfo): + mMimeTypeVector(drmSupportInfo.mMimeTypeVector), + mFileSuffixVector(drmSupportInfo.mFileSuffixVector), + mDescription(drmSupportInfo.mDescription) { + +} + +bool DrmSupportInfo::operator<(const DrmSupportInfo& drmSupportInfo) const { + // Do we need to check mMimeTypeVector & mFileSuffixVector ? + // Note Vector doesn't overrides "<" operator + return mDescription < drmSupportInfo.mDescription; +} + +bool DrmSupportInfo::operator==(const DrmSupportInfo& drmSupportInfo) const { + // Do we need to check mMimeTypeVector & mFileSuffixVector ? + // Note Vector doesn't overrides "==" operator + return (mDescription == drmSupportInfo.mDescription); +} + +bool DrmSupportInfo::isSupportedMimeType(const String8& mimeType) const { + for (unsigned int i = 0; i < mMimeTypeVector.size(); i++) { + const String8 item = mMimeTypeVector.itemAt(i); + + if (String8("") != mimeType && item.find(mimeType) != -1) { + return true; + } + } + return false; +} + +bool DrmSupportInfo::isSupportedFileSuffix(const String8& fileType) const { + for (unsigned int i = 0; i < mFileSuffixVector.size(); i++) { + const String8 item = mFileSuffixVector.itemAt(i); + + if (String8("") != fileType && item.find(fileType) != -1) { + return true; + } + } + return false; +} + +DrmSupportInfo& DrmSupportInfo::operator=(const DrmSupportInfo& drmSupportInfo) { + mMimeTypeVector = drmSupportInfo.mMimeTypeVector; + mFileSuffixVector = drmSupportInfo.mFileSuffixVector; + mDescription = drmSupportInfo.mDescription; + return *this; +} + +int DrmSupportInfo::getMimeTypeCount(void) const { + return mMimeTypeVector.size(); +} + +int DrmSupportInfo::getFileSuffixCount(void) const { + return mFileSuffixVector.size(); +} + +status_t DrmSupportInfo::addMimeType(const String8& mimeType) { + mMimeTypeVector.push(mimeType); + return DRM_NO_ERROR; +} + +status_t DrmSupportInfo::addFileSuffix(const String8& fileSuffix) { + mFileSuffixVector.push(fileSuffix); + return DRM_NO_ERROR; +} + +status_t DrmSupportInfo::setDescription(const String8& description) { + mDescription = description; + return DRM_NO_ERROR; +} + +String8 DrmSupportInfo::getDescription() const { + return mDescription; +} + +DrmSupportInfo::FileSuffixIterator DrmSupportInfo::getFileSuffixIterator() { + return FileSuffixIterator(this); +} + +DrmSupportInfo::MimeTypeIterator DrmSupportInfo::getMimeTypeIterator() { + return MimeTypeIterator(this); +} + +DrmSupportInfo::FileSuffixIterator::FileSuffixIterator( + const DrmSupportInfo::FileSuffixIterator& iterator) : + mDrmSupportInfo(iterator.mDrmSupportInfo), + mIndex(iterator.mIndex) { + +} + +DrmSupportInfo::FileSuffixIterator& DrmSupportInfo::FileSuffixIterator::operator=( + const DrmSupportInfo::FileSuffixIterator& iterator) { + mDrmSupportInfo = iterator.mDrmSupportInfo; + mIndex = iterator.mIndex; + return *this; +} + +bool DrmSupportInfo::FileSuffixIterator::hasNext() { + return mIndex < mDrmSupportInfo->mFileSuffixVector.size(); +} + +String8& DrmSupportInfo::FileSuffixIterator::next() { + String8& value = mDrmSupportInfo->mFileSuffixVector.editItemAt(mIndex); + mIndex++; + return value; +} + +DrmSupportInfo::MimeTypeIterator::MimeTypeIterator( + const DrmSupportInfo::MimeTypeIterator& iterator) : + mDrmSupportInfo(iterator.mDrmSupportInfo), + mIndex(iterator.mIndex) { + +} + +DrmSupportInfo::MimeTypeIterator& DrmSupportInfo::MimeTypeIterator::operator=( + const DrmSupportInfo::MimeTypeIterator& iterator) { + mDrmSupportInfo = iterator.mDrmSupportInfo; + mIndex = iterator.mIndex; + return *this; +} + +bool DrmSupportInfo::MimeTypeIterator::hasNext() { + return mIndex < mDrmSupportInfo->mMimeTypeVector.size(); +} + +String8& DrmSupportInfo::MimeTypeIterator::next() { + String8& value = mDrmSupportInfo->mMimeTypeVector.editItemAt(mIndex); + mIndex++; + return value; +} + diff --git a/drm/common/IDrmIOService.cpp b/drm/common/IDrmIOService.cpp new file mode 100644 index 0000000..e44ca55 --- /dev/null +++ b/drm/common/IDrmIOService.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <drm/drm_framework_common.h> +#include "IDrmIOService.h" + +using namespace android; + +void BpDrmIOService::writeToFile(const String8& filePath, const String8& dataBuffer) { + Parcel data, reply; + + data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor()); + data.writeString8(filePath); + data.writeString8(dataBuffer); + + remote()->transact(WRITE_TO_FILE, data, &reply); +} + +String8 BpDrmIOService::readFromFile(const String8& filePath) { + + Parcel data, reply; + + data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor()); + data.writeString8(filePath); + + remote()->transact(READ_FROM_FILE, data, &reply); + return reply.readString8(); +} + +IMPLEMENT_META_INTERFACE(DrmIOService, "drm.IDrmIOService"); + +status_t BnDrmIOService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + + switch (code) { + case WRITE_TO_FILE: + { + CHECK_INTERFACE(IDrmIOService, data, reply); + + writeToFile(data.readString8(), data.readString8()); + return DRM_NO_ERROR; + } + + case READ_FROM_FILE: + { + CHECK_INTERFACE(IDrmIOService, data, reply); + + String8 dataBuffer = readFromFile(data.readString8()); + reply->writeString8(dataBuffer); + return DRM_NO_ERROR; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp new file mode 100644 index 0000000..723b50e --- /dev/null +++ b/drm/common/IDrmManagerService.cpp @@ -0,0 +1,1501 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "IDrmManagerService(Native)" +#include <utils/Log.h> + +#include <stdint.h> +#include <sys/types.h> +#include <binder/IPCThreadState.h> + +#include <drm/DrmInfo.h> +#include <drm/DrmConstraints.h> +#include <drm/DrmMetadata.h> +#include <drm/DrmRights.h> +#include <drm/DrmInfoStatus.h> +#include <drm/DrmConvertedStatus.h> +#include <drm/DrmInfoRequest.h> +#include <drm/DrmSupportInfo.h> + +#include "IDrmManagerService.h" + +#define INVALID_BUFFER_LENGTH -1 + +using namespace android; + +int BpDrmManagerService::addUniqueId(int uniqueId) { + LOGV("add uniqueid"); + Parcel data, reply; + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + remote()->transact(ADD_UNIQUEID, data, &reply); + return reply.readInt32(); +} + +void BpDrmManagerService::removeUniqueId(int uniqueId) { + LOGV("remove uniqueid"); + Parcel data, reply; + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + remote()->transact(REMOVE_UNIQUEID, data, &reply); +} + +void BpDrmManagerService::addClient(int uniqueId) { + Parcel data, reply; + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + remote()->transact(ADD_CLIENT, data, &reply); +} + +void BpDrmManagerService::removeClient(int uniqueId) { + Parcel data, reply; + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + remote()->transact(REMOVE_CLIENT, data, &reply); +} + +status_t BpDrmManagerService::setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) { + LOGV("setDrmServiceListener"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeStrongBinder(drmServiceListener->asBinder()); + remote()->transact(SET_DRM_SERVICE_LISTENER, data, &reply); + return reply.readInt32(); +} + +status_t BpDrmManagerService::installDrmEngine(int uniqueId, const String8& drmEngineFile) { + LOGV("Install DRM Engine"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeString8(drmEngineFile); + + remote()->transact(INSTALL_DRM_ENGINE, data, &reply); + return reply.readInt32(); +} + +DrmConstraints* BpDrmManagerService::getConstraints( + int uniqueId, const String8* path, const int action) { + LOGV("Get Constraints"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeString8(*path); + data.writeInt32(action); + + remote()->transact(GET_CONSTRAINTS_FROM_CONTENT, data, &reply); + + DrmConstraints* drmConstraints = NULL; + if (0 != reply.dataAvail()) { + //Filling Drm Constraints + drmConstraints = new DrmConstraints(); + + const int size = reply.readInt32(); + for (int index = 0; index < size; ++index) { + const String8 key(reply.readString8()); + const int bufferSize = reply.readInt32(); + char* data = NULL; + if (0 < bufferSize) { + data = new char[bufferSize]; + reply.read(data, bufferSize); + } + drmConstraints->put(&key, data); + } + } + return drmConstraints; +} + +DrmMetadata* BpDrmManagerService::getMetadata(int uniqueId, const String8* path) { + LOGV("Get Metadata"); + Parcel data, reply; + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + DrmMetadata* drmMetadata = NULL; + data.writeString8(*path); + remote()->transact(GET_METADATA_FROM_CONTENT, data, &reply); + + if (0 != reply.dataAvail()) { + //Filling Drm Metadata + drmMetadata = new DrmMetadata(); + + const int size = reply.readInt32(); + for (int index = 0; index < size; ++index) { + const String8 key(reply.readString8()); + const int bufferSize = reply.readInt32(); + char* data = NULL; + if (0 < bufferSize) { + data = new char[bufferSize]; + reply.read(data, bufferSize); + } + drmMetadata->put(&key, data); + } + } + return drmMetadata; +} + +bool BpDrmManagerService::canHandle(int uniqueId, const String8& path, const String8& mimeType) { + LOGV("Can Handle"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + data.writeString8(path); + data.writeString8(mimeType); + + remote()->transact(CAN_HANDLE, data, &reply); + + return static_cast<bool>(reply.readInt32()); +} + +DrmInfoStatus* BpDrmManagerService::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) { + LOGV("Process DRM Info"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + //Filling DRM info + data.writeInt32(drmInfo->getInfoType()); + const DrmBuffer dataBuffer = drmInfo->getData(); + const int dataBufferSize = dataBuffer.length; + data.writeInt32(dataBufferSize); + if (0 < dataBufferSize) { + data.write(dataBuffer.data, dataBufferSize); + } + data.writeString8(drmInfo->getMimeType()); + + data.writeInt32(drmInfo->getCount()); + DrmInfo::KeyIterator keyIt = drmInfo->keyIterator(); + + while (keyIt.hasNext()) { + const String8 key = keyIt.next(); + data.writeString8(key); + const String8 value = drmInfo->get(key); + data.writeString8((value == String8("")) ? String8("NULL") : value); + } + + remote()->transact(PROCESS_DRM_INFO, data, &reply); + + DrmInfoStatus* drmInfoStatus = NULL; + if (0 != reply.dataAvail()) { + //Filling DRM Info Status + const int statusCode = reply.readInt32(); + const int infoType = reply.readInt32(); + const String8 mimeType = reply.readString8(); + + DrmBuffer* drmBuffer = NULL; + if (0 != reply.dataAvail()) { + const int bufferSize = reply.readInt32(); + char* data = NULL; + if (0 < bufferSize) { + data = new char[bufferSize]; + reply.read(data, bufferSize); + } + drmBuffer = new DrmBuffer(data, bufferSize); + } + drmInfoStatus = new DrmInfoStatus(statusCode, infoType, drmBuffer, mimeType); + } + return drmInfoStatus; +} + +DrmInfo* BpDrmManagerService::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest) { + LOGV("Acquire DRM Info"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + //Filling DRM Info Request + data.writeInt32(drmInforequest->getInfoType()); + data.writeString8(drmInforequest->getMimeType()); + + data.writeInt32(drmInforequest->getCount()); + DrmInfoRequest::KeyIterator keyIt = drmInforequest->keyIterator(); + + while (keyIt.hasNext()) { + const String8 key = keyIt.next(); + data.writeString8(key); + const String8 value = drmInforequest->get(key); + data.writeString8((value == String8("")) ? String8("NULL") : value); + } + + remote()->transact(ACQUIRE_DRM_INFO, data, &reply); + + DrmInfo* drmInfo = NULL; + if (0 != reply.dataAvail()) { + //Filling DRM Info + const int infoType = reply.readInt32(); + const int bufferSize = reply.readInt32(); + char* data = NULL; + + if (0 < bufferSize) { + data = new char[bufferSize]; + reply.read(data, bufferSize); + } + drmInfo = new DrmInfo(infoType, DrmBuffer(data, bufferSize), reply.readString8()); + + const int size = reply.readInt32(); + for (int index = 0; index < size; ++index) { + const String8 key(reply.readString8()); + const String8 value(reply.readString8()); + drmInfo->put(key, (value == String8("NULL")) ? String8("") : value); + } + } + return drmInfo; +} + +status_t BpDrmManagerService::saveRights( + int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + LOGV("Save Rights"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + //Filling Drm Rights + const DrmBuffer dataBuffer = drmRights.getData(); + data.writeInt32(dataBuffer.length); + data.write(dataBuffer.data, dataBuffer.length); + + const String8 mimeType = drmRights.getMimeType(); + data.writeString8((mimeType == String8("")) ? String8("NULL") : mimeType); + + const String8 accountId = drmRights.getAccountId(); + data.writeString8((accountId == String8("")) ? String8("NULL") : accountId); + + const String8 subscriptionId = drmRights.getSubscriptionId(); + data.writeString8((subscriptionId == String8("")) ? String8("NULL") : subscriptionId); + + data.writeString8((rightsPath == String8("")) ? String8("NULL") : rightsPath); + data.writeString8((contentPath == String8("")) ? String8("NULL") : contentPath); + + remote()->transact(SAVE_RIGHTS, data, &reply); + return reply.readInt32(); +} + +String8 BpDrmManagerService::getOriginalMimeType(int uniqueId, const String8& path) { + LOGV("Get Original MimeType"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeString8(path); + + remote()->transact(GET_ORIGINAL_MIMETYPE, data, &reply); + return reply.readString8(); +} + +int BpDrmManagerService::getDrmObjectType( + int uniqueId, const String8& path, const String8& mimeType) { + LOGV("Get Drm object type"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeString8(path); + data.writeString8(mimeType); + + remote()->transact(GET_DRM_OBJECT_TYPE, data, &reply); + + return reply.readInt32(); +} + +int BpDrmManagerService::checkRightsStatus(int uniqueId, const String8& path, int action) { + LOGV("checkRightsStatus"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeString8(path); + data.writeInt32(action); + + remote()->transact(CHECK_RIGHTS_STATUS, data, &reply); + + return reply.readInt32(); +} + +status_t BpDrmManagerService::consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) { + LOGV("consumeRights"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + data.writeInt32(decryptHandle->decryptId); + data.writeString8(decryptHandle->mimeType); + data.writeInt32(decryptHandle->decryptApiType); + data.writeInt32(decryptHandle->status); + + if (NULL != decryptHandle->decryptInfo) { + data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength); + } else { + data.writeInt32(INVALID_BUFFER_LENGTH); + } + + data.writeInt32(action); + data.writeInt32(static_cast< int>(reserve)); + + remote()->transact(CONSUME_RIGHTS, data, &reply); + return reply.readInt32(); +} + +status_t BpDrmManagerService::setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) { + LOGV("setPlaybackStatus"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + data.writeInt32(decryptHandle->decryptId); + data.writeString8(decryptHandle->mimeType); + data.writeInt32(decryptHandle->decryptApiType); + data.writeInt32(decryptHandle->status); + + if (NULL != decryptHandle->decryptInfo) { + data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength); + } else { + data.writeInt32(INVALID_BUFFER_LENGTH); + } + + data.writeInt32(playbackStatus); + data.writeInt32(position); + + remote()->transact(SET_PLAYBACK_STATUS, data, &reply); + return reply.readInt32(); +} + +bool BpDrmManagerService::validateAction( + int uniqueId, const String8& path, + int action, const ActionDescription& description) { + LOGV("validateAction"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeString8(path); + data.writeInt32(action); + data.writeInt32(description.outputType); + data.writeInt32(description.configuration); + + remote()->transact(VALIDATE_ACTION, data, &reply); + + return static_cast<bool>(reply.readInt32()); +} + +status_t BpDrmManagerService::removeRights(int uniqueId, const String8& path) { + LOGV("removeRights"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeString8(path); + + remote()->transact(REMOVE_RIGHTS, data, &reply); + return reply.readInt32(); +} + +status_t BpDrmManagerService::removeAllRights(int uniqueId) { + LOGV("removeAllRights"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + remote()->transact(REMOVE_ALL_RIGHTS, data, &reply); + return reply.readInt32(); +} + +int BpDrmManagerService::openConvertSession(int uniqueId, const String8& mimeType) { + LOGV("openConvertSession"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeString8(mimeType); + + remote()->transact(OPEN_CONVERT_SESSION, data, &reply); + return reply.readInt32(); +} + +DrmConvertedStatus* BpDrmManagerService::convertData( + int uniqueId, int convertId, const DrmBuffer* inputData) { + LOGV("convertData"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeInt32(convertId); + data.writeInt32(inputData->length); + data.write(inputData->data, inputData->length); + + remote()->transact(CONVERT_DATA, data, &reply); + + DrmConvertedStatus* drmConvertedStatus = NULL; + + if (0 != reply.dataAvail()) { + //Filling DRM Converted Status + const int statusCode = reply.readInt32(); + const int offset = reply.readInt32(); + + DrmBuffer* convertedData = NULL; + if (0 != reply.dataAvail()) { + const int bufferSize = reply.readInt32(); + char* data = NULL; + if (0 < bufferSize) { + data = new char[bufferSize]; + reply.read(data, bufferSize); + } + convertedData = new DrmBuffer(data, bufferSize); + } + drmConvertedStatus = new DrmConvertedStatus(statusCode, convertedData, offset); + } + return drmConvertedStatus; +} + +DrmConvertedStatus* BpDrmManagerService::closeConvertSession(int uniqueId, int convertId) { + LOGV("closeConvertSession"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeInt32(convertId); + + remote()->transact(CLOSE_CONVERT_SESSION, data, &reply); + + DrmConvertedStatus* drmConvertedStatus = NULL; + + if (0 != reply.dataAvail()) { + //Filling DRM Converted Status + const int statusCode = reply.readInt32(); + const int offset = reply.readInt32(); + + DrmBuffer* convertedData = NULL; + if (0 != reply.dataAvail()) { + const int bufferSize = reply.readInt32(); + char* data = NULL; + if (0 < bufferSize) { + data = new char[bufferSize]; + reply.read(data, bufferSize); + } + convertedData = new DrmBuffer(data, bufferSize); + } + drmConvertedStatus = new DrmConvertedStatus(statusCode, convertedData, offset); + } + return drmConvertedStatus; +} + +status_t BpDrmManagerService::getAllSupportInfo( + int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) { + LOGV("Get All Support Info"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + remote()->transact(GET_ALL_SUPPORT_INFO, data, &reply); + + //Filling DRM Support Info + const int arraySize = reply.readInt32(); + if (0 < arraySize) { + *drmSupportInfoArray = new DrmSupportInfo[arraySize]; + + for (int index = 0; index < arraySize; ++index) { + DrmSupportInfo drmSupportInfo; + + const int fileSuffixVectorSize = reply.readInt32(); + for (int i = 0; i < fileSuffixVectorSize; ++i) { + drmSupportInfo.addFileSuffix(reply.readString8()); + } + + const int mimeTypeVectorSize = reply.readInt32(); + for (int i = 0; i < mimeTypeVectorSize; ++i) { + drmSupportInfo.addMimeType(reply.readString8()); + } + + drmSupportInfo.setDescription(reply.readString8()); + (*drmSupportInfoArray)[index] = drmSupportInfo; + } + } + *length = arraySize; + return reply.readInt32(); +} + +DecryptHandle* BpDrmManagerService::openDecryptSession( + int uniqueId, int fd, int offset, int length) { + LOGV("Entering BpDrmManagerService::openDecryptSession"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeFileDescriptor(fd); + data.writeInt32(offset); + data.writeInt32(length); + + remote()->transact(OPEN_DECRYPT_SESSION, data, &reply); + + DecryptHandle* handle = NULL; + if (0 != reply.dataAvail()) { + handle = new DecryptHandle(); + handle->decryptId = reply.readInt32(); + handle->mimeType = reply.readString8(); + handle->decryptApiType = reply.readInt32(); + handle->status = reply.readInt32(); + handle->decryptInfo = NULL; + if (0 != reply.dataAvail()) { + handle->decryptInfo = new DecryptInfo(); + handle->decryptInfo->decryptBufferLength = reply.readInt32(); + } + } else { + LOGE("no decryptHandle is generated in service side"); + } + return handle; +} + +DecryptHandle* BpDrmManagerService::openDecryptSession(int uniqueId, const char* uri) { + LOGV("Entering BpDrmManagerService::openDecryptSession"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeString8(String8(uri)); + + remote()->transact(OPEN_DECRYPT_SESSION_FROM_URI, data, &reply); + + DecryptHandle* handle = NULL; + if (0 != reply.dataAvail()) { + handle = new DecryptHandle(); + handle->decryptId = reply.readInt32(); + handle->mimeType = reply.readString8(); + handle->decryptApiType = reply.readInt32(); + handle->status = reply.readInt32(); + handle->decryptInfo = NULL; + if (0 != reply.dataAvail()) { + handle->decryptInfo = new DecryptInfo(); + handle->decryptInfo->decryptBufferLength = reply.readInt32(); + } + } else { + LOGE("no decryptHandle is generated in service side"); + } + return handle; +} + +status_t BpDrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + LOGV("closeDecryptSession"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + data.writeInt32(decryptHandle->decryptId); + data.writeString8(decryptHandle->mimeType); + data.writeInt32(decryptHandle->decryptApiType); + data.writeInt32(decryptHandle->status); + + if (NULL != decryptHandle->decryptInfo) { + data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength); + } else { + data.writeInt32(INVALID_BUFFER_LENGTH); + } + + remote()->transact(CLOSE_DECRYPT_SESSION, data, &reply); + + if (NULL != decryptHandle->decryptInfo) { + LOGV("deleting decryptInfo"); + delete decryptHandle->decryptInfo; decryptHandle->decryptInfo = NULL; + } + delete decryptHandle; decryptHandle = NULL; + return reply.readInt32(); +} + +status_t BpDrmManagerService::initializeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) { + LOGV("initializeDecryptUnit"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + data.writeInt32(decryptHandle->decryptId); + data.writeString8(decryptHandle->mimeType); + data.writeInt32(decryptHandle->decryptApiType); + data.writeInt32(decryptHandle->status); + + if (NULL != decryptHandle->decryptInfo) { + data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength); + } else { + data.writeInt32(INVALID_BUFFER_LENGTH); + } + data.writeInt32(decryptUnitId); + + data.writeInt32(headerInfo->length); + data.write(headerInfo->data, headerInfo->length); + + remote()->transact(INITIALIZE_DECRYPT_UNIT, data, &reply); + return reply.readInt32(); +} + +status_t BpDrmManagerService::decrypt( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) { + LOGV("decrypt"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + data.writeInt32(decryptHandle->decryptId); + data.writeString8(decryptHandle->mimeType); + data.writeInt32(decryptHandle->decryptApiType); + data.writeInt32(decryptHandle->status); + + if (NULL != decryptHandle->decryptInfo) { + data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength); + } else { + data.writeInt32(INVALID_BUFFER_LENGTH); + } + + data.writeInt32(decryptUnitId); + data.writeInt32((*decBuffer)->length); + + data.writeInt32(encBuffer->length); + data.write(encBuffer->data, encBuffer->length); + + if (NULL != IV) { + data.writeInt32(IV->length); + data.write(IV->data, IV->length); + } + + remote()->transact(DECRYPT, data, &reply); + + const status_t status = reply.readInt32(); + LOGV("Return value of decrypt() is %d", status); + + const int size = reply.readInt32(); + (*decBuffer)->length = size; + reply.read((void *)(*decBuffer)->data, size); + + return status; +} + +status_t BpDrmManagerService::finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + LOGV("finalizeDecryptUnit"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + data.writeInt32(decryptHandle->decryptId); + data.writeString8(decryptHandle->mimeType); + data.writeInt32(decryptHandle->decryptApiType); + data.writeInt32(decryptHandle->status); + + if (NULL != decryptHandle->decryptInfo) { + data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength); + } else { + data.writeInt32(INVALID_BUFFER_LENGTH); + } + + data.writeInt32(decryptUnitId); + + remote()->transact(FINALIZE_DECRYPT_UNIT, data, &reply); + return reply.readInt32(); +} + +ssize_t BpDrmManagerService::pread( + int uniqueId, DecryptHandle* decryptHandle, void* buffer, + ssize_t numBytes, off_t offset) { + LOGV("read"); + Parcel data, reply; + int result; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + data.writeInt32(decryptHandle->decryptId); + data.writeString8(decryptHandle->mimeType); + data.writeInt32(decryptHandle->decryptApiType); + data.writeInt32(decryptHandle->status); + + if (NULL != decryptHandle->decryptInfo) { + data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength); + } else { + data.writeInt32(INVALID_BUFFER_LENGTH); + } + + data.writeInt32(numBytes); + data.writeInt32(offset); + + remote()->transact(PREAD, data, &reply); + result = reply.readInt32(); + if (0 < result) { + reply.read(buffer, result); + } + return result; +} + +IMPLEMENT_META_INTERFACE(DrmManagerService, "drm.IDrmManagerService"); + +status_t BnDrmManagerService::onTransact( + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) { + LOGV("Entering BnDrmManagerService::onTransact with code %d", code); + + switch (code) { + case ADD_UNIQUEID: + { + LOGV("BnDrmManagerService::onTransact :ADD_UNIQUEID"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + int uniqueId = addUniqueId(data.readInt32()); + reply->writeInt32(uniqueId); + return DRM_NO_ERROR; + } + + case REMOVE_UNIQUEID: + { + LOGV("BnDrmManagerService::onTransact :REMOVE_UNIQUEID"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + removeUniqueId(data.readInt32()); + return DRM_NO_ERROR; + } + + case ADD_CLIENT: + { + LOGV("BnDrmManagerService::onTransact :ADD_CLIENT"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + addClient(data.readInt32()); + return DRM_NO_ERROR; + } + + case REMOVE_CLIENT: + { + LOGV("BnDrmManagerService::onTransact :REMOVE_CLIENT"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + removeClient(data.readInt32()); + return DRM_NO_ERROR; + } + + case SET_DRM_SERVICE_LISTENER: + { + LOGV("BnDrmManagerService::onTransact :SET_DRM_SERVICE_LISTENER"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + const sp<IDrmServiceListener> drmServiceListener + = interface_cast<IDrmServiceListener> (data.readStrongBinder()); + + status_t status = setDrmServiceListener(uniqueId, drmServiceListener); + + reply->writeInt32(status); + return DRM_NO_ERROR; + } + + case INSTALL_DRM_ENGINE: + { + LOGV("BnDrmManagerService::onTransact :INSTALL_DRM_ENGINE"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + status_t status = installDrmEngine(data.readInt32(), data.readString8()); + + reply->writeInt32(status); + return DRM_NO_ERROR; + } + + case GET_CONSTRAINTS_FROM_CONTENT: + { + LOGV("BnDrmManagerService::onTransact :GET_CONSTRAINTS_FROM_CONTENT"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + const String8 path = data.readString8(); + + DrmConstraints* drmConstraints = getConstraints(uniqueId, &path, data.readInt32()); + + if (NULL != drmConstraints) { + //Filling DRM Constraints contents + reply->writeInt32(drmConstraints->getCount()); + + DrmConstraints::KeyIterator keyIt = drmConstraints->keyIterator(); + while (keyIt.hasNext()) { + const String8 key = keyIt.next(); + reply->writeString8(key); + const char* value = drmConstraints->getAsByteArray(&key); + int bufferSize = 0; + if (NULL != value) { + bufferSize = strlen(value); + } + reply->writeInt32(bufferSize + 1); + reply->write(value, bufferSize + 1); + } + } + delete drmConstraints; drmConstraints = NULL; + return DRM_NO_ERROR; + } + + case GET_METADATA_FROM_CONTENT: + { + LOGV("BnDrmManagerService::onTransact :GET_METADATA_FROM_CONTENT"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + const String8 path = data.readString8(); + + DrmMetadata* drmMetadata = getMetadata(uniqueId, &path); + if (NULL != drmMetadata) { + //Filling DRM Metadata contents + reply->writeInt32(drmMetadata->getCount()); + + DrmMetadata::KeyIterator keyIt = drmMetadata->keyIterator(); + while (keyIt.hasNext()) { + const String8 key = keyIt.next(); + reply->writeString8(key); + const char* value = drmMetadata->getAsByteArray(&key); + int bufferSize = 0; + if (NULL != value) { + bufferSize = strlen(value); + reply->writeInt32(bufferSize + 1); + reply->write(value, bufferSize + 1); + } else { + reply->writeInt32(0); + } + } + } + delete drmMetadata; drmMetadata = NULL; + return NO_ERROR; + } + + case CAN_HANDLE: + { + LOGV("BnDrmManagerService::onTransact :CAN_HANDLE"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + const String8 path = data.readString8(); + const String8 mimeType = data.readString8(); + + bool result = canHandle(uniqueId, path, mimeType); + + reply->writeInt32(result); + return DRM_NO_ERROR; + } + + case PROCESS_DRM_INFO: + { + LOGV("BnDrmManagerService::onTransact :PROCESS_DRM_INFO"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + + //Filling DRM info + const int infoType = data.readInt32(); + const int bufferSize = data.readInt32(); + char* buffer = NULL; + if (0 < bufferSize) { + buffer = (char *)data.readInplace(bufferSize); + } + const DrmBuffer drmBuffer(buffer, bufferSize); + DrmInfo* drmInfo = new DrmInfo(infoType, drmBuffer, data.readString8()); + + const int size = data.readInt32(); + for (int index = 0; index < size; ++index) { + const String8 key(data.readString8()); + const String8 value(data.readString8()); + drmInfo->put(key, (value == String8("NULL")) ? String8("") : value); + } + + DrmInfoStatus* drmInfoStatus = processDrmInfo(uniqueId, drmInfo); + + if (NULL != drmInfoStatus) { + //Filling DRM Info Status contents + reply->writeInt32(drmInfoStatus->statusCode); + reply->writeInt32(drmInfoStatus->infoType); + reply->writeString8(drmInfoStatus->mimeType); + + if (NULL != drmInfoStatus->drmBuffer) { + const DrmBuffer* drmBuffer = drmInfoStatus->drmBuffer; + const int bufferSize = drmBuffer->length; + reply->writeInt32(bufferSize); + if (0 < bufferSize) { + reply->write(drmBuffer->data, bufferSize); + } + delete [] drmBuffer->data; + delete drmBuffer; drmBuffer = NULL; + } + } + delete drmInfo; drmInfo = NULL; + delete drmInfoStatus; drmInfoStatus = NULL; + return DRM_NO_ERROR; + } + + case ACQUIRE_DRM_INFO: + { + LOGV("BnDrmManagerService::onTransact :ACQUIRE_DRM_INFO"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + + //Filling DRM info Request + DrmInfoRequest* drmInfoRequest = new DrmInfoRequest(data.readInt32(), data.readString8()); + + const int size = data.readInt32(); + for (int index = 0; index < size; ++index) { + const String8 key(data.readString8()); + const String8 value(data.readString8()); + drmInfoRequest->put(key, (value == String8("NULL")) ? String8("") : value); + } + + DrmInfo* drmInfo = acquireDrmInfo(uniqueId, drmInfoRequest); + + if (NULL != drmInfo) { + //Filling DRM Info + const DrmBuffer drmBuffer = drmInfo->getData(); + reply->writeInt32(drmInfo->getInfoType()); + + const int bufferSize = drmBuffer.length; + reply->writeInt32(bufferSize); + if (0 < bufferSize) { + reply->write(drmBuffer.data, bufferSize); + } + reply->writeString8(drmInfo->getMimeType()); + reply->writeInt32(drmInfo->getCount()); + + DrmInfo::KeyIterator keyIt = drmInfo->keyIterator(); + while (keyIt.hasNext()) { + const String8 key = keyIt.next(); + reply->writeString8(key); + const String8 value = drmInfo->get(key); + reply->writeString8((value == String8("")) ? String8("NULL") : value); + } + delete [] drmBuffer.data; + } + delete drmInfoRequest; drmInfoRequest = NULL; + delete drmInfo; drmInfo = NULL; + return DRM_NO_ERROR; + } + + case SAVE_RIGHTS: + { + LOGV("BnDrmManagerService::onTransact :SAVE_RIGHTS"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + + //Filling DRM Rights + const int bufferSize = data.readInt32(); + const DrmBuffer drmBuffer((char *)data.readInplace(bufferSize), bufferSize); + + const String8 mimeType(data.readString8()); + const String8 accountId(data.readString8()); + const String8 subscriptionId(data.readString8()); + const String8 rightsPath(data.readString8()); + const String8 contentPath(data.readString8()); + + DrmRights drmRights(drmBuffer, + ((mimeType == String8("NULL")) ? String8("") : mimeType), + ((accountId == String8("NULL")) ? String8("") : accountId), + ((subscriptionId == String8("NULL")) ? String8("") : subscriptionId)); + + const status_t status = saveRights(uniqueId, drmRights, + ((rightsPath == String8("NULL")) ? String8("") : rightsPath), + ((contentPath == String8("NULL")) ? String8("") : contentPath)); + + reply->writeInt32(status); + return DRM_NO_ERROR; + } + + case GET_ORIGINAL_MIMETYPE: + { + LOGV("BnDrmManagerService::onTransact :GET_ORIGINAL_MIMETYPE"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const String8 originalMimeType = getOriginalMimeType(data.readInt32(), data.readString8()); + + reply->writeString8(originalMimeType); + return DRM_NO_ERROR; + } + + case GET_DRM_OBJECT_TYPE: + { + LOGV("BnDrmManagerService::onTransact :GET_DRM_OBJECT_TYPE"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int drmObjectType + = getDrmObjectType(data.readInt32(), data.readString8(), data.readString8()); + + reply->writeInt32(drmObjectType); + return DRM_NO_ERROR; + } + + case CHECK_RIGHTS_STATUS: + { + LOGV("BnDrmManagerService::onTransact :CHECK_RIGHTS_STATUS"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int result + = checkRightsStatus(data.readInt32(), data.readString8(), data.readInt32()); + + reply->writeInt32(result); + return DRM_NO_ERROR; + } + + case CONSUME_RIGHTS: + { + LOGV("BnDrmManagerService::onTransact :CONSUME_RIGHTS"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + + DecryptHandle handle; + handle.decryptId = data.readInt32(); + handle.mimeType = data.readString8(); + handle.decryptApiType = data.readInt32(); + handle.status = data.readInt32(); + handle.decryptInfo = NULL; + + const int bufferLength = data.readInt32(); + if (INVALID_BUFFER_LENGTH != bufferLength) { + handle.decryptInfo = new DecryptInfo(); + handle.decryptInfo->decryptBufferLength = bufferLength; + } + + const status_t status + = consumeRights(uniqueId, &handle, data.readInt32(), + static_cast<bool>(data.readInt32())); + reply->writeInt32(status); + + delete handle.decryptInfo; handle.decryptInfo = NULL; + return DRM_NO_ERROR; + } + + case SET_PLAYBACK_STATUS: + { + LOGV("BnDrmManagerService::onTransact :SET_PLAYBACK_STATUS"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + + DecryptHandle handle; + handle.decryptId = data.readInt32(); + handle.mimeType = data.readString8(); + handle.decryptApiType = data.readInt32(); + handle.status = data.readInt32(); + handle.decryptInfo = NULL; + + const int bufferLength = data.readInt32(); + if (INVALID_BUFFER_LENGTH != bufferLength) { + handle.decryptInfo = new DecryptInfo(); + handle.decryptInfo->decryptBufferLength = bufferLength; + } + + const status_t status + = setPlaybackStatus(uniqueId, &handle, data.readInt32(), data.readInt32()); + reply->writeInt32(status); + + delete handle.decryptInfo; handle.decryptInfo = NULL; + return DRM_NO_ERROR; + } + + case VALIDATE_ACTION: + { + LOGV("BnDrmManagerService::onTransact :VALIDATE_ACTION"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + bool result = validateAction( + data.readInt32(), + data.readString8(), + data.readInt32(), + ActionDescription(data.readInt32(), data.readInt32())); + + reply->writeInt32(result); + return DRM_NO_ERROR; + } + + case REMOVE_RIGHTS: + { + LOGV("BnDrmManagerService::onTransact :REMOVE_RIGHTS"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const status_t status = removeRights(data.readInt32(), data.readString8()); + reply->writeInt32(status); + + return DRM_NO_ERROR; + } + + case REMOVE_ALL_RIGHTS: + { + LOGV("BnDrmManagerService::onTransact :REMOVE_ALL_RIGHTS"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const status_t status = removeAllRights(data.readInt32()); + reply->writeInt32(status); + + return DRM_NO_ERROR; + } + + case OPEN_CONVERT_SESSION: + { + LOGV("BnDrmManagerService::onTransact :OPEN_CONVERT_SESSION"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int convertId = openConvertSession(data.readInt32(), data.readString8()); + + reply->writeInt32(convertId); + return DRM_NO_ERROR; + } + + case CONVERT_DATA: + { + LOGV("BnDrmManagerService::onTransact :CONVERT_DATA"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + const int convertId = data.readInt32(); + + //Filling input data + const int bufferSize = data.readInt32(); + DrmBuffer* inputData = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize); + + DrmConvertedStatus* drmConvertedStatus = convertData(uniqueId, convertId, inputData); + + if (NULL != drmConvertedStatus) { + //Filling Drm Converted Ststus + reply->writeInt32(drmConvertedStatus->statusCode); + reply->writeInt32(drmConvertedStatus->offset); + + if (NULL != drmConvertedStatus->convertedData) { + const DrmBuffer* convertedData = drmConvertedStatus->convertedData; + const int bufferSize = convertedData->length; + reply->writeInt32(bufferSize); + if (0 < bufferSize) { + reply->write(convertedData->data, bufferSize); + } + delete [] convertedData->data; + delete convertedData; convertedData = NULL; + } + } + delete inputData; inputData = NULL; + delete drmConvertedStatus; drmConvertedStatus = NULL; + return DRM_NO_ERROR; + } + + case CLOSE_CONVERT_SESSION: + { + LOGV("BnDrmManagerService::onTransact :CLOSE_CONVERT_SESSION"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + DrmConvertedStatus* drmConvertedStatus + = closeConvertSession(data.readInt32(), data.readInt32()); + + if (NULL != drmConvertedStatus) { + //Filling Drm Converted Ststus + reply->writeInt32(drmConvertedStatus->statusCode); + reply->writeInt32(drmConvertedStatus->offset); + + if (NULL != drmConvertedStatus->convertedData) { + const DrmBuffer* convertedData = drmConvertedStatus->convertedData; + const int bufferSize = convertedData->length; + reply->writeInt32(bufferSize); + if (0 < bufferSize) { + reply->write(convertedData->data, bufferSize); + } + delete [] convertedData->data; + delete convertedData; convertedData = NULL; + } + } + delete drmConvertedStatus; drmConvertedStatus = NULL; + return DRM_NO_ERROR; + } + + case GET_ALL_SUPPORT_INFO: + { + LOGV("BnDrmManagerService::onTransact :GET_ALL_SUPPORT_INFO"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + int length = 0; + DrmSupportInfo* drmSupportInfoArray = NULL; + + status_t status = getAllSupportInfo(uniqueId, &length, &drmSupportInfoArray); + + reply->writeInt32(length); + for (int i = 0; i < length; ++i) { + DrmSupportInfo drmSupportInfo = drmSupportInfoArray[i]; + + reply->writeInt32(drmSupportInfo.getFileSuffixCount()); + DrmSupportInfo::FileSuffixIterator fileSuffixIt + = drmSupportInfo.getFileSuffixIterator(); + while (fileSuffixIt.hasNext()) { + reply->writeString8(fileSuffixIt.next()); + } + + reply->writeInt32(drmSupportInfo.getMimeTypeCount()); + DrmSupportInfo::MimeTypeIterator mimeTypeIt = drmSupportInfo.getMimeTypeIterator(); + while (mimeTypeIt.hasNext()) { + reply->writeString8(mimeTypeIt.next()); + } + reply->writeString8(drmSupportInfo.getDescription()); + } + delete [] drmSupportInfoArray; drmSupportInfoArray = NULL; + reply->writeInt32(status); + return DRM_NO_ERROR; + } + + case OPEN_DECRYPT_SESSION: + { + LOGV("BnDrmManagerService::onTransact :OPEN_DECRYPT_SESSION"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + const int fd = data.readFileDescriptor(); + + DecryptHandle* handle + = openDecryptSession(uniqueId, fd, data.readInt32(), data.readInt32()); + + if (NULL != handle) { + reply->writeInt32(handle->decryptId); + reply->writeString8(handle->mimeType); + reply->writeInt32(handle->decryptApiType); + reply->writeInt32(handle->status); + if (NULL != handle->decryptInfo) { + reply->writeInt32(handle->decryptInfo->decryptBufferLength); + delete handle->decryptInfo; handle->decryptInfo = NULL; + } + } else { + LOGE("NULL decryptHandle is returned"); + } + delete handle; handle = NULL; + return DRM_NO_ERROR; + } + + case OPEN_DECRYPT_SESSION_FROM_URI: + { + LOGV("BnDrmManagerService::onTransact :OPEN_DECRYPT_SESSION_FROM_URI"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + const String8 uri = data.readString8(); + + DecryptHandle* handle = openDecryptSession(uniqueId, uri.string()); + + if (NULL != handle) { + reply->writeInt32(handle->decryptId); + reply->writeString8(handle->mimeType); + reply->writeInt32(handle->decryptApiType); + reply->writeInt32(handle->status); + if (NULL != handle->decryptInfo) { + reply->writeInt32(handle->decryptInfo->decryptBufferLength); + delete handle->decryptInfo; handle->decryptInfo = NULL; + } + } else { + LOGE("NULL decryptHandle is returned"); + } + delete handle; handle = NULL; + return DRM_NO_ERROR; + } + + case CLOSE_DECRYPT_SESSION: + { + LOGV("BnDrmManagerService::onTransact :CLOSE_DECRYPT_SESSION"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + + DecryptHandle* handle = new DecryptHandle(); + handle->decryptId = data.readInt32(); + handle->mimeType = data.readString8(); + handle->decryptApiType = data.readInt32(); + handle->status = data.readInt32(); + handle->decryptInfo = NULL; + + const int bufferLength = data.readInt32(); + if (INVALID_BUFFER_LENGTH != bufferLength) { + handle->decryptInfo = new DecryptInfo(); + handle->decryptInfo->decryptBufferLength = bufferLength; + } + + const status_t status = closeDecryptSession(uniqueId, handle); + reply->writeInt32(status); + return DRM_NO_ERROR; + } + + case INITIALIZE_DECRYPT_UNIT: + { + LOGV("BnDrmManagerService::onTransact :INITIALIZE_DECRYPT_UNIT"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + + DecryptHandle handle; + handle.decryptId = data.readInt32(); + handle.mimeType = data.readString8(); + handle.decryptApiType = data.readInt32(); + handle.status = data.readInt32(); + handle.decryptInfo = NULL; + + const int bufferLength = data.readInt32(); + if (INVALID_BUFFER_LENGTH != bufferLength) { + handle.decryptInfo = new DecryptInfo(); + handle.decryptInfo->decryptBufferLength = bufferLength; + } + const int decryptUnitId = data.readInt32(); + + //Filling Header info + const int bufferSize = data.readInt32(); + DrmBuffer* headerInfo = NULL; + headerInfo = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize); + + const status_t status + = initializeDecryptUnit(uniqueId, &handle, decryptUnitId, headerInfo); + reply->writeInt32(status); + + delete handle.decryptInfo; handle.decryptInfo = NULL; + delete headerInfo; headerInfo = NULL; + return DRM_NO_ERROR; + } + + case DECRYPT: + { + LOGV("BnDrmManagerService::onTransact :DECRYPT"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + + DecryptHandle handle; + handle.decryptId = data.readInt32(); + handle.mimeType = data.readString8(); + handle.decryptApiType = data.readInt32(); + handle.status = data.readInt32(); + handle.decryptInfo = NULL; + + const int bufferLength = data.readInt32(); + if (INVALID_BUFFER_LENGTH != bufferLength) { + handle.decryptInfo = new DecryptInfo(); + handle.decryptInfo->decryptBufferLength = bufferLength; + } + const int decryptUnitId = data.readInt32(); + const int decBufferSize = data.readInt32(); + + const int encBufferSize = data.readInt32(); + DrmBuffer* encBuffer + = new DrmBuffer((char *)data.readInplace(encBufferSize), encBufferSize); + + char* buffer = NULL; + buffer = new char[decBufferSize]; + DrmBuffer* decBuffer = new DrmBuffer(buffer, decBufferSize); + + DrmBuffer* IV = NULL; + if (0 != data.dataAvail()) { + const int ivBufferlength = data.readInt32(); + IV = new DrmBuffer((char *)data.readInplace(ivBufferlength), ivBufferlength); + } + + const status_t status + = decrypt(uniqueId, &handle, decryptUnitId, encBuffer, &decBuffer, IV); + + reply->writeInt32(status); + + const int size = decBuffer->length; + reply->writeInt32(size); + reply->write(decBuffer->data, size); + + delete handle.decryptInfo; handle.decryptInfo = NULL; + delete encBuffer; encBuffer = NULL; + delete decBuffer; decBuffer = NULL; + delete [] buffer; buffer = NULL; + delete IV; IV = NULL; + return DRM_NO_ERROR; + } + + case FINALIZE_DECRYPT_UNIT: + { + LOGV("BnDrmManagerService::onTransact :FINALIZE_DECRYPT_UNIT"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + + DecryptHandle handle; + handle.decryptId = data.readInt32(); + handle.mimeType = data.readString8(); + handle.decryptApiType = data.readInt32(); + handle.status = data.readInt32(); + handle.decryptInfo = NULL; + + const int bufferLength = data.readInt32(); + if (INVALID_BUFFER_LENGTH != bufferLength) { + handle.decryptInfo = new DecryptInfo(); + handle.decryptInfo->decryptBufferLength = bufferLength; + } + + const status_t status = finalizeDecryptUnit(uniqueId, &handle, data.readInt32()); + reply->writeInt32(status); + + delete handle.decryptInfo; handle.decryptInfo = NULL; + return DRM_NO_ERROR; + } + + case PREAD: + { + LOGV("BnDrmManagerService::onTransact :READ"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + const int uniqueId = data.readInt32(); + + DecryptHandle handle; + handle.decryptId = data.readInt32(); + handle.mimeType = data.readString8(); + handle.decryptApiType = data.readInt32(); + handle.status = data.readInt32(); + handle.decryptInfo = NULL; + + const int bufferLength = data.readInt32(); + if (INVALID_BUFFER_LENGTH != bufferLength) { + handle.decryptInfo = new DecryptInfo(); + handle.decryptInfo->decryptBufferLength = bufferLength; + } + + const int numBytes = data.readInt32(); + char* buffer = new char[numBytes]; + + const off_t offset = data.readInt32(); + + ssize_t result = pread(uniqueId, &handle, buffer, numBytes, offset); + reply->writeInt32(result); + if (0 < result) { + reply->write(buffer, result); + } + + delete handle.decryptInfo; handle.decryptInfo = NULL; + delete [] buffer, buffer = NULL; + return DRM_NO_ERROR; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + diff --git a/drm/common/IDrmServiceListener.cpp b/drm/common/IDrmServiceListener.cpp new file mode 100644 index 0000000..6eeea40 --- /dev/null +++ b/drm/common/IDrmServiceListener.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <drm/drm_framework_common.h> +#include <drm/DrmInfoEvent.h> +#include "IDrmServiceListener.h" + +using namespace android; + +status_t BpDrmServiceListener::notify(const DrmInfoEvent& event) { + Parcel data, reply; + + data.writeInterfaceToken(IDrmServiceListener::getInterfaceDescriptor()); + data.writeInt32(event.getUniqueId()); + data.writeInt32(event.getType()); + data.writeString8(event.getMessage()); + + remote()->transact(NOTIFY, data, &reply); + return reply.readInt32(); +} + +IMPLEMENT_META_INTERFACE(DrmServiceListener, "drm.IDrmServiceListener"); + +status_t BnDrmServiceListener::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + + switch (code) { + case NOTIFY: + { + CHECK_INTERFACE(IDrmServiceListener, data, reply); + int uniqueId = data.readInt32(); + int type = data.readInt32(); + const String8& message = data.readString8(); + + status_t status = notify(DrmInfoEvent(uniqueId, type, message)); + reply->writeInt32(status); + + return DRM_NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + diff --git a/drm/common/ReadWriteUtils.cpp b/drm/common/ReadWriteUtils.cpp new file mode 100644 index 0000000..7ec4fa2 --- /dev/null +++ b/drm/common/ReadWriteUtils.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ReadWriteUtils" +#include <utils/Log.h> + +#include <ReadWriteUtils.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <utils/String8.h> + +using namespace android; + +#define FAILURE -1 + +String8 ReadWriteUtils::readBytes(const String8& filePath) { + FILE* file = NULL; + file = fopen(filePath.string(), "r"); + + String8 string(""); + if (NULL != file) { + int fd = fileno(file); + struct stat sb; + + if (fstat(fd, &sb) == 0 && sb.st_size > 0) { + int length = sb.st_size; + char* bytes = new char[length]; + if (length == read(fd, (void*) bytes, length)) { + string.append(bytes, length); + } + delete bytes; + } + fclose(file); + } + return string; +} + +int ReadWriteUtils::readBytes(const String8& filePath, char** buffer) { + FILE* file = NULL; + file = fopen(filePath.string(), "r"); + int length = 0; + + if (NULL != file) { + int fd = fileno(file); + struct stat sb; + + if (fstat(fd, &sb) == 0 && sb.st_size > 0) { + length = sb.st_size; + *buffer = new char[length]; + if (length != read(fd, (void*) *buffer, length)) { + length = FAILURE; + } + } + fclose(file); + } + return length; +} + +void ReadWriteUtils::writeToFile(const String8& filePath, const String8& data) { + FILE* file = NULL; + file = fopen(filePath.string(), "w+"); + + if (NULL != file) { + int fd = fileno(file); + + int size = data.size(); + if (FAILURE != ftruncate(fd, size)) { + if (size != write(fd, data.string(), size)) { + LOGE("Failed to write the data to: %s", filePath.string()); + } + } + fclose(file); + } +} + +void ReadWriteUtils::appendToFile(const String8& filePath, const String8& data) { + FILE* file = NULL; + file = fopen(filePath.string(), "a+"); + + if (NULL != file) { + int fd = fileno(file); + + int size = data.size(); + if (size != write(fd, data.string(), size)) { + LOGE("Failed to write the data to: %s", filePath.string()); + } + fclose(file); + } +} + diff --git a/drm/drmioserver/Android.mk b/drm/drmioserver/Android.mk new file mode 100644 index 0000000..11571c7 --- /dev/null +++ b/drm/drmioserver/Android.mk @@ -0,0 +1,43 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + main_drmioserver.cpp \ + DrmIOService.cpp + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libbinder + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon + +LOCAL_C_INCLUDES := \ + $(TOP)/frameworks/base/drm/libdrmframework/include \ + $(TOP)/frameworks/base/include + +LOCAL_MODULE:= drmioserver + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) diff --git a/drm/drmioserver/DrmIOService.cpp b/drm/drmioserver/DrmIOService.cpp new file mode 100644 index 0000000..60e6e70 --- /dev/null +++ b/drm/drmioserver/DrmIOService.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "DrmIOService" +#include <utils/Log.h> + +#include <binder/IServiceManager.h> +#include "DrmIOService.h" +#include "ReadWriteUtils.h" + +using namespace android; + +void DrmIOService::instantiate() { + LOGV("instantiate"); + defaultServiceManager()->addService(String16("drm.drmIOService"), new DrmIOService()); +} + +DrmIOService::DrmIOService() { + LOGV("created"); +} + +DrmIOService::~DrmIOService() { + LOGV("Destroyed"); +} + +void DrmIOService::writeToFile(const String8& filePath, const String8& dataBuffer) { + LOGV("Entering writeToFile"); + ReadWriteUtils::writeToFile(filePath, dataBuffer); +} + +String8 DrmIOService::readFromFile(const String8& filePath) { + LOGV("Entering readFromFile"); + return ReadWriteUtils::readBytes(filePath); +} + diff --git a/drm/drmioserver/main_drmioserver.cpp b/drm/drmioserver/main_drmioserver.cpp new file mode 100644 index 0000000..7ed048d --- /dev/null +++ b/drm/drmioserver/main_drmioserver.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/types.h> +#include <unistd.h> +#include <grp.h> + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> +#include <private/android_filesystem_config.h> + +#include <DrmIOService.h> + +using namespace android; + +int main(int argc, char** argv) +{ + sp<ProcessState> proc(ProcessState::self()); + sp<IServiceManager> sm = defaultServiceManager(); + LOGI("ServiceManager: %p", sm.get()); + DrmIOService::instantiate(); + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); +} + diff --git a/drm/drmserver/Android.mk b/drm/drmserver/Android.mk new file mode 100644 index 0000000..5df2ff8 --- /dev/null +++ b/drm/drmserver/Android.mk @@ -0,0 +1,46 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + main_drmserver.cpp \ + DrmManager.cpp \ + DrmManagerService.cpp \ + StringTokenizer.cpp + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libbinder + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon + +LOCAL_C_INCLUDES := \ + $(TOP)/frameworks/base/include \ + $(TOP)/frameworks/base/drm/libdrmframework/include \ + $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include + +LOCAL_MODULE:= drmserver + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp new file mode 100644 index 0000000..49df1c8 --- /dev/null +++ b/drm/drmserver/DrmManager.cpp @@ -0,0 +1,557 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "DrmManager(Native)" +#include "utils/Log.h" + +#include <utils/String8.h> +#include <drm/DrmInfo.h> +#include <drm/DrmInfoEvent.h> +#include <drm/DrmRights.h> +#include <drm/DrmConstraints.h> +#include <drm/DrmMetadata.h> +#include <drm/DrmInfoStatus.h> +#include <drm/DrmInfoRequest.h> +#include <drm/DrmSupportInfo.h> +#include <drm/DrmConvertedStatus.h> +#include <IDrmEngine.h> + +#include "DrmManager.h" +#include "ReadWriteUtils.h" + +#define DECRYPT_FILE_ERROR -1 + +using namespace android; + +Vector<int> DrmManager::mUniqueIdVector; +const String8 DrmManager::EMPTY_STRING(""); + +DrmManager::DrmManager() : + mDecryptSessionId(0), + mConvertId(0) { + +} + +DrmManager::~DrmManager() { + +} + +int DrmManager::addUniqueId(int uniqueId) { + if (0 == uniqueId) { + int temp = 0; + bool foundUniqueId = false; + srand(time(NULL)); + + while (!foundUniqueId) { + const int size = mUniqueIdVector.size(); + temp = rand() % 100; + + int index = 0; + for (; index < size; ++index) { + if (mUniqueIdVector.itemAt(index) == temp) { + foundUniqueId = false; + break; + } + } + if (index == size) { + foundUniqueId = true; + } + } + uniqueId = temp; + } + mUniqueIdVector.push(uniqueId); + return uniqueId; +} + +void DrmManager::removeUniqueId(int uniqueId) { + for (unsigned int i = 0; i < mUniqueIdVector.size(); i++) { + if (uniqueId == mUniqueIdVector.itemAt(i)) { + mUniqueIdVector.removeAt(i); + break; + } + } +} + +status_t DrmManager::loadPlugIns() { + String8 pluginDirPath("/system/lib/drm/plugins/native"); + return loadPlugIns(pluginDirPath); +} + +status_t DrmManager::loadPlugIns(const String8& plugInDirPath) { + if (mSupportInfoToPlugInIdMap.isEmpty()) { + mPlugInManager.loadPlugIns(plugInDirPath); + Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList(); + for (unsigned int i = 0; i < plugInPathList.size(); ++i) { + String8 plugInPath = plugInPathList[i]; + DrmSupportInfo* info = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0); + if (NULL != info) { + mSupportInfoToPlugInIdMap.add(*info, plugInPath); + } + } + } + return DRM_NO_ERROR; +} + +status_t DrmManager::unloadPlugIns() { + mConvertSessionMap.clear(); + mDecryptSessionMap.clear(); + mPlugInManager.unloadPlugIns(); + mSupportInfoToPlugInIdMap.clear(); + return DRM_NO_ERROR; +} + +status_t DrmManager::setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) { + Mutex::Autolock _l(mLock); + if (NULL != drmServiceListener.get()) { + mServiceListeners.add(uniqueId, drmServiceListener); + } else { + mServiceListeners.removeItem(uniqueId); + } + return DRM_NO_ERROR; +} + +void DrmManager::addClient(int uniqueId) { + if (!mSupportInfoToPlugInIdMap.isEmpty()) { + Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList(); + for (unsigned int index = 0; index < plugInIdList.size(); index++) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index)); + rDrmEngine.initialize(uniqueId); + rDrmEngine.setOnInfoListener(uniqueId, this); + } + } +} + +void DrmManager::removeClient(int uniqueId) { + Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList(); + for (unsigned int index = 0; index < plugInIdList.size(); index++) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index)); + rDrmEngine.terminate(uniqueId); + } +} + +DrmConstraints* DrmManager::getConstraints(int uniqueId, const String8* path, const int action) { + const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + return rDrmEngine.getConstraints(uniqueId, path, action); + } + return NULL; +} + +DrmMetadata* DrmManager::getMetadata(int uniqueId, const String8* path) { + const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + return rDrmEngine.getMetadata(uniqueId, path); + } + return NULL; +} + +status_t DrmManager::installDrmEngine(int uniqueId, const String8& absolutePath) { + mPlugInManager.loadPlugIn(absolutePath); + + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(absolutePath); + rDrmEngine.initialize(uniqueId); + rDrmEngine.setOnInfoListener(uniqueId, this); + + DrmSupportInfo* info = rDrmEngine.getSupportInfo(0); + mSupportInfoToPlugInIdMap.add(*info, absolutePath); + + return DRM_NO_ERROR; +} + +bool DrmManager::canHandle(int uniqueId, const String8& path, const String8& mimeType) { + const String8 plugInId = getSupportedPlugInId(mimeType); + bool result = (EMPTY_STRING != plugInId) ? true : false; + + if (0 < path.length()) { + if (result) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + result = rDrmEngine.canHandle(uniqueId, path); + } else { + result = canHandle(uniqueId, path); + } + } + return result; +} + +DrmInfoStatus* DrmManager::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) { + const String8 plugInId = getSupportedPlugInId(drmInfo->getMimeType()); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + return rDrmEngine.processDrmInfo(uniqueId, drmInfo); + } + return NULL; +} + +bool DrmManager::canHandle(int uniqueId, const String8& path) { + bool result = false; + Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList(); + + for (unsigned int i = 0; i < plugInPathList.size(); ++i) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInPathList[i]); + result = rDrmEngine.canHandle(uniqueId, path); + + if (result) { + break; + } + } + return result; +} + +DrmInfo* DrmManager::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) { + const String8 plugInId = getSupportedPlugInId(drmInfoRequest->getMimeType()); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + return rDrmEngine.acquireDrmInfo(uniqueId, drmInfoRequest); + } + return NULL; +} + +status_t DrmManager::saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + const String8 plugInId = getSupportedPlugInId(drmRights.getMimeType()); + status_t result = DRM_ERROR_UNKNOWN; + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + result = rDrmEngine.saveRights(uniqueId, drmRights, rightsPath, contentPath); + } + return result; +} + +String8 DrmManager::getOriginalMimeType(int uniqueId, const String8& path) { + const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + return rDrmEngine.getOriginalMimeType(uniqueId, path); + } + return EMPTY_STRING; +} + +int DrmManager::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) { + const String8 plugInId = getSupportedPlugInId(uniqueId, path, mimeType); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + return rDrmEngine.getDrmObjectType(uniqueId, path, mimeType); + } + return DrmObjectType::UNKNOWN; +} + +int DrmManager::checkRightsStatus(int uniqueId, const String8& path, int action) { + const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + return rDrmEngine.checkRightsStatus(uniqueId, path, action); + } + return RightsStatus::RIGHTS_INVALID; +} + +status_t DrmManager::consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) { + status_t result = DRM_ERROR_UNKNOWN; + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + result = drmEngine->consumeRights(uniqueId, decryptHandle, action, reserve); + } + return result; +} + +status_t DrmManager::setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) { + status_t result = DRM_ERROR_UNKNOWN; + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + result = drmEngine->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position); + } + return result; +} + +bool DrmManager::validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description) { + const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + return rDrmEngine.validateAction(uniqueId, path, action, description); + } + return false; +} + +status_t DrmManager::removeRights(int uniqueId, const String8& path) { + const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path); + status_t result = DRM_ERROR_UNKNOWN; + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + result = rDrmEngine.removeRights(uniqueId, path); + } + return result; +} + +status_t DrmManager::removeAllRights(int uniqueId) { + Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList(); + status_t result = DRM_ERROR_UNKNOWN; + for (unsigned int index = 0; index < plugInIdList.size(); index++) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index)); + result = rDrmEngine.removeAllRights(uniqueId); + if (DRM_NO_ERROR != result) { + break; + } + } + return result; +} + +int DrmManager::openConvertSession(int uniqueId, const String8& mimeType) { + int convertId = -1; + + const String8 plugInId = getSupportedPlugInId(mimeType); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + + if (DRM_NO_ERROR == rDrmEngine.openConvertSession(uniqueId, mConvertId + 1)) { + Mutex::Autolock _l(mConvertLock); + ++mConvertId; + convertId = mConvertId; + mConvertSessionMap.add(convertId, &rDrmEngine); + } + } + return convertId; +} + +DrmConvertedStatus* DrmManager::convertData( + int uniqueId, int convertId, const DrmBuffer* inputData) { + DrmConvertedStatus *drmConvertedStatus = NULL; + + if (mConvertSessionMap.indexOfKey(convertId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mConvertSessionMap.valueFor(convertId); + drmConvertedStatus = drmEngine->convertData(uniqueId, convertId, inputData); + } + return drmConvertedStatus; +} + +DrmConvertedStatus* DrmManager::closeConvertSession(int uniqueId, int convertId) { + DrmConvertedStatus *drmConvertedStatus = NULL; + + if (mConvertSessionMap.indexOfKey(convertId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mConvertSessionMap.valueFor(convertId); + drmConvertedStatus = drmEngine->closeConvertSession(uniqueId, convertId); + mConvertSessionMap.removeItem(convertId); + } + return drmConvertedStatus; +} + +status_t DrmManager::getAllSupportInfo( + int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) { + Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList(); + int size = plugInPathList.size(); + int validPlugins = 0; + + if (0 < size) { + Vector<DrmSupportInfo> drmSupportInfoList; + + for (int i = 0; i < size; ++i) { + String8 plugInPath = plugInPathList[i]; + DrmSupportInfo* drmSupportInfo + = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0); + if (NULL != drmSupportInfo) { + drmSupportInfoList.add(*drmSupportInfo); + delete drmSupportInfo; drmSupportInfo = NULL; + } + } + + validPlugins = drmSupportInfoList.size(); + if (0 < validPlugins) { + *drmSupportInfoArray = new DrmSupportInfo[validPlugins]; + for (int i = 0; i < validPlugins; ++i) { + (*drmSupportInfoArray)[i] = drmSupportInfoList[i]; + } + } + } + *length = validPlugins; + return DRM_NO_ERROR; +} + +DecryptHandle* DrmManager::openDecryptSession(int uniqueId, int fd, int offset, int length) { + Mutex::Autolock _l(mDecryptLock); + status_t result = DRM_ERROR_CANNOT_HANDLE; + Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList(); + + DecryptHandle* handle = new DecryptHandle(); + if (NULL != handle) { + handle->decryptId = mDecryptSessionId + 1; + + for (unsigned int index = 0; index < plugInIdList.size(); index++) { + String8 plugInId = plugInIdList.itemAt(index); + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + result = rDrmEngine.openDecryptSession(uniqueId, handle, fd, offset, length); + + if (DRM_NO_ERROR == result) { + ++mDecryptSessionId; + mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine); + break; + } + } + } + if (DRM_NO_ERROR != result) { + delete handle; handle = NULL; + LOGE("DrmManager::openDecryptSession: no capable plug-in found"); + } + return handle; +} + +DecryptHandle* DrmManager::openDecryptSession(int uniqueId, const char* uri) { + Mutex::Autolock _l(mDecryptLock); + status_t result = DRM_ERROR_CANNOT_HANDLE; + Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList(); + + DecryptHandle* handle = new DecryptHandle(); + if (NULL != handle) { + handle->decryptId = mDecryptSessionId + 1; + + for (unsigned int index = 0; index < plugInIdList.size(); index++) { + String8 plugInId = plugInIdList.itemAt(index); + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + result = rDrmEngine.openDecryptSession(uniqueId, handle, uri); + + if (DRM_NO_ERROR == result) { + ++mDecryptSessionId; + mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine); + break; + } + } + } + if (DRM_NO_ERROR != result) { + delete handle; handle = NULL; + LOGE("DrmManager::openDecryptSession: no capable plug-in found"); + } + return handle; +} + +status_t DrmManager::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + Mutex::Autolock _l(mDecryptLock); + status_t result = DRM_ERROR_UNKNOWN; + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + result = drmEngine->closeDecryptSession(uniqueId, decryptHandle); + if (DRM_NO_ERROR == result) { + mDecryptSessionMap.removeItem(decryptHandle->decryptId); + } + } + return result; +} + +status_t DrmManager::initializeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) { + status_t result = DRM_ERROR_UNKNOWN; + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + result = drmEngine->initializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo); + } + return result; +} + +status_t DrmManager::decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) { + status_t result = DRM_ERROR_UNKNOWN; + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + result = drmEngine->decrypt( + uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV); + } + return result; +} + +status_t DrmManager::finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + status_t result = DRM_ERROR_UNKNOWN; + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + result = drmEngine->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId); + } + return result; +} + +ssize_t DrmManager::pread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset) { + ssize_t result = DECRYPT_FILE_ERROR; + + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + result = drmEngine->pread(uniqueId, decryptHandle, buffer, numBytes, offset); + } + return result; +} + +String8 DrmManager::getSupportedPlugInId( + int uniqueId, const String8& path, const String8& mimeType) { + String8 plugInId(""); + + if (EMPTY_STRING != mimeType) { + plugInId = getSupportedPlugInId(mimeType); + } else { + plugInId = getSupportedPlugInIdFromPath(uniqueId, path); + } + return plugInId; +} + +String8 DrmManager::getSupportedPlugInId(const String8& mimeType) { + String8 plugInId(""); + + if (EMPTY_STRING != mimeType) { + for (unsigned int index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) { + const DrmSupportInfo& drmSupportInfo = mSupportInfoToPlugInIdMap.keyAt(index); + + if (drmSupportInfo.isSupportedMimeType(mimeType)) { + plugInId = mSupportInfoToPlugInIdMap.valueFor(drmSupportInfo); + break; + } + } + } + return plugInId; +} + +String8 DrmManager::getSupportedPlugInIdFromPath(int uniqueId, const String8& path) { + String8 plugInId(""); + const String8 fileSuffix = path.getPathExtension(); + + for (unsigned int index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) { + const DrmSupportInfo& drmSupportInfo = mSupportInfoToPlugInIdMap.keyAt(index); + + if (drmSupportInfo.isSupportedFileSuffix(fileSuffix)) { + String8 key = mSupportInfoToPlugInIdMap.valueFor(drmSupportInfo); + IDrmEngine& drmEngine = mPlugInManager.getPlugIn(key); + + if (drmEngine.canHandle(uniqueId, path)) { + plugInId = key; + break; + } + } + } + return plugInId; +} + +void DrmManager::onInfo(const DrmInfoEvent& event) { + Mutex::Autolock _l(mLock); + for (unsigned int index = 0; index < mServiceListeners.size(); index++) { + int uniqueId = mServiceListeners.keyAt(index); + + if (uniqueId == event.getUniqueId()) { + sp<IDrmServiceListener> serviceListener = mServiceListeners.valueFor(uniqueId); + serviceListener->notify(event); + } + } +} + diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp new file mode 100644 index 0000000..4dcfa72 --- /dev/null +++ b/drm/drmserver/DrmManagerService.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "DrmManagerService(Native)" +#include <utils/Log.h> + +#include <private/android_filesystem_config.h> + +#include <errno.h> +#include <utils/threads.h> +#include <binder/IServiceManager.h> +#include <binder/IPCThreadState.h> +#include <sys/stat.h> +#include "DrmManagerService.h" +#include "DrmManager.h" + +using namespace android; + +static Vector<uid_t> trustedUids; + +static bool isProtectedCallAllowed() { + // TODO + // Following implementation is just for reference. + // Each OEM manufacturer should implement/replace with their own solutions. + bool result = false; + + IPCThreadState* ipcState = IPCThreadState::self(); + uid_t uid = ipcState->getCallingUid(); + + for (unsigned int i = 0; i < trustedUids.size(); ++i) { + if (trustedUids[i] == uid) { + result = true; + break; + } + } + return result; +} + +void DrmManagerService::instantiate() { + LOGV("instantiate"); + defaultServiceManager()->addService(String16("drm.drmManager"), new DrmManagerService()); + + if (0 >= trustedUids.size()) { + // TODO + // Following implementation is just for reference. + // Each OEM manufacturer should implement/replace with their own solutions. + + // Add trusted uids here + trustedUids.push(AID_MEDIA); + } +} + +DrmManagerService::DrmManagerService() : + mDrmManager(NULL) { + LOGV("created"); + mDrmManager = new DrmManager(); + mDrmManager->loadPlugIns(); +} + +DrmManagerService::~DrmManagerService() { + LOGV("Destroyed"); + mDrmManager->unloadPlugIns(); + delete mDrmManager; mDrmManager = NULL; +} + +int DrmManagerService::addUniqueId(int uniqueId) { + return mDrmManager->addUniqueId(uniqueId); +} + +void DrmManagerService::removeUniqueId(int uniqueId) { + mDrmManager->removeUniqueId(uniqueId); +} + +void DrmManagerService::addClient(int uniqueId) { + mDrmManager->addClient(uniqueId); +} + +void DrmManagerService::removeClient(int uniqueId) { + mDrmManager->removeClient(uniqueId); +} + +status_t DrmManagerService::setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) { + LOGV("Entering setDrmServiceListener"); + mDrmManager->setDrmServiceListener(uniqueId, drmServiceListener); + return DRM_NO_ERROR; +} + +status_t DrmManagerService::installDrmEngine(int uniqueId, const String8& drmEngineFile) { + LOGV("Entering installDrmEngine"); + return mDrmManager->installDrmEngine(uniqueId, drmEngineFile); +} + +DrmConstraints* DrmManagerService::getConstraints( + int uniqueId, const String8* path, const int action) { + LOGV("Entering getConstraints from content"); + return mDrmManager->getConstraints(uniqueId, path, action); +} + +DrmMetadata* DrmManagerService::getMetadata(int uniqueId, const String8* path) { + LOGV("Entering getMetadata from content"); + return mDrmManager->getMetadata(uniqueId, path); +} + +bool DrmManagerService::canHandle(int uniqueId, const String8& path, const String8& mimeType) { + LOGV("Entering canHandle"); + return mDrmManager->canHandle(uniqueId, path, mimeType); +} + +DrmInfoStatus* DrmManagerService::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) { + LOGV("Entering processDrmInfo"); + return mDrmManager->processDrmInfo(uniqueId, drmInfo); +} + +DrmInfo* DrmManagerService::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) { + LOGV("Entering acquireDrmInfo"); + return mDrmManager->acquireDrmInfo(uniqueId, drmInfoRequest); +} + +status_t DrmManagerService::saveRights( + int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + LOGV("Entering saveRights"); + return mDrmManager->saveRights(uniqueId, drmRights, rightsPath, contentPath); +} + +String8 DrmManagerService::getOriginalMimeType(int uniqueId, const String8& path) { + LOGV("Entering getOriginalMimeType"); + return mDrmManager->getOriginalMimeType(uniqueId, path); +} + +int DrmManagerService::getDrmObjectType( + int uniqueId, const String8& path, const String8& mimeType) { + LOGV("Entering getDrmObjectType"); + return mDrmManager->getDrmObjectType(uniqueId, path, mimeType); +} + +int DrmManagerService::checkRightsStatus( + int uniqueId, const String8& path, int action) { + LOGV("Entering checkRightsStatus"); + return mDrmManager->checkRightsStatus(uniqueId, path, action); +} + +status_t DrmManagerService::consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) { + LOGV("Entering consumeRights"); + return mDrmManager->consumeRights(uniqueId, decryptHandle, action, reserve); +} + +status_t DrmManagerService::setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) { + LOGV("Entering setPlaybackStatus"); + return mDrmManager->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position); +} + +bool DrmManagerService::validateAction( + int uniqueId, const String8& path, + int action, const ActionDescription& description) { + LOGV("Entering validateAction"); + return mDrmManager->validateAction(uniqueId, path, action, description); +} + +status_t DrmManagerService::removeRights(int uniqueId, const String8& path) { + LOGV("Entering removeRights"); + return mDrmManager->removeRights(uniqueId, path); +} + +status_t DrmManagerService::removeAllRights(int uniqueId) { + LOGV("Entering removeAllRights"); + return mDrmManager->removeAllRights(uniqueId); +} + +int DrmManagerService::openConvertSession(int uniqueId, const String8& mimeType) { + LOGV("Entering openConvertSession"); + return mDrmManager->openConvertSession(uniqueId, mimeType); +} + +DrmConvertedStatus* DrmManagerService::convertData( + int uniqueId, int convertId, const DrmBuffer* inputData) { + LOGV("Entering convertData"); + return mDrmManager->convertData(uniqueId, convertId, inputData); +} + +DrmConvertedStatus* DrmManagerService::closeConvertSession(int uniqueId, int convertId) { + LOGV("Entering closeConvertSession"); + return mDrmManager->closeConvertSession(uniqueId, convertId); +} + +status_t DrmManagerService::getAllSupportInfo( + int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) { + LOGV("Entering getAllSupportInfo"); + return mDrmManager->getAllSupportInfo(uniqueId, length, drmSupportInfoArray); +} + +DecryptHandle* DrmManagerService::openDecryptSession( + int uniqueId, int fd, int offset, int length) { + LOGV("Entering DrmManagerService::openDecryptSession"); + if (isProtectedCallAllowed()) { + return mDrmManager->openDecryptSession(uniqueId, fd, offset, length); + } + + return NULL; +} + +DecryptHandle* DrmManagerService::openDecryptSession( + int uniqueId, const char* uri) { + LOGV("Entering DrmManagerService::openDecryptSession with uri"); + if (isProtectedCallAllowed()) { + return mDrmManager->openDecryptSession(uniqueId, uri); + } + + return NULL; +} + +status_t DrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + LOGV("Entering closeDecryptSession"); + return mDrmManager->closeDecryptSession(uniqueId, decryptHandle); +} + +status_t DrmManagerService::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) { + LOGV("Entering initializeDecryptUnit"); + return mDrmManager->initializeDecryptUnit(uniqueId,decryptHandle, decryptUnitId, headerInfo); +} + +status_t DrmManagerService::decrypt( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) { + LOGV("Entering decrypt"); + return mDrmManager->decrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV); +} + +status_t DrmManagerService::finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + LOGV("Entering finalizeDecryptUnit"); + return mDrmManager->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId); +} + +ssize_t DrmManagerService::pread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset) { + LOGV("Entering pread"); + return mDrmManager->pread(uniqueId, decryptHandle, buffer, numBytes, offset); +} + diff --git a/drm/drmserver/StringTokenizer.cpp b/drm/drmserver/StringTokenizer.cpp new file mode 100644 index 0000000..2130a00 --- /dev/null +++ b/drm/drmserver/StringTokenizer.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StringTokenizer.h" + +using namespace android; + +StringTokenizer::StringTokenizer(const String8& string, const String8& delimiter) { + splitString(string, delimiter); +} + +void StringTokenizer::splitString(const String8& string, const String8& delimiter) { + for (unsigned int i = 0; i < string.length(); i++) { + unsigned int position = string.find(delimiter.string(), i); + if (string.length() != position) { + String8 token(string.string()+i, position-i); + if (token.length()) { + mStringTokenizerVector.push(token); + i = position + delimiter.length() - 1; + } + } else { + mStringTokenizerVector.push(String8(string.string()+i, string.length()-i)); + break; + } + } +} + +StringTokenizer::Iterator StringTokenizer::iterator() { + return Iterator(this); +} + +StringTokenizer::Iterator::Iterator(const StringTokenizer::Iterator& iterator) : + mStringTokenizer(iterator.mStringTokenizer), + mIndex(iterator.mIndex) { +} + +StringTokenizer::Iterator& StringTokenizer::Iterator::operator=( + const StringTokenizer::Iterator& iterator) { + mStringTokenizer = iterator.mStringTokenizer; + mIndex = iterator.mIndex; + return *this; +} + +bool StringTokenizer::Iterator::hasNext() { + return mIndex < mStringTokenizer->mStringTokenizerVector.size(); +} + +String8& StringTokenizer::Iterator::next() { + String8& value = mStringTokenizer->mStringTokenizerVector.editItemAt(mIndex); + mIndex++; + return value; +} + diff --git a/drm/drmserver/main_drmserver.cpp b/drm/drmserver/main_drmserver.cpp new file mode 100644 index 0000000..6d10646 --- /dev/null +++ b/drm/drmserver/main_drmserver.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/types.h> +#include <unistd.h> +#include <grp.h> + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> +#include <private/android_filesystem_config.h> + +#include <DrmManagerService.h> + +using namespace android; + +int main(int argc, char** argv) +{ + sp<ProcessState> proc(ProcessState::self()); + sp<IServiceManager> sm = defaultServiceManager(); + LOGI("ServiceManager: %p", sm.get()); + DrmManagerService::instantiate(); + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); +} + diff --git a/drm/java/android/drm/DrmConvertedStatus.java b/drm/java/android/drm/DrmConvertedStatus.java new file mode 100644 index 0000000..f200552 --- /dev/null +++ b/drm/java/android/drm/DrmConvertedStatus.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +/** + * This is an entity class which wraps the status of the conversion, the converted + * data/checksum data and the offset. Offset is going to be used in the case of close + * session where the agent will inform where the header and body signature should be added + * + * As a result of {@link DrmManagerClient#convertData(int, byte [])} and + * {@link DrmManagerClient#closeConvertSession(int)} an instance of DrmConvertedStatus + * would be returned. + * + */ +public class DrmConvertedStatus { + // Should be in sync with DrmConvertedStatus.cpp + public static final int STATUS_OK = 1; + public static final int STATUS_INPUTDATA_ERROR = 2; + public static final int STATUS_ERROR = 3; + + public final int statusCode; + public final byte[] convertedData; + public final int offset; + + /** + * constructor to create DrmConvertedStatus object with given parameters + * + * @param _statusCode Status of the conversion + * @param _convertedData Converted data/checksum data + * @param _offset Offset value + */ + public DrmConvertedStatus(int _statusCode, byte[] _convertedData, int _offset) { + statusCode = _statusCode; + convertedData = _convertedData; + offset = _offset; + } +} + diff --git a/drm/java/android/drm/DrmErrorEvent.java b/drm/java/android/drm/DrmErrorEvent.java new file mode 100644 index 0000000..20fd8aa --- /dev/null +++ b/drm/java/android/drm/DrmErrorEvent.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +/** + * This is an entity class which would be passed to caller in + * {@link DrmManagerClient.OnErrorListener#onError(DrmManagerClient, DrmErrorEvent)} + * + */ +public class DrmErrorEvent extends DrmEvent { + /** + * TYPE_RIGHTS_NOT_INSTALLED, when something went wrong installing the rights. + */ + public static final int TYPE_RIGHTS_NOT_INSTALLED = 2001; + /** + * TYPE_RIGHTS_RENEWAL_NOT_ALLOWED, when the server rejects renewal of rights. + */ + public static final int TYPE_RIGHTS_RENEWAL_NOT_ALLOWED = 2002; + /** + * TYPE_NOT_SUPPORTED, when answer from server can not be handled by the native agent. + */ + public static final int TYPE_NOT_SUPPORTED = 2003; + /** + * TYPE_OUT_OF_MEMORY, when memory allocation fail during renewal. + * Can in the future perhaps be used to trigger garbage collector. + */ + public static final int TYPE_OUT_OF_MEMORY = 2004; + /** + * TYPE_NO_INTERNET_CONNECTION, when the Internet connection is missing and no attempt + * can be made to renew rights. + */ + public static final int TYPE_NO_INTERNET_CONNECTION = 2005; + /** + * TYPE_PROCESS_DRM_INFO_FAILED, when failed to process DrmInfo. + */ + public static final int TYPE_PROCESS_DRM_INFO_FAILED = 2006; + /** + * TYPE_REMOVE_ALL_RIGHTS_FAILED, when failed to remove all the rights objects + * associated with all DRM schemes. + */ + public static final int TYPE_REMOVE_ALL_RIGHTS_FAILED = 2007; + + /** + * constructor to create DrmErrorEvent object with given parameters + * + * @param uniqueId Unique session identifier + * @param type Type of information + * @param message Message description + */ + public DrmErrorEvent(int uniqueId, int type, String message) { + super(uniqueId, type, message); + } +} + diff --git a/drm/java/android/drm/DrmEvent.java b/drm/java/android/drm/DrmEvent.java new file mode 100644 index 0000000..f7bc5cd --- /dev/null +++ b/drm/java/android/drm/DrmEvent.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +/** + * This is the base class which would be used to notify the caller + * about any event occurred in DRM framework. + * + */ +public class DrmEvent { + /** + * Constant field signifies that all the rights information associated with + * all DRM schemes are removed successfully + */ + public static final int TYPE_ALL_RIGHTS_REMOVED = 1001; + /** + * Constant field signifies that given information is processed successfully + */ + public static final int TYPE_DRM_INFO_PROCESSED = 1002; + + public static final String DRM_INFO_STATUS_OBJECT = "drm_info_status_object"; + + private final int mUniqueId; + private final int mType; + private String mMessage = ""; + + /** + * constructor for DrmEvent class + * + * @param uniqueId Unique session identifier + * @param type Type of information + * @param message Message description + */ + protected DrmEvent(int uniqueId, int type, String message) { + mUniqueId = uniqueId; + mType = type; + + if (null != message) { + mMessage = message; + } + } + + /** + * Returns the Unique Id associated with this object + * + * @return Unique Id + */ + public int getUniqueId() { + return mUniqueId; + } + + /** + * Returns the Type of information associated with this object + * + * @return Type of information + */ + public int getType() { + return mType; + } + + /** + * Returns the message description associated with this object + * + * @return message description + */ + public String getMessage() { + return mMessage; + } +} + diff --git a/drm/java/android/drm/DrmInfo.java b/drm/java/android/drm/DrmInfo.java new file mode 100644 index 0000000..7d3fbf1 --- /dev/null +++ b/drm/java/android/drm/DrmInfo.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; + +/** + * This is an entity class in which necessary information required to transact + * between device and online DRM server is described. DRM Framework achieves + * server registration, license acquisition and any other server related transaction + * by passing an instance of this class to {@link DrmManagerClient#processDrmInfo(DrmInfo)}. + * + * Caller can retrieve the {@link DrmInfo} instance by using + * {@link DrmManagerClient#acquireDrmInfo(DrmInfoRequest)} + * by passing {@link DrmInfoRequest} instance. + * + */ +public class DrmInfo { + private byte[] mData; + private final String mMimeType; + private final int mInfoType; + // It would be used to add attributes specific to + // DRM scheme such as account id, path or multiple path's + private final HashMap<String, Object> mAttributes = new HashMap<String, Object>(); + + /** + * constructor to create DrmInfo object with given parameters + * + * @param infoType Type of information + * @param data Trigger data + * @param mimeType MIME type + */ + public DrmInfo(int infoType, byte[] data, String mimeType) { + mInfoType = infoType; + mMimeType = mimeType; + mData = data; + } + + /** + * constructor to create DrmInfo object with given parameters + * + * @param infoType Type of information + * @param path Trigger data + * @param mimeType MIME type + */ + public DrmInfo(int infoType, String path, String mimeType) { + mInfoType = infoType; + mMimeType = mimeType; + try { + mData = DrmUtils.readBytes(path); + } catch (IOException e) { + // As the given path is invalid, + // set mData = null, so that further processDrmInfo() + // call would fail with IllegalArgumentException because of mData = null + mData = null; + } + } + + /** + * Adds optional information as <key, value> pair to this object + * + * @param key Key to add + * @param value Value to add + * To put custom object into DrmInfo, custom object has to + * override toString() implementation. + */ + public void put(String key, Object value) { + mAttributes.put(key, value); + } + + /** + * Retrieves the value of given key, if not found returns null + * + * @param key Key whose value to be retrieved + * @return The value or null + */ + public Object get(String key) { + return mAttributes.get(key); + } + + /** + * Returns Iterator object to walk through the keys associated with this instance + * + * @return Iterator object + */ + public Iterator<String> keyIterator() { + return mAttributes.keySet().iterator(); + } + + /** + * Returns Iterator object to walk through the values associated with this instance + * + * @return Iterator object + */ + public Iterator<Object> iterator() { + return mAttributes.values().iterator(); + } + + /** + * Returns the trigger data associated with this object + * + * @return Trigger data + */ + public byte[] getData() { + return mData; + } + + /** + * Returns the mimetype associated with this object + * + * @return MIME type + */ + public String getMimeType() { + return mMimeType; + } + + /** + * Returns information type associated with this instance + * + * @return Information type + */ + public int getInfoType() { + return mInfoType; + } + + /** + * Returns whether this instance is valid or not + * + * @return + * true if valid + * false if invalid + */ + boolean isValid() { + return (null != mMimeType && !mMimeType.equals("") + && null != mData && mData.length > 0 && DrmInfoRequest.isValidType(mInfoType)); + } +} + diff --git a/drm/java/android/drm/DrmInfoEvent.java b/drm/java/android/drm/DrmInfoEvent.java new file mode 100644 index 0000000..a778e06 --- /dev/null +++ b/drm/java/android/drm/DrmInfoEvent.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +/** + * This is an entity class which would be passed to caller in + * {@link DrmManagerClient.OnInfoListener#onInfo(DrmManagerClient, DrmInfoEvent)} + * + */ +public class DrmInfoEvent extends DrmEvent { + /** + * TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT, when registration has been already done + * by another account ID. + */ + public static final int TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT = 1; + /** + * TYPE_REMOVE_RIGHTS, when the rights needs to be removed completely. + */ + public static final int TYPE_REMOVE_RIGHTS = 2; + /** + * TYPE_RIGHTS_INSTALLED, when the rights are downloaded and installed ok. + */ + public static final int TYPE_RIGHTS_INSTALLED = 3; + /** + * TYPE_WAIT_FOR_RIGHTS, rights object is on it's way to phone, + * wait before calling checkRights again. + */ + public static final int TYPE_WAIT_FOR_RIGHTS = 4; + /** + * TYPE_ACCOUNT_ALREADY_REGISTERED, when registration has been + * already done for the given account. + */ + public static final int TYPE_ACCOUNT_ALREADY_REGISTERED = 5; + + /** + * constructor to create DrmInfoEvent object with given parameters + * + * @param uniqueId Unique session identifier + * @param type Type of information + * @param message Message description + */ + public DrmInfoEvent(int uniqueId, int type, String message) { + super(uniqueId, type, message); + } +} + diff --git a/drm/java/android/drm/DrmInfoRequest.java b/drm/java/android/drm/DrmInfoRequest.java new file mode 100644 index 0000000..a5a799c --- /dev/null +++ b/drm/java/android/drm/DrmInfoRequest.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +import java.util.HashMap; +import java.util.Iterator; + +/** + * This is an entity class used to pass required parameters to get + * the necessary information to communicate with online DRM server + * + * An instance of this class is passed to {@link DrmManagerClient#acquireDrmInfo(DrmInfoRequest)} + * to get the instance of {@link DrmInfo} + * + */ +public class DrmInfoRequest { + // Changes in following constants should be in sync with DrmInfoRequest.cpp + /** + * Constants defines the type of {@link DrmInfoRequest} + */ + public static final int TYPE_REGISTRATION_INFO = 1; + public static final int TYPE_UNREGISTRATION_INFO = 2; + public static final int TYPE_RIGHTS_ACQUISITION_INFO = 3; + public static final int TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO = 4; + + /** + * Key to pass the unique id for the account or the user + */ + public static final String ACCOUNT_ID = "account_id"; + + /** + * Key to pass the unique id used for subscription + */ + public static final String SUBSCRIPTION_ID = "subscription_id"; + + private final int mInfoType; + private final String mMimeType; + private final HashMap<String, Object> mRequestInformation = new HashMap<String, Object>(); + + /** + * constructor to create DrmInfoRequest object with type and mimetype + * + * @param infoType Type of information + * @param mimeType MIME type + */ + public DrmInfoRequest(int infoType, String mimeType) { + mInfoType = infoType; + mMimeType = mimeType; + } + + /** + * Returns the mimetype associated with this object + * + * @return MIME type + */ + public String getMimeType() { + return mMimeType; + } + + /** + * Returns Information type associated with this instance + * + * @return Information type + */ + public int getInfoType() { + return mInfoType; + } + + /** + * Adds optional information as <key, value> pair to this object. + * + * @param key Key to add + * @param value Value to add + */ + public void put(String key, Object value) { + mRequestInformation.put(key, value); + } + + /** + * Retrieves the value of given key, if not found returns null + * + * @param key Key whose value to be retrieved + * @return The value or null + */ + public Object get(String key) { + return mRequestInformation.get(key); + } + + /** + * Returns Iterator object to walk through the keys associated with this instance + * + * @return Iterator object + */ + public Iterator<String> keyIterator() { + return mRequestInformation.keySet().iterator(); + } + + /** + * Returns Iterator object to walk through the values associated with this instance + * + * @return Iterator object + */ + public Iterator<Object> iterator() { + return mRequestInformation.values().iterator(); + } + + /** + * Returns whether this instance is valid or not + * + * @return + * true if valid + * false if invalid + */ + boolean isValid() { + return (null != mMimeType && !mMimeType.equals("") + && null != mRequestInformation && isValidType(mInfoType)); + } + + /* package */ static boolean isValidType(int infoType) { + boolean isValid = false; + + switch (infoType) { + case TYPE_REGISTRATION_INFO: + case TYPE_UNREGISTRATION_INFO: + case TYPE_RIGHTS_ACQUISITION_INFO: + case TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO: + isValid = true; + break; + } + return isValid; + } +} + diff --git a/drm/java/android/drm/DrmInfoStatus.java b/drm/java/android/drm/DrmInfoStatus.java new file mode 100644 index 0000000..b37ea51 --- /dev/null +++ b/drm/java/android/drm/DrmInfoStatus.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +/** + * This is an entity class which wraps the result of communication between device + * and online DRM server. + * + * As a result of {@link DrmManagerClient#processDrmInfo(DrmInfo)} an instance of DrmInfoStatus + * would be returned. This class holds {@link ProcessedData}, which could be used to instantiate + * {@link DrmRights#DrmRights(ProcessedData, String)} in license acquisition. + * + */ +public class DrmInfoStatus { + // Should be in sync with DrmInfoStatus.cpp + public static final int STATUS_OK = 1; + public static final int STATUS_ERROR = 2; + + public final int statusCode; + public final int infoType; + public final String mimeType; + public final ProcessedData data; + + /** + * constructor to create DrmInfoStatus object with given parameters + * + * @param _statusCode Status of the communication + * @param _infoType Type of the DRM information processed + * @param _data The processed data + * @param _mimeType MIME type + */ + public DrmInfoStatus(int _statusCode, int _infoType, ProcessedData _data, String _mimeType) { + statusCode = _statusCode; + infoType = _infoType; + data = _data; + mimeType = _mimeType; + } +} + diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java new file mode 100644 index 0000000..6caf678 --- /dev/null +++ b/drm/java/android/drm/DrmManagerClient.java @@ -0,0 +1,812 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteException; +import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.provider.MediaStore; +import android.util.Log; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Interface of DRM Framework. + * Java application will instantiate this class + * to access DRM agent through DRM Framework. + * + */ +public class DrmManagerClient { + /** + * Constant field signifies the success or no error occurred + */ + public static final int ERROR_NONE = 0; + /** + * Constant field signifies that error occurred and the reason is not known + */ + public static final int ERROR_UNKNOWN = -2000; + + private static final String TAG = "DrmManagerClient"; + + static { + // Load the respective library + System.loadLibrary("drmframework_jni"); + } + + /** + * Interface definition of a callback to be invoked to communicate + * some info and/or warning about DrmManagerClient. + */ + public interface OnInfoListener { + /** + * Called to indicate an info or a warning. + * + * @param client DrmManagerClient instance + * @param event instance which wraps reason and necessary information + */ + public void onInfo(DrmManagerClient client, DrmInfoEvent event); + } + + /** + * Interface definition of a callback to be invoked to communicate + * the result of time consuming APIs asynchronously + */ + public interface OnEventListener { + /** + * Called to indicate the result of asynchronous APIs. + * + * @param client DrmManagerClient instance + * @param event instance which wraps type and message + * @param attributes resultant values in key and value pair. + */ + public void onEvent(DrmManagerClient client, DrmEvent event, + HashMap<String, Object> attributes); + } + + /** + * Interface definition of a callback to be invoked to communicate + * the error occurred + */ + public interface OnErrorListener { + /** + * Called to indicate the error occurred. + * + * @param client DrmManagerClient instance + * @param event instance which wraps error type and message + */ + public void onError(DrmManagerClient client, DrmErrorEvent event); + } + + private static final int ACTION_REMOVE_ALL_RIGHTS = 1001; + private static final int ACTION_PROCESS_DRM_INFO = 1002; + + private int mUniqueId; + private int mNativeContext; + private Context mContext; + private InfoHandler mInfoHandler; + private EventHandler mEventHandler; + private OnInfoListener mOnInfoListener; + private OnEventListener mOnEventListener; + private OnErrorListener mOnErrorListener; + + private class EventHandler extends Handler { + + public EventHandler(Looper looper) { + super(looper); + } + + public void handleMessage(Message msg) { + DrmEvent event = null; + DrmErrorEvent error = null; + HashMap<String, Object> attributes = new HashMap<String, Object>(); + + switch(msg.what) { + case ACTION_PROCESS_DRM_INFO: { + final DrmInfo drmInfo = (DrmInfo) msg.obj; + DrmInfoStatus status = _processDrmInfo(mUniqueId, drmInfo); + if (null != status && DrmInfoStatus.STATUS_OK == status.statusCode) { + attributes.put(DrmEvent.DRM_INFO_STATUS_OBJECT, status); + event = new DrmEvent(mUniqueId, getEventType(status.infoType), null); + } else { + int infoType = (null != status) ? status.infoType : drmInfo.getInfoType(); + error = new DrmErrorEvent(mUniqueId, getErrorType(infoType), null); + } + break; + } + case ACTION_REMOVE_ALL_RIGHTS: { + if (ERROR_NONE == _removeAllRights(mUniqueId)) { + event = new DrmEvent(mUniqueId, DrmEvent.TYPE_ALL_RIGHTS_REMOVED, null); + } else { + error = new DrmErrorEvent(mUniqueId, + DrmErrorEvent.TYPE_REMOVE_ALL_RIGHTS_FAILED, null); + } + break; + } + default: + Log.e(TAG, "Unknown message type " + msg.what); + return; + } + if (null != mOnEventListener && null != event) { + mOnEventListener.onEvent(DrmManagerClient.this, event, attributes); + } + if (null != mOnErrorListener && null != error) { + mOnErrorListener.onError(DrmManagerClient.this, error); + } + } + } + + /** + * {@hide} + */ + public static void notify( + Object thisReference, int uniqueId, int infoType, String message) { + DrmManagerClient instance = (DrmManagerClient)((WeakReference)thisReference).get(); + + if (null != instance && null != instance.mInfoHandler) { + Message m = instance.mInfoHandler.obtainMessage( + InfoHandler.INFO_EVENT_TYPE, uniqueId, infoType, message); + instance.mInfoHandler.sendMessage(m); + } + } + + private class InfoHandler extends Handler { + public static final int INFO_EVENT_TYPE = 1; + + public InfoHandler(Looper looper) { + super(looper); + } + + public void handleMessage(Message msg) { + DrmInfoEvent event = null; + DrmErrorEvent error = null; + + switch (msg.what) { + case InfoHandler.INFO_EVENT_TYPE: + int uniqueId = msg.arg1; + int infoType = msg.arg2; + String message = msg.obj.toString(); + + switch (infoType) { + case DrmInfoEvent.TYPE_REMOVE_RIGHTS: { + try { + DrmUtils.removeFile(message); + } catch (IOException e) { + e.printStackTrace(); + } + event = new DrmInfoEvent(uniqueId, infoType, message); + break; + } + case DrmInfoEvent.TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT: { + event = new DrmInfoEvent(uniqueId, infoType, message); + break; + } + default: + error = new DrmErrorEvent(uniqueId, infoType, message); + break; + } + + if (null != mOnInfoListener && null != event) { + mOnInfoListener.onInfo(DrmManagerClient.this, event); + } + if (null != mOnErrorListener && null != error) { + mOnErrorListener.onError(DrmManagerClient.this, error); + } + return; + default: + Log.e(TAG, "Unknown message type " + msg.what); + return; + } + } + } + + /** + * To instantiate DrmManagerClient + * + * @param context context of the caller + */ + public DrmManagerClient(Context context) { + mContext = context; + + HandlerThread infoThread = new HandlerThread("DrmManagerClient.InfoHandler"); + infoThread.start(); + mInfoHandler = new InfoHandler(infoThread.getLooper()); + + HandlerThread eventThread = new HandlerThread("DrmManagerClient.EventHandler"); + eventThread.start(); + mEventHandler = new EventHandler(eventThread.getLooper()); + + // save the unique id + mUniqueId = hashCode(); + + _initialize(mUniqueId, new WeakReference<DrmManagerClient>(this)); + } + + protected void finalize() { + _finalize(mUniqueId); + } + + /** + * Register a callback to be invoked when the caller required to receive + * supplementary information. + * + * @param infoListener + */ + public synchronized void setOnInfoListener(OnInfoListener infoListener) { + if (null != infoListener) { + mOnInfoListener = infoListener; + } + } + + /** + * Register a callback to be invoked when the caller required to receive + * the result of asynchronous APIs. + * + * @param eventListener + */ + public synchronized void setOnEventListener(OnEventListener eventListener) { + if (null != eventListener) { + mOnEventListener = eventListener; + } + } + + /** + * Register a callback to be invoked when the caller required to receive + * error result of asynchronous APIs. + * + * @param errorListener + */ + public synchronized void setOnErrorListener(OnErrorListener errorListener) { + if (null != errorListener) { + mOnErrorListener = errorListener; + } + } + + /** + * Retrieves informations about all the plug-ins registered with DrmFramework. + * + * @return Array of DrmEngine plug-in strings + */ + public String[] getAvailableDrmEngines() { + DrmSupportInfo[] supportInfos = _getAllSupportInfo(mUniqueId); + ArrayList<String> descriptions = new ArrayList<String>(); + + for (int i = 0; i < supportInfos.length; i++) { + descriptions.add(supportInfos[i].getDescriprition()); + } + + String[] drmEngines = new String[descriptions.size()]; + return descriptions.toArray(drmEngines); + } + + /** + * Get constraints information evaluated from DRM content + * + * @param path Content path from where DRM constraints would be retrieved. + * @param action Actions defined in {@link DrmStore.Action} + * @return ContentValues instance in which constraints key-value pairs are embedded + * or null in case of failure + */ + public ContentValues getConstraints(String path, int action) { + if (null == path || path.equals("") || !DrmStore.Action.isValid(action)) { + throw new IllegalArgumentException("Given usage or path is invalid/null"); + } + return _getConstraints(mUniqueId, path, action); + } + + /** + * Get metadata information from DRM content + * + * @param path Content path from where DRM metadata would be retrieved. + * @return ContentValues instance in which metadata key-value pairs are embedded + * or null in case of failure + */ + public ContentValues getMetadata(String path) { + if (null == path || path.equals("")) { + throw new IllegalArgumentException("Given path is invalid/null"); + } + return _getMetadata(mUniqueId, path); + } + + /** + * Get constraints information evaluated from DRM content + * + * @param uri Content URI from where DRM constraints would be retrieved. + * @param action Actions defined in {@link DrmStore.Action} + * @return ContentValues instance in which constraints key-value pairs are embedded + * or null in case of failure + */ + public ContentValues getConstraints(Uri uri, int action) { + if (null == uri || Uri.EMPTY == uri) { + throw new IllegalArgumentException("Uri should be non null"); + } + return getConstraints(convertUriToPath(uri), action); + } + + /** + * Get metadata information from DRM content + * + * @param uri Content URI from where DRM metadata would be retrieved. + * @return ContentValues instance in which metadata key-value pairs are embedded + * or null in case of failure + */ + public ContentValues getMetadata(Uri uri) { + if (null == uri || Uri.EMPTY == uri) { + throw new IllegalArgumentException("Uri should be non null"); + } + return getMetadata(convertUriToPath(uri)); + } + + /** + * Save DRM rights to specified rights path + * and make association with content path. + * + * <p class="note">In case of OMA or WM-DRM, rightsPath and contentPath could be null.</p> + * + * @param drmRights DrmRights to be saved + * @param rightsPath File path where rights to be saved + * @param contentPath File path where content was saved + * @return + * ERROR_NONE for success + * ERROR_UNKNOWN for failure + * @throws IOException if failed to save rights information in the given path + */ + public int saveRights( + DrmRights drmRights, String rightsPath, String contentPath) throws IOException { + if (null == drmRights || !drmRights.isValid()) { + throw new IllegalArgumentException("Given drmRights or contentPath is not valid"); + } + if (null != rightsPath && !rightsPath.equals("")) { + DrmUtils.writeToFile(rightsPath, drmRights.getData()); + } + return _saveRights(mUniqueId, drmRights, rightsPath, contentPath); + } + + /** + * Install new DRM Engine Plug-in at the runtime + * + * @param engineFilePath Path of the plug-in file to be installed + * {@hide} + */ + public void installDrmEngine(String engineFilePath) { + if (null == engineFilePath || engineFilePath.equals("")) { + throw new IllegalArgumentException( + "Given engineFilePath: "+ engineFilePath + "is not valid"); + } + _installDrmEngine(mUniqueId, engineFilePath); + } + + /** + * Check whether the given mimetype or path can be handled. + * + * @param path Path of the content to be handled + * @param mimeType Mimetype of the object to be handled + * @return + * true - if the given mimeType or path can be handled + * false - cannot be handled. + */ + public boolean canHandle(String path, String mimeType) { + if ((null == path || path.equals("")) && (null == mimeType || mimeType.equals(""))) { + throw new IllegalArgumentException("Path or the mimetype should be non null"); + } + return _canHandle(mUniqueId, path, mimeType); + } + + /** + * Check whether the given mimetype or uri can be handled. + * + * @param uri Content URI of the data to be handled. + * @param mimeType Mimetype of the object to be handled + * @return + * true - if the given mimeType or path can be handled + * false - cannot be handled. + */ + public boolean canHandle(Uri uri, String mimeType) { + if ((null == uri || Uri.EMPTY == uri) && (null == mimeType || mimeType.equals(""))) { + throw new IllegalArgumentException("Uri or the mimetype should be non null"); + } + return canHandle(convertUriToPath(uri), mimeType); + } + + /** + * Executes given drm information based on its type + * + * @param drmInfo Information needs to be processed + * @return + * ERROR_NONE for success + * ERROR_UNKNOWN for failure + */ + public int processDrmInfo(DrmInfo drmInfo) { + if (null == drmInfo || !drmInfo.isValid()) { + throw new IllegalArgumentException("Given drmInfo is invalid/null"); + } + int result = ERROR_UNKNOWN; + if (null != mEventHandler) { + Message msg = mEventHandler.obtainMessage(ACTION_PROCESS_DRM_INFO, drmInfo); + result = (mEventHandler.sendMessage(msg)) ? ERROR_NONE : result; + } + return result; + } + + /** + * Retrieves necessary information for register, unregister or rights acquisition. + * + * @param drmInfoRequest Request information to retrieve drmInfo + * @return DrmInfo Instance as a result of processing given input + */ + public DrmInfo acquireDrmInfo(DrmInfoRequest drmInfoRequest) { + if (null == drmInfoRequest || !drmInfoRequest.isValid()) { + throw new IllegalArgumentException("Given drmInfoRequest is invalid/null"); + } + return _acquireDrmInfo(mUniqueId, drmInfoRequest); + } + + /** + * Executes given DrmInfoRequest and returns the rights information asynchronously. + * This is a utility API which consists of {@link #acquireDrmInfo(DrmInfoRequest)} + * and {@link #processDrmInfo(DrmInfo)}. + * It can be used if selected DRM agent can work with this combined sequences. + * In case of some DRM schemes, such as OMA DRM, application needs to invoke + * {@link #acquireDrmInfo(DrmInfoRequest)} and {@link #processDrmInfo(DrmInfo)}, separately. + * + * @param drmInfoRequest Request information to retrieve drmInfo + * @return + * ERROR_NONE for success + * ERROR_UNKNOWN for failure + */ + public int acquireRights(DrmInfoRequest drmInfoRequest) { + DrmInfo drmInfo = acquireDrmInfo(drmInfoRequest); + return processDrmInfo(drmInfo); + } + + /** + * Retrieves the type of the protected object (content, rights, etc..) + * using specified path or mimetype. At least one parameter should be non null + * to retrieve DRM object type + * + * @param path Path of the content or null. + * @param mimeType Mimetype of the content or null. + * @return Type of the DRM content. + * @see DrmStore.DrmObjectType + */ + public int getDrmObjectType(String path, String mimeType) { + if ((null == path || path.equals("")) && (null == mimeType || mimeType.equals(""))) { + throw new IllegalArgumentException("Path or the mimetype should be non null"); + } + return _getDrmObjectType(mUniqueId, path, mimeType); + } + + /** + * Retrieves the type of the protected object (content, rights, etc..) + * using specified uri or mimetype. At least one parameter should be non null + * to retrieve DRM object type + * + * @param uri The content URI of the data + * @param mimeType Mimetype of the content or null. + * @return Type of the DRM content. + * @see DrmStore.DrmObjectType + */ + public int getDrmObjectType(Uri uri, String mimeType) { + if ((null == uri || Uri.EMPTY == uri) && (null == mimeType || mimeType.equals(""))) { + throw new IllegalArgumentException("Uri or the mimetype should be non null"); + } + String path = ""; + try { + path = convertUriToPath(uri); + } catch (Exception e) { + // Even uri is invalid the mimetype shall be valid, so allow to proceed further. + Log.w(TAG, "Given Uri could not be found in media store"); + } + return getDrmObjectType(path, mimeType); + } + + /** + * Retrieves the mime type embedded inside the original content + * + * @param path Path of the protected content + * @return Mimetype of the original content, such as "video/mpeg" + */ + public String getOriginalMimeType(String path) { + if (null == path || path.equals("")) { + throw new IllegalArgumentException("Given path should be non null"); + } + return _getOriginalMimeType(mUniqueId, path); + } + + /** + * Retrieves the mime type embedded inside the original content + * + * @param uri The content URI of the data + * @return Mimetype of the original content, such as "video/mpeg" + */ + public String getOriginalMimeType(Uri uri) { + if (null == uri || Uri.EMPTY == uri) { + throw new IllegalArgumentException("Given uri is not valid"); + } + return getOriginalMimeType(convertUriToPath(uri)); + } + + /** + * Check whether the given content has valid rights or not + * + * @param path Path of the protected content + * @return Status of the rights for the protected content + * @see DrmStore.RightsStatus + */ + public int checkRightsStatus(String path) { + return checkRightsStatus(path, DrmStore.Action.DEFAULT); + } + + /** + * Check whether the given content has valid rights or not + * + * @param uri The content URI of the data + * @return Status of the rights for the protected content + * @see DrmStore.RightsStatus + */ + public int checkRightsStatus(Uri uri) { + if (null == uri || Uri.EMPTY == uri) { + throw new IllegalArgumentException("Given uri is not valid"); + } + return checkRightsStatus(convertUriToPath(uri)); + } + + /** + * Check whether the given content has valid rights or not for specified action. + * + * @param path Path of the protected content + * @param action Action to perform + * @return Status of the rights for the protected content + * @see DrmStore.RightsStatus + */ + public int checkRightsStatus(String path, int action) { + if (null == path || path.equals("") || !DrmStore.Action.isValid(action)) { + throw new IllegalArgumentException("Given path or action is not valid"); + } + return _checkRightsStatus(mUniqueId, path, action); + } + + /** + * Check whether the given content has valid rights or not for specified action. + * + * @param uri The content URI of the data + * @param action Action to perform + * @return Status of the rights for the protected content + * @see DrmStore.RightsStatus + */ + public int checkRightsStatus(Uri uri, int action) { + if (null == uri || Uri.EMPTY == uri) { + throw new IllegalArgumentException("Given uri is not valid"); + } + return checkRightsStatus(convertUriToPath(uri), action); + } + + /** + * Removes the rights associated with the given protected content + * + * @param path Path of the protected content + * @return + * ERROR_NONE for success + * ERROR_UNKNOWN for failure + */ + public int removeRights(String path) { + if (null == path || path.equals("")) { + throw new IllegalArgumentException("Given path should be non null"); + } + return _removeRights(mUniqueId, path); + } + + /** + * Removes the rights associated with the given protected content + * + * @param uri The content URI of the data + * @return + * ERROR_NONE for success + * ERROR_UNKNOWN for failure + */ + public int removeRights(Uri uri) { + if (null == uri || Uri.EMPTY == uri) { + throw new IllegalArgumentException("Given uri is not valid"); + } + return removeRights(convertUriToPath(uri)); + } + + /** + * Removes all the rights information of every plug-in associated with + * DRM framework. Will be used in master reset + * + * @return + * ERROR_NONE for success + * ERROR_UNKNOWN for failure + */ + public int removeAllRights() { + int result = ERROR_UNKNOWN; + if (null != mEventHandler) { + Message msg = mEventHandler.obtainMessage(ACTION_REMOVE_ALL_RIGHTS); + result = (mEventHandler.sendMessage(msg)) ? ERROR_NONE : result; + } + return result; + } + + /** + * This API is for Forward Lock based DRM scheme. + * Each time the application tries to download a new DRM file + * which needs to be converted, then the application has to + * begin with calling this API. + * + * @param mimeType Description/MIME type of the input data packet + * @return convert ID which will be used for maintaining convert session. + */ + public int openConvertSession(String mimeType) { + if (null == mimeType || mimeType.equals("")) { + throw new IllegalArgumentException("Path or the mimeType should be non null"); + } + return _openConvertSession(mUniqueId, mimeType); + } + + /** + * Accepts and converts the input data which is part of DRM file. + * The resultant converted data and the status is returned in the DrmConvertedInfo + * object. This method will be called each time there are new block + * of data received by the application. + * + * @param convertId Handle for the convert session + * @param inputData Input Data which need to be converted + * @return Return object contains the status of the data conversion, + * the output converted data and offset. In this case the + * application will ignore the offset information. + */ + public DrmConvertedStatus convertData(int convertId, byte[] inputData) { + if (null == inputData || 0 >= inputData.length) { + throw new IllegalArgumentException("Given inputData should be non null"); + } + return _convertData(mUniqueId, convertId, inputData); + } + + /** + * Informs the Drm Agent when there is no more data which need to be converted + * or when an error occurs. Upon successful conversion of the complete data, + * the agent will inform that where the header and body signature + * should be added. This signature appending is needed to integrity + * protect the converted file. + * + * @param convertId Handle for the convert session + * @return Return object contains the status of the data conversion, + * the header and body signature data. It also informs + * the application on which offset these signature data should be appended. + */ + public DrmConvertedStatus closeConvertSession(int convertId) { + return _closeConvertSession(mUniqueId, convertId); + } + + private int getEventType(int infoType) { + int eventType = -1; + + switch (infoType) { + case DrmInfoRequest.TYPE_REGISTRATION_INFO: + case DrmInfoRequest.TYPE_UNREGISTRATION_INFO: + case DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_INFO: + eventType = DrmEvent.TYPE_DRM_INFO_PROCESSED; + break; + } + return eventType; + } + + private int getErrorType(int infoType) { + int error = -1; + + switch (infoType) { + case DrmInfoRequest.TYPE_REGISTRATION_INFO: + case DrmInfoRequest.TYPE_UNREGISTRATION_INFO: + case DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_INFO: + error = DrmErrorEvent.TYPE_PROCESS_DRM_INFO_FAILED; + break; + } + return error; + } + + /** + * This method expects uri in the following format + * content://media/<table_name>/<row_index> (or) + * file://sdcard/test.mp4 + * + * Here <table_name> shall be "video" or "audio" or "images" + * <row_index> the index of the content in given table + */ + private String convertUriToPath(Uri uri) { + String path = null; + if (null != uri) { + String scheme = uri.getScheme(); + if (null == scheme || scheme.equals("") || + scheme.equals(ContentResolver.SCHEME_FILE)) { + path = uri.getPath(); + } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) { + String[] projection = new String[] {MediaStore.MediaColumns.DATA}; + Cursor cursor = null; + try { + cursor = mContext.getContentResolver().query(uri, projection, null, + null, null); + if (null == cursor || 0 == cursor.getCount() || !cursor.moveToFirst()) { + throw new IllegalArgumentException("Given Uri could not be found" + + " in media store"); + } + int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA); + path = cursor.getString(pathIndex); + } catch (SQLiteException e) { + throw new IllegalArgumentException("Given Uri is not formatted in a way " + + "so that it can be found in media store."); + } finally { + if (null != cursor) { + cursor.close(); + } + } + } else { + throw new IllegalArgumentException("Given Uri scheme is not supported"); + } + } + return path; + } + + // private native interfaces + private native void _initialize(int uniqueId, Object weak_this); + + private native void _finalize(int uniqueId); + + private native void _installDrmEngine(int uniqueId, String engineFilepath); + + private native ContentValues _getConstraints(int uniqueId, String path, int usage); + + private native ContentValues _getMetadata(int uniqueId, String path); + + private native boolean _canHandle(int uniqueId, String path, String mimeType); + + private native DrmInfoStatus _processDrmInfo(int uniqueId, DrmInfo drmInfo); + + private native DrmInfo _acquireDrmInfo(int uniqueId, DrmInfoRequest drmInfoRequest); + + private native int _saveRights( + int uniqueId, DrmRights drmRights, String rightsPath, String contentPath); + + private native int _getDrmObjectType(int uniqueId, String path, String mimeType); + + private native String _getOriginalMimeType(int uniqueId, String path); + + private native int _checkRightsStatus(int uniqueId, String path, int action); + + private native int _removeRights(int uniqueId, String path); + + private native int _removeAllRights(int uniqueId); + + private native int _openConvertSession(int uniqueId, String mimeType); + + private native DrmConvertedStatus _convertData( + int uniqueId, int convertId, byte[] inputData); + + private native DrmConvertedStatus _closeConvertSession(int uniqueId, int convertId); + + private native DrmSupportInfo[] _getAllSupportInfo(int uniqueId); +} + diff --git a/drm/java/android/drm/DrmRights.java b/drm/java/android/drm/DrmRights.java new file mode 100644 index 0000000..103af07 --- /dev/null +++ b/drm/java/android/drm/DrmRights.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +import java.io.File; +import java.io.IOException; + +/** + * This is an entity class which wraps the license information which was + * retrieved from the online DRM server. + * + * Caller can instantiate {@link DrmRights} by + * invoking {@link DrmRights#DrmRights(ProcessedData, String)} + * constructor by using the result of {@link DrmManagerClient#processDrmInfo(DrmInfo)} interface. + * Caller can also instantiate {@link DrmRights} using the file path + * which contains rights information. + * + */ +public class DrmRights { + private byte[] mData; + private String mMimeType; + private String mAccountId = "_NO_USER"; + private String mSubscriptionId = ""; + + /** + * constructor to create DrmRights object with given parameters + * + * @param rightsFilePath Path of the file containing rights data + * @param mimeType MIME type + */ + public DrmRights(String rightsFilePath, String mimeType) { + File file = new File(rightsFilePath); + instantiate(file, mimeType); + } + + /** + * constructor to create DrmRights object with given parameters + * + * @param rightsFilePath Path of the file containing rights data + * @param mimeType MIME type + * @param accountId Account Id of the user + */ + public DrmRights(String rightsFilePath, String mimeType, String accountId) { + this(rightsFilePath, mimeType); + + if (null != accountId && !accountId.equals("")) { + mAccountId = accountId; + } + } + + /** + * constructor to create DrmRights object with given parameters + * + * @param rightsFilePath Path of the file containing rights data + * @param mimeType MIME type + * @param accountId Account Id of the user + * @param subscriptionId Subscription Id of the user + */ + public DrmRights( + String rightsFilePath, String mimeType, String accountId, String subscriptionId) { + this(rightsFilePath, mimeType); + + if (null != accountId && !accountId.equals("")) { + mAccountId = accountId; + } + + if (null != subscriptionId && !subscriptionId.equals("")) { + mSubscriptionId = subscriptionId; + } + } + + /** + * constructor to create DrmRights object with given parameters + * + * @param rightsFile File containing rights data + * @param mimeType MIME type + */ + public DrmRights(File rightsFile, String mimeType) { + instantiate(rightsFile, mimeType); + } + + private void instantiate(File rightsFile, String mimeType) { + try { + mData = DrmUtils.readBytes(rightsFile); + } catch (IOException e) { + e.printStackTrace(); + } + + mMimeType = mimeType; + } + + /** + * constructor to create DrmRights object with given parameters + * The user can pass String or binary data<p> + * Usage:<p> + * i) String(e.g. data is instance of String):<br> + * - new DrmRights(data.getBytes(), mimeType)<p> + * ii) Binary data<br> + * - new DrmRights(binaryData[], mimeType)<br> + * + * @param data Processed data + * @param mimeType MIME type + */ + public DrmRights(ProcessedData data, String mimeType) { + mData = data.getData(); + + String accountId = data.getAccountId(); + if (null != accountId && !accountId.equals("")) { + mAccountId = accountId; + } + + String subscriptionId = data.getSubscriptionId(); + if (null != subscriptionId && !subscriptionId.equals("")) { + mSubscriptionId = subscriptionId; + } + + mMimeType = mimeType; + } + + /** + * Returns the rights data associated with this object + * + * @return Rights data + */ + public byte[] getData() { + return mData; + } + + /** + * Returns the mimetype associated with this object + * + * @return MIME type + */ + public String getMimeType() { + return mMimeType; + } + + /** + * Returns the account-id associated with this object + * + * @return Account Id + */ + public String getAccountId() { + return mAccountId; + } + + /** + * Returns the subscription-id associated with this object + * + * @return Subscription Id + */ + public String getSubscriptionId() { + return mSubscriptionId; + } + + /** + * Returns whether this instance is valid or not + * + * @return + * true if valid + * false if invalid + */ + /*package*/ boolean isValid() { + return (null != mMimeType && !mMimeType.equals("") + && null != mData && mData.length > 0); + } +} + diff --git a/drm/java/android/drm/DrmStore.java b/drm/java/android/drm/DrmStore.java new file mode 100644 index 0000000..44df90c --- /dev/null +++ b/drm/java/android/drm/DrmStore.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +/** + * This class defines all the constants used by DRM framework + * + */ +public class DrmStore { + /** + * Columns representing drm constraints + */ + public interface ConstraintsColumns { + /** + * The max repeat count + * <P>Type: INTEGER</P> + */ + public static final String MAX_REPEAT_COUNT = "max_repeat_count"; + + /** + * The remaining repeat count + * <P>Type: INTEGER</P> + */ + public static final String REMAINING_REPEAT_COUNT = "remaining_repeat_count"; + + /** + * The time before which the protected file can not be played/viewed + * <P>Type: TEXT</P> + */ + public static final String LICENSE_START_TIME = "license_start_time"; + + /** + * The time after which the protected file can not be played/viewed + * <P>Type: TEXT</P> + */ + public static final String LICENSE_EXPIRY_TIME = "license_expiry_time"; + + /** + * The available time for license + * <P>Type: TEXT</P> + */ + public static final String LICENSE_AVAILABLE_TIME = "license_available_time"; + + /** + * The data stream for extended metadata + * <P>Type: TEXT</P> + */ + public static final String EXTENDED_METADATA = "extended_metadata"; + } + + /** + * Defines constants related to DRM types + */ + public static class DrmObjectType { + /** + * Field specifies the unknown type + */ + public static final int UNKNOWN = 0x00; + /** + * Field specifies the protected content type + */ + public static final int CONTENT = 0x01; + /** + * Field specifies the rights information + */ + public static final int RIGHTS_OBJECT = 0x02; + /** + * Field specifies the trigger information + */ + public static final int TRIGGER_OBJECT = 0x03; + } + + /** + * Defines constants related to playback + */ + public static class Playback { + /** + * Constant field signifies playback start + */ + public static final int START = 0x00; + /** + * Constant field signifies playback stop + */ + public static final int STOP = 0x01; + /** + * Constant field signifies playback paused + */ + public static final int PAUSE = 0x02; + /** + * Constant field signifies playback resumed + */ + public static final int RESUME = 0x03; + + /* package */ static boolean isValid(int playbackStatus) { + boolean isValid = false; + + switch (playbackStatus) { + case START: + case STOP: + case PAUSE: + case RESUME: + isValid = true; + } + return isValid; + } + } + + /** + * Defines actions that can be performed on protected content + */ + public static class Action { + /** + * Constant field signifies that the default action + */ + public static final int DEFAULT = 0x00; + /** + * Constant field signifies that the content can be played + */ + public static final int PLAY = 0x01; + /** + * Constant field signifies that the content can be set as ring tone + */ + public static final int RINGTONE = 0x02; + /** + * Constant field signifies that the content can be transfered + */ + public static final int TRANSFER = 0x03; + /** + * Constant field signifies that the content can be set as output + */ + public static final int OUTPUT = 0x04; + /** + * Constant field signifies that preview is allowed + */ + public static final int PREVIEW = 0x05; + /** + * Constant field signifies that the content can be executed + */ + public static final int EXECUTE = 0x06; + /** + * Constant field signifies that the content can displayed + */ + public static final int DISPLAY = 0x07; + + /* package */ static boolean isValid(int action) { + boolean isValid = false; + + switch (action) { + case DEFAULT: + case PLAY: + case RINGTONE: + case TRANSFER: + case OUTPUT: + case PREVIEW: + case EXECUTE: + case DISPLAY: + isValid = true; + } + return isValid; + } + } + + /** + * Defines constants related to status of the rights + */ + public static class RightsStatus { + /** + * Constant field signifies that the rights are valid + */ + public static final int RIGHTS_VALID = 0x00; + /** + * Constant field signifies that the rights are invalid + */ + public static final int RIGHTS_INVALID = 0x01; + /** + * Constant field signifies that the rights are expired for the content + */ + public static final int RIGHTS_EXPIRED = 0x02; + /** + * Constant field signifies that the rights are not acquired for the content + */ + public static final int RIGHTS_NOT_ACQUIRED = 0x03; + } +} + diff --git a/drm/java/android/drm/DrmSupportInfo.java b/drm/java/android/drm/DrmSupportInfo.java new file mode 100644 index 0000000..0886af8 --- /dev/null +++ b/drm/java/android/drm/DrmSupportInfo.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * This is an entity class which wraps the capability of each plug-in, + * such as mimetype's and file suffixes it could handle. + * + * Plug-in developer could return the capability of the plugin by passing + * {@link DrmSupportInfo} instance. + * + */ +public class DrmSupportInfo { + private final ArrayList<String> mFileSuffixList = new ArrayList<String>(); + private final ArrayList<String> mMimeTypeList = new ArrayList<String>(); + private String mDescription = ""; + + /** + * Add the mime-type to the support info such that respective plug-in is + * capable of handling the given mime-type. + * + * @param mimeType MIME type + */ + public void addMimeType(String mimeType) { + mMimeTypeList.add(mimeType); + } + + /** + * Add the file suffix to the support info such that respective plug-in is + * capable of handling the given file suffix. + * + * @param fileSuffix File suffix which can be handled + */ + public void addFileSuffix(String fileSuffix) { + mFileSuffixList.add(fileSuffix); + } + + /** + * Returns the iterator to walk to through mime types of this object + * + * @return Iterator object + */ + public Iterator<String> getMimeTypeIterator() { + return mMimeTypeList.iterator(); + } + + /** + * Returns the iterator to walk to through file suffixes of this object + * + * @return Iterator object + */ + public Iterator<String> getFileSuffixIterator() { + return mFileSuffixList.iterator(); + } + + /** + * Set the unique description about the plugin + * + * @param description Unique description + */ + public void setDescription(String description) { + if (null != description) { + mDescription = description; + } + } + + /** + * Returns the unique description associated with the plugin + * + * @return Unique description + */ + public String getDescriprition() { + return mDescription; + } + + /** + * Overridden hash code implementation + * + * @return Hash code value + */ + public int hashCode() { + return mFileSuffixList.hashCode() + mMimeTypeList.hashCode() + mDescription.hashCode(); + } + + /** + * Overridden equals implementation + * + * @param object The object to be compared + * @return + * true if equal + * false if not equal + */ + public boolean equals(Object object) { + boolean result = false; + + if (object instanceof DrmSupportInfo) { + result = mFileSuffixList.equals(((DrmSupportInfo) object).mFileSuffixList) && + mMimeTypeList.equals(((DrmSupportInfo) object).mMimeTypeList) && + mDescription.equals(((DrmSupportInfo) object).mDescription); + } + return result; + } + + /** + * Returns whether given mime-type is supported or not + * + * @param mimeType MIME type + * @return + * true if mime type is supported + * false if mime type is not supported + */ + /* package */ boolean isSupportedMimeType(String mimeType) { + if (null != mimeType && !mimeType.equals("")) { + for (int i = 0; i < mMimeTypeList.size(); i++) { + String completeMimeType = mMimeTypeList.get(i); + if (completeMimeType.startsWith(mimeType)) { + return true; + } + } + } + return false; + } + + /** + * Returns whether given file suffix is supported or not + * + * @param fileSuffix File suffix + * @return + * true - if file suffix is supported + * false - if file suffix is not supported + */ + /* package */ boolean isSupportedFileSuffix(String fileSuffix) { + return mFileSuffixList.contains(fileSuffix); + } +} + diff --git a/drm/java/android/drm/DrmUtils.java b/drm/java/android/drm/DrmUtils.java new file mode 100644 index 0000000..8903485 --- /dev/null +++ b/drm/java/android/drm/DrmUtils.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Iterator; + +/** + * The utility class used in the DRM Framework. This inclueds APIs for file operations + * and ExtendedMetadataParser for parsing extended metadata BLOB in DRM constraints. + * + */ +public class DrmUtils { + /* Should be used when we need to read from local file */ + /* package */ static byte[] readBytes(String path) throws IOException { + File file = new File(path); + return readBytes(file); + } + + /* Should be used when we need to read from local file */ + /* package */ static byte[] readBytes(File file) throws IOException { + FileInputStream inputStream = new FileInputStream(file); + BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); + byte[] data = null; + + try { + int length = bufferedStream.available(); + if (length > 0) { + data = new byte[length]; + // read the entire data + bufferedStream.read(data); + } + } finally { + quiteDispose(bufferedStream); + quiteDispose(inputStream); + } + return data; + } + + /* package */ static void writeToFile(final String path, byte[] data) throws IOException { + /* check for invalid inputs */ + FileOutputStream outputStream = null; + + if (null != path && null != data) { + try { + outputStream = new FileOutputStream(path); + outputStream.write(data); + } finally { + quiteDispose(outputStream); + } + } + } + + /* package */ static void removeFile(String path) throws IOException { + File file = new File(path); + file.delete(); + } + + private static void quiteDispose(InputStream stream) { + try { + if (null != stream) { + stream.close(); + } + } catch (IOException e) { + // no need to care, at least as of now + } + } + + private static void quiteDispose(OutputStream stream) { + try { + if (null != stream) { + stream.close(); + } + } catch (IOException e) { + // no need to care + } + } + + /** + * Get an instance of ExtendedMetadataParser to be used for parsing + * extended metadata BLOB in DRM constraints. <br> + * + * extendedMetadata BLOB is retrieved by specifing + * key DrmStore.ConstraintsColumns.EXTENDED_METADATA. + * + * @param extendedMetadata BLOB in which key-value pairs of extended metadata are embedded. + * + */ + public static ExtendedMetadataParser getExtendedMetadataParser(byte[] extendedMetadata) { + return new ExtendedMetadataParser(extendedMetadata); + } + + /** + * Utility parser to parse the extended meta-data embedded inside DRM constraints<br><br> + * + * Usage example<br> + * byte[] extendedMetadata<br> + * = + * constraints.getAsByteArray(DrmStore.ConstraintsColumns.EXTENDED_METADATA);<br> + * ExtendedMetadataParser parser = getExtendedMetadataParser(extendedMetadata);<br> + * Iterator keyIterator = parser.keyIterator();<br> + * while (keyIterator.hasNext()) {<br> + * String extendedMetadataKey = keyIterator.next();<br> + * String extendedMetadataValue = + * parser.get(extendedMetadataKey);<br> + * } + */ + public static class ExtendedMetadataParser { + HashMap<String, String> mMap = new HashMap<String, String>(); + + private int readByte(byte[] constraintData, int arrayIndex) { + //Convert byte[] into int. + return (int)constraintData[arrayIndex]; + } + + private String readMultipleBytes( + byte[] constraintData, int numberOfBytes, int arrayIndex) { + byte[] returnBytes = new byte[numberOfBytes]; + for (int j = arrayIndex, i = 0; j < arrayIndex + numberOfBytes; j++,i++) { + returnBytes[i] = constraintData[j]; + } + return new String(returnBytes); + } + + /* + * This will parse the following format + * KeyLengthValueLengthKeyValueKeyLength1ValueLength1Key1Value1..\0 + */ + private ExtendedMetadataParser(byte[] constraintData) { + //Extract KeyValue Pair Info, till terminator occurs. + int index = 0; + + while (index < constraintData.length) { + //Parse Key Length + int keyLength = readByte(constraintData, index); + index++; + + //Parse Value Length + int valueLength = readByte(constraintData, index); + index++; + + //Fetch key + String strKey = readMultipleBytes(constraintData, keyLength, index); + index += keyLength; + + //Fetch Value + String strValue = readMultipleBytes(constraintData, valueLength, index); + if (strValue.equals(" ")) { + strValue = ""; + } + index += valueLength; + mMap.put(strKey, strValue); + } + } + + public Iterator<String> iterator() { + return mMap.values().iterator(); + } + + public Iterator<String> keyIterator() { + return mMap.keySet().iterator(); + } + + public String get(String key) { + return mMap.get(key); + } + } +} + diff --git a/drm/java/android/drm/ProcessedData.java b/drm/java/android/drm/ProcessedData.java new file mode 100644 index 0000000..579264f --- /dev/null +++ b/drm/java/android/drm/ProcessedData.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.drm; + +/** + * This is an entity class which wraps the result of transaction between + * device and online DRM server by using {@link DrmManagerClient#processDrmInfo(DrmInfo)} + * + * In license acquisition scenario this class would hold the binary data + * of rights information. + * + */ +public class ProcessedData { + private final byte[] mData; + private String mAccountId = "_NO_USER"; + private String mSubscriptionId = ""; + + /** + * constructor to create ProcessedData object with given parameters + * + * @param data Rights data + * @param accountId Account Id of the user + */ + /* package */ ProcessedData(byte[] data, String accountId) { + mData = data; + mAccountId = accountId; + } + + /** + * constructor to create ProcessedData object with given parameters + * + * @param data Rights data + * @param accountId Account Id of the user + * @param subscriptionId Subscription Id of the user + */ + /* package */ ProcessedData(byte[] data, String accountId, String subscriptionId) { + mData = data; + mAccountId = accountId; + mSubscriptionId = subscriptionId; + } + + /** + * Returns the processed data as a result. + * + * @return Rights data associated + */ + public byte[] getData() { + return mData; + } + + /** + * Returns the account-id associated with this object + * + * @return Account Id associated + */ + public String getAccountId() { + return mAccountId; + } + + /** + * Returns the subscription-id associated with this object + * + * @return Subscription Id associated + */ + public String getSubscriptionId() { + return mSubscriptionId; + } +} + diff --git a/drm/jni/Android.mk b/drm/jni/Android.mk new file mode 100644 index 0000000..b65e4da --- /dev/null +++ b/drm/jni/Android.mk @@ -0,0 +1,50 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + android_drm_DrmManagerClient.cpp + +LOCAL_MODULE:= libdrmframework_jni + +LOCAL_SHARED_LIBRARIES := \ + libdrmframework \ + libutils \ + libandroid_runtime \ + libnativehelper \ + libbinder + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_STATIC_LIBRARIES := + +LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) \ + $(TOP)/frameworks/base/drm/libdrmframework/include \ + $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include \ + $(TOP)/frameworks/base/include + +LOCAL_PRELINK_MODULE := false + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp new file mode 100644 index 0000000..e131839 --- /dev/null +++ b/drm/jni/android_drm_DrmManagerClient.cpp @@ -0,0 +1,795 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "android_drm_DrmManagerClient" +#include <utils/Log.h> + +#include <jni.h> +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> + +#include <drm/DrmInfo.h> +#include <drm/DrmRights.h> +#include <drm/DrmInfoEvent.h> +#include <drm/DrmInfoStatus.h> +#include <drm/DrmInfoRequest.h> +#include <drm/DrmSupportInfo.h> +#include <drm/DrmConstraints.h> +#include <drm/DrmMetadata.h> +#include <drm/DrmConvertedStatus.h> +#include <drm/drm_framework_common.h> + +#include <DrmManagerClientImpl.h> + +using namespace android; + +/** + * Utility class used to extract the value from the provided java object. + * May need to add some utility function to create java object. + */ +class Utility { +public: + static String8 getStringValue(JNIEnv* env, jobject object, const char* fieldName); + + static char* getByteArrayValue( + JNIEnv* env, jobject object, const char* fieldName, int* dataLength); + + static char* getByteArrayValue( + JNIEnv* env, jbyteArray byteArray, int* dataLength); + + static String8 getStringValue(JNIEnv* env, jstring string); + + static int getIntValue(JNIEnv* env, jobject object, const char* fieldName); +}; + +String8 Utility::getStringValue(JNIEnv* env, jobject object, const char* fieldName) { + String8 dataString(""); + + /* Look for the instance field with the name fieldName */ + jfieldID fieldID + = env->GetFieldID(env->GetObjectClass(object), fieldName , "Ljava/lang/String;"); + + if (NULL != fieldID) { + jstring valueString = (jstring) env->GetObjectField(object, fieldID); + + if (NULL != valueString && valueString != env->NewStringUTF("")) { + char* bytes = const_cast< char* > (env->GetStringUTFChars(valueString, NULL)); + + const int length = strlen(bytes) + 1; + char *data = new char[length]; + strncpy(data, bytes, length); + dataString = String8(data); + + env->ReleaseStringUTFChars(valueString, bytes); + delete [] data; data = NULL; + } else { + LOGV("Failed to retrieve the data from the field %s", fieldName); + } + } + return dataString; +} + +String8 Utility::getStringValue(JNIEnv* env, jstring string) { + String8 dataString(""); + + if (NULL != string && string != env->NewStringUTF("")) { + char* bytes = const_cast< char* > (env->GetStringUTFChars(string, NULL)); + + const int length = strlen(bytes) + 1; + char *data = new char[length]; + strncpy(data, bytes, length); + dataString = String8(data); + + env->ReleaseStringUTFChars(string, bytes); + delete [] data; data = NULL; + } + return dataString; +} + +char* Utility::getByteArrayValue( + JNIEnv* env, jobject object, const char* fieldName, int* dataLength) { + char* data = NULL; + *dataLength = 0; + + jfieldID fieldID = env->GetFieldID(env->GetObjectClass(object), fieldName , "[B"); + + if (NULL != fieldID) { + jbyteArray byteArray = (jbyteArray) env->GetObjectField(object, fieldID); + if (NULL != byteArray) { + jint length = env->GetArrayLength(byteArray); + + *dataLength = length; + if (0 < *dataLength) { + data = new char[length]; + env->GetByteArrayRegion(byteArray, (jint)0, length, (jbyte *) data); + } + } + } + return data; +} + +char* Utility::getByteArrayValue(JNIEnv* env, jbyteArray byteArray, int* dataLength) { + char* data = NULL; + if (NULL != byteArray) { + jint length = env->GetArrayLength(byteArray); + + *dataLength = length; + if (0 < *dataLength) { + data = new char[length]; + env->GetByteArrayRegion(byteArray, (jint)0, length, (jbyte *) data); + } + } + return data; +} + +int Utility::getIntValue(JNIEnv* env, jobject object, const char* fieldName) { + jfieldID fieldID; + int intValue = -1; + + /* Get a reference to obj’s class */ + jclass clazz = env->GetObjectClass(object); + /* Look for the instance field with the name fieldName */ + fieldID = env->GetFieldID(clazz, fieldName , "I"); + + if (NULL != fieldID) { + intValue = (int) env->GetIntField(object, fieldID); + } + + return intValue; +} + +class JNIOnInfoListener : public DrmManagerClient::OnInfoListener { +public: + JNIOnInfoListener(JNIEnv* env, jobject thiz, jobject weak_thiz); + + virtual ~JNIOnInfoListener(); + void onInfo(const DrmInfoEvent& event); + +private: + JNIOnInfoListener(); + jclass mClass; + jobject mObject; +}; + +JNIOnInfoListener::JNIOnInfoListener(JNIEnv* env, jobject thiz, jobject weak_thiz) { + jclass clazz = env->GetObjectClass(thiz); + + if (clazz == NULL) { + LOGE("Can't find android/drm/DrmManagerClient"); + jniThrowException(env, "java/lang/Exception", NULL); + return; + } + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewGlobalRef(weak_thiz); +} + +JNIOnInfoListener::~JNIOnInfoListener() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mObject); + env->DeleteGlobalRef(mClass); +} + +void JNIOnInfoListener::onInfo(const DrmInfoEvent& event) { + jint uniqueId = event.getUniqueId(); + jint type = event.getType(); + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jstring message = env->NewStringUTF(event.getMessage().string()); + LOGV("JNIOnInfoListener::onInfo => %d | %d | %s", uniqueId, type, event.getMessage().string()); + + env->CallStaticVoidMethod( + mClass, + env->GetStaticMethodID(mClass, "notify", "(Ljava/lang/Object;IILjava/lang/String;)V"), + mObject, uniqueId, type, message); +} + +static Mutex sLock; + +static sp<DrmManagerClientImpl> setDrmManagerClientImpl( + JNIEnv* env, jobject thiz, const sp<DrmManagerClientImpl>& client) { + Mutex::Autolock l(sLock); + jclass clazz = env->FindClass("android/drm/DrmManagerClient"); + jfieldID fieldId = env->GetFieldID(clazz, "mNativeContext", "I"); + + sp<DrmManagerClientImpl> old = (DrmManagerClientImpl*)env->GetIntField(thiz, fieldId); + if (client.get()) { + client->incStrong(thiz); + } + if (old != 0) { + old->decStrong(thiz); + } + env->SetIntField(thiz, fieldId, (int)client.get()); + return old; +} + +static sp<DrmManagerClientImpl> getDrmManagerClientImpl(JNIEnv* env, jobject thiz) { + Mutex::Autolock l(sLock); + jclass clazz = env->FindClass("android/drm/DrmManagerClient"); + jfieldID fieldId = env->GetFieldID(clazz, "mNativeContext", "I"); + + DrmManagerClientImpl* const client = (DrmManagerClientImpl*)env->GetIntField(thiz, fieldId); + return sp<DrmManagerClientImpl>(client); +} + +static void android_drm_DrmManagerClient_initialize( + JNIEnv* env, jobject thiz, jint uniqueId, jobject weak_thiz) { + LOGV("initialize - Enter"); + + sp<DrmManagerClientImpl> drmManager = DrmManagerClientImpl::create(&uniqueId); + drmManager->addClient(uniqueId); + + // Set the listener to DrmManager + sp<DrmManagerClient::OnInfoListener> listener = new JNIOnInfoListener(env, thiz, weak_thiz); + drmManager->setOnInfoListener(uniqueId, listener); + + setDrmManagerClientImpl(env, thiz, drmManager); + LOGV("initialize - Exit"); +} + +static void android_drm_DrmManagerClient_finalize(JNIEnv* env, jobject thiz, jint uniqueId) { + LOGV("finalize - Enter"); + DrmManagerClientImpl::remove(uniqueId); + getDrmManagerClientImpl(env, thiz)->setOnInfoListener(uniqueId, NULL); + + sp<DrmManagerClientImpl> oldClient = setDrmManagerClientImpl(env, thiz, NULL); + if (oldClient != NULL) { + oldClient->setOnInfoListener(uniqueId, NULL); + oldClient->removeClient(uniqueId); + } + LOGV("finalize - Exit"); +} + +static jobject android_drm_DrmManagerClient_getConstraintsFromContent( + JNIEnv* env, jobject thiz, jint uniqueId, jstring jpath, jint usage) { + LOGV("GetConstraints - Enter"); + + const String8 pathString = Utility::getStringValue(env, jpath); + DrmConstraints* pConstraints + = getDrmManagerClientImpl(env, thiz)->getConstraints(uniqueId, &pathString, usage); + + jclass localRef = env->FindClass("android/content/ContentValues"); + jobject constraints = NULL; + + if (NULL != localRef && NULL != pConstraints) { + // Get the constructor id + jmethodID constructorId = env->GetMethodID(localRef, "<init>", "()V"); + // create the java DrmConstraints object + constraints = env->NewObject(localRef, constructorId); + + DrmConstraints::KeyIterator keyIt = pConstraints->keyIterator(); + + while (keyIt.hasNext()) { + String8 key = keyIt.next(); + + // insert the entry<constraintKey, constraintValue> to newly created java object + if (DrmConstraints::EXTENDED_METADATA == key) { + const char* value = pConstraints->getAsByteArray(&key); + if (NULL != value) { + jbyteArray dataArray = env->NewByteArray(strlen(value)); + env->SetByteArrayRegion(dataArray, 0, strlen(value), (jbyte*)value); + env->CallVoidMethod( + constraints, env->GetMethodID(localRef, "put", "(Ljava/lang/String;[B)V"), + env->NewStringUTF(key.string()), dataArray); + } + } else { + String8 value = pConstraints->get(key); + env->CallVoidMethod( + constraints, + env->GetMethodID(localRef, "put", "(Ljava/lang/String;Ljava/lang/String;)V"), + env->NewStringUTF(key.string()), env->NewStringUTF(value.string())); + } + } + } + + delete pConstraints; pConstraints = NULL; + LOGV("GetConstraints - Exit"); + return constraints; +} + +static jobject android_drm_DrmManagerClient_getMetadataFromContent( + JNIEnv* env, jobject thiz, jint uniqueId, jstring jpath) { + LOGV("GetMetadata - Enter"); + const String8 pathString = Utility::getStringValue(env, jpath); + DrmMetadata* pMetadata = + getDrmManagerClientImpl(env, thiz)->getMetadata(uniqueId, &pathString); + + jobject metadata = NULL; + + jclass localRef = NULL; + localRef = env->FindClass("android/content/ContentValues"); + if (NULL != localRef && NULL != pMetadata) { + // Get the constructor id + jmethodID constructorId = NULL; + constructorId = env->GetMethodID(localRef, "<init>", "()V"); + if (NULL != constructorId) { + // create the java DrmMetadata object + metadata = env->NewObject(localRef, constructorId); + if (NULL != metadata) { + DrmMetadata::KeyIterator keyIt = pMetadata->keyIterator(); + while (keyIt.hasNext()) { + String8 key = keyIt.next(); + // insert the entry<constraintKey, constraintValue> + // to newly created java object + String8 value = pMetadata->get(key); + env->CallVoidMethod(metadata, env->GetMethodID(localRef, "put", + "(Ljava/lang/String;Ljava/lang/String;)V"), + env->NewStringUTF(key.string()), env->NewStringUTF(value.string())); + } + } + } + } + delete pMetadata; pMetadata = NULL; + LOGV("GetMetadata - Exit"); + return metadata; +} + +static jobjectArray android_drm_DrmManagerClient_getAllSupportInfo( + JNIEnv* env, jobject thiz, jint uniqueId) { + LOGV("GetAllSupportInfo - Enter"); + DrmSupportInfo* drmSupportInfoArray = NULL; + + int length = 0; + getDrmManagerClientImpl(env, thiz)->getAllSupportInfo(uniqueId, &length, &drmSupportInfoArray); + + jclass clazz = env->FindClass("android/drm/DrmSupportInfo"); + + jobjectArray array = (jobjectArray)env->NewObjectArray(length, clazz, NULL); + + for (int i = 0; i < length; i++) { + DrmSupportInfo info = drmSupportInfoArray[i]; + + jobject drmSupportInfo = env->NewObject(clazz, env->GetMethodID(clazz, "<init>", "()V")); + + jmethodID addMimeTypeId + = env->GetMethodID(clazz, "addMimeType", "(Ljava/lang/String;)V"); + jmethodID addFileSuffixId + = env->GetMethodID(clazz, "addFileSuffix", "(Ljava/lang/String;)V"); + + env->CallVoidMethod( + drmSupportInfo, env->GetMethodID(clazz, "setDescription", "(Ljava/lang/String;)V"), + env->NewStringUTF(info.getDescription().string())); + + DrmSupportInfo::MimeTypeIterator iterator = info.getMimeTypeIterator(); + while (iterator.hasNext()) { + String8 value = iterator.next(); + env->CallVoidMethod(drmSupportInfo, addMimeTypeId, env->NewStringUTF(value.string())); + } + + DrmSupportInfo::FileSuffixIterator it = info.getFileSuffixIterator(); + while (it.hasNext()) { + String8 value = it.next(); + env->CallVoidMethod( + drmSupportInfo, addFileSuffixId, env->NewStringUTF(value.string())); + } + + env->SetObjectArrayElement(array, i, drmSupportInfo); + } + + delete [] drmSupportInfoArray; drmSupportInfoArray = NULL; + LOGV("GetAllSupportInfo - Exit"); + return array; +} + +static void android_drm_DrmManagerClient_installDrmEngine( + JNIEnv* env, jobject thiz, jint uniqueId, jstring engineFilePath) { + LOGV("installDrmEngine - Enter"); + //getDrmManagerClient(env, thiz) + // ->installDrmEngine(uniqueId, Utility::getStringValue(env, engineFilePath)); + LOGV("installDrmEngine - Exit"); +} + +static jint android_drm_DrmManagerClient_saveRights( + JNIEnv* env, jobject thiz, jint uniqueId, + jobject drmRights, jstring rightsPath, jstring contentPath) { + LOGV("saveRights - Enter"); + int result = DRM_ERROR_UNKNOWN; + int dataLength = 0; + char* mData = Utility::getByteArrayValue(env, drmRights, "mData", &dataLength); + + if (NULL != mData) { + DrmRights rights(DrmBuffer(mData, dataLength), + Utility::getStringValue(env, drmRights, "mMimeType"), + Utility::getStringValue(env, drmRights, "mAccountId"), + Utility::getStringValue(env, drmRights, "mSubscriptionId")); + result = getDrmManagerClientImpl(env, thiz) + ->saveRights(uniqueId, rights, Utility::getStringValue(env, rightsPath), + Utility::getStringValue(env, contentPath)); + } + + delete mData; mData = NULL; + LOGV("saveRights - Exit"); + return result; +} + +static jboolean android_drm_DrmManagerClient_canHandle( + JNIEnv* env, jobject thiz, jint uniqueId, jstring path, jstring mimeType) { + LOGV("canHandle - Enter"); + jboolean result + = getDrmManagerClientImpl(env, thiz) + ->canHandle(uniqueId, Utility::getStringValue(env, path), + Utility::getStringValue(env, mimeType)); + LOGV("canHandle - Exit"); + return result; +} + +static jobject android_drm_DrmManagerClient_processDrmInfo( + JNIEnv* env, jobject thiz, jint uniqueId, jobject drmInfoObject) { + LOGV("processDrmInfo - Enter"); + int dataLength = 0; + const String8 mMimeType = Utility::getStringValue(env, drmInfoObject, "mMimeType"); + char* mData = Utility::getByteArrayValue(env, drmInfoObject, "mData", &dataLength); + int mInfoType = Utility::getIntValue(env, drmInfoObject, "mInfoType"); + + const DrmBuffer buffer(mData, dataLength); + DrmInfo drmInfo(mInfoType, buffer, mMimeType); + + jclass clazz = env->FindClass("android/drm/DrmInfo"); + jobject keyIterator + = env->CallObjectMethod(drmInfoObject, + env->GetMethodID(clazz, "keyIterator", "()Ljava/util/Iterator;")); + + jmethodID hasNextId = env->GetMethodID(env->FindClass("java/util/Iterator"), "hasNext", "()Z"); + + while (env->CallBooleanMethod(keyIterator, hasNextId)) { + jstring key = (jstring) env->CallObjectMethod(keyIterator, + env->GetMethodID(env->FindClass("java/util/Iterator"), + "next", "()Ljava/lang/Object;")); + + jobject valueObject = env->CallObjectMethod(drmInfoObject, + env->GetMethodID(clazz, "get", "(Ljava/lang/String;)Ljava/lang/Object;"), key); + + jstring valString = NULL; + if (NULL != valueObject) { + valString = (jstring) env->CallObjectMethod(valueObject, + env->GetMethodID(env->FindClass("java/lang/Object"), + "toString", "()Ljava/lang/String;")); + } + + String8 keyString = Utility::getStringValue(env, key); + String8 valueString = Utility::getStringValue(env, valString); + LOGV("Key: %s | Value: %s", keyString.string(), valueString.string()); + + drmInfo.put(keyString, valueString); + } + + DrmInfoStatus* pDrmInfoStatus + = getDrmManagerClientImpl(env, thiz)->processDrmInfo(uniqueId, &drmInfo); + + jclass localRef = env->FindClass("android/drm/DrmInfoStatus"); + jobject drmInfoStatus = NULL; + + if (NULL != localRef && NULL != pDrmInfoStatus) { + int statusCode = pDrmInfoStatus->statusCode; + int infoType = pDrmInfoStatus->infoType; + + jbyteArray dataArray = NULL; + if (NULL != pDrmInfoStatus->drmBuffer) { + int length = pDrmInfoStatus->drmBuffer->length; + dataArray = env->NewByteArray(length); + env->SetByteArrayRegion( + dataArray, 0, length, (jbyte*) pDrmInfoStatus->drmBuffer->data); + + delete [] pDrmInfoStatus->drmBuffer->data; + delete pDrmInfoStatus->drmBuffer; pDrmInfoStatus->drmBuffer = NULL; + } + jclass clazz = env->FindClass("android/drm/ProcessedData"); + jmethodID constructorId + = env->GetMethodID(clazz, "<init>", "([BLjava/lang/String;Ljava/lang/String;)V"); + jobject processedData = env->NewObject(clazz, constructorId, dataArray, + env->NewStringUTF((drmInfo.get(DrmInfoRequest::ACCOUNT_ID)).string()), + env->NewStringUTF((drmInfo.get(DrmInfoRequest::SUBSCRIPTION_ID)).string())); + + constructorId + = env->GetMethodID(localRef, + "<init>", "(IILandroid/drm/ProcessedData;Ljava/lang/String;)V"); + + drmInfoStatus = env->NewObject(localRef, constructorId, statusCode, infoType, + processedData, env->NewStringUTF(pDrmInfoStatus->mimeType.string())); + } + + delete mData; mData = NULL; + delete pDrmInfoStatus; pDrmInfoStatus = NULL; + + LOGV("processDrmInfo - Exit"); + return drmInfoStatus; +} + +static jobject android_drm_DrmManagerClient_acquireDrmInfo( + JNIEnv* env, jobject thiz, jint uniqueId, jobject drmInfoRequest) { + LOGV("acquireDrmInfo Enter"); + const String8 mMimeType = Utility::getStringValue(env, drmInfoRequest, "mMimeType"); + int mInfoType = Utility::getIntValue(env, drmInfoRequest, "mInfoType"); + + DrmInfoRequest drmInfoReq(mInfoType, mMimeType); + + jclass clazz = env->FindClass("android/drm/DrmInfoRequest"); + jobject keyIterator + = env->CallObjectMethod(drmInfoRequest, + env->GetMethodID(clazz, "keyIterator", "()Ljava/util/Iterator;")); + + jmethodID hasNextId = env->GetMethodID(env->FindClass("java/util/Iterator"), "hasNext", "()Z"); + + while (env->CallBooleanMethod(keyIterator, hasNextId)) { + jstring key + = (jstring) env->CallObjectMethod(keyIterator, + env->GetMethodID(env->FindClass("java/util/Iterator"), + "next", "()Ljava/lang/Object;")); + + jstring value = (jstring) env->CallObjectMethod(drmInfoRequest, + env->GetMethodID(clazz, "get", "(Ljava/lang/String;)Ljava/lang/Object;"), key); + + String8 keyString = Utility::getStringValue(env, key); + String8 valueString = Utility::getStringValue(env, value); + LOGV("Key: %s | Value: %s", keyString.string(), valueString.string()); + + drmInfoReq.put(keyString, valueString); + } + + DrmInfo* pDrmInfo = getDrmManagerClientImpl(env, thiz)->acquireDrmInfo(uniqueId, &drmInfoReq); + + jobject drmInfoObject = NULL; + + if (NULL != pDrmInfo) { + jclass localRef = env->FindClass("android/drm/DrmInfo"); + + if (NULL != localRef) { + int length = pDrmInfo->getData().length; + + jbyteArray dataArray = env->NewByteArray(length); + env->SetByteArrayRegion(dataArray, 0, length, (jbyte*)pDrmInfo->getData().data); + + drmInfoObject + = env->NewObject(localRef, + env->GetMethodID(localRef, "<init>", "(I[BLjava/lang/String;)V"), + mInfoType, dataArray, env->NewStringUTF(pDrmInfo->getMimeType().string())); + + DrmInfo::KeyIterator it = pDrmInfo->keyIterator(); + jmethodID putMethodId + = env->GetMethodID(localRef, "put", "(Ljava/lang/String;Ljava/lang/Object;)V"); + + while (it.hasNext()) { + String8 key = it.next(); + String8 value = pDrmInfo->get(key); + + env->CallVoidMethod(drmInfoObject, putMethodId, + env->NewStringUTF(key.string()), env->NewStringUTF(value.string())); + } + } + delete [] pDrmInfo->getData().data; + } + + delete pDrmInfo; pDrmInfo = NULL; + + LOGV("acquireDrmInfo Exit"); + return drmInfoObject; +} + +static jint android_drm_DrmManagerClient_getDrmObjectType( + JNIEnv* env, jobject thiz, jint uniqueId, jstring path, jstring mimeType) { + LOGV("getDrmObjectType Enter"); + int drmObjectType + = getDrmManagerClientImpl(env, thiz) + ->getDrmObjectType(uniqueId, Utility::getStringValue(env, path), + Utility::getStringValue(env, mimeType)); + LOGV("getDrmObjectType Exit"); + return drmObjectType; +} + +static jstring android_drm_DrmManagerClient_getOriginalMimeType( + JNIEnv* env, jobject thiz, jint uniqueId, jstring path) { + LOGV("getOriginalMimeType Enter"); + String8 mimeType + = getDrmManagerClientImpl(env, thiz) + ->getOriginalMimeType(uniqueId, Utility::getStringValue(env, path)); + LOGV("getOriginalMimeType Exit"); + return env->NewStringUTF(mimeType.string()); +} + +static jint android_drm_DrmManagerClient_checkRightsStatus( + JNIEnv* env, jobject thiz, jint uniqueId, jstring path, int action) { + LOGV("getOriginalMimeType Enter"); + int rightsStatus + = getDrmManagerClientImpl(env, thiz) + ->checkRightsStatus(uniqueId, Utility::getStringValue(env, path), action); + LOGV("getOriginalMimeType Exit"); + return rightsStatus; +} + +static jint android_drm_DrmManagerClient_removeRights( + JNIEnv* env, jobject thiz, jint uniqueId, jstring path) { + LOGV("removeRights"); + return getDrmManagerClientImpl(env, thiz) + ->removeRights(uniqueId, Utility::getStringValue(env, path)); +} + +static jint android_drm_DrmManagerClient_removeAllRights( + JNIEnv* env, jobject thiz, jint uniqueId) { + LOGV("removeAllRights"); + return getDrmManagerClientImpl(env, thiz)->removeAllRights(uniqueId); +} + +static jint android_drm_DrmManagerClient_openConvertSession( + JNIEnv* env, jobject thiz, jint uniqueId, jstring mimeType) { + LOGV("openConvertSession Enter"); + int convertId + = getDrmManagerClientImpl(env, thiz) + ->openConvertSession(uniqueId, Utility::getStringValue(env, mimeType)); + LOGV("openConvertSession Exit"); + return convertId; +} + +static jobject android_drm_DrmManagerClient_convertData( + JNIEnv* env, jobject thiz, jint uniqueId, jint convertId, jbyteArray inputData) { + LOGV("convertData Enter"); + + int dataLength = 0; + char* mData = Utility::getByteArrayValue(env, inputData, &dataLength); + const DrmBuffer buffer(mData, dataLength); + + DrmConvertedStatus* pDrmConvertedStatus + = getDrmManagerClientImpl(env, thiz)->convertData(uniqueId, convertId, &buffer); + + jclass localRef = env->FindClass("android/drm/DrmConvertedStatus"); + + jobject drmConvertedStatus = NULL; + + if (NULL != localRef && NULL != pDrmConvertedStatus) { + int statusCode = pDrmConvertedStatus->statusCode; + + jbyteArray dataArray = NULL; + if (NULL != pDrmConvertedStatus->convertedData) { + int length = pDrmConvertedStatus->convertedData->length; + dataArray = env->NewByteArray(length); + env->SetByteArrayRegion(dataArray, 0, length, + (jbyte*) pDrmConvertedStatus->convertedData->data); + + delete [] pDrmConvertedStatus->convertedData->data; + delete pDrmConvertedStatus->convertedData; pDrmConvertedStatus->convertedData = NULL; + } + jmethodID constructorId = env->GetMethodID(localRef, "<init>", "(I[BI)V"); + drmConvertedStatus + = env->NewObject(localRef, constructorId, + statusCode, dataArray, pDrmConvertedStatus->offset); + } + + delete mData; mData = NULL; + delete pDrmConvertedStatus; pDrmConvertedStatus = NULL; + + LOGV("convertData - Exit"); + return drmConvertedStatus; +} + +static jobject android_drm_DrmManagerClient_closeConvertSession( + JNIEnv* env, jobject thiz, int uniqueId, jint convertId) { + + LOGV("closeConvertSession Enter"); + + DrmConvertedStatus* pDrmConvertedStatus + = getDrmManagerClientImpl(env, thiz)->closeConvertSession(uniqueId, convertId); + + jclass localRef = env->FindClass("android/drm/DrmConvertedStatus"); + + jobject drmConvertedStatus = NULL; + + if (NULL != localRef && NULL != pDrmConvertedStatus) { + int statusCode = pDrmConvertedStatus->statusCode; + + jbyteArray dataArray = NULL; + if (NULL != pDrmConvertedStatus->convertedData) { + int length = pDrmConvertedStatus->convertedData->length; + dataArray = env->NewByteArray(length); + env->SetByteArrayRegion( + dataArray, 0, length, (jbyte*) pDrmConvertedStatus->convertedData->data); + + delete [] pDrmConvertedStatus->convertedData->data; + delete pDrmConvertedStatus->convertedData; pDrmConvertedStatus->convertedData = NULL; + } + jmethodID constructorId = env->GetMethodID(localRef, "<init>", "(I[BI)V"); + drmConvertedStatus + = env->NewObject(localRef, constructorId, + statusCode, dataArray, pDrmConvertedStatus->offset); + } + + delete pDrmConvertedStatus; pDrmConvertedStatus = NULL; + + LOGV("closeConvertSession - Exit"); + return drmConvertedStatus; +} + +static JNINativeMethod nativeMethods[] = { + + {"_initialize", "(ILjava/lang/Object;)V", + (void*)android_drm_DrmManagerClient_initialize}, + + {"_finalize", "(I)V", + (void*)android_drm_DrmManagerClient_finalize}, + + {"_getConstraints", "(ILjava/lang/String;I)Landroid/content/ContentValues;", + (void*)android_drm_DrmManagerClient_getConstraintsFromContent}, + + {"_getMetadata", "(ILjava/lang/String;)Landroid/content/ContentValues;", + (void*)android_drm_DrmManagerClient_getMetadataFromContent}, + + {"_getAllSupportInfo", "(I)[Landroid/drm/DrmSupportInfo;", + (void*)android_drm_DrmManagerClient_getAllSupportInfo}, + + {"_installDrmEngine", "(ILjava/lang/String;)V", + (void*)android_drm_DrmManagerClient_installDrmEngine}, + + {"_canHandle", "(ILjava/lang/String;Ljava/lang/String;)Z", + (void*)android_drm_DrmManagerClient_canHandle}, + + {"_processDrmInfo", "(ILandroid/drm/DrmInfo;)Landroid/drm/DrmInfoStatus;", + (void*)android_drm_DrmManagerClient_processDrmInfo}, + + {"_acquireDrmInfo", "(ILandroid/drm/DrmInfoRequest;)Landroid/drm/DrmInfo;", + (void*)android_drm_DrmManagerClient_acquireDrmInfo}, + + {"_saveRights", "(ILandroid/drm/DrmRights;Ljava/lang/String;Ljava/lang/String;)I", + (void*)android_drm_DrmManagerClient_saveRights}, + + {"_getDrmObjectType", "(ILjava/lang/String;Ljava/lang/String;)I", + (void*)android_drm_DrmManagerClient_getDrmObjectType}, + + {"_getOriginalMimeType", "(ILjava/lang/String;)Ljava/lang/String;", + (void*)android_drm_DrmManagerClient_getOriginalMimeType}, + + {"_checkRightsStatus", "(ILjava/lang/String;I)I", + (void*)android_drm_DrmManagerClient_checkRightsStatus}, + + {"_removeRights", "(ILjava/lang/String;)I", + (void*)android_drm_DrmManagerClient_removeRights}, + + {"_removeAllRights", "(I)I", + (void*)android_drm_DrmManagerClient_removeAllRights}, + + {"_openConvertSession", "(ILjava/lang/String;)I", + (void*)android_drm_DrmManagerClient_openConvertSession}, + + {"_convertData", "(II[B)Landroid/drm/DrmConvertedStatus;", + (void*)android_drm_DrmManagerClient_convertData}, + + {"_closeConvertSession", "(II)Landroid/drm/DrmConvertedStatus;", + (void*)android_drm_DrmManagerClient_closeConvertSession}, +}; + +static int registerNativeMethods(JNIEnv* env) { + int result = -1; + + /* look up the class */ + jclass clazz = env->FindClass("android/drm/DrmManagerClient"); + + if (NULL != clazz) { + if (env->RegisterNatives(clazz, nativeMethods, sizeof(nativeMethods) + / sizeof(nativeMethods[0])) == JNI_OK) { + result = 0; + } + } + return result; +} + +jint JNI_OnLoad(JavaVM* vm, void* reserved) { + JNIEnv* env = NULL; + jint result = -1; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) { + if (NULL != env && registerNativeMethods(env) == 0) { + result = JNI_VERSION_1_4; + } + } + return result; +} + diff --git a/drm/libdrmframework/Android.mk b/drm/libdrmframework/Android.mk new file mode 100644 index 0000000..99133ba --- /dev/null +++ b/drm/libdrmframework/Android.mk @@ -0,0 +1,49 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + DrmManagerClientImpl.cpp \ + DrmManagerClient.cpp + +LOCAL_MODULE:= libdrmframework + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libbinder + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_STATIC_LIBRARIES := \ + libdrmframeworkcommon + +LOCAL_C_INCLUDES += \ + $(TOP)/frameworks/base/drm/libdrmframework/include \ + $(TOP)/frameworks/base/include + +LOCAL_PRELINK_MODULE := false + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/drm/libdrmframework/DrmManagerClient.cpp b/drm/libdrmframework/DrmManagerClient.cpp new file mode 100644 index 0000000..8bb00c3 --- /dev/null +++ b/drm/libdrmframework/DrmManagerClient.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utils/String8.h> +#include <binder/IServiceManager.h> +#include <drm/DrmManagerClient.h> + +#include "DrmManagerClientImpl.h" + +using namespace android; + +DrmManagerClient::DrmManagerClient(): + mUniqueId(0), mDrmManagerClientImpl(NULL) { + mDrmManagerClientImpl = DrmManagerClientImpl::create(&mUniqueId); + mDrmManagerClientImpl->addClient(mUniqueId); +} + +DrmManagerClient::~DrmManagerClient() { + DrmManagerClientImpl::remove(mUniqueId); + mDrmManagerClientImpl->removeClient(mUniqueId); + mDrmManagerClientImpl->setOnInfoListener(mUniqueId, NULL); + delete mDrmManagerClientImpl; mDrmManagerClientImpl = NULL; +} + +status_t DrmManagerClient::setOnInfoListener( + const sp<DrmManagerClient::OnInfoListener>& infoListener) { + return mDrmManagerClientImpl->setOnInfoListener(mUniqueId, infoListener); +} + +DrmConstraints* DrmManagerClient::getConstraints(const String8* path, const int action) { + return mDrmManagerClientImpl->getConstraints(mUniqueId, path, action); +} + +DrmMetadata* DrmManagerClient::getMetadata(const String8* path) { + return mDrmManagerClientImpl->getMetadata(mUniqueId, path); +} + +bool DrmManagerClient::canHandle(const String8& path, const String8& mimeType) { + return mDrmManagerClientImpl->canHandle(mUniqueId, path, mimeType); +} + +DrmInfoStatus* DrmManagerClient::processDrmInfo(const DrmInfo* drmInfo) { + return mDrmManagerClientImpl->processDrmInfo(mUniqueId, drmInfo); +} + +DrmInfo* DrmManagerClient::acquireDrmInfo(const DrmInfoRequest* drmInfoRequest) { + return mDrmManagerClientImpl->acquireDrmInfo(mUniqueId, drmInfoRequest); +} + +status_t DrmManagerClient::saveRights( + const DrmRights& drmRights, const String8& rightsPath, const String8& contentPath) { + return mDrmManagerClientImpl->saveRights(mUniqueId, drmRights, rightsPath, contentPath); +} + +String8 DrmManagerClient::getOriginalMimeType(const String8& path) { + return mDrmManagerClientImpl->getOriginalMimeType(mUniqueId, path); +} + +int DrmManagerClient::getDrmObjectType(const String8& path, const String8& mimeType) { + return mDrmManagerClientImpl->getDrmObjectType( mUniqueId, path, mimeType); +} + +int DrmManagerClient::checkRightsStatus(const String8& path, int action) { + return mDrmManagerClientImpl->checkRightsStatus(mUniqueId, path, action); +} + +status_t DrmManagerClient::consumeRights(DecryptHandle* decryptHandle, int action, bool reserve) { + Mutex::Autolock _l(mDecryptLock); + return mDrmManagerClientImpl->consumeRights(mUniqueId, decryptHandle, action, reserve); +} + +status_t DrmManagerClient::setPlaybackStatus( + DecryptHandle* decryptHandle, int playbackStatus, int position) { + return mDrmManagerClientImpl + ->setPlaybackStatus(mUniqueId, decryptHandle, playbackStatus, position); +} + +bool DrmManagerClient::validateAction( + const String8& path, int action, const ActionDescription& description) { + return mDrmManagerClientImpl->validateAction(mUniqueId, path, action, description); +} + +status_t DrmManagerClient::removeRights(const String8& path) { + return mDrmManagerClientImpl->removeRights(mUniqueId, path); +} + +status_t DrmManagerClient::removeAllRights() { + return mDrmManagerClientImpl->removeAllRights(mUniqueId); +} + +int DrmManagerClient::openConvertSession(const String8& mimeType) { + return mDrmManagerClientImpl->openConvertSession(mUniqueId, mimeType); +} + +DrmConvertedStatus* DrmManagerClient::convertData(int convertId, const DrmBuffer* inputData) { + return mDrmManagerClientImpl->convertData(mUniqueId, convertId, inputData); +} + +DrmConvertedStatus* DrmManagerClient::closeConvertSession(int convertId) { + return mDrmManagerClientImpl->closeConvertSession(mUniqueId, convertId); +} + +status_t DrmManagerClient::getAllSupportInfo(int* length, DrmSupportInfo** drmSupportInfoArray) { + return mDrmManagerClientImpl->getAllSupportInfo(mUniqueId, length, drmSupportInfoArray); +} + +DecryptHandle* DrmManagerClient::openDecryptSession(int fd, int offset, int length) { + return mDrmManagerClientImpl->openDecryptSession(mUniqueId, fd, offset, length); +} + +DecryptHandle* DrmManagerClient::openDecryptSession(const char* uri) { + return mDrmManagerClientImpl->openDecryptSession(mUniqueId, uri); +} + +status_t DrmManagerClient::closeDecryptSession(DecryptHandle* decryptHandle) { + return mDrmManagerClientImpl->closeDecryptSession(mUniqueId, decryptHandle); +} + +status_t DrmManagerClient::initializeDecryptUnit( + DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) { + Mutex::Autolock _l(mDecryptLock); + return mDrmManagerClientImpl->initializeDecryptUnit( + mUniqueId, decryptHandle, decryptUnitId, headerInfo); +} + +status_t DrmManagerClient::decrypt( + DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) { + Mutex::Autolock _l(mDecryptLock); + return mDrmManagerClientImpl->decrypt( + mUniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV); +} + +status_t DrmManagerClient::finalizeDecryptUnit(DecryptHandle* decryptHandle, int decryptUnitId) { + Mutex::Autolock _l(mDecryptLock); + return mDrmManagerClientImpl->finalizeDecryptUnit(mUniqueId, decryptHandle, decryptUnitId); +} + +ssize_t DrmManagerClient::pread( + DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) { + Mutex::Autolock _l(mDecryptLock); + return mDrmManagerClientImpl->pread(mUniqueId, decryptHandle, buffer, numBytes, offset); +} + diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp new file mode 100644 index 0000000..eea312b --- /dev/null +++ b/drm/libdrmframework/DrmManagerClientImpl.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "DrmManagerClientImpl(Native)" +#include <utils/Log.h> + +#include <utils/String8.h> +#include <utils/Vector.h> +#include <binder/IServiceManager.h> + +#include "DrmManagerClientImpl.h" + +using namespace android; + +#define INVALID_VALUE -1 + +Mutex DrmManagerClientImpl::mMutex; +sp<IDrmManagerService> DrmManagerClientImpl::mDrmManagerService; +const String8 DrmManagerClientImpl::EMPTY_STRING(""); + +DrmManagerClientImpl* DrmManagerClientImpl::create(int* pUniqueId) { + if (0 == *pUniqueId) { + int uniqueId = getDrmManagerService()->addUniqueId(*pUniqueId); + *pUniqueId = uniqueId; + } else { + getDrmManagerService()->addUniqueId(*pUniqueId); + } + return new DrmManagerClientImpl(); +} + +void DrmManagerClientImpl::remove(int uniqueId) { + getDrmManagerService()->removeUniqueId(uniqueId); +} + +const sp<IDrmManagerService>& DrmManagerClientImpl::getDrmManagerService() { + mMutex.lock(); + if (NULL == mDrmManagerService.get()) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16("drm.drmManager")); + if (binder != 0) { + break; + } + LOGW("DrmManagerService not published, waiting..."); + struct timespec reqt; + reqt.tv_sec = 0; + reqt.tv_nsec = 500000000; //0.5 sec + nanosleep(&reqt, NULL); + } while (true); + + mDrmManagerService = interface_cast<IDrmManagerService>(binder); + } + mMutex.unlock(); + return mDrmManagerService; +} + +void DrmManagerClientImpl::addClient(int uniqueId) { + getDrmManagerService()->addClient(uniqueId); +} + +void DrmManagerClientImpl::removeClient(int uniqueId) { + getDrmManagerService()->removeClient(uniqueId); +} + +status_t DrmManagerClientImpl::setOnInfoListener( + int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener) { + Mutex::Autolock _l(mLock); + mOnInfoListener = infoListener; + return getDrmManagerService()->setDrmServiceListener(uniqueId, + (NULL != infoListener.get()) ? this : NULL); +} + +status_t DrmManagerClientImpl::installDrmEngine(int uniqueId, const String8& drmEngineFile) { + status_t status = DRM_ERROR_UNKNOWN; + if (EMPTY_STRING != drmEngineFile) { + status = getDrmManagerService()->installDrmEngine(uniqueId, drmEngineFile); + } + return status; +} + +DrmConstraints* DrmManagerClientImpl::getConstraints( + int uniqueId, const String8* path, const int action) { + DrmConstraints *drmConstraints = NULL; + if ((NULL != path) && (EMPTY_STRING != *path)) { + drmConstraints = getDrmManagerService()->getConstraints(uniqueId, path, action); + } + return drmConstraints; +} + +DrmMetadata* DrmManagerClientImpl::getMetadata(int uniqueId, const String8* path) { + DrmMetadata *drmMetadata = NULL; + if ((NULL != path) && (EMPTY_STRING != *path)) { + drmMetadata = getDrmManagerService()->getMetadata(uniqueId, path); + } + return drmMetadata; +} + +bool DrmManagerClientImpl::canHandle(int uniqueId, const String8& path, const String8& mimeType) { + bool retCode = false; + if ((EMPTY_STRING != path) || (EMPTY_STRING != mimeType)) { + retCode = getDrmManagerService()->canHandle(uniqueId, path, mimeType); + } + return retCode; +} + +DrmInfoStatus* DrmManagerClientImpl::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) { + DrmInfoStatus *drmInfoStatus = NULL; + if (NULL != drmInfo) { + drmInfoStatus = getDrmManagerService()->processDrmInfo(uniqueId, drmInfo); + } + return drmInfoStatus; +} + +DrmInfo* DrmManagerClientImpl::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) { + DrmInfo* drmInfo = NULL; + if (NULL != drmInfoRequest) { + drmInfo = getDrmManagerService()->acquireDrmInfo(uniqueId, drmInfoRequest); + } + return drmInfo; +} + +status_t DrmManagerClientImpl::saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + status_t status = DRM_ERROR_UNKNOWN; + if (EMPTY_STRING != contentPath) { + status = getDrmManagerService()->saveRights(uniqueId, drmRights, rightsPath, contentPath); + } + return status; +} + +String8 DrmManagerClientImpl::getOriginalMimeType(int uniqueId, const String8& path) { + String8 mimeType = EMPTY_STRING; + if (EMPTY_STRING != path) { + mimeType = getDrmManagerService()->getOriginalMimeType(uniqueId, path); + } + return mimeType; +} + +int DrmManagerClientImpl::getDrmObjectType( + int uniqueId, const String8& path, const String8& mimeType) { + int drmOjectType = DrmObjectType::UNKNOWN; + if ((EMPTY_STRING != path) || (EMPTY_STRING != mimeType)) { + drmOjectType = getDrmManagerService()->getDrmObjectType(uniqueId, path, mimeType); + } + return drmOjectType; +} + +int DrmManagerClientImpl::checkRightsStatus( + int uniqueId, const String8& path, int action) { + int rightsStatus = RightsStatus::RIGHTS_INVALID; + if (EMPTY_STRING != path) { + rightsStatus = getDrmManagerService()->checkRightsStatus(uniqueId, path, action); + } + return rightsStatus; +} + +status_t DrmManagerClientImpl::consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) { + status_t status = DRM_ERROR_UNKNOWN; + if (NULL != decryptHandle) { + status = getDrmManagerService()->consumeRights(uniqueId, decryptHandle, action, reserve); + } + return status; +} + +status_t DrmManagerClientImpl::setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) { + status_t status = DRM_ERROR_UNKNOWN; + if (NULL != decryptHandle) { + status = getDrmManagerService()->setPlaybackStatus( + uniqueId, decryptHandle, playbackStatus, position); + } + return status; +} + +bool DrmManagerClientImpl::validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description) { + bool retCode = false; + if (EMPTY_STRING != path) { + retCode = getDrmManagerService()->validateAction(uniqueId, path, action, description); + } + return retCode; +} + +status_t DrmManagerClientImpl::removeRights(int uniqueId, const String8& path) { + status_t status = DRM_ERROR_UNKNOWN; + if (EMPTY_STRING != path) { + status = getDrmManagerService()->removeRights(uniqueId, path); + } + return status; +} + +status_t DrmManagerClientImpl::removeAllRights(int uniqueId) { + return getDrmManagerService()->removeAllRights(uniqueId); +} + +int DrmManagerClientImpl::openConvertSession(int uniqueId, const String8& mimeType) { + int retCode = INVALID_VALUE; + if (EMPTY_STRING != mimeType) { + retCode = getDrmManagerService()->openConvertSession(uniqueId, mimeType); + } + return retCode; +} + +DrmConvertedStatus* DrmManagerClientImpl::convertData( + int uniqueId, int convertId, const DrmBuffer* inputData) { + DrmConvertedStatus* drmConvertedStatus = NULL; + if (NULL != inputData) { + drmConvertedStatus = getDrmManagerService()->convertData(uniqueId, convertId, inputData); + } + return drmConvertedStatus; +} + +DrmConvertedStatus* DrmManagerClientImpl::closeConvertSession(int uniqueId, int convertId) { + return getDrmManagerService()->closeConvertSession(uniqueId, convertId); +} + +status_t DrmManagerClientImpl::getAllSupportInfo( + int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) { + status_t status = DRM_ERROR_UNKNOWN; + if ((NULL != drmSupportInfoArray) && (NULL != length)) { + status = getDrmManagerService()->getAllSupportInfo(uniqueId, length, drmSupportInfoArray); + } + return status; +} + +DecryptHandle* DrmManagerClientImpl::openDecryptSession( + int uniqueId, int fd, int offset, int length) { + return getDrmManagerService()->openDecryptSession(uniqueId, fd, offset, length); +} + +DecryptHandle* DrmManagerClientImpl::openDecryptSession(int uniqueId, const char* uri) { + DecryptHandle* handle = NULL; + if (NULL != uri) { + handle = getDrmManagerService()->openDecryptSession(uniqueId, uri); + } + return handle; +} + +status_t DrmManagerClientImpl::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + status_t status = DRM_ERROR_UNKNOWN; + if (NULL != decryptHandle) { + status = getDrmManagerService()->closeDecryptSession( uniqueId, decryptHandle); + } + return status; +} + +status_t DrmManagerClientImpl::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) { + status_t status = DRM_ERROR_UNKNOWN; + if ((NULL != decryptHandle) && (NULL != headerInfo)) { + status = getDrmManagerService()->initializeDecryptUnit( + uniqueId, decryptHandle, decryptUnitId, headerInfo); + } + return status; +} + +status_t DrmManagerClientImpl::decrypt(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) { + status_t status = DRM_ERROR_UNKNOWN; + if ((NULL != decryptHandle) && (NULL != encBuffer) + && (NULL != decBuffer) && (NULL != *decBuffer)) { + status = getDrmManagerService()->decrypt( + uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV); + } + return status; +} + +status_t DrmManagerClientImpl::finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + status_t status = DRM_ERROR_UNKNOWN; + if (NULL != decryptHandle) { + status + = getDrmManagerService()->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId); + } + return status; +} + +ssize_t DrmManagerClientImpl::pread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset) { + ssize_t retCode = INVALID_VALUE; + if ((NULL != decryptHandle) && (NULL != buffer) && (0 < numBytes)) { + retCode = getDrmManagerService()->pread(uniqueId, decryptHandle, buffer, numBytes, offset); + } + return retCode; +} + +status_t DrmManagerClientImpl::notify(const DrmInfoEvent& event) { + if (NULL != mOnInfoListener.get()) { + Mutex::Autolock _l(mLock); + sp<DrmManagerClient::OnInfoListener> listener = mOnInfoListener; + listener->onInfo(event); + } + return DRM_NO_ERROR; +} + diff --git a/drm/libdrmframework/include/DrmIOService.h b/drm/libdrmframework/include/DrmIOService.h new file mode 100644 index 0000000..244124e --- /dev/null +++ b/drm/libdrmframework/include/DrmIOService.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DRM_IO_SERVICE_H__ +#define __DRM_IO_SERVICE_H__ + +#include "IDrmIOService.h" + +namespace android { + +/** + * This is the implementation class for DRM IO service. + * + * The instance of this class is created while starting the DRM IO service. + * + */ +class DrmIOService : public BnDrmIOService { +public: + static void instantiate(); + +private: + DrmIOService(); + virtual ~DrmIOService(); + +public: + void writeToFile(const String8& filePath, const String8& dataBuffer); + String8 readFromFile(const String8& filePath); +}; + +}; + +#endif /* __DRM_IO_SERVICE_H__ */ + diff --git a/drm/libdrmframework/include/DrmManager.h b/drm/libdrmframework/include/DrmManager.h new file mode 100644 index 0000000..bc462c2 --- /dev/null +++ b/drm/libdrmframework/include/DrmManager.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DRM_MANAGER_H__ +#define __DRM_MANAGER_H__ + +#include <utils/Errors.h> +#include <utils/threads.h> +#include <drm/drm_framework_common.h> +#include "IDrmEngine.h" +#include "PlugInManager.h" +#include "IDrmServiceListener.h" + +namespace android { + +class IDrmManager; +class DrmRegistrationInfo; +class DrmUnregistrationInfo; +class DrmRightsAcquisitionInfo; +class DrmContentIds; +class DrmConstraints; +class DrmMetadata; +class DrmRights; +class DrmInfo; +class DrmInfoStatus; +class DrmConvertedStatus; +class DrmInfoRequest; +class DrmSupportInfo; +class ActionDescription; + +/** + * This is implementation class for DRM Manager. This class delegates the + * functionality to corresponding DRM Engine. + * + * The DrmManagerService class creates an instance of this class. + * + */ +class DrmManager : public IDrmEngine::OnInfoListener { +public: + DrmManager(); + virtual ~DrmManager(); + +public: + int addUniqueId(int uniqueId); + + void removeUniqueId(int uniqueId); + + void addClient(int uniqueId); + + void removeClient(int uniqueId); + + status_t loadPlugIns(); + + status_t loadPlugIns(const String8& plugInDirPath); + + status_t unloadPlugIns(); + + status_t setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& drmServiceListener); + + status_t installDrmEngine(int uniqueId, const String8& drmEngineFile); + + DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action); + + DrmMetadata* getMetadata(int uniqueId, const String8* path); + + bool canHandle(int uniqueId, const String8& path, const String8& mimeType); + + DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo); + + DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest); + + status_t saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath); + + String8 getOriginalMimeType(int uniqueId, const String8& path); + + int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType); + + int checkRightsStatus(int uniqueId, const String8& path, int action); + + status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + status_t setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + bool validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + + status_t removeRights(int uniqueId, const String8& path); + + status_t removeAllRights(int uniqueId); + + int openConvertSession(int uniqueId, const String8& mimeType); + + DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData); + + DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId); + + status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray); + + DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length); + + DecryptHandle* openDecryptSession(int uniqueId, const char* uri); + + status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV); + + status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId); + + ssize_t pread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset); + + void onInfo(const DrmInfoEvent& event); + +private: + String8 getSupportedPlugInId(int uniqueId, const String8& path, const String8& mimeType); + + String8 getSupportedPlugInId(const String8& mimeType); + + String8 getSupportedPlugInIdFromPath(int uniqueId, const String8& path); + + bool canHandle(int uniqueId, const String8& path); + +private: + static Vector<int> mUniqueIdVector; + static const String8 EMPTY_STRING; + + int mDecryptSessionId; + int mConvertId; + Mutex mLock; + Mutex mDecryptLock; + Mutex mConvertLock; + TPlugInManager<IDrmEngine> mPlugInManager; + KeyedVector< DrmSupportInfo, String8 > mSupportInfoToPlugInIdMap; + KeyedVector< int, IDrmEngine*> mConvertSessionMap; + KeyedVector< int, sp<IDrmServiceListener> > mServiceListeners; + KeyedVector< int, IDrmEngine*> mDecryptSessionMap; +}; + +}; + +#endif /* __DRM_MANAGER_H__ */ + diff --git a/drm/libdrmframework/include/DrmManagerClientImpl.h b/drm/libdrmframework/include/DrmManagerClientImpl.h new file mode 100644 index 0000000..ff84fc7 --- /dev/null +++ b/drm/libdrmframework/include/DrmManagerClientImpl.h @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DRM_MANAGER_CLIENT_IMPL_H__ +#define __DRM_MANAGER_CLIENT_IMPL_H__ + +#include <binder/IMemory.h> +#include <utils/threads.h> +#include <drm/DrmManagerClient.h> + +#include "IDrmManagerService.h" + +namespace android { + +class DrmInfoEvent; +/** + * This is implementation class for DrmManagerClient class. + * + * Only the JNI layer creates an instance of this class to delegate + * functionality to Native later. + * + */ +class DrmManagerClientImpl : public BnDrmServiceListener { +private: + DrmManagerClientImpl() { } + +public: + static DrmManagerClientImpl* create(int* pUniqueId); + + static void remove(int uniqueId); + + virtual ~DrmManagerClientImpl() { } + +public: + /** + * Adds the client respective to given unique id. + * + * @param[in] uniqueId Unique identifier for a session + */ + void addClient(int uniqueId); + + /** + * Removes the client respective to given unique id. + * + * @param[in] uniqueId Unique identifier for a session + */ + void removeClient(int uniqueId); + + /** + * Register a callback to be invoked when the caller required to + * receive necessary information + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] infoListener Listener + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t setOnInfoListener( + int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener); + + /** + * Get constraint information associated with input content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Actions defined such as, + * Action::DEFAULT, Action::PLAY, etc + * @return DrmConstraints + * key-value pairs of constraint are embedded in it + * @note + * In case of error, return NULL + */ + DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action); + + /** + * Get metadata information associated with input content. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @return DrmMetadata + * key-value pairs of metadata are embedded in it + * @note + * In case of error, return NULL + */ + DrmMetadata* getMetadata(int uniqueId, const String8* path); + + /** + * Check whether the given mimetype or path can be handled + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the content needs to be handled + * @param[in] mimetype Mimetype of the content needs to be handled + * @return + * True if DrmManager can handle given path or mime type. + */ + bool canHandle(int uniqueId, const String8& path, const String8& mimeType); + + /** + * Executes given drm information based on its type + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmInfo Information needs to be processed + * @return DrmInfoStatus + * instance as a result of processing given input + */ + DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo); + + /** + * Retrieves necessary information for registration, unregistration or rights + * acquisition information. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmInfoRequest Request information to retrieve drmInfo + * @return DrmInfo + * instance as a result of processing given input + */ + DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest); + + /** + * Save DRM rights to specified rights path + * and make association with content path + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmRights DrmRights to be saved + * @param[in] rightsPath File path where rights to be saved + * @param[in] contentPath File path where content was saved + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath); + + /** + * Retrieves the mime type embedded inside the original content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path the path of the protected content + * @return String8 + * Returns mime-type of the original content, such as "video/mpeg" + */ + String8 getOriginalMimeType(int uniqueId, const String8& path); + + /** + * Retrieves the type of the protected object (content, rights, etc..) + * using specified path or mimetype. At least one parameter should be non null + * to retrieve DRM object type + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the content or null. + * @param[in] mimeType Mime type of the content or null. + * @return type of the DRM content, + * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT + */ + int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType); + + /** + * Check whether the given content has valid rights or not + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Action to perform (Action::DEFAULT, Action::PLAY, etc) + * @return the status of the rights for the protected content, + * such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc. + */ + int checkRightsStatus(int uniqueId, const String8& path, int action); + + /** + * Consumes the rights for a content. + * If the reserve parameter is true the rights is reserved until the same + * application calls this api again with the reserve parameter set to false. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc) + * @param[in] reserve True if the rights should be reserved. + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + /** + * Informs the DRM engine about the playback actions performed on the DRM files. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE) + * @param[in] position Position in the file (in milliseconds) where the start occurs. + * Only valid together with Playback::START. + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + /** + * Validates whether an action on the DRM content is allowed or not. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc) + * @param[in] description Detailed description of the action + * @return true if the action is allowed. + */ + bool validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + + /** + * Removes the rights associated with the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t removeRights(int uniqueId, const String8& path); + + /** + * Removes all the rights information of each plug-in associated with + * DRM framework. Will be used in master reset + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t removeAllRights(int uniqueId); + + /** + * This API is for Forward Lock based DRM scheme. + * Each time the application tries to download a new DRM file + * which needs to be converted, then the application has to + * begin with calling this API. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] mimeType Description/MIME type of the input data packet + * @return Return handle for the convert session + */ + int openConvertSession(int uniqueId, const String8& mimeType); + + /** + * Accepts and converts the input data which is part of DRM file. + * The resultant converted data and the status is returned in the DrmConvertedInfo + * object. This method will be called each time there are new block + * of data received by the application. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] convertId Handle for the convert session + * @param[in] inputData Input Data which need to be converted + * @return Return object contains the status of the data conversion, + * the output converted data and offset. In this case the + * application will ignore the offset information. + */ + DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData); + + /** + * Informs the Drm Agent when there is no more data which need to be converted + * or when an error occurs. Upon successful conversion of the complete data, + * the agent will inform that where the header and body signature + * should be added. This signature appending is needed to integrity + * protect the converted file. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] convertId Handle for the convert session + * @return Return object contains the status of the data conversion, + * the header and body signature data. It also informs + * the application on which offset these signature data + * should be appended. + */ + DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId); + + /** + * Retrieves all DrmSupportInfo instance that native DRM framework can handle. + * This interface is meant to be used by JNI layer + * + * @param[in] uniqueId Unique identifier for a session + * @param[out] length Number of elements in drmSupportInfoArray + * @param[out] drmSupportInfoArray Array contains all DrmSupportInfo + * that native DRM framework can handle + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray); + + /** + * Open the decrypt session to decrypt the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] fd File descriptor of the protected content to be decrypted + * @param[in] offset Start position of the content + * @param[in] length The length of the protected content + * @return + * Handle for the decryption session + */ + DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length); + + /** + * Open the decrypt session to decrypt the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] uri Path of the protected content to be decrypted + * @return + * Handle for the decryption session + */ + DecryptHandle* openDecryptSession(int uniqueId, const char* uri); + + /** + * Close the decrypt session for the given handle + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + /** + * Initialize decryption for the given unit of the protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID + * @param[in] headerInfo Information for initializing decryption of this decrypUnit + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + /** + * Decrypt the protected content buffers for the given unit + * This method will be called any number of times, based on number of + * encrypted streams received from application. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID + * @param[in] encBuffer Encrypted data block + * @param[out] decBuffer Decrypted data block + * @param[in] IV Optional buffer + * @return status_t + * Returns the error code for this API + * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED + * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED, + * DRM_ERROR_DECRYPT for failure. + */ + status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV); + + /** + * Finalize decryption for the given unit of the protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId); + + /** + * Reads the specified number of bytes from an open DRM file. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[out] buffer Reference to the buffer that should receive the read data. + * @param[in] numBytes Number of bytes to read. + * @param[in] offset Offset with which to update the file position. + * + * @return Number of bytes read. Returns -1 for Failure. + */ + ssize_t pread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset); + + /** + * Notify the event to the registered listener + * + * @param[in] event The event to be notified + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t notify(const DrmInfoEvent& event); + +private: + /** + * Install new DRM Engine Plug-in at the runtime + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmEngine Shared Object(so) File in which DRM Engine defined + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t installDrmEngine(int uniqueId, const String8& drmEngineFile); + +private: + Mutex mLock; + sp<DrmManagerClient::OnInfoListener> mOnInfoListener; + +private: + static Mutex mMutex; + static sp<IDrmManagerService> mDrmManagerService; + static const sp<IDrmManagerService>& getDrmManagerService(); + static const String8 EMPTY_STRING; +}; + +}; + +#endif /* __DRM_MANAGER_CLIENT_IMPL_H__ */ + diff --git a/drm/libdrmframework/include/DrmManagerService.h b/drm/libdrmframework/include/DrmManagerService.h new file mode 100644 index 0000000..f346356 --- /dev/null +++ b/drm/libdrmframework/include/DrmManagerService.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DRM_MANAGER_SERVICE_H__ +#define __DRM_MANAGER_SERVICE_H__ + +#include <utils/RefBase.h> +#include <utils/KeyedVector.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include "IDrmManagerService.h" +#include "IDrmServiceListener.h" + +namespace android { + +class DrmManager; +class String8; +class Mutex; + +/** + * This is the implementation class for DRM manager service. This delegates + * the responsibility to DrmManager. + * + * The instance of this class is created while starting the DRM manager service. + * + */ +class DrmManagerService : public BnDrmManagerService { +public: + static void instantiate(); + +private: + DrmManagerService(); + virtual ~DrmManagerService(); + +public: + int addUniqueId(int uniqueId); + + void removeUniqueId(int uniqueId); + + void addClient(int uniqueId); + + void removeClient(int uniqueId); + + status_t setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& drmServiceListener); + + status_t installDrmEngine(int uniqueId, const String8& drmEngineFile); + + DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action); + + DrmMetadata* getMetadata(int uniqueId, const String8* path); + + bool canHandle(int uniqueId, const String8& path, const String8& mimeType); + + DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo); + + DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest); + + status_t saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath); + + String8 getOriginalMimeType(int uniqueId, const String8& path); + + int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType); + + int checkRightsStatus(int uniqueId, const String8& path,int action); + + status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + status_t setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + bool validateAction(int uniqueId, const String8& path, + int action, const ActionDescription& description); + + status_t removeRights(int uniqueId, const String8& path); + + status_t removeAllRights(int uniqueId); + + int openConvertSession(int uniqueId, const String8& mimeType); + + DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData); + + DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId); + + status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray); + + DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length); + + DecryptHandle* openDecryptSession(int uniqueId, const char* uri); + + status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV); + + status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId); + + ssize_t pread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset); + +private: + DrmManager* mDrmManager; +}; + +}; + +#endif /* __DRM_MANAGER_SERVICE_H__ */ + diff --git a/drm/libdrmframework/include/IDrmIOService.h b/drm/libdrmframework/include/IDrmIOService.h new file mode 100644 index 0000000..5e0d907 --- /dev/null +++ b/drm/libdrmframework/include/IDrmIOService.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __IDRM_IO_SERVICE_H__ +#define __IDRM_IO_SERVICE_H__ + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +/** + * This is the interface class for DRM IO service. + * + */ +class IDrmIOService : public IInterface +{ +public: + enum { + WRITE_TO_FILE = IBinder::FIRST_CALL_TRANSACTION, + READ_FROM_FILE + }; + +public: + DECLARE_META_INTERFACE(DrmIOService); + +public: + /** + * Writes the data into the file path provided + * + * @param[in] filePath Path of the file + * @param[in] dataBuffer Data to write + */ + virtual void writeToFile(const String8& filePath, const String8& dataBuffer) = 0; + + /** + * Reads the data from the file path provided + * + * @param[in] filePath Path of the file + * @return Data read from the file + */ + virtual String8 readFromFile(const String8& filePath) = 0; +}; + +/** + * This is the Binder implementation class for DRM IO service. + */ +class BpDrmIOService: public BpInterface<IDrmIOService> +{ +public: + BpDrmIOService(const sp<IBinder>& impl) + : BpInterface<IDrmIOService>(impl) {} + + virtual void writeToFile(const String8& filePath, const String8& dataBuffer); + + virtual String8 readFromFile(const String8& filePath); +}; + +/** + * This is the Binder implementation class for DRM IO service. + */ +class BnDrmIOService: public BnInterface<IDrmIOService> +{ +public: + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); +}; + +}; + +#endif /* __IDRM_IO_SERVICE_H__ */ + diff --git a/drm/libdrmframework/include/IDrmManagerService.h b/drm/libdrmframework/include/IDrmManagerService.h new file mode 100644 index 0000000..f1dabd3 --- /dev/null +++ b/drm/libdrmframework/include/IDrmManagerService.h @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __IDRM_MANAGER_SERVICE_H__ +#define __IDRM_MANAGER_SERVICE_H__ + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <drm/drm_framework_common.h> +#include "IDrmServiceListener.h" + +namespace android { + +class DrmContentIds; +class DrmConstraints; +class DrmMetadata; +class DrmRights; +class DrmInfo; +class DrmInfoStatus; +class DrmInfoRequest; +class DrmSupportInfo; +class DrmConvertedStatus; +class String8; +class ActionDescription; + +/** + * This is the interface class for DRM Manager service. + * + */ +class IDrmManagerService : public IInterface +{ +public: + enum { + ADD_UNIQUEID = IBinder::FIRST_CALL_TRANSACTION, + REMOVE_UNIQUEID, + ADD_CLIENT, + REMOVE_CLIENT, + SET_DRM_SERVICE_LISTENER, + INSTALL_DRM_ENGINE, + GET_CONSTRAINTS_FROM_CONTENT, + GET_METADATA_FROM_CONTENT, + CAN_HANDLE, + PROCESS_DRM_INFO, + ACQUIRE_DRM_INFO, + SAVE_RIGHTS, + GET_ORIGINAL_MIMETYPE, + GET_DRM_OBJECT_TYPE, + CHECK_RIGHTS_STATUS, + CONSUME_RIGHTS, + SET_PLAYBACK_STATUS, + VALIDATE_ACTION, + REMOVE_RIGHTS, + REMOVE_ALL_RIGHTS, + OPEN_CONVERT_SESSION, + CONVERT_DATA, + CLOSE_CONVERT_SESSION, + GET_ALL_SUPPORT_INFO, + OPEN_DECRYPT_SESSION, + OPEN_DECRYPT_SESSION_FROM_URI, + CLOSE_DECRYPT_SESSION, + INITIALIZE_DECRYPT_UNIT, + DECRYPT, + FINALIZE_DECRYPT_UNIT, + PREAD + }; + +public: + DECLARE_META_INTERFACE(DrmManagerService); + +public: + virtual int addUniqueId(int uniqueId) = 0; + + virtual void removeUniqueId(int uniqueId) = 0; + + virtual void addClient(int uniqueId) = 0; + + virtual void removeClient(int uniqueId) = 0; + + virtual status_t setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& infoListener) = 0; + + virtual status_t installDrmEngine(int uniqueId, const String8& drmEngineFile) = 0; + + virtual DrmConstraints* getConstraints( + int uniqueId, const String8* path, const int action) = 0; + + virtual DrmMetadata* getMetadata(int uniqueId, const String8* path) = 0; + + virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType) = 0; + + virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0; + + virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest) = 0; + + virtual status_t saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) = 0; + + virtual String8 getOriginalMimeType(int uniqueId, const String8& path) = 0; + + virtual int getDrmObjectType( + int uniqueId, const String8& path, const String8& mimeType) = 0; + + virtual int checkRightsStatus(int uniqueId, const String8& path, int action) = 0; + + virtual status_t consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0; + + virtual status_t setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) = 0; + + virtual bool validateAction( + int uniqueId, const String8& path, + int action, const ActionDescription& description) = 0; + + virtual status_t removeRights(int uniqueId, const String8& path) = 0; + + virtual status_t removeAllRights(int uniqueId) = 0; + + virtual int openConvertSession(int uniqueId, const String8& mimeType) = 0; + + virtual DrmConvertedStatus* convertData( + int uniqueId, int convertId, const DrmBuffer* inputData) = 0; + + virtual DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId) = 0; + + virtual status_t getAllSupportInfo( + int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) = 0; + + virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length) = 0; + + virtual DecryptHandle* openDecryptSession(int uniqueId, const char* uri) = 0; + + virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0; + + virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) = 0; + + virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0; + + virtual status_t finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0; + + virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes,off_t offset) = 0; +}; + +/** + * This is the Binder implementation class for DRM Manager service. + */ +class BpDrmManagerService: public BpInterface<IDrmManagerService> +{ +public: + BpDrmManagerService(const sp<IBinder>& impl) + : BpInterface<IDrmManagerService>(impl) {} + + virtual int addUniqueId(int uniqueId); + + virtual void removeUniqueId(int uniqueId); + + virtual void addClient(int uniqueId); + + virtual void removeClient(int uniqueId); + + virtual status_t setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& infoListener); + + virtual status_t installDrmEngine(int uniqueId, const String8& drmEngineFile); + + virtual DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action); + + virtual DrmMetadata* getMetadata(int uniqueId, const String8* path); + + virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType); + + virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo); + + virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest); + + virtual status_t saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath); + + virtual String8 getOriginalMimeType(int uniqueId, const String8& path); + + virtual int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType); + + virtual int checkRightsStatus(int uniqueId, const String8& path, int action); + + virtual status_t consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + virtual status_t setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + virtual bool validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + + virtual status_t removeRights(int uniqueId, const String8& path); + + virtual status_t removeAllRights(int uniqueId); + + virtual int openConvertSession(int uniqueId, const String8& mimeType); + + virtual DrmConvertedStatus* convertData( + int uniqueId, int convertId, const DrmBuffer* inputData); + + virtual DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId); + + virtual status_t getAllSupportInfo( + int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray); + + virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length); + + virtual DecryptHandle* openDecryptSession(int uniqueId, const char* uri); + + virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV); + + virtual status_t finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId); + + virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset); +}; + +/** + * This is the Binder implementation class for DRM Manager service. + */ +class BnDrmManagerService: public BnInterface<IDrmManagerService> +{ +public: + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); +}; + +}; + +#endif /* __IDRM_MANAGER_SERVICE_H__ */ + diff --git a/drm/libdrmframework/include/IDrmServiceListener.h b/drm/libdrmframework/include/IDrmServiceListener.h new file mode 100644 index 0000000..7f7109f --- /dev/null +++ b/drm/libdrmframework/include/IDrmServiceListener.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __IDRM_SERVICE_LISTENER_H__ +#define __IDRM_SERVICE_LISTENER_H__ + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +class DrmInfoEvent; + +/** + * This is the interface class for DRM service listener. + * + */ +class IDrmServiceListener : public IInterface +{ +public: + enum { + NOTIFY = IBinder::FIRST_CALL_TRANSACTION, + }; + +public: + DECLARE_META_INTERFACE(DrmServiceListener); + +public: + virtual status_t notify(const DrmInfoEvent& event) = 0; +}; + +/** + * This is the Binder implementation class for DRM service listener. + */ +class BpDrmServiceListener: public BpInterface<IDrmServiceListener> +{ +public: + BpDrmServiceListener(const sp<IBinder>& impl) + : BpInterface<IDrmServiceListener>(impl) {} + + virtual status_t notify(const DrmInfoEvent& event); +}; + +/** + * This is the Binder implementation class for DRM service listener. + */ +class BnDrmServiceListener: public BnInterface<IDrmServiceListener> +{ +public: + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); +}; + +}; + +#endif /* __IDRM_SERVICE_LISTENER_H__ */ + diff --git a/drm/libdrmframework/include/PlugInManager.h b/drm/libdrmframework/include/PlugInManager.h new file mode 100644 index 0000000..9ad195f --- /dev/null +++ b/drm/libdrmframework/include/PlugInManager.h @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PLUGIN_MANAGER_H__ +#define __PLUGIN_MANAGER_H__ + +#include <dlfcn.h> +#include <sys/types.h> +#include <dirent.h> + +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/KeyedVector.h> + +namespace android { + +const char* const PLUGIN_MANAGER_CREATE = "create"; +const char* const PLUGIN_MANAGER_DESTROY = "destroy"; +const char* const PLUGIN_EXTENSION = ".so"; + +/** + * This is the template class for Plugin manager. + * + * The DrmManager uses this class to handle the plugins. + * + */ +template<typename Type> +class TPlugInManager { +private: + typedef void* HANDLE; + typedef Type* create_t(void); + typedef void destroy_t(Type*); + typedef create_t* FPCREATE; + typedef destroy_t* FPDESTORY; + + typedef struct _PlugInContainer { + String8 sPath; + HANDLE hHandle; + FPCREATE fpCreate; + FPDESTORY fpDestory; + Type* pInstance; + + _PlugInContainer(): + sPath("") + ,hHandle(NULL) + ,fpCreate(NULL) + ,fpDestory(NULL) + ,pInstance(NULL) + {} + } PlugInContainer; + + typedef KeyedVector<String8, PlugInContainer*> PlugInMap; + PlugInMap m_plugInMap; + + typedef Vector<String8> PlugInIdList; + PlugInIdList m_plugInIdList; + +public: + /** + * Load all the plug-ins in the specified directory + * + * @param[in] rsPlugInDirPath + * Directory path which plug-ins (dynamic library) are stored + * @note Plug-ins should be implemented according to the specification + */ + void loadPlugIns(const String8& rsPlugInDirPath) { + Vector<String8> plugInFileList = getPlugInPathList(rsPlugInDirPath); + + if (!plugInFileList.isEmpty()) { + for (unsigned int i = 0; i < plugInFileList.size(); ++i) { + loadPlugIn(plugInFileList[i]); + } + } + } + + /** + * Unload all the plug-ins + * + */ + void unloadPlugIns() { + for (unsigned int i = 0; i < m_plugInIdList.size(); ++i) { + unloadPlugIn(m_plugInIdList[i]); + } + m_plugInIdList.clear(); + } + + /** + * Get all the IDs of available plug-ins + * + * @return[in] plugInIdList + * String type Vector in which all plug-in IDs are stored + */ + Vector<String8> getPlugInIdList() const { + return m_plugInIdList; + } + + /** + * Get a plug-in reference of specified ID + * + * @param[in] rsPlugInId + * Plug-in ID to be used + * @return plugIn + * Reference of specified plug-in instance + */ + Type& getPlugIn(const String8& rsPlugInId) { + if (!contains(rsPlugInId)) { + // This error case never happens + } + return *(m_plugInMap.valueFor(rsPlugInId)->pInstance); + } + +public: + /** + * Load a plug-in stored in the specified path + * + * @param[in] rsPlugInPath + * Plug-in (dynamic library) file path + * @note Plug-in should be implemented according to the specification + */ + void loadPlugIn(const String8& rsPlugInPath) { + if (contains(rsPlugInPath)) { + return; + } + + PlugInContainer* pPlugInContainer = new PlugInContainer(); + + pPlugInContainer->hHandle = dlopen(rsPlugInPath.string(), RTLD_LAZY); + + if (NULL == pPlugInContainer->hHandle) { + delete pPlugInContainer; + pPlugInContainer = NULL; + return; + } + + pPlugInContainer->sPath = rsPlugInPath; + pPlugInContainer->fpCreate + = (FPCREATE)dlsym(pPlugInContainer->hHandle, PLUGIN_MANAGER_CREATE); + pPlugInContainer->fpDestory + = (FPDESTORY)dlsym(pPlugInContainer->hHandle, PLUGIN_MANAGER_DESTROY); + + if (NULL != pPlugInContainer->fpCreate && NULL != pPlugInContainer->fpDestory) { + pPlugInContainer->pInstance = (Type*)pPlugInContainer->fpCreate(); + m_plugInIdList.add(rsPlugInPath); + m_plugInMap.add(rsPlugInPath, pPlugInContainer); + } else { + dlclose(pPlugInContainer->hHandle); + delete pPlugInContainer; + pPlugInContainer = NULL; + return; + } + } + + /** + * Unload a plug-in stored in the specified path + * + * @param[in] rsPlugInPath + * Plug-in (dynamic library) file path + */ + void unloadPlugIn(const String8& rsPlugInPath) { + if (!contains(rsPlugInPath)) { + return; + } + + PlugInContainer* pPlugInContainer = m_plugInMap.valueFor(rsPlugInPath); + pPlugInContainer->fpDestory(pPlugInContainer->pInstance); + dlclose(pPlugInContainer->hHandle); + + m_plugInMap.removeItem(rsPlugInPath); + delete pPlugInContainer; + pPlugInContainer = NULL; + } + +private: + /** + * True if TPlugInManager contains rsPlugInId + */ + bool contains(const String8& rsPlugInId) { + return m_plugInMap.indexOfKey(rsPlugInId) != NAME_NOT_FOUND; + } + + /** + * Return file path list of plug-ins stored in the specified directory + * + * @param[in] rsDirPath + * Directory path in which plug-ins are stored + * @return plugInFileList + * String type Vector in which file path of plug-ins are stored + */ + Vector<String8> getPlugInPathList(const String8& rsDirPath) { + Vector<String8> fileList; + DIR* pDir = opendir(rsDirPath.string()); + struct dirent* pEntry = new dirent(); + + while (NULL != pDir && NULL != (pEntry = readdir(pDir))) { + if (!isPlugIn(pEntry)) { + continue; + } + String8 plugInPath; + plugInPath += rsDirPath; + plugInPath += "/"; + plugInPath += pEntry->d_name; + + fileList.add(plugInPath); + } + + if (NULL != pDir) { + closedir(pDir); + } + delete pEntry; + pEntry = NULL; + + return fileList; + } + + /** + * True if the input name denotes plug-in + */ + bool isPlugIn(const struct dirent* pEntry) const { + String8 sName(pEntry->d_name); + int extentionPos = sName.size() - String8(PLUGIN_EXTENSION).size(); + if (extentionPos < 0) { + return false; + } + return extentionPos == (int)sName.find(PLUGIN_EXTENSION); + } + + /** + * True if the input entry is "." or ".." + */ + bool isDotOrDDot(const struct dirent* pEntry) const { + String8 sName(pEntry->d_name); + return "." == sName || ".." == sName; + } + + /** + * True if input entry is directory + */ + bool isDirectory(const struct dirent* pEntry) const { + return DT_DIR == pEntry->d_type; + } + + /** + * True if input entry is regular file + */ + bool isRegularFile(const struct dirent* pEntry) const { + return DT_REG == pEntry->d_type; + } + + /** + * True if input entry is link + */ + bool isLink(const struct dirent* pEntry) const { + return DT_LNK == pEntry->d_type; + } +}; + +}; + +#endif /* __PLUGIN_MANAGER_H__ */ + diff --git a/drm/libdrmframework/include/ReadWriteUtils.h b/drm/libdrmframework/include/ReadWriteUtils.h new file mode 100644 index 0000000..529b342 --- /dev/null +++ b/drm/libdrmframework/include/ReadWriteUtils.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __READ_WRITE_UTILS_H__ +#define __READ_WRITE_UTILS_H__ + +#include <utils/FileMap.h> +#include <drm/drm_framework_common.h> + +namespace android { + +/** + * This is an utility class which performs IO operations. + * + */ +class ReadWriteUtils { +public: + /** + * Constructor for ReadWriteUtils + */ + ReadWriteUtils() {} + + /** + * Destructor for ReadWriteUtils + */ + virtual ~ReadWriteUtils(); + +public: + /** + * Reads the data from the file path provided + * + * @param[in] filePath Path of the file + * @return Data read from the file + */ + static String8 readBytes(const String8& filePath); + /** + * Reads the data into the given buffer from the file path provided + * + * @param[in] filePath Path of the file + * @param[out] buffer Data read from the file + * @return Length of the data read from the file + */ + static int readBytes(const String8& filePath, char** buffer); + /** + * Writes the data into the file path provided + * + * @param[in] filePath Path of the file + * @param[in] dataBuffer Data to write + */ + static void writeToFile(const String8& filePath, const String8& data); + /** + * Appends the data into the file path provided + * + * @param[in] filePath Path of the file + * @param[in] dataBuffer Data to append + */ + static void appendToFile(const String8& filePath, const String8& data); + +private: + FileMap* mFileMap; +}; + +}; + +#endif /* __READ_WRITE_UTILS_H__ */ + diff --git a/drm/libdrmframework/include/StringTokenizer.h b/drm/libdrmframework/include/StringTokenizer.h new file mode 100644 index 0000000..70e7558 --- /dev/null +++ b/drm/libdrmframework/include/StringTokenizer.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __STRING_TOKENIZER_H__ +#define __STRING_TOKENIZER_H__ + +#include <drm/drm_framework_common.h> + +namespace android { + +/** + * This is an utility class for String manipulation. + * + */ +class StringTokenizer { +public: + /** + * Iterator for string tokens + */ + class Iterator { + friend class StringTokenizer; + private: + Iterator(StringTokenizer* StringTokenizer) + : mStringTokenizer(StringTokenizer), mIndex(0) {} + + public: + Iterator(const Iterator& iterator); + Iterator& operator=(const Iterator& iterator); + virtual ~Iterator() {} + + public: + bool hasNext(); + String8& next(); + + private: + StringTokenizer* mStringTokenizer; + unsigned int mIndex; + }; + +public: + /** + * Constructor for StringTokenizer + * + * @param[in] string Complete string data + * @param[in] delimeter Delimeter used to split the string + */ + StringTokenizer(const String8& string, const String8& delimeter); + + /** + * Destructor for StringTokenizer + */ + ~StringTokenizer() {} + +private: + /** + * Splits the string according to the delimeter + */ + void splitString(const String8& string, const String8& delimeter); + +public: + /** + * Returns Iterator object to walk through the split string values + * + * @return Iterator object + */ + Iterator iterator(); + +private: + Vector<String8> mStringTokenizerVector; +}; + +}; +#endif /* __STRING_TOKENIZER_H__ */ + diff --git a/drm/libdrmframework/plugins/Android.mk b/drm/libdrmframework/plugins/Android.mk new file mode 100644 index 0000000..9ee7961 --- /dev/null +++ b/drm/libdrmframework/plugins/Android.mk @@ -0,0 +1,16 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include $(call all-subdir-makefiles) diff --git a/drm/libdrmframework/plugins/common/Android.mk b/drm/libdrmframework/plugins/common/Android.mk new file mode 100644 index 0000000..9ee7961 --- /dev/null +++ b/drm/libdrmframework/plugins/common/Android.mk @@ -0,0 +1,16 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include $(call all-subdir-makefiles) diff --git a/drm/libdrmframework/plugins/common/include/DrmEngineBase.h b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h new file mode 100644 index 0000000..67b6355 --- /dev/null +++ b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DRM_ENGINE_BASE_H__ +#define __DRM_ENGINE_BASE_H__ + +#include <drm/drm_framework_common.h> +#include "IDrmEngine.h" + +namespace android { + +/** + * This class is an interface for plug-in developers + * + * Responsibility of this class is control the sequence of actual plug-in. + * All each plug-in developer has to do is implement onXXX() type virtual interfaces. + */ +class DrmEngineBase : public IDrmEngine { +public: + DrmEngineBase(); + virtual ~DrmEngineBase(); + +public: + DrmConstraints* getConstraints(int uniqueId, const String8* path, int action); + + DrmMetadata* getMetadata(int uniqueId, const String8* path); + + status_t initialize(int uniqueId); + + status_t setOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener); + + status_t terminate(int uniqueId); + + bool canHandle(int uniqueId, const String8& path); + + DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo); + + status_t saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath); + + DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest); + + String8 getOriginalMimeType(int uniqueId, const String8& path); + + int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType); + + int checkRightsStatus(int uniqueId, const String8& path, int action); + + status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + status_t setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + bool validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + + status_t removeRights(int uniqueId, const String8& path); + + status_t removeAllRights(int uniqueId); + + status_t openConvertSession(int uniqueId, int convertId); + + DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData); + + DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId); + + DrmSupportInfo* getSupportInfo(int uniqueId); + + status_t openDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length); + + status_t openDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, const char* uri); + + status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV); + + status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId); + + ssize_t pread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset); + +protected: + ///////////////////////////////////////////////////// + // Interface for plug-in developers // + // each plug-in has to implement following method // + ///////////////////////////////////////////////////// + /** + * Get constraint information associated with input content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Actions defined such as, + * Action::DEFAULT, Action::PLAY, etc + * @return DrmConstraints + * key-value pairs of constraint are embedded in it + * @note + * In case of error, return NULL + */ + virtual DrmConstraints* onGetConstraints( + int uniqueId, const String8* path, int action) = 0; + + /** + * Get metadata information associated with input content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @return DrmMetadata + * key-value pairs of metadata + * @note + * In case of error, return NULL + */ + virtual DrmMetadata* onGetMetadata(int uniqueId, const String8* path) = 0; + + /** + * Initialize plug-in + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onInitialize(int uniqueId) = 0; + + /** + * Register a callback to be invoked when the caller required to + * receive necessary information + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] infoListener Listener + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onSetOnInfoListener( + int uniqueId, const IDrmEngine::OnInfoListener* infoListener) = 0; + + /** + * Terminate the plug-in + * and release resource bound to plug-in + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onTerminate(int uniqueId) = 0; + + /** + * Get whether the given content can be handled by this plugin or not + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path the protected object + * @return bool + * Returns true if this plugin can handle , false in case of not able to handle + */ + virtual bool onCanHandle(int uniqueId, const String8& path) = 0; + + /** + * Executes given drm information based on its type + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmInfo Information needs to be processed + * @return DrmInfoStatus + * instance as a result of processing given input + */ + virtual DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0; + + /** + * Save DRM rights to specified rights path + * and make association with content path + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmRights DrmRights to be saved + * @param[in] rightsPath File path where rights to be saved + * @param[in] contentPath File path where content was saved + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onSaveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightspath, const String8& contentPath) = 0; + + /** + * Retrieves necessary information for registration, unregistration or rights + * acquisition information. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmInfoRequest Request information to retrieve drmInfo + * @return DrmInfo + * instance as a result of processing given input + */ + virtual DrmInfo* onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest) = 0; + + /** + * Retrieves the mime type embedded inside the original content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @return String8 + * Returns mime-type of the original content, such as "video/mpeg" + */ + virtual String8 onGetOriginalMimeType(int uniqueId, const String8& path) = 0; + + /** + * Retrieves the type of the protected object (content, rights, etc..) + * using specified path or mimetype. At least one parameter should be non null + * to retrieve DRM object type + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the content or null. + * @param[in] mimeType Mime type of the content or null. + * @return type of the DRM content, + * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT + */ + virtual int onGetDrmObjectType( + int uniqueId, const String8& path, const String8& mimeType) = 0; + + /** + * Check whether the given content has valid rights or not + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Action to perform (Action::DEFAULT, Action::PLAY, etc) + * @return the status of the rights for the protected content, + * such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc. + */ + virtual int onCheckRightsStatus(int uniqueId, const String8& path, int action) = 0; + + /** + * Consumes the rights for a content. + * If the reserve parameter is true the rights is reserved until the same + * application calls this api again with the reserve parameter set to false. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc) + * @param[in] reserve True if the rights should be reserved. + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, + int action, bool reserve) = 0; + + /** + * Informs the DRM Engine about the playback actions performed on the DRM files. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE) + * @param[in] position Position in the file (in milliseconds) where the start occurs. + * Only valid together with Playback::START. + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onSetPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) = 0; + + /** + * Validates whether an action on the DRM content is allowed or not. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc) + * @param[in] description Detailed description of the action + * @return true if the action is allowed. + */ + virtual bool onValidateAction(int uniqueId, const String8& path, + int action, const ActionDescription& description) = 0; + + /** + * Removes the rights associated with the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onRemoveRights(int uniqueId, const String8& path) = 0; + + /** + * Removes all the rights information of each plug-in associated with + * DRM framework. Will be used in master reset + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onRemoveAllRights(int uniqueId) = 0; + + /** + * This API is for Forward Lock based DRM scheme. + * Each time the application tries to download a new DRM file + * which needs to be converted, then the application has to + * begin with calling this API. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] convertId Handle for the convert session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onOpenConvertSession(int uniqueId, int convertId) = 0; + + /** + * Accepts and converts the input data which is part of DRM file. + * The resultant converted data and the status is returned in the DrmConvertedInfo + * object. This method will be called each time there are new block + * of data received by the application. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] convertId Handle for the convert session + * @param[in] inputData Input Data which need to be converted + * @return Return object contains the status of the data conversion, + * the output converted data and offset. In this case the + * application will ignore the offset information. + */ + virtual DrmConvertedStatus* onConvertData( + int uniqueId, int convertId, const DrmBuffer* inputData) = 0; + + /** + * Informs the Drm Agent when there is no more data which need to be converted + * or when an error occurs. Upon successful conversion of the complete data, + * the agent will inform that where the header and body signature + * should be added. This signature appending is needed to integrity + * protect the converted file. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] convertId Handle for the convert session + * @return Return object contains the status of the data conversion, + * the header and body signature data. It also informs + * the application on which offset these signature data + * should be appended. + */ + virtual DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId) = 0; + + /** + * Returns the information about the Drm Engine capabilities which includes + * supported MimeTypes and file suffixes. + * + * @param[in] uniqueId Unique identifier for a session + * @return DrmSupportInfo + * instance which holds the capabilities of a plug-in + */ + virtual DrmSupportInfo* onGetSupportInfo(int uniqueId) = 0; + + /** + * Open the decrypt session to decrypt the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the current decryption session + * @param[in] fd File descriptor of the protected content to be decrypted + * @param[in] offset Start position of the content + * @param[in] length The length of the protected content + * @return + * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success + */ + virtual status_t onOpenDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) = 0; + + /** + * Open the decrypt session to decrypt the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the current decryption session + * @param[in] uri Path of the protected content to be decrypted + * @return + * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success + */ + virtual status_t onOpenDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, const char* uri) = 0; + + /** + * Close the decrypt session for the given handle + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0; + + /** + * Initialize decryption for the given unit of the protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptId Handle for the decryption session + * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID + * @param[in] headerInfo Information for initializing decryption of this decrypUnit + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) = 0; + + /** + * Decrypt the protected content buffers for the given unit + * This method will be called any number of times, based on number of + * encrypted streams received from application. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptId Handle for the decryption session + * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID + * @param[in] encBuffer Encrypted data block + * @param[out] decBuffer Decrypted data block + * @param[in] IV Optional buffer + * @return status_t + * Returns the error code for this API + * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED + * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED, + * DRM_ERROR_DECRYPT for failure. + */ + virtual status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0; + + /** + * Finalize decryption for the given unit of the protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t onFinalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0; + + /** + * Reads the specified number of bytes from an open DRM file. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[out] buffer Reference to the buffer that should receive the read data. + * @param[in] numBytes Number of bytes to read. + * @param[in] offset Offset with which to update the file position. + * + * @return Number of bytes read. Returns -1 for Failure. + */ + virtual ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset) = 0; +}; + +}; + +#endif /* __DRM_ENGINE_BASE_H__ */ + diff --git a/drm/libdrmframework/plugins/common/include/IDrmEngine.h b/drm/libdrmframework/plugins/common/include/IDrmEngine.h new file mode 100644 index 0000000..f839070 --- /dev/null +++ b/drm/libdrmframework/plugins/common/include/IDrmEngine.h @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __IDRM_ENGINE_H__ +#define __IDRM_ENGINE_H__ + +#include <drm/drm_framework_common.h> + +namespace android { + +class DrmContentIds; +class DrmConstraints; +class DrmMetadata; +class DrmRights; +class DrmInfo; +class DrmInfoStatus; +class DrmConvertedStatus; +class DrmInfoRequest; +class DrmSupportInfo; +class DrmInfoEvent; + +/** + * This class is an interface for plug-in user + * + * Responsibility of this class is provide generic interface to DRM Engine Manager. + * Each interface need to be as abstract as possible. + */ +class IDrmEngine { +public: + virtual ~IDrmEngine() { + } + +public: + class OnInfoListener { + + public: + virtual void onInfo(const DrmInfoEvent& event) = 0; + + virtual ~OnInfoListener() { } + }; + +public: + + ////////////////////////////////// + // Implementation of IDrmEngine // + ////////////////////////////////// + + /** + * Initialize plug-in + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t initialize(int uniqueId) = 0; + + /** + * Register a callback to be invoked when the caller required to + * receive necessary information + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] infoListener Listener + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t setOnInfoListener( + int uniqueId, const IDrmEngine::OnInfoListener* infoListener) = 0; + + /** + * Terminate the plug-in + * and release resource bound to plug-in + * e.g.) release native resource + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t terminate(int uniqueId) = 0; + + /** + * Get constraint information associated with input content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Actions defined such as, + * Action::DEFAULT, Action::PLAY, etc + * @return DrmConstraints + * key-value pairs of constraint are embedded in it + * @note + * In case of error, return NULL + */ + virtual DrmConstraints* getConstraints( + int uniqueId, const String8* path, int action) = 0; + + /** + * Get metadata information associated with input content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @return DrmMetadata + * key-value pairs of metadata + * @note + * In case of error, return NULL + */ + virtual DrmMetadata* getMetadata(int uniqueId, const String8* path) = 0; + + /** + * Get whether the given content can be handled by this plugin or not + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path the protected object + * @return bool + * true if this plugin can handle , false in case of not able to handle + */ + virtual bool canHandle(int uniqueId, const String8& path) = 0; + + /** + * Executes given drm information based on its type + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmInfo Information needs to be processed + * @return DrmInfoStatus + * instance as a result of processing given input + */ + virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0; + + /** + * Retrieves necessary information for registration, unregistration or rights + * acquisition information. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmInfoRequest Request information to retrieve drmInfo + * @return DrmInfo + * instance as a result of processing given input + */ + virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) = 0; + + /** + * Save DRM rights to specified rights path + * and make association with content path + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmRights DrmRights to be saved + * @param[in] rightsPath File path where rights to be saved + * @param[in] contentPath File path where content was saved + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) = 0; + + /** + * Retrieves the mime type embedded inside the original content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @return String8 + * Returns mime-type of the original content, such as "video/mpeg" + */ + virtual String8 getOriginalMimeType(int uniqueId, const String8& path) = 0; + + /** + * Retrieves the type of the protected object (content, rights, etc..) + * using specified path or mimetype. At least one parameter should be non null + * to retrieve DRM object type + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the content or null. + * @param[in] mimeType Mime type of the content or null. + * @return type of the DRM content, + * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT + */ + virtual int getDrmObjectType( + int uniqueId, const String8& path, const String8& mimeType) = 0; + + /** + * Check whether the given content has valid rights or not + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Action to perform (Action::DEFAULT, Action::PLAY, etc) + * @return the status of the rights for the protected content, + * such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc. + */ + virtual int checkRightsStatus(int uniqueId, const String8& path, int action) = 0; + + /** + * Consumes the rights for a content. + * If the reserve parameter is true the rights is reserved until the same + * application calls this api again with the reserve parameter set to false. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc) + * @param[in] reserve True if the rights should be reserved. + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0; + + /** + * Informs the DRM Engine about the playback actions performed on the DRM files. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE) + * @param[in] position Position in the file (in milliseconds) where the start occurs. + * Only valid together with Playback::START. + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t setPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle, + int playbackStatus, int position) = 0; + + /** + * Validates whether an action on the DRM content is allowed or not. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc) + * @param[in] description Detailed description of the action + * @return true if the action is allowed. + */ + virtual bool validateAction(int uniqueId, const String8& path, + int action, const ActionDescription& description) = 0; + + /** + * Removes the rights associated with the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t removeRights(int uniqueId, const String8& path) = 0; + + /** + * Removes all the rights information of each plug-in associated with + * DRM framework. Will be used in master reset + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t removeAllRights(int uniqueId) = 0; + + /** + * This API is for Forward Lock based DRM scheme. + * Each time the application tries to download a new DRM file + * which needs to be converted, then the application has to + * begin with calling this API. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] convertId Handle for the convert session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t openConvertSession(int uniqueId, int convertId) = 0; + + /** + * Accepts and converts the input data which is part of DRM file. + * The resultant converted data and the status is returned in the DrmConvertedInfo + * object. This method will be called each time there are new block + * of data received by the application. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] convertId Handle for the convert session + * @param[in] inputData Input Data which need to be converted + * @return Return object contains the status of the data conversion, + * the output converted data and offset. In this case the + * application will ignore the offset information. + */ + virtual DrmConvertedStatus* convertData( + int uniqueId, int convertId, const DrmBuffer* inputData) = 0; + + /** + * Informs the Drm Agent when there is no more data which need to be converted + * or when an error occurs. Upon successful conversion of the complete data, + * the agent will inform that where the header and body signature + * should be added. This signature appending is needed to integrity + * protect the converted file. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] convertId Handle for the convert session + * @return Return object contains the status of the data conversion, + * the header and body signature data. It also informs + * the application on which offset these signature data + * should be appended. + */ + virtual DrmConvertedStatus* closeConvertSession( int uniqueId, int convertId) = 0; + + /** + * Returns the information about the Drm Engine capabilities which includes + * supported MimeTypes and file suffixes. + * + * @param[in] uniqueId Unique identifier for a session + * @return DrmSupportInfo + * instance which holds the capabilities of a plug-in + */ + virtual DrmSupportInfo* getSupportInfo(int uniqueId) = 0; + + /** + * Open the decrypt session to decrypt the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the current decryption session + * @param[in] fd File descriptor of the protected content to be decrypted + * @param[in] offset Start position of the content + * @param[in] length The length of the protected content + * @return + * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success + */ + virtual status_t openDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) = 0; + + /** + * Open the decrypt session to decrypt the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the current decryption session + * @param[in] uri Path of the protected content to be decrypted + * @return + * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success + */ + virtual status_t openDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, const char* uri) = 0; + + /** + * Close the decrypt session for the given handle + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0; + + /** + * Initialize decryption for the given unit of the protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID + * @param[in] headerInfo Information for initializing decryption of this decrypUnit + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) = 0; + + /** + * Decrypt the protected content buffers for the given unit + * This method will be called any number of times, based on number of + * encrypted streams received from application. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID + * @param[in] encBuffer Encrypted data block + * @param[out] decBuffer Decrypted data block + * @param[in] IV Optional buffer + * @return status_t + * Returns the error code for this API + * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED + * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED, + * DRM_ERROR_DECRYPT for failure. + */ + virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0; + + /** + * Finalize decryption for the given unit of the protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + virtual status_t finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0; + + /** + * Reads the specified number of bytes from an open DRM file. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[out] buffer Reference to the buffer that should receive the read data. + * @param[in] numBytes Number of bytes to read. + * @param[in] offset Offset with which to update the file position. + * + * @return Number of bytes read. Returns -1 for Failure. + */ + virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset) = 0; +}; + +}; + +#endif /* __IDRM_ENGINE_H__ */ + diff --git a/drm/libdrmframework/plugins/common/util/Android.mk b/drm/libdrmframework/plugins/common/util/Android.mk new file mode 100644 index 0000000..15dda80 --- /dev/null +++ b/drm/libdrmframework/plugins/common/util/Android.mk @@ -0,0 +1,52 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + src/MimeTypeUtil.cpp + +LOCAL_MODULE := libdrmutility + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libdl \ + libdvm \ + libandroid_runtime \ + libnativehelper \ + liblog + + +base := frameworks/base + +LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) \ + $(base)/include \ + $(base)/include/drm \ + $(base)/include/drm/plugins \ + $(LOCAL_PATH)/include + + +ifneq ($(TARGET_BUILD_VARIANT),user) +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/tools + +endif + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) diff --git a/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h b/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h new file mode 100644 index 0000000..4d12a61 --- /dev/null +++ b/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MIMETYPEUTIL_H__ +#define __MIMETYPEUTIL_H__ + +#include <utils/String8.h> + +namespace android { + +class MimeTypeUtil { + +public: + + MimeTypeUtil() {} + + virtual ~MimeTypeUtil() {} + +/** + * May convert the mimetype if there is a well known + * replacement mimetype otherwise the original mimetype + * is returned. + * + * @param mimeType - mimetype in lower case to convert. + * + * @return mimetype or null. + */ +static String8 convertMimeType(String8& mimeType); + +}; +}; + +#endif /* __MIMETYPEUTIL_H__ */ diff --git a/drm/libdrmframework/plugins/common/util/include/SessionMap.h b/drm/libdrmframework/plugins/common/util/include/SessionMap.h new file mode 100644 index 0000000..3dff58c --- /dev/null +++ b/drm/libdrmframework/plugins/common/util/include/SessionMap.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SESSIONMAP_H__ +#define __SESSIONMAP_H__ + +#include <utils/KeyedVector.h> + +namespace android { + +/** + * A wrapper template class for handling DRM Engine sessions. + */ +template <typename NODE> +class SessionMap { + +public: + KeyedVector<int, NODE> map; + + SessionMap() {} + + virtual ~SessionMap() { + destroyMap(); + } + +/** + * Adds a new value in the session map table. It expects memory to be allocated already + * for the session object + * + * @param key - key or Session ID + * @param value - session object to add + * + * @return boolean result of adding value. returns false if key is already exist. + */ +bool addValue(int key, NODE value) { + bool result = false; + + if (!isCreated(key)) { + map.add(key, value); + result = true; + } + + return result; +} + + +/** + * returns the session object by the key + * + * @param key - key or Session ID + * + * @return session object as per the key + */ +NODE getValue(int key) { + NODE value = NULL; + + if (isCreated(key)) { + value = (NODE) map.valueFor(key); + } + + return value; +} + +/** + * returns the number of objects in the session map table + * + * @return count of number of session objects. + */ +int getSize() { + return map.size(); +} + +/** + * returns the session object by the index in the session map table + * + * @param index - index of the value required + * + * @return session object as per the index + */ +NODE getValueAt(unsigned int index) { + NODE value = NULL; + + if (map.size() > index) { + value = map.valueAt(index); + } + + return value; +} + +/** + * deletes the object from session map. It also frees up memory for the session object. + * + * @param key - key of the value to be deleted + * + */ +void removeValue(int key) { + deleteValue(getValue(key)); + map.removeItem(key); +} + +/** + * decides if session is already created. + * + * @param key - key of the value for the session + * + * @return boolean result of whether session is created + */ +bool isCreated(int key) { + return (0 <= map.indexOfKey(key)); +} + +/** + * empty the entire session table. It releases all the memory for session objects. + */ +void destroyMap() { + int size = map.size(); + int i = 0; + + for (i = 0; i < size; i++) { + deleteValue(map.valueAt(i)); + } + + map.clear(); +} + +/** + * free up the memory for the session object. + * Make sure if any reference to the session object anywhere, otherwise it will be a + * dangle pointer after this call. + * + * @param value - session object to free + * + */ +void deleteValue(NODE value) { + delete value; +} + +}; + +}; + +#endif /* __SESSIONMAP_H__ */ diff --git a/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp new file mode 100644 index 0000000..4ee903e --- /dev/null +++ b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <MimeTypeUtil.h> +#include <utils/Log.h> + +namespace android { + +#undef LOG_TAG +#define LOG_TAG "MimeTypeUtil" + +enum { + MIMETYPE_AUDIO = 0, + MIMETYPE_APPLICATION = 1, + MIMETYPE_IMAGE = 2, + MIMETYPE_VIDEO = 3, + MIMETYPE_LAST = -1, +}; + +struct MimeGroup{ + int type; // Audio, video,.. use the enum values + const char* pGroup; // "audio/", "video/",.. should contain the last "/" + int size; // Number of bytes. e.g. "audio/" = 6 bytes +}; + +struct MimeTypeList{ + int type; + const char* pMimeExt; // Everything after the '/' e.g. audio/x-mpeg -> "x-mpeg" + int size; // Number of bytes. e.g. "x-mpeg" = 6 bytes + const char* pMimeType; // Mimetype that should be returned +}; + + +// Known mimetypes by android +static const char mime_type_audio_mpeg[] = "audio/mpeg"; +static const char mime_type_audio_3gpp[] = "audio/3gpp"; +static const char mime_type_audio_amr[] = "audio/amr-wb"; +static const char mime_type_audio_aac[] = "audio/mp4a-latm"; +static const char mime_type_audio_wav[] = "audio/wav"; + +static const char mime_type_video_mpeg4[] = "video/mpeg4"; +static const char mime_type_video_3gpp[] = "video/3gpp"; + +// Known mimetype groups +static const char mime_group_audio[] = "audio/"; +static const char mime_group_application[] = "application/"; +static const char mime_group_image[] = "image/"; +static const char mime_group_video[] = "video/"; + +static struct MimeGroup mimeGroup[] = { + {MIMETYPE_AUDIO, mime_group_audio, sizeof(mime_group_audio)-1}, + {MIMETYPE_APPLICATION, mime_group_application, sizeof(mime_group_application)-1}, + {MIMETYPE_IMAGE, mime_group_image, sizeof(mime_group_image)-1}, + {MIMETYPE_VIDEO, mime_group_video, sizeof(mime_group_video)-1}, + {MIMETYPE_LAST, NULL, 0} // Must be last entry +}; + +// List of all mimetypes that should be converted. +static struct MimeTypeList mimeTypeList[] = { + // Mp3 mime types + {MIMETYPE_AUDIO, "mp3", sizeof("mp3")-1, mime_type_audio_mpeg}, + {MIMETYPE_AUDIO, "x-mpeg", sizeof("x-mpeg")-1, mime_type_audio_mpeg}, + {MIMETYPE_AUDIO, "x-mp3", sizeof("x-mp3")-1, mime_type_audio_mpeg}, + {MIMETYPE_AUDIO, "mpg", sizeof("mpg")-1, mime_type_audio_mpeg}, + {MIMETYPE_AUDIO, "mpg3", sizeof("mpg")-1, mime_type_audio_mpeg}, + {MIMETYPE_AUDIO, "x-mpg", sizeof("x-mpg")-1, mime_type_audio_mpeg}, + {MIMETYPE_AUDIO, "x-mpegaudio", sizeof("x-mpegaudio")-1, mime_type_audio_mpeg}, + + // 3gpp audio mime types + {MIMETYPE_AUDIO, "3gp", sizeof("3gp")-1, mime_type_audio_3gpp}, + + // Amr audio mime types + {MIMETYPE_AUDIO, "amr", sizeof("amr")-1, mime_type_audio_amr}, + + // Aac audio mime types + {MIMETYPE_AUDIO, "aac", sizeof("aac")-1, mime_type_audio_aac}, + + // Wav audio mime types + {MIMETYPE_AUDIO, "x-wav", sizeof("x-wav")-1, mime_type_audio_wav}, + + // Mpeg4 video mime types + {MIMETYPE_VIDEO, "mpg4", sizeof("mpg4")-1, mime_type_video_mpeg4}, + {MIMETYPE_VIDEO, "mp4v-es", sizeof("mp4v-es")-1, mime_type_video_mpeg4}, + + // 3gpp video mime types + {MIMETYPE_VIDEO, "3gp", sizeof("3gp")-1, mime_type_video_3gpp}, + + // Must be last entry + {MIMETYPE_LAST, NULL, 0, NULL} +}; + +/** + * May convert the mimetype if there is a well known + * replacement mimetype otherwise the original mimetype + * is returned. + * + * @param mimeType - mimetype in lower case to convert. + * + * @return mimetype or null. + */ +String8 MimeTypeUtil::convertMimeType(String8& mimeType) { + String8 result = mimeType; + const char* pTmp; + const char* pMimeType; + struct MimeGroup* pGroup; + struct MimeTypeList* pMimeItem; + int len; + + pMimeType = mimeType.string(); + if (NULL != pMimeType) { + /* Check which group the mimetype is */ + pGroup = mimeGroup; + + while (MIMETYPE_LAST != pGroup->type) { + if (0 == strncmp(pMimeType, pGroup->pGroup, pGroup->size)) { + break; + } + pGroup++; + } + + /* Go through the mimetype list. Only check items of the correct group */ + if (MIMETYPE_LAST != pGroup->type) { + pMimeItem = mimeTypeList; + len = strlen (pMimeType+pGroup->size); + + while (MIMETYPE_LAST != pMimeItem->type) { + if ((len == pMimeItem->size) && + (0 == strcmp(pMimeType+pGroup->size, pMimeItem->pMimeExt))) { + result = String8(pMimeItem->pMimeType); + break; + } + pMimeItem++; + } + } + LOGI("convertMimeType got mimetype %s, converted into mimetype %s", + pMimeType, result.string()); + } + + return result; +} +}; diff --git a/drm/libdrmframework/plugins/forward-lock/Android.mk b/drm/libdrmframework/plugins/forward-lock/Android.mk new file mode 100644 index 0000000..9ee7961 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/Android.mk @@ -0,0 +1,16 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include $(call all-subdir-makefiles) diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk new file mode 100644 index 0000000..d4a6f18 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk @@ -0,0 +1,67 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +base := frameworks/base + +# Determine whether the DRM framework uses 64-bit data types for file offsets and do the same. +ifneq ($(shell grep -c 'off64_t offset' $(base)/drm/libdrmframework/plugins/common/include/IDrmEngine.h), 0) +LOCAL_CFLAGS += -DUSE_64BIT_DRM_API +endif + +LOCAL_SRC_FILES:= \ + src/FwdLockEngine.cpp + +LOCAL_MODULE := libfwdlockengine + +LOCAL_SHARED_LIBRARIES := \ + libicui18n \ + libicuuc \ + libutils \ + libdl \ + libandroid_runtime \ + libnativehelper \ + libcrypto \ + libssl \ + libdrmframework + +LOCAL_STATIC_LIBRARIES := \ + libdrmutility \ + libdrmframeworkcommon \ + libfwdlock-common \ + libfwdlock-converter \ + libfwdlock-decoder + +LOCAL_PRELINK_MODULE := false + +LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) \ + $(base)/include/drm \ + $(base)/drm/libdrmframework/plugins/common/include \ + $(base)/drm/libdrmframework/plugins/common/util/include \ + $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/common \ + $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/converter \ + $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/decoder \ + $(LOCAL_PATH)/include \ + external/openssl/include + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm/plugins/native + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h new file mode 100644 index 0000000..34804cf --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h @@ -0,0 +1,559 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FWDLOCKENGINE_H__ +#define __FWDLOCKENGINE_H__ + +#include <DrmEngineBase.h> +#include <DrmConstraints.h> +#include <DrmRights.h> +#include <DrmInfo.h> +#include <DrmInfoStatus.h> +#include <DrmConvertedStatus.h> +#include <DrmInfoRequest.h> +#include <DrmSupportInfo.h> +#include <DrmInfoEvent.h> + +#include "SessionMap.h" +#include "FwdLockConv.h" + +namespace android { + +/** + * Forward Lock Engine class. + */ +class FwdLockEngine : public android::DrmEngineBase { + +public: + FwdLockEngine(); + virtual ~FwdLockEngine(); + +protected: +/** + * Get constraint information associated with input content. + * + * @param uniqueId Unique identifier for a session + * @param path Path of the protected content + * @param action Actions defined such as, + * Action::DEFAULT, Action::PLAY, etc + * @return DrmConstraints + * key-value pairs of constraint are embedded in it + * @note + * In case of error, return NULL + */ +DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action); + +/** + * Get metadata information associated with input content. + * + * @param uniqueId Unique identifier for a session + * @param path Path of the protected content + * @return DrmMetadata + * For Forward Lock engine, it returns an empty object + * @note + * In case of error, returns NULL + */ +DrmMetadata* onGetMetadata(int uniqueId, const String8* path); + +/** + * Initialize plug-in. + * + * @param uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t onInitialize(int uniqueId); + +/** + * Register a callback to be invoked when the caller required to + * receive necessary information. + * + * @param uniqueId Unique identifier for a session + * @param infoListener Listener + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener); + +/** + * Terminate the plug-in and release resources bound to it. + * + * @param uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t onTerminate(int uniqueId); + +/** + * Get whether the given content can be handled by this plugin or not. + * + * @param uniqueId Unique identifier for a session + * @param path Path to the protected object + * @return bool + * Returns true if this plugin can handle , false in case of not able to handle + */ +bool onCanHandle(int uniqueId, const String8& path); + +/** + * Processes the given DRM information as appropriate for its type. + * Not used for Forward Lock Engine. + * + * @param uniqueId Unique identifier for a session + * @param drmInfo Information that needs to be processed + * @return DrmInfoStatus + * instance as a result of processing given input + */ +DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo); + +/** + * Save DRM rights to specified rights path + * and make association with content path. + * + * @param uniqueId Unique identifier for a session + * @param drmRights DrmRights to be saved + * @param rightsPath File path where rights to be saved + * @param contentPath File path where content was saved + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t onSaveRights(int uniqueId, + const DrmRights& drmRights, + const String8& rightsPath, + const String8& contentPath); + +/** + * Retrieves necessary information for registration, unregistration or rights + * acquisition information. + * + * @param uniqueId Unique identifier for a session + * @param drmInfoRequest Request information to retrieve drmInfo + * @return DrmInfo + * instance as a result of processing given input + */ +DrmInfo* onAcquireDrmInfo(int uniqueId, + const DrmInfoRequest* drmInfoRequest); + +/** + * Retrieves the mime type embedded inside the original content. + * + * @param uniqueId Unique identifier for a session + * @param path Path of the protected content + * @return String8 + * Returns mime-type of the original content, such as "video/mpeg" + */ +String8 onGetOriginalMimeType(int uniqueId, const String8& path); + +/** + * Retrieves the type of the protected object (content, rights, etc..) + * using specified path or mimetype. At least one parameter should be non null + * to retrieve DRM object type. + * + * @param uniqueId Unique identifier for a session + * @param path Path of the content or null. + * @param mimeType Mime type of the content or null. + * @return type of the DRM content, + * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT + */ +int onGetDrmObjectType(int uniqueId, + const String8& path, + const String8& mimeType); + +/** + * Check whether the given content has valid rights or not. + * + * @param uniqueId Unique identifier for a session + * @param path Path of the protected content + * @param action Action to perform (Action::DEFAULT, Action::PLAY, etc) + * @return the status of the rights for the protected content, + * such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc. + */ +int onCheckRightsStatus(int uniqueId, + const String8& path, + int action); + +/** + * Consumes the rights for a content. + * If the reserve parameter is true the rights are reserved until the same + * application calls this api again with the reserve parameter set to false. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the decryption session + * @param action Action to perform. (Action::DEFAULT, Action::PLAY, etc) + * @param reserve True if the rights should be reserved. + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t onConsumeRights(int uniqueId, + DecryptHandle* decryptHandle, + int action, + bool reserve); + +/** + * Informs the DRM Engine about the playback actions performed on the DRM files. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the decryption session + * @param playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE) + * @param position Position in the file (in milliseconds) where the start occurs. + * Only valid together with Playback::START. + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +#ifdef USE_64BIT_DRM_API +status_t onSetPlaybackStatus(int uniqueId, + DecryptHandle* decryptHandle, + int playbackStatus, + int64_t position); +#else +status_t onSetPlaybackStatus(int uniqueId, + DecryptHandle* decryptHandle, + int playbackStatus, + int position); +#endif + +/** + * Validates whether an action on the DRM content is allowed or not. + * + * @param uniqueId Unique identifier for a session + * @param path Path of the protected content + * @param action Action to validate (Action::PLAY, Action::TRANSFER, etc) + * @param description Detailed description of the action + * @return true if the action is allowed. + */ +bool onValidateAction(int uniqueId, + const String8& path, + int action, + const ActionDescription& description); + +/** + * Removes the rights associated with the given protected content. + * Not used for Forward Lock Engine. + * + * @param uniqueId Unique identifier for a session + * @param path Path of the protected content + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t onRemoveRights(int uniqueId, const String8& path); + +/** + * Removes all the rights information of each plug-in associated with + * DRM framework. Will be used in master reset but does nothing for + * Forward Lock Engine. + * + * @param uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t onRemoveAllRights(int uniqueId); + +/** + * Starts the Forward Lock file conversion session. + * Each time the application tries to download a new DRM file + * which needs to be converted, then the application has to + * begin with calling this API. The convertId is used as the conversion session key + * and must not be the same for different convert sessions. + * + * @param uniqueId Unique identifier for a session + * @param convertId Handle for the convert session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t onOpenConvertSession(int uniqueId, int convertId); + +/** + * Accepts and converts the input data which is part of DRM file. + * The resultant converted data and the status is returned in the DrmConvertedInfo + * object. This method will be called each time there is a new block + * of data received by the application. + * + * @param uniqueId Unique identifier for a session + * @param convertId Handle for the convert session + * @param inputData Input Data which need to be converted + * @return Return object contains the status of the data conversion, + * the output converted data and offset. In this case the + * application will ignore the offset information. + */ +DrmConvertedStatus* onConvertData(int uniqueId, + int convertId, + const DrmBuffer* inputData); + +/** + * Closes the convert session in case of data supply completed or error occurred. + * Upon successful conversion of the complete data, it returns signature calculated over + * the entire data used over a conversion session. This signature must be copied to the offset + * mentioned in the DrmConvertedStatus. Signature is used for data integrity protection. + * + * @param uniqueId Unique identifier for a session + * @param convertId Handle for the convert session + * @return Return object contains the status of the data conversion, + * the header and body signature data. It also informs + * the application about the file offset at which this + * signature data should be written. + */ +DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId); + +/** + * Returns the information about the Drm Engine capabilities which includes + * supported MimeTypes and file suffixes. + * + * @param uniqueId Unique identifier for a session + * @return DrmSupportInfo + * instance which holds the capabilities of a plug-in + */ +DrmSupportInfo* onGetSupportInfo(int uniqueId); + +/** + * Open the decrypt session to decrypt the given protected content. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the current decryption session + * @param fd File descriptor of the protected content to be decrypted + * @param offset Start position of the content + * @param length The length of the protected content + * @return + * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success + */ +#ifdef USE_64BIT_DRM_API +status_t onOpenDecryptSession(int uniqueId, + DecryptHandle* decryptHandle, + int fd, off64_t offset, off64_t length); +#else +status_t onOpenDecryptSession(int uniqueId, + DecryptHandle* decryptHandle, + int fd, int offset, int length); +#endif + +/** + * Open the decrypt session to decrypt the given protected content. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the current decryption session + * @param uri Path of the protected content to be decrypted + * @return + * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success + */ +status_t onOpenDecryptSession(int uniqueId, + DecryptHandle* decryptHandle, + const char* uri); + +/** + * Close the decrypt session for the given handle. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the decryption session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t onCloseDecryptSession(int uniqueId, + DecryptHandle* decryptHandle); + +/** + * Initialize decryption for the given unit of the protected content. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the decryption session + * @param decryptUnitId ID which specifies decryption unit, such as track ID + * @param headerInfo Information for initializing decryption of this decrypUnit + * @return + * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success + */ +status_t onInitializeDecryptUnit(int uniqueId, + DecryptHandle* decryptHandle, + int decryptUnitId, + const DrmBuffer* headerInfo); + +/** + * Decrypt the protected content buffers for the given unit. + * This method will be called any number of times, based on number of + * encrypted streams received from application. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the decryption session + * @param decryptUnitId ID which specifies decryption unit, such as track ID + * @param encBuffer Encrypted data block + * @param decBuffer Decrypted data block + * @return status_t + * Returns the error code for this API + * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED + * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED, + * DRM_ERROR_DECRYPT for failure. + */ +status_t onDecrypt(int uniqueId, + DecryptHandle* decryptHandle, + int decryptUnitId, + const DrmBuffer* encBuffer, + DrmBuffer** decBuffer); + +/** + * Decrypt the protected content buffers for the given unit. + * This method will be called any number of times, based on number of + * encrypted streams received from application. + * + * @param uniqueId Unique identifier for a session + * @param decryptId Handle for the decryption session + * @param decryptUnitId ID Specifies decryption unit, such as track ID + * @param encBuffer Encrypted data block + * @param decBuffer Decrypted data block + * @param IV Optional buffer + * @return status_t + * Returns the error code for this API + * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED + * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED, + * DRM_ERROR_DECRYPT for failure. + */ +status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer, + DrmBuffer** decBuffer, DrmBuffer* IV); + +/** + * Finalize decryption for the given unit of the protected content. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the decryption session + * @param decryptUnitId ID Specifies decryption unit, such as track ID + * @return + * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success + */ +status_t onFinalizeDecryptUnit(int uniqueId, + DecryptHandle* decryptHandle, + int decryptUnitId); + +/** + * Reads the specified number of bytes from an open DRM file. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the decryption session + * @param buffer Reference to the buffer that should receive the read data. + * @param numBytes Number of bytes to read. + * + * @return Number of bytes read. + * @retval -1 Failure. + */ +ssize_t onRead(int uniqueId, + DecryptHandle* decryptHandle, + void* pBuffer, + int numBytes); + +/** + * Updates the file position within an open DRM file. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the decryption session + * @param offset Offset with which to update the file position. + * @param whence One of SEEK_SET, SEEK_CUR, and SEEK_END. + * These constants are defined in unistd.h. + * + * @return New file position. + * @retval ((off_t)-1) Failure. + */ +#ifdef USE_64BIT_DRM_API +off64_t onLseek(int uniqueId, + DecryptHandle* decryptHandle, + off64_t offset, + int whence); +#else +off_t onLseek(int uniqueId, + DecryptHandle* decryptHandle, + off_t offset, + int whence); +#endif + +/** + * Reads the specified number of bytes from an open DRM file. + * + * @param uniqueId Unique identifier for a session + * @param decryptHandle Handle for the decryption session + * @param buffer Reference to the buffer that should receive the read data. + * @param numBytes Number of bytes to read. + * @param offset Offset with which to update the file position. + * + * @return Number of bytes read. Returns -1 for Failure. + */ +#ifdef USE_64BIT_DRM_API +ssize_t onPread(int uniqueId, + DecryptHandle* decryptHandle, + void* buffer, + ssize_t numBytes, + off64_t offset); +#else +ssize_t onPread(int uniqueId, + DecryptHandle* decryptHandle, + void* buffer, + ssize_t numBytes, + off_t offset); +#endif + +private: + +/** + * Session Class for Forward Lock Conversion. An object of this class is created + * for every conversion. + */ +class ConvertSession { + public : + int uniqueId; + FwdLockConv_Output_t output; + + ConvertSession() { + uniqueId = 0; + memset(&output, 0, sizeof(FwdLockConv_Output_t)); + } + + virtual ~ConvertSession() {} +}; + +/** + * Session Class for Forward Lock decoder. An object of this class is created + * for every decoding session. + */ +class DecodeSession { + public : + int fileDesc; + off_t offset; + + DecodeSession() { + fileDesc = -1; + offset = 0; + } + + DecodeSession(int fd) { + fileDesc = fd; + offset = 0; + } + + virtual ~DecodeSession() {} +}; + +/** + * Session Map Tables for Conversion and Decoding of forward lock files. + */ +SessionMap<ConvertSession*> convertSessionMap; +SessionMap<DecodeSession*> decodeSessionMap; + +/** + * Converts the error code from Forward Lock Converter to DrmConvertStatus error code. + * + * @param Forward Lock Converter error code + * + * @return Status code from DrmConvertStatus. + */ +static int getConvertedStatus(FwdLockConv_Status_t status); +}; + +}; + +#endif /* __FWDLOCKENGINE_H__ */ diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h new file mode 100644 index 0000000..da95d60 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FWDLOCKENGINECONST_H__ +#define __FWDLOCKENGINECONST_H__ + +namespace android { + +/** + * Constants for forward Lock Engine used for exposing engine's capabilities. + */ +#define FWDLOCK_EXTENSION_FL ("FL") +#define FWDLOCK_DOTEXTENSION_FL (".fl") +#define FWDLOCK_MIMETYPE_FL ("application/x-android-drm-fl") + +#define FWDLOCK_EXTENSION_DM ("DM") +#define FWDLOCK_DOTEXTENSION_DM (".dm") +#define FWDLOCK_MIMETYPE_DM ("application/vnd.oma.drm.message") + +#define FWDLOCK_DESCRIPTION ("OMA V1 Forward Lock") + +}; + +#endif /* __FWDLOCKENGINECONST_H__ */ diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp new file mode 100644 index 0000000..d430f72 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SessionMap.h" +#include "FwdLockEngine.h" +#include <utils/Log.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include "drm_framework_common.h" +#include <fcntl.h> +#include <limits.h> +#include <DrmRights.h> +#include <DrmConstraints.h> +#include <DrmMetadata.h> +#include <DrmInfo.h> +#include <DrmInfoStatus.h> +#include <DrmInfoRequest.h> +#include <DrmSupportInfo.h> +#include <DrmConvertedStatus.h> +#include <utils/String8.h> +#include "FwdLockConv.h" +#include "FwdLockFile.h" +#include "FwdLockGlue.h" +#include "FwdLockEngineConst.h" +#include "MimeTypeUtil.h" + +#undef LOG_TAG +#define LOG_TAG "FwdLockEngine" + +using namespace android; +// This extern "C" is mandatory to be managed by TPlugInManager +extern "C" IDrmEngine* create() { + return new FwdLockEngine(); +} + +// This extern "C" is mandatory to be managed by TPlugInManager +extern "C" void destroy(IDrmEngine* plugIn) { + delete plugIn; +} + +FwdLockEngine::FwdLockEngine() { + LOGD("FwdLockEngine Construction"); +} + +FwdLockEngine::~FwdLockEngine() { + LOGD("FwdLockEngine Destruction"); + + convertSessionMap.destroyMap(); + decodeSessionMap.destroyMap(); +} + +int FwdLockEngine::getConvertedStatus(FwdLockConv_Status_t status) { + int retStatus = DrmConvertedStatus::STATUS_ERROR; + + switch(status) { + case FwdLockConv_Status_OK: + retStatus = DrmConvertedStatus::STATUS_OK; + break; + case FwdLockConv_Status_SyntaxError: + case FwdLockConv_Status_InvalidArgument: + case FwdLockConv_Status_UnsupportedFileFormat: + case FwdLockConv_Status_UnsupportedContentTransferEncoding: + LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \ + "Returning STATUS_INPUTDATA_ERROR", status); + retStatus = DrmConvertedStatus::STATUS_INPUTDATA_ERROR; + break; + default: + LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \ + "Returning STATUS_ERROR", status); + retStatus = DrmConvertedStatus::STATUS_ERROR; + break; + } + + return retStatus; +} + +DrmConstraints* FwdLockEngine::onGetConstraints(int uniqueId, const String8* path, int action) { + DrmConstraints* drmConstraints = NULL; + + LOGD("FwdLockEngine::onGetConstraints"); + + if (NULL != path && + (RightsStatus::RIGHTS_VALID == onCheckRightsStatus(uniqueId, *path, action))) { + // Return the empty constraints to show no error condition. + drmConstraints = new DrmConstraints(); + } + + return drmConstraints; +} + +DrmMetadata* FwdLockEngine::onGetMetadata(int uniqueId, const String8* path) { + DrmMetadata* drmMetadata = NULL; + + LOGD("FwdLockEngine::onGetMetadata"); + + if (NULL != path) { + // Returns empty metadata to show no error condition. + drmMetadata = new DrmMetadata(); + } + + return drmMetadata; +} + +android::status_t FwdLockEngine::onInitialize(int uniqueId) { + LOGD("FwdLockEngine::onInitialize"); + + + if (FwdLockGlue_InitializeKeyEncryption()) { + LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption succeeded"); + } else { + LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption failed:" + "errno = %d", errno); + } + + return DRM_NO_ERROR; +} + +android::status_t +FwdLockEngine::onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener) { + // Not used + LOGD("FwdLockEngine::onSetOnInfoListener"); + + return DRM_NO_ERROR; +} + +android::status_t FwdLockEngine::onTerminate(int uniqueId) { + LOGD("FwdLockEngine::onTerminate"); + + return DRM_NO_ERROR; +} + +DrmSupportInfo* FwdLockEngine::onGetSupportInfo(int uniqueId) { + DrmSupportInfo* pSupportInfo = new DrmSupportInfo(); + + LOGD("FwdLockEngine::onGetSupportInfo"); + + // fill all Forward Lock mimetypes and extensions + if (NULL != pSupportInfo) { + pSupportInfo->addMimeType(String8(FWDLOCK_MIMETYPE_FL)); + pSupportInfo->addFileSuffix(String8(FWDLOCK_DOTEXTENSION_FL)); + pSupportInfo->addMimeType(String8(FWDLOCK_MIMETYPE_DM)); + pSupportInfo->addFileSuffix(String8(FWDLOCK_DOTEXTENSION_DM)); + + pSupportInfo->setDescription(String8(FWDLOCK_DESCRIPTION)); + } + + return pSupportInfo; +} + +bool FwdLockEngine::onCanHandle(int uniqueId, const String8& path) { + bool result = false; + + String8 extString = path.getPathExtension(); + + extString.toLower(); + + if ((extString == String8(FWDLOCK_DOTEXTENSION_FL)) || + (extString == String8(FWDLOCK_DOTEXTENSION_DM))) { + result = true; + } + return result; +} + +DrmInfoStatus* FwdLockEngine::onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) { + DrmInfoStatus *drmInfoStatus = NULL; + + // Nothing to process + + drmInfoStatus = new DrmInfoStatus((int)DrmInfoStatus::STATUS_OK, 0, NULL, String8("")); + + LOGD("FwdLockEngine::onProcessDrmInfo"); + + return drmInfoStatus; +} + +status_t FwdLockEngine::onSaveRights( + int uniqueId, + const DrmRights& drmRights, + const String8& rightsPath, + const String8& contentPath) { + // No rights to save. Return + LOGD("FwdLockEngine::onSaveRights"); + return DRM_ERROR_UNKNOWN; +} + +DrmInfo* FwdLockEngine::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) { + DrmInfo* drmInfo = NULL; + + // Nothing to be done for Forward Lock file + LOGD("FwdLockEngine::onAcquireDrmInfo"); + + return drmInfo; +} + +int FwdLockEngine::onCheckRightsStatus(int uniqueId, + const String8& path, + int action) { + int result = RightsStatus::RIGHTS_INVALID; + + LOGD("FwdLockEngine::onCheckRightsStatus"); + + // Only Transfer action is not allowed for forward Lock files. + if (onCanHandle(uniqueId, path)) { + switch(action) { + case Action::DEFAULT: + case Action::PLAY: + case Action::RINGTONE: + case Action::OUTPUT: + case Action::PREVIEW: + case Action::EXECUTE: + case Action::DISPLAY: + result = RightsStatus::RIGHTS_VALID; + break; + + case Action::TRANSFER: + default: + result = RightsStatus::RIGHTS_INVALID; + break; + } + } + + return result; +} + +status_t FwdLockEngine::onConsumeRights(int uniqueId, + DecryptHandle* decryptHandle, + int action, + bool reserve) { + // No rights consumption + LOGD("FwdLockEngine::onConsumeRights"); + return DRM_NO_ERROR; +} + +bool FwdLockEngine::onValidateAction(int uniqueId, + const String8& path, + int action, + const ActionDescription& description) { + LOGD("FwdLockEngine::onValidateAction"); + + // For the forwardlock engine checkRights and ValidateAction are the same. + return (onCheckRightsStatus(uniqueId, path, action) == RightsStatus::RIGHTS_VALID); +} + +String8 FwdLockEngine::onGetOriginalMimeType(int uniqueId, const String8& path) { + LOGD("FwdLockEngine::onGetOriginalMimeType"); + String8 mimeString = String8(""); + int fileDesc = FwdLockFile_open(path.string()); + + if (-1 < fileDesc) { + const char* pMimeType = FwdLockFile_GetContentType(fileDesc); + + if (NULL != pMimeType) { + String8 contentType = String8(pMimeType); + contentType.toLower(); + mimeString = MimeTypeUtil::convertMimeType(contentType); + } + + FwdLockFile_close(fileDesc); + } + + return mimeString; +} + +int FwdLockEngine::onGetDrmObjectType(int uniqueId, + const String8& path, + const String8& mimeType) { + String8 mimeStr = String8(mimeType); + + LOGD("FwdLockEngine::onGetDrmObjectType"); + + mimeStr.toLower(); + + /* Checks whether + * 1. path and mime type both are not empty strings (meaning unavailable) else content is unknown + * 2. if one of them is empty string and if other is known then its a DRM Content Object. + * 3. if both of them are available, then both may be of known type + * (regardless of the relation between them to make it compatible with other DRM Engines) + */ + if (((0 == path.length()) || onCanHandle(uniqueId, path)) && + ((0 == mimeType.length()) || ((mimeStr == String8(FWDLOCK_MIMETYPE_FL)) || + (mimeStr == String8(FWDLOCK_MIMETYPE_DM)))) && (mimeType != path) ) { + return DrmObjectType::CONTENT; + } + + return DrmObjectType::UNKNOWN; +} + +status_t FwdLockEngine::onRemoveRights(int uniqueId, const String8& path) { + // No Rights to remove + LOGD("FwdLockEngine::onRemoveRights"); + return DRM_NO_ERROR; +} + +status_t FwdLockEngine::onRemoveAllRights(int uniqueId) { + // No rights to remove + LOGD("FwdLockEngine::onRemoveAllRights"); + return DRM_NO_ERROR; +} + +#ifdef USE_64BIT_DRM_API +status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle, + int playbackStatus, int64_t position) { +#else +status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle, + int playbackStatus, int position) { +#endif + // Not used + LOGD("FwdLockEngine::onSetPlaybackStatus"); + return DRM_NO_ERROR; +} + +status_t FwdLockEngine::onOpenConvertSession(int uniqueId, + int convertId) { + status_t result = DRM_ERROR_UNKNOWN; + LOGD("FwdLockEngine::onOpenConvertSession"); + if (!convertSessionMap.isCreated(convertId)) { + ConvertSession *newSession = new ConvertSession(); + if (FwdLockConv_Status_OK == + FwdLockConv_OpenSession(&(newSession->uniqueId), &(newSession->output))) { + convertSessionMap.addValue(convertId, newSession); + result = DRM_NO_ERROR; + } else { + LOGD("FwdLockEngine::onOpenConvertSession -- FwdLockConv_OpenSession failed."); + delete newSession; + } + } + return result; +} + +DrmConvertedStatus* FwdLockEngine::onConvertData(int uniqueId, + int convertId, + const DrmBuffer* inputData) { + FwdLockConv_Status_t retStatus = FwdLockConv_Status_InvalidArgument; + DrmBuffer *convResult = new DrmBuffer(NULL, 0); + int offset = -1; + + if (NULL != inputData && convertSessionMap.isCreated(convertId)) { + ConvertSession *convSession = convertSessionMap.getValue(convertId); + + if (NULL != convSession) { + retStatus = FwdLockConv_ConvertData(convSession->uniqueId, + inputData->data, + inputData->length, + &(convSession->output)); + + if (FwdLockConv_Status_OK == retStatus) { + // return bytes from conversion if available + if (convSession->output.fromConvertData.numBytes > 0) { + convResult->data = new char[convSession->output.fromConvertData.numBytes]; + + if (NULL != convResult->data) { + convResult->length = convSession->output.fromConvertData.numBytes; + memcpy(convResult->data, + (char *)convSession->output.fromConvertData.pBuffer, + convResult->length); + } + } + } else { + offset = convSession->output.fromConvertData.errorPos; + } + } + } + return new DrmConvertedStatus(getConvertedStatus(retStatus), convResult, offset); +} + +DrmConvertedStatus* FwdLockEngine::onCloseConvertSession(int uniqueId, + int convertId) { + FwdLockConv_Status_t retStatus = FwdLockConv_Status_InvalidArgument; + DrmBuffer *convResult = new DrmBuffer(NULL, 0); + int offset = -1; + + LOGD("FwdLockEngine::onCloseConvertSession"); + + if (convertSessionMap.isCreated(convertId)) { + ConvertSession *convSession = convertSessionMap.getValue(convertId); + + if (NULL != convSession) { + retStatus = FwdLockConv_CloseSession(convSession->uniqueId, &(convSession->output)); + + if (FwdLockConv_Status_OK == retStatus) { + offset = convSession->output.fromCloseSession.fileOffset; + convResult->data = new char[FWD_LOCK_SIGNATURES_SIZE]; + + if (NULL != convResult->data) { + convResult->length = FWD_LOCK_SIGNATURES_SIZE; + memcpy(convResult->data, + (char *)convSession->output.fromCloseSession.signatures, + convResult->length); + } + } + } + convertSessionMap.removeValue(convertId); + } + return new DrmConvertedStatus(getConvertedStatus(retStatus), convResult, offset); +} + +#ifdef USE_64BIT_DRM_API +status_t FwdLockEngine::onOpenDecryptSession(int uniqueId, + DecryptHandle* decryptHandle, + int fd, + off64_t offset, + off64_t length) { +#else +status_t FwdLockEngine::onOpenDecryptSession(int uniqueId, + DecryptHandle* decryptHandle, + int fd, + int offset, + int length) { +#endif + status_t result = DRM_ERROR_CANNOT_HANDLE; + int fileDesc = -1; + + LOGD("FwdLockEngine::onOpenDecryptSession"); + + if ((-1 < fd) && + (NULL != decryptHandle) && + (!decodeSessionMap.isCreated(decryptHandle->decryptId))) { + fileDesc = dup(fd); + } else { + LOGD("FwdLockEngine::onOpenDecryptSession parameter error"); + return result; + } + + if (-1 < fileDesc && + -1 < ::lseek(fileDesc, offset, SEEK_SET) && + -1 < FwdLockFile_attach(fileDesc)) { + // check for file integrity. This must be done to protect the content mangling. + int retVal = FwdLockFile_CheckHeaderIntegrity(fileDesc); + DecodeSession* decodeSession = new DecodeSession(fileDesc); + + if (retVal && NULL != decodeSession) { + decodeSessionMap.addValue(decryptHandle->decryptId, decodeSession); + const char *pmime= FwdLockFile_GetContentType(fileDesc); + String8 contentType = String8(pmime == NULL ? "" : pmime); + contentType.toLower(); + decryptHandle->mimeType = MimeTypeUtil::convertMimeType(contentType); + decryptHandle->decryptApiType = DecryptApiType::CONTAINER_BASED; + decryptHandle->status = RightsStatus::RIGHTS_VALID; + decryptHandle->decryptInfo = NULL; + result = DRM_NO_ERROR; + } else { + LOGD("FwdLockEngine::onOpenDecryptSession Integrity Check failed for the fd"); + FwdLockFile_detach(fileDesc); + ::close(fileDesc); + delete decodeSession; + } + } + + LOGD("FwdLockEngine::onOpenDecryptSession Exit. result = %d", result); + + return result; +} + +status_t FwdLockEngine::onOpenDecryptSession(int uniqueId, + DecryptHandle* decryptHandle, + const char* uri) { + status_t result = DRM_ERROR_CANNOT_HANDLE; + const char fileTag [] = "file://"; + + if (NULL != decryptHandle && NULL != uri && strlen(uri) > sizeof(fileTag)) { + String8 uriTag = String8(uri); + uriTag.toLower(); + + if (0 == strncmp(uriTag.string(), fileTag, sizeof(fileTag) - 1)) { + const char *filePath = strchr(uri + sizeof(fileTag) - 1, '/'); + if (NULL != filePath && onCanHandle(uniqueId, String8(filePath))) { + int fd = open(filePath, O_RDONLY); + + if (-1 < fd) { + // offset is always 0 and length is not used. so any positive size. + result = onOpenDecryptSession(uniqueId, decryptHandle, fd, 0, 1); + + // fd is duplicated already if success. closing the file + close(fd); + } + } + } + } + + return result; +} + +status_t FwdLockEngine::onCloseDecryptSession(int uniqueId, + DecryptHandle* decryptHandle) { + status_t result = DRM_ERROR_UNKNOWN; + LOGD("FwdLockEngine::onCloseDecryptSession"); + + if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) { + DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId); + if (NULL != session && session->fileDesc > -1) { + FwdLockFile_detach(session->fileDesc); + ::close(session->fileDesc); + decodeSessionMap.removeValue(decryptHandle->decryptId); + result = DRM_NO_ERROR; + } + } + + LOGD("FwdLockEngine::onCloseDecryptSession Exit"); + return result; +} + +status_t FwdLockEngine::onInitializeDecryptUnit(int uniqueId, + DecryptHandle* decryptHandle, + int decryptUnitId, + const DrmBuffer* headerInfo) { + LOGD("FwdLockEngine::onInitializeDecryptUnit"); + return DRM_ERROR_UNKNOWN; +} + +status_t FwdLockEngine::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) { + LOGD("FwdLockEngine::onDecrypt"); + return DRM_ERROR_UNKNOWN; +} + +status_t FwdLockEngine::onDecrypt(int uniqueId, + DecryptHandle* decryptHandle, + int decryptUnitId, + const DrmBuffer* encBuffer, + DrmBuffer** decBuffer) { + LOGD("FwdLockEngine::onDecrypt"); + return DRM_ERROR_UNKNOWN; +} + +status_t FwdLockEngine::onFinalizeDecryptUnit(int uniqueId, + DecryptHandle* decryptHandle, + int decryptUnitId) { + LOGD("FwdLockEngine::onFinalizeDecryptUnit"); + return DRM_ERROR_UNKNOWN; +} + +ssize_t FwdLockEngine::onRead(int uniqueId, + DecryptHandle* decryptHandle, + void* buffer, + int numBytes) { + ssize_t size = -1; + + if (NULL != decryptHandle && + decodeSessionMap.isCreated(decryptHandle->decryptId) && + NULL != buffer && + numBytes > -1) { + DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId); + if (NULL != session && session->fileDesc > -1) { + size = FwdLockFile_read(session->fileDesc, buffer, numBytes); + + if (0 > size) { + session->offset = ((off_t)-1); + } else { + session->offset += size; + } + } + } + + return size; +} + +#ifdef USE_64BIT_DRM_API +off64_t FwdLockEngine::onLseek(int uniqueId, DecryptHandle* decryptHandle, + off64_t offset, int whence) { +#else +off_t FwdLockEngine::onLseek(int uniqueId, DecryptHandle* decryptHandle, + off_t offset, int whence) { +#endif + off_t offval = -1; + + if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) { + DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId); + if (NULL != session && session->fileDesc > -1) { + offval = FwdLockFile_lseek(session->fileDesc, offset, whence); + session->offset = offval; + } + } + + return offval; +} + +#ifdef USE_64BIT_DRM_API +ssize_t FwdLockEngine::onPread(int uniqueId, + DecryptHandle* decryptHandle, + void* buffer, + ssize_t numBytes, + off64_t offset) { +#else +ssize_t FwdLockEngine::onPread(int uniqueId, + DecryptHandle* decryptHandle, + void* buffer, + ssize_t numBytes, + off_t offset) { +#endif + ssize_t bytesRead = -1; + + DecodeSession* decoderSession = NULL; + + if ((NULL != decryptHandle) && + (NULL != (decoderSession = decodeSessionMap.getValue(decryptHandle->decryptId))) && + (NULL != buffer) && + (numBytes > -1) && + (offset > -1)) { + if (offset != decoderSession->offset) { + decoderSession->offset = onLseek(uniqueId, decryptHandle, offset, SEEK_SET); + } + + if (((off_t)-1) != decoderSession->offset) { + bytesRead = onRead(uniqueId, decryptHandle, buffer, numBytes); + if (bytesRead < 0) { + LOGD("FwdLockEngine::onPread error reading"); + } + } + } else { + LOGD("FwdLockEngine::onPread decryptId not found"); + } + + return bytesRead; +} diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk new file mode 100644 index 0000000..9ee7961 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk @@ -0,0 +1,16 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include $(call all-subdir-makefiles) diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk new file mode 100644 index 0000000..6c5d3cf --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk @@ -0,0 +1,32 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + FwdLockGlue.c + +LOCAL_C_INCLUDES := \ + external/openssl/include + +LOCAL_SHARED_LIBRARIES := libcrypto + +LOCAL_MODULE := libfwdlock-common + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c new file mode 100644 index 0000000..92bda8f --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <openssl/aes.h> + +#include "FwdLockGlue.h" + +#define TRUE 1 +#define FALSE 0 + +#define KEY_SIZE 16 +#define KEY_SIZE_IN_BITS (KEY_SIZE * 8) + +static int isInitialized = FALSE; + +static const char strKeyFilename[] = "/data/drm/fwdlock/kek.dat"; + +static AES_KEY encryptionRoundKeys; +static AES_KEY decryptionRoundKeys; + +/** + * Creates all directories along the fully qualified path of the given file. + * + * @param[in] path A reference to the fully qualified path of a file. + * @param[in] mode The access mode to use for the directories being created. + * + * @return A Boolean value indicating whether the operation was successful. + */ +static int FwdLockGlue_CreateDirectories(const char *path, mode_t mode) { + int result = TRUE; + size_t partialPathLength = strlen(path); + char *partialPath = malloc(partialPathLength + 1); + if (partialPath == NULL) { + result = FALSE; + } else { + size_t i; + for (i = 0; i < partialPathLength; ++i) { + if (path[i] == '/' && i > 0) { + partialPath[i] = '\0'; + if (mkdir(partialPath, mode) != 0 && errno != EEXIST) { + result = FALSE; + break; + } + } + partialPath[i] = path[i]; + } + free(partialPath); + } + return result; +} + +/** + * Initializes the round keys used for encryption and decryption of session keys. First creates a + * device-unique key-encryption key if none exists yet. + */ +static void FwdLockGlue_InitializeRoundKeys() { + unsigned char keyEncryptionKey[KEY_SIZE]; + int fileDesc = open(strKeyFilename, O_RDONLY); + if (fileDesc >= 0) { + if (read(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) { + isInitialized = TRUE; + } + (void)close(fileDesc); + } else if (errno == ENOENT && + FwdLockGlue_GetRandomNumber(keyEncryptionKey, KEY_SIZE) && + FwdLockGlue_CreateDirectories(strKeyFilename, S_IRWXU)) { + fileDesc = open(strKeyFilename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR); + if (fileDesc >= 0) { + if (write(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) { + isInitialized = TRUE; + } + (void)close(fileDesc); + } + } + if (isInitialized) { + if (AES_set_encrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &encryptionRoundKeys) != 0 || + AES_set_decrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &decryptionRoundKeys) != 0) { + isInitialized = FALSE; + } + } + memset(keyEncryptionKey, 0, KEY_SIZE); // Zero out key data. +} + +/** + * Validates the padding of a decrypted key. + * + * @param[in] pData A reference to the buffer containing the decrypted key and padding. + * @param[in] decryptedKeyLength The length in bytes of the decrypted key. + * + * @return A Boolean value indicating whether the padding was valid. + */ +static int FwdLockGlue_ValidatePadding(const unsigned char *pData, size_t decryptedKeyLength) { + size_t i; + size_t padding = AES_BLOCK_SIZE - (decryptedKeyLength % AES_BLOCK_SIZE); + pData += decryptedKeyLength; + for (i = 0; i < padding; ++i) { + if ((size_t)*pData != padding) { + return FALSE; + } + ++pData; + } + return TRUE; +} + +int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes) { + // Generate 'cryptographically secure' random bytes by reading them from "/dev/urandom" (the + // non-blocking version of "/dev/random"). + ssize_t numBytesRead = 0; + int fileDesc = open("/dev/urandom", O_RDONLY); + if (fileDesc >= 0) { + numBytesRead = read(fileDesc, pBuffer, numBytes); + (void)close(fileDesc); + } + return numBytesRead >= 0 && (size_t)numBytesRead == numBytes; +} + +int FwdLockGlue_InitializeKeyEncryption() { + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once(&once, FwdLockGlue_InitializeRoundKeys); + return isInitialized; +} + +size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength) { + return ((plaintextKeyLength / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE; +} + +int FwdLockGlue_EncryptKey(const void *pPlaintextKey, + size_t plaintextKeyLength, + void *pEncryptedKey, + size_t encryptedKeyLength) { + int result = FALSE; + assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(plaintextKeyLength)); + if (FwdLockGlue_InitializeKeyEncryption()) { + unsigned char initVector[AES_BLOCK_SIZE]; + if (FwdLockGlue_GetRandomNumber(initVector, AES_BLOCK_SIZE)) { + size_t padding = AES_BLOCK_SIZE - (plaintextKeyLength % AES_BLOCK_SIZE); + size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE; + memcpy(pEncryptedKey, pPlaintextKey, plaintextKeyLength); + memset((unsigned char *)pEncryptedKey + plaintextKeyLength, (int)padding, padding); + memcpy((unsigned char *)pEncryptedKey + dataLength, initVector, AES_BLOCK_SIZE); + AES_cbc_encrypt(pEncryptedKey, pEncryptedKey, dataLength, &encryptionRoundKeys, + initVector, AES_ENCRYPT); + result = TRUE; + } + } + return result; +} + +int FwdLockGlue_DecryptKey(const void *pEncryptedKey, + size_t encryptedKeyLength, + void *pDecryptedKey, + size_t decryptedKeyLength) { + int result = FALSE; + assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(decryptedKeyLength)); + if (FwdLockGlue_InitializeKeyEncryption()) { + size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE; + unsigned char *pData = malloc(dataLength); + if (pData != NULL) { + unsigned char initVector[AES_BLOCK_SIZE]; + memcpy(pData, pEncryptedKey, dataLength); + memcpy(initVector, (const unsigned char *)pEncryptedKey + dataLength, AES_BLOCK_SIZE); + AES_cbc_encrypt(pData, pData, dataLength, &decryptionRoundKeys, initVector, + AES_DECRYPT); + memcpy(pDecryptedKey, pData, decryptedKeyLength); + result = FwdLockGlue_ValidatePadding(pData, decryptedKeyLength); + free(pData); + } + } + return result; +} diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h new file mode 100644 index 0000000..f36f6ea --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FWDLOCKGLUE_H__ +#define __FWDLOCKGLUE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Generates the specified number of cryptographically secure random bytes. + * + * @param[out] pBuffer A reference to the buffer that should receive the random data. + * @param[in] numBytes The number of random bytes to generate. + * + * @return A Boolean value indicating whether the operation was successful. + */ +int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes); + +/** + * Performs initialization of the key-encryption key. Should be called once during startup to + * facilitate encryption and decryption of session keys. + * + * @return A Boolean value indicating whether the operation was successful. + */ +int FwdLockGlue_InitializeKeyEncryption(); + +/** + * Returns the length of the encrypted key, given the length of the plaintext key. + * + * @param[in] plaintextKeyLength The length in bytes of the plaintext key. + * + * @return The length in bytes of the encrypted key. + */ +size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength); + +/** + * Encrypts the given session key using a key-encryption key unique to this device. + * + * @param[in] pPlaintextKey A reference to the buffer containing the plaintext key. + * @param[in] plaintextKeyLength The length in bytes of the plaintext key. + * @param[out] pEncryptedKey A reference to the buffer containing the encrypted key. + * @param[in] encryptedKeyLength The length in bytes of the encrypted key. + * + * @return A Boolean value indicating whether the operation was successful. + */ +int FwdLockGlue_EncryptKey(const void *pPlaintextKey, + size_t plaintextKeyLength, + void *pEncryptedKey, + size_t encryptedKeyLength); + +/** + * Decrypts the given session key using a key-encryption key unique to this device. + * + * @param[in] pEncryptedKey A reference to the buffer containing the encrypted key. + * @param[in] encryptedKeyLength The length in bytes of the encrypted key. + * @param[out] pDecryptedKey A reference to the buffer containing the decrypted key. + * @param[in] decryptedKeyLength The length in bytes of the decrypted key. + * + * @return A Boolean value indicating whether the operation was successful. + */ +int FwdLockGlue_DecryptKey(const void *pEncryptedKey, + size_t encryptedKeyLength, + void *pDecryptedKey, + size_t decryptedKeyLength); + +#ifdef __cplusplus +} +#endif + +#endif // __FWDLOCKGLUE_H__ diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk new file mode 100644 index 0000000..00bb788 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk @@ -0,0 +1,37 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + FwdLockConv.c + +LOCAL_C_INCLUDES := \ + frameworks/base/drm/libdrmframework/plugins/forward-lock/internal-format/common \ + external/openssl/include + +LOCAL_SHARED_LIBRARIES := libcrypto + +LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common + +LOCAL_STATIC_LIBRARIES := libfwdlock-common + +LOCAL_MODULE := libfwdlock-converter + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c new file mode 100644 index 0000000..14ea9e9 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c @@ -0,0 +1,1339 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#include <ctype.h> +#include <fcntl.h> +#include <limits.h> +#include <pthread.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <openssl/aes.h> +#include <openssl/hmac.h> + +#include "FwdLockConv.h" +#include "FwdLockGlue.h" + +#define TRUE 1 +#define FALSE 0 + +#define INVALID_OFFSET ((off64_t)-1) + +#define MAX_NUM_SESSIONS 32 + +#define OUTPUT_BUFFER_SIZE_INCREMENT 1024 +#define READ_BUFFER_SIZE 1024 + +#define MAX_BOUNDARY_LENGTH 70 +#define MAX_DELIMITER_LENGTH (MAX_BOUNDARY_LENGTH + 4) + +#define STRING_LENGTH_INCREMENT 25 + +#define KEY_SIZE AES_BLOCK_SIZE +#define KEY_SIZE_IN_BITS (KEY_SIZE * 8) + +#define SHA1_HASH_SIZE 20 + +#define FWD_LOCK_VERSION 0 +#define FWD_LOCK_SUBFORMAT 0 +#define USAGE_RESTRICTION_FLAGS 0 +#define CONTENT_TYPE_LENGTH_POS 7 +#define TOP_HEADER_SIZE 8 + +/** + * Data type for the parser states of the converter. + */ +typedef enum FwdLockConv_ParserState { + FwdLockConv_ParserState_WantsOpenDelimiter, + FwdLockConv_ParserState_WantsMimeHeaders, + FwdLockConv_ParserState_WantsBinaryEncodedData, + FwdLockConv_ParserState_WantsBase64EncodedData, + FwdLockConv_ParserState_Done +} FwdLockConv_ParserState_t; + +/** + * Data type for the scanner states of the converter. + */ +typedef enum FwdLockConv_ScannerState { + FwdLockConv_ScannerState_WantsFirstDash, + FwdLockConv_ScannerState_WantsSecondDash, + FwdLockConv_ScannerState_WantsCR, + FwdLockConv_ScannerState_WantsLF, + FwdLockConv_ScannerState_WantsBoundary, + FwdLockConv_ScannerState_WantsBoundaryEnd, + FwdLockConv_ScannerState_WantsMimeHeaderNameStart, + FwdLockConv_ScannerState_WantsMimeHeaderName, + FwdLockConv_ScannerState_WantsMimeHeaderNameEnd, + FwdLockConv_ScannerState_WantsContentTypeStart, + FwdLockConv_ScannerState_WantsContentType, + FwdLockConv_ScannerState_WantsContentTransferEncodingStart, + FwdLockConv_ScannerState_Wants_A_OR_I, + FwdLockConv_ScannerState_Wants_N, + FwdLockConv_ScannerState_Wants_A, + FwdLockConv_ScannerState_Wants_R, + FwdLockConv_ScannerState_Wants_Y, + FwdLockConv_ScannerState_Wants_S, + FwdLockConv_ScannerState_Wants_E, + FwdLockConv_ScannerState_Wants_6, + FwdLockConv_ScannerState_Wants_4, + FwdLockConv_ScannerState_Wants_B, + FwdLockConv_ScannerState_Wants_I, + FwdLockConv_ScannerState_Wants_T, + FwdLockConv_ScannerState_WantsContentTransferEncodingEnd, + FwdLockConv_ScannerState_WantsMimeHeaderValueEnd, + FwdLockConv_ScannerState_WantsMimeHeadersEnd, + FwdLockConv_ScannerState_WantsByte1, + FwdLockConv_ScannerState_WantsByte1_AfterCRLF, + FwdLockConv_ScannerState_WantsByte2, + FwdLockConv_ScannerState_WantsByte3, + FwdLockConv_ScannerState_WantsByte4, + FwdLockConv_ScannerState_WantsPadding, + FwdLockConv_ScannerState_WantsWhitespace, + FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF, + FwdLockConv_ScannerState_WantsDelimiter +} FwdLockConv_ScannerState_t; + +/** + * Data type for the content transfer encoding. + */ +typedef enum FwdLockConv_ContentTransferEncoding { + FwdLockConv_ContentTransferEncoding_Undefined, + FwdLockConv_ContentTransferEncoding_Binary, + FwdLockConv_ContentTransferEncoding_Base64 +} FwdLockConv_ContentTransferEncoding_t; + +/** + * Data type for a dynamically growing string. + */ +typedef struct FwdLockConv_String { + char *ptr; + size_t length; + size_t maxLength; + size_t lengthIncrement; +} FwdLockConv_String_t; + +/** + * Data type for the per-file state information needed by the converter. + */ +typedef struct FwdLockConv_Session { + FwdLockConv_ParserState_t parserState; + FwdLockConv_ScannerState_t scannerState; + FwdLockConv_ScannerState_t savedScannerState; + off64_t numCharsConsumed; + char delimiter[MAX_DELIMITER_LENGTH]; + size_t delimiterLength; + size_t delimiterMatchPos; + FwdLockConv_String_t mimeHeaderName; + FwdLockConv_String_t contentType; + FwdLockConv_ContentTransferEncoding_t contentTransferEncoding; + unsigned char sessionKey[KEY_SIZE]; + void *pEncryptedSessionKey; + size_t encryptedSessionKeyLength; + AES_KEY encryptionRoundKeys; + HMAC_CTX signingContext; + unsigned char topHeader[TOP_HEADER_SIZE]; + unsigned char counter[AES_BLOCK_SIZE]; + unsigned char keyStream[AES_BLOCK_SIZE]; + int keyStreamIndex; + unsigned char ch; + size_t outputBufferSize; + size_t dataOffset; + size_t numDataBytes; +} FwdLockConv_Session_t; + +static FwdLockConv_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL }; + +static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER; + +static const FwdLockConv_String_t nullString = { NULL, 0, 0, STRING_LENGTH_INCREMENT }; + +static const unsigned char topHeaderTemplate[] = + { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS }; + +static const char strContent[] = "content-"; +static const char strType[] = "type"; +static const char strTransferEncoding[] = "transfer-encoding"; +static const char strTextPlain[] = "text/plain"; +static const char strApplicationVndOmaDrmRightsXml[] = "application/vnd.oma.drm.rights+xml"; +static const char strApplicationVndOmaDrmContent[] = "application/vnd.oma.drm.content"; + +static const size_t strlenContent = sizeof strContent - 1; +static const size_t strlenTextPlain = sizeof strTextPlain - 1; + +static const signed char base64Values[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 +}; + +/** + * Acquires an unused converter session. + * + * @return A session ID. + */ +static int FwdLockConv_AcquireSession() { + int sessionId = -1; + int i; + pthread_mutex_lock(&sessionAcquisitionMutex); + for (i = 0; i < MAX_NUM_SESSIONS; ++i) { + if (sessionPtrs[i] == NULL) { + sessionPtrs[i] = malloc(sizeof *sessionPtrs[i]); + if (sessionPtrs[i] != NULL) { + sessionId = i; + } + break; + } + } + pthread_mutex_unlock(&sessionAcquisitionMutex); + return sessionId; +} + +/** + * Checks whether a session ID is in range and currently in use. + * + * @param[in] sessionID A session ID. + * + * @return A Boolean value indicating whether the session ID is in range and currently in use. + */ +static int FwdLockConv_IsValidSession(int sessionId) { + return 0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL; +} + +/** + * Releases a converter session. + * + * @param[in] sessionID A session ID. + */ +static void FwdLockConv_ReleaseSession(int sessionId) { + pthread_mutex_lock(&sessionAcquisitionMutex); + assert(FwdLockConv_IsValidSession(sessionId)); + memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data. + free(sessionPtrs[sessionId]); + sessionPtrs[sessionId] = NULL; + pthread_mutex_unlock(&sessionAcquisitionMutex); +} + +/** + * Derives cryptographically independent keys for encryption and signing from the session key. + * + * @param[in,out] pSession A reference to a converter session. + * + * @return A status code. + */ +static int FwdLockConv_DeriveKeys(FwdLockConv_Session_t *pSession) { + FwdLockConv_Status_t status; + struct FwdLockConv_DeriveKeys_Data { + AES_KEY sessionRoundKeys; + unsigned char value[KEY_SIZE]; + unsigned char key[KEY_SIZE]; + } *pData = malloc(sizeof *pData); + if (pData == NULL) { + status = FwdLockConv_Status_OutOfMemory; + } else { + if (AES_set_encrypt_key(pSession->sessionKey, KEY_SIZE_IN_BITS, + &pData->sessionRoundKeys) != 0) { + status = FwdLockConv_Status_ProgramError; + } else { + // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key. + memset(pData->value, 0, KEY_SIZE); + AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys); + if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS, + &pSession->encryptionRoundKeys) != 0) { + status = FwdLockConv_Status_ProgramError; + } else { + // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key. + ++pData->value[0]; + AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys); + HMAC_CTX_init(&pSession->signingContext); + HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL); + status = FwdLockConv_Status_OK; + } + } + memset(pData, 0, sizeof pData); // Zero out key data. + free(pData); + } + return status; +} + +/** + * Checks whether a given character is valid in a boundary. Note that the boundary may contain + * leading and internal spaces. + * + * @param[in] ch The character to check. + * + * @return A Boolean value indicating whether the given character is valid in a boundary. + */ +static int FwdLockConv_IsBoundaryChar(int ch) { + return isalnum(ch) || ch == '\'' || + ch == '(' || ch == ')' || ch == '+' || ch == '_' || ch == ',' || ch == '-' || + ch == '.' || ch == '/' || ch == ':' || ch == '=' || ch == '?' || ch == ' '; +} + +/** + * Checks whether a given character should be considered whitespace, using a narrower definition + * than the standard-library isspace() function. + * + * @param[in] ch The character to check. + * + * @return A Boolean value indicating whether the given character should be considered whitespace. + */ +static int FwdLockConv_IsWhitespace(int ch) { + return ch == ' ' || ch == '\t'; +} + +/** + * Removes trailing spaces from the delimiter. + * + * @param[in,out] pSession A reference to a converter session. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_RightTrimDelimiter(FwdLockConv_Session_t *pSession) { + while (pSession->delimiterLength > 4 && + pSession->delimiter[pSession->delimiterLength - 1] == ' ') { + --pSession->delimiterLength; + } + if (pSession->delimiterLength > 4) { + return FwdLockConv_Status_OK; + } + return FwdLockConv_Status_SyntaxError; +} + +/** + * Matches the open delimiter. + * + * @param[in,out] pSession A reference to a converter session. + * @param[in] ch A character. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_MatchOpenDelimiter(FwdLockConv_Session_t *pSession, + int ch) { + FwdLockConv_Status_t status = FwdLockConv_Status_OK; + switch (pSession->scannerState) { + case FwdLockConv_ScannerState_WantsFirstDash: + if (ch == '-') { + pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash; + } else if (ch == '\r') { + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } else { + pSession->scannerState = FwdLockConv_ScannerState_WantsCR; + } + break; + case FwdLockConv_ScannerState_WantsSecondDash: + if (ch == '-') { + // The delimiter starts with "\r\n--" (the open delimiter may omit the initial "\r\n"). + // The rest is the user-defined boundary that should come next. + pSession->delimiter[0] = '\r'; + pSession->delimiter[1] = '\n'; + pSession->delimiter[2] = '-'; + pSession->delimiter[3] = '-'; + pSession->delimiterLength = 4; + pSession->scannerState = FwdLockConv_ScannerState_WantsBoundary; + } else if (ch == '\r') { + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } else { + pSession->scannerState = FwdLockConv_ScannerState_WantsCR; + } + break; + case FwdLockConv_ScannerState_WantsCR: + if (ch == '\r') { + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } + break; + case FwdLockConv_ScannerState_WantsLF: + if (ch == '\n') { + pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash; + } else if (ch != '\r') { + pSession->scannerState = FwdLockConv_ScannerState_WantsCR; + } + break; + case FwdLockConv_ScannerState_WantsBoundary: + if (FwdLockConv_IsBoundaryChar(ch)) { + // The boundary may contain leading and internal spaces, so trailing spaces will also be + // matched here. These will be removed later. + if (pSession->delimiterLength < MAX_DELIMITER_LENGTH) { + pSession->delimiter[pSession->delimiterLength++] = ch; + } else if (ch != ' ') { + status = FwdLockConv_Status_SyntaxError; + } + } else if (ch == '\r') { + status = FwdLockConv_RightTrimDelimiter(pSession); + if (status == FwdLockConv_Status_OK) { + pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd; + } + } else if (ch == '\t') { + status = FwdLockConv_RightTrimDelimiter(pSession); + if (status == FwdLockConv_Status_OK) { + pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace; + } + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsWhitespace: + if (ch == '\r') { + pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd; + } else if (!FwdLockConv_IsWhitespace(ch)) { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsBoundaryEnd: + if (ch == '\n') { + pSession->parserState = FwdLockConv_ParserState_WantsMimeHeaders; + pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + default: + status = FwdLockConv_Status_ProgramError; + break; + } + return status; +} + +/** + * Checks whether a given character is valid in a MIME header name. + * + * @param[in] ch The character to check. + * + * @return A Boolean value indicating whether the given character is valid in a MIME header name. + */ +static int FwdLockConv_IsMimeHeaderNameChar(int ch) { + return isgraph(ch) && ch != ':'; +} + +/** + * Checks whether a given character is valid in a MIME header value. + * + * @param[in] ch The character to check. + * + * @return A Boolean value indicating whether the given character is valid in a MIME header value. + */ +static int FwdLockConv_IsMimeHeaderValueChar(int ch) { + return isgraph(ch) && ch != ';'; +} + +/** + * Appends a character to the specified dynamically growing string. + * + * @param[in,out] pString A reference to a dynamically growing string. + * @param[in] ch The character to append. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_StringAppend(FwdLockConv_String_t *pString, int ch) { + if (pString->length == pString->maxLength) { + size_t newMaxLength = pString->maxLength + pString->lengthIncrement; + char *newPtr = realloc(pString->ptr, newMaxLength + 1); + if (newPtr == NULL) { + return FwdLockConv_Status_OutOfMemory; + } + pString->ptr = newPtr; + pString->maxLength = newMaxLength; + } + pString->ptr[pString->length++] = ch; + pString->ptr[pString->length] = '\0'; + return FwdLockConv_Status_OK; +} + +/** + * Attempts to recognize the MIME header name and changes the scanner state accordingly. + * + * @param[in,out] pSession A reference to a converter session. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_RecognizeMimeHeaderName(FwdLockConv_Session_t *pSession) { + FwdLockConv_Status_t status = FwdLockConv_Status_OK; + if (strncmp(pSession->mimeHeaderName.ptr, strContent, strlenContent) == 0) { + if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strType) == 0) { + if (pSession->contentType.ptr == NULL) { + pSession->scannerState = FwdLockConv_ScannerState_WantsContentTypeStart; + } else { + status = FwdLockConv_Status_SyntaxError; + } + } else if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strTransferEncoding) == 0) { + if (pSession->contentTransferEncoding == + FwdLockConv_ContentTransferEncoding_Undefined) { + pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingStart; + } else { + status = FwdLockConv_Status_SyntaxError; + } + } else { + pSession->scannerState = FwdLockConv_ScannerState_WantsCR; + } + } else { + pSession->scannerState = FwdLockConv_ScannerState_WantsCR; + } + return status; +} + +/** + * Applies defaults to missing MIME header values. + * + * @param[in,out] pSession A reference to a converter session. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_ApplyDefaults(FwdLockConv_Session_t *pSession) { + if (pSession->contentType.ptr == NULL) { + // Content type is missing: default to "text/plain". + pSession->contentType.ptr = malloc(sizeof strTextPlain); + if (pSession->contentType.ptr == NULL) { + return FwdLockConv_Status_OutOfMemory; + } + memcpy(pSession->contentType.ptr, strTextPlain, sizeof strTextPlain); + pSession->contentType.length = strlenTextPlain; + pSession->contentType.maxLength = strlenTextPlain; + } + if (pSession->contentTransferEncoding == FwdLockConv_ContentTransferEncoding_Undefined) { + // Content transfer encoding is missing: default to binary. + pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary; + } + return FwdLockConv_Status_OK; +} + +/** + * Verifies that the content type is supported. + * + * @param[in,out] pSession A reference to a converter session. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_VerifyContentType(FwdLockConv_Session_t *pSession) { + FwdLockConv_Status_t status; + if (pSession->contentType.ptr == NULL) { + status = FwdLockConv_Status_ProgramError; + } else if (strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmRightsXml) == 0 || + strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmContent) == 0) { + status = FwdLockConv_Status_UnsupportedFileFormat; + } else { + status = FwdLockConv_Status_OK; + } + return status; +} + +/** + * Writes the header of the output file. + * + * @param[in,out] pSession A reference to a converter session. + * @param[out] pOutput The output from the conversion process. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_WriteHeader(FwdLockConv_Session_t *pSession, + FwdLockConv_Output_t *pOutput) { + FwdLockConv_Status_t status; + if (pSession->contentType.length > UCHAR_MAX) { + status = FwdLockConv_Status_SyntaxError; + } else { + pSession->outputBufferSize = OUTPUT_BUFFER_SIZE_INCREMENT; + pOutput->fromConvertData.pBuffer = malloc(pSession->outputBufferSize); + if (pOutput->fromConvertData.pBuffer == NULL) { + status = FwdLockConv_Status_OutOfMemory; + } else { + size_t encryptedSessionKeyPos = TOP_HEADER_SIZE + pSession->contentType.length; + size_t dataSignaturePos = encryptedSessionKeyPos + pSession->encryptedSessionKeyLength; + size_t headerSignaturePos = dataSignaturePos + SHA1_HASH_SIZE; + pSession->dataOffset = headerSignaturePos + SHA1_HASH_SIZE; + memcpy(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate); + pSession->topHeader[CONTENT_TYPE_LENGTH_POS] = + (unsigned char)pSession->contentType.length; + memcpy(pOutput->fromConvertData.pBuffer, pSession->topHeader, TOP_HEADER_SIZE); + memcpy((char *)pOutput->fromConvertData.pBuffer + TOP_HEADER_SIZE, + pSession->contentType.ptr, pSession->contentType.length); + memcpy((char *)pOutput->fromConvertData.pBuffer + encryptedSessionKeyPos, + pSession->pEncryptedSessionKey, pSession->encryptedSessionKeyLength); + + // Set the signatures to all zeros for now; they will have to be updated later. + memset((char *)pOutput->fromConvertData.pBuffer + dataSignaturePos, 0, + SHA1_HASH_SIZE); + memset((char *)pOutput->fromConvertData.pBuffer + headerSignaturePos, 0, + SHA1_HASH_SIZE); + + pOutput->fromConvertData.numBytes = pSession->dataOffset; + status = FwdLockConv_Status_OK; + } + } + return status; +} + +/** + * Matches the MIME headers. + * + * @param[in,out] pSession A reference to a converter session. + * @param[in] ch A character. + * @param[out] pOutput The output from the conversion process. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_MatchMimeHeaders(FwdLockConv_Session_t *pSession, + int ch, + FwdLockConv_Output_t *pOutput) { + FwdLockConv_Status_t status = FwdLockConv_Status_OK; + switch (pSession->scannerState) { + case FwdLockConv_ScannerState_WantsMimeHeaderNameStart: + if (FwdLockConv_IsMimeHeaderNameChar(ch)) { + pSession->mimeHeaderName.length = 0; + status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch)); + if (status == FwdLockConv_Status_OK) { + pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderName; + } + } else if (ch == '\r') { + pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeadersEnd; + } else if (!FwdLockConv_IsWhitespace(ch)) { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsMimeHeaderName: + if (FwdLockConv_IsMimeHeaderNameChar(ch)) { + status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch)); + } else if (ch == ':') { + status = FwdLockConv_RecognizeMimeHeaderName(pSession); + } else if (FwdLockConv_IsWhitespace(ch)) { + pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameEnd; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsMimeHeaderNameEnd: + if (ch == ':') { + status = FwdLockConv_RecognizeMimeHeaderName(pSession); + } else if (!FwdLockConv_IsWhitespace(ch)) { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsContentTypeStart: + if (FwdLockConv_IsMimeHeaderValueChar(ch)) { + status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch)); + if (status == FwdLockConv_Status_OK) { + pSession->scannerState = FwdLockConv_ScannerState_WantsContentType; + } + } else if (!FwdLockConv_IsWhitespace(ch)) { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsContentType: + if (FwdLockConv_IsMimeHeaderValueChar(ch)) { + status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch)); + } else if (ch == ';') { + pSession->scannerState = FwdLockConv_ScannerState_WantsCR; + } else if (ch == '\r') { + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } else if (FwdLockConv_IsWhitespace(ch)) { + pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsContentTransferEncodingStart: + if (ch == 'b' || ch == 'B') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_A_OR_I; + } else if (ch == '7' || ch == '8') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_B; + } else if (!FwdLockConv_IsWhitespace(ch)) { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_A_OR_I: + if (ch == 'i' || ch == 'I') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_N; + } else if (ch == 'a' || ch == 'A') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_S; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_N: + if (ch == 'n' || ch == 'N') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_A; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_A: + if (ch == 'a' || ch == 'A') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_R; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_R: + if (ch == 'r' || ch == 'R') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_Y; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_Y: + if (ch == 'y' || ch == 'Y') { + pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary; + pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_S: + if (ch == 's' || ch == 'S') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_E; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_E: + if (ch == 'e' || ch == 'E') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_6; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_6: + if (ch == '6') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_4; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_4: + if (ch == '4') { + pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Base64; + pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_B: + if (ch == 'b' || ch == 'B') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_I; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_I: + if (ch == 'i' || ch == 'I') { + pSession->scannerState = FwdLockConv_ScannerState_Wants_T; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_Wants_T: + if (ch == 't' || ch == 'T') { + pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary; + pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_WantsContentTransferEncodingEnd: + if (ch == ';') { + pSession->scannerState = FwdLockConv_ScannerState_WantsCR; + } else if (ch == '\r') { + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } else if (FwdLockConv_IsWhitespace(ch)) { + pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd; + } else { + status = FwdLockConv_Status_UnsupportedContentTransferEncoding; + } + break; + case FwdLockConv_ScannerState_WantsMimeHeaderValueEnd: + if (ch == ';') { + pSession->scannerState = FwdLockConv_ScannerState_WantsCR; + } else if (ch == '\r') { + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } else if (!FwdLockConv_IsWhitespace(ch)) { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsCR: + if (ch == '\r') { + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } + break; + case FwdLockConv_ScannerState_WantsLF: + if (ch == '\n') { + pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsMimeHeadersEnd: + if (ch == '\n') { + status = FwdLockConv_ApplyDefaults(pSession); + if (status == FwdLockConv_Status_OK) { + status = FwdLockConv_VerifyContentType(pSession); + } + if (status == FwdLockConv_Status_OK) { + status = FwdLockConv_WriteHeader(pSession, pOutput); + } + if (status == FwdLockConv_Status_OK) { + if (pSession->contentTransferEncoding == + FwdLockConv_ContentTransferEncoding_Binary) { + pSession->parserState = FwdLockConv_ParserState_WantsBinaryEncodedData; + } else { + pSession->parserState = FwdLockConv_ParserState_WantsBase64EncodedData; + } + pSession->scannerState = FwdLockConv_ScannerState_WantsByte1; + } + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + default: + status = FwdLockConv_Status_ProgramError; + break; + } + return status; +} + +/** + * Increments the counter, treated as a 16-byte little-endian number, by one. + * + * @param[in,out] pSession A reference to a converter session. + */ +static void FwdLockConv_IncrementCounter(FwdLockConv_Session_t *pSession) { + size_t i = 0; + while ((++pSession->counter[i] == 0) && (++i < AES_BLOCK_SIZE)) + ; +} + +/** + * Encrypts the given character and writes it to the output buffer. + * + * @param[in,out] pSession A reference to a converter session. + * @param[in] ch The character to encrypt and write. + * @param[in,out] pOutput The output from the conversion process. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_WriteEncryptedChar(FwdLockConv_Session_t *pSession, + unsigned char ch, + FwdLockConv_Output_t *pOutput) { + if (pOutput->fromConvertData.numBytes == pSession->outputBufferSize) { + void *pBuffer; + pSession->outputBufferSize += OUTPUT_BUFFER_SIZE_INCREMENT; + pBuffer = realloc(pOutput->fromConvertData.pBuffer, pSession->outputBufferSize); + if (pBuffer == NULL) { + return FwdLockConv_Status_OutOfMemory; + } + pOutput->fromConvertData.pBuffer = pBuffer; + } + if (++pSession->keyStreamIndex == AES_BLOCK_SIZE) { + FwdLockConv_IncrementCounter(pSession); + pSession->keyStreamIndex = 0; + } + if (pSession->keyStreamIndex == 0) { + AES_encrypt(pSession->counter, pSession->keyStream, &pSession->encryptionRoundKeys); + } + ch ^= pSession->keyStream[pSession->keyStreamIndex]; + ((unsigned char *)pOutput->fromConvertData.pBuffer)[pOutput->fromConvertData.numBytes++] = ch; + ++pSession->numDataBytes; + return FwdLockConv_Status_OK; +} + +/** + * Matches binary-encoded content data and encrypts it, while looking out for the close delimiter. + * + * @param[in,out] pSession A reference to a converter session. + * @param[in] ch A character. + * @param[in,out] pOutput The output from the conversion process. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_MatchBinaryEncodedData(FwdLockConv_Session_t *pSession, + int ch, + FwdLockConv_Output_t *pOutput) { + FwdLockConv_Status_t status = FwdLockConv_Status_OK; + switch (pSession->scannerState) { + case FwdLockConv_ScannerState_WantsByte1: + if (ch != pSession->delimiter[pSession->delimiterMatchPos]) { + // The partial match of the delimiter turned out to be spurious. Flush the matched bytes + // to the output buffer and start over. + size_t i; + for (i = 0; i < pSession->delimiterMatchPos; ++i) { + status = FwdLockConv_WriteEncryptedChar(pSession, pSession->delimiter[i], pOutput); + if (status != FwdLockConv_Status_OK) { + return status; + } + } + pSession->delimiterMatchPos = 0; + } + if (ch != pSession->delimiter[pSession->delimiterMatchPos]) { + // The current character isn't part of the delimiter. Write it to the output buffer. + status = FwdLockConv_WriteEncryptedChar(pSession, ch, pOutput); + } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) { + // The entire delimiter has been matched. The only valid characters now are the "--" + // that complete the close delimiter (no more message parts are expected). + pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash; + } + break; + case FwdLockConv_ScannerState_WantsFirstDash: + if (ch == '-') { + pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsSecondDash: + if (ch == '-') { + pSession->parserState = FwdLockConv_ParserState_Done; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + default: + status = FwdLockConv_Status_ProgramError; + break; + } + return status; +} + +/** + * Checks whether a given character is valid in base64-encoded data. + * + * @param[in] ch The character to check. + * + * @return A Boolean value indicating whether the given character is valid in base64-encoded data. + */ +static int FwdLockConv_IsBase64Char(int ch) { + return 0 <= ch && ch <= 'z' && base64Values[ch] >= 0; +} + +/** + * Matches base64-encoded content data and encrypts it, while looking out for the close delimiter. + * + * @param[in,out] pSession A reference to a converter session. + * @param[in] ch A character. + * @param[in,out] pOutput The output from the conversion process. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_MatchBase64EncodedData(FwdLockConv_Session_t *pSession, + int ch, + FwdLockConv_Output_t *pOutput) { + FwdLockConv_Status_t status = FwdLockConv_Status_OK; + switch (pSession->scannerState) { + case FwdLockConv_ScannerState_WantsByte1: + case FwdLockConv_ScannerState_WantsByte1_AfterCRLF: + if (FwdLockConv_IsBase64Char(ch)) { + pSession->ch = base64Values[ch] << 2; + pSession->scannerState = FwdLockConv_ScannerState_WantsByte2; + } else if (ch == '\r') { + pSession->savedScannerState = FwdLockConv_ScannerState_WantsByte1_AfterCRLF; + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } else if (ch == '-') { + if (pSession->scannerState == FwdLockConv_ScannerState_WantsByte1_AfterCRLF) { + pSession->delimiterMatchPos = 3; + pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter; + } else { + status = FwdLockConv_Status_SyntaxError; + } + } else if (!FwdLockConv_IsWhitespace(ch)) { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsByte2: + if (FwdLockConv_IsBase64Char(ch)) { + pSession->ch |= base64Values[ch] >> 4; + status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput); + if (status == FwdLockConv_Status_OK) { + pSession->ch = base64Values[ch] << 4; + pSession->scannerState = FwdLockConv_ScannerState_WantsByte3; + } + } else if (ch == '\r') { + pSession->savedScannerState = pSession->scannerState; + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } else if (!FwdLockConv_IsWhitespace(ch)) { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsByte3: + if (FwdLockConv_IsBase64Char(ch)) { + pSession->ch |= base64Values[ch] >> 2; + status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput); + if (status == FwdLockConv_Status_OK) { + pSession->ch = base64Values[ch] << 6; + pSession->scannerState = FwdLockConv_ScannerState_WantsByte4; + } + } else if (ch == '\r') { + pSession->savedScannerState = pSession->scannerState; + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } else if (ch == '=') { + pSession->scannerState = FwdLockConv_ScannerState_WantsPadding; + } else if (!FwdLockConv_IsWhitespace(ch)) { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsByte4: + if (FwdLockConv_IsBase64Char(ch)) { + pSession->ch |= base64Values[ch]; + status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput); + if (status == FwdLockConv_Status_OK) { + pSession->scannerState = FwdLockConv_ScannerState_WantsByte1; + } + } else if (ch == '\r') { + pSession->savedScannerState = pSession->scannerState; + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } else if (ch == '=') { + pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace; + } else if (!FwdLockConv_IsWhitespace(ch)) { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsLF: + if (ch == '\n') { + pSession->scannerState = pSession->savedScannerState; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsPadding: + if (ch == '=') { + pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsWhitespace: + case FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF: + if (ch == '\r') { + pSession->savedScannerState = FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF; + pSession->scannerState = FwdLockConv_ScannerState_WantsLF; + } else if (ch == '-') { + if (pSession->scannerState == FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF) { + pSession->delimiterMatchPos = 3; + pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter; + } else { + status = FwdLockConv_Status_SyntaxError; + } + } else if (FwdLockConv_IsWhitespace(ch)) { + pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsDelimiter: + if (ch != pSession->delimiter[pSession->delimiterMatchPos]) { + status = FwdLockConv_Status_SyntaxError; + } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) { + pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash; + } + break; + case FwdLockConv_ScannerState_WantsFirstDash: + if (ch == '-') { + pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + case FwdLockConv_ScannerState_WantsSecondDash: + if (ch == '-') { + pSession->parserState = FwdLockConv_ParserState_Done; + } else { + status = FwdLockConv_Status_SyntaxError; + } + break; + default: + status = FwdLockConv_Status_ProgramError; + break; + } + return status; +} + +/** + * Pushes a single character into the converter's state machine. + * + * @param[in,out] pSession A reference to a converter session. + * @param[in] ch A character. + * @param[in,out] pOutput The output from the conversion process. + * + * @return A status code. + */ +static FwdLockConv_Status_t FwdLockConv_PushChar(FwdLockConv_Session_t *pSession, + int ch, + FwdLockConv_Output_t *pOutput) { + FwdLockConv_Status_t status; + ++pSession->numCharsConsumed; + switch (pSession->parserState) { + case FwdLockConv_ParserState_WantsOpenDelimiter: + status = FwdLockConv_MatchOpenDelimiter(pSession, ch); + break; + case FwdLockConv_ParserState_WantsMimeHeaders: + status = FwdLockConv_MatchMimeHeaders(pSession, ch, pOutput); + break; + case FwdLockConv_ParserState_WantsBinaryEncodedData: + status = FwdLockConv_MatchBinaryEncodedData(pSession, ch, pOutput); + break; + case FwdLockConv_ParserState_WantsBase64EncodedData: + status = FwdLockConv_MatchBase64EncodedData(pSession, ch, pOutput); + break; + case FwdLockConv_ParserState_Done: + status = FwdLockConv_Status_OK; + break; + default: + status = FwdLockConv_Status_ProgramError; + break; + } + return status; +} + +FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput) { + FwdLockConv_Status_t status; + if (pSessionId == NULL || pOutput == NULL) { + status = FwdLockConv_Status_InvalidArgument; + } else { + *pSessionId = FwdLockConv_AcquireSession(); + if (*pSessionId < 0) { + status = FwdLockConv_Status_TooManySessions; + } else { + FwdLockConv_Session_t *pSession = sessionPtrs[*pSessionId]; + pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE); + if (pSession->encryptedSessionKeyLength < AES_BLOCK_SIZE) { + // The encrypted session key is used as the CTR-mode nonce, so it must be at least + // the size of a single AES block. + status = FwdLockConv_Status_ProgramError; + } else { + pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength); + if (pSession->pEncryptedSessionKey == NULL) { + status = FwdLockConv_Status_OutOfMemory; + } else { + if (!FwdLockGlue_GetRandomNumber(pSession->sessionKey, KEY_SIZE)) { + status = FwdLockConv_Status_RandomNumberGenerationFailed; + } else if (!FwdLockGlue_EncryptKey(pSession->sessionKey, KEY_SIZE, + pSession->pEncryptedSessionKey, + pSession->encryptedSessionKeyLength)) { + status = FwdLockConv_Status_KeyEncryptionFailed; + } else { + status = FwdLockConv_DeriveKeys(pSession); + } + if (status == FwdLockConv_Status_OK) { + memset(pSession->sessionKey, 0, KEY_SIZE); // Zero out key data. + memcpy(pSession->counter, pSession->pEncryptedSessionKey, AES_BLOCK_SIZE); + pSession->parserState = FwdLockConv_ParserState_WantsOpenDelimiter; + pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash; + pSession->numCharsConsumed = 0; + pSession->delimiterMatchPos = 0; + pSession->mimeHeaderName = nullString; + pSession->contentType = nullString; + pSession->contentTransferEncoding = + FwdLockConv_ContentTransferEncoding_Undefined; + pSession->keyStreamIndex = -1; + pOutput->fromConvertData.pBuffer = NULL; + pOutput->fromConvertData.errorPos = INVALID_OFFSET; + } else { + free(pSession->pEncryptedSessionKey); + } + } + } + if (status != FwdLockConv_Status_OK) { + FwdLockConv_ReleaseSession(*pSessionId); + *pSessionId = -1; + } + } + } + return status; +} + +FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId, + const void *pBuffer, + size_t numBytes, + FwdLockConv_Output_t *pOutput) { + FwdLockConv_Status_t status; + if (!FwdLockConv_IsValidSession(sessionId) || pBuffer == NULL || pOutput == NULL) { + status = FwdLockConv_Status_InvalidArgument; + } else { + size_t i; + FwdLockConv_Session_t *pSession = sessionPtrs[sessionId]; + pSession->dataOffset = 0; + pSession->numDataBytes = 0; + pOutput->fromConvertData.numBytes = 0; + status = FwdLockConv_Status_OK; + + for (i = 0; i < numBytes; ++i) { + status = FwdLockConv_PushChar(pSession, ((char *)pBuffer)[i], pOutput); + if (status != FwdLockConv_Status_OK) { + break; + } + } + if (status == FwdLockConv_Status_OK) { + // Update the data signature. + HMAC_Update(&pSession->signingContext, + &((unsigned char *)pOutput->fromConvertData.pBuffer)[pSession->dataOffset], + pSession->numDataBytes); + } else if (status == FwdLockConv_Status_SyntaxError) { + pOutput->fromConvertData.errorPos = pSession->numCharsConsumed; + } + } + return status; +} + +FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput) { + FwdLockConv_Status_t status; + if (!FwdLockConv_IsValidSession(sessionId) || pOutput == NULL) { + status = FwdLockConv_Status_InvalidArgument; + } else { + FwdLockConv_Session_t *pSession = sessionPtrs[sessionId]; + free(pOutput->fromConvertData.pBuffer); + if (pSession->parserState != FwdLockConv_ParserState_Done) { + pOutput->fromCloseSession.errorPos = pSession->numCharsConsumed; + status = FwdLockConv_Status_SyntaxError; + } else { + // Finalize the data signature. + size_t signatureSize; + HMAC_Final(&pSession->signingContext, pOutput->fromCloseSession.signatures, + &signatureSize); + if (signatureSize != SHA1_HASH_SIZE) { + status = FwdLockConv_Status_ProgramError; + } else { + // Calculate the header signature, which is a signature of the rest of the header + // including the data signature. + HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL); + HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE); + HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->contentType.ptr, + pSession->contentType.length); + HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey, + pSession->encryptedSessionKeyLength); + HMAC_Update(&pSession->signingContext, pOutput->fromCloseSession.signatures, + signatureSize); + HMAC_Final(&pSession->signingContext, &pOutput->fromCloseSession. + signatures[signatureSize], &signatureSize); + if (signatureSize != SHA1_HASH_SIZE) { + status = FwdLockConv_Status_ProgramError; + } else { + pOutput->fromCloseSession.fileOffset = TOP_HEADER_SIZE + + pSession->contentType.length + pSession->encryptedSessionKeyLength; + status = FwdLockConv_Status_OK; + } + } + pOutput->fromCloseSession.errorPos = INVALID_OFFSET; + } + free(pSession->mimeHeaderName.ptr); + free(pSession->contentType.ptr); + free(pSession->pEncryptedSessionKey); + HMAC_CTX_cleanup(&pSession->signingContext); + FwdLockConv_ReleaseSession(sessionId); + } + return status; +} + +FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc, + FwdLockConv_ReadFunc_t *fpReadFunc, + int outputFileDesc, + FwdLockConv_WriteFunc_t *fpWriteFunc, + FwdLockConv_LSeekFunc_t *fpLSeekFunc, + off64_t *pErrorPos) { + FwdLockConv_Status_t status; + if (pErrorPos != NULL) { + *pErrorPos = INVALID_OFFSET; + } + if (fpReadFunc == NULL || fpWriteFunc == NULL || fpLSeekFunc == NULL || inputFileDesc < 0 || + outputFileDesc < 0) { + status = FwdLockConv_Status_InvalidArgument; + } else { + char *pReadBuffer = malloc(READ_BUFFER_SIZE); + if (pReadBuffer == NULL) { + status = FwdLockConv_Status_OutOfMemory; + } else { + int sessionId; + FwdLockConv_Output_t output; + status = FwdLockConv_OpenSession(&sessionId, &output); + if (status == FwdLockConv_Status_OK) { + ssize_t numBytesRead; + FwdLockConv_Status_t closeStatus; + while ((numBytesRead = + fpReadFunc(inputFileDesc, pReadBuffer, READ_BUFFER_SIZE)) > 0) { + status = FwdLockConv_ConvertData(sessionId, pReadBuffer, (size_t)numBytesRead, + &output); + if (status == FwdLockConv_Status_OK) { + if (output.fromConvertData.pBuffer != NULL && + output.fromConvertData.numBytes > 0) { + ssize_t numBytesWritten = fpWriteFunc(outputFileDesc, + output.fromConvertData.pBuffer, + output.fromConvertData.numBytes); + if (numBytesWritten != (ssize_t)output.fromConvertData.numBytes) { + status = FwdLockConv_Status_FileWriteError; + break; + } + } + } else { + if (status == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) { + *pErrorPos = output.fromConvertData.errorPos; + } + break; + } + } // end while + if (numBytesRead < 0) { + status = FwdLockConv_Status_FileReadError; + } + closeStatus = FwdLockConv_CloseSession(sessionId, &output); + if (status == FwdLockConv_Status_OK) { + if (closeStatus != FwdLockConv_Status_OK) { + if (closeStatus == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) { + *pErrorPos = output.fromCloseSession.errorPos; + } + status = closeStatus; + } else if (fpLSeekFunc(outputFileDesc, output.fromCloseSession.fileOffset, + SEEK_SET) < 0) { + status = FwdLockConv_Status_FileSeekError; + } else if (fpWriteFunc(outputFileDesc, output.fromCloseSession.signatures, + FWD_LOCK_SIGNATURES_SIZE) != FWD_LOCK_SIGNATURES_SIZE) { + status = FwdLockConv_Status_FileWriteError; + } + } + } + free(pReadBuffer); + } + } + return status; +} + +FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename, + const char *pOutputFilename, + off64_t *pErrorPos) { + FwdLockConv_Status_t status; + if (pErrorPos != NULL) { + *pErrorPos = INVALID_OFFSET; + } + if (pInputFilename == NULL || pOutputFilename == NULL) { + status = FwdLockConv_Status_InvalidArgument; + } else { + int inputFileDesc = open(pInputFilename, O_RDONLY); + if (inputFileDesc < 0) { + status = FwdLockConv_Status_FileNotFound; + } else { + int outputFileDesc = open(pOutputFilename, O_CREAT | O_TRUNC | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (outputFileDesc < 0) { + status = FwdLockConv_Status_FileCreationFailed; + } else { + status = FwdLockConv_ConvertOpenFile(inputFileDesc, read, outputFileDesc, write, + lseek64, pErrorPos); + if (close(outputFileDesc) == 0 && status != FwdLockConv_Status_OK) { + remove(pOutputFilename); + } + } + (void)close(inputFileDesc); + } + } + return status; +} diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h new file mode 100644 index 0000000..e20c0c3 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FWDLOCKCONV_H__ +#define __FWDLOCKCONV_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +/** + * The size of the data and header signatures combined. The signatures are adjacent to each other in + * the produced output file. + */ +#define FWD_LOCK_SIGNATURES_SIZE (2 * 20) + +/** + * Data type for the output from FwdLockConv_ConvertData. + */ +typedef struct FwdLockConv_ConvertData_Output { + /// The converted data. + void *pBuffer; + + /// The size of the converted data. + size_t numBytes; + + /// The file position where the error occurred, in the case of a syntax error. + off64_t errorPos; +} FwdLockConv_ConvertData_Output_t; + +/** + * Data type for the output from FwdLockConv_CloseSession. + */ +typedef struct FwdLockConv_CloseSession_Output { + /// The final set of signatures. + unsigned char signatures[FWD_LOCK_SIGNATURES_SIZE]; + + /// The offset in the produced output file where the signatures are located. + off64_t fileOffset; + + /// The file position where the error occurred, in the case of a syntax error. + off64_t errorPos; +} FwdLockConv_CloseSession_Output_t; + +/** + * Data type for the output from the conversion process. + */ +typedef union FwdLockConv_Output { + FwdLockConv_ConvertData_Output_t fromConvertData; + FwdLockConv_CloseSession_Output_t fromCloseSession; +} FwdLockConv_Output_t; + +/** + * Data type for the Posix-style read function used by the converter in pull mode. + * + * @param[in] fileDesc The file descriptor of a file opened for reading. + * @param[out] pBuffer A reference to the buffer that should receive the read data. + * @param[in] numBytes The number of bytes to read. + * + * @return The number of bytes read. + * @retval -1 Failure. + */ +typedef ssize_t FwdLockConv_ReadFunc_t(int fileDesc, void *pBuffer, size_t numBytes); + +/** + * Data type for the Posix-style write function used by the converter in pull mode. + * + * @param[in] fileDesc The file descriptor of a file opened for writing. + * @param[in] pBuffer A reference to the buffer containing the data to be written. + * @param[in] numBytes The number of bytes to write. + * + * @return The number of bytes written. + * @retval -1 Failure. + */ +typedef ssize_t FwdLockConv_WriteFunc_t(int fileDesc, const void *pBuffer, size_t numBytes); + +/** + * Data type for the Posix-style lseek function used by the converter in pull mode. + * + * @param[in] fileDesc The file descriptor of a file opened for writing. + * @param[in] offset The offset with which to update the file position. + * @param[in] whence One of SEEK_SET, SEEK_CUR, and SEEK_END. + * + * @return The new file position. + * @retval ((off64_t)-1) Failure. + */ +typedef off64_t FwdLockConv_LSeekFunc_t(int fileDesc, off64_t offset, int whence); + +/** + * The status codes returned by the converter functions. + */ +typedef enum FwdLockConv_Status { + /// The operation was successful. + FwdLockConv_Status_OK = 0, + + /// An actual argument to the function is invalid (a program error on the caller's part). + FwdLockConv_Status_InvalidArgument = 1, + + /// There is not enough free dynamic memory to complete the operation. + FwdLockConv_Status_OutOfMemory = 2, + + /// An error occurred while opening the input file. + FwdLockConv_Status_FileNotFound = 3, + + /// An error occurred while creating the output file. + FwdLockConv_Status_FileCreationFailed = 4, + + /// An error occurred while reading from the input file. + FwdLockConv_Status_FileReadError = 5, + + /// An error occurred while writing to the output file. + FwdLockConv_Status_FileWriteError = 6, + + /// An error occurred while seeking to a new file position within the output file. + FwdLockConv_Status_FileSeekError = 7, + + /// The input file is not a syntactically correct OMA DRM v1 Forward Lock file. + FwdLockConv_Status_SyntaxError = 8, + + /// Support for this DRM file format has been disabled in the current product configuration. + FwdLockConv_Status_UnsupportedFileFormat = 9, + + /// The content transfer encoding is not one of "binary", "base64", "7bit", or "8bit" + /// (case-insensitive). + FwdLockConv_Status_UnsupportedContentTransferEncoding = 10, + + /// The generation of a random number failed. + FwdLockConv_Status_RandomNumberGenerationFailed = 11, + + /// Key encryption failed. + FwdLockConv_Status_KeyEncryptionFailed = 12, + + /// The calculation of a keyed hash for integrity protection failed. + FwdLockConv_Status_IntegrityProtectionFailed = 13, + + /// There are too many ongoing sessions for another one to be opened. + FwdLockConv_Status_TooManySessions = 14, + + /// An unexpected error occurred. + FwdLockConv_Status_ProgramError = 15 +} FwdLockConv_Status_t; + +/** + * Opens a session for converting an OMA DRM v1 Forward Lock file to the internal Forward Lock file + * format. + * + * @param[out] pSessionId The session ID. + * @param[out] pOutput The output from the conversion process (initialized). + * + * @return A status code. + * @retval FwdLockConv_Status_OK + * @retval FwdLockConv_Status_InvalidArgument + * @retval FwdLockConv_Status_TooManySessions + */ +FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput); + +/** + * Supplies the converter with data to convert. The caller is expected to write the converted data + * to file. Can be called an arbitrary number of times. + * + * @param[in] sessionId The session ID. + * @param[in] pBuffer A reference to a buffer containing the data to convert. + * @param[in] numBytes The number of bytes to convert. + * @param[in,out] pOutput The output from the conversion process (allocated/reallocated). + * + * @return A status code. + * @retval FwdLockConv_Status_OK + * @retval FwdLockConv_Status_InvalidArgument + * @retval FwdLockConv_Status_OutOfMemory + * @retval FwdLockConv_Status_SyntaxError + * @retval FwdLockConv_Status_UnsupportedFileFormat + * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding + * @retval FwdLockConv_Status_RandomNumberGenerationFailed + * @retval FwdLockConv_Status_KeyEncryptionFailed + * @retval FwdLockConv_Status_DataEncryptionFailed + */ +FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId, + const void *pBuffer, + size_t numBytes, + FwdLockConv_Output_t *pOutput); + +/** + * Closes a session for converting an OMA DRM v1 Forward Lock file to the internal Forward Lock + * file format. The caller must update the produced output file at the indicated file offset with + * the final set of signatures. + * + * @param[in] sessionId The session ID. + * @param[in,out] pOutput The output from the conversion process (deallocated and overwritten). + * + * @return A status code. + * @retval FwdLockConv_Status_OK + * @retval FwdLockConv_Status_InvalidArgument + * @retval FwdLockConv_Status_OutOfMemory + * @retval FwdLockConv_Status_IntegrityProtectionFailed + */ +FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput); + +/** + * Converts an open OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull + * mode. + * + * @param[in] inputFileDesc The file descriptor of the open input file. + * @param[in] fpReadFunc A reference to a read function that can operate on the open input file. + * @param[in] outputFileDesc The file descriptor of the open output file. + * @param[in] fpWriteFunc A reference to a write function that can operate on the open output file. + * @param[in] fpLSeekFunc A reference to an lseek function that can operate on the open output file. + * @param[out] pErrorPos + * The file position where the error occurred, in the case of a syntax error. May be NULL. + * + * @return A status code. + * @retval FwdLockConv_Status_OK + * @retval FwdLockConv_Status_InvalidArgument + * @retval FwdLockConv_Status_OutOfMemory + * @retval FwdLockConv_Status_FileReadError + * @retval FwdLockConv_Status_FileWriteError + * @retval FwdLockConv_Status_FileSeekError + * @retval FwdLockConv_Status_SyntaxError + * @retval FwdLockConv_Status_UnsupportedFileFormat + * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding + * @retval FwdLockConv_Status_RandomNumberGenerationFailed + * @retval FwdLockConv_Status_KeyEncryptionFailed + * @retval FwdLockConv_Status_DataEncryptionFailed + * @retval FwdLockConv_Status_IntegrityProtectionFailed + * @retval FwdLockConv_Status_TooManySessions + */ +FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc, + FwdLockConv_ReadFunc_t *fpReadFunc, + int outputFileDesc, + FwdLockConv_WriteFunc_t *fpWriteFunc, + FwdLockConv_LSeekFunc_t *fpLSeekFunc, + off64_t *pErrorPos); + +/** + * Converts an OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull mode. + * + * @param[in] pInputFilename A reference to the input filename. + * @param[in] pOutputFilename A reference to the output filename. + * @param[out] pErrorPos + * The file position where the error occurred, in the case of a syntax error. May be NULL. + * + * @return A status code. + * @retval FwdLockConv_Status_OK + * @retval FwdLockConv_Status_InvalidArgument + * @retval FwdLockConv_Status_OutOfMemory + * @retval FwdLockConv_Status_FileNotFound + * @retval FwdLockConv_Status_FileCreationFailed + * @retval FwdLockConv_Status_FileReadError + * @retval FwdLockConv_Status_FileWriteError + * @retval FwdLockConv_Status_FileSeekError + * @retval FwdLockConv_Status_SyntaxError + * @retval FwdLockConv_Status_UnsupportedFileFormat + * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding + * @retval FwdLockConv_Status_RandomNumberGenerationFailed + * @retval FwdLockConv_Status_KeyEncryptionFailed + * @retval FwdLockConv_Status_DataEncryptionFailed + * @retval FwdLockConv_Status_IntegrityProtectionFailed + * @retval FwdLockConv_Status_TooManySessions + */ +FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename, + const char *pOutputFilename, + off64_t *pErrorPos); + +#ifdef __cplusplus +} +#endif + +#endif // __FWDLOCKCONV_H__ diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk new file mode 100644 index 0000000..b625edf --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk @@ -0,0 +1,37 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + FwdLockFile.c + +LOCAL_C_INCLUDES := \ + frameworks/base/drm/libdrmframework/plugins/forward-lock/internal-format/common \ + external/openssl/include + +LOCAL_SHARED_LIBRARIES := libcrypto + +LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common + +LOCAL_STATIC_LIBRARIES := libfwdlock-common + +LOCAL_MODULE := libfwdlock-decoder + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c new file mode 100644 index 0000000..98284e7 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <openssl/aes.h> +#include <openssl/hmac.h> + +#include "FwdLockFile.h" +#include "FwdLockGlue.h" + +#define TRUE 1 +#define FALSE 0 + +#define INVALID_OFFSET ((off64_t)-1) + +#define INVALID_BLOCK_INDEX ((uint64_t)-1) + +#define MAX_NUM_SESSIONS 128 + +#define KEY_SIZE AES_BLOCK_SIZE +#define KEY_SIZE_IN_BITS (KEY_SIZE * 8) + +#define SHA1_HASH_SIZE 20 +#define SHA1_BLOCK_SIZE 64 + +#define FWD_LOCK_VERSION 0 +#define FWD_LOCK_SUBFORMAT 0 +#define USAGE_RESTRICTION_FLAGS 0 +#define CONTENT_TYPE_LENGTH_POS 7 +#define TOP_HEADER_SIZE 8 + +#define SIG_CALC_BUFFER_SIZE (16 * SHA1_BLOCK_SIZE) + +/** + * Data type for the per-file state information needed by the decoder. + */ +typedef struct FwdLockFile_Session { + int fileDesc; + unsigned char topHeader[TOP_HEADER_SIZE]; + char *pContentType; + size_t contentTypeLength; + void *pEncryptedSessionKey; + size_t encryptedSessionKeyLength; + unsigned char dataSignature[SHA1_HASH_SIZE]; + unsigned char headerSignature[SHA1_HASH_SIZE]; + off64_t dataOffset; + off64_t filePos; + AES_KEY encryptionRoundKeys; + HMAC_CTX signingContext; + unsigned char keyStream[AES_BLOCK_SIZE]; + uint64_t blockIndex; +} FwdLockFile_Session_t; + +static FwdLockFile_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL }; + +static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER; + +static const unsigned char topHeaderTemplate[] = + { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS }; + +/** + * Acquires an unused file session for the given file descriptor. + * + * @param[in] fileDesc A file descriptor. + * + * @return A session ID. + */ +static int FwdLockFile_AcquireSession(int fileDesc) { + int sessionId = -1; + if (fileDesc < 0) { + errno = EBADF; + } else { + int i; + pthread_mutex_lock(&sessionAcquisitionMutex); + for (i = 0; i < MAX_NUM_SESSIONS; ++i) { + int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS; + if (sessionPtrs[candidateSessionId] == NULL) { + sessionPtrs[candidateSessionId] = malloc(sizeof **sessionPtrs); + if (sessionPtrs[candidateSessionId] != NULL) { + sessionPtrs[candidateSessionId]->fileDesc = fileDesc; + sessionPtrs[candidateSessionId]->pContentType = NULL; + sessionPtrs[candidateSessionId]->pEncryptedSessionKey = NULL; + sessionId = candidateSessionId; + } + break; + } + } + pthread_mutex_unlock(&sessionAcquisitionMutex); + if (i == MAX_NUM_SESSIONS) { + errno = ENFILE; + } + } + return sessionId; +} + +/** + * Finds the file session associated to the given file descriptor. + * + * @param[in] fileDesc A file descriptor. + * + * @return A session ID. + */ +static int FwdLockFile_FindSession(int fileDesc) { + int sessionId = -1; + if (fileDesc < 0) { + errno = EBADF; + } else { + int i; + pthread_mutex_lock(&sessionAcquisitionMutex); + for (i = 0; i < MAX_NUM_SESSIONS; ++i) { + int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS; + if (sessionPtrs[candidateSessionId] != NULL && + sessionPtrs[candidateSessionId]->fileDesc == fileDesc) { + sessionId = candidateSessionId; + break; + } + } + pthread_mutex_unlock(&sessionAcquisitionMutex); + if (i == MAX_NUM_SESSIONS) { + errno = EBADF; + } + } + return sessionId; +} + +/** + * Releases a file session. + * + * @param[in] sessionID A session ID. + */ +static void FwdLockFile_ReleaseSession(int sessionId) { + pthread_mutex_lock(&sessionAcquisitionMutex); + assert(0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL); + free(sessionPtrs[sessionId]->pContentType); + free(sessionPtrs[sessionId]->pEncryptedSessionKey); + memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data. + free(sessionPtrs[sessionId]); + sessionPtrs[sessionId] = NULL; + pthread_mutex_unlock(&sessionAcquisitionMutex); +} + +/** + * Derives keys for encryption and signing from the encrypted session key. + * + * @param[in,out] pSession A reference to a file session. + * + * @return A Boolean value indicating whether key derivation was successful. + */ +static int FwdLockFile_DeriveKeys(FwdLockFile_Session_t * pSession) { + int result; + struct FwdLockFile_DeriveKeys_Data { + AES_KEY sessionRoundKeys; + unsigned char value[KEY_SIZE]; + unsigned char key[KEY_SIZE]; + } *pData = malloc(sizeof *pData); + if (pData == NULL) { + result = FALSE; + } else { + result = FwdLockGlue_DecryptKey(pSession->pEncryptedSessionKey, + pSession->encryptedSessionKeyLength, pData->key, KEY_SIZE); + if (result) { + if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS, &pData->sessionRoundKeys) != 0) { + result = FALSE; + } else { + // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key. + memset(pData->value, 0, KEY_SIZE); + AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys); + if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS, + &pSession->encryptionRoundKeys) != 0) { + result = FALSE; + } else { + // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key. + ++pData->value[0]; + AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys); + HMAC_CTX_init(&pSession->signingContext); + HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL); + } + } + } + if (!result) { + errno = ENOSYS; + } + memset(pData, 0, sizeof pData); // Zero out key data. + free(pData); + } + return result; +} + +/** + * Calculates the counter, treated as a 16-byte little-endian number, used to generate the keystream + * for the given block. + * + * @param[in] pNonce A reference to the nonce. + * @param[in] blockIndex The index number of the block. + * @param[out] pCounter A reference to the counter. + */ +static void FwdLockFile_CalculateCounter(const unsigned char *pNonce, + uint64_t blockIndex, + unsigned char *pCounter) { + unsigned char carry = 0; + size_t i = 0; + for (; i < sizeof blockIndex; ++i) { + unsigned char part = pNonce[i] + (unsigned char)(blockIndex >> (i * CHAR_BIT)); + pCounter[i] = part + carry; + carry = (part < pNonce[i] || pCounter[i] < part) ? 1 : 0; + } + for (; i < AES_BLOCK_SIZE; ++i) { + pCounter[i] = pNonce[i] + carry; + carry = (pCounter[i] < pNonce[i]) ? 1 : 0; + } +} + +/** + * Decrypts the byte at the current file position using AES-128-CTR. In CTR (or "counter") mode, + * encryption and decryption are performed using the same algorithm. + * + * @param[in,out] pSession A reference to a file session. + * @param[in] pByte The byte to decrypt. + */ +void FwdLockFile_DecryptByte(FwdLockFile_Session_t * pSession, unsigned char *pByte) { + uint64_t blockIndex = pSession->filePos / AES_BLOCK_SIZE; + uint64_t blockOffset = pSession->filePos % AES_BLOCK_SIZE; + if (blockIndex != pSession->blockIndex) { + // The first 16 bytes of the encrypted session key is used as the nonce. + unsigned char counter[AES_BLOCK_SIZE]; + FwdLockFile_CalculateCounter(pSession->pEncryptedSessionKey, blockIndex, counter); + AES_encrypt(counter, pSession->keyStream, &pSession->encryptionRoundKeys); + pSession->blockIndex = blockIndex; + } + *pByte ^= pSession->keyStream[blockOffset]; +} + +int FwdLockFile_attach(int fileDesc) { + int sessionId = FwdLockFile_AcquireSession(fileDesc); + if (sessionId >= 0) { + FwdLockFile_Session_t *pSession = sessionPtrs[sessionId]; + int isSuccess = FALSE; + if (read(fileDesc, pSession->topHeader, TOP_HEADER_SIZE) == TOP_HEADER_SIZE && + memcmp(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate) == 0) { + pSession->contentTypeLength = pSession->topHeader[CONTENT_TYPE_LENGTH_POS]; + assert(pSession->contentTypeLength <= UCHAR_MAX); // Untaint scalar for code checkers. + pSession->pContentType = malloc(pSession->contentTypeLength + 1); + if (pSession->pContentType != NULL && + read(fileDesc, pSession->pContentType, pSession->contentTypeLength) == + (ssize_t)pSession->contentTypeLength) { + pSession->pContentType[pSession->contentTypeLength] = '\0'; + pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE); + pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength); + if (pSession->pEncryptedSessionKey != NULL && + read(fileDesc, pSession->pEncryptedSessionKey, + pSession->encryptedSessionKeyLength) == + (ssize_t)pSession->encryptedSessionKeyLength && + read(fileDesc, pSession->dataSignature, SHA1_HASH_SIZE) == + SHA1_HASH_SIZE && + read(fileDesc, pSession->headerSignature, SHA1_HASH_SIZE) == + SHA1_HASH_SIZE) { + isSuccess = FwdLockFile_DeriveKeys(pSession); + } + } + } + if (isSuccess) { + pSession->dataOffset = pSession->contentTypeLength + + pSession->encryptedSessionKeyLength + TOP_HEADER_SIZE + 2 * SHA1_HASH_SIZE; + pSession->filePos = 0; + pSession->blockIndex = INVALID_BLOCK_INDEX; + } else { + FwdLockFile_ReleaseSession(sessionId); + sessionId = -1; + } + } + return (sessionId >= 0) ? 0 : -1; +} + +int FwdLockFile_open(const char *pFilename) { + int fileDesc = open(pFilename, O_RDONLY); + if (fileDesc >= 0 && FwdLockFile_attach(fileDesc) < 0) { + (void)close(fileDesc); + fileDesc = -1; + } + return fileDesc; +} + +ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes) { + ssize_t numBytesRead; + int sessionId = FwdLockFile_FindSession(fileDesc); + if (sessionId < 0) { + numBytesRead = -1; + } else { + FwdLockFile_Session_t *pSession = sessionPtrs[sessionId]; + ssize_t i; + numBytesRead = read(pSession->fileDesc, pBuffer, numBytes); + for (i = 0; i < numBytesRead; ++i) { + FwdLockFile_DecryptByte(pSession, &((unsigned char *)pBuffer)[i]); + ++pSession->filePos; + } + } + return numBytesRead; +} + +off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence) { + off64_t newFilePos; + int sessionId = FwdLockFile_FindSession(fileDesc); + if (sessionId < 0) { + newFilePos = INVALID_OFFSET; + } else { + FwdLockFile_Session_t *pSession = sessionPtrs[sessionId]; + switch (whence) { + case SEEK_SET: + newFilePos = lseek64(pSession->fileDesc, pSession->dataOffset + offset, whence); + break; + case SEEK_CUR: + case SEEK_END: + newFilePos = lseek64(pSession->fileDesc, offset, whence); + break; + default: + errno = EINVAL; + newFilePos = INVALID_OFFSET; + break; + } + if (newFilePos != INVALID_OFFSET) { + if (newFilePos < pSession->dataOffset) { + // The new file position is illegal for an internal Forward Lock file. Restore the + // original file position. + (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos, + SEEK_SET); + errno = EINVAL; + newFilePos = INVALID_OFFSET; + } else { + // The return value should be the file position that lseek64() would have returned + // for the embedded content file. + pSession->filePos = newFilePos - pSession->dataOffset; + newFilePos = pSession->filePos; + } + } + } + return newFilePos; +} + +int FwdLockFile_detach(int fileDesc) { + int sessionId = FwdLockFile_FindSession(fileDesc); + if (sessionId < 0) { + return -1; + } + HMAC_CTX_cleanup(&sessionPtrs[sessionId]->signingContext); + FwdLockFile_ReleaseSession(sessionId); + return 0; +} + +int FwdLockFile_close(int fileDesc) { + return (FwdLockFile_detach(fileDesc) == 0) ? close(fileDesc) : -1; +} + +int FwdLockFile_CheckDataIntegrity(int fileDesc) { + int result; + int sessionId = FwdLockFile_FindSession(fileDesc); + if (sessionId < 0) { + result = FALSE; + } else { + struct FwdLockFile_CheckDataIntegrity_Data { + unsigned char signature[SHA1_HASH_SIZE]; + unsigned char buffer[SIG_CALC_BUFFER_SIZE]; + } *pData = malloc(sizeof *pData); + if (pData == NULL) { + result = FALSE; + } else { + FwdLockFile_Session_t *pSession = sessionPtrs[sessionId]; + if (lseek64(pSession->fileDesc, pSession->dataOffset, SEEK_SET) != + pSession->dataOffset) { + result = FALSE; + } else { + ssize_t numBytesRead; + size_t signatureSize = SHA1_HASH_SIZE; + while ((numBytesRead = + read(pSession->fileDesc, pData->buffer, SIG_CALC_BUFFER_SIZE)) > 0) { + HMAC_Update(&pSession->signingContext, pData->buffer, (size_t)numBytesRead); + } + if (numBytesRead < 0) { + result = FALSE; + } else { + HMAC_Final(&pSession->signingContext, pData->signature, &signatureSize); + assert(signatureSize == SHA1_HASH_SIZE); + result = memcmp(pData->signature, pSession->dataSignature, signatureSize) == 0; + } + HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL); + (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos, + SEEK_SET); + } + free(pData); + } + } + return result; +} + +int FwdLockFile_CheckHeaderIntegrity(int fileDesc) { + int result; + int sessionId = FwdLockFile_FindSession(fileDesc); + if (sessionId < 0) { + result = FALSE; + } else { + FwdLockFile_Session_t *pSession = sessionPtrs[sessionId]; + unsigned char signature[SHA1_HASH_SIZE]; + size_t signatureSize = SHA1_HASH_SIZE; + HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE); + HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->pContentType, + pSession->contentTypeLength); + HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey, + pSession->encryptedSessionKeyLength); + HMAC_Update(&pSession->signingContext, pSession->dataSignature, signatureSize); + HMAC_Final(&pSession->signingContext, signature, &signatureSize); + assert(signatureSize == SHA1_HASH_SIZE); + result = memcmp(signature, pSession->headerSignature, signatureSize) == 0; + HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL); + } + return result; +} + +int FwdLockFile_CheckIntegrity(int fileDesc) { + return FwdLockFile_CheckHeaderIntegrity(fileDesc) && FwdLockFile_CheckDataIntegrity(fileDesc); +} + +const char *FwdLockFile_GetContentType(int fileDesc) { + int sessionId = FwdLockFile_FindSession(fileDesc); + if (sessionId < 0) { + return NULL; + } + return sessionPtrs[sessionId]->pContentType; +} diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h new file mode 100644 index 0000000..fc64050 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FWDLOCKFILE_H__ +#define __FWDLOCKFILE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +/** + * Attaches to an open Forward Lock file. The file position is assumed to be at the beginning of the + * file. + * + * @param[in] fileDesc The file descriptor of an open Forward Lock file. + * + * @return A status code. + * @retval 0 Success. + * @retval -1 Failure. + */ +int FwdLockFile_attach(int fileDesc); + +/** + * Opens a Forward Lock file for reading. + * + * @param[in] pFilename A reference to a filename. + * + * @return A file descriptor. + * @retval -1 Failure. + */ +int FwdLockFile_open(const char *pFilename); + +/** + * Reads the specified number of bytes from an open Forward Lock file. + * + * @param[in] fileDesc The file descriptor of an open Forward Lock file. + * @param[out] pBuffer A reference to the buffer that should receive the read data. + * @param[in] numBytes The number of bytes to read. + * + * @return The number of bytes read. + * @retval -1 Failure. + */ +ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes); + +/** + * Updates the file position within an open Forward Lock file. + * + * @param[in] fileDesc The file descriptor of an open Forward Lock file. + * @param[in] offset The offset with which to update the file position. + * @param[in] whence One of SEEK_SET, SEEK_CUR, and SEEK_END. + * + * @return The new file position. + * @retval ((off64_t)-1) Failure. + */ +off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence); + +/** + * Detaches from an open Forward Lock file. + * + * @param[in] fileDesc The file descriptor of an open Forward Lock file. + * + * @return A status code. + * @retval 0 Success. + * @retval -1 Failure. + */ +int FwdLockFile_detach(int fileDesc); + +/** + * Closes an open Forward Lock file. + * + * @param[in] fileDesc The file descriptor of an open Forward Lock file. + * + * @return A status code. + * @retval 0 Success. + * @retval -1 Failure. + */ +int FwdLockFile_close(int fileDesc); + +/** + * Checks the data integrity of an open Forward Lock file. + * + * @param[in] fileDesc The file descriptor of an open Forward Lock file. + * + * @return A Boolean value indicating whether the integrity check was successful. + */ +int FwdLockFile_CheckDataIntegrity(int fileDesc); + +/** + * Checks the header integrity of an open Forward Lock file. + * + * @param[in] fileDesc The file descriptor of an open Forward Lock file. + * + * @return A Boolean value indicating whether the integrity check was successful. + */ +int FwdLockFile_CheckHeaderIntegrity(int fileDesc); + +/** + * Checks both the data and header integrity of an open Forward Lock file. + * + * @param[in] fileDesc The file descriptor of an open Forward Lock file. + * + * @return A Boolean value indicating whether the integrity check was successful. + */ +int FwdLockFile_CheckIntegrity(int fileDesc); + +/** + * Returns the content type of an open Forward Lock file. + * + * @param[in] fileDesc The file descriptor of an open Forward Lock file. + * + * @return + * A reference to the content type. The reference remains valid as long as the file is kept open. + */ +const char *FwdLockFile_GetContentType(int fileDesc); + +#ifdef __cplusplus +} +#endif + +#endif // __FWDLOCKFILE_H__ diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html new file mode 100755 index 0000000..8f95cd2 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html @@ -0,0 +1,1039 @@ +<html> + +<head> +<meta http-equiv=Content-Type content="text/html; charset=windows-1252"> +<meta name=Generator content="Microsoft Word 12 (filtered)"> +<title>Forward Lock Converter and Decoder</title> +<style> +<!-- + /* Font Definitions */ + @font-face + {font-family:SimSun; + panose-1:2 1 6 0 3 1 1 1 1 1;} +@font-face + {font-family:"Cambria Math"; + panose-1:2 4 5 3 5 4 6 3 2 4;} +@font-face + {font-family:Tahoma; + panose-1:2 11 6 4 3 5 4 4 2 4;} +@font-face + {font-family:"Lucida Console","DejaVu Sans Mono"; + panose-1:2 11 6 9 4 5 4 2 2 4;} +@font-face + {font-family:"\@SimSun"; + panose-1:2 1 6 0 3 1 1 1 1 1;} + /* Style Definitions */ + p.MsoNormal, li.MsoNormal, div.MsoNormal + {margin:0cm; + margin-bottom:.0001pt; + font-size:12.0pt; + font-family:"Times New Roman","serif";} +h1 + {margin-right:0cm; + margin-left:21.6pt; + text-indent:-21.6pt; + page-break-after:avoid; + font-size:16.0pt; + font-family:"Arial","sans-serif";} +h2 + {margin-top:12.0pt; + margin-right:0cm; + margin-bottom:3.0pt; + margin-left:28.8pt; + text-indent:-28.8pt; + page-break-after:avoid; + font-size:14.0pt; + font-family:"Arial","sans-serif"; + font-style:italic;} +h3 + {margin-top:12.0pt; + margin-right:0cm; + margin-bottom:3.0pt; + margin-left:36.0pt; + text-indent:-36.0pt; + page-break-after:avoid; + font-size:13.0pt; + font-family:"Arial","sans-serif";} +h4 + {margin-top:12.0pt; + margin-right:0cm; + margin-bottom:3.0pt; + margin-left:43.2pt; + text-indent:-43.2pt; + page-break-after:avoid; + font-size:14.0pt; + font-family:"Times New Roman","serif";} +h5 + {margin-top:12.0pt; + margin-right:0cm; + margin-bottom:3.0pt; + margin-left:50.4pt; + text-indent:-50.4pt; + font-size:13.0pt; + font-family:"Times New Roman","serif"; + font-style:italic;} +h6 + {margin-top:12.0pt; + margin-right:0cm; + margin-bottom:3.0pt; + margin-left:57.6pt; + text-indent:-57.6pt; + font-size:11.0pt; + font-family:"Times New Roman","serif";} +p.MsoHeading7, li.MsoHeading7, div.MsoHeading7 + {margin-top:12.0pt; + margin-right:0cm; + margin-bottom:3.0pt; + margin-left:64.8pt; + text-indent:-64.8pt; + font-size:12.0pt; + font-family:"Times New Roman","serif";} +p.MsoHeading8, li.MsoHeading8, div.MsoHeading8 + {margin-top:12.0pt; + margin-right:0cm; + margin-bottom:3.0pt; + margin-left:72.0pt; + text-indent:-72.0pt; + font-size:12.0pt; + font-family:"Times New Roman","serif"; + font-style:italic;} +p.MsoHeading9, li.MsoHeading9, div.MsoHeading9 + {margin-top:12.0pt; + margin-right:0cm; + margin-bottom:3.0pt; + margin-left:79.2pt; + text-indent:-79.2pt; + font-size:11.0pt; + font-family:"Arial","sans-serif";} +p.MsoToc1, li.MsoToc1, div.MsoToc1 + {margin-top:6.0pt; + margin-right:0cm; + margin-bottom:6.0pt; + margin-left:0cm; + line-height:150%; + font-size:10.5pt; + font-family:"Times New Roman","serif"; + text-transform:uppercase; + font-weight:bold;} +p.MsoToc2, li.MsoToc2, div.MsoToc2 + {margin-top:0cm; + margin-right:0cm; + margin-bottom:0cm; + margin-left:12.0pt; + margin-bottom:.0001pt; + line-height:150%; + font-size:10.5pt; + font-family:"Times New Roman","serif"; + font-variant:small-caps;} +p.MsoToc3, li.MsoToc3, div.MsoToc3 + {margin-top:0cm; + margin-right:0cm; + margin-bottom:0cm; + margin-left:24.0pt; + margin-bottom:.0001pt; + line-height:150%; + font-size:10.5pt; + font-family:"Times New Roman","serif"; + font-style:italic;} +p.MsoToc4, li.MsoToc4, div.MsoToc4 + {margin-top:0cm; + margin-right:0cm; + margin-bottom:0cm; + margin-left:36.0pt; + margin-bottom:.0001pt; + font-size:9.0pt; + font-family:"Times New Roman","serif";} +p.MsoToc5, li.MsoToc5, div.MsoToc5 + {margin-top:0cm; + margin-right:0cm; + margin-bottom:0cm; + margin-left:48.0pt; + margin-bottom:.0001pt; + font-size:9.0pt; + font-family:"Times New Roman","serif";} +p.MsoToc6, li.MsoToc6, div.MsoToc6 + {margin-top:0cm; + margin-right:0cm; + margin-bottom:0cm; + margin-left:60.0pt; + margin-bottom:.0001pt; + font-size:9.0pt; + font-family:"Times New Roman","serif";} +p.MsoToc7, li.MsoToc7, div.MsoToc7 + {margin-top:0cm; + margin-right:0cm; + margin-bottom:0cm; + margin-left:72.0pt; + margin-bottom:.0001pt; + font-size:9.0pt; + font-family:"Times New Roman","serif";} +p.MsoToc8, li.MsoToc8, div.MsoToc8 + {margin-top:0cm; + margin-right:0cm; + margin-bottom:0cm; + margin-left:84.0pt; + margin-bottom:.0001pt; + font-size:9.0pt; + font-family:"Times New Roman","serif";} +p.MsoToc9, li.MsoToc9, div.MsoToc9 + {margin-top:0cm; + margin-right:0cm; + margin-bottom:0cm; + margin-left:96.0pt; + margin-bottom:.0001pt; + font-size:9.0pt; + font-family:"Times New Roman","serif";} +p.MsoFootnoteText, li.MsoFootnoteText, div.MsoFootnoteText + {margin:0cm; + margin-bottom:.0001pt; + font-size:10.0pt; + font-family:"Times New Roman","serif";} +p.MsoHeader, li.MsoHeader, div.MsoHeader + {margin:0cm; + margin-bottom:.0001pt; + font-size:12.0pt; + font-family:"Times New Roman","serif";} +p.MsoFooter, li.MsoFooter, div.MsoFooter + {margin:0cm; + margin-bottom:.0001pt; + font-size:12.0pt; + font-family:"Times New Roman","serif";} +p.MsoCaption, li.MsoCaption, div.MsoCaption + {margin:0cm; + margin-bottom:.0001pt; + font-size:11.0pt; + font-family:"Times New Roman","serif"; + font-weight:bold;} +span.MsoFootnoteReference + {vertical-align:super;} +p.MsoTitle, li.MsoTitle, div.MsoTitle + {margin-top:12.0pt; + margin-right:0cm; + margin-bottom:120.0pt; + margin-left:0cm; + text-align:center; + font-size:16.0pt; + font-family:"Arial","sans-serif"; + font-weight:bold;} +p.MsoBodyText, li.MsoBodyText, div.MsoBodyText + {mso-style-link:"Body Text Char"; + margin-top:0cm; + margin-right:0cm; + margin-bottom:6.0pt; + margin-left:0cm; + font-size:12.0pt; + font-family:"Times New Roman","serif";} +a:link, span.MsoHyperlink + {color:blue; + text-decoration:underline;} +a:visited, span.MsoHyperlinkFollowed + {color:purple; + text-decoration:underline;} +p.MsoAcetate, li.MsoAcetate, div.MsoAcetate + {margin:0cm; + margin-bottom:.0001pt; + font-size:8.0pt; + font-family:"Tahoma","sans-serif";} +span.BodyTextChar + {mso-style-name:"Body Text Char"; + mso-style-link:"Body Text";} + /* Page Definitions */ + @page WordSection1 + {size:595.45pt 841.7pt; + margin:72.0pt 90.0pt 72.0pt 90.0pt;} +div.WordSection1 + {page:WordSection1;} +@page WordSection2 + {size:595.45pt 841.7pt; + margin:72.0pt 90.0pt 72.0pt 90.0pt;} +div.WordSection2 + {page:WordSection2;} + /* List Definitions */ + ol + {margin-bottom:0cm;} +ul + {margin-bottom:0cm;} +--> +</style> + +</head> + +<body lang=EN-US link=blue vlink=purple> + +<div class=WordSection1> + +<p class=MsoTitle>Forward Lock Converter And Decoder</p> + +<p class=MsoToc1><span +class=MsoHyperlink><a href="#_Toc276471422">1<span style='font-size:12.0pt; +line-height:150%;color:windowtext;text-transform:none;font-weight:normal; +text-decoration:none'> </span>Introduction<span style='color:windowtext; +display:none;text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p> + +<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471423">2<span +style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none; +font-weight:normal;text-decoration:none'> </span>Overview<span +style='color:windowtext;display:none;text-decoration:none'>... </span><span +style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p> + +<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471424">3<span +style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none; +font-weight:normal;text-decoration:none'> </span>Use Cases<span +style='color:windowtext;display:none;text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p> + +<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important; +text-transform:uppercase'><a href="#_Toc276471425">3.1<span style='font-size: +12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration: +none'> </span>Converter<span style='color:windowtext;display:none; +text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></span></p> + +<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471426">3.1.1<span +style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal; +text-decoration:none'> </span>Convert Data (Push-Mode Conversion)<span +style='color:windowtext;display:none;text-decoration:none'> </span><span +style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p> + +<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471427">3.1.2<span +style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal; +text-decoration:none'> </span>Convert File (Pull-Mode Conversion)<span +style='color:windowtext;display:none;text-decoration:none'> </span><span +style='color:windowtext;display:none;text-decoration:none'>6</span></a></span></p> + +<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important; +text-transform:uppercase'><a href="#_Toc276471428">3.2<span style='font-size: +12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration: +none'> </span>Decoder<span style='color:windowtext;display:none; +text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>7</span></a></span></span></p> + +<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471429">3.2.1<span +style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal; +text-decoration:none'> </span>Check Integrity<span style='color:windowtext; +display:none;text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>8</span></a></span></p> + +<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471430">3.2.2<span +style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal; +text-decoration:none'> </span>Get Content Type<span style='color:windowtext; +display:none;text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>9</span></a></span></p> + +<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471431">3.2.3<span +style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal; +text-decoration:none'> </span>Decode File<span style='color:windowtext; +display:none;text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>10</span></a></span></p> + +<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471432">4<span +style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none; +font-weight:normal;text-decoration:none'> </span>Definition of the +Internal Forward Lock File Format<span style='color:windowtext;display:none; +text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>11</span></a></span></p> + +<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important; +text-transform:uppercase'><a href="#_Toc276471433">4.1<span style='font-size: +12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration: +none'> </span>Key Derivation<span style='color:windowtext;display:none; +text-decoration:none'>.. </span><span +style='color:windowtext;display:none;text-decoration:none'>11</span></a></span></span></p> + +<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important; +text-transform:uppercase'><a href="#_Toc276471434">4.2<span style='font-size: +12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration: +none'> </span>Calculation of the Counters<span style='color:windowtext; +display:none;text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></span></p> + +<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471435">5<span +style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none; +font-weight:normal;text-decoration:none'> </span>Unit Test Cases<span +style='color:windowtext;display:none;text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></p> + +<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471436">6<span +style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none; +font-weight:normal;text-decoration:none'> </span>References<span +style='color:windowtext;display:none;text-decoration:none'>. </span><span +style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></p> + +<p class=MsoBodyText></p> + +</div> + +<span style='font-size:12.0pt;font-family:"Times New Roman","serif"'><br +clear=all style='page-break-before:right'> +</span> + +<div class=WordSection2> + +<h1><a name="_Toc276471422"></a><a name="_Ref263085474">1<span +style='font:7.0pt "Times New Roman"'> </span>Introduction</a></h1> + +<p class=MsoBodyText>The internal Forward Lock file format is used for encrypting +inherently unencrypted OMA DRM version 1 Forward Lock and Combined Delivery +files so they can be securely stored on externally accessible file system partitions +such as memory stick.</p> + +<p class=MsoBodyText>Our general strategy is to convert such <i>OMA DRM Message</i> +(‘.dm’) files to internal Forward Lock (‘.fl’) files as soon as they are +downloaded or otherwise transferred to the phone, and not actually provide any +decoders for ‘.dm’ files.</p> + +<h1><a name="_Toc276471423">2<span style='font:7.0pt "Times New Roman"'> +</span>Overview</a></h1> + +<p class=MsoBodyText>The <i>Forward Lock Converter</i> converts OMA DRM Message +files to the internal file format. The <i>Forward Lock Decoder</i> provides a +POSIX-level API for transparent reading and seeking through such a converted +file as if it were unencrypted. The API also includes functions for checking a +file’s integrity and getting the MIME type of its embedded content.</p> + +<p class=MsoBodyText style='margin-bottom:24.0pt'>The converter and decoder are +built into two separate libraries, which share common code for random number +generation and key encryption in a third library. For test purposes there is +also a unit test application. See Figure 1.</p> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=288 height=364 +src="images/image001.gif"></p> + +<p class=MsoCaption style='margin-top:12.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref262730885">Figure </a>1. Block diagram illustrating the dependencies between the executable modules.</p> + +<b><span style='font-size:16.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h1><a name="_Toc276471424">3<span style='font:7.0pt "Times New Roman"'> +</span>Use Cases</a></h1> + +<p class=MsoBodyText>This section describes all the use cases for the converter +and decoder. It shows the sequence of API calls that should be used to solve +these use cases.</p> + +<h2><a name="_Toc276471425">3.1<span style='font:7.0pt "Times New Roman"'> +</span>Converter</a></h2> + +<p class=MsoBodyText>Through the converter API, conversion can be performed in one +of two ways:</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span><i>Push-mode +conversion</i> is when the client progressively feeds data to the converter as +it arrives. This is appropriate when data arrives gradually in chunks, with +idle time in between. Consequently, push mode is used for converting files +being downloaded through HTTP. See section 3.1.1.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span><i>Pull-mode +conversion</i> is when the converter drives the process and consumes data from +the client as it needs it. This is appropriate when the entire file to be +converted is readily available. Hence, pull mode is used by the unit test application. +See section 3.1.2.</p> + +<p class=MsoBodyText>Internally, pull-mode conversion is implemented in terms +of the API for push-mode conversion.</p> + +<h3><a name="_Toc276471426"></a><a name="_Ref263085478">3.1.1<span +style='font:7.0pt "Times New Roman"'> </span>Convert Data +(Push-Mode Conversion)</a></h3> + +<p class=MsoBodyText>Push-mode conversion is performed as follows (see also Figure 2):</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_OpenSession</span> +initializes the output parameter and returns a <i>session ID</i> to be used in +subsequent calls to the API. The output parameter is a union of return values +whose correct use at any given moment is determined by the API function last +called.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_ConvertData</span> +is called repeatedly until no more input data remains. Each call converts the +maximum amount of data possible and writes it to the output buffer. The client then +writes this data to file.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_CloseSession</span> +cleans up the session and deallocates the output buffer. If all has gone well, a +two-part cryptographic signature of the output file is calculated. The client +must go back and rewrite part of the file header with this updated signature +information.</p> + +<p class=MsoBodyText>Every time a file is being converted, the converter calls <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetRandomNumber</span> +to generate a new, unique session key. No two converted files look alike, even +if the original files are the same.</p> + +<p class=MsoBodyText><b>Note:</b> The random bytes cannot come from any bare-minimum +implementation of the C-library <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>rand</span> +function—they must be cryptographically secure. Otherwise, security will be +compromised.</p> + +<p class=MsoBodyText>The session key is encrypted and stored within the +converted file. Key encryption is performed using <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetEncryptedKeyLength</span> and <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_EncryptKey</span>. +These two functions, together with the corresponding decryption function (<span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_DecryptKey</span>), +are the integration points where an OEM manufacturer may implement their own +key-encryption scheme.</p> + +<p class=MsoBodyText><b>Note:</b> The key-encryption key must be unique to each +device; this is what makes the files forward lock–protected. Ideally, it should +be derived from secret hardware parameters, but at the very least it should be +persistent from one master reset to the next.</p> + +<div style='margin-bottom:24.0pt;border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt; +background:#F2F2F2'> + +<p class=MsoBodyText style='background:#F2F2F2;border: +none;padding:0cm'><b>Note:</b> In the open-source implementation of the <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>libfwdlock-common</span> +library, a random key-encryption key is generated and stored in plaintext in +the file system, without being obfuscated in any way (doing so would be futile +since the source code is openly available). This key must be kept secret from +the user, and shouldn’t be possible to extract through backup-and-restore +functionality or the like. OEM manufacturers will probably want to implement a +truly hardware-based device-unique key.</p> + +</div> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=531 height=563 +src="images/image002.gif"></p> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref263085187">Figure </a>2. Converter UC: Convert Data.</p> + +<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h3><a name="_Toc276471427"></a><a name="_Ref263163082">3.1.2<span +style='font:7.0pt "Times New Roman"'> </span>Convert File +(Pull-Mode Conversion)</a></h3> + +<p class=MsoBodyText>Pull-mode conversion is performed by calling <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertFile</span> +with the filename, unless there is need for a specialized <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>read</span> function, in +which case <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertOpenFile</span> +should be used directly instead. See Figure 3.</p> + +<p class=MsoBodyText style='margin-bottom:24.0pt'>Internally, <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertFile</span> +calls <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertOpenFile</span>. +The latter then proceeds with the conversion using the push-mode API, acting as +the client in the previous use case; see section 3.1.1.</p> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=531 height=731 +src="images/image003.gif"></p> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref263085208">Figure </a>3. Converter UC: Convert File.</p> + +<b><i><span style='font-size:14.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></i></b> + +<h2><a name="_Toc276471428">3.2<span style='font:7.0pt "Times New Roman"'> +</span>Decoder</a></h2> + +<p class=MsoBodyText>The decoder API allows the client to do the following:</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span>Check +the integrity of an internal Forward Lock file, i.e., detect whether it has +been manipulated in any way; see section 3.2.1.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span>Get +the MIME type of the embedded content (the “original” MIME type before DRM protection +was applied); see section 3.2.2.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span +style='font:7.0pt "Times New Roman"'> </span>Decode +the file by random access, i.e., read and seek through it in an arbitrary +manner; see section 3.2.3.</p> + +<p class=MsoBodyText>All subsequent operations on a file first require it to be +opened. Opening a file returns a <i>file descriptor</i>—a handle to be used in +these subsequent operations.</p> + +<p class=MsoBodyText>If the filename is known, an internal Forward Lock file +can be opened using <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>. +If only the file descriptor of an already open file is available, a decoding +session can instead be initialized using <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>.</p> + +<p class=MsoBodyText>Internally, <span style='font-size:10.0pt;font-family: +"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> calls <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>. For efficiency +reasons, <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span> +therefore assumes that the file position is at the beginning of the file when +the function gets called. A client who calls it directly must make sure that +this assumption holds.</p> + +<p class=MsoBodyText>When a file is being attached, the session key stored in +the file during conversion is decrypted using <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetEncryptedKeyLength</span> and <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_DecryptKey</span>, +in order to set up for decoding and integrity checking.</p> + +<p class=MsoBodyText>For just getting the content type, however, retrieving the +session key would strictly speaking not be necessary, so there is an +opportunity here to optimize for that if it proves necessary later.</p> + +<p class=MsoBodyText>Symmetrical to <span style='font-size:10.0pt;font-family: +"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> and <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>, there are also functions +for closing a file or detaching from it:</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span>If +it was opened with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> +it should be closed with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_close</span>.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span>If +it was attached with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span> +it should be detached with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_detach</span>.</p> + +<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h3><a name="_Ref263163099"></a><a name="_Toc276471429">3.2.1<span +style='font:7.0pt "Times New Roman"'> </span>Check Integrity</a></h3> + +<p class=MsoBodyText>There are three methods for checking the integrity of an +internal Forward Lock file, in whole or in part (see also Figure 4):</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>, +which checks the integrity of the encrypted content data.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span>, +which checks the integrity of the file header, including the content type and +other fields not currently supported but reserved for future use.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckIntegrity</span>, +which internally calls first <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span> +and then <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>.</p> + +<p class=MsoBodyText style='margin-bottom:24.0pt'><span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span> is +generally much faster than <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>, +whose running time is directly proportional to the size of the file.</p> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=575 +src="images/image004.gif"></p> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref263163308">Figure </a>4. Decoder UC: Check Integrity.</p> + +<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h3><a name="_Toc276471430"></a><a name="_Ref263163117">3.2.2<span +style='font:7.0pt "Times New Roman"'> </span>Get Content Type</a></h3> + +<p class=MsoBodyText style='margin-bottom:24.0pt'><span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_GetContentType</span> returns a +read-only reference to an ASCII string containing the MIME type of the +embedded content. This reference is valid as long as the file is kept open. +Clients who need access to the content type after closing the file should make +a copy of the string. See Figure 5 below.</p> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=488 +src="images/image005.gif"></p> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref263163392">Figure </a>5. Decoder UC: Get Content Type.</p> + +<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h3><a name="_Toc276471431"></a><a name="_Ref263163137">3.2.3<span +style='font:7.0pt "Times New Roman"'> </span>Decode File</a></h3> + +<p class=MsoBodyText>After opening an internal Forward Lock file (or attaching +to an already open one), it can be transparently read from as if it were +unencrypted. Any number of calls to read data from the current file position or +set it to a new one (which is what <span style='font-size:10.0pt;font-family: +"Lucida Console","DejaVu Sans Mono"'>lseek</span> does) can be made in any order; this is what we +call <i>random access</i>. See Figure 6.</p> + +<p class=MsoBodyText>The Forward Lock Decoder versions of the <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>read</span>, <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>lseek</span>, and <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>close</span> functions +have the exact same signatures as their POSIX counterparts. So, for example, +the call <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_lseek(fd, +0, SEEK_END)</span> returns the size of the embedded content data, i.e., the +size of the original file before DRM protection.</p> + +<p class=MsoBodyText style='margin-bottom:24.0pt'>Moreover, <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> +is like regular POSIX <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>open</span> +except it takes only the filename as a parameter—access is always read-only.</p> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=522 +src="images/image006.gif"></p> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref263166303">Figure </a>6. Decoder UC: Decode File.</p> + +<b><span style='font-size:16.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h1><a name="_Toc276471432">4<span style='font:7.0pt "Times New Roman"'> +</span>Definition of the Internal Forward Lock File Format</a></h1> + +<p class=MsoBodyText style='margin-bottom:12.0pt'>The inner structure of an internal +Forward Lock file is defined in Table 1 below.</p> + +<table class=MsoNormalTable border=1 cellspacing=0 cellpadding=0 + style='border-collapse:collapse;border:none'> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><b>Offset [bytes]</b></p> + </td> + <td width=96 valign=top style='width:72.0pt;border:solid windowtext 1.0pt; + border-left:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><b>Size [bytes]</b></p> + </td> + <td width=361 valign=top style='width:270.85pt;border:solid windowtext 1.0pt; + border-left:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><b>Description</b></p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>0</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>4</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>The file signature (so-called + <i>magic number</i>): a four-character code consisting of the letters + F-W-L-K.</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>4</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>1</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Version number (0 for the + first version).</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>5</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>1</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Indicates the subformat:</p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x00 Forward Lock</i></p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x01 Combined Delivery</i></p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>6</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>1</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Usage restriction flags (prohibitions + against usage as ringtone or as wallpaper and screen saver). Also indicates + if the file is bound to a specific SIM card.</p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x00 No usage + restrictions</i></p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x01 Ringtone usage + prohibited</i></p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x02 Screen usage + prohibited</i></p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x80 Bound to SIM</i></p> + <p class=MsoNormal style='page-break-after:avoid'>(Any number of these may be + OR-ed together.)</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>7</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>1</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Length of the MIME content + type (<i>k</i>).</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>8</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><i>k</i></p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>The MIME content type + (ASCII-encoded without null-character termination).</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><i>l </i>= 0 or 16</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>If the subformat is + Combined Delivery, this field contains the auto-generated content ID (16 bytes). + If not, this field is zero-size.</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><i>m </i>= 0 or 9</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>If the file is bound to a + specific SIM card, this field contains the 9-byte packed IMSI number. If not, + this field is zero-size.</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i>+<i>m</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><i>n</i> ≥ 16</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>The encrypted session key, the + first sixteen bytes of which are also used as the CTR-mode <i>nonce</i> (similar + to the CBC-mode <i>initialization vector</i>).</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>20</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Data signature—the SHA-1 + HMAC of the encrypted content data.</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>28+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>20</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Header signature—the SHA-1 + HMAC of all the fields above, including the encrypted session key and data + signature.</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>48+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><i><to the end of the + file></i></p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>The content data encrypted + using 128-bit AES in CTR mode.</p> + </td> + </tr> +</table> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm;page-break-after:avoid'><a name="_Ref151269206">Table </a>1. Definition of the fields of an internal Forward Lock file.</p> + +<p class=MsoBodyText>As of now, neither Combined Delivery nor usage +restrictions (including SIM binding) are supported. These fields are reserved +for future use.</p> + +<h2><a name="_Toc276471433">4.1<span style='font:7.0pt "Times New Roman"'> +</span>Key Derivation</a></h2> + +<p class=MsoBodyText>The session key consists of sixteen bytes fetched from a +cryptographically secure random number generator. From the session key, two +separate keys are derived: one used for encryption, the other for signing.</p> + +<p class=MsoBodyText>The encryption key is the output from encrypting the +16-byte all-zero input block {0, 0, …, 0} using 128-bit AES with the random session +key as the key. The signing key is the output from encrypting the 16-byte input +block {1, 0, …, 0} the same way. The keys so derived will be cryptographically +independent from each other.</p> + +<p class=MsoBodyText>The session key is encrypted using a hardware-dependent +key-encryption key unique to each device. The encrypted session key is stored +inside the file, and its first sixteen bytes are also used as the <i>nonce</i> +for the CTR-mode encryption of the content data.</p> + +<h2><a name="_Toc276471434">4.2<span style='font:7.0pt "Times New Roman"'> +</span>Calculation of the Counters</a></h2> + +<p class=MsoBodyText>Using CTR (“counter”) mode, a block cipher such as AES can +be turned into a stream cipher. The process of encryption and decryption is +well defined in [1], except for the specifics of the calculation of the +counters. For the internal Forward Lock file format, the counters are +calculated as follows:</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span>The +nonce is interpreted as a 128-bit unsigned integer in little-endian format.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span>The +zero-based block sequence number (also a little-endian unsigned integer) is +added modulo 2<sup>128</sup> to the nonce to produce the counter for a given +block.</p> + +<h1><a name="_Toc276471435">5<span style='font:7.0pt "Times New Roman"'> +</span>Unit Test Cases</a></h1> + +<p class=MsoBodyText>Unit test cases for the converter and decoder come in two +varieties:</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span><i>Black-box</i> +test cases aim to verify that you get sensible results from malformed or +“tricky” input data.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span><i>White-box</i> +test cases aim to maximize code coverage using knowledge of code internals.</p> + +<p class=MsoBodyText>The black-box test cases are dependent on a specifically +designed set of input files found in the <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>forward-lock/internal-format/test/res</span> +directory in the repository. For ‘tests’ variants of the software, these input +files will be automatically installed in the file system image during build.</p> + +<p class=MsoBodyText>Run the test cases from the ADB shell command line as +follows:</p> + +<p class=MsoNormal style='margin-top:0cm;margin-right:0cm;margin-bottom:6.0pt; +margin-left:21.55pt'><span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'># +gtest_fwdlock</span></p> + +<p class=MsoBodyText>If all black-box but no white-box test cases fail, the +input files probably can’t be found in the working directory.</p> + +<h1><a name="_Toc276471436">6<span style='font:7.0pt "Times New Roman"'> +</span>References</a></h1> + +<p class=MsoBodyText style='margin-left:28.9pt;text-indent:-28.9pt'>[1]<span +style='font:7.0pt "Times New Roman"'> +</span><a +href="http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf">Dworkin, +Morris: “Recommendation for Block Cipher Modes of Operation—Methods and +Techniques,” NIST Special Publication 800-38A, December 2001.</a><a +name="_Ref151269073"></a></p> + +</div> + +</body> + +</html> diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif Binary files differnew file mode 100644 index 0000000..ee94513 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif Binary files differnew file mode 100644 index 0000000..8c12f46 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif Binary files differnew file mode 100644 index 0000000..9e019ca --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif Binary files differnew file mode 100644 index 0000000..cae1d01 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif Binary files differnew file mode 100644 index 0000000..0d87be9 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif Binary files differnew file mode 100644 index 0000000..9445b6b --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif diff --git a/drm/libdrmframework/plugins/passthru/Android.mk b/drm/libdrmframework/plugins/passthru/Android.mk new file mode 100644 index 0000000..7856d37 --- /dev/null +++ b/drm/libdrmframework/plugins/passthru/Android.mk @@ -0,0 +1,48 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + src/DrmPassthruPlugIn.cpp + +LOCAL_MODULE := libdrmpassthruplugin + +LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon + +LOCAL_SHARED_LIBRARIES := \ + libutils + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_PRELINK_MODULE := false + +LOCAL_C_INCLUDES += \ + $(TOP)/frameworks/base/drm/libdrmframework/include \ + $(TOP)/frameworks/base/drm/libdrmframework/plugins/passthru/include \ + $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include \ + $(TOP)/frameworks/base/include + +# Set the following flag to enable the decryption passthru flow +#LOCAL_CFLAGS += -DENABLE_PASSTHRU_DECRYPTION + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h new file mode 100644 index 0000000..bbcd9ed --- /dev/null +++ b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DRM_PASSTHRU_PLUGIN_H__ +#define __DRM_PASSTHRU_PLUGIN_H__ + +#include <DrmEngineBase.h> + +namespace android { + +class DrmPassthruPlugIn : public DrmEngineBase { + +public: + DrmPassthruPlugIn(); + virtual ~DrmPassthruPlugIn(); + +protected: + DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action); + + DrmMetadata* onGetMetadata(int uniqueId, const String8* path); + + status_t onInitialize(int uniqueId); + + status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener); + + status_t onTerminate(int uniqueId); + + bool onCanHandle(int uniqueId, const String8& path); + + DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo); + + status_t onSaveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath); + + DrmInfo* onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest); + + String8 onGetOriginalMimeType(int uniqueId, const String8& path); + + int onGetDrmObjectType(int uniqueId, const String8& path, const String8& mimeType); + + int onCheckRightsStatus(int uniqueId, const String8& path, int action); + + status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + status_t onSetPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + bool onValidateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + + status_t onRemoveRights(int uniqueId, const String8& path); + + status_t onRemoveAllRights(int uniqueId); + + status_t onOpenConvertSession(int uniqueId, int convertId); + + DrmConvertedStatus* onConvertData(int uniqueId, int convertId, const DrmBuffer* inputData); + + DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId); + + DrmSupportInfo* onGetSupportInfo(int uniqueId); + + status_t onOpenDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length); + + status_t onOpenDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, const char* uri); + + status_t onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + status_t onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV); + + status_t onFinalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId); + + ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset); + +private: + DecryptHandle* openDecryptSessionImpl(); +}; + +}; + +#endif /* __DRM_PASSTHRU_PLUGIN_H__ */ + diff --git a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp new file mode 100644 index 0000000..dee1fdb --- /dev/null +++ b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "DrmPassthruPlugIn" +#include <utils/Log.h> + +#include <drm/DrmRights.h> +#include <drm/DrmConstraints.h> +#include <drm/DrmMetadata.h> +#include <drm/DrmInfo.h> +#include <drm/DrmInfoEvent.h> +#include <drm/DrmInfoStatus.h> +#include <drm/DrmConvertedStatus.h> +#include <drm/DrmInfoRequest.h> +#include <drm/DrmSupportInfo.h> +#include <DrmPassthruPlugIn.h> + +using namespace android; + + +// This extern "C" is mandatory to be managed by TPlugInManager +extern "C" IDrmEngine* create() { + return new DrmPassthruPlugIn(); +} + +// This extern "C" is mandatory to be managed by TPlugInManager +extern "C" void destroy(IDrmEngine* pPlugIn) { + delete pPlugIn; + pPlugIn = NULL; +} + +DrmPassthruPlugIn::DrmPassthruPlugIn() + : DrmEngineBase() { + +} + +DrmPassthruPlugIn::~DrmPassthruPlugIn() { + +} + +DrmMetadata* DrmPassthruPlugIn::onGetMetadata(int uniqueId, const String8* path) { + return NULL; +} + +DrmConstraints* DrmPassthruPlugIn::onGetConstraints( + int uniqueId, const String8* path, int action) { + LOGD("DrmPassthruPlugIn::onGetConstraints From Path: %d", uniqueId); + DrmConstraints* drmConstraints = new DrmConstraints(); + + String8 value("dummy_available_time"); + char* charValue = NULL; + charValue = new char[value.length() + 1]; + strncpy(charValue, value.string(), value.length()); + + //Just add dummy available time for verification + drmConstraints->put(&(DrmConstraints::LICENSE_AVAILABLE_TIME), charValue); + + return drmConstraints; +} + +DrmInfoStatus* DrmPassthruPlugIn::onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) { + LOGD("DrmPassthruPlugIn::onProcessDrmInfo - Enter : %d", uniqueId); + DrmInfoStatus* drmInfoStatus = NULL; + if (NULL != drmInfo) { + switch (drmInfo->getInfoType()) { + case DrmInfoRequest::TYPE_REGISTRATION_INFO: { + const DrmBuffer* emptyBuffer = new DrmBuffer(); + drmInfoStatus = new DrmInfoStatus(DrmInfoStatus::STATUS_OK, + DrmInfoRequest::TYPE_REGISTRATION_INFO, emptyBuffer, drmInfo->getMimeType()); + break; + } + case DrmInfoRequest::TYPE_UNREGISTRATION_INFO: { + const DrmBuffer* emptyBuffer = new DrmBuffer(); + drmInfoStatus = new DrmInfoStatus(DrmInfoStatus::STATUS_OK, + DrmInfoRequest::TYPE_UNREGISTRATION_INFO, emptyBuffer, drmInfo->getMimeType()); + break; + } + case DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO: { + String8 licenseString("dummy_license_string"); + const int bufferSize = licenseString.size(); + char* data = NULL; + data = new char[bufferSize]; + memcpy(data, licenseString.string(), bufferSize); + const DrmBuffer* buffer = new DrmBuffer(data, bufferSize); + drmInfoStatus = new DrmInfoStatus(DrmInfoStatus::STATUS_OK, + DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO, buffer, drmInfo->getMimeType()); + break; + } + } + } + LOGD("DrmPassthruPlugIn::onProcessDrmInfo - Exit"); + return drmInfoStatus; +} + +status_t DrmPassthruPlugIn::onSetOnInfoListener( + int uniqueId, const IDrmEngine::OnInfoListener* infoListener) { + LOGD("DrmPassthruPlugIn::onSetOnInfoListener : %d", uniqueId); + return DRM_NO_ERROR; +} + +status_t DrmPassthruPlugIn::onInitialize(int uniqueId) { + LOGD("DrmPassthruPlugIn::onInitialize : %d", uniqueId); + return DRM_NO_ERROR; +} + +status_t DrmPassthruPlugIn::onTerminate(int uniqueId) { + LOGD("DrmPassthruPlugIn::onTerminate : %d", uniqueId); + return DRM_NO_ERROR; +} + +DrmSupportInfo* DrmPassthruPlugIn::onGetSupportInfo(int uniqueId) { + LOGD("DrmPassthruPlugIn::onGetSupportInfo : %d", uniqueId); + DrmSupportInfo* drmSupportInfo = new DrmSupportInfo(); + // Add mimetype's + drmSupportInfo->addMimeType(String8("application/vnd.passthru.drm")); + // Add File Suffixes + drmSupportInfo->addFileSuffix(String8(".passthru")); + // Add plug-in description + drmSupportInfo->setDescription(String8("Passthru plug-in")); + return drmSupportInfo; +} + +status_t DrmPassthruPlugIn::onSaveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + LOGD("DrmPassthruPlugIn::onSaveRights : %d", uniqueId); + return DRM_NO_ERROR; +} + +DrmInfo* DrmPassthruPlugIn::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) { + LOGD("DrmPassthruPlugIn::onAcquireDrmInfo : %d", uniqueId); + DrmInfo* drmInfo = NULL; + + if (NULL != drmInfoRequest) { + String8 dataString("dummy_acquistion_string"); + int length = dataString.length(); + char* data = NULL; + data = new char[length]; + memcpy(data, dataString.string(), length); + drmInfo = new DrmInfo(drmInfoRequest->getInfoType(), + DrmBuffer(data, length), drmInfoRequest->getMimeType()); + } + return drmInfo; +} + +bool DrmPassthruPlugIn::onCanHandle(int uniqueId, const String8& path) { + LOGD("DrmPassthruPlugIn::canHandle: %s ", path.string()); + String8 extension = path.getPathExtension(); + extension.toLower(); + return (String8(".passthru") == extension); +} + +String8 DrmPassthruPlugIn::onGetOriginalMimeType(int uniqueId, const String8& path) { + LOGD("DrmPassthruPlugIn::onGetOriginalMimeType() : %d", uniqueId); + return String8("video/passthru"); +} + +int DrmPassthruPlugIn::onGetDrmObjectType( + int uniqueId, const String8& path, const String8& mimeType) { + LOGD("DrmPassthruPlugIn::onGetDrmObjectType() : %d", uniqueId); + return DrmObjectType::UNKNOWN; +} + +int DrmPassthruPlugIn::onCheckRightsStatus(int uniqueId, const String8& path, int action) { + LOGD("DrmPassthruPlugIn::onCheckRightsStatus() : %d", uniqueId); + int rightsStatus = RightsStatus::RIGHTS_VALID; + return rightsStatus; +} + +status_t DrmPassthruPlugIn::onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, + int action, bool reserve) { + LOGD("DrmPassthruPlugIn::onConsumeRights() : %d", uniqueId); + return DRM_NO_ERROR; +} + +status_t DrmPassthruPlugIn::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle, + int playbackStatus, int position) { + LOGD("DrmPassthruPlugIn::onSetPlaybackStatus() : %d", uniqueId); + return DRM_NO_ERROR; +} + +bool DrmPassthruPlugIn::onValidateAction(int uniqueId, const String8& path, + int action, const ActionDescription& description) { + LOGD("DrmPassthruPlugIn::onValidateAction() : %d", uniqueId); + return true; +} + +status_t DrmPassthruPlugIn::onRemoveRights(int uniqueId, const String8& path) { + LOGD("DrmPassthruPlugIn::onRemoveRights() : %d", uniqueId); + return DRM_NO_ERROR; +} + +status_t DrmPassthruPlugIn::onRemoveAllRights(int uniqueId) { + LOGD("DrmPassthruPlugIn::onRemoveAllRights() : %d", uniqueId); + return DRM_NO_ERROR; +} + +status_t DrmPassthruPlugIn::onOpenConvertSession(int uniqueId, int convertId) { + LOGD("DrmPassthruPlugIn::onOpenConvertSession() : %d", uniqueId); + return DRM_NO_ERROR; +} + +DrmConvertedStatus* DrmPassthruPlugIn::onConvertData( + int uniqueId, int convertId, const DrmBuffer* inputData) { + LOGD("DrmPassthruPlugIn::onConvertData() : %d", uniqueId); + DrmBuffer* convertedData = NULL; + + if (NULL != inputData && 0 < inputData->length) { + int length = inputData->length; + char* data = NULL; + data = new char[length]; + convertedData = new DrmBuffer(data, length); + memcpy(convertedData->data, inputData->data, length); + } + return new DrmConvertedStatus(DrmConvertedStatus::STATUS_OK, convertedData, 0 /*offset*/); +} + +DrmConvertedStatus* DrmPassthruPlugIn::onCloseConvertSession(int uniqueId, int convertId) { + LOGD("DrmPassthruPlugIn::onCloseConvertSession() : %d", uniqueId); + return new DrmConvertedStatus(DrmConvertedStatus::STATUS_OK, NULL, 0 /*offset*/); +} + +status_t DrmPassthruPlugIn::onOpenDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) { + LOGD("DrmPassthruPlugIn::onOpenDecryptSession() : %d", uniqueId); + +#ifdef ENABLE_PASSTHRU_DECRYPTION + decryptHandle->mimeType = String8("video/passthru"); + decryptHandle->decryptApiType = DecryptApiType::ELEMENTARY_STREAM_BASED; + decryptHandle->status = DRM_NO_ERROR; + decryptHandle->decryptInfo = NULL; + return DRM_NO_ERROR; +#endif + + return DRM_ERROR_CANNOT_HANDLE; +} + +status_t DrmPassthruPlugIn::onOpenDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, const char* uri) { + return DRM_ERROR_CANNOT_HANDLE; +} + +status_t DrmPassthruPlugIn::onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + LOGD("DrmPassthruPlugIn::onCloseDecryptSession() : %d", uniqueId); + if (NULL != decryptHandle) { + if (NULL != decryptHandle->decryptInfo) { + delete decryptHandle->decryptInfo; decryptHandle->decryptInfo = NULL; + } + delete decryptHandle; decryptHandle = NULL; + } + return DRM_NO_ERROR; +} + +status_t DrmPassthruPlugIn::onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) { + LOGD("DrmPassthruPlugIn::onInitializeDecryptUnit() : %d", uniqueId); + return DRM_NO_ERROR; +} + +status_t DrmPassthruPlugIn::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) { + LOGD("DrmPassthruPlugIn::onDecrypt() : %d", uniqueId); + /** + * As a workaround implementation passthru would copy the given + * encrypted buffer as it is to decrypted buffer. Note, decBuffer + * memory has to be allocated by the caller. + */ + if (NULL != (*decBuffer) && 0 < (*decBuffer)->length) { + memcpy((*decBuffer)->data, encBuffer->data, encBuffer->length); + (*decBuffer)->length = encBuffer->length; + } + return DRM_NO_ERROR; +} + +status_t DrmPassthruPlugIn::onFinalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + LOGD("DrmPassthruPlugIn::onFinalizeDecryptUnit() : %d", uniqueId); + return DRM_NO_ERROR; +} + +ssize_t DrmPassthruPlugIn::onPread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off_t offset) { + LOGD("DrmPassthruPlugIn::onPread() : %d", uniqueId); + return 0; +} + |
