RDK Documentation (Open Sourced RDK Components)
opencdmsessionadapter.cpp
Go to the documentation of this file.
1 /**
2  * @file opencdmsessionadapter.cpp
3  * @brief Handles operation with OCDM session to handle DRM License data
4  */
5 #include "config.h"
7 #include "AampDrmHelper.h"
8 #include "AampUtils.h"
9 
10 #include <gst/gst.h>
11 #include <assert.h>
12 #include <iostream>
13 #include <sstream>
14 #include <string>
15 #include <errno.h>
16 #include <string.h>
17 #include <vector>
18 #include <sys/utsname.h>
19 #include <unistd.h>
20 #include <sys/syscall.h>
21 #include "priv_aamp.h"
22 
23 #include <sys/time.h>
24 #include <gst/gstbuffer.h>
25 
26 
27 /**
28  * @fn AAMPOCDMSessionAdapter
29  * @brief AAMPOCDMSessionAdapter constructor
30  */
31 AAMPOCDMSessionAdapter::AAMPOCDMSessionAdapter(AampLogManager *logObj, std::shared_ptr<AampDrmHelper> drmHelper, AampDrmCallbacks *callbacks) :
32  AampDrmSession(logObj, drmHelper->ocdmSystemId()),
33  m_eKeyState(KEY_INIT),
34  m_pOpenCDMSystem(NULL),
35  m_pOpenCDMSession(NULL),
36  m_pOutputProtection(NULL),
37  decryptMutex(),
38  m_sessionID(),
39  m_challenge(),
40  timeBeforeCallback(0),
41  m_challengeReady(),
42  m_challengeSize(0),
43  m_keyStatus(InternalError),
44  m_keyStateIndeterminate(false),
45  m_keyStatusReady(),
46  m_OCDMSessionCallbacks(),
47  m_destUrl(),
48  m_drmHelper(drmHelper),
49  m_drmCallbacks(callbacks),
50  m_keyStatusWait(),
51  m_keyId(),
52  m_keyStored()
53 {
54  AAMPLOG_WARN("AAMPOCDMSessionAdapter :: enter ");
55  pthread_mutex_init(&decryptMutex, NULL);
56 
57  AAMPLOG_WARN("AAMPOCDMSessionAdapter :: key process timeout is %d", drmHelper->keyProcessTimeout());
58 
59  initAampDRMSystem();
60 
61  // Get output protection pointer
63  AAMPLOG_WARN("AAMPOCDMSessionAdapter :: exit ");
64 }
65 
66 
67 void AAMPOCDMSessionAdapter::initAampDRMSystem()
68 {
69  logprintf("initAampDRMSystem :: enter ");
70  pthread_mutex_lock(&decryptMutex);
71  if (m_pOpenCDMSystem == nullptr) {
72 #ifdef USE_THUNDER_OCDM_API_0_2
73  m_pOpenCDMSystem = opencdm_create_system(m_keySystem.c_str());
74 #else
75  m_pOpenCDMSystem = opencdm_create_system();
76 #endif
77  if (m_pOpenCDMSystem == nullptr) {
78  AAMPLOG_ERR("opencdm_create_system() FAILED");
79  }
80  }
81  pthread_mutex_unlock(&decryptMutex);
82  AAMPLOG_WARN("initAampDRMSystem :: exit ");
83 }
84 
85 
86 AAMPOCDMSessionAdapter::~AAMPOCDMSessionAdapter()
87 {
88  AAMPLOG_WARN("[HHH]OCDMSessionAdapter destructor called! keySystem %s", m_keySystem.c_str());
90 
91  pthread_mutex_destroy(&decryptMutex);
92 
93  if (m_pOpenCDMSystem) {
94  opencdm_destruct_system(m_pOpenCDMSystem);
95  m_pOpenCDMSystem = NULL;
96  }
97 
98  if(m_pOutputProtection) {
99  m_pOutputProtection->Release();
100  }
101 }
102 
103 
104 void AAMPOCDMSessionAdapter::generateAampDRMSession(const uint8_t *f_pbInitData,
105  uint32_t f_cbInitData, std::string &customData)
106 {
107  AAMPLOG_INFO("at %p, with %p, %p", this , m_pOpenCDMSystem, m_pOpenCDMSession);
108 
109  pthread_mutex_lock(&decryptMutex);
110 
111  if (m_pOpenCDMSystem == nullptr)
112  {
113  AAMPLOG_WARN("OpenCDM system not present, unable to generate DRM session");
114  m_eKeyState = KEY_ERROR;
115  }
116  else
117  {
118  memset(&m_OCDMSessionCallbacks, 0, sizeof(m_OCDMSessionCallbacks));
119  timeBeforeCallback = aamp_GetCurrentTimeMS();
120  m_OCDMSessionCallbacks.process_challenge_callback = [](OpenCDMSession* session, void* userData, const char destUrl[], const uint8_t challenge[], const uint16_t challengeSize) {
121  AAMPOCDMSessionAdapter* userSession = reinterpret_cast<AAMPOCDMSessionAdapter*>(userData);
122  userSession->timeBeforeCallback = ((aamp_GetCurrentTimeMS())-(userSession->timeBeforeCallback));
123  logprintf("Duration for process_challenge_callback %lld",(userSession->timeBeforeCallback));
124  userSession->processOCDMChallenge(destUrl, challenge, challengeSize);
125  };
126 
127  m_OCDMSessionCallbacks.key_update_callback = [](OpenCDMSession* session, void* userData, const uint8_t key[], const uint8_t keySize) {
128  AAMPOCDMSessionAdapter* userSession = reinterpret_cast<AAMPOCDMSessionAdapter*>(userData);
129  userSession->keyUpdateOCDM(key, keySize);
130  };
131 
132  m_OCDMSessionCallbacks.error_message_callback = [](OpenCDMSession* session, void* userData, const char message[]) {
133  };
134 
135  m_OCDMSessionCallbacks.keys_updated_callback = [](const OpenCDMSession* session, void* userData) {
136  AAMPOCDMSessionAdapter* userSession = reinterpret_cast<AAMPOCDMSessionAdapter*>(userData);
137  userSession->keysUpdatedOCDM();
138  };
139  const unsigned char *customDataMessage = customData.empty() ? nullptr:reinterpret_cast<const unsigned char *>(customData.c_str()) ;
140  const uint16_t customDataMessageLength = customData.length();
141  AAMPLOG_INFO("data length : %d: ", customDataMessageLength);
142 #ifdef USE_THUNDER_OCDM_API_0_2
143  OpenCDMError ocdmRet = opencdm_construct_session(m_pOpenCDMSystem, LicenseType::Temporary, "video/mp4",
144 #else
145  OpenCDMError ocdmRet = opencdm_construct_session(m_pOpenCDMSystem, m_keySystem.c_str(), LicenseType::Temporary, "video/mp4",
146 #endif
147 
148  const_cast<unsigned char*>(f_pbInitData), f_cbInitData,
149  customDataMessage, customDataMessageLength,
150  &m_OCDMSessionCallbacks,
151  static_cast<void*>(this),
152  &m_pOpenCDMSession);
153  if (ocdmRet != ERROR_NONE)
154  {
155  AAMPLOG_ERR("Error constructing OCDM session. OCDM err=0x%x", ocdmRet);
156  m_eKeyState = KEY_ERROR;
157  }
158  }
159 
160  pthread_mutex_unlock(&decryptMutex);
161 }
162 
163 
164 void AAMPOCDMSessionAdapter::processOCDMChallenge(const char destUrl[], const uint8_t challenge[], const uint16_t challengeSize) {
165 
166  AAMPLOG_INFO("at %p, with %p, %p", this , m_pOpenCDMSystem, m_pOpenCDMSession);
167 
168  const std::string challengeData(reinterpret_cast<const char *>(challenge), challengeSize);
169  const std::set<std::string> individualisationTypes = {"individualization-request", "3"};
170  const std::string delimiter(":Type:");
171  const size_t delimiterPos = challengeData.find(delimiter);
172  const std::string messageType = challengeData.substr(0, delimiterPos);
173 
174  // Check if this message should be forwarded using a DRM callback.
175  // Example message: individualization-request:Type:(payload)
176  if ((delimiterPos != std::string::npos) && (individualisationTypes.count(messageType) > 0))
177  {
178  AAMPLOG_WARN("processOCDMChallenge received message with type=%s", messageType.c_str());
179 
180  if (m_drmCallbacks)
181  {
182  m_drmCallbacks->individualization(challengeData.substr(delimiterPos + delimiter.length()));
183  }
184  }
185  else
186  {
187  // Assuming this is a standard challenge callback
188  m_challenge = challengeData;
189  AAMPLOG_WARN("processOCDMChallenge challenge = %s", m_challenge.c_str());
190 
191  m_destUrl.assign(destUrl);
192  AAMPLOG_WARN("processOCDMChallenge destUrl = %s (default value used as drm server)", m_destUrl.c_str());
193 
194  m_challengeReady.signal();
195  }
196 }
197 
198 
199 void AAMPOCDMSessionAdapter::keyUpdateOCDM(const uint8_t key[], const uint8_t keySize) {
200  AAMPLOG_INFO("at %p, with %p, %p", this , m_pOpenCDMSystem, m_pOpenCDMSession);
201  if (m_pOpenCDMSession) {
202  m_keyStatus = opencdm_session_status(m_pOpenCDMSession, key, keySize);
203  m_keyStateIndeterminate = false;
204  }
205  else {
206  m_keyStored.clear();
207  m_keyStored.assign(key, key+keySize);
208  m_keyStateIndeterminate = true;
209  }
210 
211 }
212 
213 void AAMPOCDMSessionAdapter::keysUpdatedOCDM() {
214  AAMPLOG_INFO("at %p, with %p, %p", this , m_pOpenCDMSystem, m_pOpenCDMSession);
215  m_keyStatusReady.signal();
216 }
217 
218 
219 DrmData * AAMPOCDMSessionAdapter::aampGenerateKeyRequest(string& destinationURL, uint32_t timeout)
220 {
221  AAMPLOG_INFO("at %p, with %p, %p", this , m_pOpenCDMSystem, m_pOpenCDMSession);
222  DrmData * result = NULL;
223 
224  m_eKeyState = KEY_ERROR;
225 
226  if (m_challengeReady.wait(timeout) == true) {
227  if (m_challenge.empty() != true) {
228  std::string delimiter (":Type:");
229  std::string requestType (m_challenge.substr(0, m_challenge.find(delimiter)));
230  if ( (requestType.size() != 0) && (requestType.size() != m_challenge.size()) ) {
231  (void) m_challenge.erase(0, m_challenge.find(delimiter) + delimiter.length());
232  }
233 
234  result = new DrmData(reinterpret_cast<unsigned char*>(const_cast<char*>(m_challenge.c_str())), m_challenge.length());
235  destinationURL.assign((m_destUrl.c_str()));
236  AAMPLOG_WARN("destinationURL is %s (default value used as drm server)", destinationURL.c_str());
237  m_eKeyState = KEY_PENDING;
238  }
239  else {
240  AAMPLOG_WARN("Empty keyRequest");
241  }
242  } else {
243  AAMPLOG_WARN("Timed out waiting for keyRequest");
244  }
245  return result;
246 }
247 
248 
250 {
251  AAMPLOG_INFO("at %p, with %p, %p", this , m_pOpenCDMSystem, m_pOpenCDMSession);
252  int retValue = -1;
253  const uint8_t* keyMessage = NULL;
254  uint16_t keyMessageLength = 0;
255 
256  OpenCDMError status = OpenCDMError::ERROR_NONE;
257 
258  if (key)
259  {
260  keyMessage = (const uint8_t *)key->getData().c_str();
261  keyMessageLength = key->getDataLength();
262  }
263 
264  if (keyMessage)
265  {
266  AAMPLOG_INFO("Calling opencdm_session_update, key length=%u", keyMessageLength);
267  status = opencdm_session_update(m_pOpenCDMSession, keyMessage, keyMessageLength);
268  }
269  else
270  {
271  // If no key data has been provided then this suggests the key acquisition
272  // will be performed by the DRM implementation itself. Hence there is no
273  // need to call opencdm_session_update
274  AAMPLOG_INFO("NULL key data provided, assuming external key acquisition");
275  }
276 
277  if (status == OpenCDMError::ERROR_NONE) {
278  if (m_keyStatusReady.wait(timeout) == true) {
279  AAMPLOG_WARN("Key Status updated");
280  }
281  // The key could be signalled ready before the session is even created, so we need to check we didn't miss it
282  if (m_keyStateIndeterminate) {
283  m_keyStatus = opencdm_session_status(m_pOpenCDMSession, m_keyStored.data(), m_keyStored.size());
284  m_keyStateIndeterminate = false;
285  AAMPLOG_WARN("Key arrived early, new state is %d", m_keyStatus);
286  }
287 #ifdef USE_THUNDER_OCDM_API_0_2
288  if (m_keyStatus == Usable) {
289 #else
290  if (m_keyStatus == KeyStatus::Usable) {
291 #endif
292  AAMPLOG_WARN("processKey: Key Usable!");
293  m_eKeyState = KEY_READY;
294  retValue = 0;
295  }
296 #ifdef USE_THUNDER_OCDM_API_0_2
297  else if(m_keyStatus == HWError)
298 #else
299  else if(m_keyStatus == KeyStatus::HWError)
300 #endif
301  {
302  // BCOM-3537 - SAGE Hang .. Need to restart the wpecdmi process and then self kill player to recover
303  AAMPLOG_WARN("processKey: Update() returned HWError.Restarting process...");
304  int systemResult = -1;
305  // In Release another process handles opencdm which needs to be restarts .In Sprint this process is not available.
306  // So check if process exists before killing it .
307  systemResult = system("pgrep WPEcdmi");
308  if(systemResult == 0)
309  {
310  systemResult = system("pkill -9 WPEcdmi");
311  if(systemResult != 0)
312  {
313  AAMPLOG_WARN("Unable to shutdown WPEcdmi process.%d", systemResult);
314  }
315  }
316  else
317  {
318  // check for WPEFramework process
319  systemResult = system("pgrep WPEFramework");
320  if(systemResult == 0)
321  {
322  systemResult = system("pkill -9 WPEFramework");
323  if(systemResult != 0)
324  {
325  AAMPLOG_WARN("Unable to shutdown WPEFramework process.%d", systemResult);
326  }
327  }
328  }
329 
330  // wait for 5sec for all the logs to be flushed
331  sleep(5);
332  // Now kill self
333  if(kill(getpid(), SIGKILL) < 0)
334  {
335  AAMPLOG_WARN("Kill Failed = %d", errno); //CID:88415 - checked return
336  }
337  }
338  else {
339 #ifdef USE_THUNDER_OCDM_API_0_2
340  if(m_keyStatus == OutputRestricted)
341 #else
342  if(m_keyStatus == KeyStatus::OutputRestricted)
343 #endif
344  {
345  AAMPLOG_WARN("processKey: Update() Output restricted keystatus: %d", (int) m_keyStatus);
346  retValue = HDCP_OUTPUT_PROTECTION_FAILURE;
347  }
348 #ifdef USE_THUNDER_OCDM_API_0_2
349  else if(m_keyStatus == OutputRestrictedHDCP22)
350 #else
351  else if(m_keyStatus == KeyStatus::OutputRestrictedHDCP22)
352 #endif
353  {
354  AAMPLOG_WARN("processKey: Update() Output Compliance error keystatus: %d\n", (int) m_keyStatus);
355  retValue = HDCP_COMPLIANCE_CHECK_FAILURE;
356  }
357  else
358  {
359  AAMPLOG_WARN("processKey: Update() returned keystatus: %d\n", (int) m_keyStatus);
360  retValue = (int) m_keyStatus;
361  }
362  m_eKeyState = KEY_ERROR;
363  }
364  }
365  m_keyStatusWait.signal();
366  return retValue;
367 }
368 
369 
370 bool AAMPOCDMSessionAdapter::waitForState(KeyState state, const uint32_t timeout)
371 {
372  if (m_eKeyState == state) {
373  return true;
374  }
375  if (!m_keyStatusWait.wait(timeout)) {
376  return false;
377  }
378  return m_eKeyState == state;
379 }
380 
381 
383 {
384  return m_eKeyState;
385 }
386 
387 
389 {
390  AAMPLOG_WARN("[HHH] clearDecryptContext.");
391 
392  pthread_mutex_lock(&decryptMutex);
393 
394  if (m_pOpenCDMSession) {
395  opencdm_session_close(m_pOpenCDMSession);
396  opencdm_destruct_session(m_pOpenCDMSession);
397  m_pOpenCDMSession = NULL;
398  }
399 
400  pthread_mutex_unlock(&decryptMutex);
401  m_eKeyState = KEY_INIT;
402 }
403 
404 
405 void AAMPOCDMSessionAdapter::setKeyId(const std::vector<uint8_t>& keyId)
406 {
407  m_keyId = keyId;
408 }
409 
410 
411 bool AAMPOCDMSessionAdapter::verifyOutputProtection()
412 {
413  if (m_drmHelper->isHdcp22Required() && m_pOutputProtection->IsSourceUHD())
414  {
415  // Source material is UHD
416  if (!m_pOutputProtection->isHDCPConnection2_2())
417  {
418  // UHD and not HDCP 2.2
419  AAMPLOG_WARN("UHD source but not HDCP 2.2. FAILING decrypt");
420  return false;
421  }
422  }
423 
424  return true;
425 }
AAMPOCDMSessionAdapter::waitForState
bool waitForState(KeyState state, const uint32_t timeout) override
Waits for the current state of DRM Session to match required.. Timeout is that from the helper....
Definition: opencdmsessionadapter.cpp:370
AampDrmCallbacks
DRM callback interface.
Definition: AampDrmCallbacks.h:34
AampDrmHelper.h
Implented DRM helper functionalities.
DrmData
To hold DRM key, license request etc.
Definition: AampDrmData.h:32
logprintf
void logprintf(const char *format,...)
Print logs to console / log fil.
Definition: aamplogging.cpp:432
AAMPOCDMSessionAdapter::getState
KeyState getState()
Get the current state of DRM Session.
Definition: opencdmsessionadapter.cpp:382
AAMPOCDMSessionAdapter::aampGenerateKeyRequest
DrmData * aampGenerateKeyRequest(string &destinationURL, uint32_t timeout)
Generate key request from DRM session Caller function should free the returned memory.
Definition: opencdmsessionadapter.cpp:219
AampLogManager
AampLogManager Class.
Definition: AampLogManager.h:150
KEY_PENDING
@ KEY_PENDING
Definition: AampDrmSession.h:57
AampOutputProtection::IsSourceUHD
bool IsSourceUHD()
Check if source is UHD using video decoder dimensions.
Definition: aampoutputprotection.cpp:102
AAMPOCDMSessionAdapter::clearDecryptContext
void clearDecryptContext()
Clear the current session context So that new init data can be bound.
Definition: opencdmsessionadapter.cpp:388
DrmData::getDataLength
int getDataLength()
Getter method for dataLength.
Definition: AampDRMutils.cpp:83
AAMPOCDMSessionAdapter::aampDRMProcessKey
int aampDRMProcessKey(DrmData *key, uint32_t timeout)
Updates the received key to DRM session.
Definition: opencdmsessionadapter.cpp:249
AAMPOCDMSessionAdapter
Open CDM DRM session.
Definition: opencdmsessionadapter.h:82
AampDrmSession
Base class for DRM sessions.
Definition: AampDrmSession.h:69
AampOutputProtection::GetAampOutputProcectionInstance
static AampOutputProtection * GetAampOutputProcectionInstance()
Singleton for object creation.
Definition: aampoutputprotection.cpp:441
AampOutputProtection::isHDCPConnection2_2
bool isHDCPConnection2_2()
Get PlayRedy OP levels.
Definition: aampoutputprotection.h:230
priv_aamp.h
Private functions and types used internally by AAMP.
KEY_INIT
@ KEY_INIT
Definition: AampDrmSession.h:56
opencdmsessionadapter.h
Handles operation with OCDM session to handle DRM License data.
KEY_READY
@ KEY_READY
Definition: AampDrmSession.h:58
aamp_GetCurrentTimeMS
long long aamp_GetCurrentTimeMS(void)
Get current time from epoch is milliseconds.
Definition: AampUtils.cpp:92
KeyState
KeyState
DRM session states.
Definition: AampDrmSession.h:54
AAMPOCDMSessionAdapter::generateAampDRMSession
void generateAampDRMSession(const uint8_t *f_pbInitData, uint32_t f_cbInitData, std::string &customData)
Create drm session with given init data.
Definition: opencdmsessionadapter.cpp:104
DrmData::getData
const std::string & getData()
Getter method for data.
Definition: AampDRMutils.cpp:75
KEY_ERROR
@ KEY_ERROR
Definition: AampDrmSession.h:59