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, Parcel *parcel)
209{
210    // 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    }
227}
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
242    size_t cryptosize = sizeof(CryptoInfo) +
243                        sizeof(CryptoPlugin::SubSample) * numSubSamples;
244    CryptoInfo *ret = (CryptoInfo*) malloc(cryptosize);
245    if (ret == NULL) {
246        ALOGE("couldn't allocate %zu bytes", cryptosize);
247        return NULL;
248    }
249    ret->numSubSamples = numSubSamples;
250    memcpy(ret->key, key, kBlockSize);
251    memcpy(ret->iv, iv, kBlockSize);
252    ret->mode = mode;
253    ret->pattern.mEncryptBlocks = 0;
254    ret->pattern.mSkipBlocks = 0;
255    ret->subSamples = (CryptoPlugin::SubSample*)(ret + 1);
256    CryptoPlugin::SubSample *subSamples = ret->subSamples;
257
258    for (int i = 0; i < numSubSamples; i++) {
259        subSamples[i].mNumBytesOfClearData = (clearbytes == NULL) ? 0 : clearbytes[i];
260        subSamples[i].mNumBytesOfEncryptedData = (encryptedbytes == NULL) ?
261                                                  0 :
262                                                  encryptedbytes[i];
263    }
264
265    return ret;
266}
267
268NuPlayerDrm::CryptoInfo *NuPlayerDrm::getSampleCryptoInfo(sp<MetaData> meta)
269{
270    uint32_t type;
271    const void *crypteddata;
272    size_t cryptedsize;
273
274    if (meta == NULL) {
275        ALOGE("getSampleCryptoInfo: Unexpected. No meta data for sample.");
276        return NULL;
277    }
278
279    if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
280        return NULL;
281    }
282    size_t numSubSamples = cryptedsize / sizeof(size_t);
283
284    if (numSubSamples <= 0) {
285        ALOGE("getSampleCryptoInfo INVALID numSubSamples: %zu", numSubSamples);
286        return NULL;
287    }
288
289    const void *cleardata;
290    size_t clearsize;
291    if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
292        if (clearsize != cryptedsize) {
293            // The two must be of the same length.
294            ALOGE("getSampleCryptoInfo mismatch cryptedsize: %zu != clearsize: %zu",
295                    cryptedsize, clearsize);
296            return NULL;
297        }
298    }
299
300    const void *key;
301    size_t keysize;
302    if (meta->findData(kKeyCryptoKey, &type, &key, &keysize)) {
303        if (keysize != kBlockSize) {
304            ALOGE("getSampleCryptoInfo Keys must be %d bytes in length: %zu",
305                    kBlockSize, keysize);
306            // Keys must be 16 bytes in length.
307            return NULL;
308        }
309    }
310
311    const void *iv;
312    size_t ivsize;
313    if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
314        if (ivsize != kBlockSize) {
315            ALOGE("getSampleCryptoInfo IV must be %d bytes in length: %zu",
316                    kBlockSize, ivsize);
317            // IVs must be 16 bytes in length.
318            return NULL;
319        }
320    }
321
322    int32_t mode;
323    if (!meta->findInt32(kKeyCryptoMode, &mode)) {
324        mode = CryptoPlugin::kMode_AES_CTR;
325    }
326
327    return makeCryptoInfo(numSubSamples,
328            (uint8_t*) key,
329            (uint8_t*) iv,
330            (CryptoPlugin::Mode)mode,
331            (size_t*) cleardata,
332            (size_t*) crypteddata);
333}
334
335}   // namespace android
336
337