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 32namespace android { 33 34KeyedVector<Vector<uint8_t>, String8> Crypto::mUUIDToLibraryPathMap; 35KeyedVector<String8, wp<SharedLibrary> > Crypto::mLibraryPathToOpenLibraryMap; 36Mutex Crypto::mMapLock; 37 38static 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 48Crypto::Crypto() 49 : mInitCheck(NO_INIT), 50 mFactory(NULL), 51 mPlugin(NULL) { 52} 53 54Crypto::~Crypto() { 55 delete mPlugin; 56 mPlugin = NULL; 57 closeFactory(); 58} 59 60void Crypto::closeFactory() { 61 delete mFactory; 62 mFactory = NULL; 63 mLibrary.clear(); 64} 65 66status_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 */ 83void 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 141bool 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 176bool 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 187status_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 206status_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 223bool 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 237ssize_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 270void 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 278status_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