blob: 8d876dabe86d0c1e30a8193cc17ca3f8fa8dabb2 [file] [log] [blame]
Hassan Shojaniacefac142017-02-06 21:02:02 -08001/*
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>
Jeff Tinker7d2c6e82018-02-16 16:14:59 -080023#include <mediadrm/IMediaDrmService.h>
Hassan Shojaniacefac142017-02-06 21:02:02 -080024#include <utils/Log.h>
25
26
27namespace android {
28
29// static helpers - internal
30
31sp<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
59sp<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
89Vector<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
151Vector<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 if (drm->isCryptoSchemeSupported(uuid.ptr(), String8()))
163 supportedDRMs.add(uuid);
164 }
165
166 drm.clear();
167 } else {
168 ALOGE("getSupportedDrmSchemes: Can't create Drm obj: %d", status);
169 }
170
171 ALOGV("getSupportedDrmSchemes: psshDRMs: %zu supportedDRMs: %zu",
172 psshDRMs.size(), supportedDRMs.size());
173
174 return supportedDRMs;
175}
176
177// static helpers - public
178
179sp<ICrypto> NuPlayerDrm::createCryptoAndPlugin(const uint8_t uuid[16],
180 const Vector<uint8_t> &drmSessionId, status_t &status)
181{
182 // Extra check
183 if (drmSessionId.isEmpty()) {
184 status = INVALID_OPERATION;
185 ALOGE("createCryptoAndPlugin: Failed. Empty drmSessionId. status: %d", status);
186 return NULL;
187 }
188
189 status = OK;
190 sp<ICrypto> crypto = createCrypto(&status);
191 if (crypto == NULL) {
192 ALOGE("createCryptoAndPlugin: createCrypto failed. status: %d", status);
193 return NULL;
194 }
195 ALOGV("createCryptoAndPlugin: createCrypto succeeded");
196
197 status = crypto->createPlugin(uuid, drmSessionId.array(), drmSessionId.size());
198 if (status != OK) {
199 ALOGE("createCryptoAndPlugin: createCryptoPlugin failed. status: %d", status);
200 // crypto will clean itself when leaving the current scope
201 return NULL;
202 }
203
204 return crypto;
205}
206
207// Parcel has only private copy constructor so passing it in rather than returning
Hassan Shojaniadd4ce182017-04-20 15:25:39 -0700208void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel)
Hassan Shojaniacefac142017-02-06 21:02:02 -0800209{
Hassan Shojaniacefac142017-02-06 21:02:02 -0800210 // 1) PSSH bytes
211 parcel->writeUint32(psshsize);
212 parcel->writeByteArray(psshsize, (const uint8_t*)pssh);
213
214 ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO PSSH: size: %zu %s", psshsize,
215 DrmUUID::arrayToHex((uint8_t*)pssh, psshsize).string());
216
217 // 2) supportedDRMs
218 Vector<DrmUUID> supportedDRMs = getSupportedDrmSchemes(pssh, psshsize);
219 parcel->writeUint32(supportedDRMs.size());
220 for (size_t i = 0; i < supportedDRMs.size(); i++) {
221 DrmUUID uuid = supportedDRMs[i];
222 parcel->writeByteArray(DrmUUID::UUID_SIZE, uuid.ptr());
223
224 ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i,
225 uuid.toHexString().string());
226 }
Hassan Shojaniacefac142017-02-06 21:02:02 -0800227}
228
229////////////////////////////////////////////////////////////////////////////////////////////
230/// Helpers for NuPlayerDecoder
231////////////////////////////////////////////////////////////////////////////////////////////
232
233NuPlayerDrm::CryptoInfo *NuPlayerDrm::makeCryptoInfo(
234 int numSubSamples,
235 uint8_t key[kBlockSize],
236 uint8_t iv[kBlockSize],
237 CryptoPlugin::Mode mode,
238 size_t *clearbytes,
239 size_t *encryptedbytes)
240{
241 // size needed to store all the crypto data
Marco Nelissen62e83c92018-07-31 15:25:58 -0700242 size_t cryptosize;
243 // sizeof(CryptoInfo) + sizeof(CryptoPlugin::SubSample) * numSubSamples;
244 if (__builtin_mul_overflow(sizeof(CryptoPlugin::SubSample), numSubSamples, &cryptosize) ||
245 __builtin_add_overflow(cryptosize, sizeof(CryptoInfo), &cryptosize)) {
246 ALOGE("crypto size overflow");
247 return NULL;
248 }
249
Hassan Shojaniacefac142017-02-06 21:02:02 -0800250 CryptoInfo *ret = (CryptoInfo*) malloc(cryptosize);
251 if (ret == NULL) {
252 ALOGE("couldn't allocate %zu bytes", cryptosize);
253 return NULL;
254 }
255 ret->numSubSamples = numSubSamples;
256 memcpy(ret->key, key, kBlockSize);
257 memcpy(ret->iv, iv, kBlockSize);
258 ret->mode = mode;
259 ret->pattern.mEncryptBlocks = 0;
260 ret->pattern.mSkipBlocks = 0;
261 ret->subSamples = (CryptoPlugin::SubSample*)(ret + 1);
262 CryptoPlugin::SubSample *subSamples = ret->subSamples;
263
264 for (int i = 0; i < numSubSamples; i++) {
265 subSamples[i].mNumBytesOfClearData = (clearbytes == NULL) ? 0 : clearbytes[i];
266 subSamples[i].mNumBytesOfEncryptedData = (encryptedbytes == NULL) ?
267 0 :
268 encryptedbytes[i];
269 }
270
271 return ret;
272}
273
Marco Nelissen3d21ae32018-02-16 08:24:08 -0800274NuPlayerDrm::CryptoInfo *NuPlayerDrm::getSampleCryptoInfo(MetaDataBase &meta)
Hassan Shojaniacefac142017-02-06 21:02:02 -0800275{
276 uint32_t type;
277 const void *crypteddata;
278 size_t cryptedsize;
279
Marco Nelissen3d21ae32018-02-16 08:24:08 -0800280 if (!meta.findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
Hassan Shojaniacefac142017-02-06 21:02:02 -0800281 return NULL;
282 }
283 size_t numSubSamples = cryptedsize / sizeof(size_t);
284
285 if (numSubSamples <= 0) {
286 ALOGE("getSampleCryptoInfo INVALID numSubSamples: %zu", numSubSamples);
287 return NULL;
288 }
289
290 const void *cleardata;
291 size_t clearsize;
Marco Nelissen3d21ae32018-02-16 08:24:08 -0800292 if (meta.findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
Hassan Shojaniacefac142017-02-06 21:02:02 -0800293 if (clearsize != cryptedsize) {
294 // The two must be of the same length.
295 ALOGE("getSampleCryptoInfo mismatch cryptedsize: %zu != clearsize: %zu",
296 cryptedsize, clearsize);
297 return NULL;
298 }
299 }
300
301 const void *key;
302 size_t keysize;
Marco Nelissen3d21ae32018-02-16 08:24:08 -0800303 if (meta.findData(kKeyCryptoKey, &type, &key, &keysize)) {
Hassan Shojaniacefac142017-02-06 21:02:02 -0800304 if (keysize != kBlockSize) {
Hassan Shojaniae00e3922017-02-15 20:24:41 -0800305 ALOGE("getSampleCryptoInfo Keys must be %d bytes in length: %zu",
Hassan Shojaniacefac142017-02-06 21:02:02 -0800306 kBlockSize, keysize);
307 // Keys must be 16 bytes in length.
308 return NULL;
309 }
310 }
311
312 const void *iv;
313 size_t ivsize;
Marco Nelissen3d21ae32018-02-16 08:24:08 -0800314 if (meta.findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
Hassan Shojaniacefac142017-02-06 21:02:02 -0800315 if (ivsize != kBlockSize) {
Hassan Shojaniae00e3922017-02-15 20:24:41 -0800316 ALOGE("getSampleCryptoInfo IV must be %d bytes in length: %zu",
Hassan Shojaniacefac142017-02-06 21:02:02 -0800317 kBlockSize, ivsize);
318 // IVs must be 16 bytes in length.
319 return NULL;
320 }
321 }
322
323 int32_t mode;
Marco Nelissen3d21ae32018-02-16 08:24:08 -0800324 if (!meta.findInt32(kKeyCryptoMode, &mode)) {
Hassan Shojaniacefac142017-02-06 21:02:02 -0800325 mode = CryptoPlugin::kMode_AES_CTR;
326 }
327
328 return makeCryptoInfo(numSubSamples,
329 (uint8_t*) key,
330 (uint8_t*) iv,
331 (CryptoPlugin::Mode)mode,
332 (size_t*) cleardata,
333 (size_t*) crypteddata);
334}
335
336} // namespace android
337