1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_NDEBUG 0
18#define LOG_TAG "MockCasPlugin"
19
20#include <media/stagefright/foundation/hexdump.h>
21#include <media/stagefright/MediaErrors.h>
22#include <utils/Log.h>
23
24#include "MockCasPlugin.h"
25#include "MockSessionLibrary.h"
26
27android::CasFactory* createCasFactory() {
28    return new android::MockCasFactory();
29}
30
31android::DescramblerFactory* createDescramblerFactory() {
32    return new android::MockDescramblerFactory();
33}
34
35namespace android {
36
37static const int32_t sMockId = 0xFFFF;
38
39bool MockCasFactory::isSystemIdSupported(int32_t CA_system_id) const {
40    return CA_system_id == sMockId;
41}
42
43status_t MockCasFactory::queryPlugins(
44        std::vector<CasPluginDescriptor> *descriptors) const {
45    descriptors->clear();
46    descriptors->push_back({sMockId, String8("MockCAS")});
47    return OK;
48}
49
50status_t MockCasFactory::createPlugin(
51        int32_t CA_system_id,
52        uint64_t appData,
53        CasPluginCallback callback,
54        CasPlugin **plugin) {
55    if (!isSystemIdSupported(CA_system_id)) {
56        return BAD_VALUE;
57    }
58
59    *plugin = new MockCasPlugin();
60    return OK;
61}
62
63///////////////////////////////////////////////////////////////////////////////
64
65bool MockDescramblerFactory::isSystemIdSupported(int32_t CA_system_id) const {
66    return CA_system_id == sMockId;
67}
68
69status_t MockDescramblerFactory::createPlugin(
70        int32_t CA_system_id, DescramblerPlugin** plugin) {
71    if (!isSystemIdSupported(CA_system_id)) {
72        return BAD_VALUE;
73    }
74
75    *plugin = new MockDescramblerPlugin();
76    return OK;
77}
78
79///////////////////////////////////////////////////////////////////////////////
80
81static String8 arrayToString(const std::vector<uint8_t> &array) {
82    String8 result;
83    for (size_t i = 0; i < array.size(); i++) {
84        result.appendFormat("%02x ", array[i]);
85    }
86    if (result.isEmpty()) {
87        result.append("(null)");
88    }
89    return result;
90}
91
92MockCasPlugin::MockCasPlugin() {
93    ALOGV("CTOR");
94}
95
96MockCasPlugin::~MockCasPlugin() {
97    ALOGV("DTOR");
98    MockSessionLibrary::get()->destroyPlugin(this);
99}
100
101status_t MockCasPlugin::setPrivateData(const CasData &data) {
102    ALOGV("setPrivateData");
103    return OK;
104}
105
106status_t MockCasPlugin::openSession(CasSessionId* sessionId) {
107    ALOGV("openSession");
108    return MockSessionLibrary::get()->addSession(this, sessionId);
109}
110
111status_t MockCasPlugin::closeSession(const CasSessionId &sessionId) {
112    ALOGV("closeSession: sessionId=%s", arrayToString(sessionId).string());
113    Mutex::Autolock lock(mLock);
114
115    sp<MockCasSession> session =
116            MockSessionLibrary::get()->findSession(sessionId);
117    if (session == NULL) {
118        return BAD_VALUE;
119    }
120
121    MockSessionLibrary::get()->destroySession(sessionId);
122    return OK;
123}
124
125status_t MockCasPlugin::setSessionPrivateData(
126        const CasSessionId &sessionId, const CasData &data) {
127    ALOGV("setSessionPrivateData: sessionId=%s",
128            arrayToString(sessionId).string());
129    Mutex::Autolock lock(mLock);
130
131    sp<MockCasSession> session =
132            MockSessionLibrary::get()->findSession(sessionId);
133    if (session == NULL) {
134        return BAD_VALUE;
135    }
136    return OK;
137}
138
139status_t MockCasPlugin::processEcm(
140        const CasSessionId &sessionId, const CasEcm& ecm) {
141    ALOGV("processEcm: sessionId=%s", arrayToString(sessionId).string());
142    Mutex::Autolock lock(mLock);
143
144    sp<MockCasSession> session =
145            MockSessionLibrary::get()->findSession(sessionId);
146    if (session == NULL) {
147        return BAD_VALUE;
148    }
149    ALOGV("ECM: size=%d", ecm.size());
150    ALOGV("ECM: data=%s", arrayToString(ecm).string());
151
152    return OK;
153}
154
155status_t MockCasPlugin::processEmm(const CasEmm& emm) {
156    ALOGV("processEmm");
157    Mutex::Autolock lock(mLock);
158
159    ALOGV("EMM: size=%d", emm.size());
160    ALOGV("EMM: data=%s", arrayToString(emm).string());
161
162    return OK;
163}
164
165status_t MockCasPlugin::sendEvent(
166        int32_t event, int arg, const CasData &eventData) {
167    ALOGV("sendEvent: event=%d", event);
168    Mutex::Autolock lock(mLock);
169
170    return OK;
171}
172
173status_t MockCasPlugin::provision(const String8 &str) {
174    ALOGV("provision: provisionString=%s", str.string());
175    Mutex::Autolock lock(mLock);
176
177    return OK;
178}
179
180status_t MockCasPlugin::refreshEntitlements(
181        int32_t refreshType, const CasData &refreshData) {
182    ALOGV("refreshEntitlements: refreshData=%s", arrayToString(refreshData).string());
183    Mutex::Autolock lock(mLock);
184
185    return OK;
186}
187
188/////////////////////////////////////////////////////////////////
189bool MockDescramblerPlugin::requiresSecureDecoderComponent(
190        const char *mime) const {
191    ALOGV("MockDescramblerPlugin::requiresSecureDecoderComponent"
192            "(mime=%s)", mime);
193    return false;
194}
195
196status_t MockDescramblerPlugin::setMediaCasSession(
197        const CasSessionId &sessionId) {
198    ALOGV("MockDescramblerPlugin::setMediaCasSession");
199    sp<MockCasSession> session =
200            MockSessionLibrary::get()->findSession(sessionId);
201
202    if (session == NULL) {
203        ALOGE("MockDescramblerPlugin: session not found");
204        return ERROR_DRM_SESSION_NOT_OPENED;
205    }
206
207    return OK;
208}
209
210ssize_t MockDescramblerPlugin::descramble(
211        bool secure,
212        ScramblingControl scramblingControl,
213        size_t numSubSamples,
214        const SubSample *subSamples,
215        const void *srcPtr,
216        int32_t srcOffset,
217        void *dstPtr,
218        int32_t dstOffset,
219        AString *errorDetailMsg) {
220    ALOGV("MockDescramblerPlugin::descramble(secure=%d, sctrl=%d,"
221          "subSamples=%s, srcPtr=%p, dstPtr=%p, srcOffset=%d, dstOffset=%d)",
222          (int)secure, (int)scramblingControl,
223          subSamplesToString(subSamples, numSubSamples).string(),
224          srcPtr, dstPtr, srcOffset, dstOffset);
225
226    return 0;
227}
228
229// Conversion utilities
230String8 MockDescramblerPlugin::arrayToString(
231        uint8_t const *array, size_t len) const
232{
233    String8 result("{ ");
234    for (size_t i = 0; i < len; i++) {
235        result.appendFormat("0x%02x ", array[i]);
236    }
237    result += "}";
238    return result;
239}
240
241String8 MockDescramblerPlugin::subSamplesToString(
242        SubSample const *subSamples, size_t numSubSamples) const
243{
244    String8 result;
245    for (size_t i = 0; i < numSubSamples; i++) {
246        result.appendFormat("[%zu] {clear:%u, encrypted:%u} ", i,
247                            subSamples[i].mNumBytesOfClearData,
248                            subSamples[i].mNumBytesOfEncryptedData);
249    }
250    return result;
251}
252
253} // namespace android
254
255