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      */
isCryptoSchemeSupported(@onNull UUID uuid)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      */
isCryptoSchemeSupported( @onNull UUID uuid, @NonNull String mimeType)174     public static final boolean isCryptoSchemeSupported(
175             @NonNull UUID uuid, @NonNull String mimeType) {
176         return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), mimeType);
177     }
178 
getByteArrayFromUUID(@onNull UUID uuid)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 
isCryptoSchemeSupportedNative( @onNull byte[] uuid, @Nullable String mimeType)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      */
MediaDrm(@onNull UUID uuid)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          */
MediaDrmStateException(int errorCode, @Nullable String detailMessage)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
keyStatusListFromParcel(@onNull Parcel parcel)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      */
postEventFromNative(@onNull Object mediadrm_ref, int what, int eventType, int extra, @Nullable Object obj)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
openSession()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      */
closeSession(@onNull byte[] sessionId)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 
KeyRequest()696         KeyRequest() {}
697 
698         /**
699          * Get the opaque message data
700          */
701         @NonNull
getData()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
getDefaultUrl()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
getRequestType()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
getKeyRequest( @onNull byte[] scope, @Nullable byte[] init, @Nullable String mimeType, @KeyType int keyType, @Nullable HashMap<String, String> optionalParameters)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
provideKeyResponse( @onNull byte[] scope, @NonNull byte[] response)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      */
restoreKeys(@onNull byte[] sessionId, @NonNull byte[] keySetId)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      */
removeKeys(@onNull byte[] sessionId)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
queryKeyStatus(@onNull byte[] sessionId)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 {
ProvisionRequest()844         ProvisionRequest() {}
845 
846         /**
847          * Get the opaque message data
848          */
849         @NonNull
getData()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
getDefaultUrl()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
getProvisionRequest()891     public ProvisionRequest getProvisionRequest() {
892         return getProvisionRequestNative(CERTIFICATE_TYPE_NONE, "");
893     }
894 
895     @NonNull
getProvisionRequestNative(int certType, @NonNull String certAuthority)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      */
provideProvisionResponse(@onNull byte[] response)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? */
provideProvisionResponseNative(@onNull byte[] response)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
getSecureStops()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
getSecureStop(@onNull byte[] ssid)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      */
releaseSecureStops(@onNull byte[] ssRelease)951     public native void releaseSecureStops(@NonNull byte[] ssRelease);
952 
953     /**
954      * Remove all secure stops without requiring interaction with the server.
955      */
releaseAllSecureStops()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
getPropertyString(@onNull @tringProperty String propertyName)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      */
setPropertyString( String propertyName, @NonNull String value)1026     public native void setPropertyString(
1027             String propertyName, @NonNull String value);
1028 
1029     /**
1030      * Set a DRM engine plugin byte array property value.
1031      */
setPropertyByteArray( String propertyName, @NonNull byte[] value)1032     public native void setPropertyByteArray(
1033             String propertyName, @NonNull byte[] value);
1034 
setCipherAlgorithmNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm)1035     private static final native void setCipherAlgorithmNative(
1036             @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
1037 
setMacAlgorithmNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm)1038     private static final native void setMacAlgorithmNative(
1039             @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
1040 
1041     @NonNull
encryptNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv)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
decryptNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv)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
signNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull byte[] keyId, @NonNull byte[] message)1052     private static final native byte[] signNative(
1053             @NonNull MediaDrm drm, @NonNull byte[] sessionId,
1054             @NonNull byte[] keyId, @NonNull byte[] message);
1055 
verifyNative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature)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 
CryptoSession(@onNull byte[] sessionId, @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)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
encrypt( @onNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv)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
decrypt( @onNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv)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
sign(@onNull byte[] keyid, @NonNull byte[] message)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          */
verify( @onNull byte[] keyid, @NonNull byte[] message, @NonNull byte[] signature)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      */
getCryptoSession( @onNull byte[] sessionId, @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)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 
CertificateRequest(@onNull byte[] data, @NonNull String defaultUrl)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
getData()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
getDefaultUrl()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
getCertificateRequest( @ertificateType int certType, @NonNull String certAuthority)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 {
Certificate()1232         Certificate() {}
1233 
1234         /**
1235          * Get the wrapped private key data
1236          */
1237         @NonNull
getWrappedPrivateKey()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
getContent()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
provideCertificateResponse(@onNull byte[] response)1285     public Certificate provideCertificateResponse(@NonNull byte[] response)
1286             throws DeniedByServerException {
1287         return provideProvisionResponseNative(response);
1288     }
1289 
1290     @NonNull
signRSANative( @onNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message)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
signRSA( @onNull byte[] sessionId, @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message)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
finalize()1314     protected void finalize() {
1315         native_finalize();
1316     }
1317 
release()1318     public native final void release();
native_init()1319     private static native final void native_init();
1320 
native_setup(Object mediadrm_this, byte[] uuid, String appPackageName)1321     private native final void native_setup(Object mediadrm_this, byte[] uuid,
1322             String appPackageName);
1323 
native_finalize()1324     private native final void native_finalize();
1325 
1326     static {
1327         System.loadLibrary("media_jni");
native_init()1328         native_init();
1329     }
1330 }
1331