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