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