1/*
2 * Copyright (C) 2013 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 "Drm"
19#include <utils/Log.h>
20
21#include <dirent.h>
22#include <dlfcn.h>
23
24#include "Drm.h"
25
26#include <media/drm/DrmAPI.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> Drm::mUUIDToLibraryPathMap;
35KeyedVector<String8, wp<SharedLibrary> > Drm::mLibraryPathToOpenLibraryMap;
36Mutex Drm::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
48Drm::Drm()
49    : mInitCheck(NO_INIT),
50      mListener(NULL),
51      mFactory(NULL),
52      mPlugin(NULL) {
53}
54
55Drm::~Drm() {
56    delete mPlugin;
57    mPlugin = NULL;
58    closeFactory();
59}
60
61void Drm::closeFactory() {
62    delete mFactory;
63    mFactory = NULL;
64    mLibrary.clear();
65}
66
67status_t Drm::initCheck() const {
68    return mInitCheck;
69}
70
71status_t Drm::setListener(const sp<IDrmClient>& listener)
72{
73    Mutex::Autolock lock(mEventLock);
74    if (mListener != NULL){
75        mListener->asBinder()->unlinkToDeath(this);
76    }
77    if (listener != NULL) {
78        listener->asBinder()->linkToDeath(this);
79    }
80    mListener = listener;
81    return NO_ERROR;
82}
83
84void Drm::sendEvent(DrmPlugin::EventType eventType, int extra,
85                    Vector<uint8_t> const *sessionId,
86                    Vector<uint8_t> const *data)
87{
88    mEventLock.lock();
89    sp<IDrmClient> listener = mListener;
90    mEventLock.unlock();
91
92    if (listener != NULL) {
93        Parcel obj;
94        if (sessionId && sessionId->size()) {
95            obj.writeInt32(sessionId->size());
96            obj.write(sessionId->array(), sessionId->size());
97        } else {
98            obj.writeInt32(0);
99        }
100
101        if (data && data->size()) {
102            obj.writeInt32(data->size());
103            obj.write(data->array(), data->size());
104        } else {
105            obj.writeInt32(0);
106        }
107
108        Mutex::Autolock lock(mNotifyLock);
109        listener->notify(eventType, extra, &obj);
110    }
111}
112
113/*
114 * Search the plugins directory for a plugin that supports the scheme
115 * specified by uuid
116 *
117 * If found:
118 *    mLibrary holds a strong pointer to the dlopen'd library
119 *    mFactory is set to the library's factory method
120 *    mInitCheck is set to OK
121 *
122 * If not found:
123 *    mLibrary is cleared and mFactory are set to NULL
124 *    mInitCheck is set to an error (!OK)
125 */
126void Drm::findFactoryForScheme(const uint8_t uuid[16]) {
127
128    closeFactory();
129
130    // lock static maps
131    Mutex::Autolock autoLock(mMapLock);
132
133    // first check cache
134    Vector<uint8_t> uuidVector;
135    uuidVector.appendArray(uuid, sizeof(uuid));
136    ssize_t index = mUUIDToLibraryPathMap.indexOfKey(uuidVector);
137    if (index >= 0) {
138        if (loadLibraryForScheme(mUUIDToLibraryPathMap[index], uuid)) {
139            mInitCheck = OK;
140            return;
141        } else {
142            ALOGE("Failed to load from cached library path!");
143            mInitCheck = ERROR_UNSUPPORTED;
144            return;
145        }
146    }
147
148    // no luck, have to search
149    String8 dirPath("/vendor/lib/mediadrm");
150    DIR* pDir = opendir(dirPath.string());
151
152    if (pDir == NULL) {
153        mInitCheck = ERROR_UNSUPPORTED;
154        ALOGE("Failed to open plugin directory %s", dirPath.string());
155        return;
156    }
157
158
159    struct dirent* pEntry;
160    while ((pEntry = readdir(pDir))) {
161
162        String8 pluginPath = dirPath + "/" + pEntry->d_name;
163
164        if (pluginPath.getPathExtension() == ".so") {
165
166            if (loadLibraryForScheme(pluginPath, uuid)) {
167                mUUIDToLibraryPathMap.add(uuidVector, pluginPath);
168                mInitCheck = OK;
169                closedir(pDir);
170                return;
171            }
172        }
173    }
174
175    closedir(pDir);
176
177    ALOGE("Failed to find drm plugin");
178    mInitCheck = ERROR_UNSUPPORTED;
179}
180
181bool Drm::loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]) {
182
183    // get strong pointer to open shared library
184    ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path);
185    if (index >= 0) {
186        mLibrary = mLibraryPathToOpenLibraryMap[index].promote();
187    } else {
188        index = mLibraryPathToOpenLibraryMap.add(path, NULL);
189    }
190
191    if (!mLibrary.get()) {
192        mLibrary = new SharedLibrary(path);
193        if (!*mLibrary) {
194            return false;
195        }
196
197        mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary);
198    }
199
200    typedef DrmFactory *(*CreateDrmFactoryFunc)();
201
202    CreateDrmFactoryFunc createDrmFactory =
203        (CreateDrmFactoryFunc)mLibrary->lookup("createDrmFactory");
204
205    if (createDrmFactory == NULL ||
206        (mFactory = createDrmFactory()) == NULL ||
207        !mFactory->isCryptoSchemeSupported(uuid)) {
208        closeFactory();
209        return false;
210    }
211    return true;
212}
213
214bool Drm::isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
215
216    Mutex::Autolock autoLock(mLock);
217
218    if (!mFactory || !mFactory->isCryptoSchemeSupported(uuid)) {
219        findFactoryForScheme(uuid);
220        if (mInitCheck != OK) {
221            return false;
222        }
223    }
224
225    if (mimeType != "") {
226        return mFactory->isContentTypeSupported(mimeType);
227    }
228
229    return true;
230}
231
232status_t Drm::createPlugin(const uint8_t uuid[16]) {
233    Mutex::Autolock autoLock(mLock);
234
235    if (mPlugin != NULL) {
236        return -EINVAL;
237    }
238
239    if (!mFactory || !mFactory->isCryptoSchemeSupported(uuid)) {
240        findFactoryForScheme(uuid);
241    }
242
243    if (mInitCheck != OK) {
244        return mInitCheck;
245    }
246
247    status_t result = mFactory->createDrmPlugin(uuid, &mPlugin);
248    mPlugin->setListener(this);
249    return result;
250}
251
252status_t Drm::destroyPlugin() {
253    Mutex::Autolock autoLock(mLock);
254
255    if (mInitCheck != OK) {
256        return mInitCheck;
257    }
258
259    if (mPlugin == NULL) {
260        return -EINVAL;
261    }
262
263    delete mPlugin;
264    mPlugin = NULL;
265
266    return OK;
267}
268
269status_t Drm::openSession(Vector<uint8_t> &sessionId) {
270    Mutex::Autolock autoLock(mLock);
271
272    if (mInitCheck != OK) {
273        return mInitCheck;
274    }
275
276    if (mPlugin == NULL) {
277        return -EINVAL;
278    }
279
280    return mPlugin->openSession(sessionId);
281}
282
283status_t Drm::closeSession(Vector<uint8_t> const &sessionId) {
284    Mutex::Autolock autoLock(mLock);
285
286    if (mInitCheck != OK) {
287        return mInitCheck;
288    }
289
290    if (mPlugin == NULL) {
291        return -EINVAL;
292    }
293
294    return mPlugin->closeSession(sessionId);
295}
296
297status_t Drm::getKeyRequest(Vector<uint8_t> const &sessionId,
298                            Vector<uint8_t> const &initData,
299                            String8 const &mimeType, DrmPlugin::KeyType keyType,
300                            KeyedVector<String8, String8> const &optionalParameters,
301                            Vector<uint8_t> &request, String8 &defaultUrl) {
302    Mutex::Autolock autoLock(mLock);
303
304    if (mInitCheck != OK) {
305        return mInitCheck;
306    }
307
308    if (mPlugin == NULL) {
309        return -EINVAL;
310    }
311
312    return mPlugin->getKeyRequest(sessionId, initData, mimeType, keyType,
313                                  optionalParameters, request, defaultUrl);
314}
315
316status_t Drm::provideKeyResponse(Vector<uint8_t> const &sessionId,
317                                 Vector<uint8_t> const &response,
318                                 Vector<uint8_t> &keySetId) {
319    Mutex::Autolock autoLock(mLock);
320
321    if (mInitCheck != OK) {
322        return mInitCheck;
323    }
324
325    if (mPlugin == NULL) {
326        return -EINVAL;
327    }
328
329    return mPlugin->provideKeyResponse(sessionId, response, keySetId);
330}
331
332status_t Drm::removeKeys(Vector<uint8_t> const &keySetId) {
333    Mutex::Autolock autoLock(mLock);
334
335    if (mInitCheck != OK) {
336        return mInitCheck;
337    }
338
339    if (mPlugin == NULL) {
340        return -EINVAL;
341    }
342
343    return mPlugin->removeKeys(keySetId);
344}
345
346status_t Drm::restoreKeys(Vector<uint8_t> const &sessionId,
347                          Vector<uint8_t> const &keySetId) {
348    Mutex::Autolock autoLock(mLock);
349
350    if (mInitCheck != OK) {
351        return mInitCheck;
352    }
353
354    if (mPlugin == NULL) {
355        return -EINVAL;
356    }
357
358    return mPlugin->restoreKeys(sessionId, keySetId);
359}
360
361status_t Drm::queryKeyStatus(Vector<uint8_t> const &sessionId,
362                             KeyedVector<String8, String8> &infoMap) const {
363    Mutex::Autolock autoLock(mLock);
364
365    if (mInitCheck != OK) {
366        return mInitCheck;
367    }
368
369    if (mPlugin == NULL) {
370        return -EINVAL;
371    }
372
373    return mPlugin->queryKeyStatus(sessionId, infoMap);
374}
375
376status_t Drm::getProvisionRequest(Vector<uint8_t> &request, String8 &defaultUrl) {
377    Mutex::Autolock autoLock(mLock);
378
379    if (mInitCheck != OK) {
380        return mInitCheck;
381    }
382
383    if (mPlugin == NULL) {
384        return -EINVAL;
385    }
386
387    return mPlugin->getProvisionRequest(request, defaultUrl);
388}
389
390status_t Drm::provideProvisionResponse(Vector<uint8_t> const &response) {
391    Mutex::Autolock autoLock(mLock);
392
393    if (mInitCheck != OK) {
394        return mInitCheck;
395    }
396
397    if (mPlugin == NULL) {
398        return -EINVAL;
399    }
400
401    return mPlugin->provideProvisionResponse(response);
402}
403
404
405status_t Drm::getSecureStops(List<Vector<uint8_t> > &secureStops) {
406    Mutex::Autolock autoLock(mLock);
407
408    if (mInitCheck != OK) {
409        return mInitCheck;
410    }
411
412    if (mPlugin == NULL) {
413        return -EINVAL;
414    }
415
416    return mPlugin->getSecureStops(secureStops);
417}
418
419status_t Drm::releaseSecureStops(Vector<uint8_t> const &ssRelease) {
420    Mutex::Autolock autoLock(mLock);
421
422    if (mInitCheck != OK) {
423        return mInitCheck;
424    }
425
426    if (mPlugin == NULL) {
427        return -EINVAL;
428    }
429
430    return mPlugin->releaseSecureStops(ssRelease);
431}
432
433status_t Drm::getPropertyString(String8 const &name, String8 &value ) const {
434    Mutex::Autolock autoLock(mLock);
435
436    if (mInitCheck != OK) {
437        return mInitCheck;
438    }
439
440    if (mPlugin == NULL) {
441        return -EINVAL;
442    }
443
444    return mPlugin->getPropertyString(name, value);
445}
446
447status_t Drm::getPropertyByteArray(String8 const &name, Vector<uint8_t> &value ) const {
448    Mutex::Autolock autoLock(mLock);
449
450    if (mInitCheck != OK) {
451        return mInitCheck;
452    }
453
454    if (mPlugin == NULL) {
455        return -EINVAL;
456    }
457
458    return mPlugin->getPropertyByteArray(name, value);
459}
460
461status_t Drm::setPropertyString(String8 const &name, String8 const &value ) const {
462    Mutex::Autolock autoLock(mLock);
463
464    if (mInitCheck != OK) {
465        return mInitCheck;
466    }
467
468    if (mPlugin == NULL) {
469        return -EINVAL;
470    }
471
472    return mPlugin->setPropertyString(name, value);
473}
474
475status_t Drm::setPropertyByteArray(String8 const &name,
476                                   Vector<uint8_t> const &value ) const {
477    Mutex::Autolock autoLock(mLock);
478
479    if (mInitCheck != OK) {
480        return mInitCheck;
481    }
482
483    if (mPlugin == NULL) {
484        return -EINVAL;
485    }
486
487    return mPlugin->setPropertyByteArray(name, value);
488}
489
490
491status_t Drm::setCipherAlgorithm(Vector<uint8_t> const &sessionId,
492                                 String8 const &algorithm) {
493    Mutex::Autolock autoLock(mLock);
494
495    if (mInitCheck != OK) {
496        return mInitCheck;
497    }
498
499    if (mPlugin == NULL) {
500        return -EINVAL;
501    }
502
503    return mPlugin->setCipherAlgorithm(sessionId, algorithm);
504}
505
506status_t Drm::setMacAlgorithm(Vector<uint8_t> const &sessionId,
507                              String8 const &algorithm) {
508    Mutex::Autolock autoLock(mLock);
509
510    if (mInitCheck != OK) {
511        return mInitCheck;
512    }
513
514    if (mPlugin == NULL) {
515        return -EINVAL;
516    }
517
518    return mPlugin->setMacAlgorithm(sessionId, algorithm);
519}
520
521status_t Drm::encrypt(Vector<uint8_t> const &sessionId,
522                      Vector<uint8_t> const &keyId,
523                      Vector<uint8_t> const &input,
524                      Vector<uint8_t> const &iv,
525                      Vector<uint8_t> &output) {
526    Mutex::Autolock autoLock(mLock);
527
528    if (mInitCheck != OK) {
529        return mInitCheck;
530    }
531
532    if (mPlugin == NULL) {
533        return -EINVAL;
534    }
535
536    return mPlugin->encrypt(sessionId, keyId, input, iv, output);
537}
538
539status_t Drm::decrypt(Vector<uint8_t> const &sessionId,
540                      Vector<uint8_t> const &keyId,
541                      Vector<uint8_t> const &input,
542                      Vector<uint8_t> const &iv,
543                      Vector<uint8_t> &output) {
544    Mutex::Autolock autoLock(mLock);
545
546    if (mInitCheck != OK) {
547        return mInitCheck;
548    }
549
550    if (mPlugin == NULL) {
551        return -EINVAL;
552    }
553
554    return mPlugin->decrypt(sessionId, keyId, input, iv, output);
555}
556
557status_t Drm::sign(Vector<uint8_t> const &sessionId,
558                   Vector<uint8_t> const &keyId,
559                   Vector<uint8_t> const &message,
560                   Vector<uint8_t> &signature) {
561    Mutex::Autolock autoLock(mLock);
562
563    if (mInitCheck != OK) {
564        return mInitCheck;
565    }
566
567    if (mPlugin == NULL) {
568        return -EINVAL;
569    }
570
571    return mPlugin->sign(sessionId, keyId, message, signature);
572}
573
574status_t Drm::verify(Vector<uint8_t> const &sessionId,
575                     Vector<uint8_t> const &keyId,
576                     Vector<uint8_t> const &message,
577                     Vector<uint8_t> const &signature,
578                     bool &match) {
579    Mutex::Autolock autoLock(mLock);
580
581    if (mInitCheck != OK) {
582        return mInitCheck;
583    }
584
585    if (mPlugin == NULL) {
586        return -EINVAL;
587    }
588
589    return mPlugin->verify(sessionId, keyId, message, signature, match);
590}
591
592void Drm::binderDied(const wp<IBinder> &the_late_who)
593{
594    delete mPlugin;
595    mPlugin = NULL;
596    closeFactory();
597    mListener.clear();
598}
599
600}  // namespace android
601