Home | History | Annotate | Download | only in libmediadrm
      1 /*
      2  * Copyright (C) 2012 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 "Crypto"
     19 #include <utils/Log.h>
     20 #include <dirent.h>
     21 #include <dlfcn.h>
     22 
     23 #include <binder/IMemory.h>
     24 #include <media/Crypto.h>
     25 #include <media/DrmPluginPath.h>
     26 #include <media/hardware/CryptoAPI.h>
     27 #include <media/stagefright/foundation/ADebug.h>
     28 #include <media/stagefright/foundation/AString.h>
     29 #include <media/stagefright/foundation/hexdump.h>
     30 #include <media/stagefright/MediaErrors.h>
     31 
     32 namespace android {
     33 
     34 KeyedVector<Vector<uint8_t>, String8> Crypto::mUUIDToLibraryPathMap;
     35 KeyedVector<String8, wp<SharedLibrary> > Crypto::mLibraryPathToOpenLibraryMap;
     36 Mutex Crypto::mMapLock;
     37 
     38 static bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs) {
     39     if (lhs.size() < rhs.size()) {
     40         return true;
     41     } else if (lhs.size() > rhs.size()) {
     42         return false;
     43     }
     44 
     45     return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0;
     46 }
     47 
     48 Crypto::Crypto()
     49     : mInitCheck(NO_INIT),
     50       mFactory(NULL),
     51       mPlugin(NULL) {
     52 }
     53 
     54 Crypto::~Crypto() {
     55     delete mPlugin;
     56     mPlugin = NULL;
     57     closeFactory();
     58 }
     59 
     60 void Crypto::closeFactory() {
     61     delete mFactory;
     62     mFactory = NULL;
     63     mLibrary.clear();
     64 }
     65 
     66 status_t Crypto::initCheck() const {
     67     return mInitCheck;
     68 }
     69 
     70 /*
     71  * Search the plugins directory for a plugin that supports the scheme
     72  * specified by uuid
     73  *
     74  * If found:
     75  *    mLibrary holds a strong pointer to the dlopen'd library
     76  *    mFactory is set to the library's factory method
     77  *    mInitCheck is set to OK
     78  *
     79  * If not found:
     80  *    mLibrary is cleared and mFactory are set to NULL
     81  *    mInitCheck is set to an error (!OK)
     82  */
     83 void Crypto::findFactoryForScheme(const uint8_t uuid[16]) {
     84 
     85     closeFactory();
     86 
     87     // lock static maps
     88     Mutex::Autolock autoLock(mMapLock);
     89 
     90     // first check cache
     91     Vector<uint8_t> uuidVector;
     92     uuidVector.appendArray(uuid, sizeof(uuid[0]) * 16);
     93     ssize_t index = mUUIDToLibraryPathMap.indexOfKey(uuidVector);
     94     if (index >= 0) {
     95         if (loadLibraryForScheme(mUUIDToLibraryPathMap[index], uuid)) {
     96             mInitCheck = OK;
     97             return;
     98         } else {
     99             ALOGE("Failed to load from cached library path!");
    100             mInitCheck = ERROR_UNSUPPORTED;
    101             return;
    102         }
    103     }
    104 
    105     // no luck, have to search
    106     String8 dirPath(getDrmPluginPath());
    107     String8 pluginPath;
    108 
    109     DIR* pDir = opendir(dirPath.string());
    110     if (pDir) {
    111         struct dirent* pEntry;
    112         while ((pEntry = readdir(pDir))) {
    113 
    114             pluginPath = dirPath + "/" + pEntry->d_name;
    115 
    116             if (pluginPath.getPathExtension() == ".so") {
    117 
    118                 if (loadLibraryForScheme(pluginPath, uuid)) {
    119                     mUUIDToLibraryPathMap.add(uuidVector, pluginPath);
    120                     mInitCheck = OK;
    121                     closedir(pDir);
    122                     return;
    123                 }
    124             }
    125         }
    126 
    127         closedir(pDir);
    128     }
    129 
    130     // try the legacy libdrmdecrypt.so
    131     pluginPath = "libdrmdecrypt.so";
    132     if (loadLibraryForScheme(pluginPath, uuid)) {
    133         mUUIDToLibraryPathMap.add(uuidVector, pluginPath);
    134         mInitCheck = OK;
    135         return;
    136     }
    137 
    138     mInitCheck = ERROR_UNSUPPORTED;
    139 }
    140 
    141 bool Crypto::loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]) {
    142 
    143     // get strong pointer to open shared library
    144     ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path);
    145     if (index >= 0) {
    146         mLibrary = mLibraryPathToOpenLibraryMap[index].promote();
    147     } else {
    148         index = mLibraryPathToOpenLibraryMap.add(path, NULL);
    149     }
    150 
    151     if (!mLibrary.get()) {
    152         mLibrary = new SharedLibrary(path);
    153         if (!*mLibrary) {
    154             ALOGE("loadLibraryForScheme failed:%s", mLibrary->lastError());
    155             return false;
    156         }
    157 
    158         mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary);
    159     }
    160 
    161     typedef CryptoFactory *(*CreateCryptoFactoryFunc)();
    162 
    163     CreateCryptoFactoryFunc createCryptoFactory =
    164         (CreateCryptoFactoryFunc)mLibrary->lookup("createCryptoFactory");
    165 
    166     if (createCryptoFactory == NULL ||
    167         (mFactory = createCryptoFactory()) == NULL ||
    168         !mFactory->isCryptoSchemeSupported(uuid)) {
    169         ALOGE("createCryptoFactory failed:%s", mLibrary->lastError());
    170         closeFactory();
    171         return false;
    172     }
    173     return true;
    174 }
    175 
    176 bool Crypto::isCryptoSchemeSupported(const uint8_t uuid[16]) {
    177     Mutex::Autolock autoLock(mLock);
    178 
    179     if (mFactory && mFactory->isCryptoSchemeSupported(uuid)) {
    180         return true;
    181     }
    182 
    183     findFactoryForScheme(uuid);
    184     return (mInitCheck == OK);
    185 }
    186 
    187 status_t Crypto::createPlugin(
    188         const uint8_t uuid[16], const void *data, size_t size) {
    189     Mutex::Autolock autoLock(mLock);
    190 
    191     if (mPlugin != NULL) {
    192         return -EINVAL;
    193     }
    194 
    195     if (!mFactory || !mFactory->isCryptoSchemeSupported(uuid)) {
    196         findFactoryForScheme(uuid);
    197     }
    198 
    199     if (mInitCheck != OK) {
    200         return mInitCheck;
    201     }
    202 
    203     return mFactory->createPlugin(uuid, data, size, &mPlugin);
    204 }
    205 
    206 status_t Crypto::destroyPlugin() {
    207     Mutex::Autolock autoLock(mLock);
    208 
    209     if (mInitCheck != OK) {
    210         return mInitCheck;
    211     }
    212 
    213     if (mPlugin == NULL) {
    214         return -EINVAL;
    215     }
    216 
    217     delete mPlugin;
    218     mPlugin = NULL;
    219 
    220     return OK;
    221 }
    222 
    223 bool Crypto::requiresSecureDecoderComponent(const char *mime) const {
    224     Mutex::Autolock autoLock(mLock);
    225 
    226     if (mInitCheck != OK) {
    227         return mInitCheck;
    228     }
    229 
    230     if (mPlugin == NULL) {
    231         return -EINVAL;
    232     }
    233 
    234     return mPlugin->requiresSecureDecoderComponent(mime);
    235 }
    236 
    237 ssize_t Crypto::decrypt(const uint8_t key[16], const uint8_t iv[16],
    238         CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern,
    239         const sp<IMemory> &source, size_t offset,
    240         const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
    241         const ICrypto::DestinationBuffer &destination, AString *errorDetailMsg) {
    242 
    243     Mutex::Autolock autoLock(mLock);
    244 
    245     if (mInitCheck != OK) {
    246         return mInitCheck;
    247     }
    248 
    249     if (mPlugin == NULL) {
    250         return -EINVAL;
    251     }
    252 
    253     const void *srcPtr = static_cast<uint8_t *>(source->pointer()) + offset;
    254 
    255     void *destPtr;
    256     bool secure = false;
    257     if (destination.mType == kDestinationTypeNativeHandle) {
    258         destPtr = static_cast<void *>(destination.mHandle);
    259         secure = true;
    260     } else {
    261         destPtr = destination.mSharedMemory->pointer();
    262     }
    263 
    264     ssize_t result = mPlugin->decrypt(secure, key, iv, mode, pattern, srcPtr, subSamples,
    265             numSubSamples, destPtr, errorDetailMsg);
    266 
    267     return result;
    268 }
    269 
    270 void Crypto::notifyResolution(uint32_t width, uint32_t height) {
    271     Mutex::Autolock autoLock(mLock);
    272 
    273     if (mInitCheck == OK && mPlugin != NULL) {
    274         mPlugin->notifyResolution(width, height);
    275     }
    276 }
    277 
    278 status_t Crypto::setMediaDrmSession(const Vector<uint8_t> &sessionId) {
    279     Mutex::Autolock autoLock(mLock);
    280 
    281     status_t result = NO_INIT;
    282     if (mInitCheck == OK && mPlugin != NULL) {
    283         result = mPlugin->setMediaDrmSession(sessionId);
    284     }
    285     return result;
    286 }
    287 
    288 }  // namespace android
    289