Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6545b4e3 authored by Shraddha Basantwani's avatar Shraddha Basantwani
Browse files

Add default implementation for CAS AIDL

Bug: 230377377, 227673974
Test: manual
Change-Id: I85015dd6e1a69ff9b57f832f5b1cd01fb65dda25
parent fdeb39f8
Loading
Loading
Loading
Loading
+93 −0
Original line number Diff line number Diff line
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 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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
+193 −0
Original line number Diff line number Diff line
/*
 * 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
+56 −0
Original line number Diff line number Diff line
/*
 * 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
Loading