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 "Crypto.h"
24
25#include <binder/IMemory.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("/vendor/lib/mediadrm");
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(
238        bool secure,
239        const uint8_t key[16],
240        const uint8_t iv[16],
241        CryptoPlugin::Mode mode,
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            secure, key, iv, mode, srcPtr, subSamples, numSubSamples, dstPtr,
260            errorDetailMsg);
261}
262
263void Crypto::notifyResolution(uint32_t width, uint32_t height) {
264    Mutex::Autolock autoLock(mLock);
265
266    if (mInitCheck == OK && mPlugin != NULL) {
267        mPlugin->notifyResolution(width, height);
268    }
269}
270
271status_t Crypto::setMediaDrmSession(const Vector<uint8_t> &sessionId) {
272    Mutex::Autolock autoLock(mLock);
273
274    status_t result = NO_INIT;
275    if (mInitCheck == OK && mPlugin != NULL) {
276        result = mPlugin->setMediaDrmSession(sessionId);
277    }
278    return result;
279}
280
281}  // namespace android
282