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 "NuPlayerDrm"
19
20 #include "NuPlayerDrm.h"
21
22 #include <binder/IServiceManager.h>
23 #include <mediadrm/IMediaDrmService.h>
24 #include <utils/Log.h>
25
26
27 namespace android {
28
29 // static helpers - internal
30
CreateDrm(status_t * pstatus)31 sp<IDrm> NuPlayerDrm::CreateDrm(status_t *pstatus)
32 {
33 status_t &status = *pstatus;
34 sp<IServiceManager> sm = defaultServiceManager();
35 sp<IBinder> binder = sm->getService(String16("media.drm"));
36 ALOGV("CreateDrm binder %p", (binder != NULL ? binder.get() : 0));
37
38 sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
39 if (service == NULL) {
40 ALOGE("CreateDrm failed at IMediaDrmService");
41 return NULL;
42 }
43
44 sp<IDrm> drm = service->makeDrm();
45 if (drm == NULL) {
46 ALOGE("CreateDrm failed at makeDrm");
47 return NULL;
48 }
49
50 // this is before plugin creation so NO_INIT is fine
51 status = drm->initCheck();
52 if (status != OK && status != NO_INIT) {
53 ALOGE("CreateDrm failed drm->initCheck(): %d", status);
54 return NULL;
55 }
56 return drm;
57 }
58
createCrypto(status_t * pstatus)59 sp<ICrypto> NuPlayerDrm::createCrypto(status_t *pstatus)
60 {
61 status_t &status = *pstatus;
62 sp<IServiceManager> sm = defaultServiceManager();
63 sp<IBinder> binder = sm->getService(String16("media.drm"));
64
65 sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
66 if (service == NULL) {
67 status = UNKNOWN_ERROR;
68 ALOGE("CreateCrypto failed at IMediaDrmService");
69 return NULL;
70 }
71
72 sp<ICrypto> crypto = service->makeCrypto();
73 if (crypto == NULL) {
74 status = UNKNOWN_ERROR;
75 ALOGE("createCrypto failed");
76 return NULL;
77 }
78
79 // this is before plugin creation so NO_INIT is fine
80 status = crypto->initCheck();
81 if (status != OK && status != NO_INIT) {
82 ALOGE("createCrypto failed crypto->initCheck(): %d", status);
83 return NULL;
84 }
85
86 return crypto;
87 }
88
parsePSSH(const void * pssh,size_t psshsize)89 Vector<DrmUUID> NuPlayerDrm::parsePSSH(const void *pssh, size_t psshsize)
90 {
91 Vector<DrmUUID> drmSchemes, empty;
92 const int DATALEN_SIZE = 4;
93
94 // the format of the buffer is 1 or more of:
95 // {
96 // 16 byte uuid
97 // 4 byte data length N
98 // N bytes of data
99 // }
100 // Determine the number of entries in the source data.
101 // Since we got the data from stagefright, we trust it is valid and properly formatted.
102
103 const uint8_t *data = (const uint8_t*)pssh;
104 size_t len = psshsize;
105 size_t numentries = 0;
106 while (len > 0) {
107 if (len < DrmUUID::UUID_SIZE) {
108 ALOGE("ParsePSSH: invalid PSSH data");
109 return empty;
110 }
111
112 const uint8_t *uuidPtr = data;
113
114 // skip uuid
115 data += DrmUUID::UUID_SIZE;
116 len -= DrmUUID::UUID_SIZE;
117
118 // get data length
119 if (len < DATALEN_SIZE) {
120 ALOGE("ParsePSSH: invalid PSSH data");
121 return empty;
122 }
123
124 uint32_t datalen = *((uint32_t*)data);
125 data += DATALEN_SIZE;
126 len -= DATALEN_SIZE;
127
128 if (len < datalen) {
129 ALOGE("ParsePSSH: invalid PSSH data");
130 return empty;
131 }
132
133 // skip the data
134 data += datalen;
135 len -= datalen;
136
137 DrmUUID _uuid(uuidPtr);
138 drmSchemes.add(_uuid);
139
140 ALOGV("ParsePSSH[%zu]: %s: %s", numentries,
141 _uuid.toHexString().string(),
142 DrmUUID::arrayToHex(data, datalen).string()
143 );
144
145 numentries++;
146 }
147
148 return drmSchemes;
149 }
150
getSupportedDrmSchemes(const void * pssh,size_t psshsize)151 Vector<DrmUUID> NuPlayerDrm::getSupportedDrmSchemes(const void *pssh, size_t psshsize)
152 {
153 Vector<DrmUUID> psshDRMs = parsePSSH(pssh, psshsize);
154
155 Vector<DrmUUID> supportedDRMs;
156 // temporary DRM object for crypto Scheme enquiry (without creating a plugin)
157 status_t status = OK;
158 sp<IDrm> drm = CreateDrm(&status);
159 if (drm != NULL) {
160 for (size_t i = 0; i < psshDRMs.size(); i++) {
161 DrmUUID uuid = psshDRMs[i];
162 bool isSupported = false;
163 status = drm->isCryptoSchemeSupported(uuid.ptr(), String8(),
164 DrmPlugin::kSecurityLevelUnknown, &isSupported);
165 if (status == OK && isSupported) {
166 supportedDRMs.add(uuid);
167 }
168 }
169
170 drm.clear();
171 } else {
172 ALOGE("getSupportedDrmSchemes: Can't create Drm obj: %d", status);
173 }
174
175 ALOGV("getSupportedDrmSchemes: psshDRMs: %zu supportedDRMs: %zu",
176 psshDRMs.size(), supportedDRMs.size());
177
178 return supportedDRMs;
179 }
180
181 // static helpers - public
182
createCryptoAndPlugin(const uint8_t uuid[16],const Vector<uint8_t> & drmSessionId,status_t & status)183 sp<ICrypto> NuPlayerDrm::createCryptoAndPlugin(const uint8_t uuid[16],
184 const Vector<uint8_t> &drmSessionId, status_t &status)
185 {
186 // Extra check
187 if (drmSessionId.isEmpty()) {
188 status = INVALID_OPERATION;
189 ALOGE("createCryptoAndPlugin: Failed. Empty drmSessionId. status: %d", status);
190 return NULL;
191 }
192
193 status = OK;
194 sp<ICrypto> crypto = createCrypto(&status);
195 if (crypto == NULL) {
196 ALOGE("createCryptoAndPlugin: createCrypto failed. status: %d", status);
197 return NULL;
198 }
199 ALOGV("createCryptoAndPlugin: createCrypto succeeded");
200
201 status = crypto->createPlugin(uuid, drmSessionId.array(), drmSessionId.size());
202 if (status != OK) {
203 ALOGE("createCryptoAndPlugin: createCryptoPlugin failed. status: %d", status);
204 // crypto will clean itself when leaving the current scope
205 return NULL;
206 }
207
208 return crypto;
209 }
210
211 // Parcel has only private copy constructor so passing it in rather than returning
retrieveDrmInfo(const void * pssh,size_t psshsize,Parcel * parcel)212 void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel)
213 {
214 // 1) PSSH bytes
215 parcel->writeUint32(psshsize);
216 parcel->writeByteArray(psshsize, (const uint8_t*)pssh);
217
218 ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO PSSH: size: %zu %s", psshsize,
219 DrmUUID::arrayToHex((uint8_t*)pssh, psshsize).string());
220
221 // 2) supportedDRMs
222 Vector<DrmUUID> supportedDRMs = getSupportedDrmSchemes(pssh, psshsize);
223 parcel->writeUint32(supportedDRMs.size());
224 for (size_t i = 0; i < supportedDRMs.size(); i++) {
225 DrmUUID uuid = supportedDRMs[i];
226 parcel->writeByteArray(DrmUUID::UUID_SIZE, uuid.ptr());
227
228 ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i,
229 uuid.toHexString().string());
230 }
231 }
232
233 ////////////////////////////////////////////////////////////////////////////////////////////
234 /// Helpers for NuPlayerDecoder
235 ////////////////////////////////////////////////////////////////////////////////////////////
236
makeCryptoInfo(int numSubSamples,uint8_t key[kBlockSize],uint8_t iv[kBlockSize],CryptoPlugin::Mode mode,size_t * clearbytes,size_t * encryptedbytes)237 NuPlayerDrm::CryptoInfo *NuPlayerDrm::makeCryptoInfo(
238 int numSubSamples,
239 uint8_t key[kBlockSize],
240 uint8_t iv[kBlockSize],
241 CryptoPlugin::Mode mode,
242 size_t *clearbytes,
243 size_t *encryptedbytes)
244 {
245 // size needed to store all the crypto data
246 size_t cryptosize;
247 // sizeof(CryptoInfo) + sizeof(CryptoPlugin::SubSample) * numSubSamples;
248 if (__builtin_mul_overflow(sizeof(CryptoPlugin::SubSample), numSubSamples, &cryptosize) ||
249 __builtin_add_overflow(cryptosize, sizeof(CryptoInfo), &cryptosize)) {
250 ALOGE("crypto size overflow");
251 return NULL;
252 }
253
254 CryptoInfo *ret = (CryptoInfo*) malloc(cryptosize);
255 if (ret == NULL) {
256 ALOGE("couldn't allocate %zu bytes", cryptosize);
257 return NULL;
258 }
259 ret->numSubSamples = numSubSamples;
260 memcpy(ret->key, key, kBlockSize);
261 memcpy(ret->iv, iv, kBlockSize);
262 ret->mode = mode;
263 ret->pattern.mEncryptBlocks = 0;
264 ret->pattern.mSkipBlocks = 0;
265 ret->subSamples = (CryptoPlugin::SubSample*)(ret + 1);
266 CryptoPlugin::SubSample *subSamples = ret->subSamples;
267
268 for (int i = 0; i < numSubSamples; i++) {
269 subSamples[i].mNumBytesOfClearData = (clearbytes == NULL) ? 0 : clearbytes[i];
270 subSamples[i].mNumBytesOfEncryptedData = (encryptedbytes == NULL) ?
271 0 :
272 encryptedbytes[i];
273 }
274
275 return ret;
276 }
277
getSampleCryptoInfo(MetaDataBase & meta)278 NuPlayerDrm::CryptoInfo *NuPlayerDrm::getSampleCryptoInfo(MetaDataBase &meta)
279 {
280 uint32_t type;
281 const void *crypteddata;
282 size_t cryptedsize;
283
284 if (!meta.findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
285 return NULL;
286 }
287 size_t numSubSamples = cryptedsize / sizeof(size_t);
288
289 if (numSubSamples <= 0) {
290 ALOGE("getSampleCryptoInfo INVALID numSubSamples: %zu", numSubSamples);
291 return NULL;
292 }
293
294 const void *cleardata;
295 size_t clearsize;
296 if (meta.findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
297 if (clearsize != cryptedsize) {
298 // The two must be of the same length.
299 ALOGE("getSampleCryptoInfo mismatch cryptedsize: %zu != clearsize: %zu",
300 cryptedsize, clearsize);
301 return NULL;
302 }
303 }
304
305 const void *key;
306 size_t keysize;
307 if (meta.findData(kKeyCryptoKey, &type, &key, &keysize)) {
308 if (keysize != kBlockSize) {
309 ALOGE("getSampleCryptoInfo Keys must be %d bytes in length: %zu",
310 kBlockSize, keysize);
311 // Keys must be 16 bytes in length.
312 return NULL;
313 }
314 }
315
316 const void *iv;
317 size_t ivsize;
318 if (meta.findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
319 if (ivsize != kBlockSize) {
320 ALOGE("getSampleCryptoInfo IV must be %d bytes in length: %zu",
321 kBlockSize, ivsize);
322 // IVs must be 16 bytes in length.
323 return NULL;
324 }
325 }
326
327 int32_t mode;
328 if (!meta.findInt32(kKeyCryptoMode, &mode)) {
329 mode = CryptoPlugin::kMode_AES_CTR;
330 }
331
332 return makeCryptoInfo(numSubSamples,
333 (uint8_t*) key,
334 (uint8_t*) iv,
335 (CryptoPlugin::Mode)mode,
336 (size_t*) cleardata,
337 (size_t*) crypteddata);
338 }
339
340 } // namespace android
341
342