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