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