• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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             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 
createCryptoAndPlugin(const uint8_t uuid[16],const Vector<uint8_t> & drmSessionId,status_t & status)179 sp<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
retrieveDrmInfo(const void * pssh,size_t psshsize,Parcel * parcel)208 void 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 
makeCryptoInfo(int numSubSamples,uint8_t key[kBlockSize],uint8_t iv[kBlockSize],CryptoPlugin::Mode mode,size_t * clearbytes,size_t * encryptedbytes)233 NuPlayerDrm::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 
getSampleCryptoInfo(sp<MetaData> meta)268 NuPlayerDrm::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