NdkMediaDrm.cpp revision e419d7cd5c62b4b5866a45d59c5770bb470193c1
1/*
2 * Copyright (C) 2014 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 "NdkMediaDrm"
19
20#include "NdkMediaDrm.h"
21
22#include <utils/Log.h>
23#include <utils/StrongPointer.h>
24#include <gui/Surface.h>
25
26#include <media/IDrm.h>
27#include <media/IDrmClient.h>
28#include <media/stagefright/MediaErrors.h>
29#include <binder/IServiceManager.h>
30#include <media/IMediaPlayerService.h>
31#include <ndk/NdkMediaCrypto.h>
32
33
34using namespace android;
35
36typedef Vector<uint8_t> idvec_t;
37
38struct AMediaDrm {
39    sp<IDrm> mDrm;
40    sp<IDrmClient> mDrmClient;
41    AMediaDrmEventListener mListener;
42    List<idvec_t> mIds;
43    KeyedVector<String8, String8> mQueryResults;
44    Vector<uint8_t> mKeyRequest;
45    Vector<uint8_t> mProvisionRequest;
46    String8 mProvisionUrl;
47    String8 mPropertyString;
48    Vector<uint8_t> mPropertyByteArray;
49    List<Vector<uint8_t> > mSecureStops;
50};
51
52extern "C" {
53
54static media_status_t translateStatus(status_t status) {
55    media_status_t result = AMEDIA_ERROR_UNKNOWN;
56    switch (status) {
57        case OK:
58            result = AMEDIA_OK;
59            break;
60        case android::ERROR_DRM_NOT_PROVISIONED:
61            result = AMEDIA_DRM_NOT_PROVISIONED;
62            break;
63        case android::ERROR_DRM_RESOURCE_BUSY:
64            result = AMEDIA_DRM_RESOURCE_BUSY;
65            break;
66        case android::ERROR_DRM_DEVICE_REVOKED:
67            result = AMEDIA_DRM_DEVICE_REVOKED;
68            break;
69        case android::ERROR_DRM_CANNOT_HANDLE:
70            result = AMEDIA_ERROR_INVALID_PARAMETER;
71            break;
72        case android::ERROR_DRM_TAMPER_DETECTED:
73            result = AMEDIA_DRM_TAMPER_DETECTED;
74            break;
75        case android::ERROR_DRM_SESSION_NOT_OPENED:
76            result = AMEDIA_DRM_SESSION_NOT_OPENED;
77            break;
78        case android::ERROR_DRM_NO_LICENSE:
79            result = AMEDIA_DRM_NEED_KEY;
80            break;
81        case android::ERROR_DRM_LICENSE_EXPIRED:
82            result = AMEDIA_DRM_LICENSE_EXPIRED;
83            break;
84        default:
85            break;
86    }
87    return result;
88}
89
90static sp<IDrm> CreateDrm() {
91    sp<IServiceManager> sm = defaultServiceManager();
92
93    sp<IBinder> binder =
94        sm->getService(String16("media.player"));
95
96    sp<IMediaPlayerService> service =
97        interface_cast<IMediaPlayerService>(binder);
98
99    if (service == NULL) {
100        return NULL;
101    }
102
103    sp<IDrm> drm = service->makeDrm();
104
105    if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) {
106        return NULL;
107    }
108
109    return drm;
110}
111
112
113static sp<IDrm> CreateDrmFromUUID(const AMediaUUID uuid) {
114    sp<IDrm> drm = CreateDrm();
115
116    if (drm == NULL) {
117        return NULL;
118    }
119
120    status_t err = drm->createPlugin(uuid);
121
122    if (err != OK) {
123        return NULL;
124    }
125
126    return drm;
127}
128
129EXPORT
130bool AMediaDrm_isCryptoSchemeSupported(const AMediaUUID uuid, const char *mimeType) {
131    sp<IDrm> drm = CreateDrm();
132
133    if (drm == NULL) {
134        return false;
135    }
136
137    String8 mimeStr = mimeType ? String8(mimeType) : String8("");
138    return drm->isCryptoSchemeSupported(uuid, mimeStr);
139}
140
141EXPORT
142AMediaDrm* AMediaDrm_createByUUID(const AMediaUUID uuid) {
143    AMediaDrm *mObj = new AMediaDrm();
144    mObj->mDrm = CreateDrmFromUUID(uuid);
145    return mObj;
146}
147
148EXPORT
149void AMediaDrm_release(AMediaDrm *mObj) {
150    if (mObj->mDrm != NULL) {
151        mObj->mDrm->setListener(NULL);
152        mObj->mDrm->destroyPlugin();
153        mObj->mDrm.clear();
154    }
155    delete mObj;
156}
157
158#if 0
159void AMediaDrm_setOnEventListener(AMediaDrm *mObj, AMediaDrmEventListener listener) {
160    mObj->mListener = listener;
161}
162#endif
163
164
165static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
166    iter = mObj->mIds.begin();
167    while (iter != mObj->mIds.end()) {
168        if (iter->array() == id.ptr && iter->size() == id.length) {
169            return true;
170        }
171    }
172    return false;
173}
174
175EXPORT
176media_status_t AMediaDrm_openSession(AMediaDrm *mObj, AMediaDrmSessionId &sessionId) {
177    if (!mObj || mObj->mDrm == NULL) {
178        return AMEDIA_ERROR_INVALID_OBJECT;
179    }
180    Vector<uint8_t> session;
181    status_t status = mObj->mDrm->openSession(session);
182    if (status == OK) {
183        mObj->mIds.push_front(session);
184        List<idvec_t>::iterator iter = mObj->mIds.begin();
185        sessionId.ptr = iter->array();
186        sessionId.length = iter->size();
187    }
188    return AMEDIA_OK;
189}
190
191EXPORT
192media_status_t AMediaDrm_closeSession(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId) {
193    if (!mObj || mObj->mDrm == NULL) {
194        return AMEDIA_ERROR_INVALID_OBJECT;
195    }
196
197    List<idvec_t>::iterator iter;
198    if (!findId(mObj, sessionId, iter)) {
199        return AMEDIA_DRM_SESSION_NOT_OPENED;
200    }
201    mObj->mDrm->closeSession(*iter);
202    mObj->mIds.erase(iter);
203    return AMEDIA_OK;
204}
205
206EXPORT
207media_status_t AMediaDrm_getKeyRequest(AMediaDrm *mObj, const AMediaDrmScope &scope,
208        const uint8_t *init, size_t initSize, const char *mimeType, AMediaDrmKeyType keyType,
209        const AMediaDrmKeyValue *optionalParameters, size_t numOptionalParameters,
210        const uint8_t *&keyRequest, size_t &keyRequestSize) {
211
212    if (!mObj || mObj->mDrm == NULL) {
213        return AMEDIA_ERROR_INVALID_OBJECT;
214    }
215    if (!mimeType) {
216        return AMEDIA_ERROR_INVALID_PARAMETER;
217    }
218
219    List<idvec_t>::iterator iter;
220    if (!findId(mObj, scope, iter)) {
221        return AMEDIA_DRM_SESSION_NOT_OPENED;
222    }
223
224    Vector<uint8_t> mdInit;
225    mdInit.appendArray(init, initSize);
226    DrmPlugin::KeyType mdKeyType;
227    switch (keyType) {
228        case KEY_TYPE_STREAMING:
229            mdKeyType = DrmPlugin::kKeyType_Streaming;
230            break;
231        case KEY_TYPE_OFFLINE:
232            mdKeyType = DrmPlugin::kKeyType_Offline;
233            break;
234        case KEY_TYPE_RELEASE:
235            mdKeyType = DrmPlugin::kKeyType_Release;
236            break;
237        default:
238            return AMEDIA_ERROR_INVALID_PARAMETER;
239    }
240    KeyedVector<String8, String8> mdOptionalParameters;
241    for (size_t i = 0; i < numOptionalParameters; i++) {
242        mdOptionalParameters.add(String8(optionalParameters[i].mKey),
243                String8(optionalParameters[i].mValue));
244    }
245    String8 defaultUrl;
246    status_t status = mObj->mDrm->getKeyRequest(*iter, mdInit, String8(mimeType),
247            mdKeyType, mdOptionalParameters, mObj->mKeyRequest, defaultUrl);
248    if (status != OK) {
249        return translateStatus(status);
250    } else {
251        keyRequest = mObj->mKeyRequest.array();
252        keyRequestSize = mObj->mKeyRequest.size();
253    }
254    return AMEDIA_OK;
255}
256
257EXPORT
258media_status_t AMediaDrm_provideKeyResponse(AMediaDrm *mObj, const AMediaDrmScope &scope,
259        const uint8_t *response, size_t responseSize, AMediaDrmKeySetId &keySetId) {
260
261    if (!mObj || mObj->mDrm == NULL) {
262        return AMEDIA_ERROR_INVALID_OBJECT;
263    }
264    if (!response || !responseSize) {
265        return AMEDIA_ERROR_INVALID_PARAMETER;
266    }
267
268    List<idvec_t>::iterator iter;
269    if (!findId(mObj, scope, iter)) {
270        return AMEDIA_DRM_SESSION_NOT_OPENED;
271    }
272    Vector<uint8_t> mdResponse;
273    mdResponse.appendArray(response, responseSize);
274
275    Vector<uint8_t> mdKeySetId;
276    status_t status = mObj->mDrm->provideKeyResponse(*iter, mdResponse, mdKeySetId);
277    if (status == OK) {
278        mObj->mIds.push_front(mdKeySetId);
279        List<idvec_t>::iterator iter = mObj->mIds.begin();
280        keySetId.ptr = iter->array();
281        keySetId.length = iter->size();
282    } else {
283        keySetId.ptr = NULL;
284        keySetId.length = 0;
285    }
286    return AMEDIA_OK;
287}
288
289EXPORT
290media_status_t AMediaDrm_restoreKeys(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
291        const AMediaDrmKeySetId &keySetId) {
292
293    if (!mObj || mObj->mDrm == NULL) {
294        return AMEDIA_ERROR_INVALID_OBJECT;
295    }
296    List<idvec_t>::iterator iter;
297    if (!findId(mObj, sessionId, iter)) {
298        return AMEDIA_DRM_SESSION_NOT_OPENED;
299    }
300    Vector<uint8_t> keySet;
301    keySet.appendArray(keySetId.ptr, keySetId.length);
302    return translateStatus(mObj->mDrm->restoreKeys(*iter, keySet));
303}
304
305EXPORT
306media_status_t AMediaDrm_removeKeys(AMediaDrm *mObj, const AMediaDrmSessionId &keySetId) {
307    if (!mObj || mObj->mDrm == NULL) {
308        return AMEDIA_ERROR_INVALID_OBJECT;
309    }
310    List<idvec_t>::iterator iter;
311    status_t status;
312    if (!findId(mObj, keySetId, iter)) {
313        Vector<uint8_t> keySet;
314        keySet.appendArray(keySetId.ptr, keySetId.length);
315        status = mObj->mDrm->removeKeys(keySet);
316    } else {
317        status = mObj->mDrm->removeKeys(*iter);
318        mObj->mIds.erase(iter);
319    }
320    return translateStatus(status);
321}
322
323EXPORT
324media_status_t AMediaDrm_queryKeyStatus(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
325        AMediaDrmKeyValue *keyValuePairs, size_t &numPairs) {
326
327    if (!mObj || mObj->mDrm == NULL) {
328        return AMEDIA_ERROR_INVALID_OBJECT;
329    }
330    List<idvec_t>::iterator iter;
331    if (!findId(mObj, sessionId, iter)) {
332        return AMEDIA_DRM_SESSION_NOT_OPENED;
333    }
334
335    status_t status = mObj->mDrm->queryKeyStatus(*iter, mObj->mQueryResults);
336    if (status != OK) {
337        numPairs = 0;
338        return translateStatus(status);
339    }
340
341    if (mObj->mQueryResults.size() > numPairs) {
342        numPairs = mObj->mQueryResults.size();
343        return AMEDIA_DRM_SHORT_BUFFER;
344    }
345
346    for (size_t i = 0; i < mObj->mQueryResults.size(); i++) {
347        keyValuePairs[i].mKey = mObj->mQueryResults.keyAt(i).string();
348        keyValuePairs[i].mValue = mObj->mQueryResults.keyAt(i).string();
349    }
350    numPairs = mObj->mQueryResults.size();
351    return AMEDIA_OK;
352}
353
354EXPORT
355media_status_t AMediaDrm_getProvisionRequest(AMediaDrm *mObj, const uint8_t *&provisionRequest,
356        size_t &provisionRequestSize, const char *&serverUrl) {
357    if (!mObj || mObj->mDrm == NULL) {
358        return AMEDIA_ERROR_INVALID_OBJECT;
359    }
360    if (!provisionRequestSize || !serverUrl) {
361        return AMEDIA_ERROR_INVALID_PARAMETER;
362    }
363
364    status_t status = mObj->mDrm->getProvisionRequest(String8(""), String8(""),
365            mObj->mProvisionRequest, mObj->mProvisionUrl);
366    if (status != OK) {
367        return translateStatus(status);
368    } else {
369        provisionRequest = mObj->mProvisionRequest.array();
370        provisionRequestSize = mObj->mProvisionRequest.size();
371        serverUrl = mObj->mProvisionUrl.string();
372    }
373    return AMEDIA_OK;
374}
375
376EXPORT
377media_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *mObj,
378        const uint8_t *response, size_t responseSize) {
379    if (!mObj || mObj->mDrm == NULL) {
380        return AMEDIA_ERROR_INVALID_OBJECT;
381    }
382    if (!response || !responseSize) {
383        return AMEDIA_ERROR_INVALID_PARAMETER;
384    }
385
386    Vector<uint8_t> mdResponse;
387    mdResponse.appendArray(response, responseSize);
388
389    Vector<uint8_t> unused;
390    return translateStatus(mObj->mDrm->provideProvisionResponse(mdResponse, unused, unused));
391}
392
393EXPORT
394media_status_t AMediaDrm_getSecureStops(AMediaDrm *mObj,
395        AMediaDrmSecureStop *secureStops, size_t &numSecureStops) {
396
397    if (!mObj || mObj->mDrm == NULL) {
398        return AMEDIA_ERROR_INVALID_OBJECT;
399    }
400    status_t status = mObj->mDrm->getSecureStops(mObj->mSecureStops);
401    if (status != OK) {
402        numSecureStops = 0;
403        return translateStatus(status);
404    }
405    if (numSecureStops < mObj->mSecureStops.size()) {
406        return AMEDIA_DRM_SHORT_BUFFER;
407    }
408    List<Vector<uint8_t> >::iterator iter = mObj->mSecureStops.begin();
409    size_t i = 0;
410    while (iter != mObj->mSecureStops.end()) {
411        secureStops[i].ptr = iter->array();
412        secureStops[i].length = iter->size();
413        ++iter;
414        ++i;
415    }
416    numSecureStops = mObj->mSecureStops.size();
417    return AMEDIA_OK;
418}
419
420EXPORT
421media_status_t AMediaDrm_releaseSecureStops(AMediaDrm *mObj,
422        const AMediaDrmSecureStop &ssRelease) {
423
424    if (!mObj || mObj->mDrm == NULL) {
425        return AMEDIA_ERROR_INVALID_OBJECT;
426    }
427
428    Vector<uint8_t> release;
429    release.appendArray(ssRelease.ptr, ssRelease.length);
430    return translateStatus(mObj->mDrm->releaseSecureStops(release));
431}
432
433
434EXPORT
435media_status_t AMediaDrm_getPropertyString(AMediaDrm *mObj, const char *propertyName,
436        const char *&propertyValue) {
437
438    if (!mObj || mObj->mDrm == NULL) {
439        return AMEDIA_ERROR_INVALID_OBJECT;
440    }
441
442    status_t status = mObj->mDrm->getPropertyString(String8(propertyName),
443            mObj->mPropertyString);
444
445    if (status == OK) {
446        propertyValue = mObj->mPropertyString.string();
447    } else {
448        propertyValue = NULL;
449    }
450    return translateStatus(status);
451}
452
453EXPORT
454media_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *mObj,
455        const char *propertyName, AMediaDrmByteArray &propertyValue) {
456    if (!mObj || mObj->mDrm == NULL) {
457        return AMEDIA_ERROR_INVALID_OBJECT;
458    }
459
460    status_t status = mObj->mDrm->getPropertyByteArray(String8(propertyName),
461            mObj->mPropertyByteArray);
462
463    if (status == OK) {
464        propertyValue.ptr = mObj->mPropertyByteArray.array();
465        propertyValue.length = mObj->mPropertyByteArray.size();
466    } else {
467        propertyValue.ptr = NULL;
468        propertyValue.length = 0;
469    }
470    return translateStatus(status);
471}
472
473EXPORT
474media_status_t AMediaDrm_setPropertyString(AMediaDrm *mObj,
475        const char *propertyName, const char *value) {
476    if (!mObj || mObj->mDrm == NULL) {
477        return AMEDIA_ERROR_INVALID_OBJECT;
478    }
479
480    return translateStatus(mObj->mDrm->setPropertyString(String8(propertyName),
481                    String8(value)));
482}
483
484EXPORT
485media_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *mObj,
486        const char *propertyName, const uint8_t *value, size_t valueSize) {
487
488    Vector<uint8_t> byteArray;
489    byteArray.appendArray(value, valueSize);
490
491    return translateStatus(mObj->mDrm->getPropertyByteArray(String8(propertyName),
492                    byteArray));
493}
494
495
496static media_status_t encrypt_decrypt_common(AMediaDrm *mObj,
497        const AMediaDrmSessionId &sessionId,
498        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
499        const uint8_t *input, uint8_t *output, size_t dataSize, bool encrypt) {
500
501    if (!mObj || mObj->mDrm == NULL) {
502        return AMEDIA_ERROR_INVALID_OBJECT;
503    }
504    List<idvec_t>::iterator iter;
505    if (!findId(mObj, sessionId, iter)) {
506        return AMEDIA_DRM_SESSION_NOT_OPENED;
507    }
508
509    status_t status = mObj->mDrm->setCipherAlgorithm(*iter, String8(cipherAlgorithm));
510    if (status != OK) {
511        return translateStatus(status);
512    }
513
514    Vector<uint8_t> keyIdVec;
515    const size_t kKeyIdSize = 16;
516    keyIdVec.appendArray(keyId, kKeyIdSize);
517
518    Vector<uint8_t> inputVec;
519    inputVec.appendArray(input, dataSize);
520
521    Vector<uint8_t> ivVec;
522    const size_t kIvSize = 16;
523    ivVec.appendArray(iv, kIvSize);
524
525    Vector<uint8_t> outputVec;
526    if (encrypt) {
527        status_t status = mObj->mDrm->encrypt(*iter, keyIdVec, inputVec, ivVec, outputVec);
528    } else {
529        status_t status = mObj->mDrm->decrypt(*iter, keyIdVec, inputVec, ivVec, outputVec);
530    }
531    if (status == OK) {
532        memcpy(output, outputVec.array(), outputVec.size());
533    }
534    return translateStatus(status);
535}
536
537EXPORT
538media_status_t AMediaDrm_encrypt(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
539        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
540        const uint8_t *input, uint8_t *output, size_t dataSize) {
541    return encrypt_decrypt_common(mObj, sessionId, cipherAlgorithm, keyId, iv,
542            input, output, dataSize, true);
543}
544
545EXPORT
546media_status_t AMediaDrm_decrypt(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
547        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
548        const uint8_t *input, uint8_t *output, size_t dataSize) {
549    return encrypt_decrypt_common(mObj, sessionId, cipherAlgorithm, keyId, iv,
550            input, output, dataSize, false);
551}
552
553EXPORT
554media_status_t AMediaDrm_sign(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
555        const char *macAlgorithm, uint8_t *keyId, uint8_t *message, size_t messageSize,
556        uint8_t *signature, size_t *signatureSize) {
557
558    if (!mObj || mObj->mDrm == NULL) {
559        return AMEDIA_ERROR_INVALID_OBJECT;
560    }
561    List<idvec_t>::iterator iter;
562    if (!findId(mObj, sessionId, iter)) {
563        return AMEDIA_DRM_SESSION_NOT_OPENED;
564    }
565
566    status_t status = mObj->mDrm->setMacAlgorithm(*iter, String8(macAlgorithm));
567    if (status != OK) {
568        return translateStatus(status);
569    }
570
571    Vector<uint8_t> keyIdVec;
572    const size_t kKeyIdSize = 16;
573    keyIdVec.appendArray(keyId, kKeyIdSize);
574
575    Vector<uint8_t> messageVec;
576    messageVec.appendArray(message, messageSize);
577
578    Vector<uint8_t> signatureVec;
579    status = mObj->mDrm->sign(*iter, keyIdVec, messageVec, signatureVec);
580    if (signatureVec.size() > *signatureSize) {
581        return AMEDIA_DRM_SHORT_BUFFER;
582    }
583    if (status == OK) {
584        memcpy(signature, signatureVec.array(), signatureVec.size());
585    }
586    return translateStatus(status);
587}
588
589EXPORT
590media_status_t AMediaDrm_verify(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
591        const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize,
592        const uint8_t *signature, size_t signatureSize) {
593
594    if (!mObj || mObj->mDrm == NULL) {
595        return AMEDIA_ERROR_INVALID_OBJECT;
596    }
597    List<idvec_t>::iterator iter;
598    if (!findId(mObj, sessionId, iter)) {
599        return AMEDIA_DRM_SESSION_NOT_OPENED;
600    }
601
602    status_t status = mObj->mDrm->setMacAlgorithm(*iter, String8(macAlgorithm));
603    if (status != OK) {
604        return translateStatus(status);
605    }
606
607    Vector<uint8_t> keyIdVec;
608    const size_t kKeyIdSize = 16;
609    keyIdVec.appendArray(keyId, kKeyIdSize);
610
611    Vector<uint8_t> messageVec;
612    messageVec.appendArray(message, messageSize);
613
614    Vector<uint8_t> signatureVec;
615    signatureVec.appendArray(signature, signatureSize);
616
617    bool match;
618    status = mObj->mDrm->verify(*iter, keyIdVec, messageVec, signatureVec, match);
619    if (status == OK) {
620        return match ? AMEDIA_OK : AMEDIA_DRM_VERIFY_FAILED;
621    }
622    return translateStatus(status);
623}
624
625} // extern "C"
626
627