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 <mediadrm/IMediaDrmService.h> 24 #include <utils/Log.h> 25 26 27 namespace android { 28 29 // static helpers - internal 30 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 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 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 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 bool isSupported = false; 163 status = drm->isCryptoSchemeSupported(uuid.ptr(), String8(), 164 DrmPlugin::kSecurityLevelUnknown, &isSupported); 165 if (status == OK && isSupported) { 166 supportedDRMs.add(uuid); 167 } 168 } 169 170 drm.clear(); 171 } else { 172 ALOGE("getSupportedDrmSchemes: Can't create Drm obj: %d", status); 173 } 174 175 ALOGV("getSupportedDrmSchemes: psshDRMs: %zu supportedDRMs: %zu", 176 psshDRMs.size(), supportedDRMs.size()); 177 178 return supportedDRMs; 179 } 180 181 // static helpers - public 182 183 sp<ICrypto> NuPlayerDrm::createCryptoAndPlugin(const uint8_t uuid[16], 184 const Vector<uint8_t> &drmSessionId, status_t &status) 185 { 186 // Extra check 187 if (drmSessionId.isEmpty()) { 188 status = INVALID_OPERATION; 189 ALOGE("createCryptoAndPlugin: Failed. Empty drmSessionId. status: %d", status); 190 return NULL; 191 } 192 193 status = OK; 194 sp<ICrypto> crypto = createCrypto(&status); 195 if (crypto == NULL) { 196 ALOGE("createCryptoAndPlugin: createCrypto failed. status: %d", status); 197 return NULL; 198 } 199 ALOGV("createCryptoAndPlugin: createCrypto succeeded"); 200 201 status = crypto->createPlugin(uuid, drmSessionId.array(), drmSessionId.size()); 202 if (status != OK) { 203 ALOGE("createCryptoAndPlugin: createCryptoPlugin failed. status: %d", status); 204 // crypto will clean itself when leaving the current scope 205 return NULL; 206 } 207 208 return crypto; 209 } 210 211 // Parcel has only private copy constructor so passing it in rather than returning 212 void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel) 213 { 214 // 1) PSSH bytes 215 parcel->writeUint32(psshsize); 216 parcel->writeByteArray(psshsize, (const uint8_t*)pssh); 217 218 ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO PSSH: size: %zu %s", psshsize, 219 DrmUUID::arrayToHex((uint8_t*)pssh, psshsize).string()); 220 221 // 2) supportedDRMs 222 Vector<DrmUUID> supportedDRMs = getSupportedDrmSchemes(pssh, psshsize); 223 parcel->writeUint32(supportedDRMs.size()); 224 for (size_t i = 0; i < supportedDRMs.size(); i++) { 225 DrmUUID uuid = supportedDRMs[i]; 226 parcel->writeByteArray(DrmUUID::UUID_SIZE, uuid.ptr()); 227 228 ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i, 229 uuid.toHexString().string()); 230 } 231 } 232 233 //////////////////////////////////////////////////////////////////////////////////////////// 234 /// Helpers for NuPlayerDecoder 235 //////////////////////////////////////////////////////////////////////////////////////////// 236 237 NuPlayerDrm::CryptoInfo *NuPlayerDrm::makeCryptoInfo( 238 int numSubSamples, 239 uint8_t key[kBlockSize], 240 uint8_t iv[kBlockSize], 241 CryptoPlugin::Mode mode, 242 size_t *clearbytes, 243 size_t *encryptedbytes) 244 { 245 // size needed to store all the crypto data 246 size_t cryptosize; 247 // sizeof(CryptoInfo) + sizeof(CryptoPlugin::SubSample) * numSubSamples; 248 if (__builtin_mul_overflow(sizeof(CryptoPlugin::SubSample), numSubSamples, &cryptosize) || 249 __builtin_add_overflow(cryptosize, sizeof(CryptoInfo), &cryptosize)) { 250 ALOGE("crypto size overflow"); 251 return NULL; 252 } 253 254 CryptoInfo *ret = (CryptoInfo*) malloc(cryptosize); 255 if (ret == NULL) { 256 ALOGE("couldn't allocate %zu bytes", cryptosize); 257 return NULL; 258 } 259 ret->numSubSamples = numSubSamples; 260 memcpy(ret->key, key, kBlockSize); 261 memcpy(ret->iv, iv, kBlockSize); 262 ret->mode = mode; 263 ret->pattern.mEncryptBlocks = 0; 264 ret->pattern.mSkipBlocks = 0; 265 ret->subSamples = (CryptoPlugin::SubSample*)(ret + 1); 266 CryptoPlugin::SubSample *subSamples = ret->subSamples; 267 268 for (int i = 0; i < numSubSamples; i++) { 269 subSamples[i].mNumBytesOfClearData = (clearbytes == NULL) ? 0 : clearbytes[i]; 270 subSamples[i].mNumBytesOfEncryptedData = (encryptedbytes == NULL) ? 271 0 : 272 encryptedbytes[i]; 273 } 274 275 return ret; 276 } 277 278 NuPlayerDrm::CryptoInfo *NuPlayerDrm::getSampleCryptoInfo(MetaDataBase &meta) 279 { 280 uint32_t type; 281 const void *crypteddata; 282 size_t cryptedsize; 283 284 if (!meta.findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) { 285 return NULL; 286 } 287 size_t numSubSamples = cryptedsize / sizeof(size_t); 288 289 if (numSubSamples <= 0) { 290 ALOGE("getSampleCryptoInfo INVALID numSubSamples: %zu", numSubSamples); 291 return NULL; 292 } 293 294 const void *cleardata; 295 size_t clearsize; 296 if (meta.findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) { 297 if (clearsize != cryptedsize) { 298 // The two must be of the same length. 299 ALOGE("getSampleCryptoInfo mismatch cryptedsize: %zu != clearsize: %zu", 300 cryptedsize, clearsize); 301 return NULL; 302 } 303 } 304 305 const void *key; 306 size_t keysize; 307 if (meta.findData(kKeyCryptoKey, &type, &key, &keysize)) { 308 if (keysize != kBlockSize) { 309 ALOGE("getSampleCryptoInfo Keys must be %d bytes in length: %zu", 310 kBlockSize, keysize); 311 // Keys must be 16 bytes in length. 312 return NULL; 313 } 314 } 315 316 const void *iv; 317 size_t ivsize; 318 if (meta.findData(kKeyCryptoIV, &type, &iv, &ivsize)) { 319 if (ivsize != kBlockSize) { 320 ALOGE("getSampleCryptoInfo IV must be %d bytes in length: %zu", 321 kBlockSize, ivsize); 322 // IVs must be 16 bytes in length. 323 return NULL; 324 } 325 } 326 327 int32_t mode; 328 if (!meta.findInt32(kKeyCryptoMode, &mode)) { 329 mode = CryptoPlugin::kMode_AES_CTR; 330 } 331 332 return makeCryptoInfo(numSubSamples, 333 (uint8_t*) key, 334 (uint8_t*) iv, 335 (CryptoPlugin::Mode)mode, 336 (size_t*) cleardata, 337 (size_t*) crypteddata); 338 } 339 340 } // namespace android 341 342