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