MediaDrm.java revision 5ffbae64920bf38e16c5831996f00c3d28ae1186
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.
286         */
287        void onExpirationUpdate(
288                @NonNull MediaDrm md, @NonNull byte[] sessionId, long expirationTime);
289    }
290
291    /**
292     * Register a callback to be invoked when the state of keys in a session
293     * change, e.g. when a license update occurs or when a license expires.
294     *
295     * @param listener the callback that will be run when key status changes, or
296     *     {@code null} to unregister the previously registered callback.
297     * @param handler the handler on which the listener should be invoked, or
298     *     null if the listener should be invoked on the calling thread's looper.
299     */
300    public void setOnKeyStatusChangeListener(
301            @Nullable OnKeyStatusChangeListener listener, @Nullable Handler handler) {
302        if (listener != null) {
303            Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
304            if (looper != null) {
305                if (mEventHandler == null || mEventHandler.getLooper() != looper) {
306                    mEventHandler = new EventHandler(this, looper);
307                }
308            }
309        }
310        mOnKeyStatusChangeListener = listener;
311    }
312
313    /**
314     * Interface definition for a callback to be invoked when the keys in a drm
315     * session change states.
316     */
317    public interface OnKeyStatusChangeListener
318    {
319        /**
320         * Called when the keys in a session change status, such as when the license
321         * is renewed or expires.
322         *
323         * @param md the MediaDrm object on which the event occurred
324         * @param sessionId the DRM session ID on which the event occurred
325         * @param keyInformation a list of {@link MediaDrm.KeyStatus}
326         *     instances indicating the status for each key in the session
327         * @param hasNewUsableKey indicates if a key has been added that is usable,
328         *     which may trigger an attempt to resume playback on the media stream
329         *     if it is currently blocked waiting for a key.
330         */
331        void onKeyStatusChange(
332                @NonNull MediaDrm md, @NonNull byte[] sessionId,
333                @NonNull List<KeyStatus> keyInformation,
334                boolean hasNewUsableKey);
335    }
336
337    /**
338     * Defines the status of a key.
339     * A KeyStatus for each key in a session is provided to the
340     * {@link OnKeyStatusChangeListener#onKeyStatusChange}
341     * listener.
342     */
343    public static final class KeyStatus {
344        private final byte[] mKeyId;
345        private final int mStatusCode;
346
347        /**
348         * The key is currently usable to decrypt media data
349         */
350        public static final int STATUS_USABLE = 0;
351
352        /**
353         * The key is no longer usable to decrypt media data because its
354         * expiration time has passed.
355         */
356        public static final int STATUS_EXPIRED = 1;
357
358        /**
359         * The key is not currently usable to decrypt media data because its
360         * output requirements cannot currently be met.
361         */
362        public static final int STATUS_OUTPUT_NOT_ALLOWED = 2;
363
364        /**
365         * The status of the key is not yet known and is being determined.
366         * The status will be updated with the actual status when it has
367         * been determined.
368         */
369        public static final int STATUS_PENDING = 3;
370
371        /**
372         * The key is not currently usable to decrypt media data because of an
373         * internal error in processing unrelated to input parameters.  This error
374         * is not actionable by an app.
375         */
376        public static final int STATUS_INTERNAL_ERROR = 4;
377
378        /** @hide */
379        @IntDef({
380            STATUS_USABLE,
381            STATUS_EXPIRED,
382            STATUS_OUTPUT_NOT_ALLOWED,
383            STATUS_PENDING,
384            STATUS_INTERNAL_ERROR,
385        })
386        @Retention(RetentionPolicy.SOURCE)
387        public @interface KeyStatusCode {}
388
389        KeyStatus(@NonNull byte[] keyId, @KeyStatusCode int statusCode) {
390            mKeyId = keyId;
391            mStatusCode = statusCode;
392        }
393
394        /**
395         * Returns the status code for the key
396         * @return one of {@link #STATUS_USABLE}, {@link #STATUS_EXPIRED},
397         * {@link #STATUS_OUTPUT_NOT_ALLOWED}, {@link #STATUS_PENDING}
398         * or {@link #STATUS_INTERNAL_ERROR}.
399         */
400        @KeyStatusCode
401        public int getStatusCode() { return mStatusCode; }
402
403        /**
404         * Returns the id for the key
405         */
406        @NonNull
407        public byte[] getKeyId() { return mKeyId; }
408    }
409
410    /**
411     * Register a callback to be invoked when an event occurs
412     *
413     * @param listener the callback that will be run.  Use {@code null} to
414     *        stop receiving event callbacks.
415     */
416    public void setOnEventListener(@Nullable OnEventListener listener)
417    {
418        mOnEventListener = listener;
419    }
420
421    /**
422     * Interface definition for a callback to be invoked when a drm event
423     * occurs
424     */
425    public interface OnEventListener
426    {
427        /**
428         * Called when an event occurs that requires the app to be notified
429         *
430         * @param md the MediaDrm object on which the event occurred
431         * @param sessionId the DRM session ID on which the event occurred,
432         *        or {@code null} if there is no session ID associated with the event.
433         * @param event indicates the event type
434         * @param extra an secondary error code
435         * @param data optional byte array of data that may be associated with the event
436         */
437        void onEvent(
438                @NonNull MediaDrm md, @Nullable byte[] sessionId,
439                @DrmEvent int event, int extra,
440                @Nullable byte[] data);
441    }
442
443    /**
444     * This event type indicates that the app needs to request a certificate from
445     * the provisioning server.  The request message data is obtained using
446     * {@link #getProvisionRequest}
447     *
448     * @deprecated Handle provisioning via {@link android.media.NotProvisionedException}
449     * instead.
450     */
451    public static final int EVENT_PROVISION_REQUIRED = 1;
452
453    /**
454     * This event type indicates that the app needs to request keys from a license
455     * server.  The request message data is obtained using {@link #getKeyRequest}.
456     */
457    public static final int EVENT_KEY_REQUIRED = 2;
458
459    /**
460     * This event type indicates that the licensed usage duration for keys in a session
461     * has expired.  The keys are no longer valid.
462     */
463    public static final int EVENT_KEY_EXPIRED = 3;
464
465    /**
466     * This event may indicate some specific vendor-defined condition, see your
467     * DRM provider documentation for details
468     */
469    public static final int EVENT_VENDOR_DEFINED = 4;
470
471    /**
472     * This event indicates that a session opened by the app has been reclaimed by the resource
473     * manager.
474     */
475    public static final int EVENT_SESSION_RECLAIMED = 5;
476
477    /** @hide */
478    @IntDef({
479        EVENT_PROVISION_REQUIRED,
480        EVENT_KEY_REQUIRED,
481        EVENT_KEY_EXPIRED,
482        EVENT_VENDOR_DEFINED,
483        EVENT_SESSION_RECLAIMED,
484    })
485    @Retention(RetentionPolicy.SOURCE)
486    public @interface DrmEvent {}
487
488    private static final int DRM_EVENT = 200;
489    private static final int EXPIRATION_UPDATE = 201;
490    private static final int KEY_STATUS_CHANGE = 202;
491
492    private class EventHandler extends Handler
493    {
494        private MediaDrm mMediaDrm;
495
496        public EventHandler(@NonNull MediaDrm md, @NonNull Looper looper) {
497            super(looper);
498            mMediaDrm = md;
499        }
500
501        @Override
502        public void handleMessage(@NonNull Message msg) {
503            if (mMediaDrm.mNativeContext == 0) {
504                Log.w(TAG, "MediaDrm went away with unhandled events");
505                return;
506            }
507            switch(msg.what) {
508
509            case DRM_EVENT:
510                if (mOnEventListener != null) {
511                    if (msg.obj != null && msg.obj instanceof Parcel) {
512                        Parcel parcel = (Parcel)msg.obj;
513                        byte[] sessionId = parcel.createByteArray();
514                        if (sessionId.length == 0) {
515                            sessionId = null;
516                        }
517                        byte[] data = parcel.createByteArray();
518                        if (data.length == 0) {
519                            data = null;
520                        }
521
522                        Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
523                        mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
524                    }
525                }
526                return;
527
528            case KEY_STATUS_CHANGE:
529                if (mOnKeyStatusChangeListener != null) {
530                    if (msg.obj != null && msg.obj instanceof Parcel) {
531                        Parcel parcel = (Parcel)msg.obj;
532                        byte[] sessionId = parcel.createByteArray();
533                        if (sessionId.length > 0) {
534                            List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel);
535                            boolean hasNewUsableKey = (parcel.readInt() != 0);
536
537                            Log.i(TAG, "Drm key status changed");
538                            mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId,
539                                    keyStatusList, hasNewUsableKey);
540                        }
541                    }
542                }
543                return;
544
545            case EXPIRATION_UPDATE:
546                if (mOnExpirationUpdateListener != null) {
547                    if (msg.obj != null && msg.obj instanceof Parcel) {
548                        Parcel parcel = (Parcel)msg.obj;
549                        byte[] sessionId = parcel.createByteArray();
550                        if (sessionId.length > 0) {
551                            long expirationTime = parcel.readLong();
552
553                            Log.i(TAG, "Drm key expiration update: " + expirationTime);
554                            mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId,
555                                    expirationTime);
556                        }
557                    }
558                }
559                return;
560
561            default:
562                Log.e(TAG, "Unknown message type " + msg.what);
563                return;
564            }
565        }
566    }
567
568    /**
569     * Parse a list of KeyStatus objects from an event parcel
570     */
571    @NonNull
572    private List<KeyStatus> keyStatusListFromParcel(@NonNull Parcel parcel) {
573        int nelems = parcel.readInt();
574        List<KeyStatus> keyStatusList = new ArrayList(nelems);
575        while (nelems-- > 0) {
576            byte[] keyId = parcel.createByteArray();
577            int keyStatusCode = parcel.readInt();
578            keyStatusList.add(new KeyStatus(keyId, keyStatusCode));
579        }
580        return keyStatusList;
581    }
582
583    /**
584     * This method is called from native code when an event occurs.  This method
585     * just uses the EventHandler system to post the event back to the main app thread.
586     * We use a weak reference to the original MediaPlayer object so that the native
587     * code is safe from the object disappearing from underneath it.  (This is
588     * the cookie passed to native_setup().)
589     */
590    private static void postEventFromNative(@NonNull Object mediadrm_ref,
591            int what, int eventType, int extra, @Nullable Object obj)
592    {
593        MediaDrm md = (MediaDrm)((WeakReference<MediaDrm>)mediadrm_ref).get();
594        if (md == null) {
595            return;
596        }
597        if (md.mEventHandler != null) {
598            Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj);
599            md.mEventHandler.sendMessage(m);
600        }
601    }
602
603    /**
604     * Open a new session with the MediaDrm object.  A session ID is returned.
605     *
606     * @throws NotProvisionedException if provisioning is needed
607     * @throws ResourceBusyException if required resources are in use
608     */
609    @NonNull
610    public native byte[] openSession() throws NotProvisionedException,
611            ResourceBusyException;
612
613    /**
614     * Close a session on the MediaDrm object that was previously opened
615     * with {@link #openSession}.
616     */
617    public native void closeSession(@NonNull byte[] sessionId);
618
619    /**
620     * This key request type species that the keys will be for online use, they will
621     * not be saved to the device for subsequent use when the device is not connected
622     * to a network.
623     */
624    public static final int KEY_TYPE_STREAMING = 1;
625
626    /**
627     * This key request type specifies that the keys will be for offline use, they
628     * will be saved to the device for use when the device is not connected to a network.
629     */
630    public static final int KEY_TYPE_OFFLINE = 2;
631
632    /**
633     * This key request type specifies that previously saved offline keys should be released.
634     */
635    public static final int KEY_TYPE_RELEASE = 3;
636
637    /** @hide */
638    @IntDef({
639        KEY_TYPE_STREAMING,
640        KEY_TYPE_OFFLINE,
641        KEY_TYPE_RELEASE,
642    })
643    @Retention(RetentionPolicy.SOURCE)
644    public @interface KeyType {}
645
646    /**
647     * Contains the opaque data an app uses to request keys from a license server
648     */
649    public static final class KeyRequest {
650        private byte[] mData;
651        private String mDefaultUrl;
652        private int mRequestType;
653
654        /**
655         * Key request type is initial license request
656         */
657        public static final int REQUEST_TYPE_INITIAL = 0;
658
659        /**
660         * Key request type is license renewal
661         */
662        public static final int REQUEST_TYPE_RENEWAL = 1;
663
664        /**
665         * Key request type is license release
666         */
667        public static final int REQUEST_TYPE_RELEASE = 2;
668
669        /** @hide */
670        @IntDef({
671            REQUEST_TYPE_INITIAL,
672            REQUEST_TYPE_RENEWAL,
673            REQUEST_TYPE_RELEASE,
674        })
675        @Retention(RetentionPolicy.SOURCE)
676        public @interface RequestType {}
677
678        KeyRequest() {}
679
680        /**
681         * Get the opaque message data
682         */
683        @NonNull
684        public byte[] getData() {
685            if (mData == null) {
686                // this should never happen as mData is initialized in
687                // JNI after construction of the KeyRequest object. The check
688                // is needed here to guarantee @NonNull annotation.
689                throw new RuntimeException("KeyRequest is not initialized");
690            }
691            return mData;
692        }
693
694        /**
695         * Get the default URL to use when sending the key request message to a
696         * server, if known.  The app may prefer to use a different license
697         * server URL from other sources.
698         * This method returns an empty string if the default URL is not known.
699         */
700        @NonNull
701        public String getDefaultUrl() {
702            if (mDefaultUrl == null) {
703                // this should never happen as mDefaultUrl is initialized in
704                // JNI after construction of the KeyRequest object. The check
705                // is needed here to guarantee @NonNull annotation.
706                throw new RuntimeException("KeyRequest is not initialized");
707            }
708            return mDefaultUrl;
709        }
710
711        /**
712         * Get the type of the request
713         * @return one of {@link #REQUEST_TYPE_INITIAL},
714         * {@link #REQUEST_TYPE_RENEWAL} or {@link #REQUEST_TYPE_RELEASE}
715         */
716        @RequestType
717        public int getRequestType() { return mRequestType; }
718    };
719
720    /**
721     * A key request/response exchange occurs between the app and a license server
722     * to obtain or release keys used to decrypt encrypted content.
723     * <p>
724     * getKeyRequest() is used to obtain an opaque key request byte array that is
725     * delivered to the license server.  The opaque key request byte array is returned
726     * in KeyRequest.data.  The recommended URL to deliver the key request to is
727     * returned in KeyRequest.defaultUrl.
728     * <p>
729     * After the app has received the key request response from the server,
730     * it should deliver to the response to the DRM engine plugin using the method
731     * {@link #provideKeyResponse}.
732     *
733     * @param scope may be a sessionId or a keySetId, depending on the specified keyType.
734     * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE,
735     * scope should be set to the sessionId the keys will be provided to.  When the keyType
736     * is KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys
737     * being released. Releasing keys from a device invalidates them for all sessions.
738     * @param init container-specific data, its meaning is interpreted based on the
739     * mime type provided in the mimeType parameter.  It could contain, for example,
740     * the content ID, key ID or other data obtained from the content metadata that is
741     * required in generating the key request. init may be null when keyType is
742     * KEY_TYPE_RELEASE.
743     * @param mimeType identifies the mime type of the content
744     * @param keyType specifes the type of the request. The request may be to acquire
745     * keys for streaming or offline content, or to release previously acquired
746     * keys, which are identified by a keySetId.
747     * @param optionalParameters are included in the key request message to
748     * allow a client application to provide additional message parameters to the server.
749     * This may be {@code null} if no additional parameters are to be sent.
750     * @throws NotProvisionedException if reprovisioning is needed, due to a
751     * problem with the certifcate
752     */
753    @NonNull
754    public native KeyRequest getKeyRequest(
755            @NonNull byte[] scope, @Nullable byte[] init,
756            @Nullable String mimeType, @KeyType int keyType,
757            @Nullable HashMap<String, String> optionalParameters)
758            throws NotProvisionedException;
759
760
761    /**
762     * A key response is received from the license server by the app, then it is
763     * provided to the DRM engine plugin using provideKeyResponse.  When the
764     * response is for an offline key request, a keySetId is returned that can be
765     * used to later restore the keys to a new session with the method
766     * {@link #restoreKeys}.
767     * When the response is for a streaming or release request, null is returned.
768     *
769     * @param scope may be a sessionId or keySetId depending on the type of the
770     * response.  Scope should be set to the sessionId when the response is for either
771     * streaming or offline key requests.  Scope should be set to the keySetId when
772     * the response is for a release request.
773     * @param response the byte array response from the server
774     *
775     * @throws NotProvisionedException if the response indicates that
776     * reprovisioning is required
777     * @throws DeniedByServerException if the response indicates that the
778     * server rejected the request
779     */
780    @Nullable
781    public native byte[] provideKeyResponse(
782            @NonNull byte[] scope, @NonNull byte[] response)
783            throws NotProvisionedException, DeniedByServerException;
784
785
786    /**
787     * Restore persisted offline keys into a new session.  keySetId identifies the
788     * keys to load, obtained from a prior call to {@link #provideKeyResponse}.
789     *
790     * @param sessionId the session ID for the DRM session
791     * @param keySetId identifies the saved key set to restore
792     */
793    public native void restoreKeys(@NonNull byte[] sessionId, @NonNull byte[] keySetId);
794
795    /**
796     * Remove the current keys from a session.
797     *
798     * @param sessionId the session ID for the DRM session
799     */
800    public native void removeKeys(@NonNull byte[] sessionId);
801
802    /**
803     * Request an informative description of the key status for the session.  The status is
804     * in the form of {name, value} pairs.  Since DRM license policies vary by vendor,
805     * the specific status field names are determined by each DRM vendor.  Refer to your
806     * DRM provider documentation for definitions of the field names for a particular
807     * DRM engine plugin.
808     *
809     * @param sessionId the session ID for the DRM session
810     */
811    @NonNull
812    public native HashMap<String, String> queryKeyStatus(@NonNull byte[] sessionId);
813
814    /**
815     * Contains the opaque data an app uses to request a certificate from a provisioning
816     * server
817     */
818    public static final class ProvisionRequest {
819        ProvisionRequest() {}
820
821        /**
822         * Get the opaque message data
823         */
824        @NonNull
825        public byte[] getData() {
826            if (mData == null) {
827                // this should never happen as mData is initialized in
828                // JNI after construction of the KeyRequest object. The check
829                // is needed here to guarantee @NonNull annotation.
830                throw new RuntimeException("ProvisionRequest is not initialized");
831            }
832            return mData;
833        }
834
835        /**
836         * Get the default URL to use when sending the provision request
837         * message to a server, if known. The app may prefer to use a different
838         * provisioning server URL obtained from other sources.
839         * This method returns an empty string if the default URL is not known.
840         */
841        @NonNull
842        public String getDefaultUrl() {
843            if (mDefaultUrl == null) {
844                // this should never happen as mDefaultUrl is initialized in
845                // JNI after construction of the ProvisionRequest object. The check
846                // is needed here to guarantee @NonNull annotation.
847                throw new RuntimeException("ProvisionRequest is not initialized");
848            }
849            return mDefaultUrl;
850        }
851
852        private byte[] mData;
853        private String mDefaultUrl;
854    }
855
856    /**
857     * A provision request/response exchange occurs between the app and a provisioning
858     * server to retrieve a device certificate.  If provisionining is required, the
859     * EVENT_PROVISION_REQUIRED event will be sent to the event handler.
860     * getProvisionRequest is used to obtain the opaque provision request byte array that
861     * should be delivered to the provisioning server. The provision request byte array
862     * is returned in ProvisionRequest.data. The recommended URL to deliver the provision
863     * request to is returned in ProvisionRequest.defaultUrl.
864     */
865    @NonNull
866    public ProvisionRequest getProvisionRequest() {
867        return getProvisionRequestNative(CERTIFICATE_TYPE_NONE, "");
868    }
869
870    @NonNull
871    private native ProvisionRequest getProvisionRequestNative(int certType,
872           @NonNull String certAuthority);
873
874    /**
875     * After a provision response is received by the app, it is provided to the DRM
876     * engine plugin using this method.
877     *
878     * @param response the opaque provisioning response byte array to provide to the
879     * DRM engine plugin.
880     *
881     * @throws DeniedByServerException if the response indicates that the
882     * server rejected the request
883     */
884    public void provideProvisionResponse(@NonNull byte[] response)
885            throws DeniedByServerException {
886        provideProvisionResponseNative(response);
887    }
888
889    @NonNull
890    /* could there be a valid response with 0-sized certificate or key? */
891    private native Certificate provideProvisionResponseNative(@NonNull byte[] response)
892            throws DeniedByServerException;
893
894    /**
895     * Remove provisioning from a device.  Only system apps may unprovision a
896     * device.  Note that removing provisioning will invalidate any keys saved
897     * for offline use (KEY_TYPE_OFFLINE), which may render downloaded content
898     * unplayable until new licenses are acquired.  Since provisioning is global
899     * to the device, license invalidation will apply to all content downloaded
900     * by any app, so appropriate warnings should be given to the user.
901     * @hide
902     */
903    @SystemApi
904    public native void unprovisionDevice();
905
906    /**
907     * A means of enforcing limits on the number of concurrent streams per subscriber
908     * across devices is provided via SecureStop. This is achieved by securely
909     * monitoring the lifetime of sessions.
910     * <p>
911     * Information from the server related to the current playback session is written
912     * to persistent storage on the device when each MediaCrypto object is created.
913     * <p>
914     * In the normal case, playback will be completed, the session destroyed and the
915     * Secure Stops will be queried. The app queries secure stops and forwards the
916     * secure stop message to the server which verifies the signature and notifies the
917     * server side database that the session destruction has been confirmed. The persisted
918     * record on the client is only removed after positive confirmation that the server
919     * received the message using releaseSecureStops().
920     */
921    @NonNull
922    public native List<byte[]> getSecureStops();
923
924    /**
925     * Access secure stop by secure stop ID.
926     *
927     * @param ssid - The secure stop ID provided by the license server.
928     */
929    @NonNull
930    public native byte[] getSecureStop(@NonNull byte[] ssid);
931
932    /**
933     * Process the SecureStop server response message ssRelease.  After authenticating
934     * the message, remove the SecureStops identified in the response.
935     *
936     * @param ssRelease the server response indicating which secure stops to release
937     */
938    public native void releaseSecureStops(@NonNull byte[] ssRelease);
939
940    /**
941     * Remove all secure stops without requiring interaction with the server.
942     */
943     public native void releaseAllSecureStops();
944
945    /**
946     * String property name: identifies the maker of the DRM engine plugin
947     */
948    public static final String PROPERTY_VENDOR = "vendor";
949
950    /**
951     * String property name: identifies the version of the DRM engine plugin
952     */
953    public static final String PROPERTY_VERSION = "version";
954
955    /**
956     * String property name: describes the DRM engine plugin
957     */
958    public static final String PROPERTY_DESCRIPTION = "description";
959
960    /**
961     * String property name: a comma-separated list of cipher and mac algorithms
962     * supported by CryptoSession.  The list may be empty if the DRM engine
963     * plugin does not support CryptoSession operations.
964     */
965    public static final String PROPERTY_ALGORITHMS = "algorithms";
966
967    /** @hide */
968    @StringDef({
969        PROPERTY_VENDOR,
970        PROPERTY_VERSION,
971        PROPERTY_DESCRIPTION,
972        PROPERTY_ALGORITHMS,
973    })
974    @Retention(RetentionPolicy.SOURCE)
975    public @interface StringProperty {}
976
977    /**
978     * Read a DRM engine plugin String property value, given the property name string.
979     * <p>
980     * Standard fields names are:
981     * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION},
982     * {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHMS}
983     */
984    /* FIXME this throws IllegalStateException for invalid property names */
985    @NonNull
986    public native String getPropertyString(@NonNull @StringProperty String propertyName);
987
988    /**
989     * Byte array property name: the device unique identifier is established during
990     * device provisioning and provides a means of uniquely identifying each device.
991     */
992    /* FIXME this throws IllegalStateException for invalid property names */
993    public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
994
995    /** @hide */
996    @StringDef({
997        PROPERTY_DEVICE_UNIQUE_ID,
998    })
999    @Retention(RetentionPolicy.SOURCE)
1000    public @interface ArrayProperty {}
1001
1002    /**
1003     * Read a DRM engine plugin byte array property value, given the property name string.
1004     * <p>
1005     * Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID}
1006     */
1007    @NonNull
1008    public native byte[] getPropertyByteArray(@ArrayProperty String propertyName);
1009
1010    /**
1011     * Set a DRM engine plugin String property value.
1012     */
1013    public native void setPropertyString(
1014            @StringProperty String propertyName, @NonNull String value);
1015
1016    /**
1017     * Set a DRM engine plugin byte array property value.
1018     */
1019    public native void setPropertyByteArray(
1020            @ArrayProperty String propertyName, @NonNull byte[] value);
1021
1022    private static final native void setCipherAlgorithmNative(
1023            @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
1024
1025    private static final native void setMacAlgorithmNative(
1026            @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
1027
1028    @NonNull
1029    private static final native byte[] encryptNative(
1030            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
1031            @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv);
1032
1033    @NonNull
1034    private static final native byte[] decryptNative(
1035            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
1036            @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv);
1037
1038    @NonNull
1039    private static final native byte[] signNative(
1040            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
1041            @NonNull byte[] keyId, @NonNull byte[] message);
1042
1043    private static final native boolean verifyNative(
1044            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
1045            @NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature);
1046
1047    /**
1048     * In addition to supporting decryption of DASH Common Encrypted Media, the
1049     * MediaDrm APIs provide the ability to securely deliver session keys from
1050     * an operator's session key server to a client device, based on the factory-installed
1051     * root of trust, and then perform encrypt, decrypt, sign and verify operations
1052     * with the session key on arbitrary user data.
1053     * <p>
1054     * The CryptoSession class implements generic encrypt/decrypt/sign/verify methods
1055     * based on the established session keys.  These keys are exchanged using the
1056     * getKeyRequest/provideKeyResponse methods.
1057     * <p>
1058     * Applications of this capability could include securing various types of
1059     * purchased or private content, such as applications, books and other media,
1060     * photos or media delivery protocols.
1061     * <p>
1062     * Operators can create session key servers that are functionally similar to a
1063     * license key server, except that instead of receiving license key requests and
1064     * providing encrypted content keys which are used specifically to decrypt A/V media
1065     * content, the session key server receives session key requests and provides
1066     * encrypted session keys which can be used for general purpose crypto operations.
1067     * <p>
1068     * A CryptoSession is obtained using {@link #getCryptoSession}
1069     */
1070    public final class CryptoSession {
1071        private MediaDrm mDrm;
1072        private byte[] mSessionId;
1073
1074        CryptoSession(@NonNull MediaDrm drm, @NonNull byte[] sessionId,
1075                @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)
1076        {
1077            mSessionId = sessionId;
1078            mDrm = drm;
1079            setCipherAlgorithmNative(drm, sessionId, cipherAlgorithm);
1080            setMacAlgorithmNative(drm, sessionId, macAlgorithm);
1081        }
1082
1083        /**
1084         * Encrypt data using the CryptoSession's cipher algorithm
1085         *
1086         * @param keyid specifies which key to use
1087         * @param input the data to encrypt
1088         * @param iv the initialization vector to use for the cipher
1089         */
1090        @NonNull
1091        public byte[] encrypt(
1092                @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) {
1093            return encryptNative(mDrm, mSessionId, keyid, input, iv);
1094        }
1095
1096        /**
1097         * Decrypt data using the CryptoSessions's cipher algorithm
1098         *
1099         * @param keyid specifies which key to use
1100         * @param input the data to encrypt
1101         * @param iv the initialization vector to use for the cipher
1102         */
1103        @NonNull
1104        public byte[] decrypt(
1105                @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) {
1106            return decryptNative(mDrm, mSessionId, keyid, input, iv);
1107        }
1108
1109        /**
1110         * Sign data using the CryptoSessions's mac algorithm.
1111         *
1112         * @param keyid specifies which key to use
1113         * @param message the data for which a signature is to be computed
1114         */
1115        @NonNull
1116        public byte[] sign(@NonNull byte[] keyid, @NonNull byte[] message) {
1117            return signNative(mDrm, mSessionId, keyid, message);
1118        }
1119
1120        /**
1121         * Verify a signature using the CryptoSessions's mac algorithm. Return true
1122         * if the signatures match, false if they do no.
1123         *
1124         * @param keyid specifies which key to use
1125         * @param message the data to verify
1126         * @param signature the reference signature which will be compared with the
1127         *        computed signature
1128         */
1129        public boolean verify(
1130                @NonNull byte[] keyid, @NonNull byte[] message, @NonNull byte[] signature) {
1131            return verifyNative(mDrm, mSessionId, keyid, message, signature);
1132        }
1133    };
1134
1135    /**
1136     * Obtain a CryptoSession object which can be used to encrypt, decrypt,
1137     * sign and verify messages or data using the session keys established
1138     * for the session using methods {@link #getKeyRequest} and
1139     * {@link #provideKeyResponse} using a session key server.
1140     *
1141     * @param sessionId the session ID for the session containing keys
1142     * to be used for encrypt, decrypt, sign and/or verify
1143     * @param cipherAlgorithm the algorithm to use for encryption and
1144     * decryption ciphers. The algorithm string conforms to JCA Standard
1145     * Names for Cipher Transforms and is case insensitive.  For example
1146     * "AES/CBC/NoPadding".
1147     * @param macAlgorithm the algorithm to use for sign and verify
1148     * The algorithm string conforms to JCA Standard Names for Mac
1149     * Algorithms and is case insensitive.  For example "HmacSHA256".
1150     * <p>
1151     * The list of supported algorithms for a DRM engine plugin can be obtained
1152     * using the method {@link #getPropertyString} with the property name
1153     * "algorithms".
1154     */
1155    public CryptoSession getCryptoSession(
1156            @NonNull byte[] sessionId,
1157            @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)
1158    {
1159        return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm);
1160    }
1161
1162    /**
1163     * Contains the opaque data an app uses to request a certificate from a provisioning
1164     * server
1165     *
1166     * @hide - not part of the public API at this time
1167     */
1168    public static final class CertificateRequest {
1169        private byte[] mData;
1170        private String mDefaultUrl;
1171
1172        CertificateRequest(@NonNull byte[] data, @NonNull String defaultUrl) {
1173            mData = data;
1174            mDefaultUrl = defaultUrl;
1175        }
1176
1177        /**
1178         * Get the opaque message data
1179         */
1180        @NonNull
1181        public byte[] getData() { return mData; }
1182
1183        /**
1184         * Get the default URL to use when sending the certificate request
1185         * message to a server, if known. The app may prefer to use a different
1186         * certificate server URL obtained from other sources.
1187         */
1188        @NonNull
1189        public String getDefaultUrl() { return mDefaultUrl; }
1190    }
1191
1192    /**
1193     * Generate a certificate request, specifying the certificate type
1194     * and authority. The response received should be passed to
1195     * provideCertificateResponse.
1196     *
1197     * @param certType Specifies the certificate type.
1198     *
1199     * @param certAuthority is passed to the certificate server to specify
1200     * the chain of authority.
1201     *
1202     * @hide - not part of the public API at this time
1203     */
1204    @NonNull
1205    public CertificateRequest getCertificateRequest(
1206            @CertificateType int certType, @NonNull String certAuthority)
1207    {
1208        ProvisionRequest provisionRequest = getProvisionRequestNative(certType, certAuthority);
1209        return new CertificateRequest(provisionRequest.getData(),
1210                provisionRequest.getDefaultUrl());
1211    }
1212
1213    /**
1214     * Contains the wrapped private key and public certificate data associated
1215     * with a certificate.
1216     *
1217     * @hide - not part of the public API at this time
1218     */
1219    public static final class Certificate {
1220        Certificate() {}
1221
1222        /**
1223         * Get the wrapped private key data
1224         */
1225        @NonNull
1226        public byte[] getWrappedPrivateKey() {
1227            if (mWrappedKey == null) {
1228                // this should never happen as mWrappedKey is initialized in
1229                // JNI after construction of the KeyRequest object. The check
1230                // is needed here to guarantee @NonNull annotation.
1231                throw new RuntimeException("Cerfificate is not initialized");
1232            }
1233            return mWrappedKey;
1234        }
1235
1236        /**
1237         * Get the PEM-encoded certificate chain
1238         */
1239        @NonNull
1240        public byte[] getContent() {
1241            if (mCertificateData == null) {
1242                // this should never happen as mCertificateData is initialized in
1243                // JNI after construction of the KeyRequest object. The check
1244                // is needed here to guarantee @NonNull annotation.
1245                throw new RuntimeException("Cerfificate is not initialized");
1246            }
1247            return mCertificateData;
1248        }
1249
1250        private byte[] mWrappedKey;
1251        private byte[] mCertificateData;
1252    }
1253
1254
1255    /**
1256     * Process a response from the certificate server.  The response
1257     * is obtained from an HTTP Post to the url provided by getCertificateRequest.
1258     * <p>
1259     * The public X509 certificate chain and wrapped private key are returned
1260     * in the returned Certificate objec.  The certificate chain is in PEM format.
1261     * The wrapped private key should be stored in application private
1262     * storage, and used when invoking the signRSA method.
1263     *
1264     * @param response the opaque certificate response byte array to provide to the
1265     * DRM engine plugin.
1266     *
1267     * @throws DeniedByServerException if the response indicates that the
1268     * server rejected the request
1269     *
1270     * @hide - not part of the public API at this time
1271     */
1272    @NonNull
1273    public Certificate provideCertificateResponse(@NonNull byte[] response)
1274            throws DeniedByServerException {
1275        return provideProvisionResponseNative(response);
1276    }
1277
1278    @NonNull
1279    private static final native byte[] signRSANative(
1280            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
1281            @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message);
1282
1283    /**
1284     * Sign data using an RSA key
1285     *
1286     * @param sessionId a sessionId obtained from openSession on the MediaDrm object
1287     * @param algorithm the signing algorithm to use, e.g. "PKCS1-BlockType1"
1288     * @param wrappedKey - the wrapped (encrypted) RSA private key obtained
1289     * from provideCertificateResponse
1290     * @param message the data for which a signature is to be computed
1291     *
1292     * @hide - not part of the public API at this time
1293     */
1294    @NonNull
1295    public byte[] signRSA(
1296            @NonNull byte[] sessionId, @NonNull String algorithm,
1297            @NonNull byte[] wrappedKey, @NonNull byte[] message) {
1298        return signRSANative(this, sessionId, algorithm, wrappedKey, message);
1299    }
1300
1301    @Override
1302    protected void finalize() {
1303        native_finalize();
1304    }
1305
1306    public native final void release();
1307    private static native final void native_init();
1308
1309    private native final void native_setup(Object mediadrm_this, byte[] uuid);
1310
1311    private native final void native_finalize();
1312
1313    static {
1314        System.loadLibrary("media_jni");
1315        native_init();
1316    }
1317}
1318