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