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