Add default implementation for CAS AIDL

Bug: 230377377, 227673974
Test: manual
Change-Id: I85015dd6e1a69ff9b57f832f5b1cd01fb65dda25
This commit is contained in:
Shraddha Basantwani 2022-09-21 16:26:19 +05:30
parent fdeb39f878
commit 6545b4e343
18 changed files with 1413 additions and 0 deletions

93
cas/aidl/default/Android.bp Executable file
View file

@ -0,0 +1,93 @@
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_library_static {
name: "libcasexampleimpl",
vendor_available: true,
srcs: [
"CasImpl.cpp",
"DescramblerImpl.cpp",
"MediaCasService.cpp",
"SharedLibrary.cpp",
"TypeConvert.cpp",
],
shared_libs: [
"android.hardware.cas-V1-ndk",
"libbinder_ndk",
"liblog",
"libutils",
"libcutils",
],
static_libs: [
"libaidlcommonsupport",
],
header_libs: [
"libstagefright_foundation_headers",
"media_plugin_headers",
],
}
cc_defaults {
name: "cas_service_example_defaults",
vendor: true,
relative_install_path: "hw",
srcs: ["service.cpp"],
static_libs: [
"libaidlcommonsupport",
"libcasexampleimpl",
],
shared_libs: [
"android.hardware.cas-V1-ndk",
"libbinder_ndk",
"liblog",
"libutils",
"libcutils",
],
header_libs: ["media_plugin_headers"],
vintf_fragments: ["android.hardware.cas-service.xml"],
}
cc_binary {
name: "android.hardware.cas-service.example",
defaults: ["cas_service_example_defaults"],
init_rc: ["cas-default.rc"],
}
cc_binary {
name: "android.hardware.cas-service.example-lazy",
defaults: ["cas_service_example_defaults"],
init_rc: ["cas-default-lazy.rc"],
cflags: ["-DLAZY_SERVICE"],
}
cc_fuzz {
name: "android.hardware.cas-service_fuzzer",
vendor: true,
defaults: ["service_fuzzer_defaults"],
srcs: ["fuzzer.cpp"],
shared_libs: [
"android.hardware.cas-V1-ndk",
"libcutils",
"liblog",
],
static_libs: [
"libaidlcommonsupport",
"libcasexampleimpl",
],
header_libs: ["media_plugin_headers"],
fuzz_config: {
componentid: 1344,
},
}

242
cas/aidl/default/CasImpl.cpp Executable file
View file

@ -0,0 +1,242 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
icensed 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_TAG "android.hardware.cas-CasImpl"
#include <media/cas/CasAPI.h>
#include <utils/Log.h>
#include "CasImpl.h"
#include "TypeConvert.h"
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
CasImpl::CasImpl(const shared_ptr<ICasListener>& listener) : mListener(listener) {
ALOGV("CTOR");
}
CasImpl::~CasImpl() {
ALOGV("DTOR");
release();
}
// static
void CasImpl::OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size) {
if (appData == NULL) {
ALOGE("Invalid appData!");
return;
}
CasImpl* casImpl = static_cast<CasImpl*>(appData);
casImpl->onEvent(event, arg, data, size);
}
// static
void CasImpl::CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size,
const CasSessionId* sessionId) {
if (appData == NULL) {
ALOGE("Invalid appData!");
return;
}
CasImpl* casImpl = static_cast<CasImpl*>(appData);
casImpl->onEvent(sessionId, event, arg, data, size);
}
// static
void CasImpl::StatusUpdate(void* appData, int32_t event, int32_t arg) {
if (appData == NULL) {
ALOGE("Invalid appData!");
return;
}
CasImpl* casImpl = static_cast<CasImpl*>(appData);
casImpl->onStatusUpdate(event, arg);
}
void CasImpl::init(CasPlugin* plugin) {
shared_ptr<CasPlugin> holder(plugin);
atomic_store(&mPluginHolder, holder);
}
void CasImpl::onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size) {
if (mListener == NULL) {
return;
}
vector<uint8_t> eventData;
if (data != NULL) {
eventData.assign(data, data + size);
}
mListener->onEvent(event, arg, eventData);
}
void CasImpl::onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data,
size_t size) {
if (mListener == NULL) {
return;
}
vector<uint8_t> eventData;
if (data != NULL) {
eventData.assign(data, data + size);
}
if (sessionId != NULL) {
mListener->onSessionEvent(*sessionId, event, arg, eventData);
} else {
mListener->onEvent(event, arg, eventData);
}
}
void CasImpl::onStatusUpdate(int32_t event, int32_t arg) {
if (mListener == NULL) {
return;
}
mListener->onStatusUpdate(static_cast<StatusEvent>(event), arg);
}
ScopedAStatus CasImpl::setPluginStatusUpdateCallback() {
ALOGV("%s", __FUNCTION__);
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
return toStatus(holder->setStatusCallback(&CasImpl::StatusUpdate));
}
ScopedAStatus CasImpl::setPrivateData(const vector<uint8_t>& pvtData) {
ALOGV("%s", __FUNCTION__);
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
return toStatus(holder->setPrivateData(pvtData));
}
ScopedAStatus CasImpl::openSession(SessionIntent intent, ScramblingMode mode,
vector<uint8_t>* sessionId) {
ALOGV("%s", __FUNCTION__);
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
status_t err = INVALID_OPERATION;
if (holder.get() != nullptr) {
err = holder->openSession(static_cast<uint32_t>(intent), static_cast<uint32_t>(mode),
sessionId);
holder.reset();
}
return toStatus(err);
}
ScopedAStatus CasImpl::setSessionPrivateData(const vector<uint8_t>& sessionId,
const vector<uint8_t>& pvtData) {
ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
return toStatus(holder->setSessionPrivateData(sessionId, pvtData));
}
ScopedAStatus CasImpl::closeSession(const vector<uint8_t>& sessionId) {
ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
return toStatus(holder->closeSession(sessionId));
}
ScopedAStatus CasImpl::processEcm(const vector<uint8_t>& sessionId, const vector<uint8_t>& ecm) {
ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
return toStatus(holder->processEcm(sessionId, ecm));
}
ScopedAStatus CasImpl::processEmm(const vector<uint8_t>& emm) {
ALOGV("%s", __FUNCTION__);
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
return toStatus(holder->processEmm(emm));
}
ScopedAStatus CasImpl::sendEvent(int32_t event, int32_t arg, const vector<uint8_t>& eventData) {
ALOGV("%s", __FUNCTION__);
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
status_t err = holder->sendEvent(event, arg, eventData);
return toStatus(err);
}
ScopedAStatus CasImpl::sendSessionEvent(const vector<uint8_t>& sessionId, int32_t event,
int32_t arg, const vector<uint8_t>& eventData) {
ALOGV("%s", __FUNCTION__);
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
status_t err = holder->sendSessionEvent(sessionId, event, arg, eventData);
return toStatus(err);
}
ScopedAStatus CasImpl::provision(const string& provisionString) {
ALOGV("%s: provisionString=%s", __FUNCTION__, provisionString.c_str());
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
return toStatus(holder->provision(String8(provisionString.c_str())));
}
ScopedAStatus CasImpl::refreshEntitlements(int32_t refreshType,
const vector<uint8_t>& refreshData) {
ALOGV("%s", __FUNCTION__);
shared_ptr<CasPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
status_t err = holder->refreshEntitlements(refreshType, refreshData);
return toStatus(err);
}
ScopedAStatus CasImpl::release() {
ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get());
shared_ptr<CasPlugin> holder(nullptr);
atomic_store(&mPluginHolder, holder);
return ScopedAStatus::ok();
}
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

93
cas/aidl/default/CasImpl.h Executable file
View file

@ -0,0 +1,93 @@
/*
* Copyright (C) 2022 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 <aidl/android/hardware/cas/BnCas.h>
#include <aidl/android/hardware/cas/ICasListener.h>
#include <media/stagefright/foundation/ABase.h>
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
using namespace ::android;
using namespace std;
using ndk::ScopedAStatus;
class CasImpl : public BnCas {
public:
CasImpl(const shared_ptr<ICasListener>& listener);
virtual ~CasImpl();
static void OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size);
static void CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size,
const CasSessionId* sessionId);
static void StatusUpdate(void* appData, int32_t event, int32_t arg);
void init(CasPlugin* plugin);
void onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size);
void onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data,
size_t size);
void onStatusUpdate(int32_t event, int32_t arg);
// ICas inherits
ScopedAStatus setPluginStatusUpdateCallback();
virtual ScopedAStatus setPrivateData(const vector<uint8_t>& pvtData) override;
virtual ScopedAStatus openSession(SessionIntent intent, ScramblingMode mode,
vector<uint8_t>* sessionId) override;
virtual ScopedAStatus closeSession(const vector<uint8_t>& sessionId) override;
virtual ScopedAStatus setSessionPrivateData(const vector<uint8_t>& sessionId,
const vector<uint8_t>& pvtData) override;
virtual ScopedAStatus processEcm(const vector<uint8_t>& sessionId,
const vector<uint8_t>& ecm) override;
virtual ScopedAStatus processEmm(const vector<uint8_t>& emm) override;
virtual ScopedAStatus sendEvent(int32_t event, int32_t arg,
const vector<uint8_t>& eventData) override;
virtual ScopedAStatus sendSessionEvent(const vector<uint8_t>& sessionId, int32_t event,
int32_t arg, const vector<uint8_t>& eventData) override;
virtual ScopedAStatus provision(const string& provisionString) override;
virtual ScopedAStatus refreshEntitlements(int32_t refreshType,
const vector<uint8_t>& refreshData) override;
virtual ScopedAStatus release() override;
private:
struct PluginHolder;
shared_ptr<CasPlugin> mPluginHolder;
shared_ptr<ICasListener> mListener;
DISALLOW_EVIL_CONSTRUCTORS(CasImpl);
};
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,193 @@
/*
* Copyright (C) 2022 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_TAG "android.hardware.cas-DescramblerImpl"
#include <aidlcommonsupport/NativeHandle.h>
#include <inttypes.h>
#include <media/cas/DescramblerAPI.h>
#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/AUtils.h>
#include <sys/mman.h>
#include <utils/Log.h>
#include "DescramblerImpl.h"
#include "TypeConvert.h"
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
#define CHECK_SUBSAMPLE_DEF(type) \
static_assert(sizeof(SubSample) == sizeof(type::SubSample), "SubSample: size doesn't match"); \
static_assert(offsetof(SubSample, numBytesOfClearData) == \
offsetof(type::SubSample, mNumBytesOfClearData), \
"SubSample: numBytesOfClearData offset doesn't match"); \
static_assert(offsetof(SubSample, numBytesOfEncryptedData) == \
offsetof(type::SubSample, mNumBytesOfEncryptedData), \
"SubSample: numBytesOfEncryptedData offset doesn't match")
CHECK_SUBSAMPLE_DEF(DescramblerPlugin);
CHECK_SUBSAMPLE_DEF(CryptoPlugin);
DescramblerImpl::DescramblerImpl(DescramblerPlugin* plugin) : mPluginHolder(plugin) {
ALOGV("CTOR: plugin=%p", mPluginHolder.get());
}
DescramblerImpl::~DescramblerImpl() {
ALOGV("DTOR: plugin=%p", mPluginHolder.get());
release();
}
ScopedAStatus DescramblerImpl::setMediaCasSession(const vector<uint8_t>& in_sessionId) {
ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(in_sessionId).string());
shared_ptr<DescramblerPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
return toStatus(holder->setMediaCasSession(in_sessionId));
}
ScopedAStatus DescramblerImpl::requiresSecureDecoderComponent(const string& in_mime,
bool* _aidl_return) {
shared_ptr<DescramblerPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
*_aidl_return = false;
}
*_aidl_return = holder->requiresSecureDecoderComponent(String8(in_mime.c_str()));
return ScopedAStatus::ok();
}
static inline bool validateRangeForSize(int64_t offset, int64_t length, int64_t size) {
return isInRange<int64_t, uint64_t>(0, (uint64_t)size, offset, (uint64_t)length);
}
ScopedAStatus DescramblerImpl::descramble(ScramblingControl scramblingControl,
const vector<SubSample>& subSamples,
const SharedBuffer& srcBuffer, int64_t srcOffset,
const DestinationBuffer& dstBuffer, int64_t dstOffset,
int32_t* _aidl_return) {
ALOGV("%s", __FUNCTION__);
// heapbase's size is stored in int64_t, but mapMemory's mmap will map size in
// size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed but the
// mapped memory's actual size will be smaller than the reported size.
if (srcBuffer.heapBase.size > SIZE_MAX) {
ALOGE("Invalid memory size: %" PRIu64 "", srcBuffer.heapBase.size);
android_errorWriteLog(0x534e4554, "79376389");
return toStatus(BAD_VALUE);
}
void* srcPtr = mmap(NULL, srcBuffer.heapBase.size, PROT_READ | PROT_WRITE, MAP_SHARED,
srcBuffer.heapBase.fd.get(), 0);
// Validate if the offset and size in the SharedBuffer is consistent with the
// mapped heapbase, since the offset and size is controlled by client.
if (srcPtr == NULL) {
ALOGE("Failed to map src buffer.");
return toStatus(BAD_VALUE);
}
if (!validateRangeForSize(srcBuffer.offset, srcBuffer.size, srcBuffer.heapBase.size)) {
ALOGE("Invalid src buffer range: offset %" PRIu64 ", size %" PRIu64
", srcMem"
"size %" PRIu64 "",
srcBuffer.offset, srcBuffer.size, srcBuffer.heapBase.size);
android_errorWriteLog(0x534e4554, "67962232");
return toStatus(BAD_VALUE);
}
// use 64-bit here to catch bad subsample size that might be overflowing.
uint64_t totalBytesInSubSamples = 0;
for (size_t i = 0; i < subSamples.size(); i++) {
uint32_t numBytesOfClearData = subSamples[i].numBytesOfClearData;
uint32_t numBytesOfEncryptedData = subSamples[i].numBytesOfEncryptedData;
totalBytesInSubSamples += (uint64_t)numBytesOfClearData + numBytesOfEncryptedData;
}
// Further validate if the specified srcOffset and requested total subsample size
// is consistent with the source shared buffer size.
if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
ALOGE("Invalid srcOffset and subsample size: "
"srcOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64
", srcBuffer"
"size %" PRIu64 "",
srcOffset, totalBytesInSubSamples, srcBuffer.size);
android_errorWriteLog(0x534e4554, "67962232");
return toStatus(BAD_VALUE);
}
srcPtr = (uint8_t*)srcPtr + srcBuffer.offset;
void* dstPtr = NULL;
if (dstBuffer.getTag() == DestinationBuffer::Tag::nonsecureMemory) {
// When using shared memory, src buffer is also used as dst,
// we don't map it again here.
dstPtr = srcPtr;
// In this case the dst and src would be the same buffer, need to validate
// dstOffset against the buffer size too.
if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
ALOGE("Invalid dstOffset and subsample size: "
"dstOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64
", srcBuffer"
"size %" PRIu64 "",
dstOffset, totalBytesInSubSamples, srcBuffer.size);
android_errorWriteLog(0x534e4554, "67962232");
return toStatus(BAD_VALUE);
}
} else {
native_handle_t* handle = makeFromAidl(dstBuffer.get<DestinationBuffer::secureMemory>());
dstPtr = static_cast<void*>(handle);
}
// Get a local copy of the shared_ptr for the plugin. Note that before
// calling the callback, this shared_ptr must be manually reset, since
// the client side could proceed as soon as the callback is called
// without waiting for this method to go out of scope.
shared_ptr<DescramblerPlugin> holder = atomic_load(&mPluginHolder);
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
// Casting SubSample to DescramblerPlugin::SubSample, but need to ensure
// structs are actually identical
auto returnStatus =
holder->descramble(dstBuffer.getTag() != DestinationBuffer::Tag::nonsecureMemory,
(DescramblerPlugin::ScramblingControl)scramblingControl,
subSamples.size(), (DescramblerPlugin::SubSample*)subSamples.data(),
srcPtr, srcOffset, dstPtr, dstOffset, NULL);
holder.reset();
*_aidl_return = returnStatus;
return toStatus(returnStatus >= 0 ? OK : returnStatus);
}
ScopedAStatus DescramblerImpl::release() {
ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get());
shared_ptr<DescramblerPlugin> holder(nullptr);
atomic_store(&mPluginHolder, holder);
return ScopedAStatus::ok();
}
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2022 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 <aidl/android/hardware/cas/BnDescrambler.h>
#include <media/stagefright/foundation/ABase.h>
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
using namespace ::android;
using namespace std;
using ndk::ScopedAStatus;
class DescramblerImpl : public BnDescrambler {
public:
DescramblerImpl(DescramblerPlugin* plugin);
virtual ~DescramblerImpl();
virtual ScopedAStatus setMediaCasSession(const vector<uint8_t>& in_sessionId) override;
virtual ScopedAStatus requiresSecureDecoderComponent(const string& in_mime,
bool* _aidl_return) override;
virtual ScopedAStatus descramble(ScramblingControl in_scramblingControl,
const vector<SubSample>& in_subSamples,
const SharedBuffer& in_srcBuffer, int64_t in_srcOffset,
const DestinationBuffer& in_dstBuffer, int64_t in_dstOffset,
int32_t* _aidl_return) override;
virtual ScopedAStatus release() override;
private:
shared_ptr<DescramblerPlugin> mPluginHolder;
DISALLOW_EVIL_CONSTRUCTORS(DescramblerImpl);
};
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

219
cas/aidl/default/FactoryLoader.h Executable file
View file

@ -0,0 +1,219 @@
/*
* Copyright (C) 2022 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 <dirent.h>
#include <dlfcn.h>
#include <media/cas/CasAPI.h>
#include <utils/KeyedVector.h>
#include <utils/Mutex.h>
#include "SharedLibrary.h"
using namespace std;
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
using namespace ::android;
template <class T>
class FactoryLoader {
public:
FactoryLoader(const char* name) : mFactory(NULL), mCreateFactoryFuncName(name) {}
virtual ~FactoryLoader() { closeFactory(); }
bool findFactoryForScheme(int32_t CA_system_id, shared_ptr<SharedLibrary>* library = NULL,
T** factory = NULL);
bool enumeratePlugins(vector<AidlCasPluginDescriptor>* results);
private:
typedef T* (*CreateFactoryFunc)();
Mutex mMapLock;
T* mFactory;
const char* mCreateFactoryFuncName;
shared_ptr<SharedLibrary> mLibrary;
KeyedVector<int32_t, String8> mCASystemIdToLibraryPathMap;
KeyedVector<String8, shared_ptr<SharedLibrary>> mLibraryPathToOpenLibraryMap;
bool loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
shared_ptr<SharedLibrary>* library, T** factory);
bool queryPluginsFromPath(const String8& path, vector<AidlCasPluginDescriptor>* results);
bool openFactory(const String8& path);
void closeFactory();
};
template <class T>
bool FactoryLoader<T>::findFactoryForScheme(int32_t CA_system_id,
shared_ptr<SharedLibrary>* library, T** factory) {
if (library != NULL) {
library->reset();
}
if (factory != NULL) {
*factory = NULL;
}
Mutex::Autolock autoLock(mMapLock);
// first check cache
ssize_t index = mCASystemIdToLibraryPathMap.indexOfKey(CA_system_id);
if (index >= 0) {
return loadFactoryForSchemeFromPath(mCASystemIdToLibraryPathMap[index], CA_system_id,
library, factory);
}
// no luck, have to search
#ifdef __LP64__
String8 dirPath("/vendor/lib64/mediacas");
#else
String8 dirPath("/vendor/lib/mediacas");
#endif
DIR* pDir = opendir(dirPath.string());
if (pDir == NULL) {
ALOGE("Failed to open plugin directory %s", dirPath.string());
return false;
}
struct dirent* pEntry;
while ((pEntry = readdir(pDir))) {
String8 pluginPath = dirPath + "/" + pEntry->d_name;
if (pluginPath.getPathExtension() == ".so") {
if (loadFactoryForSchemeFromPath(pluginPath, CA_system_id, library, factory)) {
mCASystemIdToLibraryPathMap.add(CA_system_id, pluginPath);
closedir(pDir);
return true;
}
}
}
closedir(pDir);
ALOGE("Failed to find plugin");
return false;
}
template <class T>
bool FactoryLoader<T>::enumeratePlugins(vector<AidlCasPluginDescriptor>* results) {
ALOGI("enumeratePlugins");
results->clear();
#ifdef __LP64__
String8 dirPath("/vendor/lib64/mediacas");
#else
String8 dirPath("/vendor/lib/mediacas");
#endif
DIR* pDir = opendir(dirPath.string());
if (pDir == NULL) {
ALOGE("Failed to open plugin directory %s", dirPath.string());
return false;
}
Mutex::Autolock autoLock(mMapLock);
struct dirent* pEntry;
while ((pEntry = readdir(pDir))) {
String8 pluginPath = dirPath + "/" + pEntry->d_name;
if (pluginPath.getPathExtension() == ".so") {
queryPluginsFromPath(pluginPath, results);
}
}
return true;
}
template <class T>
bool FactoryLoader<T>::loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
shared_ptr<SharedLibrary>* library,
T** factory) {
closeFactory();
if (!openFactory(path) || !mFactory->isSystemIdSupported(CA_system_id)) {
closeFactory();
return false;
}
if (library != NULL) {
*library = mLibrary;
}
if (factory != NULL) {
*factory = mFactory;
}
return true;
}
template <class T>
bool FactoryLoader<T>::queryPluginsFromPath(const String8& path,
vector<AidlCasPluginDescriptor>* results) {
closeFactory();
vector<CasPluginDescriptor> descriptors;
if (!openFactory(path) || mFactory->queryPlugins(&descriptors) != OK) {
closeFactory();
return false;
}
for (auto it = descriptors.begin(); it != descriptors.end(); it++) {
results->push_back(
AidlCasPluginDescriptor{.caSystemId = it->CA_system_id, .name = it->name.c_str()});
}
return true;
}
template <class T>
bool FactoryLoader<T>::openFactory(const String8& path) {
// get strong pointer to open shared library
ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path);
if (index >= 0) {
mLibrary = mLibraryPathToOpenLibraryMap[index];
} else {
index = mLibraryPathToOpenLibraryMap.add(path, NULL);
}
if (!mLibrary.get()) {
mLibrary = ::ndk::SharedRefBase::make<SharedLibrary>(path);
if (!*mLibrary) {
return false;
}
mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary);
}
CreateFactoryFunc createFactory = (CreateFactoryFunc)mLibrary->lookup(mCreateFactoryFuncName);
if (createFactory == NULL || (mFactory = createFactory()) == NULL) {
return false;
}
return true;
}
template <class T>
void FactoryLoader<T>::closeFactory() {
delete mFactory;
mFactory = NULL;
mLibrary.reset();
}
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,101 @@
/*
* Copyright (C) 2022 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_TAG "android.hardware.cas-MediaCasService"
#include <media/cas/CasAPI.h>
#include <media/cas/DescramblerAPI.h>
#include <utils/Log.h>
#include "CasImpl.h"
#include "DescramblerImpl.h"
#include "MediaCasService.h"
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
MediaCasService::MediaCasService()
: mCasLoader("createCasFactory"), mDescramblerLoader("createDescramblerFactory") {}
MediaCasService::~MediaCasService() {}
ScopedAStatus MediaCasService::enumeratePlugins(
vector<AidlCasPluginDescriptor>* aidlCasPluginDescriptors) {
ALOGV("%s", __FUNCTION__);
mCasLoader.enumeratePlugins(aidlCasPluginDescriptors);
return ScopedAStatus::ok();
}
ScopedAStatus MediaCasService::isSystemIdSupported(int32_t CA_system_id, bool* _aidl_return) {
ALOGV("isSystemIdSupported: CA_system_id=%d", CA_system_id);
*_aidl_return = mCasLoader.findFactoryForScheme(CA_system_id);
return ScopedAStatus::ok();
}
ScopedAStatus MediaCasService::createPlugin(int32_t CA_system_id,
const shared_ptr<ICasListener>& listener,
shared_ptr<ICas>* _aidl_return) {
ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
if (listener == NULL) ALOGV("%s: Listener is NULL", __FUNCTION__);
CasFactory* factory;
shared_ptr<SharedLibrary> library;
if (mCasLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
CasPlugin* plugin = NULL;
shared_ptr<CasImpl> casImpl = ::ndk::SharedRefBase::make<CasImpl>(listener);
if (factory->createPlugin(CA_system_id, casImpl.get(), &CasImpl::CallBackExt, &plugin) ==
OK &&
plugin != NULL) {
casImpl->init(plugin);
*_aidl_return = casImpl;
casImpl->setPluginStatusUpdateCallback();
}
}
return ScopedAStatus::ok();
}
ScopedAStatus MediaCasService::isDescramblerSupported(int32_t CA_system_id, bool* _aidl_return) {
ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
*_aidl_return = mDescramblerLoader.findFactoryForScheme(CA_system_id);
return ScopedAStatus::ok();
}
ScopedAStatus MediaCasService::createDescrambler(int32_t CA_system_id,
shared_ptr<IDescrambler>* _aidl_return) {
ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
DescramblerFactory* factory;
shared_ptr<SharedLibrary> library;
if (mDescramblerLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
DescramblerPlugin* plugin = NULL;
if (factory->createPlugin(CA_system_id, &plugin) == OK && plugin != NULL) {
*_aidl_return = ::ndk::SharedRefBase::make<DescramblerImpl>(plugin);
}
}
return ScopedAStatus::ok();
}
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2022 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 <aidl/android/hardware/cas/BnMediaCasService.h>
#include <media/cas/DescramblerAPI.h>
#include "FactoryLoader.h"
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
using namespace ::android;
using namespace std;
using ndk::ScopedAStatus;
class MediaCasService : public BnMediaCasService {
public:
MediaCasService();
virtual ScopedAStatus enumeratePlugins(
vector<AidlCasPluginDescriptor>* aidlCasPluginDescriptors) override;
virtual ScopedAStatus isSystemIdSupported(int32_t in_CA_system_id, bool* _aidl_return) override;
virtual ScopedAStatus createPlugin(int32_t in_CA_system_id,
const shared_ptr<ICasListener>& in_listener,
shared_ptr<ICas>* _aidl_return) override;
virtual ScopedAStatus isDescramblerSupported(int32_t in_CA_system_id,
bool* _aidl_return) override;
virtual ScopedAStatus createDescrambler(int32_t in_CA_system_id,
shared_ptr<IDescrambler>* _aidl_return) override;
private:
FactoryLoader<CasFactory> mCasLoader;
FactoryLoader<DescramblerFactory> mDescramblerLoader;
virtual ~MediaCasService();
};
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

3
cas/aidl/default/OWNERS Executable file
View file

@ -0,0 +1,3 @@
# Bug component: 1344
quxiangfang@google.com
hgchen@google.com

View file

@ -0,0 +1,61 @@
/*
* Copyright (C) 2022 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_TAG "android.hardware.cas-SharedLibrary"
#include "SharedLibrary.h"
#include <dlfcn.h>
#include <utils/Log.h>
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
SharedLibrary::SharedLibrary(const String8& path) {
mLibHandle = dlopen(path.string(), RTLD_NOW);
}
SharedLibrary::~SharedLibrary() {
if (mLibHandle != NULL) {
dlclose(mLibHandle);
mLibHandle = NULL;
}
}
bool SharedLibrary::operator!() const {
return mLibHandle == NULL;
}
void* SharedLibrary::lookup(const char* symbol) const {
if (!mLibHandle) {
return NULL;
}
// Clear last error before we load the symbol again,
// in case the caller didn't retrieve it.
(void)dlerror();
return dlsym(mLibHandle, symbol);
}
const char* SharedLibrary::lastError() const {
const char* error = dlerror();
return error ? error : "No errors or unknown error";
}
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2022 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 <android/binder_interface_utils.h>
#include <media/stagefright/foundation/ABase.h>
#include <utils/String8.h>
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
using namespace ::android;
class SharedLibrary : public ndk::SharedRefBase {
public:
explicit SharedLibrary(const String8& path);
~SharedLibrary();
bool operator!() const;
void* lookup(const char* symbol) const;
const char* lastError() const;
private:
void* mLibHandle;
DISALLOW_EVIL_CONSTRUCTORS(SharedLibrary);
};
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

110
cas/aidl/default/TypeConvert.cpp Executable file
View file

@ -0,0 +1,110 @@
/*
* Copyright (C) 2022 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_TAG "android.hardware.cas-TypeConvert"
#include <aidl/android/hardware/cas/Status.h>
#include <utils/Log.h>
#include "TypeConvert.h"
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
ScopedAStatus toStatus(status_t legacyStatus) {
int status;
switch (legacyStatus) {
case OK:
return ndk::ScopedAStatus::ok();
case ERROR_CAS_NO_LICENSE:
status = Status::ERROR_CAS_NO_LICENSE;
break;
case ERROR_CAS_LICENSE_EXPIRED:
status = Status::ERROR_CAS_LICENSE_EXPIRED;
break;
case ERROR_CAS_SESSION_NOT_OPENED:
status = Status::ERROR_CAS_SESSION_NOT_OPENED;
break;
case ERROR_CAS_CANNOT_HANDLE:
status = Status::ERROR_CAS_CANNOT_HANDLE;
break;
case ERROR_CAS_TAMPER_DETECTED:
status = Status::ERROR_CAS_INVALID_STATE;
break;
case BAD_VALUE:
status = Status::BAD_VALUE;
break;
case ERROR_CAS_NOT_PROVISIONED:
status = Status::ERROR_CAS_NOT_PROVISIONED;
break;
case ERROR_CAS_RESOURCE_BUSY:
status = Status::ERROR_CAS_RESOURCE_BUSY;
break;
case ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION:
status = Status::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION;
break;
case ERROR_CAS_DEVICE_REVOKED:
status = Status::ERROR_CAS_DEVICE_REVOKED;
break;
case ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED:
status = Status::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED;
break;
case ERROR_CAS_DECRYPT:
status = Status::ERROR_CAS_DECRYPT;
break;
case ERROR_CAS_NEED_ACTIVATION:
status = Status::ERROR_CAS_NEED_ACTIVATION;
break;
case ERROR_CAS_NEED_PAIRING:
status = Status::ERROR_CAS_NEED_PAIRING;
break;
case ERROR_CAS_NO_CARD:
status = Status::ERROR_CAS_NO_CARD;
break;
case ERROR_CAS_CARD_MUTE:
status = Status::ERROR_CAS_CARD_MUTE;
break;
case ERROR_CAS_CARD_INVALID:
status = Status::ERROR_CAS_CARD_INVALID;
break;
case ERROR_CAS_BLACKOUT:
status = Status::ERROR_CAS_BLACKOUT;
break;
default:
ALOGW("Unable to convert legacy status: %d, defaulting to UNKNOWN", legacyStatus);
status = Status::ERROR_CAS_UNKNOWN;
break;
}
return ScopedAStatus::fromServiceSpecificError(status);
}
String8 sessionIdToString(const std::vector<uint8_t>& sessionId) {
String8 result;
for (auto it = sessionId.begin(); it != sessionId.end(); it++) {
result.appendFormat("%02x ", *it);
}
if (result.isEmpty()) {
result.append("(null)");
}
return result;
}
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

36
cas/aidl/default/TypeConvert.h Executable file
View file

@ -0,0 +1,36 @@
/*
* Copyright (C) 2022 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 <android/binder_interface_utils.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/String8.h>
namespace aidl {
namespace android {
namespace hardware {
namespace cas {
using namespace ::android;
using ndk::ScopedAStatus;
ScopedAStatus toStatus(status_t legacyStatus);
String8 sessionIdToString(const std::vector<uint8_t>& sessionId);
} // namespace cas
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,6 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.cas</name>
<fqname>IMediaCasService/default</fqname>
</hal>
</manifest>

View file

@ -0,0 +1,9 @@
service vendor.cas-default-lazy /vendor/bin/hw/android.hardware.cas-service.example-lazy
interface aidl android.hardware.cas.IMediaCasService/default
class hal
user media
group mediadrm drmrpc
ioprio rt 4
task_profiles ProcessCapacityHigh
oneshot
disabled

View file

@ -0,0 +1,7 @@
service vendor.cas-default /vendor/bin/hw/android.hardware.cas-service.example
interface aidl android.hardware.cas.IMediaCasService/default
class hal
user media
group mediadrm drmrpc
ioprio rt 4
task_profiles ProcessCapacityHigh

30
cas/aidl/default/fuzzer.cpp Executable file
View file

@ -0,0 +1,30 @@
/*
* Copyright (C) 2022 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 <MediaCasService.h>
#include <fuzzbinder/libbinder_ndk_driver.h>
#include <fuzzer/FuzzedDataProvider.h>
using aidl::android::hardware::cas::MediaCasService;
using android::fuzzService;
using ndk::SharedRefBase;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
auto mediaCasServiceAidl = SharedRefBase::make<MediaCasService>();
fuzzService(mediaCasServiceAidl->asBinder().get(), FuzzedDataProvider(data, size));
return 0;
}

50
cas/aidl/default/service.cpp Executable file
View file

@ -0,0 +1,50 @@
/*
* Copyright 2022 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.
*/
#ifdef LAZY_SERVICE
const bool kLazyService = true;
#define LOG_TAG "android.hardware.cas-service.example-lazy"
#else
const bool kLazyService = false;
#define LOG_TAG "android.hardware.cas-service.example"
#endif
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include "MediaCasService.h"
using aidl::android::hardware::cas::MediaCasService;
int main() {
ABinderProcess_setThreadPoolMaxThreadCount(8);
// Setup hwbinder service
std::shared_ptr<MediaCasService> service = ::ndk::SharedRefBase::make<MediaCasService>();
const std::string instance = std::string() + MediaCasService::descriptor + "/default";
binder_status_t status;
if (kLazyService) {
status = AServiceManager_registerLazyService(service->asBinder().get(), instance.c_str());
} else {
status = AServiceManager_addService(service->asBinder().get(), instance.c_str());
}
LOG_ALWAYS_FATAL_IF(status != STATUS_OK, "Error while registering cas service: %d", status);
ABinderProcess_joinThreadPool();
return 0;
}