Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(340)

Side by Side Diff: media/base/android/java/src/org/chromium/media/MediaDrmBridge.java

Issue 850183002: media: Support unprefixed EME API on Android. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase and address comments. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.media; 5 package org.chromium.media;
6 6
7 import android.media.MediaCrypto; 7 import android.media.MediaCrypto;
8 import android.media.MediaDrm; 8 import android.media.MediaDrm;
9 import android.os.AsyncTask; 9 import android.os.AsyncTask;
10 import android.os.Handler; 10 import android.os.Handler;
11 import android.util.Log; 11 import android.util.Log;
12 12
13 import org.apache.http.HttpResponse; 13 import org.apache.http.HttpResponse;
14 import org.apache.http.client.ClientProtocolException; 14 import org.apache.http.client.ClientProtocolException;
15 import org.apache.http.client.HttpClient; 15 import org.apache.http.client.HttpClient;
16 import org.apache.http.client.methods.HttpPost; 16 import org.apache.http.client.methods.HttpPost;
17 import org.apache.http.impl.client.DefaultHttpClient; 17 import org.apache.http.impl.client.DefaultHttpClient;
18 import org.apache.http.util.EntityUtils; 18 import org.apache.http.util.EntityUtils;
19 import org.chromium.base.CalledByNative; 19 import org.chromium.base.CalledByNative;
20 import org.chromium.base.JNINamespace; 20 import org.chromium.base.JNINamespace;
21 21
22 import java.io.IOException; 22 import java.io.IOException;
23 import java.nio.ByteBuffer; 23 import java.nio.ByteBuffer;
24 import java.nio.ByteOrder; 24 import java.nio.ByteOrder;
25 import java.util.ArrayDeque; 25 import java.util.ArrayDeque;
26 import java.util.Arrays;
26 import java.util.HashMap; 27 import java.util.HashMap;
27 import java.util.UUID; 28 import java.util.UUID;
28 29
29 /** 30 /**
30 * A wrapper of the android MediaDrm class. Each MediaDrmBridge manages multiple 31 * A wrapper of the android MediaDrm class. Each MediaDrmBridge manages multiple
31 * sessions for a single MediaSourcePlayer. 32 * sessions for a single MediaSourcePlayer.
32 */ 33 */
33 @JNINamespace("media") 34 @JNINamespace("media")
34 public class MediaDrmBridge { 35 public class MediaDrmBridge {
35 // Implementation Notes: 36 // Implementation Notes:
(...skipping 17 matching lines...) Expand all
53 // release() immediately after the error happens (e.g. after mMediaDrm) 54 // release() immediately after the error happens (e.g. after mMediaDrm)
54 // calls. Indirect calls should not call release() again to avoid 55 // calls. Indirect calls should not call release() again to avoid
55 // duplication (even though it doesn't hurt to call release() twice). 56 // duplication (even though it doesn't hurt to call release() twice).
56 57
57 private static final String TAG = "MediaDrmBridge"; 58 private static final String TAG = "MediaDrmBridge";
58 private static final String SECURITY_LEVEL = "securityLevel"; 59 private static final String SECURITY_LEVEL = "securityLevel";
59 private static final String PRIVACY_MODE = "privacyMode"; 60 private static final String PRIVACY_MODE = "privacyMode";
60 private static final String SESSION_SHARING = "sessionSharing"; 61 private static final String SESSION_SHARING = "sessionSharing";
61 private static final String ENABLE = "enable"; 62 private static final String ENABLE = "enable";
62 private static final int INVALID_SESSION_ID = 0; 63 private static final int INVALID_SESSION_ID = 0;
64 private static final char[] HEX_CHAR_LOOKUP = "0123456789ABCDEF".toCharArray ();
65
66 // KeyStatus for CDM key information. MUST keep this value in sync with
67 // CdmKeyInformation::KeyStatus.
68 private static final int KEY_STATUS_USABLE = 0;
69 private static final int KEY_STATUS_INTERNAL_ERROR = 1;
70 private static final int KEY_STATUS_EXPIRED = 2;
71 private static final int KEY_STATUS_OUTPUT_NOT_ALLOWED = 3;
63 72
64 private MediaDrm mMediaDrm; 73 private MediaDrm mMediaDrm;
65 private long mNativeMediaDrmBridge; 74 private long mNativeMediaDrmBridge;
66 private UUID mSchemeUUID; 75 private UUID mSchemeUUID;
67 private Handler mHandler; 76 private Handler mHandler;
68 77
69 // A session only for the purpose of creating a MediaCrypto object. 78 // A session only for the purpose of creating a MediaCrypto object.
70 // This session is opened when createSession() is called for the first 79 // This session is opened when createSession() is called for the first
71 // time. All following createSession() calls will create a new session and 80 // time. All following createSession() calls will create a new session and
72 // use it to call getKeyRequest(). No getKeyRequest() should ever be called 81 // use it to call getKeyRequest(). No getKeyRequest() should ever be called
73 // on |mMediaCryptoSession|. 82 // on |mMediaCryptoSession|.
74 private ByteBuffer mMediaCryptoSession; 83 private byte[] mMediaCryptoSession;
75 private MediaCrypto mMediaCrypto; 84 private MediaCrypto mMediaCrypto;
76 85
77 // The map of all opened sessions to their session reference IDs. 86 // The map of all opened sessions (excluding mMediaCryptoSession) to their m ime types.
78 private HashMap<ByteBuffer, Integer> mSessionIds; 87 private HashMap<ByteBuffer, String> mSessionIds;
79 // The map of all opened sessions to their mime types.
80 private HashMap<ByteBuffer, String> mSessionMimeTypes;
81 88
82 // The queue of all pending createSession() data. 89 // The queue of all pending createSession() data.
83 private ArrayDeque<PendingCreateSessionData> mPendingCreateSessionDataQueue; 90 private ArrayDeque<PendingCreateSessionData> mPendingCreateSessionDataQueue;
84 91
85 private boolean mResetDeviceCredentialsPending; 92 private boolean mResetDeviceCredentialsPending;
86 93
87 // MediaDrmBridge is waiting for provisioning response from the server. 94 // MediaDrmBridge is waiting for provisioning response from the server.
88 // 95 //
89 // Notes about NotProvisionedException: This exception can be thrown in a 96 // Notes about NotProvisionedException: This exception can be thrown in a
90 // lot of cases. To streamline implementation, we do not catch it in private 97 // lot of cases. To streamline implementation, we do not catch it in private
91 // non-native methods and only catch it in public APIs. 98 // non-native methods and only catch it in public APIs.
92 private boolean mProvisioningPending; 99 private boolean mProvisioningPending;
93 100
94 /** 101 /**
95 * This class contains data needed to call createSession(). 102 * This class contains data needed to call createSession().
96 */ 103 */
97 private static class PendingCreateSessionData { 104 private static class PendingCreateSessionData {
98 private final int mSessionId;
99 private final byte[] mInitData; 105 private final byte[] mInitData;
100 private final String mMimeType; 106 private final String mMimeType;
107 private final long mPromiseId;
101 108
102 private PendingCreateSessionData(int sessionId, byte[] initData, String mimeType) { 109 private PendingCreateSessionData(byte[] initData, String mimeType, long promiseId) {
103 mSessionId = sessionId;
104 mInitData = initData; 110 mInitData = initData;
105 mMimeType = mimeType; 111 mMimeType = mimeType;
106 } 112 mPromiseId = promiseId;
107
108 private int sessionId() {
109 return mSessionId;
110 } 113 }
111 114
112 private byte[] initData() { 115 private byte[] initData() {
113 return mInitData; 116 return mInitData;
114 } 117 }
115 118
116 private String mimeType() { 119 private String mimeType() {
117 return mMimeType; 120 return mMimeType;
118 } 121 }
122
123 private long promiseId() {
124 return mPromiseId;
125 }
119 } 126 }
120 127
121 private static UUID getUUIDFromBytes(byte[] data) { 128 private static UUID getUUIDFromBytes(byte[] data) {
122 if (data.length != 16) { 129 if (data.length != 16) {
123 return null; 130 return null;
124 } 131 }
125 long mostSigBits = 0; 132 long mostSigBits = 0;
126 long leastSigBits = 0; 133 long leastSigBits = 0;
127 for (int i = 0; i < 8; i++) { 134 for (int i = 0; i < 8; i++) {
128 mostSigBits = (mostSigBits << 8) | (data[i] & 0xff); 135 mostSigBits = (mostSigBits << 8) | (data[i] & 0xff);
129 } 136 }
130 for (int i = 8; i < 16; i++) { 137 for (int i = 8; i < 16; i++) {
131 leastSigBits = (leastSigBits << 8) | (data[i] & 0xff); 138 leastSigBits = (leastSigBits << 8) | (data[i] & 0xff);
132 } 139 }
133 return new UUID(mostSigBits, leastSigBits); 140 return new UUID(mostSigBits, leastSigBits);
134 } 141 }
135 142
136 /** 143 /**
137 * Gets session associated with the sessionId. 144 * Converts byte array to hex string for logging.
qinmin 2015/01/15 18:17:25 nit: s/Converts/Convert/
xhwang 2015/01/15 19:03:44 Done.
138 * 145 * This is modified from BytesToHexString() in url/url_canon_unittest.cc.
139 * @return session if sessionId maps a valid opened session. Returns null
140 * otherwise.
141 */ 146 */
142 private ByteBuffer getSession(int sessionId) { 147 private static String bytesToHexString(byte[] bytes) {
143 for (ByteBuffer session : mSessionIds.keySet()) { 148 StringBuffer hexString = new StringBuffer();
144 if (mSessionIds.get(session) == sessionId) { 149 for (int i = 0; i < bytes.length; ++i) {
145 return session; 150 hexString.append(HEX_CHAR_LOOKUP[bytes[i] >>> 4]);
146 } 151 hexString.append(HEX_CHAR_LOOKUP[bytes[i] & 0xf]);
147 } 152 }
148 return null; 153 return hexString.toString();
149 } 154 }
150 155
151 private MediaDrmBridge(UUID schemeUUID, long nativeMediaDrmBridge) 156 private MediaDrmBridge(UUID schemeUUID, long nativeMediaDrmBridge)
152 throws android.media.UnsupportedSchemeException { 157 throws android.media.UnsupportedSchemeException {
153 mSchemeUUID = schemeUUID; 158 mSchemeUUID = schemeUUID;
154 mMediaDrm = new MediaDrm(schemeUUID); 159 mMediaDrm = new MediaDrm(schemeUUID);
155 mNativeMediaDrmBridge = nativeMediaDrmBridge; 160 mNativeMediaDrmBridge = nativeMediaDrmBridge;
156 mHandler = new Handler(); 161 mHandler = new Handler();
157 mSessionIds = new HashMap<ByteBuffer, Integer>(); 162 mSessionIds = new HashMap<ByteBuffer, String>();
158 mSessionMimeTypes = new HashMap<ByteBuffer, String>();
159 mPendingCreateSessionDataQueue = new ArrayDeque<PendingCreateSessionData >(); 163 mPendingCreateSessionDataQueue = new ArrayDeque<PendingCreateSessionData >();
160 mResetDeviceCredentialsPending = false; 164 mResetDeviceCredentialsPending = false;
161 mProvisioningPending = false; 165 mProvisioningPending = false;
162 166
163 mMediaDrm.setOnEventListener(new MediaDrmListener()); 167 mMediaDrm.setOnEventListener(new MediaDrmListener());
164 mMediaDrm.setPropertyString(PRIVACY_MODE, ENABLE); 168 mMediaDrm.setPropertyString(PRIVACY_MODE, ENABLE);
165 mMediaDrm.setPropertyString(SESSION_SHARING, ENABLE); 169 mMediaDrm.setPropertyString(SESSION_SHARING, ENABLE);
166 170
167 // We could open a MediaCrypto session here to support faster start of 171 // We could open a MediaCrypto session here to support faster start of
168 // clear lead (no need to wait for createSession()). But on 172 // clear lead (no need to wait for createSession()). But on
(...skipping 14 matching lines...) Expand all
183 assert !mProvisioningPending; 187 assert !mProvisioningPending;
184 assert mMediaCryptoSession == null; 188 assert mMediaCryptoSession == null;
185 assert mMediaCrypto == null; 189 assert mMediaCrypto == null;
186 190
187 // Open media crypto session. 191 // Open media crypto session.
188 mMediaCryptoSession = openSession(); 192 mMediaCryptoSession = openSession();
189 if (mMediaCryptoSession == null) { 193 if (mMediaCryptoSession == null) {
190 Log.e(TAG, "Cannot create MediaCrypto Session."); 194 Log.e(TAG, "Cannot create MediaCrypto Session.");
191 return false; 195 return false;
192 } 196 }
193 Log.d(TAG, "MediaCrypto Session created: " + mMediaCryptoSession); 197 Log.d(TAG, "MediaCrypto Session created: " + bytesToHexString(mMediaCryp toSession));
194 198
195 // Create MediaCrypto object. 199 // Create MediaCrypto object.
196 try { 200 try {
197 if (MediaCrypto.isCryptoSchemeSupported(mSchemeUUID)) { 201 if (MediaCrypto.isCryptoSchemeSupported(mSchemeUUID)) {
198 final byte[] mediaCryptoSession = mMediaCryptoSession.array(); 202 mMediaCrypto = new MediaCrypto(mSchemeUUID, mMediaCryptoSession) ;
199 mMediaCrypto = new MediaCrypto(mSchemeUUID, mediaCryptoSession);
200 Log.d(TAG, "MediaCrypto successfully created!"); 203 Log.d(TAG, "MediaCrypto successfully created!");
201 mSessionIds.put(mMediaCryptoSession, INVALID_SESSION_ID);
202 // Notify the native code that MediaCrypto is ready. 204 // Notify the native code that MediaCrypto is ready.
203 nativeOnMediaCryptoReady(mNativeMediaDrmBridge); 205 nativeOnMediaCryptoReady(mNativeMediaDrmBridge);
204 return true; 206 return true;
205 } else { 207 } else {
206 Log.e(TAG, "Cannot create MediaCrypto for unsupported scheme."); 208 Log.e(TAG, "Cannot create MediaCrypto for unsupported scheme.");
207 } 209 }
208 } catch (android.media.MediaCryptoException e) { 210 } catch (android.media.MediaCryptoException e) {
209 Log.e(TAG, "Cannot create MediaCrypto", e); 211 Log.e(TAG, "Cannot create MediaCrypto", e);
210 } 212 }
211 213
212 release(); 214 release();
213 return false; 215 return false;
214 } 216 }
215 217
216 /** 218 /**
217 * Open a new session.. 219 * Open a new session.
218 * 220 *
219 * @return the session opened. Returns null if unexpected error happened. 221 * @return ID of the session opened. Returns null if unexpected error happen ed.
220 */ 222 */
221 private ByteBuffer openSession() throws android.media.NotProvisionedExceptio n { 223 private byte[] openSession() throws android.media.NotProvisionedException {
222 assert mMediaDrm != null; 224 assert mMediaDrm != null;
223 try { 225 try {
224 byte[] session = mMediaDrm.openSession(); 226 byte[] sessionId = mMediaDrm.openSession();
225 // ByteBuffer.wrap() is backed by the byte[]. Make a clone here in 227 // Make a clone here in case the underlying byte[] is modified.
226 // case the underlying byte[] is modified. 228 return sessionId.clone();
227 return ByteBuffer.wrap(session.clone());
228 } catch (java.lang.RuntimeException e) { // TODO(xhwang): Drop this? 229 } catch (java.lang.RuntimeException e) { // TODO(xhwang): Drop this?
229 Log.e(TAG, "Cannot open a new session", e); 230 Log.e(TAG, "Cannot open a new session", e);
230 release(); 231 release();
231 return null; 232 return null;
232 } catch (android.media.NotProvisionedException e) { 233 } catch (android.media.NotProvisionedException e) {
233 // Throw NotProvisionedException so that we can startProvisioning(). 234 // Throw NotProvisionedException so that we can startProvisioning().
234 throw e; 235 throw e;
235 } catch (android.media.MediaDrmException e) { 236 } catch (android.media.MediaDrmException e) {
236 // Other MediaDrmExceptions (e.g. ResourceBusyException) are not 237 // Other MediaDrmExceptions (e.g. ResourceBusyException) are not
237 // recoverable. 238 // recoverable.
238 Log.e(TAG, "Cannot open a new session", e); 239 Log.e(TAG, "Cannot open a new session", e);
239 release(); 240 release();
240 return null; 241 return null;
241 } 242 }
242 } 243 }
243 244
244 /** 245 /**
245 * Close a session.
246 *
247 * @param session to be closed.
248 */
249 private void closeSession(ByteBuffer session) {
250 assert mMediaDrm != null;
251 mMediaDrm.closeSession(session.array());
252 }
253
254 /**
255 * Check whether the crypto scheme is supported for the given container. 246 * Check whether the crypto scheme is supported for the given container.
256 * If |containerMimeType| is an empty string, we just return whether 247 * If |containerMimeType| is an empty string, we just return whether
257 * the crypto scheme is supported. 248 * the crypto scheme is supported.
258 * 249 *
259 * @return true if the container and the crypto scheme is supported, or 250 * @return true if the container and the crypto scheme is supported, or
260 * false otherwise. 251 * false otherwise.
261 */ 252 */
262 @CalledByNative 253 @CalledByNative
263 private static boolean isCryptoSchemeSupported(byte[] schemeUUID, String con tainerMimeType) { 254 private static boolean isCryptoSchemeSupported(byte[] schemeUUID, String con tainerMimeType) {
264 UUID cryptoScheme = getUUIDFromBytes(schemeUUID); 255 UUID cryptoScheme = getUUIDFromBytes(schemeUUID);
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
352 } 343 }
353 344
354 /** 345 /**
355 * Release the MediaDrmBridge object. 346 * Release the MediaDrmBridge object.
356 */ 347 */
357 @CalledByNative 348 @CalledByNative
358 private void release() { 349 private void release() {
359 // Do not reset mHandler and mNativeMediaDrmBridge so that we can still 350 // Do not reset mHandler and mNativeMediaDrmBridge so that we can still
360 // post KeyError back to native code. 351 // post KeyError back to native code.
361 352
353 for (PendingCreateSessionData data : mPendingCreateSessionDataQueue) {
354 onPromiseRejected(data.promiseId(), "Create session aborted.");
355 }
362 mPendingCreateSessionDataQueue.clear(); 356 mPendingCreateSessionDataQueue.clear();
363 mPendingCreateSessionDataQueue = null; 357 mPendingCreateSessionDataQueue = null;
364 358
365 for (ByteBuffer session : mSessionIds.keySet()) { 359 for (ByteBuffer sessionId : mSessionIds.keySet()) {
366 closeSession(session); 360 mMediaDrm.closeSession(sessionId.array());
367 } 361 }
368 mSessionIds.clear(); 362 mSessionIds.clear();
369 mSessionIds = null; 363 mSessionIds = null;
370 mSessionMimeTypes.clear();
371 mSessionMimeTypes = null;
372 364
373 // This session was closed in the "for" loop above. 365 // This session was closed in the "for" loop above.
374 mMediaCryptoSession = null; 366 mMediaCryptoSession = null;
375 367
376 if (mMediaCrypto != null) { 368 if (mMediaCrypto != null) {
377 mMediaCrypto.release(); 369 mMediaCrypto.release();
378 mMediaCrypto = null; 370 mMediaCrypto = null;
379 } 371 }
380 372
381 if (mMediaDrm != null) { 373 if (mMediaDrm != null) {
382 mMediaDrm.release(); 374 mMediaDrm.release();
383 mMediaDrm = null; 375 mMediaDrm = null;
384 } 376 }
385 } 377 }
386 378
387 /** 379 /**
388 * Get a key request. 380 * Get a key request.
389 * 381 *
390 * @param session Session on which we need to get the key request. 382 * @param session Session on which we need to get the key request.
qinmin 2015/01/15 18:17:25 nit:update the javadoc
xhwang 2015/01/15 19:03:44 Done.
391 * @param data Data needed to get the key request. 383 * @param data Data needed to get the key request.
392 * @param mime Mime type to get the key request. 384 * @param mime Mime type to get the key request.
393 * 385 *
394 * @return the key request. 386 * @return the key request.
395 */ 387 */
396 private MediaDrm.KeyRequest getKeyRequest(ByteBuffer session, byte[] data, S tring mime) 388 private MediaDrm.KeyRequest getKeyRequest(byte[] sessionId, byte[] data, Str ing mime)
397 throws android.media.NotProvisionedException { 389 throws android.media.NotProvisionedException {
398 assert mMediaDrm != null; 390 assert mMediaDrm != null;
399 assert mMediaCrypto != null; 391 assert mMediaCrypto != null;
400 assert !mProvisioningPending; 392 assert !mProvisioningPending;
401 393
402 HashMap<String, String> optionalParameters = new HashMap<String, String> (); 394 HashMap<String, String> optionalParameters = new HashMap<String, String> ();
403 MediaDrm.KeyRequest request = mMediaDrm.getKeyRequest( 395 MediaDrm.KeyRequest request = mMediaDrm.getKeyRequest(
404 session.array(), data, mime, MediaDrm.KEY_TYPE_STREAMING, option alParameters); 396 sessionId, data, mime, MediaDrm.KEY_TYPE_STREAMING, optionalPara meters);
405 String result = (request != null) ? "successed" : "failed"; 397 String result = (request != null) ? "successed" : "failed";
406 Log.d(TAG, "getKeyRequest " + result + "!"); 398 Log.d(TAG, "getKeyRequest " + result + "!");
407 return request; 399 return request;
408 } 400 }
409 401
410 /** 402 /**
411 * Save data to |mPendingCreateSessionDataQueue| so that we can resume the 403 * Save data to |mPendingCreateSessionDataQueue| so that we can resume the
412 * createSession() call later. 404 * createSession() call later.
413 */ 405 */
qinmin 2015/01/15 18:17:25 nit: @param?
xhwang 2015/01/15 19:03:44 Done.
414 private void savePendingCreateSessionData(int sessionId, byte[] initData, St ring mime) { 406 private void savePendingCreateSessionData(byte[] initData, String mime, long promiseId) {
415 Log.d(TAG, "savePendingCreateSessionData()"); 407 Log.d(TAG, "savePendingCreateSessionData()");
416 mPendingCreateSessionDataQueue.offer( 408 mPendingCreateSessionDataQueue.offer(
417 new PendingCreateSessionData(sessionId, initData, mime)); 409 new PendingCreateSessionData(initData, mime, promiseId));
418 } 410 }
419 411
420 /** 412 /**
421 * Process all pending createSession() calls synchronously. 413 * Process all pending createSession() calls synchronously.
422 */ 414 */
423 private void processPendingCreateSessionData() { 415 private void processPendingCreateSessionData() {
424 Log.d(TAG, "processPendingCreateSessionData()"); 416 Log.d(TAG, "processPendingCreateSessionData()");
425 assert mMediaDrm != null; 417 assert mMediaDrm != null;
426 418
427 // Check mMediaDrm != null because error may happen in createSession(). 419 // Check mMediaDrm != null because error may happen in createSession().
428 // Check !mProvisioningPending because NotProvisionedException may be 420 // Check !mProvisioningPending because NotProvisionedException may be
429 // thrown in createSession(). 421 // thrown in createSession().
430 while (mMediaDrm != null && !mProvisioningPending 422 while (mMediaDrm != null && !mProvisioningPending
431 && !mPendingCreateSessionDataQueue.isEmpty()) { 423 && !mPendingCreateSessionDataQueue.isEmpty()) {
432 PendingCreateSessionData pendingData = mPendingCreateSessionDataQueu e.poll(); 424 PendingCreateSessionData pendingData = mPendingCreateSessionDataQueu e.poll();
433 int sessionId = pendingData.sessionId();
434 byte[] initData = pendingData.initData(); 425 byte[] initData = pendingData.initData();
435 String mime = pendingData.mimeType(); 426 String mime = pendingData.mimeType();
436 createSession(sessionId, initData, mime); 427 long promiseId = pendingData.promiseId();
428 createSession(initData, mime, promiseId);
437 } 429 }
438 } 430 }
439 431
440 /** 432 /**
441 * Process pending operations asynchrnously. 433 * Process pending operations asynchrnously.
442 */ 434 */
443 private void resumePendingOperations() { 435 private void resumePendingOperations() {
444 mHandler.post(new Runnable(){ 436 mHandler.post(new Runnable(){
445 @Override 437 @Override
446 public void run() { 438 public void run() {
447 processPendingCreateSessionData(); 439 processPendingCreateSessionData();
448 } 440 }
449 }); 441 });
450 } 442 }
451 443
452 /** 444 /**
453 * Create a session with |sessionId|, |initData| and |mime|. 445 * Create a session, and generate a request with |initData| and |mime|.
454 * 446 *
455 * @param sessionId ID for the session to be created.
456 * @param initData Data needed to generate the key request. 447 * @param initData Data needed to generate the key request.
457 * @param mime Mime type. 448 * @param mime Mime type.
449 * @param promiseId Promise ID for this call.
458 */ 450 */
459 @CalledByNative 451 @CalledByNative
460 private void createSession(int sessionId, byte[] initData, String mime) { 452 private void createSession(byte[] initData, String mime, long promiseId) {
461 Log.d(TAG, "createSession()"); 453 Log.d(TAG, "createSession()");
462 if (mMediaDrm == null) { 454 if (mMediaDrm == null) {
463 Log.e(TAG, "createSession() called when MediaDrm is null."); 455 Log.e(TAG, "createSession() called when MediaDrm is null.");
464 return; 456 return;
465 } 457 }
466 458
467 if (mProvisioningPending) { 459 if (mProvisioningPending) {
468 assert mMediaCrypto == null; 460 assert mMediaCrypto == null;
469 savePendingCreateSessionData(sessionId, initData, mime); 461 savePendingCreateSessionData(initData, mime, promiseId);
470 return; 462 return;
471 } 463 }
472 464
473 boolean newSessionOpened = false; 465 boolean newSessionOpened = false;
474 ByteBuffer session = null; 466 byte[] sessionId = null;
475 try { 467 try {
476 // Create MediaCrypto if necessary. 468 // Create MediaCrypto if necessary.
477 if (mMediaCrypto == null && !createMediaCrypto()) { 469 if (mMediaCrypto == null && !createMediaCrypto()) {
478 onSessionError(sessionId); 470 onPromiseRejected(promiseId, "MediaCrypto creation failed.");
479 return; 471 return;
480 } 472 }
473 assert mMediaCryptoSession != null;
481 assert mMediaCrypto != null; 474 assert mMediaCrypto != null;
482 assert mSessionIds.containsKey(mMediaCryptoSession);
483 475
484 session = openSession(); 476 sessionId = openSession();
485 if (session == null) { 477 if (sessionId == null) {
486 Log.e(TAG, "Cannot open session in createSession()."); 478 onPromiseRejected(promiseId, "Open session failed.");
487 onSessionError(sessionId);
488 return; 479 return;
489 } 480 }
490 newSessionOpened = true; 481 newSessionOpened = true;
491 assert !mSessionIds.containsKey(session); 482 assert !sessionExists(sessionId);
492 483
493 MediaDrm.KeyRequest request = null; 484 MediaDrm.KeyRequest request = null;
494 request = getKeyRequest(session, initData, mime); 485 request = getKeyRequest(sessionId, initData, mime);
495 if (request == null) { 486 if (request == null) {
496 if (newSessionOpened) { 487 mMediaDrm.closeSession(sessionId);
497 closeSession(session); 488 onPromiseRejected(promiseId, "Generate request failed.");
498 }
499 onSessionError(sessionId);
500 return; 489 return;
501 } 490 }
502 491
503 onSessionCreated(sessionId, getWebSessionId(session)); 492 // Success!
493 Log.d(TAG, "createSession(): Session (" + bytesToHexString(sessionId ) + ") created.");
494 onPromiseResolvedWithSession(promiseId, sessionId);
504 onSessionMessage(sessionId, request); 495 onSessionMessage(sessionId, request);
505 if (newSessionOpened) { 496 mSessionIds.put(ByteBuffer.wrap(sessionId), mime);
506 Log.d(TAG, "createSession(): Session " + getWebSessionId(session )
507 + " (" + sessionId + ") created.");
508 }
509
510 mSessionIds.put(session, sessionId);
511 mSessionMimeTypes.put(session, mime);
512 } catch (android.media.NotProvisionedException e) { 497 } catch (android.media.NotProvisionedException e) {
513 Log.e(TAG, "Device not provisioned", e); 498 Log.e(TAG, "Device not provisioned", e);
514 if (newSessionOpened) { 499 if (newSessionOpened) {
515 closeSession(session); 500 mMediaDrm.closeSession(sessionId);
516 } 501 }
517 savePendingCreateSessionData(sessionId, initData, mime); 502 savePendingCreateSessionData(initData, mime, promiseId);
518 startProvisioning(); 503 startProvisioning();
519 } 504 }
520 } 505 }
521 506
522 /** 507 /**
523 * Returns whether |sessionId| is a valid key session, excluding the media 508 * Return whether |sessionId| is a valid key session, excluding the media
524 * crypto session. 509 * crypto session.
525 * 510 *
526 * @param sessionId Crypto session Id. 511 * @param sessionId Crypto session Id.
qinmin 2015/01/15 18:17:25 nit @return?
xhwang 2015/01/15 19:03:44 Done.
527 */ 512 */
528 private boolean sessionExists(ByteBuffer session) { 513 private boolean sessionExists(byte[] sessionId) {
529 if (mMediaCryptoSession == null) { 514 if (mMediaCryptoSession == null) {
530 assert mSessionIds.isEmpty(); 515 assert mSessionIds.isEmpty();
531 Log.e(TAG, "Session doesn't exist because media crypto session is no t created."); 516 Log.e(TAG, "Session doesn't exist because media crypto session is no t created.");
532 return false; 517 return false;
533 } 518 }
534 assert mSessionIds.containsKey(mMediaCryptoSession); 519 return !Arrays.equals(sessionId, mMediaCryptoSession)
535 return !session.equals(mMediaCryptoSession) && mSessionIds.containsKey(s ession); 520 && mSessionIds.containsKey(ByteBuffer.wrap(sessionId));
536 } 521 }
537 522
538 /** 523 /**
539 * Cancel a key request for a session Id. 524 * Close a session that was previously created by createSession().
540 * 525 *
541 * @param sessionId Reference ID of session to be released. 526 * @param sessionId ID of session to be closed.
527 * @param promiseId Promise ID of this call.
542 */ 528 */
543 @CalledByNative 529 @CalledByNative
544 private void releaseSession(int sessionId) { 530 private void closeSession(byte[] sessionId, long promiseId) {
545 Log.d(TAG, "releaseSession(): " + sessionId); 531 Log.d(TAG, "closeSession()");
546 if (mMediaDrm == null) { 532 if (mMediaDrm == null) {
547 Log.e(TAG, "releaseSession() called when MediaDrm is null."); 533 onPromiseRejected(promiseId, "closeSession() called when MediaDrm is null.");
548 return; 534 return;
549 } 535 }
550 536
551 ByteBuffer session = getSession(sessionId); 537 if (!sessionExists(sessionId)) {
552 if (session == null) { 538 onPromiseRejected(promiseId,
553 Log.e(TAG, "Invalid sessionId in releaseSession."); 539 "Invalid sessionId in closeSession(): " + bytesToHexString(s essionId));
554 onSessionError(sessionId);
555 return; 540 return;
556 } 541 }
557 542
558 mMediaDrm.removeKeys(session.array()); 543 mMediaDrm.removeKeys(sessionId);
559 544 mMediaDrm.closeSession(sessionId);
560 Log.d(TAG, "Session " + sessionId + "closed."); 545 mSessionIds.remove(ByteBuffer.wrap(sessionId));
561 closeSession(session); 546 onPromiseResolved(promiseId);
562 mSessionIds.remove(session); 547 Log.d(TAG, "Session " + bytesToHexString(sessionId) + "closed.");
563 onSessionClosed(sessionId);
564 } 548 }
565 549
566 /** 550 /**
567 * Add a key for a session Id. 551 * Update a session with response.
568 * 552 *
569 * @param sessionId Reference ID of session to be updated. 553 * @param sessionId Reference ID of session to be updated.
570 * @param key Response data from the server. 554 * @param response Response data from the server.
555 * @param promiseId Promise ID of this call.
571 */ 556 */
572 @CalledByNative 557 @CalledByNative
573 private void updateSession(int sessionId, byte[] key) { 558 private void updateSession(byte[] sessionId, byte[] response, long promiseId ) {
574 Log.d(TAG, "updateSession(): " + sessionId); 559 Log.d(TAG, "updateSession()");
575 if (mMediaDrm == null) { 560 if (mMediaDrm == null) {
576 Log.e(TAG, "updateSession() called when MediaDrm is null."); 561 onPromiseRejected(promiseId, "closeSession() called when MediaDrm is null.");
577 return; 562 return;
578 } 563 }
579 564
580 // TODO(xhwang): We should be able to DCHECK this when WD EME is impleme nted. 565 // TODO(xhwang): DCHECK this when prefixed EME is deprecated.
581 ByteBuffer session = getSession(sessionId); 566 if (!sessionExists(sessionId)) {
582 if (!sessionExists(session)) { 567 onPromiseRejected(
583 Log.e(TAG, "Invalid session in updateSession."); 568 promiseId, "Invalid session in updateSession: " + bytesToHex String(sessionId));
584 onSessionError(sessionId);
585 return; 569 return;
586 } 570 }
587 571
588 try { 572 try {
589 try { 573 try {
590 mMediaDrm.provideKeyResponse(session.array(), key); 574 mMediaDrm.provideKeyResponse(sessionId, response);
591 } catch (java.lang.IllegalStateException e) { 575 } catch (java.lang.IllegalStateException e) {
592 // This is not really an exception. Some error code are incorrec tly 576 // This is not really an exception. Some error code are incorrec tly
593 // reported as an exception. 577 // reported as an exception.
594 // TODO(qinmin): remove this exception catch when b/10495563 is fixed. 578 // TODO(qinmin): remove this exception catch when b/10495563 is fixed.
595 Log.e(TAG, "Exception intentionally caught when calling provideK eyResponse()", e); 579 Log.e(TAG, "Exception intentionally caught when calling provideK eyResponse()", e);
596 } 580 }
597 onSessionReady(sessionId); 581 Log.d(TAG, "Key successfully added for session " + bytesToHexString( sessionId));
598 Log.d(TAG, "Key successfully added for session " + sessionId); 582 onPromiseResolved(promiseId);
583 onSessionKeysChange(sessionId, true, KEY_STATUS_USABLE);
599 return; 584 return;
600 } catch (android.media.NotProvisionedException e) { 585 } catch (android.media.NotProvisionedException e) {
601 // TODO(xhwang): Should we handle this? 586 // TODO(xhwang): Should we handle this?
602 Log.e(TAG, "failed to provide key response", e); 587 Log.e(TAG, "failed to provide key response", e);
603 } catch (android.media.DeniedByServerException e) { 588 } catch (android.media.DeniedByServerException e) {
604 Log.e(TAG, "failed to provide key response", e); 589 Log.e(TAG, "failed to provide key response", e);
605 } 590 }
606 onSessionError(sessionId); 591 onPromiseRejected(promiseId, "Update session failed.");
607 release(); 592 release();
608 } 593 }
609 594
610 /** 595 /**
611 * Return the security level of this DRM object. 596 * Return the security level of this DRM object.
612 */ 597 */
613 @CalledByNative 598 @CalledByNative
614 private String getSecurityLevel() { 599 private String getSecurityLevel() {
615 if (mMediaDrm == null) { 600 if (mMediaDrm == null) {
616 Log.e(TAG, "getSecurityLevel() called when MediaDrm is null."); 601 Log.e(TAG, "getSecurityLevel() called when MediaDrm is null.");
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
670 mMediaDrm.provideProvisionResponse(response); 655 mMediaDrm.provideProvisionResponse(response);
671 return true; 656 return true;
672 } catch (android.media.DeniedByServerException e) { 657 } catch (android.media.DeniedByServerException e) {
673 Log.e(TAG, "failed to provide provision response", e); 658 Log.e(TAG, "failed to provide provision response", e);
674 } catch (java.lang.IllegalStateException e) { 659 } catch (java.lang.IllegalStateException e) {
675 Log.e(TAG, "failed to provide provision response", e); 660 Log.e(TAG, "failed to provide provision response", e);
676 } 661 }
677 return false; 662 return false;
678 } 663 }
679 664
680 private void onSessionCreated(final int sessionId, final String webSessionId ) { 665 // Helper functions to post native calls to prevent reentrancy.
666
667 private void onPromiseResolved(final long promiseId) {
681 mHandler.post(new Runnable(){ 668 mHandler.post(new Runnable(){
682 @Override 669 @Override
683 public void run() { 670 public void run() {
684 nativeOnSessionCreated(mNativeMediaDrmBridge, sessionId, webSess ionId); 671 nativeOnPromiseResolved(mNativeMediaDrmBridge, promiseId);
685 } 672 }
686 }); 673 });
687 } 674 }
688 675
689 private void onSessionMessage(final int sessionId, final MediaDrm.KeyRequest request) { 676 private void onPromiseResolvedWithSession(final long promiseId, final byte[] sessionId) {
690 mHandler.post(new Runnable(){ 677 mHandler.post(new Runnable(){
691 @Override 678 @Override
692 public void run() { 679 public void run() {
693 nativeOnSessionMessage(mNativeMediaDrmBridge, sessionId, 680 nativeOnPromiseResolvedWithSession(mNativeMediaDrmBridge, promis eId, sessionId);
694 request.getData(), request.getDefaultUrl());
695 } 681 }
696 }); 682 });
697 } 683 }
698 684
699 private void onSessionReady(final int sessionId) { 685 private void onPromiseRejected(final long promiseId, final String errorMessa ge) {
686 Log.e(TAG, "onPromiseRejected: " + errorMessage);
700 mHandler.post(new Runnable() { 687 mHandler.post(new Runnable() {
701 @Override 688 @Override
702 public void run() { 689 public void run() {
703 nativeOnSessionReady(mNativeMediaDrmBridge, sessionId); 690 nativeOnPromiseRejected(mNativeMediaDrmBridge, promiseId, errorM essage);
704 } 691 }
705 }); 692 });
706 } 693 }
707 694
708 private void onSessionClosed(final int sessionId) { 695 private void onSessionMessage(final byte[] sessionId, final MediaDrm.KeyRequ est request) {
709 mHandler.post(new Runnable() { 696 mHandler.post(new Runnable() {
710 @Override 697 @Override
711 public void run() { 698 public void run() {
699 nativeOnSessionMessage(mNativeMediaDrmBridge, sessionId, request .getData(),
700 request.getDefaultUrl());
701 }
702 });
703 }
704
705 private void onSessionClosed(final byte[] sessionId) {
706 mHandler.post(new Runnable() {
707 @Override
708 public void run() {
712 nativeOnSessionClosed(mNativeMediaDrmBridge, sessionId); 709 nativeOnSessionClosed(mNativeMediaDrmBridge, sessionId);
713 } 710 }
714 }); 711 });
715 } 712 }
716 713
717 private void onSessionError(final int sessionId) { 714 private void onSessionKeysChange(
718 // TODO(qinmin): pass the error code to native. 715 final byte[] sessionId, final boolean hasAdditionalUsableKey, final int keyStatus) {
719 mHandler.post(new Runnable() { 716 mHandler.post(new Runnable() {
720 @Override 717 @Override
721 public void run() { 718 public void run() {
722 nativeOnSessionError(mNativeMediaDrmBridge, sessionId); 719 nativeOnSessionKeysChange(
720 mNativeMediaDrmBridge, sessionId, hasAdditionalUsableKey , keyStatus);
723 } 721 }
724 }); 722 });
725 } 723 }
726 724
727 private String getWebSessionId(ByteBuffer session) { 725 private void onLegacySessionError(final byte[] sessionId, final String error Message) {
728 String webSessionId = null; 726 Log.e(TAG, "onLegacySessionError: " + errorMessage);
729 try { 727 mHandler.post(new Runnable() {
730 webSessionId = new String(session.array(), "UTF-8"); 728 @Override
731 } catch (java.io.UnsupportedEncodingException e) { 729 public void run() {
732 Log.e(TAG, "getWebSessionId failed", e); 730 nativeOnLegacySessionError(mNativeMediaDrmBridge, sessionId, err orMessage);
733 } catch (java.lang.NullPointerException e) { 731 }
734 Log.e(TAG, "getWebSessionId failed", e); 732 });
735 }
736 return webSessionId;
737 } 733 }
738 734
739 private class MediaDrmListener implements MediaDrm.OnEventListener { 735 private class MediaDrmListener implements MediaDrm.OnEventListener {
740 @Override 736 @Override
741 public void onEvent( 737 public void onEvent(
742 MediaDrm mediaDrm, byte[] sessionArray, int event, int extra, by te[] data) { 738 MediaDrm mediaDrm, byte[] sessionId, int event, int extra, byte[ ] data) {
743 if (sessionArray == null) { 739 if (sessionId == null) {
744 Log.e(TAG, "MediaDrmListener: Null session."); 740 Log.e(TAG, "MediaDrmListener: Null session.");
745 return; 741 return;
746 } 742 }
747 ByteBuffer session = ByteBuffer.wrap(sessionArray); 743 if (!sessionExists(sessionId)) {
748 if (!sessionExists(session)) { 744 Log.e(TAG, "MediaDrmListener: Invalid session " + bytesToHexStri ng(sessionId));
749 Log.e(TAG, "MediaDrmListener: Invalid session.");
750 return;
751 }
752 Integer sessionId = mSessionIds.get(session);
753 if (sessionId == null || sessionId == INVALID_SESSION_ID) {
754 Log.e(TAG, "MediaDrmListener: Invalid session ID.");
755 return; 745 return;
756 } 746 }
757 switch(event) { 747 switch(event) {
758 case MediaDrm.EVENT_PROVISION_REQUIRED: 748 case MediaDrm.EVENT_PROVISION_REQUIRED:
759 Log.d(TAG, "MediaDrm.EVENT_PROVISION_REQUIRED"); 749 Log.d(TAG, "MediaDrm.EVENT_PROVISION_REQUIRED");
760 break; 750 break;
761 case MediaDrm.EVENT_KEY_REQUIRED: 751 case MediaDrm.EVENT_KEY_REQUIRED:
762 Log.d(TAG, "MediaDrm.EVENT_KEY_REQUIRED"); 752 Log.d(TAG, "MediaDrm.EVENT_KEY_REQUIRED");
763 if (mProvisioningPending) { 753 if (mProvisioningPending) {
764 return; 754 return;
765 } 755 }
766 String mime = mSessionMimeTypes.get(session); 756 String mime = mSessionIds.get(ByteBuffer.wrap(sessionId));
767 MediaDrm.KeyRequest request = null; 757 MediaDrm.KeyRequest request = null;
768 try { 758 try {
769 request = getKeyRequest(session, data, mime); 759 request = getKeyRequest(sessionId, data, mime);
770 } catch (android.media.NotProvisionedException e) { 760 } catch (android.media.NotProvisionedException e) {
771 Log.e(TAG, "Device not provisioned", e); 761 Log.e(TAG, "Device not provisioned", e);
772 startProvisioning(); 762 startProvisioning();
773 return; 763 return;
774 } 764 }
775 if (request != null) { 765 if (request != null) {
776 onSessionMessage(sessionId, request); 766 onSessionMessage(sessionId, request);
777 } else { 767 } else {
778 onSessionError(sessionId); 768 onLegacySessionError(sessionId,
769 "MediaDrm EVENT_KEY_REQUIRED: Failed to generate request.");
770 onSessionKeysChange(sessionId, false, KEY_STATUS_INTERNA L_ERROR);
779 } 771 }
780 break; 772 break;
781 case MediaDrm.EVENT_KEY_EXPIRED: 773 case MediaDrm.EVENT_KEY_EXPIRED:
782 Log.d(TAG, "MediaDrm.EVENT_KEY_EXPIRED"); 774 Log.d(TAG, "MediaDrm.EVENT_KEY_EXPIRED");
783 onSessionError(sessionId); 775 onLegacySessionError(sessionId, "MediaDrm EVENT_KEY_EXPIRED. ");
776 onSessionKeysChange(sessionId, false, KEY_STATUS_EXPIRED);
784 break; 777 break;
785 case MediaDrm.EVENT_VENDOR_DEFINED: 778 case MediaDrm.EVENT_VENDOR_DEFINED:
786 Log.d(TAG, "MediaDrm.EVENT_VENDOR_DEFINED"); 779 Log.d(TAG, "MediaDrm.EVENT_VENDOR_DEFINED");
787 assert false; // Should never happen. 780 assert false; // Should never happen.
788 break; 781 break;
789 default: 782 default:
790 Log.e(TAG, "Invalid DRM event " + event); 783 Log.e(TAG, "Invalid DRM event " + event);
791 return; 784 return;
792 } 785 }
793 } 786 }
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
853 ByteBuffer uuidBuffer = ByteBuffer.allocateDirect(16); 846 ByteBuffer uuidBuffer = ByteBuffer.allocateDirect(16);
854 // MSB (byte) should be positioned at the first element. 847 // MSB (byte) should be positioned at the first element.
855 uuidBuffer.order(ByteOrder.BIG_ENDIAN); 848 uuidBuffer.order(ByteOrder.BIG_ENDIAN);
856 uuidBuffer.putLong(uuid.getMostSignificantBits()); 849 uuidBuffer.putLong(uuid.getMostSignificantBits());
857 uuidBuffer.putLong(uuid.getLeastSignificantBits()); 850 uuidBuffer.putLong(uuid.getLeastSignificantBits());
858 nativeAddKeySystemUuidMapping(keySystem, uuidBuffer); 851 nativeAddKeySystemUuidMapping(keySystem, uuidBuffer);
859 } 852 }
860 853
861 private native void nativeOnMediaCryptoReady(long nativeMediaDrmBridge); 854 private native void nativeOnMediaCryptoReady(long nativeMediaDrmBridge);
862 855
863 private native void nativeOnSessionCreated(long nativeMediaDrmBridge, int se ssionId, 856 private native void nativeOnPromiseResolved(long nativeMediaDrmBridge, long promiseId);
864 String webSessionId); 857 private native void nativeOnPromiseResolvedWithSession(
858 long nativeMediaDrmBridge, long promiseId, byte[] sessionId);
859 private native void nativeOnPromiseRejected(
860 long nativeMediaDrmBridge, long promiseId, String errorMessage);
865 861
866 private native void nativeOnSessionMessage(long nativeMediaDrmBridge, int se ssionId, 862 private native void nativeOnSessionMessage(
867 byte[] message, String destinatio nUrl); 863 long nativeMediaDrmBridge, byte[] sessionId, byte[] message, String destinationUrl);
868 864 private native void nativeOnSessionClosed(long nativeMediaDrmBridge, byte[] sessionId);
869 private native void nativeOnSessionReady(long nativeMediaDrmBridge, int sess ionId); 865 private native void nativeOnSessionKeysChange(long nativeMediaDrmBridge, byt e[] sessionId,
870 866 boolean hasAdditionalUsableKey, int keyStatus);
871 private native void nativeOnSessionClosed(long nativeMediaDrmBridge, int ses sionId); 867 private native void nativeOnLegacySessionError(
872 868 long nativeMediaDrmBridge, byte[] sessionId, String errorMessage);
873 private native void nativeOnSessionError(long nativeMediaDrmBridge, int sess ionId);
874 869
875 private native void nativeOnResetDeviceCredentialsCompleted( 870 private native void nativeOnResetDeviceCredentialsCompleted(
876 long nativeMediaDrmBridge, boolean success); 871 long nativeMediaDrmBridge, boolean success);
877 872
878 private static native void nativeAddKeySystemUuidMapping(String keySystem, B yteBuffer uuid); 873 private static native void nativeAddKeySystemUuidMapping(String keySystem, B yteBuffer uuid);
879 } 874 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698