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