Home | History | Annotate | Download | only in nuplayer
      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