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