MediaDrmProxySupport.cpp (10571B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "MediaDrmProxySupport.h" 8 9 #include "MediaCodec.h" // For MediaDrm::KeyStatus 10 #include "MediaDrmCDMCallbackProxy.h" 11 #include "mozilla/EMEUtils.h" 12 #include "mozilla/java/MediaDrmProxyNatives.h" 13 #include "mozilla/java/SessionKeyInfoWrappers.h" 14 15 namespace mozilla { 16 17 LogModule* GetMDRMNLog() { 18 static LazyLogModule log("MediaDrmProxySupport"); 19 return log; 20 } 21 22 class MediaDrmJavaCallbacksSupport 23 : public java::MediaDrmProxy::NativeMediaDrmProxyCallbacks::Natives< 24 MediaDrmJavaCallbacksSupport> { 25 public: 26 typedef java::MediaDrmProxy::NativeMediaDrmProxyCallbacks::Natives< 27 MediaDrmJavaCallbacksSupport> 28 MediaDrmProxyNativeCallbacks; 29 using MediaDrmProxyNativeCallbacks::AttachNative; 30 using MediaDrmProxyNativeCallbacks::DisposeNative; 31 32 explicit MediaDrmJavaCallbacksSupport( 33 UniquePtr<MediaDrmCDMCallbackProxy>&& aDecryptorProxyCallback) 34 : mDecryptorProxyCallback(std::move(aDecryptorProxyCallback)) { 35 MOZ_ASSERT(mDecryptorProxyCallback); 36 } 37 /* 38 * Native implementation, called by Java. 39 */ 40 void OnSessionCreated(int aCreateSessionToken, int aPromiseId, 41 jni::ByteArray::Param aSessionId, 42 jni::ByteArray::Param aRequest); 43 44 void OnSessionUpdated(int aPromiseId, jni::ByteArray::Param aSessionId); 45 46 void OnSessionClosed(int aPromiseId, jni::ByteArray::Param aSessionId); 47 48 void OnSessionMessage( 49 jni::ByteArray::Param aSessionId, 50 int /*mozilla::dom::MediaKeyMessageType*/ aSessionMessageType, 51 jni::ByteArray::Param aRequest); 52 53 void OnSessionError(jni::ByteArray::Param aSessionId, 54 jni::String::Param aMessage); 55 56 void OnSessionBatchedKeyChanged(jni::ByteArray::Param, 57 jni::ObjectArray::Param); 58 59 void OnRejectPromise(int aPromiseId, jni::String::Param aMessage); 60 61 private: 62 UniquePtr<MediaDrmCDMCallbackProxy> mDecryptorProxyCallback; 63 }; // MediaDrmJavaCallbacksSupport 64 65 void MediaDrmJavaCallbacksSupport::OnSessionCreated( 66 int aCreateSessionToken, int aPromiseId, jni::ByteArray::Param aSessionId, 67 jni::ByteArray::Param aRequest) { 68 MOZ_ASSERT(NS_IsMainThread()); 69 auto reqDataArray = aRequest->GetElements(); 70 nsCString sessionId( 71 reinterpret_cast<char*>(aSessionId->GetElements().Elements()), 72 aSessionId->Length()); 73 MDRMN_LOG("SessionId(%s) closed", sessionId.get()); 74 75 mDecryptorProxyCallback->SetSessionId(aCreateSessionToken, sessionId); 76 mDecryptorProxyCallback->ResolvePromise(aPromiseId); 77 } 78 79 void MediaDrmJavaCallbacksSupport::OnSessionUpdated( 80 int aPromiseId, jni::ByteArray::Param aSessionId) { 81 MOZ_ASSERT(NS_IsMainThread()); 82 MDRMN_LOG( 83 "SessionId(%s) closed", 84 nsCString(reinterpret_cast<char*>(aSessionId->GetElements().Elements()), 85 aSessionId->Length()) 86 .get()); 87 mDecryptorProxyCallback->ResolvePromise(aPromiseId); 88 } 89 90 void MediaDrmJavaCallbacksSupport::OnSessionClosed( 91 int aPromiseId, jni::ByteArray::Param aSessionId) { 92 MOZ_ASSERT(NS_IsMainThread()); 93 nsCString sessionId( 94 reinterpret_cast<char*>(aSessionId->GetElements().Elements()), 95 aSessionId->Length()); 96 MDRMN_LOG("SessionId(%s) closed", sessionId.get()); 97 mDecryptorProxyCallback->ResolvePromise(aPromiseId); 98 mDecryptorProxyCallback->SessionClosed(sessionId); 99 } 100 101 void MediaDrmJavaCallbacksSupport::OnSessionMessage( 102 jni::ByteArray::Param aSessionId, 103 int /*mozilla::dom::MediaKeyMessageType*/ aMessageType, 104 jni::ByteArray::Param aRequest) { 105 MOZ_ASSERT(NS_IsMainThread()); 106 nsCString sessionId( 107 reinterpret_cast<char*>(aSessionId->GetElements().Elements()), 108 aSessionId->Length()); 109 auto reqDataArray = aRequest->GetElements(); 110 111 nsTArray<uint8_t> retRequest; 112 retRequest.AppendElements(reinterpret_cast<uint8_t*>(reqDataArray.Elements()), 113 reqDataArray.Length()); 114 115 mDecryptorProxyCallback->SessionMessage( 116 sessionId, static_cast<dom::MediaKeyMessageType>(aMessageType), 117 retRequest); 118 } 119 120 void MediaDrmJavaCallbacksSupport::OnSessionError( 121 jni::ByteArray::Param aSessionId, jni::String::Param aMessage) { 122 MOZ_ASSERT(NS_IsMainThread()); 123 nsCString sessionId( 124 reinterpret_cast<char*>(aSessionId->GetElements().Elements()), 125 aSessionId->Length()); 126 nsCString errorMessage = aMessage->ToCString(); 127 MDRMN_LOG("SessionId(%s)", sessionId.get()); 128 // TODO: We cannot get system error code from media drm API. 129 // Currently use -1 as an error code. 130 mDecryptorProxyCallback->SessionError( 131 sessionId, NS_ERROR_DOM_INVALID_STATE_ERR, -1, errorMessage); 132 } 133 134 // TODO: MediaDrm.KeyStatus defined the status code not included 135 // dom::MediaKeyStatus::Released and dom::MediaKeyStatus::Output_downscaled. 136 // Should keep tracking for this if it will be changed in the future. 137 static dom::MediaKeyStatus MediaDrmKeyStatusToMediaKeyStatus(int aStatusCode) { 138 using mozilla::java::sdk::MediaDrm; 139 switch (aStatusCode) { 140 case MediaDrm::KeyStatus::STATUS_USABLE: 141 return dom::MediaKeyStatus::Usable; 142 case MediaDrm::KeyStatus::STATUS_EXPIRED: 143 return dom::MediaKeyStatus::Expired; 144 case MediaDrm::KeyStatus::STATUS_OUTPUT_NOT_ALLOWED: 145 return dom::MediaKeyStatus::Output_restricted; 146 case MediaDrm::KeyStatus::STATUS_INTERNAL_ERROR: 147 return dom::MediaKeyStatus::Internal_error; 148 case MediaDrm::KeyStatus::STATUS_PENDING: 149 return dom::MediaKeyStatus::Status_pending; 150 default: 151 return dom::MediaKeyStatus::Internal_error; 152 } 153 } 154 155 void MediaDrmJavaCallbacksSupport::OnSessionBatchedKeyChanged( 156 jni::ByteArray::Param aSessionId, jni::ObjectArray::Param aKeyInfos) { 157 MOZ_ASSERT(NS_IsMainThread()); 158 nsCString sessionId( 159 reinterpret_cast<char*>(aSessionId->GetElements().Elements()), 160 aSessionId->Length()); 161 nsTArray<jni::Object::LocalRef> keyInfosObjectArray(aKeyInfos->GetElements()); 162 163 nsTArray<CDMKeyInfo> keyInfosArray; 164 165 for (auto&& keyInfoObject : keyInfosObjectArray) { 166 java::SessionKeyInfo::LocalRef keyInfo(std::move(keyInfoObject)); 167 mozilla::jni::ByteArray::LocalRef keyIdByteArray = keyInfo->KeyId(); 168 nsTArray<int8_t> keyIdInt8Array = keyIdByteArray->GetElements(); 169 // Cast nsTArray<int8_t> to nsTArray<uint8_t> 170 nsTArray<uint8_t>* keyId = 171 reinterpret_cast<nsTArray<uint8_t>*>(&keyIdInt8Array); 172 auto keyStatus = keyInfo->Status(); // int32_t 173 keyInfosArray.AppendElement( 174 CDMKeyInfo(*keyId, dom::Optional<dom::MediaKeyStatus>( 175 MediaDrmKeyStatusToMediaKeyStatus(keyStatus)))); 176 } 177 178 mDecryptorProxyCallback->BatchedKeyStatusChanged(sessionId, keyInfosArray); 179 } 180 181 void MediaDrmJavaCallbacksSupport::OnRejectPromise( 182 int aPromiseId, jni::String::Param aMessage) { 183 MOZ_ASSERT(NS_IsMainThread()); 184 nsCString reason = aMessage->ToCString(); 185 MDRMN_LOG("OnRejectPromise aMessage(%s) ", reason.get()); 186 // Current implementation assume all the reject from MediaDrm is due to 187 // invalid state. Other cases should be handled before calling into 188 // MediaDrmProxy API. 189 ErrorResult rv; 190 rv.ThrowInvalidStateError(reason); 191 mDecryptorProxyCallback->RejectPromise(aPromiseId, std::move(rv), reason); 192 } 193 194 MediaDrmProxySupport::MediaDrmProxySupport(const nsAString& aKeySystem) 195 : mKeySystem(aKeySystem), mDestroyed(false) { 196 mJavaCallbacks = java::MediaDrmProxy::NativeMediaDrmProxyCallbacks::New(); 197 198 mBridgeProxy = java::MediaDrmProxy::Create(mKeySystem, mJavaCallbacks); 199 200 MOZ_ASSERT(mBridgeProxy, "mBridgeProxy should not be null"); 201 mMediaDrmStubId = mBridgeProxy->GetStubId()->ToString(); 202 } 203 204 MediaDrmProxySupport::~MediaDrmProxySupport() { 205 MOZ_ASSERT(mDestroyed, "Shutdown() should be called before !!"); 206 MediaDrmJavaCallbacksSupport::DisposeNative(mJavaCallbacks); 207 } 208 209 nsresult MediaDrmProxySupport::Init( 210 UniquePtr<MediaDrmCDMCallbackProxy>&& aCallback) { 211 MOZ_ASSERT(mJavaCallbacks); 212 213 MediaDrmJavaCallbacksSupport::AttachNative( 214 mJavaCallbacks, 215 mozilla::MakeUnique<MediaDrmJavaCallbacksSupport>(std::move(aCallback))); 216 return mBridgeProxy != nullptr ? NS_OK : NS_ERROR_FAILURE; 217 } 218 219 void MediaDrmProxySupport::CreateSession(uint32_t aCreateSessionToken, 220 uint32_t aPromiseId, 221 const nsCString& aInitDataType, 222 const nsTArray<uint8_t>& aInitData, 223 MediaDrmSessionType aSessionType) { 224 MOZ_ASSERT(mBridgeProxy); 225 226 auto initDataBytes = mozilla::jni::ByteArray::New( 227 reinterpret_cast<const int8_t*>(aInitData.Elements()), 228 aInitData.Length()); 229 // TODO: aSessionType is not used here. 230 // Refer to 231 // http://androidxref.com/5.1.1_r6/xref/external/chromium_org/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java#420 232 // it is hard code to streaming type. 233 mBridgeProxy->CreateSession(aCreateSessionToken, aPromiseId, 234 NS_ConvertUTF8toUTF16(aInitDataType), 235 initDataBytes); 236 } 237 238 void MediaDrmProxySupport::UpdateSession(uint32_t aPromiseId, 239 const nsCString& aSessionId, 240 const nsTArray<uint8_t>& aResponse) { 241 MOZ_ASSERT(mBridgeProxy); 242 243 auto response = mozilla::jni::ByteArray::New( 244 reinterpret_cast<const int8_t*>(aResponse.Elements()), 245 aResponse.Length()); 246 mBridgeProxy->UpdateSession(aPromiseId, NS_ConvertUTF8toUTF16(aSessionId), 247 response); 248 } 249 250 void MediaDrmProxySupport::CloseSession(uint32_t aPromiseId, 251 const nsCString& aSessionId) { 252 MOZ_ASSERT(mBridgeProxy); 253 254 mBridgeProxy->CloseSession(aPromiseId, NS_ConvertUTF8toUTF16(aSessionId)); 255 } 256 257 void MediaDrmProxySupport::Shutdown() { 258 MOZ_ASSERT(mBridgeProxy); 259 260 if (mDestroyed) { 261 return; 262 } 263 mBridgeProxy->Destroy(); 264 mDestroyed = true; 265 } 266 267 bool MediaDrmProxySupport::SetServerCertificate( 268 const nsTArray<uint8_t>& aCert) { 269 jni::ByteArray::LocalRef cert = jni::ByteArray::New( 270 reinterpret_cast<const int8_t*>(aCert.Elements()), aCert.Length()); 271 return mBridgeProxy->SetServerCertificate(cert); 272 } 273 274 } // namespace mozilla