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