MediaDrm.java revision 8a0c80fdcc46faa8cb8c9f4dda06f4b63ec2f906
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.util.Log;
29
30/**
31 * MediaDrm class can be used in conjunction with {@link android.media.MediaCrypto}
32 * to obtain licenses for decoding encrypted media data.
33 *
34 * Crypto schemes are assigned 16 byte UUIDs,
35 * the method {@link #isCryptoSchemeSupported} can be used to query if a given
36 * scheme is supported on the device.
37 *
38 * <a name="Callbacks"></a>
39 * <h3>Callbacks</h3>
40 * <p>Applications may want to register for informational events in order
41 * to be informed of some internal state update during playback or streaming.
42 * Registration for these events is done via a call to
43 * {@link #setOnEventListener(OnInfoListener)}setOnInfoListener,
44 * In order to receive the respective callback
45 * associated with this listener, applications are required to create
46 * MediaDrm objects on a thread with its own Looper running (main UI
47 * thread by default has a Looper running).
48 *
49 * @hide -- don't expose yet
50 */
51public final class MediaDrm {
52
53    private final static String TAG = "MediaDrm";
54
55    private EventHandler mEventHandler;
56    private OnEventListener mOnEventListener;
57
58    private int mNativeContext;
59
60    /**
61     * Query if the given scheme identified by its UUID is supported on
62     * this device.
63     * @param uuid The UUID of the crypto scheme.
64     */
65    public static final boolean isCryptoSchemeSupported(UUID uuid) {
66        return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid));
67    }
68
69    private static final byte[] getByteArrayFromUUID(UUID uuid) {
70        long msb = uuid.getMostSignificantBits();
71        long lsb = uuid.getLeastSignificantBits();
72
73        byte[] uuidBytes = new byte[16];
74        for (int i = 0; i < 8; ++i) {
75            uuidBytes[i] = (byte)(msb >>> (8 * (7 - i)));
76            uuidBytes[8 + i] = (byte)(lsb >>> (8 * (7 - i)));
77        }
78
79        return uuidBytes;
80    }
81
82    private static final native boolean isCryptoSchemeSupportedNative(byte[] uuid);
83
84    /**
85     * Instantiate a MediaDrm object using opaque, crypto scheme specific
86     * data.
87     * @param uuid The UUID of the crypto scheme.
88     */
89    public MediaDrm(UUID uuid) throws MediaDrmException {
90        Looper looper;
91        if ((looper = Looper.myLooper()) != null) {
92            mEventHandler = new EventHandler(this, looper);
93        } else if ((looper = Looper.getMainLooper()) != null) {
94            mEventHandler = new EventHandler(this, looper);
95        } else {
96            mEventHandler = null;
97        }
98
99        /* Native setup requires a weak reference to our object.
100         * It's easier to create it here than in C++.
101         */
102        native_setup(new WeakReference<MediaDrm>(this),
103                     getByteArrayFromUUID(uuid));
104    }
105
106    /**
107     * Register a callback to be invoked when an event occurs
108     *
109     * @param listener the callback that will be run
110     */
111    public void setOnEventListener(OnEventListener listener)
112    {
113        mOnEventListener = listener;
114    }
115
116    /**
117     * Interface definition for a callback to be invoked when a drm event
118     * occurs.
119     */
120    public interface OnEventListener
121    {
122        /**
123         * Called when an event occurs that requires the app to be notified
124         *
125         * @param md the MediaDrm object on which the event occurred
126         * @param sessionId the DRM session ID on which the event occurred
127         * @param event indicates the event type
128         * @param extra an secondary error code
129         * @param data optional byte array of data that may be associated with the event
130         */
131        void onEvent(MediaDrm md, byte[] sessionId, int event, int extra, byte[] data);
132    }
133
134    /* Do not change these values without updating their counterparts
135     * in include/media/mediadrm.h!
136     */
137    private static final int DRM_EVENT = 200;
138
139    private class EventHandler extends Handler
140    {
141        private MediaDrm mMediaDrm;
142
143        public EventHandler(MediaDrm md, Looper looper) {
144            super(looper);
145            mMediaDrm = md;
146        }
147
148        @Override
149        public void handleMessage(Message msg) {
150            if (mMediaDrm.mNativeContext == 0) {
151                Log.w(TAG, "MediaDrm went away with unhandled events");
152                return;
153            }
154            switch(msg.what) {
155
156            case DRM_EVENT:
157                Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
158
159                if (mOnEventListener != null) {
160                    Bundle bundle = msg.getData();
161                    byte[] sessionId = bundle.getByteArray("sessionId");
162                    byte[] data = bundle.getByteArray("data");
163                    mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
164                }
165                return;
166
167            default:
168                Log.e(TAG, "Unknown message type " + msg.what);
169                return;
170            }
171        }
172    }
173
174    /*
175     * Called from native code when an interesting event happens.  This method
176     * just uses the EventHandler system to post the event back to the main app thread.
177     * We use a weak reference to the original MediaPlayer object so that the native
178     * code is safe from the object disappearing from underneath it.  (This is
179     * the cookie passed to native_setup().)
180     */
181    private static void postEventFromNative(Object mediadrm_ref,
182                                            int what, int arg1, int arg2, Object obj)
183    {
184        MediaDrm md = (MediaDrm)((WeakReference)mediadrm_ref).get();
185        if (md == null) {
186            return;
187        }
188        if (md.mEventHandler != null) {
189            Message m = md.mEventHandler.obtainMessage(what, arg1, arg2, obj);
190            md.mEventHandler.sendMessage(m);
191        }
192    }
193
194    /**
195     *  Open a new session with the MediaDrm object.  A session ID is returned.
196     */
197    public native byte[] openSession() throws MediaDrmException;
198
199    /**
200     *  Close a session on the MediaDrm object.
201     */
202    public native void closeSession(byte[] sessionId) throws MediaDrmException;
203
204    public static final int MEDIA_DRM_LICENSE_TYPE_STREAMING = 1;
205    public static final int MEDIA_DRM_LICENSE_TYPE_OFFLINE = 2;
206
207    public final class LicenseRequest {
208        public LicenseRequest() {}
209        public byte[] data;
210        public String defaultUrl;
211    };
212
213    /**
214     * A license request/response exchange occurs between the app and a License
215     * Server to obtain the keys required to decrypt the content.  getLicenseRequest()
216     * is used to obtain an opaque license request byte array that is delivered to the
217     * license server.  The opaque license request byte array is returned in
218     * LicenseReqeust.data.  The recommended URL to deliver the license request to is
219     * returned in LicenseRequest.defaultUrl
220     *
221     * @param sessonId the session ID for the drm session
222     * @param init container-specific data, its meaning is interpreted based on the
223     * mime type provided in the mimeType parameter.  It could contain, for example,
224     * the content ID, key ID or other data obtained from the content metadata that is
225     * required in generating the license request.
226     * @param mimeType identifies the mime type of the content
227     * @param licenseType specifes if the license is for streaming or offline content
228     * @param optionalParameters are included in the license server request message to
229     * allow a client application to provide additional message parameters to the server.
230     */
231    public native LicenseRequest getLicenseRequest( byte[] sessionId, byte[] init,
232                                                    String mimeType, int licenseType,
233                                                    HashMap<String, String> optionalParameters )
234        throws MediaDrmException;
235
236    /**
237     * After a license response is received by the app, it is provided to the DRM plugin
238     * using provideLicenseResponse.
239     *
240     * @param sessionId the session ID for the DRM session
241     * @param response the byte array response from the server
242     */
243    public native void provideLicenseResponse( byte[] sessionId, byte[] response )
244        throws MediaDrmException;
245
246    /**
247     * Remove the keys associated with a license for a session
248     * @param sessionId the session ID for the DRM session
249     */
250    public native void removeLicense( byte[] sessionId ) throws MediaDrmException;
251
252    /**
253     * Request an informative description of the license for the session.  The status is
254     * in the form of {name, value} pairs.  Since DRM license policies vary by vendor,
255     * the specific status field names are determined by each DRM vendor.  Refer to your
256     * DRM provider documentation for definitions of the field names for a particular
257     * DrmEngine.
258     *
259     * @param sessionId the session ID for the DRM session
260     */
261    public native HashMap<String, String> queryLicenseStatus( byte[] sessionId )
262        throws MediaDrmException;
263
264    public final class ProvisionRequest {
265        public ProvisionRequest() {}
266        public byte[] data;
267        public String defaultUrl;
268    }
269
270    /**
271     * A provision request/response exchange occurs between the app and a provisioning
272     * server to retrieve a device certificate.  getProvisionRequest is used to obtain
273     * an opaque license request byte array that is delivered to the provisioning server.
274     * The opaque provision request byte array is returned in ProvisionRequest.data
275     * The recommended URL to deliver the license request to is returned in
276     * ProvisionRequest.defaultUrl.
277     */
278    public native ProvisionRequest getProvisionRequest() throws MediaDrmException;
279
280    /**
281     * After a provision response is received by the app, it is provided to the DRM
282     * plugin using this method.
283     *
284     * @param response the opaque provisioning response byte array to provide to the
285     * DrmEngine.
286     */
287    public native void provideProvisionResponse( byte[] response )
288        throws MediaDrmException;
289
290    /**
291     * A means of enforcing the contractual requirement for a concurrent stream limit
292     * per subscriber across devices is provided via SecureStop.  SecureStop is a means
293     * of securely monitoring the lifetime of sessions. Since playback on a device can
294     * be interrupted due to reboot, power failure, etc. a means of persisting the
295     * lifetime information on the device is needed.
296     *
297     * A signed version of the sessionID is written to persistent storage on the device
298     * when each MediaCrypto object is created. The sessionID is signed by the device
299     * private key to prevent tampering.
300     *
301     * In the normal case, playback will be completed, the session destroyed and the
302     * Secure Stops will be queried. The App queries secure stops and forwards the
303     * secure stop message to the server which verifies the signature and notifies the
304     * server side database that the session destruction has been confirmed. The persisted
305     * record on the client is only removed after positive confirmation that the server
306     * received the message using releaseSecureStops().
307     */
308    public native List<byte[]> getSecureStops() throws MediaDrmException;
309
310
311    /**
312     * Process the SecureStop server response message ssRelease.  After authenticating
313     * the message, remove the SecureStops identiied in the response.
314     *
315     * @param ssRelease the server response indicating which secure stops to release
316     */
317    public native void releaseSecureStops( byte[] ssRelease )
318        throws MediaDrmException;
319
320
321    /**
322     * Read a Drm plugin property value, given the property name string.  There are several
323     * forms of property access functions, depending on the data type returned.
324     *
325     * Standard fields names are:
326     *   vendor         String - identifies the maker of the plugin
327     *   version        String - identifies the version of the plugin
328     *   description    String - describes the plugin
329     *   deviceUniqueId byte[] - The device unique identifier is established during device
330     *                             provisioning and provides a means of uniquely identifying
331     *                             each device
332     */
333    public native String getPropertyString( String propertyName )
334        throws MediaDrmException;
335
336    public native byte[] getPropertyByteArray( String propertyName )
337        throws MediaDrmException;
338
339    /**
340     * Write a Drm plugin property value.  There are several forms of property setting
341     * functions, depending on the data type being set.
342     */
343    public native void setPropertyString( String propertyName, String value )
344        throws MediaDrmException;
345
346    public native void setPropertyByteArray( String propertyName, byte[] value )
347        throws MediaDrmException;
348
349    @Override
350    protected void finalize() {
351        native_finalize();
352    }
353
354    public native final void release();
355    private static native final void native_init();
356
357    private native final void native_setup(Object mediadrm_this, byte[] uuid)
358        throws MediaDrmException;
359
360    private native final void native_finalize();
361
362    static {
363        System.loadLibrary("media_jni");
364        native_init();
365    }
366}
367