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