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