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 package android.media; 18 19 import java.lang.annotation.Retention; 20 import java.lang.annotation.RetentionPolicy; 21 import java.lang.ref.WeakReference; 22 import java.util.ArrayList; 23 import java.util.HashMap; 24 import java.util.List; 25 import java.util.UUID; 26 import android.annotation.IntDef; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.annotation.StringDef; 30 import android.annotation.SystemApi; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.Parcel; 35 import android.util.Log; 36 37 /** 38 * MediaDrm can be used to obtain keys for decrypting protected media streams, in 39 * conjunction with {@link android.media.MediaCrypto}. The MediaDrm APIs 40 * are designed to support the ISO/IEC 23001-7: Common Encryption standard, but 41 * may also be used to implement other encryption schemes. 42 * <p> 43 * Encrypted content is prepared using an encryption server and stored in a content 44 * library. The encrypted content is streamed or downloaded from the content library to 45 * client devices via content servers. Licenses to view the content are obtained from 46 * a License Server. 47 * <p> 48 * <p><img src="../../../images/mediadrm_overview.png" 49 * alt="MediaDrm Overview diagram" 50 * border="0" /></p> 51 * <p> 52 * Keys are requested from the license server using a key request. The key 53 * response is delivered to the client app, which provides the response to the 54 * MediaDrm API. 55 * <p> 56 * A Provisioning server may be required to distribute device-unique credentials to 57 * the devices. 58 * <p> 59 * Enforcing requirements related to the number of devices that may play content 60 * simultaneously can be performed either through key renewal or using the secure 61 * stop methods. 62 * <p> 63 * The following sequence diagram shows the interactions between the objects 64 * involved while playing back encrypted content: 65 * <p> 66 * <p><img src="../../../images/mediadrm_decryption_sequence.png" 67 * alt="MediaDrm Overview diagram" 68 * border="0" /></p> 69 * <p> 70 * The app first constructs {@link android.media.MediaExtractor} and 71 * {@link android.media.MediaCodec} objects. It accesses the DRM-scheme-identifying UUID, 72 * typically from metadata in the content, and uses this UUID to construct an instance 73 * of a MediaDrm object that is able to support the DRM scheme required by the content. 74 * Crypto schemes are assigned 16 byte UUIDs. The method {@link #isCryptoSchemeSupported} 75 * can be used to query if a given scheme is supported on the device. 76 * <p> 77 * The app calls {@link #openSession} to generate a sessionId that will uniquely identify 78 * the session in subsequent interactions. The app next uses the MediaDrm object to 79 * obtain a key request message and send it to the license server, then provide 80 * the server's response to the MediaDrm object. 81 * <p> 82 * Once the app has a sessionId, it can construct a MediaCrypto object from the UUID and 83 * sessionId. The MediaCrypto object is registered with the MediaCodec in the 84 * {@link MediaCodec.#configure} method to enable the codec to decrypt content. 85 * <p> 86 * When the app has constructed {@link android.media.MediaExtractor}, 87 * {@link android.media.MediaCodec} and {@link android.media.MediaCrypto} objects, 88 * it proceeds to pull samples from the extractor and queue them into the decoder. For 89 * encrypted content, the samples returned from the extractor remain encrypted, they 90 * are only decrypted when the samples are delivered to the decoder. 91 * <p> 92 * MediaDrm methods throw {@link android.media.MediaDrm.MediaDrmStateException} 93 * when a method is called on a MediaDrm object that has had an unrecoverable failure 94 * in the DRM plugin or security hardware. 95 * {@link android.media.MediaDrm.MediaDrmStateException} extends 96 * {@link java.lang.IllegalStateException} with the addition of a developer-readable 97 * diagnostic information string associated with the exception. 98 * <p> 99 * In the event of a mediaserver process crash or restart while a MediaDrm object 100 * is active, MediaDrm methods may throw {@link android.media.MediaDrmResetException}. 101 * To recover, the app must release the MediaDrm object, then create and initialize 102 * a new one. 103 * <p> 104 * As {@link android.media.MediaDrmResetException} and 105 * {@link android.media.MediaDrm.MediaDrmStateException} both extend 106 * {@link java.lang.IllegalStateException}, they should be in an earlier catch() 107 * block than {@link java.lang.IllegalStateException} if handled separately. 108 * <p> 109 * <a name="Callbacks"></a> 110 * <h3>Callbacks</h3> 111 * <p>Applications should register for informational events in order 112 * to be informed of key state updates during playback or streaming. 113 * Registration for these events is done via a call to 114 * {@link #setOnEventListener}. In order to receive the respective 115 * callback associated with this listener, applications are required to create 116 * MediaDrm objects on a thread with its own Looper running (main UI 117 * thread by default has a Looper running). 118 */ 119 public final class MediaDrm { 120 121 private static final String TAG = "MediaDrm"; 122 123 private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES; 124 125 private EventHandler mEventHandler; 126 private EventHandler mOnKeyStatusChangeEventHandler; 127 private EventHandler mOnExpirationUpdateEventHandler; 128 private OnEventListener mOnEventListener; 129 private OnKeyStatusChangeListener mOnKeyStatusChangeListener; 130 private OnExpirationUpdateListener mOnExpirationUpdateListener; 131 132 private long mNativeContext; 133 134 /** 135 * Specify no certificate type 136 * 137 * @hide - not part of the public API at this time 138 */ 139 public static final int CERTIFICATE_TYPE_NONE = 0; 140 141 /** 142 * Specify X.509 certificate type 143 * 144 * @hide - not part of the public API at this time 145 */ 146 public static final int CERTIFICATE_TYPE_X509 = 1; 147 148 /** @hide */ 149 @IntDef({ 150 CERTIFICATE_TYPE_NONE, 151 CERTIFICATE_TYPE_X509, 152 }) 153 @Retention(RetentionPolicy.SOURCE) 154 public @interface CertificateType {} 155 156 /** 157 * Query if the given scheme identified by its UUID is supported on 158 * this device. 159 * @param uuid The UUID of the crypto scheme. 160 */ isCryptoSchemeSupported(@onNull UUID uuid)161 public static final boolean isCryptoSchemeSupported(@NonNull UUID uuid) { 162 return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), null); 163 } 164 165 /** 166 * Query if the given scheme identified by its UUID is supported on 167 * this device, and whether the drm plugin is able to handle the 168 * media container format specified by mimeType. 169 * @param uuid The UUID of the crypto scheme. 170 * @param mimeType The MIME type of the media container, e.g. "video/mp4" 171 * or "video/webm" 172 */ isCryptoSchemeSupported( @onNull UUID uuid, @NonNull String mimeType)173 public static final boolean isCryptoSchemeSupported( 174 @NonNull UUID uuid, @NonNull String mimeType) { 175 return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), mimeType); 176 } 177 getByteArrayFromUUID(@onNull UUID uuid)178 private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) { 179 long msb = uuid.getMostSignificantBits(); 180 long lsb = uuid.getLeastSignificantBits(); 181 182 byte[] uuidBytes = new byte[16]; 183 for (int i = 0; i < 8; ++i) { 184 uuidBytes[i] = (byte)(msb >>> (8 * (7 - i))); 185 uuidBytes[8 + i] = (byte)(lsb >>> (8 * (7 - i))); 186 } 187 188 return uuidBytes; 189 } 190 isCryptoSchemeSupportedNative( @onNull byte[] uuid, @Nullable String mimeType)191 private static final native boolean isCryptoSchemeSupportedNative( 192 @NonNull byte[] uuid, @Nullable String mimeType); 193 194 /** 195 * Instantiate a MediaDrm object 196 * 197 * @param uuid The UUID of the crypto scheme. 198 * 199 * @throws UnsupportedSchemeException if the device does not support the 200 * specified scheme UUID 201 */ MediaDrm(@onNull UUID uuid)202 public MediaDrm(@NonNull UUID uuid) throws UnsupportedSchemeException { 203 Looper looper; 204 if ((looper = Looper.myLooper()) != null) { 205 mEventHandler = new EventHandler(this, looper); 206 } else if ((looper = Looper.getMainLooper()) != null) { 207 mEventHandler = new EventHandler(this, looper); 208 } else { 209 mEventHandler = null; 210 } 211 212 /* Native setup requires a weak reference to our object. 213 * It's easier to create it here than in C++. 214 */ 215 native_setup(new WeakReference<MediaDrm>(this), 216 getByteArrayFromUUID(uuid)); 217 } 218 219 /** 220 * Thrown when an unrecoverable failure occurs during a MediaDrm operation. 221 * Extends java.lang.IllegalStateException with the addition of an error 222 * code that may be useful in diagnosing the failure. 223 */ 224 public static final class MediaDrmStateException extends java.lang.IllegalStateException { 225 private final int mErrorCode; 226 private final String mDiagnosticInfo; 227 228 /** 229 * @hide 230 */ MediaDrmStateException(int errorCode, @Nullable String detailMessage)231 public MediaDrmStateException(int errorCode, @Nullable String detailMessage) { 232 super(detailMessage); 233 mErrorCode = errorCode; 234 235 // TODO get this from DRM session 236 final String sign = errorCode < 0 ? "neg_" : ""; 237 mDiagnosticInfo = 238 "android.media.MediaDrm.error_" + sign + Math.abs(errorCode); 239 240 } 241 242 /** 243 * Retrieve the associated error code 244 * 245 * @hide 246 */ 247 public int getErrorCode() { 248 return mErrorCode; 249 } 250 251 /** 252 * Retrieve a developer-readable diagnostic information string 253 * associated with the exception. Do not show this to end-users, 254 * since this string will not be localized or generally comprehensible 255 * to end-users. 256 */ 257 @NonNull 258 public String getDiagnosticInfo() { 259 return mDiagnosticInfo; 260 } 261 } 262 263 /** 264 * Register a callback to be invoked when a session expiration update 265 * occurs. The app's OnExpirationUpdateListener will be notified 266 * when the expiration time of the keys in the session have changed. 267 * @param listener the callback that will be run, or {@code null} to unregister the 268 * previously registered callback. 269 * @param handler the handler on which the listener should be invoked, or 270 * {@code null} if the listener should be invoked on the calling thread's looper. 271 */ 272 public void setOnExpirationUpdateListener( 273 @Nullable OnExpirationUpdateListener listener, @Nullable Handler handler) { 274 if (listener != null) { 275 Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); 276 if (looper != null) { 277 if (mEventHandler == null || mEventHandler.getLooper() != looper) { 278 mEventHandler = new EventHandler(this, looper); 279 } 280 } 281 } 282 mOnExpirationUpdateListener = listener; 283 } 284 285 /** 286 * Interface definition for a callback to be invoked when a drm session 287 * expiration update occurs 288 */ 289 public interface OnExpirationUpdateListener 290 { 291 /** 292 * Called when a session expiration update occurs, to inform the app 293 * about the change in expiration time 294 * 295 * @param md the MediaDrm object on which the event occurred 296 * @param sessionId the DRM session ID on which the event occurred 297 * @param expirationTime the new expiration time for the keys in the session. 298 * The time is in milliseconds, relative to the Unix epoch. A time of 299 * 0 indicates that the keys never expire. 300 */ 301 void onExpirationUpdate( 302 @NonNull MediaDrm md, @NonNull byte[] sessionId, long expirationTime); 303 } 304 305 /** 306 * Register a callback to be invoked when the state of keys in a session 307 * change, e.g. when a license update occurs or when a license expires. 308 * 309 * @param listener the callback that will be run when key status changes, or 310 * {@code null} to unregister the previously registered callback. 311 * @param handler the handler on which the listener should be invoked, or 312 * null if the listener should be invoked on the calling thread's looper. 313 */ 314 public void setOnKeyStatusChangeListener( 315 @Nullable OnKeyStatusChangeListener listener, @Nullable Handler handler) { 316 if (listener != null) { 317 Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); 318 if (looper != null) { 319 if (mEventHandler == null || mEventHandler.getLooper() != looper) { 320 mEventHandler = new EventHandler(this, looper); 321 } 322 } 323 } 324 mOnKeyStatusChangeListener = listener; 325 } 326 327 /** 328 * Interface definition for a callback to be invoked when the keys in a drm 329 * session change states. 330 */ 331 public interface OnKeyStatusChangeListener 332 { 333 /** 334 * Called when the keys in a session change status, such as when the license 335 * is renewed or expires. 336 * 337 * @param md the MediaDrm object on which the event occurred 338 * @param sessionId the DRM session ID on which the event occurred 339 * @param keyInformation a list of {@link MediaDrm.KeyStatus} 340 * instances indicating the status for each key in the session 341 * @param hasNewUsableKey indicates if a key has been added that is usable, 342 * which may trigger an attempt to resume playback on the media stream 343 * if it is currently blocked waiting for a key. 344 */ 345 void onKeyStatusChange( 346 @NonNull MediaDrm md, @NonNull byte[] sessionId, 347 @NonNull List<KeyStatus> keyInformation, 348 boolean hasNewUsableKey); 349 } 350 351 /** 352 * Defines the status of a key. 353 * A KeyStatus for each key in a session is provided to the 354 * {@link OnKeyStatusChangeListener#onKeyStatusChange} 355 * listener. 356 */ 357 public static final class KeyStatus { 358 private final byte[] mKeyId; 359 private final int mStatusCode; 360 361 /** 362 * The key is currently usable to decrypt media data 363 */ 364 public static final int STATUS_USABLE = 0; 365 366 /** 367 * The key is no longer usable to decrypt media data because its 368 * expiration time has passed. 369 */ 370 public static final int STATUS_EXPIRED = 1; 371 372 /** 373 * The key is not currently usable to decrypt media data because its 374 * output requirements cannot currently be met. 375 */ 376 public static final int STATUS_OUTPUT_NOT_ALLOWED = 2; 377 378 /** 379 * The status of the key is not yet known and is being determined. 380 * The status will be updated with the actual status when it has 381 * been determined. 382 */ 383 public static final int STATUS_PENDING = 3; 384 385 /** 386 * The key is not currently usable to decrypt media data because of an 387 * internal error in processing unrelated to input parameters. This error 388 * is not actionable by an app. 389 */ 390 public static final int STATUS_INTERNAL_ERROR = 4; 391 392 /** @hide */ 393 @IntDef({ 394 STATUS_USABLE, 395 STATUS_EXPIRED, 396 STATUS_OUTPUT_NOT_ALLOWED, 397 STATUS_PENDING, 398 STATUS_INTERNAL_ERROR, 399 }) 400 @Retention(RetentionPolicy.SOURCE) 401 public @interface KeyStatusCode {} 402 403 KeyStatus(@NonNull byte[] keyId, @KeyStatusCode int statusCode) { 404 mKeyId = keyId; 405 mStatusCode = statusCode; 406 } 407 408 /** 409 * Returns the status code for the key 410 * @return one of {@link #STATUS_USABLE}, {@link #STATUS_EXPIRED}, 411 * {@link #STATUS_OUTPUT_NOT_ALLOWED}, {@link #STATUS_PENDING} 412 * or {@link #STATUS_INTERNAL_ERROR}. 413 */ 414 @KeyStatusCode 415 public int getStatusCode() { return mStatusCode; } 416 417 /** 418 * Returns the id for the key 419 */ 420 @NonNull 421 public byte[] getKeyId() { return mKeyId; } 422 } 423 424 /** 425 * Register a callback to be invoked when an event occurs 426 * 427 * @param listener the callback that will be run. Use {@code null} to 428 * stop receiving event callbacks. 429 */ 430 public void setOnEventListener(@Nullable OnEventListener listener) 431 { 432 mOnEventListener = listener; 433 } 434 435 /** 436 * Interface definition for a callback to be invoked when a drm event 437 * occurs 438 */ 439 public interface OnEventListener 440 { 441 /** 442 * Called when an event occurs that requires the app to be notified 443 * 444 * @param md the MediaDrm object on which the event occurred 445 * @param sessionId the DRM session ID on which the event occurred, 446 * or {@code null} if there is no session ID associated with the event. 447 * @param event indicates the event type 448 * @param extra an secondary error code 449 * @param data optional byte array of data that may be associated with the event 450 */ 451 void onEvent( 452 @NonNull MediaDrm md, @Nullable byte[] sessionId, 453 @DrmEvent int event, int extra, 454 @Nullable byte[] data); 455 } 456 457 /** 458 * This event type indicates that the app needs to request a certificate from 459 * the provisioning server. The request message data is obtained using 460 * {@link #getProvisionRequest} 461 * 462 * @deprecated Handle provisioning via {@link android.media.NotProvisionedException} 463 * instead. 464 */ 465 public static final int EVENT_PROVISION_REQUIRED = 1; 466 467 /** 468 * This event type indicates that the app needs to request keys from a license 469 * server. The request message data is obtained using {@link #getKeyRequest}. 470 */ 471 public static final int EVENT_KEY_REQUIRED = 2; 472 473 /** 474 * This event type indicates that the licensed usage duration for keys in a session 475 * has expired. The keys are no longer valid. 476 */ 477 public static final int EVENT_KEY_EXPIRED = 3; 478 479 /** 480 * This event may indicate some specific vendor-defined condition, see your 481 * DRM provider documentation for details 482 */ 483 public static final int EVENT_VENDOR_DEFINED = 4; 484 485 /** 486 * This event indicates that a session opened by the app has been reclaimed by the resource 487 * manager. 488 */ 489 public static final int EVENT_SESSION_RECLAIMED = 5; 490 491 /** @hide */ 492 @IntDef({ 493 EVENT_PROVISION_REQUIRED, 494 EVENT_KEY_REQUIRED, 495 EVENT_KEY_EXPIRED, 496 EVENT_VENDOR_DEFINED, 497 EVENT_SESSION_RECLAIMED, 498 }) 499 @Retention(RetentionPolicy.SOURCE) 500 public @interface DrmEvent {} 501 502 private static final int DRM_EVENT = 200; 503 private static final int EXPIRATION_UPDATE = 201; 504 private static final int KEY_STATUS_CHANGE = 202; 505 506 private class EventHandler extends Handler 507 { 508 private MediaDrm mMediaDrm; 509 510 public EventHandler(@NonNull MediaDrm md, @NonNull Looper looper) { 511 super(looper); 512 mMediaDrm = md; 513 } 514 515 @Override 516 public void handleMessage(@NonNull Message msg) { 517 if (mMediaDrm.mNativeContext == 0) { 518 Log.w(TAG, "MediaDrm went away with unhandled events"); 519 return; 520 } 521 switch(msg.what) { 522 523 case DRM_EVENT: 524 if (mOnEventListener != null) { 525 if (msg.obj != null && msg.obj instanceof Parcel) { 526 Parcel parcel = (Parcel)msg.obj; 527 byte[] sessionId = parcel.createByteArray(); 528 if (sessionId.length == 0) { 529 sessionId = null; 530 } 531 byte[] data = parcel.createByteArray(); 532 if (data.length == 0) { 533 data = null; 534 } 535 536 Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")"); 537 mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data); 538 } 539 } 540 return; 541 542 case KEY_STATUS_CHANGE: 543 if (mOnKeyStatusChangeListener != null) { 544 if (msg.obj != null && msg.obj instanceof Parcel) { 545 Parcel parcel = (Parcel)msg.obj; 546 byte[] sessionId = parcel.createByteArray(); 547 if (sessionId.length > 0) { 548 List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel); 549 boolean hasNewUsableKey = (parcel.readInt() != 0); 550 551 Log.i(TAG, "Drm key status changed"); 552 mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId, 553 keyStatusList, hasNewUsableKey); 554 } 555 } 556 } 557 return; 558 559 case EXPIRATION_UPDATE: 560 if (mOnExpirationUpdateListener != null) { 561 if (msg.obj != null && msg.obj instanceof Parcel) { 562 Parcel parcel = (Parcel)msg.obj; 563 byte[] sessionId = parcel.createByteArray(); 564 if (sessionId.length > 0) { 565 long expirationTime = parcel.readLong(); 566 567 Log.i(TAG, "Drm key expiration update: " + expirationTime); 568 mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId, 569 expirationTime); 570 } 571 } 572 } 573 return; 574 575 default: 576 Log.e(TAG, "Unknown message type " + msg.what); 577 return; 578 } 579 } 580 } 581 582 /** 583 * Parse a list of KeyStatus objects from an event parcel 584 */ 585 @NonNull keyStatusListFromParcel(@onNull Parcel parcel)586 private List<KeyStatus> keyStatusListFromParcel(@NonNull Parcel parcel) { 587 int nelems = parcel.readInt(); 588 List<KeyStatus> keyStatusList = new ArrayList(nelems); 589 while (nelems-- > 0) { 590 byte[] keyId = parcel.createByteArray(); 591 int keyStatusCode = parcel.readInt(); 592 keyStatusList.add(new KeyStatus(keyId, keyStatusCode)); 593 } 594 return keyStatusList; 595 } 596 597 /** 598 * This method is called from native code when an event occurs. This method 599 * just uses the EventHandler system to post the event back to the main app thread. 600 * We use a weak reference to the original MediaPlayer object so that the native 601 * code is safe from the object disappearing from underneath it. (This is 602 * the cookie passed to native_setup().) 603 */ postEventFromNative(@onNull Object mediadrm_ref, int what, int eventType, int extra, @Nullable Object obj)604 private static void postEventFromNative(@NonNull Object mediadrm_ref, 605 int what, int eventType, int extra, @Nullable Object obj) 606 { 607 MediaDrm md = (MediaDrm)((WeakReference<MediaDrm>)mediadrm_ref).get(); 608 if (md == null) { 609 return; 610 } 611 if (md.mEventHandler != null) { 612 Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj); 613 md.mEventHandler.sendMessage(m); 614 } 615 } 616 617 /** 618 * Open a new session with the MediaDrm object. A session ID is returned. 619 * 620 * @throws NotProvisionedException if provisioning is needed 621 * @throws ResourceBusyException if required resources are in use 622 */ 623 @NonNull openSession()624 public native byte[] openSession() throws NotProvisionedException, 625 ResourceBusyException; 626 627 /** 628 * Close a session on the MediaDrm object that was previously opened 629 * with {@link #openSession}. 630 */ closeSession(@onNull byte[] sessionId)631 public native void closeSession(@NonNull byte[] sessionId); 632 633 /** 634 * This key request type species that the keys will be for online use, they will 635 * not be saved to the device for subsequent use when the device is not connected 636 * to a network. 637 */ 638 public static final int KEY_TYPE_STREAMING = 1; 639 640 /** 641 * This key request type specifies that the keys will be for offline use, they 642 * will be saved to the device for use when the device is not connected to a network. 643 */ 644 public static final int KEY_TYPE_OFFLINE = 2; 645 646 /** 647 * This key request type specifies that previously saved offline keys should be released. 648 */ 649 public static final int KEY_TYPE_RELEASE = 3; 650 651 /** @hide */ 652 @IntDef({ 653 KEY_TYPE_STREAMING, 654 KEY_TYPE_OFFLINE, 655 KEY_TYPE_RELEASE, 656 }) 657 @Retention(RetentionPolicy.SOURCE) 658 public @interface KeyType {} 659 660 /** 661 * Contains the opaque data an app uses to request keys from a license server 662 */ 663 public static final class KeyRequest { 664 private byte[] mData; 665 private String mDefaultUrl; 666 private int mRequestType; 667 668 /** 669 * Key request type is initial license request 670 */ 671 public static final int REQUEST_TYPE_INITIAL = 0; 672 673 /** 674 * Key request type is license renewal 675 */ 676 public static final int REQUEST_TYPE_RENEWAL = 1; 677 678 /** 679 * Key request type is license release 680 */ 681 public static final int REQUEST_TYPE_RELEASE = 2; 682 683 /** @hide */ 684 @IntDef({ 685 REQUEST_TYPE_INITIAL, 686 REQUEST_TYPE_RENEWAL, 687 REQUEST_TYPE_RELEASE, 688 }) 689 @Retention(RetentionPolicy.SOURCE) 690 public @interface RequestType {} 691 KeyRequest()692 KeyRequest() {} 693 694 /** 695 * Get the opaque message data 696 */ 697 @NonNull getData()698 public byte[] getData() { 699 if (mData == null) { 700 // this should never happen as mData is initialized in 701 // JNI after construction of the KeyRequest object. The check 702 // is needed here to guarantee @NonNull annotation. 703 throw new RuntimeException("KeyRequest is not initialized"); 704 } 705 return mData; 706 } 707 708 /** 709 * Get the default URL to use when sending the key request message to a 710 * server, if known. The app may prefer to use a different license 711 * server URL from other sources. 712 * This method returns an empty string if the default URL is not known. 713 */ 714 @NonNull getDefaultUrl()715 public String getDefaultUrl() { 716 if (mDefaultUrl == null) { 717 // this should never happen as mDefaultUrl is initialized in 718 // JNI after construction of the KeyRequest object. The check 719 // is needed here to guarantee @NonNull annotation. 720 throw new RuntimeException("KeyRequest is not initialized"); 721 } 722 return mDefaultUrl; 723 } 724 725 /** 726 * Get the type of the request 727 * @return one of {@link #REQUEST_TYPE_INITIAL}, 728 * {@link #REQUEST_TYPE_RENEWAL} or {@link #REQUEST_TYPE_RELEASE} 729 */ 730 @RequestType getRequestType()731 public int getRequestType() { return mRequestType; } 732 }; 733 734 /** 735 * A key request/response exchange occurs between the app and a license server 736 * to obtain or release keys used to decrypt encrypted content. 737 * <p> 738 * getKeyRequest() is used to obtain an opaque key request byte array that is 739 * delivered to the license server. The opaque key request byte array is returned 740 * in KeyRequest.data. The recommended URL to deliver the key request to is 741 * returned in KeyRequest.defaultUrl. 742 * <p> 743 * After the app has received the key request response from the server, 744 * it should deliver to the response to the DRM engine plugin using the method 745 * {@link #provideKeyResponse}. 746 * 747 * @param scope may be a sessionId or a keySetId, depending on the specified keyType. 748 * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE, 749 * scope should be set to the sessionId the keys will be provided to. When the keyType 750 * is KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys 751 * being released. Releasing keys from a device invalidates them for all sessions. 752 * @param init container-specific data, its meaning is interpreted based on the 753 * mime type provided in the mimeType parameter. It could contain, for example, 754 * the content ID, key ID or other data obtained from the content metadata that is 755 * required in generating the key request. init may be null when keyType is 756 * KEY_TYPE_RELEASE. 757 * @param mimeType identifies the mime type of the content 758 * @param keyType specifes the type of the request. The request may be to acquire 759 * keys for streaming or offline content, or to release previously acquired 760 * keys, which are identified by a keySetId. 761 * @param optionalParameters are included in the key request message to 762 * allow a client application to provide additional message parameters to the server. 763 * This may be {@code null} if no additional parameters are to be sent. 764 * @throws NotProvisionedException if reprovisioning is needed, due to a 765 * problem with the certifcate 766 */ 767 @NonNull getKeyRequest( @onNull byte[] scope, @Nullable byte[] init, @Nullable String mimeType, @KeyType int keyType, @Nullable HashMap<String, String> optionalParameters)768 public native KeyRequest getKeyRequest( 769 @NonNull byte[] scope, @Nullable byte[] init, 770 @Nullable String mimeType, @KeyType int keyType, 771 @Nullable HashMap<String, String> optionalParameters) 772 throws NotProvisionedException; 773 774 775 /** 776 * A key response is received from the license server by the app, then it is 777 * provided to the DRM engine plugin using provideKeyResponse. When the 778 * response is for an offline key request, a keySetId is returned that can be 779 * used to later restore the keys to a new session with the method 780 * {@link #restoreKeys}. 781 * When the response is for a streaming or release request, null is returned. 782 * 783 * @param scope may be a sessionId or keySetId depending on the type of the 784 * response. Scope should be set to the sessionId when the response is for either 785 * streaming or offline key requests. Scope should be set to the keySetId when 786 * the response is for a release request. 787 * @param response the byte array response from the server 788 * 789 * @throws NotProvisionedException if the response indicates that 790 * reprovisioning is required 791 * @throws DeniedByServerException if the response indicates that the 792 * server rejected the request 793 */ 794 @Nullable provideKeyResponse( @onNull byte[] scope, @NonNull byte[] response)795 public native byte[] provideKeyResponse( 796 @NonNull byte[] scope, @NonNull byte[] response) 797 throws NotProvisionedException, DeniedByServerException; 798 799 800 /** 801 * Restore persisted offline keys into a new session. keySetId identifies the 802 * keys to load, obtained from a prior call to {@link #provideKeyResponse}. 803 * 804 * @param sessionId the session ID for the DRM session 805 * @param keySetId identifies the saved key set to restore 806 */ restoreKeys(@onNull byte[] sessionId, @NonNull byte[] keySetId)807 public native void restoreKeys(@NonNull byte[] sessionId, @NonNull byte[] keySetId); 808 809 /** 810 * Remove the current keys from a session. 811 * 812 * @param sessionId the session ID for the DRM session 813 */ removeKeys(@onNull byte[] sessionId)814 public native void removeKeys(@NonNull byte[] sessionId); 815 816 /** 817 * Request an informative description of the key status for the session. The status is 818 * in the form of {name, value} pairs. Since DRM license policies vary by vendor, 819 * the specific status field names are determined by each DRM vendor. Refer to your 820 * DRM provider documentation for definitions of the field names for a particular 821 * DRM engine plugin. 822 * 823 * @param sessionId the session ID for the DRM session 824 */ 825 @NonNull queryKeyStatus(@onNull byte[] sessionId)826 public native HashMap<String, String> queryKeyStatus(@NonNull byte[] sessionId); 827 828 /** 829 * Contains the opaque data an app uses to request a certificate from a provisioning 830 * server 831 */ 832 public static final class ProvisionRequest { ProvisionRequest()833 ProvisionRequest() {} 834 835 /** 836 * Get the opaque message data 837 */ 838 @NonNull getData()839 public byte[] getData() { 840 if (mData == null) { 841 // this should never happen as mData is initialized in 842 // JNI after construction of the KeyRequest object. The check 843 // is needed here to guarantee @NonNull annotation. 844 throw new RuntimeException("ProvisionRequest is not initialized"); 845 } 846 return mData; 847 } 848 849 /** 850 * Get the default URL to use when sending the provision request 851 * message to a server, if known. The app may prefer to use a different 852 * provisioning server URL obtained from other sources. 853 * This method returns an empty string if the default URL is not known. 854 */ 855 @NonNull getDefaultUrl()856 public String getDefaultUrl() { 857 if (mDefaultUrl == null) { 858 // this should never happen as mDefaultUrl is initialized in 859 // JNI after construction of the ProvisionRequest object. The check 860 // is needed here to guarantee @NonNull annotation. 861 throw new RuntimeException("ProvisionRequest is not initialized"); 862 } 863 return mDefaultUrl; 864 } 865 866 private byte[] mData; 867 private String mDefaultUrl; 868 } 869 870 /** 871 * A provision request/response exchange occurs between the app and a provisioning 872 * server to retrieve a device certificate. If provisionining is required, the 873 * EVENT_PROVISION_REQUIRED event will be sent to the event handler. 874 * getProvisionRequest is used to obtain the opaque provision request byte array that 875 * should be delivered to the provisioning server. The provision request byte array 876 * is returned in ProvisionRequest.data. The recommended URL to deliver the provision 877 * request to is returned in ProvisionRequest.defaultUrl. 878 */ 879 @NonNull getProvisionRequest()880 public ProvisionRequest getProvisionRequest() { 881 return getProvisionRequestNative(CERTIFICATE_TYPE_NONE, ""); 882 } 883 884 @NonNull getProvisionRequestNative(int certType, @NonNull String certAuthority)885 private native ProvisionRequest getProvisionRequestNative(int certType, 886 @NonNull String certAuthority); 887 888 /** 889 * After a provision response is received by the app, it is provided to the DRM 890 * engine plugin using this method. 891 * 892 * @param response the opaque provisioning response byte array to provide to the 893 * DRM engine plugin. 894 * 895 * @throws DeniedByServerException if the response indicates that the 896 * server rejected the request 897 */ provideProvisionResponse(@onNull byte[] response)898 public void provideProvisionResponse(@NonNull byte[] response) 899 throws DeniedByServerException { 900 provideProvisionResponseNative(response); 901 } 902 903 @NonNull 904 /* could there be a valid response with 0-sized certificate or key? */ provideProvisionResponseNative(@onNull byte[] response)905 private native Certificate provideProvisionResponseNative(@NonNull byte[] response) 906 throws DeniedByServerException; 907 908 /** 909 * A means of enforcing limits on the number of concurrent streams per subscriber 910 * across devices is provided via SecureStop. This is achieved by securely 911 * monitoring the lifetime of sessions. 912 * <p> 913 * Information from the server related to the current playback session is written 914 * to persistent storage on the device when each MediaCrypto object is created. 915 * <p> 916 * In the normal case, playback will be completed, the session destroyed and the 917 * Secure Stops will be queried. The app queries secure stops and forwards the 918 * secure stop message to the server which verifies the signature and notifies the 919 * server side database that the session destruction has been confirmed. The persisted 920 * record on the client is only removed after positive confirmation that the server 921 * received the message using releaseSecureStops(). 922 */ 923 @NonNull getSecureStops()924 public native List<byte[]> getSecureStops(); 925 926 /** 927 * Access secure stop by secure stop ID. 928 * 929 * @param ssid - The secure stop ID provided by the license server. 930 */ 931 @NonNull getSecureStop(@onNull byte[] ssid)932 public native byte[] getSecureStop(@NonNull byte[] ssid); 933 934 /** 935 * Process the SecureStop server response message ssRelease. After authenticating 936 * the message, remove the SecureStops identified in the response. 937 * 938 * @param ssRelease the server response indicating which secure stops to release 939 */ releaseSecureStops(@onNull byte[] ssRelease)940 public native void releaseSecureStops(@NonNull byte[] ssRelease); 941 942 /** 943 * Remove all secure stops without requiring interaction with the server. 944 */ releaseAllSecureStops()945 public native void releaseAllSecureStops(); 946 947 /** 948 * String property name: identifies the maker of the DRM engine plugin 949 */ 950 public static final String PROPERTY_VENDOR = "vendor"; 951 952 /** 953 * String property name: identifies the version of the DRM engine plugin 954 */ 955 public static final String PROPERTY_VERSION = "version"; 956 957 /** 958 * String property name: describes the DRM engine plugin 959 */ 960 public static final String PROPERTY_DESCRIPTION = "description"; 961 962 /** 963 * String property name: a comma-separated list of cipher and mac algorithms 964 * supported by CryptoSession. The list may be empty if the DRM engine 965 * plugin does not support CryptoSession operations. 966 */ 967 public static final String PROPERTY_ALGORITHMS = "algorithms"; 968 969 /** @hide */ 970 @StringDef({ 971 PROPERTY_VENDOR, 972 PROPERTY_VERSION, 973 PROPERTY_DESCRIPTION, 974 PROPERTY_ALGORITHMS, 975 }) 976 @Retention(RetentionPolicy.SOURCE) 977 public @interface StringProperty {} 978 979 /** 980 * Read a DRM engine plugin String property value, given the property name string. 981 * <p> 982 * Standard fields names are: 983 * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION}, 984 * {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHMS} 985 */ 986 /* FIXME this throws IllegalStateException for invalid property names */ 987 @NonNull getPropertyString(@onNull @tringProperty String propertyName)988 public native String getPropertyString(@NonNull @StringProperty String propertyName); 989 990 /** 991 * Byte array property name: the device unique identifier is established during 992 * device provisioning and provides a means of uniquely identifying each device. 993 */ 994 /* FIXME this throws IllegalStateException for invalid property names */ 995 public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId"; 996 997 /** @hide */ 998 @StringDef({ 999 PROPERTY_DEVICE_UNIQUE_ID, 1000 }) 1001 @Retention(RetentionPolicy.SOURCE) 1002 public @interface ArrayProperty {} 1003 1004 /** 1005 * Read a DRM engine plugin byte array property value, given the property name string. 1006 * <p> 1007 * Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID} 1008 */ 1009 @NonNull 1010 public native byte[] getPropertyByteArray(@ArrayProperty String propertyName); 1011 1012 /** 1013 * Set a DRM engine plugin String property value. 1014 */ setPropertyString( @tringProperty String propertyName, @NonNull String value)1015 public native void setPropertyString( 1016 @StringProperty String propertyName, @NonNull String value); 1017 1018 /** 1019 * Set a DRM engine plugin byte array property value. 1020 */ setPropertyByteArray( @rrayProperty String propertyName, @NonNull byte[] value)1021 public native void setPropertyByteArray( 1022 @ArrayProperty String propertyName, @NonNull byte[] value); 1023 setCipherAlgorithmNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm)1024 private static final native void setCipherAlgorithmNative( 1025 @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm); 1026 setMacAlgorithmNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm)1027 private static final native void setMacAlgorithmNative( 1028 @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm); 1029 1030 @NonNull encryptNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv)1031 private static final native byte[] encryptNative( 1032 @NonNull MediaDrm drm, @NonNull byte[] sessionId, 1033 @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv); 1034 1035 @NonNull decryptNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv)1036 private static final native byte[] decryptNative( 1037 @NonNull MediaDrm drm, @NonNull byte[] sessionId, 1038 @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv); 1039 1040 @NonNull signNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull byte[] keyId, @NonNull byte[] message)1041 private static final native byte[] signNative( 1042 @NonNull MediaDrm drm, @NonNull byte[] sessionId, 1043 @NonNull byte[] keyId, @NonNull byte[] message); 1044 verifyNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature)1045 private static final native boolean verifyNative( 1046 @NonNull MediaDrm drm, @NonNull byte[] sessionId, 1047 @NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature); 1048 1049 /** 1050 * In addition to supporting decryption of DASH Common Encrypted Media, the 1051 * MediaDrm APIs provide the ability to securely deliver session keys from 1052 * an operator's session key server to a client device, based on the factory-installed 1053 * root of trust, and then perform encrypt, decrypt, sign and verify operations 1054 * with the session key on arbitrary user data. 1055 * <p> 1056 * The CryptoSession class implements generic encrypt/decrypt/sign/verify methods 1057 * based on the established session keys. These keys are exchanged using the 1058 * getKeyRequest/provideKeyResponse methods. 1059 * <p> 1060 * Applications of this capability could include securing various types of 1061 * purchased or private content, such as applications, books and other media, 1062 * photos or media delivery protocols. 1063 * <p> 1064 * Operators can create session key servers that are functionally similar to a 1065 * license key server, except that instead of receiving license key requests and 1066 * providing encrypted content keys which are used specifically to decrypt A/V media 1067 * content, the session key server receives session key requests and provides 1068 * encrypted session keys which can be used for general purpose crypto operations. 1069 * <p> 1070 * A CryptoSession is obtained using {@link #getCryptoSession} 1071 */ 1072 public final class CryptoSession { 1073 private byte[] mSessionId; 1074 CryptoSession(@onNull byte[] sessionId, @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)1075 CryptoSession(@NonNull byte[] sessionId, 1076 @NonNull String cipherAlgorithm, 1077 @NonNull String macAlgorithm) 1078 { 1079 mSessionId = sessionId; 1080 setCipherAlgorithmNative(MediaDrm.this, sessionId, cipherAlgorithm); 1081 setMacAlgorithmNative(MediaDrm.this, sessionId, macAlgorithm); 1082 } 1083 1084 /** 1085 * Encrypt data using the CryptoSession's cipher algorithm 1086 * 1087 * @param keyid specifies which key to use 1088 * @param input the data to encrypt 1089 * @param iv the initialization vector to use for the cipher 1090 */ 1091 @NonNull encrypt( @onNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv)1092 public byte[] encrypt( 1093 @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) { 1094 return encryptNative(MediaDrm.this, mSessionId, keyid, input, iv); 1095 } 1096 1097 /** 1098 * Decrypt data using the CryptoSessions's cipher algorithm 1099 * 1100 * @param keyid specifies which key to use 1101 * @param input the data to encrypt 1102 * @param iv the initialization vector to use for the cipher 1103 */ 1104 @NonNull decrypt( @onNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv)1105 public byte[] decrypt( 1106 @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) { 1107 return decryptNative(MediaDrm.this, mSessionId, keyid, input, iv); 1108 } 1109 1110 /** 1111 * Sign data using the CryptoSessions's mac algorithm. 1112 * 1113 * @param keyid specifies which key to use 1114 * @param message the data for which a signature is to be computed 1115 */ 1116 @NonNull sign(@onNull byte[] keyid, @NonNull byte[] message)1117 public byte[] sign(@NonNull byte[] keyid, @NonNull byte[] message) { 1118 return signNative(MediaDrm.this, mSessionId, keyid, message); 1119 } 1120 1121 /** 1122 * Verify a signature using the CryptoSessions's mac algorithm. Return true 1123 * if the signatures match, false if they do no. 1124 * 1125 * @param keyid specifies which key to use 1126 * @param message the data to verify 1127 * @param signature the reference signature which will be compared with the 1128 * computed signature 1129 */ verify( @onNull byte[] keyid, @NonNull byte[] message, @NonNull byte[] signature)1130 public boolean verify( 1131 @NonNull byte[] keyid, @NonNull byte[] message, @NonNull byte[] signature) { 1132 return verifyNative(MediaDrm.this, mSessionId, keyid, message, signature); 1133 } 1134 }; 1135 1136 /** 1137 * Obtain a CryptoSession object which can be used to encrypt, decrypt, 1138 * sign and verify messages or data using the session keys established 1139 * for the session using methods {@link #getKeyRequest} and 1140 * {@link #provideKeyResponse} using a session key server. 1141 * 1142 * @param sessionId the session ID for the session containing keys 1143 * to be used for encrypt, decrypt, sign and/or verify 1144 * @param cipherAlgorithm the algorithm to use for encryption and 1145 * decryption ciphers. The algorithm string conforms to JCA Standard 1146 * Names for Cipher Transforms and is case insensitive. For example 1147 * "AES/CBC/NoPadding". 1148 * @param macAlgorithm the algorithm to use for sign and verify 1149 * The algorithm string conforms to JCA Standard Names for Mac 1150 * Algorithms and is case insensitive. For example "HmacSHA256". 1151 * <p> 1152 * The list of supported algorithms for a DRM engine plugin can be obtained 1153 * using the method {@link #getPropertyString} with the property name 1154 * "algorithms". 1155 */ getCryptoSession( @onNull byte[] sessionId, @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)1156 public CryptoSession getCryptoSession( 1157 @NonNull byte[] sessionId, 1158 @NonNull String cipherAlgorithm, @NonNull String macAlgorithm) 1159 { 1160 return new CryptoSession(sessionId, cipherAlgorithm, macAlgorithm); 1161 } 1162 1163 /** 1164 * Contains the opaque data an app uses to request a certificate from a provisioning 1165 * server 1166 * 1167 * @hide - not part of the public API at this time 1168 */ 1169 public static final class CertificateRequest { 1170 private byte[] mData; 1171 private String mDefaultUrl; 1172 CertificateRequest(@onNull byte[] data, @NonNull String defaultUrl)1173 CertificateRequest(@NonNull byte[] data, @NonNull String defaultUrl) { 1174 mData = data; 1175 mDefaultUrl = defaultUrl; 1176 } 1177 1178 /** 1179 * Get the opaque message data 1180 */ 1181 @NonNull getData()1182 public byte[] getData() { return mData; } 1183 1184 /** 1185 * Get the default URL to use when sending the certificate request 1186 * message to a server, if known. The app may prefer to use a different 1187 * certificate server URL obtained from other sources. 1188 */ 1189 @NonNull getDefaultUrl()1190 public String getDefaultUrl() { return mDefaultUrl; } 1191 } 1192 1193 /** 1194 * Generate a certificate request, specifying the certificate type 1195 * and authority. The response received should be passed to 1196 * provideCertificateResponse. 1197 * 1198 * @param certType Specifies the certificate type. 1199 * 1200 * @param certAuthority is passed to the certificate server to specify 1201 * the chain of authority. 1202 * 1203 * @hide - not part of the public API at this time 1204 */ 1205 @NonNull getCertificateRequest( @ertificateType int certType, @NonNull String certAuthority)1206 public CertificateRequest getCertificateRequest( 1207 @CertificateType int certType, @NonNull String certAuthority) 1208 { 1209 ProvisionRequest provisionRequest = getProvisionRequestNative(certType, certAuthority); 1210 return new CertificateRequest(provisionRequest.getData(), 1211 provisionRequest.getDefaultUrl()); 1212 } 1213 1214 /** 1215 * Contains the wrapped private key and public certificate data associated 1216 * with a certificate. 1217 * 1218 * @hide - not part of the public API at this time 1219 */ 1220 public static final class Certificate { Certificate()1221 Certificate() {} 1222 1223 /** 1224 * Get the wrapped private key data 1225 */ 1226 @NonNull getWrappedPrivateKey()1227 public byte[] getWrappedPrivateKey() { 1228 if (mWrappedKey == null) { 1229 // this should never happen as mWrappedKey is initialized in 1230 // JNI after construction of the KeyRequest object. The check 1231 // is needed here to guarantee @NonNull annotation. 1232 throw new RuntimeException("Cerfificate is not initialized"); 1233 } 1234 return mWrappedKey; 1235 } 1236 1237 /** 1238 * Get the PEM-encoded certificate chain 1239 */ 1240 @NonNull getContent()1241 public byte[] getContent() { 1242 if (mCertificateData == null) { 1243 // this should never happen as mCertificateData is initialized in 1244 // JNI after construction of the KeyRequest object. The check 1245 // is needed here to guarantee @NonNull annotation. 1246 throw new RuntimeException("Cerfificate is not initialized"); 1247 } 1248 return mCertificateData; 1249 } 1250 1251 private byte[] mWrappedKey; 1252 private byte[] mCertificateData; 1253 } 1254 1255 1256 /** 1257 * Process a response from the certificate server. The response 1258 * is obtained from an HTTP Post to the url provided by getCertificateRequest. 1259 * <p> 1260 * The public X509 certificate chain and wrapped private key are returned 1261 * in the returned Certificate objec. The certificate chain is in PEM format. 1262 * The wrapped private key should be stored in application private 1263 * storage, and used when invoking the signRSA method. 1264 * 1265 * @param response the opaque certificate response byte array to provide to the 1266 * DRM engine plugin. 1267 * 1268 * @throws DeniedByServerException if the response indicates that the 1269 * server rejected the request 1270 * 1271 * @hide - not part of the public API at this time 1272 */ 1273 @NonNull provideCertificateResponse(@onNull byte[] response)1274 public Certificate provideCertificateResponse(@NonNull byte[] response) 1275 throws DeniedByServerException { 1276 return provideProvisionResponseNative(response); 1277 } 1278 1279 @NonNull signRSANative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message)1280 private static final native byte[] signRSANative( 1281 @NonNull MediaDrm drm, @NonNull byte[] sessionId, 1282 @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message); 1283 1284 /** 1285 * Sign data using an RSA key 1286 * 1287 * @param sessionId a sessionId obtained from openSession on the MediaDrm object 1288 * @param algorithm the signing algorithm to use, e.g. "PKCS1-BlockType1" 1289 * @param wrappedKey - the wrapped (encrypted) RSA private key obtained 1290 * from provideCertificateResponse 1291 * @param message the data for which a signature is to be computed 1292 * 1293 * @hide - not part of the public API at this time 1294 */ 1295 @NonNull signRSA( @onNull byte[] sessionId, @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message)1296 public byte[] signRSA( 1297 @NonNull byte[] sessionId, @NonNull String algorithm, 1298 @NonNull byte[] wrappedKey, @NonNull byte[] message) { 1299 return signRSANative(this, sessionId, algorithm, wrappedKey, message); 1300 } 1301 1302 @Override finalize()1303 protected void finalize() { 1304 native_finalize(); 1305 } 1306 release()1307 public native final void release(); native_init()1308 private static native final void native_init(); 1309 native_setup(Object mediadrm_this, byte[] uuid)1310 private native final void native_setup(Object mediadrm_this, byte[] uuid); 1311 native_finalize()1312 private native final void native_finalize(); 1313 1314 static { 1315 System.loadLibrary("media_jni"); native_init()1316 native_init(); 1317 } 1318 } 1319