Chromium Code Reviews| OLD | NEW |
|---|---|
| 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.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
| 8 import android.media.MediaCrypto; | 8 import android.media.MediaCrypto; |
| 9 import android.media.MediaDrm; | 9 import android.media.MediaDrm; |
| 10 import android.os.AsyncTask; | |
| 11 import android.os.Build; | 10 import android.os.Build; |
| 12 | 11 |
| 13 import org.chromium.base.Log; | 12 import org.chromium.base.Log; |
| 14 import org.chromium.base.annotations.CalledByNative; | 13 import org.chromium.base.annotations.CalledByNative; |
| 15 import org.chromium.base.annotations.JNINamespace; | 14 import org.chromium.base.annotations.JNINamespace; |
| 16 | 15 |
| 17 import java.io.BufferedInputStream; | |
| 18 import java.io.ByteArrayOutputStream; | |
| 19 import java.io.IOException; | |
| 20 import java.net.HttpURLConnection; | |
| 21 import java.net.MalformedURLException; | |
| 22 import java.net.URL; | |
| 23 import java.nio.ByteBuffer; | 16 import java.nio.ByteBuffer; |
| 24 import java.util.ArrayDeque; | 17 import java.util.ArrayDeque; |
| 25 import java.util.ArrayList; | 18 import java.util.ArrayList; |
| 26 import java.util.Arrays; | 19 import java.util.Arrays; |
| 27 import java.util.HashMap; | 20 import java.util.HashMap; |
| 28 import java.util.List; | 21 import java.util.List; |
| 29 import java.util.UUID; | 22 import java.util.UUID; |
| 30 | 23 |
| 31 /** | 24 /** |
| 32 * A wrapper of the android MediaDrm class. Each MediaDrmBridge manages multiple | 25 * A wrapper of the android MediaDrm class. Each MediaDrmBridge manages multiple |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 50 // b) Finish createSession() if previous createSession() was interrupted | 43 // b) Finish createSession() if previous createSession() was interrupted |
| 51 // by a NotProvisionedException. | 44 // by a NotProvisionedException. |
| 52 // - Whenever an unexpected error occurred, we'll call release() to release | 45 // - Whenever an unexpected error occurred, we'll call release() to release |
| 53 // all resources and clear all states. In that case all calls to this | 46 // all resources and clear all states. In that case all calls to this |
| 54 // object will be no-op. All public APIs and callbacks should check | 47 // object will be no-op. All public APIs and callbacks should check |
| 55 // mMediaBridge to make sure release() hasn't been called. Also, we call | 48 // mMediaBridge to make sure release() hasn't been called. Also, we call |
| 56 // release() immediately after the error happens (e.g. after mMediaDrm) | 49 // release() immediately after the error happens (e.g. after mMediaDrm) |
| 57 // calls. Indirect calls should not call release() again to avoid | 50 // calls. Indirect calls should not call release() again to avoid |
| 58 // duplication (even though it doesn't hurt to call release() twice). | 51 // duplication (even though it doesn't hurt to call release() twice). |
| 59 | 52 |
| 60 private static final String TAG = "cr.media"; | 53 private static final String TAG = "cr_media"; |
| 61 private static final String SECURITY_LEVEL = "securityLevel"; | 54 private static final String SECURITY_LEVEL = "securityLevel"; |
| 62 private static final String SERVER_CERTIFICATE = "serviceCertificate"; | 55 private static final String SERVER_CERTIFICATE = "serviceCertificate"; |
| 63 private static final String PRIVACY_MODE = "privacyMode"; | 56 private static final String PRIVACY_MODE = "privacyMode"; |
| 64 private static final String SESSION_SHARING = "sessionSharing"; | 57 private static final String SESSION_SHARING = "sessionSharing"; |
| 65 private static final String ENABLE = "enable"; | 58 private static final String ENABLE = "enable"; |
| 66 private static final int INVALID_SESSION_ID = 0; | 59 private static final int INVALID_SESSION_ID = 0; |
| 67 private static final char[] HEX_CHAR_LOOKUP = "0123456789ABCDEF".toCharArray (); | 60 private static final char[] HEX_CHAR_LOOKUP = "0123456789ABCDEF".toCharArray (); |
| 68 private static final long INVALID_NATIVE_MEDIA_DRM_BRIDGE = 0; | 61 private static final long INVALID_NATIVE_MEDIA_DRM_BRIDGE = 0; |
| 69 | 62 |
| 70 // On Android L and before, MediaDrm doesn't support KeyStatus. Use a dummy | 63 // On Android L and before, MediaDrm doesn't support KeyStatus. Use a dummy |
| (...skipping 335 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 406 private MediaCrypto getMediaCrypto() { | 399 private MediaCrypto getMediaCrypto() { |
| 407 return mMediaCrypto; | 400 return mMediaCrypto; |
| 408 } | 401 } |
| 409 | 402 |
| 410 /** | 403 /** |
| 411 * Reset the device DRM credentials. | 404 * Reset the device DRM credentials. |
| 412 */ | 405 */ |
| 413 @CalledByNative | 406 @CalledByNative |
| 414 private void resetDeviceCredentials() { | 407 private void resetDeviceCredentials() { |
| 415 mResetDeviceCredentialsPending = true; | 408 mResetDeviceCredentialsPending = true; |
| 416 MediaDrm.ProvisionRequest request = mMediaDrm.getProvisionRequest(); | 409 startProvisioning(); |
| 417 PostRequestTask postTask = new PostRequestTask(request.getData()); | |
| 418 postTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, request.getDe faultUrl()); | |
| 419 } | 410 } |
| 420 | 411 |
| 421 /** | 412 /** |
| 422 * Destroy the MediaDrmBridge object. | 413 * Destroy the MediaDrmBridge object. |
| 423 */ | 414 */ |
| 424 @CalledByNative | 415 @CalledByNative |
| 425 private void destroy() { | 416 private void destroy() { |
| 426 mNativeMediaDrmBridge = INVALID_NATIVE_MEDIA_DRM_BRIDGE; | 417 mNativeMediaDrmBridge = INVALID_NATIVE_MEDIA_DRM_BRIDGE; |
| 427 if (mMediaDrm != null) { | 418 if (mMediaDrm != null) { |
| 428 release(); | 419 release(); |
| (...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 733 @CalledByNative | 724 @CalledByNative |
| 734 private String getSecurityLevel() { | 725 private String getSecurityLevel() { |
| 735 if (mMediaDrm == null) { | 726 if (mMediaDrm == null) { |
| 736 Log.e(TAG, "getSecurityLevel() called when MediaDrm is null."); | 727 Log.e(TAG, "getSecurityLevel() called when MediaDrm is null."); |
| 737 return null; | 728 return null; |
| 738 } | 729 } |
| 739 return mMediaDrm.getPropertyString("securityLevel"); | 730 return mMediaDrm.getPropertyString("securityLevel"); |
| 740 } | 731 } |
| 741 | 732 |
| 742 private void startProvisioning() { | 733 private void startProvisioning() { |
| 734 if (mProvisioningPending) { | |
| 735 Log.d(TAG, "startProvisioning: another provisioning is in progress, returning"); | |
| 736 return; | |
| 737 } | |
| 738 | |
| 743 Log.d(TAG, "startProvisioning"); | 739 Log.d(TAG, "startProvisioning"); |
| 740 mProvisioningPending = true; | |
| 744 assert mMediaDrm != null; | 741 assert mMediaDrm != null; |
| 745 assert !mProvisioningPending; | |
| 746 mProvisioningPending = true; | |
| 747 MediaDrm.ProvisionRequest request = mMediaDrm.getProvisionRequest(); | 742 MediaDrm.ProvisionRequest request = mMediaDrm.getProvisionRequest(); |
| 748 PostRequestTask postTask = new PostRequestTask(request.getData()); | 743 |
| 749 postTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, request.getDe faultUrl()); | 744 if (isNativeMediaDrmBridgeValid()) { |
| 745 nativeOnStartProvisioning( | |
| 746 mNativeMediaDrmBridge, request.getDefaultUrl(), request.getD ata()); | |
| 747 } | |
| 750 } | 748 } |
| 751 | 749 |
| 752 /** | 750 /** |
| 753 * Called when the provision response is received. | 751 * Called when the provision response is received. |
| 754 * | 752 * |
| 755 * @param response Response data from the provision server. | 753 * @param response Response data from the provision server. |
| 756 */ | 754 */ |
| 757 private void onProvisionResponse(byte[] response) { | 755 @CalledByNative |
| 758 Log.d(TAG, "onProvisionResponse()"); | 756 private void processProvisionResponse(boolean success, byte[] response) { |
| 757 Log.d(TAG, "processProvisionResponse()"); | |
| 759 assert mProvisioningPending; | 758 assert mProvisioningPending; |
| 760 mProvisioningPending = false; | 759 mProvisioningPending = false; |
| 761 | 760 |
| 762 // If |mMediaDrm| is released, there is no need to callback native. | 761 // If |mMediaDrm| is released, there is no need to callback native. |
| 763 if (mMediaDrm == null) { | 762 if (mMediaDrm == null) { |
| 764 return; | 763 return; |
| 765 } | 764 } |
| 766 | 765 |
| 767 boolean success = provideProvisionResponse(response); | 766 if (success) { |
| 767 success = provideProvisionResponse(response); | |
|
xhwang
2015/11/13 05:15:42
nit: usually we don't change the input parameter.
Tima Vaisburd
2015/11/13 21:04:44
Done.
| |
| 768 } | |
| 768 | 769 |
| 769 if (mResetDeviceCredentialsPending) { | 770 if (mResetDeviceCredentialsPending) { |
| 770 onResetDeviceCredentialsCompleted(success); | 771 onResetDeviceCredentialsCompleted(success); |
| 771 mResetDeviceCredentialsPending = false; | 772 mResetDeviceCredentialsPending = false; |
| 772 } | 773 } |
| 773 | 774 |
| 774 if (success) { | 775 if (success) { |
| 775 processPendingCreateSessionData(); | 776 processPendingCreateSessionData(); |
| 776 } | 777 } |
| 777 } | 778 } |
| 778 | 779 |
| 779 /** | 780 /** |
| 780 * Provide the provisioning response to MediaDrm. | 781 * Provides the provision response to MediaDrm. |
| 781 * | 782 * |
| 782 * @returns false if the response is invalid or on error, true otherwise. | 783 * @returns false if the response is invalid or on error, true otherwise. |
| 783 */ | 784 */ |
| 784 boolean provideProvisionResponse(byte[] response) { | 785 boolean provideProvisionResponse(byte[] response) { |
| 785 if (response == null || response.length == 0) { | 786 if (response == null || response.length == 0) { |
| 786 Log.e(TAG, "Invalid provision response."); | 787 Log.e(TAG, "Invalid provision response."); |
| 787 return false; | 788 return false; |
| 788 } | 789 } |
| 789 | 790 |
| 790 try { | 791 try { |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 959 | 960 |
| 960 @TargetApi(Build.VERSION_CODES.M) | 961 @TargetApi(Build.VERSION_CODES.M) |
| 961 private class ExpirationUpdateListener implements MediaDrm.OnExpirationUpdat eListener { | 962 private class ExpirationUpdateListener implements MediaDrm.OnExpirationUpdat eListener { |
| 962 @Override | 963 @Override |
| 963 public void onExpirationUpdate(MediaDrm md, byte[] sessionId, long expir ationTime) { | 964 public void onExpirationUpdate(MediaDrm md, byte[] sessionId, long expir ationTime) { |
| 964 Log.d(TAG, "ExpirationUpdate: " + bytesToHexString(sessionId) + ", " + expirationTime); | 965 Log.d(TAG, "ExpirationUpdate: " + bytesToHexString(sessionId) + ", " + expirationTime); |
| 965 onSessionExpirationUpdate(sessionId, expirationTime); | 966 onSessionExpirationUpdate(sessionId, expirationTime); |
| 966 } | 967 } |
| 967 } | 968 } |
| 968 | 969 |
| 969 private class PostRequestTask extends AsyncTask<String, Void, Void> { | |
| 970 private static final String TAG = "PostRequestTask"; | |
| 971 | |
| 972 private byte[] mDrmRequest; | |
| 973 private byte[] mResponseBody; | |
| 974 | |
| 975 public PostRequestTask(byte[] drmRequest) { | |
| 976 mDrmRequest = drmRequest; | |
| 977 } | |
| 978 | |
| 979 @Override | |
| 980 protected Void doInBackground(String... urls) { | |
| 981 mResponseBody = postRequest(urls[0], mDrmRequest); | |
| 982 if (mResponseBody != null) { | |
| 983 Log.d(TAG, "response length=%d", mResponseBody.length); | |
| 984 } | |
| 985 return null; | |
| 986 } | |
| 987 | |
| 988 private byte[] postRequest(String url, byte[] drmRequest) { | |
| 989 HttpURLConnection urlConnection = null; | |
| 990 try { | |
| 991 URL request = new URL(url + "&signedRequest=" + new String(drmRe quest)); | |
| 992 urlConnection = (HttpURLConnection) request.openConnection(); | |
| 993 urlConnection.setDoOutput(true); | |
| 994 urlConnection.setDoInput(true); | |
| 995 urlConnection.setUseCaches(false); | |
| 996 urlConnection.setRequestMethod("POST"); | |
| 997 urlConnection.setRequestProperty("User-Agent", "Widevine CDM v1. 0"); | |
| 998 urlConnection.setRequestProperty("Content-Type", "application/js on"); | |
| 999 | |
| 1000 int responseCode = urlConnection.getResponseCode(); | |
| 1001 if (responseCode == 200) { | |
| 1002 BufferedInputStream bis = | |
| 1003 new BufferedInputStream(urlConnection.getInputStream ()); | |
| 1004 ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |
| 1005 int read = 0; | |
| 1006 int bufferSize = 512; | |
| 1007 byte[] buffer = new byte[bufferSize]; | |
| 1008 try { | |
| 1009 while (true) { | |
| 1010 read = bis.read(buffer); | |
| 1011 if (read == -1) break; | |
| 1012 bos.write(buffer, 0, read); | |
| 1013 } | |
| 1014 } finally { | |
| 1015 bis.close(); | |
| 1016 } | |
| 1017 return bos.toByteArray(); | |
| 1018 } else { | |
| 1019 Log.d(TAG, "Server returned HTTP error code %d", responseCod e); | |
| 1020 return null; | |
| 1021 } | |
| 1022 } catch (MalformedURLException e) { | |
| 1023 e.printStackTrace(); | |
| 1024 } catch (IOException e) { | |
| 1025 e.printStackTrace(); | |
| 1026 } catch (IllegalStateException e) { | |
| 1027 e.printStackTrace(); | |
| 1028 } finally { | |
| 1029 if (urlConnection != null) urlConnection.disconnect(); | |
| 1030 } | |
| 1031 return null; | |
| 1032 } | |
| 1033 | |
| 1034 @Override | |
| 1035 protected void onPostExecute(Void v) { | |
| 1036 onProvisionResponse(mResponseBody); | |
| 1037 } | |
| 1038 } | |
| 1039 | |
| 1040 // Native functions. At the native side, must post the task immediately to | 970 // Native functions. At the native side, must post the task immediately to |
| 1041 // avoid reentrancy issues. | 971 // avoid reentrancy issues. |
| 1042 private native void nativeOnMediaCryptoReady(long nativeMediaDrmBridge); | 972 private native void nativeOnMediaCryptoReady(long nativeMediaDrmBridge); |
| 1043 | 973 |
| 974 private native void nativeOnStartProvisioning( | |
| 975 long nativeMediaDrmBridge, String defaultUrl, byte[] requestData); | |
| 976 | |
| 1044 private native void nativeOnPromiseResolved(long nativeMediaDrmBridge, long promiseId); | 977 private native void nativeOnPromiseResolved(long nativeMediaDrmBridge, long promiseId); |
| 1045 private native void nativeOnPromiseResolvedWithSession( | 978 private native void nativeOnPromiseResolvedWithSession( |
| 1046 long nativeMediaDrmBridge, long promiseId, byte[] sessionId); | 979 long nativeMediaDrmBridge, long promiseId, byte[] sessionId); |
| 1047 private native void nativeOnPromiseRejected( | 980 private native void nativeOnPromiseRejected( |
| 1048 long nativeMediaDrmBridge, long promiseId, String errorMessage); | 981 long nativeMediaDrmBridge, long promiseId, String errorMessage); |
| 1049 | 982 |
| 1050 private native void nativeOnSessionMessage(long nativeMediaDrmBridge, byte[] sessionId, | 983 private native void nativeOnSessionMessage(long nativeMediaDrmBridge, byte[] sessionId, |
| 1051 int requestType, byte[] message, String destinationUrl); | 984 int requestType, byte[] message, String destinationUrl); |
| 1052 private native void nativeOnSessionClosed(long nativeMediaDrmBridge, byte[] sessionId); | 985 private native void nativeOnSessionClosed(long nativeMediaDrmBridge, byte[] sessionId); |
| 1053 private native void nativeOnSessionKeysChange(long nativeMediaDrmBridge, byt e[] sessionId, | 986 private native void nativeOnSessionKeysChange(long nativeMediaDrmBridge, byt e[] sessionId, |
| 1054 Object[] keysInfo, boolean hasAdditionalUsableKey); | 987 Object[] keysInfo, boolean hasAdditionalUsableKey); |
| 1055 private native void nativeOnSessionExpirationUpdate( | 988 private native void nativeOnSessionExpirationUpdate( |
| 1056 long nativeMediaDrmBridge, byte[] sessionId, long expirationTime); | 989 long nativeMediaDrmBridge, byte[] sessionId, long expirationTime); |
| 1057 private native void nativeOnLegacySessionError( | 990 private native void nativeOnLegacySessionError( |
| 1058 long nativeMediaDrmBridge, byte[] sessionId, String errorMessage); | 991 long nativeMediaDrmBridge, byte[] sessionId, String errorMessage); |
| 1059 | 992 |
| 1060 private native void nativeOnResetDeviceCredentialsCompleted( | 993 private native void nativeOnResetDeviceCredentialsCompleted( |
| 1061 long nativeMediaDrmBridge, boolean success); | 994 long nativeMediaDrmBridge, boolean success); |
| 1062 } | 995 } |
| OLD | NEW |