NuPlayerDrm.cpp revision e00e39229dedbd424438a3c8a9f88104aaf8bfc6
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 %d 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 %d 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