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