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.SuppressLint; | 7 import android.annotation.SuppressLint; |
| 8 import android.annotation.TargetApi; | 8 import android.annotation.TargetApi; |
| 9 import android.media.MediaCrypto; | 9 import android.media.MediaCrypto; |
| 10 import android.media.MediaDrm; | 10 import android.media.MediaDrm; |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 90 private ArrayDeque<PendingCreateSessionData> mPendingCreateSessionDataQueue; | 90 private ArrayDeque<PendingCreateSessionData> mPendingCreateSessionDataQueue; |
| 91 | 91 |
| 92 private boolean mResetDeviceCredentialsPending; | 92 private boolean mResetDeviceCredentialsPending; |
| 93 | 93 |
| 94 // MediaDrmBridge is waiting for provisioning response from the server. | 94 // MediaDrmBridge is waiting for provisioning response from the server. |
| 95 private boolean mProvisioningPending; | 95 private boolean mProvisioningPending; |
| 96 | 96 |
| 97 // Boolean to track if 'ORIGIN' is set in MediaDrm. | 97 // Boolean to track if 'ORIGIN' is set in MediaDrm. |
| 98 private boolean mOriginSet = false; | 98 private boolean mOriginSet = false; |
| 99 | 99 |
| 100 // Delay the MediaDrm event handle if present. | |
| 101 private SessionEventDeferrer mSessionEventDeferrer = null; | |
| 102 | |
| 103 // Block MediaDrm event for |mSessionId|. | |
| 104 private static class SessionEventDeferrer { | |
|
xhwang
2017/04/05 18:31:50
Please add more comments why we need this.
yucliu1
2017/04/05 23:04:07
Done.
| |
| 105 private final SessionId mSessionId; | |
| 106 private final ArrayList<Runnable> mEventHandlers; | |
| 107 | |
| 108 SessionEventDeferrer(SessionId sessionId) { | |
| 109 mSessionId = sessionId; | |
| 110 mEventHandlers = new ArrayList<>(); | |
| 111 } | |
| 112 | |
| 113 boolean shouldDefer(SessionId sessionId) { | |
| 114 return mSessionId.isEqual(sessionId); | |
| 115 } | |
| 116 | |
| 117 void defer(Runnable handler) { | |
| 118 mEventHandlers.add(handler); | |
| 119 } | |
| 120 | |
| 121 void fire() { | |
| 122 for (Runnable r : mEventHandlers) { | |
| 123 r.run(); | |
| 124 } | |
| 125 | |
| 126 mEventHandlers.clear(); | |
| 127 } | |
| 128 } | |
| 129 | |
| 100 /** | 130 /** |
| 101 * An equivalent of MediaDrm.KeyStatus, which is only available on M+. | 131 * An equivalent of MediaDrm.KeyStatus, which is only available on M+. |
| 102 */ | 132 */ |
| 103 @MainDex | 133 @MainDex |
| 104 private static class KeyStatus { | 134 private static class KeyStatus { |
| 105 private final byte[] mKeyId; | 135 private final byte[] mKeyId; |
| 106 private final int mStatusCode; | 136 private final int mStatusCode; |
| 107 | 137 |
| 108 private KeyStatus(byte[] keyId, int statusCode) { | 138 private KeyStatus(byte[] keyId, int statusCode) { |
| 109 mKeyId = keyId; | 139 mKeyId = keyId; |
| (...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 566 assert mMediaCryptoSession != null; | 596 assert mMediaCryptoSession != null; |
| 567 assert !mProvisioningPending; | 597 assert !mProvisioningPending; |
| 568 | 598 |
| 569 if (optionalParameters == null) { | 599 if (optionalParameters == null) { |
| 570 optionalParameters = new HashMap<String, String>(); | 600 optionalParameters = new HashMap<String, String>(); |
| 571 } | 601 } |
| 572 | 602 |
| 573 MediaDrm.KeyRequest request = null; | 603 MediaDrm.KeyRequest request = null; |
| 574 | 604 |
| 575 try { | 605 try { |
| 576 request = mMediaDrm.getKeyRequest( | 606 byte[] scopeId = |
| 577 sessionId.drmId(), data, mime, keyType, optionalParameters); | 607 keyType == MediaDrm.KEY_TYPE_RELEASE ? sessionId.keySetId() : sessionId.drmId(); |
| 608 assert scopeId != null; | |
| 609 request = mMediaDrm.getKeyRequest(scopeId, data, mime, keyType, opti onalParameters); | |
| 578 } catch (IllegalStateException e) { | 610 } catch (IllegalStateException e) { |
| 579 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && e | 611 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && e |
| 580 instanceof android.media.MediaDrm.MediaDrmStateException) { | 612 instanceof android.media.MediaDrm.MediaDrmStateException) { |
| 581 // See b/21307186 for details. | 613 // See b/21307186 for details. |
| 582 Log.e(TAG, "MediaDrmStateException fired during getKeyRequest(). ", e); | 614 Log.e(TAG, "MediaDrmStateException fired during getKeyRequest(). ", e); |
| 583 } | 615 } |
| 584 } | 616 } |
| 585 | 617 |
| 586 String result = (request != null) ? "successed" : "failed"; | 618 String result = (request != null) ? "successed" : "failed"; |
| 587 Log.d(TAG, "getKeyRequest %s!", result); | 619 Log.d(TAG, "getKeyRequest %s!", result); |
| (...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 818 final SessionId sessionId = getSessionIdByEmeId(emeSessionId); | 850 final SessionId sessionId = getSessionIdByEmeId(emeSessionId); |
| 819 if (sessionId == null) { | 851 if (sessionId == null) { |
| 820 assert false; // Should never happen. | 852 assert false; // Should never happen. |
| 821 onPromiseRejected(promiseId, | 853 onPromiseRejected(promiseId, |
| 822 "Invalid session in updateSession: " + SessionId.toHexString (emeSessionId)); | 854 "Invalid session in updateSession: " + SessionId.toHexString (emeSessionId)); |
| 823 return; | 855 return; |
| 824 } | 856 } |
| 825 | 857 |
| 826 try { | 858 try { |
| 827 SessionInfo sessionInfo = mSessionManager.get(sessionId); | 859 SessionInfo sessionInfo = mSessionManager.get(sessionId); |
| 828 byte[] keySetId = mMediaDrm.provideKeyResponse(sessionId.drmId(), re sponse); | 860 boolean isKeyRelease = sessionInfo.keyType() == MediaDrm.KEY_TYPE_RE LEASE; |
| 829 | 861 |
| 830 if (keySetId != null && keySetId.length > 0) { | 862 byte[] keySetId = null; |
| 831 assert sessionInfo.keyType() == MediaDrm.KEY_TYPE_OFFLINE; | 863 if (isKeyRelease) { |
| 832 mSessionManager.setKeySetId(sessionId, keySetId, new Callback<Bo olean>() { | 864 Log.d(TAG, "updateSession() for key release"); |
| 833 @Override | 865 assert sessionId.keySetId() != null; |
| 834 public void onResult(Boolean success) { | 866 mMediaDrm.provideKeyResponse(sessionId.keySetId(), response); |
| 835 onKeyUpdated(sessionId, promiseId, success); | 867 } else { |
| 836 } | 868 keySetId = mMediaDrm.provideKeyResponse(sessionId.drmId(), respo nse); |
| 837 }); | 869 } |
| 870 | |
| 871 KeyUpdatedCallback cb = new KeyUpdatedCallback(sessionId, promiseId, isKeyRelease); | |
| 872 | |
| 873 if (isKeyRelease) { | |
| 874 mSessionManager.clearPersistentSessionInfo(sessionId, cb); | |
| 875 } else if (sessionInfo.keyType() == MediaDrm.KEY_TYPE_OFFLINE && key SetId != null | |
| 876 && keySetId.length > 0) { | |
| 877 mSessionManager.setKeySetId(sessionId, keySetId, cb); | |
| 838 } else { | 878 } else { |
| 839 // This can be either temporary license update or server certifi cate update. | 879 // This can be either temporary license update or server certifi cate update. |
| 840 onKeyUpdated(sessionId, promiseId, true); | 880 cb.onResult(true); |
| 841 } | 881 } |
| 842 | 882 |
| 843 return; | 883 return; |
| 844 } catch (android.media.NotProvisionedException e) { | 884 } catch (android.media.NotProvisionedException e) { |
| 845 // TODO(xhwang): Should we handle this? | 885 // TODO(xhwang): Should we handle this? |
| 846 Log.e(TAG, "failed to provide key response", e); | 886 Log.e(TAG, "failed to provide key response", e); |
| 847 } catch (android.media.DeniedByServerException e) { | 887 } catch (android.media.DeniedByServerException e) { |
| 848 Log.e(TAG, "failed to provide key response", e); | 888 Log.e(TAG, "failed to provide key response", e); |
| 849 } catch (java.lang.IllegalStateException e) { | 889 } catch (java.lang.IllegalStateException e) { |
| 850 Log.e(TAG, "failed to provide key response", e); | 890 Log.e(TAG, "failed to provide key response", e); |
| 851 } | 891 } |
| 852 onPromiseRejected(promiseId, "Update session failed."); | 892 onPromiseRejected(promiseId, "Update session failed."); |
| 853 release(); | 893 release(); |
| 854 } | 894 } |
| 855 | 895 |
| 856 private void onKeyUpdated(SessionId sessionId, long promiseId, boolean succe ss) { | 896 /** |
| 857 if (!success) { | 897 * Load persistent license from stroage. |
| 858 onPromiseRejected(promiseId, "failed to update key after response ac cepted"); | 898 */ |
| 899 @CalledByNative | |
| 900 private void loadSession(byte[] emeId, final long promiseId) { | |
| 901 Log.d(TAG, "loadSession()"); | |
| 902 if (mProvisioningPending) { | |
| 903 onPersistentLicenseNoExist(promiseId); | |
| 859 return; | 904 return; |
| 860 } | 905 } |
| 861 | 906 |
| 862 Log.d(TAG, "Key successfully added for session %s", sessionId.toHexStrin g()); | 907 mSessionManager.load(emeId, new Callback<SessionId>() { |
| 863 onPromiseResolved(promiseId); | 908 @Override |
| 909 public void onResult(SessionId sessionId) { | |
| 910 if (sessionId == null) { | |
| 911 onPersistentLicenseNoExist(promiseId); | |
| 912 return; | |
| 913 } | |
| 864 | 914 |
| 865 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { | 915 loadSessionWithLoadedStorage(sessionId, promiseId); |
| 866 onSessionKeysChange( | 916 } |
| 867 sessionId, getDummyKeysInfo(MediaDrm.KeyStatus.STATUS_USABLE ).toArray(), true); | 917 }); |
| 918 } | |
| 919 | |
| 920 /** | |
| 921 * Load session back to memory with MediaDrm. Load persistent storage | |
| 922 * before calling this. It will fail if persistent storage isn't loaded. | |
| 923 */ | |
| 924 private void loadSessionWithLoadedStorage(SessionId sessionId, final long pr omiseId) { | |
| 925 byte[] drmId = null; | |
| 926 try { | |
| 927 drmId = openSession(); | |
| 928 if (drmId == null) { | |
| 929 onPromiseRejected(promiseId, "Failed to open session to load lic ense"); | |
| 930 return; | |
| 931 } | |
| 932 | |
| 933 mSessionManager.setDrmId(sessionId, drmId); | |
| 934 | |
| 935 // Defer event handlers until license is loaded. | |
| 936 assert mSessionEventDeferrer == null; | |
| 937 mSessionEventDeferrer = new SessionEventDeferrer(sessionId); | |
| 938 | |
| 939 assert sessionId.keySetId() != null; | |
| 940 mMediaDrm.restoreKeys(sessionId.drmId(), sessionId.keySetId()); | |
| 941 | |
| 942 onPromiseResolvedWithSession(promiseId, sessionId); | |
| 943 | |
| 944 mSessionEventDeferrer.fire(); | |
| 945 mSessionEventDeferrer = null; | |
| 946 | |
| 947 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { | |
| 948 onSessionKeysChange(sessionId, | |
| 949 getDummyKeysInfo(MediaDrm.KeyStatus.STATUS_USABLE).toArr ay(), true, false); | |
| 950 } | |
| 951 } catch (android.media.NotProvisionedException e) { | |
| 952 onPersistentLicenseNoExist(promiseId); | |
|
xhwang
2017/04/05 18:31:50
When can this happen? When user manually unprovisi
yucliu1
2017/04/05 23:04:07
Early return.
| |
| 953 } catch (java.lang.IllegalStateException e) { | |
| 954 // license doesn't exist | |
| 955 if (sessionId.drmId() == null) { | |
| 956 // TODO(yucliu): Check if the license is released or doesn't exi st. | |
| 957 onPersistentLicenseNoExist(promiseId); | |
| 958 return; | |
| 959 } | |
| 960 | |
| 961 closeSessionNoException(sessionId); | |
| 962 mSessionManager.clearPersistentSessionInfo(sessionId, new Callback<B oolean>() { | |
| 963 @Override | |
| 964 public void onResult(Boolean success) { | |
| 965 if (!success) { | |
| 966 Log.w(TAG, "Failed to clear persistent storage for non-e xist license"); | |
| 967 } | |
| 968 | |
| 969 onPersistentLicenseNoExist(promiseId); | |
| 970 } | |
| 971 }); | |
| 868 } | 972 } |
| 869 } | 973 } |
| 870 | 974 |
| 975 private void onPersistentLicenseNoExist(long promiseId) { | |
| 976 onPromiseResolvedWithSession(promiseId, SessionId.createNoExistSessionId ()); | |
| 977 } | |
| 978 | |
| 979 /** | |
| 980 * Remove session from device. This will mark the key as released and | |
| 981 * generate a key release request. The license is removed from the device | |
| 982 * when the session is updated with a license release response. | |
| 983 */ | |
| 984 @CalledByNative | |
| 985 private void removeSession(byte[] emeId, long promiseId) { | |
| 986 Log.d(TAG, "removeSession()"); | |
| 987 SessionId sessionId = getSessionIdByEmeId(emeId); | |
| 988 | |
| 989 if (sessionId == null) { | |
| 990 onPromiseRejected(promiseId, "Session doesn't exist"); | |
| 991 return; | |
| 992 } | |
| 993 | |
| 994 SessionInfo sessionInfo = mSessionManager.get(sessionId); | |
| 995 if (sessionInfo.keyType() != MediaDrm.KEY_TYPE_OFFLINE) { | |
| 996 // TODO(yucliu): Support 'remove' of temporary session. | |
| 997 onPromiseRejected(promiseId, "Removing temporary session isn't imple mented"); | |
| 998 return; | |
| 999 } | |
| 1000 | |
| 1001 assert sessionId.keySetId() != null; | |
| 1002 | |
| 1003 mSessionManager.markKeyReleased(sessionId); | |
| 1004 | |
| 1005 try { | |
| 1006 // Get key release request. | |
| 1007 MediaDrm.KeyRequest request = getKeyRequest( | |
| 1008 sessionId, null, sessionInfo.mimeType(), MediaDrm.KEY_TYPE_R ELEASE, null); | |
| 1009 | |
| 1010 if (request == null) { | |
| 1011 onPromiseRejected(promiseId, "Fail to generate key release reque st"); | |
| 1012 return; | |
| 1013 } | |
| 1014 | |
| 1015 onPromiseResolved(promiseId); | |
| 1016 onSessionMessage(sessionId, request); | |
|
xhwang
2017/04/05 18:31:50
The spec says queue the message, then return the p
yucliu1
2017/04/05 23:04:07
Switched sequence to match spec.
| |
| 1017 } catch (android.media.NotProvisionedException e) { | |
| 1018 Log.e(TAG, "removeSession called on unprovisioned device"); | |
| 1019 onPromiseRejected(promiseId, "Unknown failure"); | |
| 1020 } | |
| 1021 } | |
| 1022 | |
| 871 /** | 1023 /** |
| 872 * Return the security level of this DRM object. | 1024 * Return the security level of this DRM object. |
| 873 */ | 1025 */ |
| 874 @CalledByNative | 1026 @CalledByNative |
| 875 private String getSecurityLevel() { | 1027 private String getSecurityLevel() { |
| 876 if (mMediaDrm == null || !isWidevine()) { | 1028 if (mMediaDrm == null || !isWidevine()) { |
| 877 Log.e(TAG, "getSecurityLevel(): MediaDrm is null or security level i s not supported."); | 1029 Log.e(TAG, "getSecurityLevel(): MediaDrm is null or security level i s not supported."); |
| 878 return null; | 1030 return null; |
| 879 } | 1031 } |
| 880 return mMediaDrm.getPropertyString("securityLevel"); | 1032 return mMediaDrm.getPropertyString("securityLevel"); |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 962 mMediaDrm.provideProvisionResponse(response); | 1114 mMediaDrm.provideProvisionResponse(response); |
| 963 return true; | 1115 return true; |
| 964 } catch (android.media.DeniedByServerException e) { | 1116 } catch (android.media.DeniedByServerException e) { |
| 965 Log.e(TAG, "failed to provide provision response", e); | 1117 Log.e(TAG, "failed to provide provision response", e); |
| 966 } catch (java.lang.IllegalStateException e) { | 1118 } catch (java.lang.IllegalStateException e) { |
| 967 Log.e(TAG, "failed to provide provision response", e); | 1119 Log.e(TAG, "failed to provide provision response", e); |
| 968 } | 1120 } |
| 969 return false; | 1121 return false; |
| 970 } | 1122 } |
| 971 | 1123 |
| 1124 /** | |
| 1125 * Delay session event handler if |mSessionEventDeferrer| exists and | |
| 1126 * matches |sessionId|. Otherwise run the handler immediately. | |
| 1127 */ | |
| 1128 private void deferEventHandleIfNeeded(SessionId sessionId, Runnable handler) { | |
| 1129 if (mSessionEventDeferrer != null && mSessionEventDeferrer.shouldDefer(s essionId)) { | |
| 1130 mSessionEventDeferrer.defer(handler); | |
| 1131 return; | |
| 1132 } | |
| 1133 | |
| 1134 handler.run(); | |
| 1135 } | |
| 1136 | |
| 972 // Helper functions to make native calls. | 1137 // Helper functions to make native calls. |
| 973 | 1138 |
| 974 private void onMediaCryptoReady(MediaCrypto mediaCrypto) { | 1139 private void onMediaCryptoReady(MediaCrypto mediaCrypto) { |
| 975 if (isNativeMediaDrmBridgeValid()) { | 1140 if (isNativeMediaDrmBridgeValid()) { |
| 976 nativeOnMediaCryptoReady(mNativeMediaDrmBridge, mediaCrypto); | 1141 nativeOnMediaCryptoReady(mNativeMediaDrmBridge, mediaCrypto); |
| 977 } | 1142 } |
| 978 } | 1143 } |
| 979 | 1144 |
| 980 private void onPromiseResolved(final long promiseId) { | 1145 private void onPromiseResolved(final long promiseId) { |
| 981 if (isNativeMediaDrmBridgeValid()) { | 1146 if (isNativeMediaDrmBridgeValid()) { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1015 mNativeMediaDrmBridge, sessionId.emeId(), requestType, request.g etData()); | 1180 mNativeMediaDrmBridge, sessionId.emeId(), requestType, request.g etData()); |
| 1016 } | 1181 } |
| 1017 | 1182 |
| 1018 private void onSessionClosed(final SessionId sessionId) { | 1183 private void onSessionClosed(final SessionId sessionId) { |
| 1019 if (isNativeMediaDrmBridgeValid()) { | 1184 if (isNativeMediaDrmBridgeValid()) { |
| 1020 nativeOnSessionClosed(mNativeMediaDrmBridge, sessionId.emeId()); | 1185 nativeOnSessionClosed(mNativeMediaDrmBridge, sessionId.emeId()); |
| 1021 } | 1186 } |
| 1022 } | 1187 } |
| 1023 | 1188 |
| 1024 private void onSessionKeysChange(final SessionId sessionId, final Object[] k eysInfo, | 1189 private void onSessionKeysChange(final SessionId sessionId, final Object[] k eysInfo, |
| 1025 final boolean hasAdditionalUsableKey) { | 1190 final boolean hasAdditionalUsableKey, final boolean isKeyRelease) { |
| 1026 if (isNativeMediaDrmBridgeValid()) { | 1191 if (isNativeMediaDrmBridgeValid()) { |
| 1027 nativeOnSessionKeysChange( | 1192 nativeOnSessionKeysChange(mNativeMediaDrmBridge, sessionId.emeId(), keysInfo, |
| 1028 mNativeMediaDrmBridge, sessionId.emeId(), keysInfo, hasAddit ionalUsableKey); | 1193 hasAdditionalUsableKey, isKeyRelease); |
| 1029 } | 1194 } |
| 1030 } | 1195 } |
| 1031 | 1196 |
| 1032 private void onSessionExpirationUpdate(final SessionId sessionId, final long expirationTime) { | 1197 private void onSessionExpirationUpdate(final SessionId sessionId, final long expirationTime) { |
| 1033 if (isNativeMediaDrmBridgeValid()) { | 1198 if (isNativeMediaDrmBridgeValid()) { |
| 1034 nativeOnSessionExpirationUpdate( | 1199 nativeOnSessionExpirationUpdate( |
| 1035 mNativeMediaDrmBridge, sessionId.emeId(), expirationTime); | 1200 mNativeMediaDrmBridge, sessionId.emeId(), expirationTime); |
| 1036 } | 1201 } |
| 1037 } | 1202 } |
| 1038 | 1203 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 1052 return; | 1217 return; |
| 1053 } | 1218 } |
| 1054 SessionId sessionId = getSessionIdByDrmId(drmSessionId); | 1219 SessionId sessionId = getSessionIdByDrmId(drmSessionId); |
| 1055 | 1220 |
| 1056 if (sessionId == null) { | 1221 if (sessionId == null) { |
| 1057 Log.e(TAG, "EventListener: Invalid session %s", | 1222 Log.e(TAG, "EventListener: Invalid session %s", |
| 1058 SessionId.toHexString(drmSessionId)); | 1223 SessionId.toHexString(drmSessionId)); |
| 1059 return; | 1224 return; |
| 1060 } | 1225 } |
| 1061 | 1226 |
| 1227 SessionInfo sessionInfo = mSessionManager.get(sessionId); | |
| 1062 switch(event) { | 1228 switch(event) { |
| 1063 case MediaDrm.EVENT_KEY_REQUIRED: | 1229 case MediaDrm.EVENT_KEY_REQUIRED: |
| 1064 Log.d(TAG, "MediaDrm.EVENT_KEY_REQUIRED"); | 1230 Log.d(TAG, "MediaDrm.EVENT_KEY_REQUIRED"); |
| 1065 if (mProvisioningPending) { | 1231 if (mProvisioningPending) { |
| 1066 return; | 1232 return; |
| 1067 } | 1233 } |
| 1068 SessionInfo sessionInfo = mSessionManager.get(sessionId); | |
| 1069 MediaDrm.KeyRequest request = null; | 1234 MediaDrm.KeyRequest request = null; |
| 1070 try { | 1235 try { |
| 1071 request = getKeyRequest(sessionId, data, sessionInfo.mim eType(), | 1236 request = getKeyRequest(sessionId, data, sessionInfo.mim eType(), |
| 1072 sessionInfo.keyType(), null); | 1237 sessionInfo.keyType(), null); |
| 1073 } catch (android.media.NotProvisionedException e) { | 1238 } catch (android.media.NotProvisionedException e) { |
| 1074 Log.e(TAG, "Device not provisioned", e); | 1239 Log.e(TAG, "Device not provisioned", e); |
| 1075 startProvisioning(); | 1240 startProvisioning(); |
| 1076 return; | 1241 return; |
| 1077 } | 1242 } |
| 1078 if (request != null) { | 1243 if (request != null) { |
| 1079 onSessionMessage(sessionId, request); | 1244 onSessionMessage(sessionId, request); |
| 1080 } else { | 1245 } else { |
| 1081 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { | 1246 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { |
| 1082 onSessionKeysChange(sessionId, | 1247 onSessionKeysChange(sessionId, |
| 1083 getDummyKeysInfo(MediaDrm.KeyStatus.STATUS_I NTERNAL_ERROR) | 1248 getDummyKeysInfo(MediaDrm.KeyStatus.STATUS_I NTERNAL_ERROR) |
| 1084 .toArray(), | 1249 .toArray(), |
| 1085 false); | 1250 false, false); |
| 1086 } | 1251 } |
| 1087 Log.e(TAG, "EventListener: getKeyRequest failed."); | 1252 Log.e(TAG, "EventListener: getKeyRequest failed."); |
| 1088 return; | 1253 return; |
| 1089 } | 1254 } |
| 1090 break; | 1255 break; |
| 1091 case MediaDrm.EVENT_KEY_EXPIRED: | 1256 case MediaDrm.EVENT_KEY_EXPIRED: |
| 1092 Log.d(TAG, "MediaDrm.EVENT_KEY_EXPIRED"); | 1257 Log.d(TAG, "MediaDrm.EVENT_KEY_EXPIRED"); |
| 1093 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { | 1258 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { |
| 1094 onSessionKeysChange(sessionId, | 1259 onSessionKeysChange(sessionId, |
| 1095 getDummyKeysInfo(MediaDrm.KeyStatus.STATUS_EXPIR ED).toArray(), | 1260 getDummyKeysInfo(MediaDrm.KeyStatus.STATUS_EXPIR ED).toArray(), |
| 1096 false); | 1261 false, sessionInfo.keyType() == MediaDrm.KEY_TYP E_RELEASE); |
| 1097 } | 1262 } |
| 1098 break; | 1263 break; |
| 1099 case MediaDrm.EVENT_VENDOR_DEFINED: | 1264 case MediaDrm.EVENT_VENDOR_DEFINED: |
| 1100 Log.d(TAG, "MediaDrm.EVENT_VENDOR_DEFINED"); | 1265 Log.d(TAG, "MediaDrm.EVENT_VENDOR_DEFINED"); |
| 1101 assert false; // Should never happen. | 1266 assert false; // Should never happen. |
| 1102 break; | 1267 break; |
| 1103 default: | 1268 default: |
| 1104 Log.e(TAG, "Invalid DRM event " + event); | 1269 Log.e(TAG, "Invalid DRM event " + event); |
| 1105 return; | 1270 return; |
| 1106 } | 1271 } |
| 1107 } | 1272 } |
| 1108 } | 1273 } |
| 1109 | 1274 |
| 1110 @TargetApi(Build.VERSION_CODES.M) | 1275 @TargetApi(Build.VERSION_CODES.M) |
| 1111 @MainDex | 1276 @MainDex |
| 1112 private class KeyStatusChangeListener implements MediaDrm.OnKeyStatusChangeL istener { | 1277 private class KeyStatusChangeListener implements MediaDrm.OnKeyStatusChangeL istener { |
| 1113 private List<KeyStatus> getKeysInfo(List<MediaDrm.KeyStatus> keyInformat ion) { | 1278 private List<KeyStatus> getKeysInfo(List<MediaDrm.KeyStatus> keyInformat ion) { |
| 1114 List<KeyStatus> keysInfo = new ArrayList<KeyStatus>(); | 1279 List<KeyStatus> keysInfo = new ArrayList<KeyStatus>(); |
| 1115 for (MediaDrm.KeyStatus keyStatus : keyInformation) { | 1280 for (MediaDrm.KeyStatus keyStatus : keyInformation) { |
| 1116 keysInfo.add(new KeyStatus(keyStatus.getKeyId(), keyStatus.getSt atusCode())); | 1281 keysInfo.add(new KeyStatus(keyStatus.getKeyId(), keyStatus.getSt atusCode())); |
| 1117 } | 1282 } |
| 1118 return keysInfo; | 1283 return keysInfo; |
| 1119 } | 1284 } |
| 1120 | 1285 |
| 1121 @Override | 1286 @Override |
| 1122 public void onKeyStatusChange(MediaDrm md, byte[] drmSessionId, | 1287 public void onKeyStatusChange(MediaDrm md, byte[] drmSessionId, |
| 1123 List<MediaDrm.KeyStatus> keyInformation, boolean hasNewUsableKey ) { | 1288 final List<MediaDrm.KeyStatus> keyInformation, final boolean has NewUsableKey) { |
| 1124 SessionId sessionId = getSessionIdByDrmId(drmSessionId); | 1289 final SessionId sessionId = getSessionIdByDrmId(drmSessionId); |
| 1125 | 1290 |
| 1126 assert sessionId != null; | 1291 assert sessionId != null; |
| 1292 assert mSessionManager.get(sessionId) != null; | |
| 1127 | 1293 |
| 1128 Log.d(TAG, "KeysStatusChange: " + sessionId.toHexString() + ", " + h asNewUsableKey); | 1294 final boolean isKeyRelease = |
| 1295 mSessionManager.get(sessionId).keyType() == MediaDrm.KEY_TYP E_RELEASE; | |
| 1129 | 1296 |
| 1130 onSessionKeysChange(sessionId, getKeysInfo(keyInformation).toArray() , hasNewUsableKey); | 1297 deferEventHandleIfNeeded(sessionId, new Runnable() { |
| 1298 @Override | |
| 1299 public void run() { | |
| 1300 Log.d(TAG, | |
| 1301 "KeysStatusChange: " + sessionId.toHexString() + ", " | |
| 1302 + hasNewUsableKey); | |
| 1303 onSessionKeysChange(sessionId, getKeysInfo(keyInformation).t oArray(), | |
| 1304 hasNewUsableKey, isKeyRelease); | |
| 1305 } | |
| 1306 }); | |
| 1131 } | 1307 } |
| 1132 } | 1308 } |
| 1133 | 1309 |
| 1134 @TargetApi(Build.VERSION_CODES.M) | 1310 @TargetApi(Build.VERSION_CODES.M) |
| 1135 @MainDex | 1311 @MainDex |
| 1136 private class ExpirationUpdateListener implements MediaDrm.OnExpirationUpdat eListener { | 1312 private class ExpirationUpdateListener implements MediaDrm.OnExpirationUpdat eListener { |
| 1137 @Override | 1313 @Override |
| 1138 public void onExpirationUpdate(MediaDrm md, byte[] drmSessionId, long ex pirationTime) { | 1314 public void onExpirationUpdate( |
| 1139 SessionId sessionId = getSessionIdByDrmId(drmSessionId); | 1315 MediaDrm md, byte[] drmSessionId, final long expirationTime) { |
| 1316 final SessionId sessionId = getSessionIdByDrmId(drmSessionId); | |
| 1140 | 1317 |
| 1141 assert sessionId != null; | 1318 assert sessionId != null; |
| 1142 | 1319 |
| 1143 Log.d(TAG, "ExpirationUpdate: " + sessionId.toHexString() + ", " + e xpirationTime); | 1320 deferEventHandleIfNeeded(sessionId, new Runnable() { |
| 1144 onSessionExpirationUpdate(sessionId, expirationTime); | 1321 @Override |
| 1322 public void run() { | |
| 1323 Log.d(TAG, | |
| 1324 "ExpirationUpdate: " + sessionId.toHexString() + ", " + expirationTime); | |
| 1325 onSessionExpirationUpdate(sessionId, expirationTime); | |
| 1326 } | |
| 1327 }); | |
| 1145 } | 1328 } |
| 1146 } | 1329 } |
| 1147 | 1330 |
| 1331 @MainDex | |
| 1332 private class KeyUpdatedCallback extends Callback<Boolean> { | |
| 1333 private final SessionId mSessionId; | |
| 1334 private final long mPromiseId; | |
| 1335 private final boolean mIsKeyRelease; | |
| 1336 | |
| 1337 KeyUpdatedCallback(SessionId sessionId, long promiseId, boolean isKeyRel ease) { | |
| 1338 mSessionId = sessionId; | |
| 1339 mPromiseId = promiseId; | |
| 1340 mIsKeyRelease = isKeyRelease; | |
| 1341 } | |
| 1342 | |
| 1343 @Override | |
| 1344 public void onResult(Boolean success) { | |
| 1345 if (!success) { | |
| 1346 onPromiseRejected(mPromiseId, "failed to update key after respon se accepted"); | |
| 1347 return; | |
| 1348 } | |
| 1349 | |
| 1350 Log.d(TAG, "Key successfully %s for session %s", mIsKeyRelease ? "re leased" : "added", | |
| 1351 mSessionId.toHexString()); | |
| 1352 onPromiseResolved(mPromiseId); | |
| 1353 | |
| 1354 if (!mIsKeyRelease && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { | |
| 1355 onSessionKeysChange(mSessionId, | |
| 1356 getDummyKeysInfo(MediaDrm.KeyStatus.STATUS_USABLE).toArr ay(), true, | |
| 1357 mIsKeyRelease); | |
| 1358 } | |
| 1359 } | |
| 1360 } | |
| 1361 | |
| 1148 // Native functions. At the native side, must post the task immediately to | 1362 // Native functions. At the native side, must post the task immediately to |
| 1149 // avoid reentrancy issues. | 1363 // avoid reentrancy issues. |
| 1150 private native void nativeOnMediaCryptoReady( | 1364 private native void nativeOnMediaCryptoReady( |
| 1151 long nativeMediaDrmBridge, MediaCrypto mediaCrypto); | 1365 long nativeMediaDrmBridge, MediaCrypto mediaCrypto); |
| 1152 | 1366 |
| 1153 private native void nativeOnStartProvisioning( | 1367 private native void nativeOnStartProvisioning( |
| 1154 long nativeMediaDrmBridge, String defaultUrl, byte[] requestData); | 1368 long nativeMediaDrmBridge, String defaultUrl, byte[] requestData); |
| 1155 | 1369 |
| 1156 private native void nativeOnPromiseResolved(long nativeMediaDrmBridge, long promiseId); | 1370 private native void nativeOnPromiseResolved(long nativeMediaDrmBridge, long promiseId); |
| 1157 private native void nativeOnPromiseResolvedWithSession( | 1371 private native void nativeOnPromiseResolvedWithSession( |
| 1158 long nativeMediaDrmBridge, long promiseId, byte[] emeSessionId); | 1372 long nativeMediaDrmBridge, long promiseId, byte[] emeSessionId); |
| 1159 private native void nativeOnPromiseRejected( | 1373 private native void nativeOnPromiseRejected( |
| 1160 long nativeMediaDrmBridge, long promiseId, String errorMessage); | 1374 long nativeMediaDrmBridge, long promiseId, String errorMessage); |
| 1161 | 1375 |
| 1162 private native void nativeOnSessionMessage( | 1376 private native void nativeOnSessionMessage( |
| 1163 long nativeMediaDrmBridge, byte[] emeSessionId, int requestType, byt e[] message); | 1377 long nativeMediaDrmBridge, byte[] emeSessionId, int requestType, byt e[] message); |
| 1164 private native void nativeOnSessionClosed(long nativeMediaDrmBridge, byte[] emeSessionId); | 1378 private native void nativeOnSessionClosed(long nativeMediaDrmBridge, byte[] emeSessionId); |
| 1165 private native void nativeOnSessionKeysChange(long nativeMediaDrmBridge, byt e[] emeSessionId, | 1379 private native void nativeOnSessionKeysChange(long nativeMediaDrmBridge, byt e[] emeSessionId, |
| 1166 Object[] keysInfo, boolean hasAdditionalUsableKey); | 1380 Object[] keysInfo, boolean hasAdditionalUsableKey, boolean isKeyRele ase); |
| 1167 private native void nativeOnSessionExpirationUpdate( | 1381 private native void nativeOnSessionExpirationUpdate( |
| 1168 long nativeMediaDrmBridge, byte[] emeSessionId, long expirationTime) ; | 1382 long nativeMediaDrmBridge, byte[] emeSessionId, long expirationTime) ; |
| 1169 | 1383 |
| 1170 private native void nativeOnResetDeviceCredentialsCompleted( | 1384 private native void nativeOnResetDeviceCredentialsCompleted( |
| 1171 long nativeMediaDrmBridge, boolean success); | 1385 long nativeMediaDrmBridge, boolean success); |
| 1172 } | 1386 } |
| OLD | NEW |