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 
operator <(const Vector<uint8_t> & lhs,const Vector<uint8_t> & rhs)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 
Crypto()48 Crypto::Crypto()
49     : mInitCheck(NO_INIT),
50       mFactory(NULL),
51       mPlugin(NULL) {
52 }
53 
~Crypto()54 Crypto::~Crypto() {
55     delete mPlugin;
56     mPlugin = NULL;
57     closeFactory();
58 }
59 
closeFactory()60 void Crypto::closeFactory() {
61     delete mFactory;
62     mFactory = NULL;
63     mLibrary.clear();
64 }
65 
initCheck() const66 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  */
findFactoryForScheme(const uint8_t uuid[16])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 
loadLibraryForScheme(const String8 & path,const uint8_t uuid[16])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 
isCryptoSchemeSupported(const uint8_t uuid[16])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 
createPlugin(const uint8_t uuid[16],const void * data,size_t size)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 
destroyPlugin()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 
requiresSecureDecoderComponent(const char * mime) const223 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 
decrypt(const uint8_t key[16],const uint8_t iv[16],CryptoPlugin::Mode mode,const CryptoPlugin::Pattern & pattern,const sp<IMemory> & source,size_t offset,const CryptoPlugin::SubSample * subSamples,size_t numSubSamples,const ICrypto::DestinationBuffer & destination,AString * errorDetailMsg)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 
notifyResolution(uint32_t width,uint32_t height)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 
setMediaDrmSession(const Vector<uint8_t> & sessionId)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