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 <media/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             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 
    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
    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 
    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 
    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