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