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