NuPlayerDrm.cpp revision dd4ce1877814d6fb005e89021272feb3ccfb81d0
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