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