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