MediaDrm.java revision 5ffbae64920bf38e16c5831996f00c3d28ae1186
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. 286 */ 287 void onExpirationUpdate( 288 @NonNull MediaDrm md, @NonNull byte[] sessionId, long expirationTime); 289 } 290 291 /** 292 * Register a callback to be invoked when the state of keys in a session 293 * change, e.g. when a license update occurs or when a license expires. 294 * 295 * @param listener the callback that will be run when key status changes, or 296 * {@code null} to unregister the previously registered callback. 297 * @param handler the handler on which the listener should be invoked, or 298 * null if the listener should be invoked on the calling thread's looper. 299 */ 300 public void setOnKeyStatusChangeListener( 301 @Nullable OnKeyStatusChangeListener listener, @Nullable Handler handler) { 302 if (listener != null) { 303 Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); 304 if (looper != null) { 305 if (mEventHandler == null || mEventHandler.getLooper() != looper) { 306 mEventHandler = new EventHandler(this, looper); 307 } 308 } 309 } 310 mOnKeyStatusChangeListener = listener; 311 } 312 313 /** 314 * Interface definition for a callback to be invoked when the keys in a drm 315 * session change states. 316 */ 317 public interface OnKeyStatusChangeListener 318 { 319 /** 320 * Called when the keys in a session change status, such as when the license 321 * is renewed or expires. 322 * 323 * @param md the MediaDrm object on which the event occurred 324 * @param sessionId the DRM session ID on which the event occurred 325 * @param keyInformation a list of {@link MediaDrm.KeyStatus} 326 * instances indicating the status for each key in the session 327 * @param hasNewUsableKey indicates if a key has been added that is usable, 328 * which may trigger an attempt to resume playback on the media stream 329 * if it is currently blocked waiting for a key. 330 */ 331 void onKeyStatusChange( 332 @NonNull MediaDrm md, @NonNull byte[] sessionId, 333 @NonNull List<KeyStatus> keyInformation, 334 boolean hasNewUsableKey); 335 } 336 337 /** 338 * Defines the status of a key. 339 * A KeyStatus for each key in a session is provided to the 340 * {@link OnKeyStatusChangeListener#onKeyStatusChange} 341 * listener. 342 */ 343 public static final class KeyStatus { 344 private final byte[] mKeyId; 345 private final int mStatusCode; 346 347 /** 348 * The key is currently usable to decrypt media data 349 */ 350 public static final int STATUS_USABLE = 0; 351 352 /** 353 * The key is no longer usable to decrypt media data because its 354 * expiration time has passed. 355 */ 356 public static final int STATUS_EXPIRED = 1; 357 358 /** 359 * The key is not currently usable to decrypt media data because its 360 * output requirements cannot currently be met. 361 */ 362 public static final int STATUS_OUTPUT_NOT_ALLOWED = 2; 363 364 /** 365 * The status of the key is not yet known and is being determined. 366 * The status will be updated with the actual status when it has 367 * been determined. 368 */ 369 public static final int STATUS_PENDING = 3; 370 371 /** 372 * The key is not currently usable to decrypt media data because of an 373 * internal error in processing unrelated to input parameters. This error 374 * is not actionable by an app. 375 */ 376 public static final int STATUS_INTERNAL_ERROR = 4; 377 378 /** @hide */ 379 @IntDef({ 380 STATUS_USABLE, 381 STATUS_EXPIRED, 382 STATUS_OUTPUT_NOT_ALLOWED, 383 STATUS_PENDING, 384 STATUS_INTERNAL_ERROR, 385 }) 386 @Retention(RetentionPolicy.SOURCE) 387 public @interface KeyStatusCode {} 388 389 KeyStatus(@NonNull byte[] keyId, @KeyStatusCode int statusCode) { 390 mKeyId = keyId; 391 mStatusCode = statusCode; 392 } 393 394 /** 395 * Returns the status code for the key 396 * @return one of {@link #STATUS_USABLE}, {@link #STATUS_EXPIRED}, 397 * {@link #STATUS_OUTPUT_NOT_ALLOWED}, {@link #STATUS_PENDING} 398 * or {@link #STATUS_INTERNAL_ERROR}. 399 */ 400 @KeyStatusCode 401 public int getStatusCode() { return mStatusCode; } 402 403 /** 404 * Returns the id for the key 405 */ 406 @NonNull 407 public byte[] getKeyId() { return mKeyId; } 408 } 409 410 /** 411 * Register a callback to be invoked when an event occurs 412 * 413 * @param listener the callback that will be run. Use {@code null} to 414 * stop receiving event callbacks. 415 */ 416 public void setOnEventListener(@Nullable OnEventListener listener) 417 { 418 mOnEventListener = listener; 419 } 420 421 /** 422 * Interface definition for a callback to be invoked when a drm event 423 * occurs 424 */ 425 public interface OnEventListener 426 { 427 /** 428 * Called when an event occurs that requires the app to be notified 429 * 430 * @param md the MediaDrm object on which the event occurred 431 * @param sessionId the DRM session ID on which the event occurred, 432 * or {@code null} if there is no session ID associated with the event. 433 * @param event indicates the event type 434 * @param extra an secondary error code 435 * @param data optional byte array of data that may be associated with the event 436 */ 437 void onEvent( 438 @NonNull MediaDrm md, @Nullable byte[] sessionId, 439 @DrmEvent int event, int extra, 440 @Nullable byte[] data); 441 } 442 443 /** 444 * This event type indicates that the app needs to request a certificate from 445 * the provisioning server. The request message data is obtained using 446 * {@link #getProvisionRequest} 447 * 448 * @deprecated Handle provisioning via {@link android.media.NotProvisionedException} 449 * instead. 450 */ 451 public static final int EVENT_PROVISION_REQUIRED = 1; 452 453 /** 454 * This event type indicates that the app needs to request keys from a license 455 * server. The request message data is obtained using {@link #getKeyRequest}. 456 */ 457 public static final int EVENT_KEY_REQUIRED = 2; 458 459 /** 460 * This event type indicates that the licensed usage duration for keys in a session 461 * has expired. The keys are no longer valid. 462 */ 463 public static final int EVENT_KEY_EXPIRED = 3; 464 465 /** 466 * This event may indicate some specific vendor-defined condition, see your 467 * DRM provider documentation for details 468 */ 469 public static final int EVENT_VENDOR_DEFINED = 4; 470 471 /** 472 * This event indicates that a session opened by the app has been reclaimed by the resource 473 * manager. 474 */ 475 public static final int EVENT_SESSION_RECLAIMED = 5; 476 477 /** @hide */ 478 @IntDef({ 479 EVENT_PROVISION_REQUIRED, 480 EVENT_KEY_REQUIRED, 481 EVENT_KEY_EXPIRED, 482 EVENT_VENDOR_DEFINED, 483 EVENT_SESSION_RECLAIMED, 484 }) 485 @Retention(RetentionPolicy.SOURCE) 486 public @interface DrmEvent {} 487 488 private static final int DRM_EVENT = 200; 489 private static final int EXPIRATION_UPDATE = 201; 490 private static final int KEY_STATUS_CHANGE = 202; 491 492 private class EventHandler extends Handler 493 { 494 private MediaDrm mMediaDrm; 495 496 public EventHandler(@NonNull MediaDrm md, @NonNull Looper looper) { 497 super(looper); 498 mMediaDrm = md; 499 } 500 501 @Override 502 public void handleMessage(@NonNull Message msg) { 503 if (mMediaDrm.mNativeContext == 0) { 504 Log.w(TAG, "MediaDrm went away with unhandled events"); 505 return; 506 } 507 switch(msg.what) { 508 509 case DRM_EVENT: 510 if (mOnEventListener != null) { 511 if (msg.obj != null && msg.obj instanceof Parcel) { 512 Parcel parcel = (Parcel)msg.obj; 513 byte[] sessionId = parcel.createByteArray(); 514 if (sessionId.length == 0) { 515 sessionId = null; 516 } 517 byte[] data = parcel.createByteArray(); 518 if (data.length == 0) { 519 data = null; 520 } 521 522 Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")"); 523 mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data); 524 } 525 } 526 return; 527 528 case KEY_STATUS_CHANGE: 529 if (mOnKeyStatusChangeListener != null) { 530 if (msg.obj != null && msg.obj instanceof Parcel) { 531 Parcel parcel = (Parcel)msg.obj; 532 byte[] sessionId = parcel.createByteArray(); 533 if (sessionId.length > 0) { 534 List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel); 535 boolean hasNewUsableKey = (parcel.readInt() != 0); 536 537 Log.i(TAG, "Drm key status changed"); 538 mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId, 539 keyStatusList, hasNewUsableKey); 540 } 541 } 542 } 543 return; 544 545 case EXPIRATION_UPDATE: 546 if (mOnExpirationUpdateListener != null) { 547 if (msg.obj != null && msg.obj instanceof Parcel) { 548 Parcel parcel = (Parcel)msg.obj; 549 byte[] sessionId = parcel.createByteArray(); 550 if (sessionId.length > 0) { 551 long expirationTime = parcel.readLong(); 552 553 Log.i(TAG, "Drm key expiration update: " + expirationTime); 554 mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId, 555 expirationTime); 556 } 557 } 558 } 559 return; 560 561 default: 562 Log.e(TAG, "Unknown message type " + msg.what); 563 return; 564 } 565 } 566 } 567 568 /** 569 * Parse a list of KeyStatus objects from an event parcel 570 */ 571 @NonNull 572 private List<KeyStatus> keyStatusListFromParcel(@NonNull Parcel parcel) { 573 int nelems = parcel.readInt(); 574 List<KeyStatus> keyStatusList = new ArrayList(nelems); 575 while (nelems-- > 0) { 576 byte[] keyId = parcel.createByteArray(); 577 int keyStatusCode = parcel.readInt(); 578 keyStatusList.add(new KeyStatus(keyId, keyStatusCode)); 579 } 580 return keyStatusList; 581 } 582 583 /** 584 * This method is called from native code when an event occurs. This method 585 * just uses the EventHandler system to post the event back to the main app thread. 586 * We use a weak reference to the original MediaPlayer object so that the native 587 * code is safe from the object disappearing from underneath it. (This is 588 * the cookie passed to native_setup().) 589 */ 590 private static void postEventFromNative(@NonNull Object mediadrm_ref, 591 int what, int eventType, int extra, @Nullable Object obj) 592 { 593 MediaDrm md = (MediaDrm)((WeakReference<MediaDrm>)mediadrm_ref).get(); 594 if (md == null) { 595 return; 596 } 597 if (md.mEventHandler != null) { 598 Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj); 599 md.mEventHandler.sendMessage(m); 600 } 601 } 602 603 /** 604 * Open a new session with the MediaDrm object. A session ID is returned. 605 * 606 * @throws NotProvisionedException if provisioning is needed 607 * @throws ResourceBusyException if required resources are in use 608 */ 609 @NonNull 610 public native byte[] openSession() throws NotProvisionedException, 611 ResourceBusyException; 612 613 /** 614 * Close a session on the MediaDrm object that was previously opened 615 * with {@link #openSession}. 616 */ 617 public native void closeSession(@NonNull byte[] sessionId); 618 619 /** 620 * This key request type species that the keys will be for online use, they will 621 * not be saved to the device for subsequent use when the device is not connected 622 * to a network. 623 */ 624 public static final int KEY_TYPE_STREAMING = 1; 625 626 /** 627 * This key request type specifies that the keys will be for offline use, they 628 * will be saved to the device for use when the device is not connected to a network. 629 */ 630 public static final int KEY_TYPE_OFFLINE = 2; 631 632 /** 633 * This key request type specifies that previously saved offline keys should be released. 634 */ 635 public static final int KEY_TYPE_RELEASE = 3; 636 637 /** @hide */ 638 @IntDef({ 639 KEY_TYPE_STREAMING, 640 KEY_TYPE_OFFLINE, 641 KEY_TYPE_RELEASE, 642 }) 643 @Retention(RetentionPolicy.SOURCE) 644 public @interface KeyType {} 645 646 /** 647 * Contains the opaque data an app uses to request keys from a license server 648 */ 649 public static final class KeyRequest { 650 private byte[] mData; 651 private String mDefaultUrl; 652 private int mRequestType; 653 654 /** 655 * Key request type is initial license request 656 */ 657 public static final int REQUEST_TYPE_INITIAL = 0; 658 659 /** 660 * Key request type is license renewal 661 */ 662 public static final int REQUEST_TYPE_RENEWAL = 1; 663 664 /** 665 * Key request type is license release 666 */ 667 public static final int REQUEST_TYPE_RELEASE = 2; 668 669 /** @hide */ 670 @IntDef({ 671 REQUEST_TYPE_INITIAL, 672 REQUEST_TYPE_RENEWAL, 673 REQUEST_TYPE_RELEASE, 674 }) 675 @Retention(RetentionPolicy.SOURCE) 676 public @interface RequestType {} 677 678 KeyRequest() {} 679 680 /** 681 * Get the opaque message data 682 */ 683 @NonNull 684 public byte[] getData() { 685 if (mData == null) { 686 // this should never happen as mData is initialized in 687 // JNI after construction of the KeyRequest object. The check 688 // is needed here to guarantee @NonNull annotation. 689 throw new RuntimeException("KeyRequest is not initialized"); 690 } 691 return mData; 692 } 693 694 /** 695 * Get the default URL to use when sending the key request message to a 696 * server, if known. The app may prefer to use a different license 697 * server URL from other sources. 698 * This method returns an empty string if the default URL is not known. 699 */ 700 @NonNull 701 public String getDefaultUrl() { 702 if (mDefaultUrl == null) { 703 // this should never happen as mDefaultUrl is initialized in 704 // JNI after construction of the KeyRequest object. The check 705 // is needed here to guarantee @NonNull annotation. 706 throw new RuntimeException("KeyRequest is not initialized"); 707 } 708 return mDefaultUrl; 709 } 710 711 /** 712 * Get the type of the request 713 * @return one of {@link #REQUEST_TYPE_INITIAL}, 714 * {@link #REQUEST_TYPE_RENEWAL} or {@link #REQUEST_TYPE_RELEASE} 715 */ 716 @RequestType 717 public int getRequestType() { return mRequestType; } 718 }; 719 720 /** 721 * A key request/response exchange occurs between the app and a license server 722 * to obtain or release keys used to decrypt encrypted content. 723 * <p> 724 * getKeyRequest() is used to obtain an opaque key request byte array that is 725 * delivered to the license server. The opaque key request byte array is returned 726 * in KeyRequest.data. The recommended URL to deliver the key request to is 727 * returned in KeyRequest.defaultUrl. 728 * <p> 729 * After the app has received the key request response from the server, 730 * it should deliver to the response to the DRM engine plugin using the method 731 * {@link #provideKeyResponse}. 732 * 733 * @param scope may be a sessionId or a keySetId, depending on the specified keyType. 734 * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE, 735 * scope should be set to the sessionId the keys will be provided to. When the keyType 736 * is KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys 737 * being released. Releasing keys from a device invalidates them for all sessions. 738 * @param init container-specific data, its meaning is interpreted based on the 739 * mime type provided in the mimeType parameter. It could contain, for example, 740 * the content ID, key ID or other data obtained from the content metadata that is 741 * required in generating the key request. init may be null when keyType is 742 * KEY_TYPE_RELEASE. 743 * @param mimeType identifies the mime type of the content 744 * @param keyType specifes the type of the request. The request may be to acquire 745 * keys for streaming or offline content, or to release previously acquired 746 * keys, which are identified by a keySetId. 747 * @param optionalParameters are included in the key request message to 748 * allow a client application to provide additional message parameters to the server. 749 * This may be {@code null} if no additional parameters are to be sent. 750 * @throws NotProvisionedException if reprovisioning is needed, due to a 751 * problem with the certifcate 752 */ 753 @NonNull 754 public native KeyRequest getKeyRequest( 755 @NonNull byte[] scope, @Nullable byte[] init, 756 @Nullable String mimeType, @KeyType int keyType, 757 @Nullable HashMap<String, String> optionalParameters) 758 throws NotProvisionedException; 759 760 761 /** 762 * A key response is received from the license server by the app, then it is 763 * provided to the DRM engine plugin using provideKeyResponse. When the 764 * response is for an offline key request, a keySetId is returned that can be 765 * used to later restore the keys to a new session with the method 766 * {@link #restoreKeys}. 767 * When the response is for a streaming or release request, null is returned. 768 * 769 * @param scope may be a sessionId or keySetId depending on the type of the 770 * response. Scope should be set to the sessionId when the response is for either 771 * streaming or offline key requests. Scope should be set to the keySetId when 772 * the response is for a release request. 773 * @param response the byte array response from the server 774 * 775 * @throws NotProvisionedException if the response indicates that 776 * reprovisioning is required 777 * @throws DeniedByServerException if the response indicates that the 778 * server rejected the request 779 */ 780 @Nullable 781 public native byte[] provideKeyResponse( 782 @NonNull byte[] scope, @NonNull byte[] response) 783 throws NotProvisionedException, DeniedByServerException; 784 785 786 /** 787 * Restore persisted offline keys into a new session. keySetId identifies the 788 * keys to load, obtained from a prior call to {@link #provideKeyResponse}. 789 * 790 * @param sessionId the session ID for the DRM session 791 * @param keySetId identifies the saved key set to restore 792 */ 793 public native void restoreKeys(@NonNull byte[] sessionId, @NonNull byte[] keySetId); 794 795 /** 796 * Remove the current keys from a session. 797 * 798 * @param sessionId the session ID for the DRM session 799 */ 800 public native void removeKeys(@NonNull byte[] sessionId); 801 802 /** 803 * Request an informative description of the key status for the session. The status is 804 * in the form of {name, value} pairs. Since DRM license policies vary by vendor, 805 * the specific status field names are determined by each DRM vendor. Refer to your 806 * DRM provider documentation for definitions of the field names for a particular 807 * DRM engine plugin. 808 * 809 * @param sessionId the session ID for the DRM session 810 */ 811 @NonNull 812 public native HashMap<String, String> queryKeyStatus(@NonNull byte[] sessionId); 813 814 /** 815 * Contains the opaque data an app uses to request a certificate from a provisioning 816 * server 817 */ 818 public static final class ProvisionRequest { 819 ProvisionRequest() {} 820 821 /** 822 * Get the opaque message data 823 */ 824 @NonNull 825 public byte[] getData() { 826 if (mData == null) { 827 // this should never happen as mData is initialized in 828 // JNI after construction of the KeyRequest object. The check 829 // is needed here to guarantee @NonNull annotation. 830 throw new RuntimeException("ProvisionRequest is not initialized"); 831 } 832 return mData; 833 } 834 835 /** 836 * Get the default URL to use when sending the provision request 837 * message to a server, if known. The app may prefer to use a different 838 * provisioning server URL obtained from other sources. 839 * This method returns an empty string if the default URL is not known. 840 */ 841 @NonNull 842 public String getDefaultUrl() { 843 if (mDefaultUrl == null) { 844 // this should never happen as mDefaultUrl is initialized in 845 // JNI after construction of the ProvisionRequest object. The check 846 // is needed here to guarantee @NonNull annotation. 847 throw new RuntimeException("ProvisionRequest is not initialized"); 848 } 849 return mDefaultUrl; 850 } 851 852 private byte[] mData; 853 private String mDefaultUrl; 854 } 855 856 /** 857 * A provision request/response exchange occurs between the app and a provisioning 858 * server to retrieve a device certificate. If provisionining is required, the 859 * EVENT_PROVISION_REQUIRED event will be sent to the event handler. 860 * getProvisionRequest is used to obtain the opaque provision request byte array that 861 * should be delivered to the provisioning server. The provision request byte array 862 * is returned in ProvisionRequest.data. The recommended URL to deliver the provision 863 * request to is returned in ProvisionRequest.defaultUrl. 864 */ 865 @NonNull 866 public ProvisionRequest getProvisionRequest() { 867 return getProvisionRequestNative(CERTIFICATE_TYPE_NONE, ""); 868 } 869 870 @NonNull 871 private native ProvisionRequest getProvisionRequestNative(int certType, 872 @NonNull String certAuthority); 873 874 /** 875 * After a provision response is received by the app, it is provided to the DRM 876 * engine plugin using this method. 877 * 878 * @param response the opaque provisioning response byte array to provide to the 879 * DRM engine plugin. 880 * 881 * @throws DeniedByServerException if the response indicates that the 882 * server rejected the request 883 */ 884 public void provideProvisionResponse(@NonNull byte[] response) 885 throws DeniedByServerException { 886 provideProvisionResponseNative(response); 887 } 888 889 @NonNull 890 /* could there be a valid response with 0-sized certificate or key? */ 891 private native Certificate provideProvisionResponseNative(@NonNull byte[] response) 892 throws DeniedByServerException; 893 894 /** 895 * Remove provisioning from a device. Only system apps may unprovision a 896 * device. Note that removing provisioning will invalidate any keys saved 897 * for offline use (KEY_TYPE_OFFLINE), which may render downloaded content 898 * unplayable until new licenses are acquired. Since provisioning is global 899 * to the device, license invalidation will apply to all content downloaded 900 * by any app, so appropriate warnings should be given to the user. 901 * @hide 902 */ 903 @SystemApi 904 public native void unprovisionDevice(); 905 906 /** 907 * A means of enforcing limits on the number of concurrent streams per subscriber 908 * across devices is provided via SecureStop. This is achieved by securely 909 * monitoring the lifetime of sessions. 910 * <p> 911 * Information from the server related to the current playback session is written 912 * to persistent storage on the device when each MediaCrypto object is created. 913 * <p> 914 * In the normal case, playback will be completed, the session destroyed and the 915 * Secure Stops will be queried. The app queries secure stops and forwards the 916 * secure stop message to the server which verifies the signature and notifies the 917 * server side database that the session destruction has been confirmed. The persisted 918 * record on the client is only removed after positive confirmation that the server 919 * received the message using releaseSecureStops(). 920 */ 921 @NonNull 922 public native List<byte[]> getSecureStops(); 923 924 /** 925 * Access secure stop by secure stop ID. 926 * 927 * @param ssid - The secure stop ID provided by the license server. 928 */ 929 @NonNull 930 public native byte[] getSecureStop(@NonNull byte[] ssid); 931 932 /** 933 * Process the SecureStop server response message ssRelease. After authenticating 934 * the message, remove the SecureStops identified in the response. 935 * 936 * @param ssRelease the server response indicating which secure stops to release 937 */ 938 public native void releaseSecureStops(@NonNull byte[] ssRelease); 939 940 /** 941 * Remove all secure stops without requiring interaction with the server. 942 */ 943 public native void releaseAllSecureStops(); 944 945 /** 946 * String property name: identifies the maker of the DRM engine plugin 947 */ 948 public static final String PROPERTY_VENDOR = "vendor"; 949 950 /** 951 * String property name: identifies the version of the DRM engine plugin 952 */ 953 public static final String PROPERTY_VERSION = "version"; 954 955 /** 956 * String property name: describes the DRM engine plugin 957 */ 958 public static final String PROPERTY_DESCRIPTION = "description"; 959 960 /** 961 * String property name: a comma-separated list of cipher and mac algorithms 962 * supported by CryptoSession. The list may be empty if the DRM engine 963 * plugin does not support CryptoSession operations. 964 */ 965 public static final String PROPERTY_ALGORITHMS = "algorithms"; 966 967 /** @hide */ 968 @StringDef({ 969 PROPERTY_VENDOR, 970 PROPERTY_VERSION, 971 PROPERTY_DESCRIPTION, 972 PROPERTY_ALGORITHMS, 973 }) 974 @Retention(RetentionPolicy.SOURCE) 975 public @interface StringProperty {} 976 977 /** 978 * Read a DRM engine plugin String property value, given the property name string. 979 * <p> 980 * Standard fields names are: 981 * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION}, 982 * {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHMS} 983 */ 984 /* FIXME this throws IllegalStateException for invalid property names */ 985 @NonNull 986 public native String getPropertyString(@NonNull @StringProperty String propertyName); 987 988 /** 989 * Byte array property name: the device unique identifier is established during 990 * device provisioning and provides a means of uniquely identifying each device. 991 */ 992 /* FIXME this throws IllegalStateException for invalid property names */ 993 public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId"; 994 995 /** @hide */ 996 @StringDef({ 997 PROPERTY_DEVICE_UNIQUE_ID, 998 }) 999 @Retention(RetentionPolicy.SOURCE) 1000 public @interface ArrayProperty {} 1001 1002 /** 1003 * Read a DRM engine plugin byte array property value, given the property name string. 1004 * <p> 1005 * Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID} 1006 */ 1007 @NonNull 1008 public native byte[] getPropertyByteArray(@ArrayProperty String propertyName); 1009 1010 /** 1011 * Set a DRM engine plugin String property value. 1012 */ 1013 public native void setPropertyString( 1014 @StringProperty String propertyName, @NonNull String value); 1015 1016 /** 1017 * Set a DRM engine plugin byte array property value. 1018 */ 1019 public native void setPropertyByteArray( 1020 @ArrayProperty String propertyName, @NonNull byte[] value); 1021 1022 private static final native void setCipherAlgorithmNative( 1023 @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm); 1024 1025 private static final native void setMacAlgorithmNative( 1026 @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm); 1027 1028 @NonNull 1029 private static final native byte[] encryptNative( 1030 @NonNull MediaDrm drm, @NonNull byte[] sessionId, 1031 @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv); 1032 1033 @NonNull 1034 private static final native byte[] decryptNative( 1035 @NonNull MediaDrm drm, @NonNull byte[] sessionId, 1036 @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv); 1037 1038 @NonNull 1039 private static final native byte[] signNative( 1040 @NonNull MediaDrm drm, @NonNull byte[] sessionId, 1041 @NonNull byte[] keyId, @NonNull byte[] message); 1042 1043 private static final native boolean verifyNative( 1044 @NonNull MediaDrm drm, @NonNull byte[] sessionId, 1045 @NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature); 1046 1047 /** 1048 * In addition to supporting decryption of DASH Common Encrypted Media, the 1049 * MediaDrm APIs provide the ability to securely deliver session keys from 1050 * an operator's session key server to a client device, based on the factory-installed 1051 * root of trust, and then perform encrypt, decrypt, sign and verify operations 1052 * with the session key on arbitrary user data. 1053 * <p> 1054 * The CryptoSession class implements generic encrypt/decrypt/sign/verify methods 1055 * based on the established session keys. These keys are exchanged using the 1056 * getKeyRequest/provideKeyResponse methods. 1057 * <p> 1058 * Applications of this capability could include securing various types of 1059 * purchased or private content, such as applications, books and other media, 1060 * photos or media delivery protocols. 1061 * <p> 1062 * Operators can create session key servers that are functionally similar to a 1063 * license key server, except that instead of receiving license key requests and 1064 * providing encrypted content keys which are used specifically to decrypt A/V media 1065 * content, the session key server receives session key requests and provides 1066 * encrypted session keys which can be used for general purpose crypto operations. 1067 * <p> 1068 * A CryptoSession is obtained using {@link #getCryptoSession} 1069 */ 1070 public final class CryptoSession { 1071 private MediaDrm mDrm; 1072 private byte[] mSessionId; 1073 1074 CryptoSession(@NonNull MediaDrm drm, @NonNull byte[] sessionId, 1075 @NonNull String cipherAlgorithm, @NonNull String macAlgorithm) 1076 { 1077 mSessionId = sessionId; 1078 mDrm = drm; 1079 setCipherAlgorithmNative(drm, sessionId, cipherAlgorithm); 1080 setMacAlgorithmNative(drm, sessionId, macAlgorithm); 1081 } 1082 1083 /** 1084 * Encrypt data using the CryptoSession's cipher algorithm 1085 * 1086 * @param keyid specifies which key to use 1087 * @param input the data to encrypt 1088 * @param iv the initialization vector to use for the cipher 1089 */ 1090 @NonNull 1091 public byte[] encrypt( 1092 @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) { 1093 return encryptNative(mDrm, mSessionId, keyid, input, iv); 1094 } 1095 1096 /** 1097 * Decrypt data using the CryptoSessions's cipher algorithm 1098 * 1099 * @param keyid specifies which key to use 1100 * @param input the data to encrypt 1101 * @param iv the initialization vector to use for the cipher 1102 */ 1103 @NonNull 1104 public byte[] decrypt( 1105 @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) { 1106 return decryptNative(mDrm, mSessionId, keyid, input, iv); 1107 } 1108 1109 /** 1110 * Sign data using the CryptoSessions's mac algorithm. 1111 * 1112 * @param keyid specifies which key to use 1113 * @param message the data for which a signature is to be computed 1114 */ 1115 @NonNull 1116 public byte[] sign(@NonNull byte[] keyid, @NonNull byte[] message) { 1117 return signNative(mDrm, mSessionId, keyid, message); 1118 } 1119 1120 /** 1121 * Verify a signature using the CryptoSessions's mac algorithm. Return true 1122 * if the signatures match, false if they do no. 1123 * 1124 * @param keyid specifies which key to use 1125 * @param message the data to verify 1126 * @param signature the reference signature which will be compared with the 1127 * computed signature 1128 */ 1129 public boolean verify( 1130 @NonNull byte[] keyid, @NonNull byte[] message, @NonNull byte[] signature) { 1131 return verifyNative(mDrm, mSessionId, keyid, message, signature); 1132 } 1133 }; 1134 1135 /** 1136 * Obtain a CryptoSession object which can be used to encrypt, decrypt, 1137 * sign and verify messages or data using the session keys established 1138 * for the session using methods {@link #getKeyRequest} and 1139 * {@link #provideKeyResponse} using a session key server. 1140 * 1141 * @param sessionId the session ID for the session containing keys 1142 * to be used for encrypt, decrypt, sign and/or verify 1143 * @param cipherAlgorithm the algorithm to use for encryption and 1144 * decryption ciphers. The algorithm string conforms to JCA Standard 1145 * Names for Cipher Transforms and is case insensitive. For example 1146 * "AES/CBC/NoPadding". 1147 * @param macAlgorithm the algorithm to use for sign and verify 1148 * The algorithm string conforms to JCA Standard Names for Mac 1149 * Algorithms and is case insensitive. For example "HmacSHA256". 1150 * <p> 1151 * The list of supported algorithms for a DRM engine plugin can be obtained 1152 * using the method {@link #getPropertyString} with the property name 1153 * "algorithms". 1154 */ 1155 public CryptoSession getCryptoSession( 1156 @NonNull byte[] sessionId, 1157 @NonNull String cipherAlgorithm, @NonNull String macAlgorithm) 1158 { 1159 return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm); 1160 } 1161 1162 /** 1163 * Contains the opaque data an app uses to request a certificate from a provisioning 1164 * server 1165 * 1166 * @hide - not part of the public API at this time 1167 */ 1168 public static final class CertificateRequest { 1169 private byte[] mData; 1170 private String mDefaultUrl; 1171 1172 CertificateRequest(@NonNull byte[] data, @NonNull String defaultUrl) { 1173 mData = data; 1174 mDefaultUrl = defaultUrl; 1175 } 1176 1177 /** 1178 * Get the opaque message data 1179 */ 1180 @NonNull 1181 public byte[] getData() { return mData; } 1182 1183 /** 1184 * Get the default URL to use when sending the certificate request 1185 * message to a server, if known. The app may prefer to use a different 1186 * certificate server URL obtained from other sources. 1187 */ 1188 @NonNull 1189 public String getDefaultUrl() { return mDefaultUrl; } 1190 } 1191 1192 /** 1193 * Generate a certificate request, specifying the certificate type 1194 * and authority. The response received should be passed to 1195 * provideCertificateResponse. 1196 * 1197 * @param certType Specifies the certificate type. 1198 * 1199 * @param certAuthority is passed to the certificate server to specify 1200 * the chain of authority. 1201 * 1202 * @hide - not part of the public API at this time 1203 */ 1204 @NonNull 1205 public CertificateRequest getCertificateRequest( 1206 @CertificateType int certType, @NonNull String certAuthority) 1207 { 1208 ProvisionRequest provisionRequest = getProvisionRequestNative(certType, certAuthority); 1209 return new CertificateRequest(provisionRequest.getData(), 1210 provisionRequest.getDefaultUrl()); 1211 } 1212 1213 /** 1214 * Contains the wrapped private key and public certificate data associated 1215 * with a certificate. 1216 * 1217 * @hide - not part of the public API at this time 1218 */ 1219 public static final class Certificate { 1220 Certificate() {} 1221 1222 /** 1223 * Get the wrapped private key data 1224 */ 1225 @NonNull 1226 public byte[] getWrappedPrivateKey() { 1227 if (mWrappedKey == null) { 1228 // this should never happen as mWrappedKey is initialized in 1229 // JNI after construction of the KeyRequest object. The check 1230 // is needed here to guarantee @NonNull annotation. 1231 throw new RuntimeException("Cerfificate is not initialized"); 1232 } 1233 return mWrappedKey; 1234 } 1235 1236 /** 1237 * Get the PEM-encoded certificate chain 1238 */ 1239 @NonNull 1240 public byte[] getContent() { 1241 if (mCertificateData == null) { 1242 // this should never happen as mCertificateData is initialized in 1243 // JNI after construction of the KeyRequest object. The check 1244 // is needed here to guarantee @NonNull annotation. 1245 throw new RuntimeException("Cerfificate is not initialized"); 1246 } 1247 return mCertificateData; 1248 } 1249 1250 private byte[] mWrappedKey; 1251 private byte[] mCertificateData; 1252 } 1253 1254 1255 /** 1256 * Process a response from the certificate server. The response 1257 * is obtained from an HTTP Post to the url provided by getCertificateRequest. 1258 * <p> 1259 * The public X509 certificate chain and wrapped private key are returned 1260 * in the returned Certificate objec. The certificate chain is in PEM format. 1261 * The wrapped private key should be stored in application private 1262 * storage, and used when invoking the signRSA method. 1263 * 1264 * @param response the opaque certificate response byte array to provide to the 1265 * DRM engine plugin. 1266 * 1267 * @throws DeniedByServerException if the response indicates that the 1268 * server rejected the request 1269 * 1270 * @hide - not part of the public API at this time 1271 */ 1272 @NonNull 1273 public Certificate provideCertificateResponse(@NonNull byte[] response) 1274 throws DeniedByServerException { 1275 return provideProvisionResponseNative(response); 1276 } 1277 1278 @NonNull 1279 private static final native byte[] signRSANative( 1280 @NonNull MediaDrm drm, @NonNull byte[] sessionId, 1281 @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message); 1282 1283 /** 1284 * Sign data using an RSA key 1285 * 1286 * @param sessionId a sessionId obtained from openSession on the MediaDrm object 1287 * @param algorithm the signing algorithm to use, e.g. "PKCS1-BlockType1" 1288 * @param wrappedKey - the wrapped (encrypted) RSA private key obtained 1289 * from provideCertificateResponse 1290 * @param message the data for which a signature is to be computed 1291 * 1292 * @hide - not part of the public API at this time 1293 */ 1294 @NonNull 1295 public byte[] signRSA( 1296 @NonNull byte[] sessionId, @NonNull String algorithm, 1297 @NonNull byte[] wrappedKey, @NonNull byte[] message) { 1298 return signRSANative(this, sessionId, algorithm, wrappedKey, message); 1299 } 1300 1301 @Override 1302 protected void finalize() { 1303 native_finalize(); 1304 } 1305 1306 public native final void release(); 1307 private static native final void native_init(); 1308 1309 private native final void native_setup(Object mediadrm_this, byte[] uuid); 1310 1311 private native final void native_finalize(); 1312 1313 static { 1314 System.loadLibrary("media_jni"); 1315 native_init(); 1316 } 1317} 1318