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