NuPlayerDrm.cpp revision cefac14261a32fb856b0d1ab31541787112e306e
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 <media/IMediaDrmService.h>
24#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
208void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize,
209        const Vector<String8> &mimes_in, Parcel *parcel)
210{
211    // 0) Make mimes a vector of unique items while keeping the original order; video first
212    Vector<String8> mimes;
213    for (size_t j = 0; j < mimes_in.size(); j++) {
214        String8 mime = mimes_in[j];
215        bool exists = false;
216        for (size_t i = 0; i < mimes.size() && !exists; i++) {
217            if (mimes[i] == mime) {
218                exists = true;
219            }
220        } // for i
221
222        if (!exists) {
223            mimes.add(mime);
224        }
225    } // for j
226
227
228    // 1) PSSH bytes
229    parcel->writeUint32(psshsize);
230    parcel->writeByteArray(psshsize, (const uint8_t*)pssh);
231
232    ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  PSSH: size: %zu %s", psshsize,
233            DrmUUID::arrayToHex((uint8_t*)pssh, psshsize).string());
234
235    // 2) supportedDRMs
236    Vector<DrmUUID> supportedDRMs = getSupportedDrmSchemes(pssh, psshsize);
237    parcel->writeUint32(supportedDRMs.size());
238    for (size_t i = 0; i < supportedDRMs.size(); i++) {
239        DrmUUID uuid = supportedDRMs[i];
240        parcel->writeByteArray(DrmUUID::UUID_SIZE, uuid.ptr());
241
242        ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  supportedScheme[%zu] %s", i,
243                uuid.toHexString().string());
244    }
245
246    // TODO: remove mimes after it's removed from Java DrmInfo
247    // 3) mimes
248    parcel->writeUint32(mimes.size());
249    for (size_t i = 0; i < mimes.size(); i++) {
250        // writing as String16 so the Java framework side can unpack it to Java String
251        String16 mime(mimes[i]);
252        parcel->writeString16(mime);
253
254        ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  MIME[%zu] %s",
255                i, mimes[i].string());
256    }
257}
258
259////////////////////////////////////////////////////////////////////////////////////////////
260/// Helpers for NuPlayerDecoder
261////////////////////////////////////////////////////////////////////////////////////////////
262
263NuPlayerDrm::CryptoInfo *NuPlayerDrm::makeCryptoInfo(
264        int numSubSamples,
265        uint8_t key[kBlockSize],
266        uint8_t iv[kBlockSize],
267        CryptoPlugin::Mode mode,
268        size_t *clearbytes,
269        size_t *encryptedbytes)
270{
271    // size needed to store all the crypto data
272    size_t cryptosize = sizeof(CryptoInfo) +
273                        sizeof(CryptoPlugin::SubSample) * numSubSamples;
274    CryptoInfo *ret = (CryptoInfo*) malloc(cryptosize);
275    if (ret == NULL) {
276        ALOGE("couldn't allocate %zu bytes", cryptosize);
277        return NULL;
278    }
279    ret->numSubSamples = numSubSamples;
280    memcpy(ret->key, key, kBlockSize);
281    memcpy(ret->iv, iv, kBlockSize);
282    ret->mode = mode;
283    ret->pattern.mEncryptBlocks = 0;
284    ret->pattern.mSkipBlocks = 0;
285    ret->subSamples = (CryptoPlugin::SubSample*)(ret + 1);
286    CryptoPlugin::SubSample *subSamples = ret->subSamples;
287
288    for (int i = 0; i < numSubSamples; i++) {
289        subSamples[i].mNumBytesOfClearData = (clearbytes == NULL) ? 0 : clearbytes[i];
290        subSamples[i].mNumBytesOfEncryptedData = (encryptedbytes == NULL) ?
291                                                  0 :
292                                                  encryptedbytes[i];
293    }
294
295    return ret;
296}
297
298NuPlayerDrm::CryptoInfo *NuPlayerDrm::getSampleCryptoInfo(sp<MetaData> meta)
299{
300    uint32_t type;
301    const void *crypteddata;
302    size_t cryptedsize;
303
304    if (meta == NULL) {
305        ALOGE("getSampleCryptoInfo: Unexpected. No meta data for sample.");
306        return NULL;
307    }
308
309    if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
310        return NULL;
311    }
312    size_t numSubSamples = cryptedsize / sizeof(size_t);
313
314    if (numSubSamples <= 0) {
315        ALOGE("getSampleCryptoInfo INVALID numSubSamples: %zu", numSubSamples);
316        return NULL;
317    }
318
319    const void *cleardata;
320    size_t clearsize;
321    if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
322        if (clearsize != cryptedsize) {
323            // The two must be of the same length.
324            ALOGE("getSampleCryptoInfo mismatch cryptedsize: %zu != clearsize: %zu",
325                    cryptedsize, clearsize);
326            return NULL;
327        }
328    }
329
330    const void *key;
331    size_t keysize;
332    if (meta->findData(kKeyCryptoKey, &type, &key, &keysize)) {
333        if (keysize != kBlockSize) {
334            ALOGE("getSampleCryptoInfo Keys must be %zu bytes in length: %zu",
335                    kBlockSize, keysize);
336            // Keys must be 16 bytes in length.
337            return NULL;
338        }
339    }
340
341    const void *iv;
342    size_t ivsize;
343    if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
344        if (ivsize != kBlockSize) {
345            ALOGE("getSampleCryptoInfo IV must be %zu bytes in length: %zu",
346                    kBlockSize, ivsize);
347            // IVs must be 16 bytes in length.
348            return NULL;
349        }
350    }
351
352    int32_t mode;
353    if (!meta->findInt32(kKeyCryptoMode, &mode)) {
354        mode = CryptoPlugin::kMode_AES_CTR;
355    }
356
357    return makeCryptoInfo(numSubSamples,
358            (uint8_t*) key,
359            (uint8_t*) iv,
360            (CryptoPlugin::Mode)mode,
361            (size_t*) cleardata,
362            (size_t*) crypteddata);
363}
364
365}   // namespace android
366
367