@webex/calling 3.0.0-next.4 → 3.0.0-next.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/CHANGELOG.md +1403 -1403
  2. package/README.md +74 -74
  3. package/dist/CallHistory/CallHistory.js +201 -0
  4. package/dist/CallHistory/CallHistory.js.map +1 -0
  5. package/dist/CallHistory/CallHistory.test.js +142 -0
  6. package/dist/CallHistory/CallHistory.test.js.map +1 -0
  7. package/dist/CallHistory/callHistoryFixtures.js +366 -0
  8. package/dist/CallHistory/callHistoryFixtures.js.map +1 -0
  9. package/dist/CallHistory/constants.js +17 -0
  10. package/dist/CallHistory/constants.js.map +1 -0
  11. package/dist/CallHistory/types.js +7 -0
  12. package/dist/CallHistory/types.js.map +1 -0
  13. package/dist/CallSettings/CallSettings.js +279 -0
  14. package/dist/CallSettings/CallSettings.js.map +1 -0
  15. package/dist/CallSettings/CallSettings.test.js +122 -0
  16. package/dist/CallSettings/CallSettings.test.js.map +1 -0
  17. package/dist/CallSettings/UcmBackendConnector.js +216 -0
  18. package/dist/CallSettings/UcmBackendConnector.js.map +1 -0
  19. package/dist/CallSettings/UcmBackendConnector.test.js +259 -0
  20. package/dist/CallSettings/UcmBackendConnector.test.js.map +1 -0
  21. package/dist/CallSettings/WxCallBackendConnector.js +547 -0
  22. package/dist/CallSettings/WxCallBackendConnector.js.map +1 -0
  23. package/dist/CallSettings/WxCallBackendConnector.test.js +802 -0
  24. package/dist/CallSettings/WxCallBackendConnector.test.js.map +1 -0
  25. package/dist/CallSettings/constants.js +19 -0
  26. package/dist/CallSettings/constants.js.map +1 -0
  27. package/dist/CallSettings/testFixtures.js +68 -0
  28. package/dist/CallSettings/testFixtures.js.map +1 -0
  29. package/dist/CallSettings/types.js +7 -0
  30. package/dist/CallSettings/types.js.map +1 -0
  31. package/dist/CallingClient/CallingClient.js +631 -0
  32. package/dist/CallingClient/CallingClient.js.map +1 -0
  33. package/dist/CallingClient/CallingClient.test.js +992 -0
  34. package/dist/CallingClient/CallingClient.test.js.map +1 -0
  35. package/dist/CallingClient/callRecordFixtures.js +101 -0
  36. package/dist/CallingClient/callRecordFixtures.js.map +1 -0
  37. package/dist/CallingClient/calling/CallerId/index.js +267 -0
  38. package/dist/CallingClient/calling/CallerId/index.js.map +1 -0
  39. package/dist/CallingClient/calling/CallerId/index.test.js +274 -0
  40. package/dist/CallingClient/calling/CallerId/index.test.js.map +1 -0
  41. package/dist/CallingClient/calling/CallerId/types.js +7 -0
  42. package/dist/CallingClient/calling/CallerId/types.js.map +1 -0
  43. package/dist/CallingClient/calling/call.js +3055 -0
  44. package/dist/CallingClient/calling/call.js.map +1 -0
  45. package/dist/CallingClient/calling/call.test.js +2858 -0
  46. package/dist/CallingClient/calling/call.test.js.map +1 -0
  47. package/dist/CallingClient/calling/callManager.js +432 -0
  48. package/dist/CallingClient/calling/callManager.js.map +1 -0
  49. package/dist/CallingClient/calling/callManager.test.js +683 -0
  50. package/dist/CallingClient/calling/callManager.test.js.map +1 -0
  51. package/dist/CallingClient/calling/index.js +30 -0
  52. package/dist/CallingClient/calling/index.js.map +1 -0
  53. package/dist/CallingClient/calling/types.js +69 -0
  54. package/dist/CallingClient/calling/types.js.map +1 -0
  55. package/dist/CallingClient/callingClientFixtures.js +143 -0
  56. package/dist/CallingClient/callingClientFixtures.js.map +1 -0
  57. package/dist/CallingClient/constants.js +134 -0
  58. package/dist/CallingClient/constants.js.map +1 -0
  59. package/dist/CallingClient/line/index.js +314 -0
  60. package/dist/CallingClient/line/index.js.map +1 -0
  61. package/dist/CallingClient/line/line.test.js +270 -0
  62. package/dist/CallingClient/line/line.test.js.map +1 -0
  63. package/dist/CallingClient/line/types.js +21 -0
  64. package/dist/CallingClient/line/types.js.map +1 -0
  65. package/dist/CallingClient/registration/index.js +19 -0
  66. package/dist/CallingClient/registration/index.js.map +1 -0
  67. package/dist/CallingClient/registration/register.js +1286 -0
  68. package/dist/CallingClient/registration/register.js.map +1 -0
  69. package/dist/CallingClient/registration/register.test.js +839 -0
  70. package/dist/CallingClient/registration/register.test.js.map +1 -0
  71. package/dist/CallingClient/registration/registerFixtures.js +36 -0
  72. package/dist/CallingClient/registration/registerFixtures.js.map +1 -0
  73. package/dist/CallingClient/registration/types.js +7 -0
  74. package/dist/CallingClient/registration/types.js.map +1 -0
  75. package/dist/CallingClient/types.js +7 -0
  76. package/dist/CallingClient/types.js.map +1 -0
  77. package/dist/Contacts/ContactsClient.js +1156 -0
  78. package/dist/Contacts/ContactsClient.js.map +1 -0
  79. package/dist/Contacts/ContactsClient.test.js +725 -0
  80. package/dist/Contacts/ContactsClient.test.js.map +1 -0
  81. package/dist/Contacts/constants.js +28 -0
  82. package/dist/Contacts/constants.js.map +1 -0
  83. package/dist/Contacts/contactFixtures.js +314 -0
  84. package/dist/Contacts/contactFixtures.js.map +1 -0
  85. package/dist/Contacts/types.js +43 -0
  86. package/dist/Contacts/types.js.map +1 -0
  87. package/dist/Errors/catalog/CallError.js +92 -0
  88. package/dist/Errors/catalog/CallError.js.map +1 -0
  89. package/dist/Errors/catalog/CallingDeviceError.js +86 -0
  90. package/dist/Errors/catalog/CallingDeviceError.js.map +1 -0
  91. package/dist/Errors/catalog/ExtendedError.js +44 -0
  92. package/dist/Errors/catalog/ExtendedError.js.map +1 -0
  93. package/dist/Errors/catalog/LineError.js +88 -0
  94. package/dist/Errors/catalog/LineError.js.map +1 -0
  95. package/dist/Errors/index.js +28 -0
  96. package/dist/Errors/index.js.map +1 -0
  97. package/dist/Errors/types.js +57 -0
  98. package/dist/Errors/types.js.map +1 -0
  99. package/dist/Events/impl/index.js +81 -0
  100. package/dist/Events/impl/index.js.map +1 -0
  101. package/dist/Events/types.js +103 -0
  102. package/dist/Events/types.js.map +1 -0
  103. package/dist/Logger/index.js +220 -0
  104. package/dist/Logger/index.js.map +1 -0
  105. package/dist/Logger/index.test.js +62 -0
  106. package/dist/Logger/index.test.js.map +1 -0
  107. package/dist/Logger/types.js +34 -0
  108. package/dist/Logger/types.js.map +1 -0
  109. package/dist/Metrics/index.js +327 -0
  110. package/dist/Metrics/index.js.map +1 -0
  111. package/dist/Metrics/index.test.js +291 -0
  112. package/dist/Metrics/index.test.js.map +1 -0
  113. package/dist/Metrics/types.js +45 -0
  114. package/dist/Metrics/types.js.map +1 -0
  115. package/dist/SDKConnector/index.js +104 -0
  116. package/dist/SDKConnector/index.js.map +1 -0
  117. package/dist/SDKConnector/index.test.js +9 -0
  118. package/dist/SDKConnector/index.test.js.map +1 -0
  119. package/dist/SDKConnector/types.js +7 -0
  120. package/dist/SDKConnector/types.js.map +1 -0
  121. package/dist/SDKConnector/utils.js +39 -0
  122. package/dist/SDKConnector/utils.js.map +1 -0
  123. package/dist/SDKConnector/utils.test.js +9 -0
  124. package/dist/SDKConnector/utils.test.js.map +1 -0
  125. package/dist/Voicemail/BroadworksBackendConnector.js +641 -0
  126. package/dist/Voicemail/BroadworksBackendConnector.js.map +1 -0
  127. package/dist/Voicemail/BroadworksBackendConnector.test.js +733 -0
  128. package/dist/Voicemail/BroadworksBackendConnector.test.js.map +1 -0
  129. package/dist/Voicemail/UcmBackendConnector.js +574 -0
  130. package/dist/Voicemail/UcmBackendConnector.js.map +1 -0
  131. package/dist/Voicemail/UcmBackendConnector.test.js +629 -0
  132. package/dist/Voicemail/UcmBackendConnector.test.js.map +1 -0
  133. package/dist/Voicemail/Voicemail.js +380 -0
  134. package/dist/Voicemail/Voicemail.js.map +1 -0
  135. package/dist/Voicemail/Voicemail.test.js +226 -0
  136. package/dist/Voicemail/Voicemail.test.js.map +1 -0
  137. package/dist/Voicemail/WxCallBackendConnector.js +547 -0
  138. package/dist/Voicemail/WxCallBackendConnector.js.map +1 -0
  139. package/dist/Voicemail/WxCallBackendConnector.test.js +942 -0
  140. package/dist/Voicemail/WxCallBackendConnector.test.js.map +1 -0
  141. package/dist/Voicemail/constants.js +37 -0
  142. package/dist/Voicemail/constants.js.map +1 -0
  143. package/dist/Voicemail/types.js +7 -0
  144. package/dist/Voicemail/types.js.map +1 -0
  145. package/dist/Voicemail/voicemailFixture.js +524 -0
  146. package/dist/Voicemail/voicemailFixture.js.map +1 -0
  147. package/dist/api.js +157 -0
  148. package/dist/api.js.map +1 -0
  149. package/dist/common/Utils.js +1257 -0
  150. package/dist/common/Utils.js.map +1 -0
  151. package/dist/common/Utils.test.js +1227 -0
  152. package/dist/common/Utils.test.js.map +1 -0
  153. package/dist/common/constants.js +49 -0
  154. package/dist/common/constants.js.map +1 -0
  155. package/dist/common/index.js +19 -0
  156. package/dist/common/index.js.map +1 -0
  157. package/dist/common/testUtil.js +959 -0
  158. package/dist/common/testUtil.js.map +1 -0
  159. package/dist/common/types.js +66 -0
  160. package/dist/common/types.js.map +1 -0
  161. package/dist/index.js +309 -0
  162. package/dist/index.js.map +1 -0
  163. package/dist/module/CallHistory/CallHistory.js +83 -81
  164. package/dist/module/CallHistory/callHistoryFixtures.js +366 -307
  165. package/dist/module/CallHistory/constants.js +9 -9
  166. package/dist/module/CallHistory/types.js +1 -1
  167. package/dist/module/CallSettings/CallSettings.js +65 -65
  168. package/dist/module/CallSettings/UcmBackendConnector.js +100 -100
  169. package/dist/module/CallSettings/WxCallBackendConnector.js +287 -287
  170. package/dist/module/CallSettings/constants.js +11 -11
  171. package/dist/module/CallSettings/testFixtures.js +62 -62
  172. package/dist/module/CallSettings/types.js +1 -1
  173. package/dist/module/CallingClient/CallingClient.js +265 -248
  174. package/dist/module/CallingClient/callRecordFixtures.js +93 -93
  175. package/dist/module/CallingClient/calling/CallerId/index.js +149 -169
  176. package/dist/module/CallingClient/calling/CallerId/types.js +1 -1
  177. package/dist/module/CallingClient/calling/call.js +1701 -1658
  178. package/dist/module/CallingClient/calling/callManager.js +278 -284
  179. package/dist/module/CallingClient/calling/index.js +2 -2
  180. package/dist/module/CallingClient/calling/types.js +53 -53
  181. package/dist/module/CallingClient/callingClientFixtures.js +156 -38
  182. package/dist/module/CallingClient/constants.js +126 -122
  183. package/dist/module/CallingClient/line/index.js +155 -158
  184. package/dist/module/CallingClient/line/types.js +10 -15
  185. package/dist/module/CallingClient/registration/index.js +1 -1
  186. package/dist/module/CallingClient/registration/register.js +515 -507
  187. package/dist/module/CallingClient/registration/registerFixtures.js +28 -28
  188. package/dist/module/CallingClient/registration/types.js +1 -1
  189. package/dist/module/CallingClient/types.js +1 -1
  190. package/dist/module/Contacts/ContactsClient.js +487 -484
  191. package/dist/module/Contacts/constants.js +20 -20
  192. package/dist/module/Contacts/contactFixtures.js +284 -284
  193. package/dist/module/Contacts/types.js +10 -10
  194. package/dist/module/Errors/catalog/CallError.js +26 -26
  195. package/dist/module/Errors/catalog/CallingDeviceError.js +18 -18
  196. package/dist/module/Errors/catalog/ExtendedError.js +10 -10
  197. package/dist/module/Errors/catalog/LineError.js +24 -24
  198. package/dist/module/Errors/index.js +3 -2
  199. package/dist/module/Errors/types.js +48 -48
  200. package/dist/module/Events/impl/index.js +19 -19
  201. package/dist/module/Events/types.js +85 -83
  202. package/dist/module/Logger/index.js +114 -114
  203. package/dist/module/Logger/types.js +25 -25
  204. package/dist/module/Metrics/index.js +232 -232
  205. package/dist/module/Metrics/types.js +37 -37
  206. package/dist/module/SDKConnector/index.js +39 -39
  207. package/dist/module/SDKConnector/types.js +1 -1
  208. package/dist/module/SDKConnector/utils.js +12 -12
  209. package/dist/module/Voicemail/BroadworksBackendConnector.js +291 -291
  210. package/dist/module/Voicemail/UcmBackendConnector.js +279 -279
  211. package/dist/module/Voicemail/Voicemail.js +110 -110
  212. package/dist/module/Voicemail/WxCallBackendConnector.js +279 -279
  213. package/dist/module/Voicemail/constants.js +29 -29
  214. package/dist/module/Voicemail/types.js +1 -1
  215. package/dist/module/Voicemail/voicemailFixture.js +510 -510
  216. package/dist/module/api.js +7 -6
  217. package/dist/module/common/Utils.js +797 -802
  218. package/dist/module/common/constants.js +41 -41
  219. package/dist/module/common/index.js +1 -1
  220. package/dist/module/common/testUtil.js +939 -938
  221. package/dist/module/common/types.js +58 -57
  222. package/dist/module/index.js +17 -8
  223. package/dist/types/CallHistory/CallHistory.d.ts +18 -17
  224. package/dist/types/CallHistory/CallHistory.d.ts.map +1 -1
  225. package/dist/types/CallHistory/callHistoryFixtures.d.ts +95 -94
  226. package/dist/types/CallHistory/callHistoryFixtures.d.ts.map +1 -1
  227. package/dist/types/CallHistory/constants.d.ts +9 -9
  228. package/dist/types/CallHistory/types.d.ts +18 -18
  229. package/dist/types/CallSettings/CallSettings.d.ts +19 -19
  230. package/dist/types/CallSettings/CallSettings.d.ts.map +1 -1
  231. package/dist/types/CallSettings/UcmBackendConnector.d.ts +19 -19
  232. package/dist/types/CallSettings/UcmBackendConnector.d.ts.map +1 -1
  233. package/dist/types/CallSettings/WxCallBackendConnector.d.ts +21 -21
  234. package/dist/types/CallSettings/constants.d.ts +11 -11
  235. package/dist/types/CallSettings/testFixtures.d.ts +15 -15
  236. package/dist/types/CallSettings/types.d.ts +108 -107
  237. package/dist/types/CallSettings/types.d.ts.map +1 -1
  238. package/dist/types/CallingClient/CallingClient.d.ts +37 -35
  239. package/dist/types/CallingClient/CallingClient.d.ts.map +1 -1
  240. package/dist/types/CallingClient/callRecordFixtures.d.ts +3 -3
  241. package/dist/types/CallingClient/calling/CallerId/index.d.ts +17 -17
  242. package/dist/types/CallingClient/calling/CallerId/index.d.ts.map +1 -1
  243. package/dist/types/CallingClient/calling/CallerId/types.d.ts +41 -41
  244. package/dist/types/CallingClient/calling/CallerId/types.d.ts.map +1 -1
  245. package/dist/types/CallingClient/calling/call.d.ts +98 -96
  246. package/dist/types/CallingClient/calling/call.d.ts.map +1 -1
  247. package/dist/types/CallingClient/calling/callManager.d.ts +24 -25
  248. package/dist/types/CallingClient/calling/callManager.d.ts.map +1 -1
  249. package/dist/types/CallingClient/calling/index.d.ts +2 -2
  250. package/dist/types/CallingClient/calling/types.d.ts +206 -206
  251. package/dist/types/CallingClient/calling/types.d.ts.map +1 -1
  252. package/dist/types/CallingClient/callingClientFixtures.d.ts +86 -18
  253. package/dist/types/CallingClient/callingClientFixtures.d.ts.map +1 -1
  254. package/dist/types/CallingClient/constants.d.ts +126 -122
  255. package/dist/types/CallingClient/constants.d.ts.map +1 -1
  256. package/dist/types/CallingClient/line/index.d.ts +43 -43
  257. package/dist/types/CallingClient/line/index.d.ts.map +1 -1
  258. package/dist/types/CallingClient/line/types.d.ts +43 -55
  259. package/dist/types/CallingClient/line/types.d.ts.map +1 -1
  260. package/dist/types/CallingClient/registration/index.d.ts +1 -1
  261. package/dist/types/CallingClient/registration/register.d.ts +64 -64
  262. package/dist/types/CallingClient/registration/register.d.ts.map +1 -1
  263. package/dist/types/CallingClient/registration/registerFixtures.d.ts +28 -28
  264. package/dist/types/CallingClient/registration/types.d.ts +20 -20
  265. package/dist/types/CallingClient/registration/types.d.ts.map +1 -1
  266. package/dist/types/CallingClient/types.d.ts +31 -29
  267. package/dist/types/CallingClient/types.d.ts.map +1 -1
  268. package/dist/types/Contacts/ContactsClient.d.ts +27 -26
  269. package/dist/types/Contacts/ContactsClient.d.ts.map +1 -1
  270. package/dist/types/Contacts/constants.d.ts +19 -19
  271. package/dist/types/Contacts/contactFixtures.d.ts +280 -280
  272. package/dist/types/Contacts/types.d.ts +74 -72
  273. package/dist/types/Contacts/types.d.ts.map +1 -1
  274. package/dist/types/Errors/catalog/CallError.d.ts +11 -11
  275. package/dist/types/Errors/catalog/CallingDeviceError.d.ts +10 -10
  276. package/dist/types/Errors/catalog/CallingDeviceError.d.ts.map +1 -1
  277. package/dist/types/Errors/catalog/ExtendedError.d.ts +6 -6
  278. package/dist/types/Errors/catalog/LineError.d.ts +10 -10
  279. package/dist/types/Errors/catalog/LineError.d.ts.map +1 -1
  280. package/dist/types/Errors/index.d.ts +3 -2
  281. package/dist/types/Errors/index.d.ts.map +1 -1
  282. package/dist/types/Errors/types.d.ts +59 -60
  283. package/dist/types/Errors/types.d.ts.map +1 -1
  284. package/dist/types/Events/impl/index.d.ts +8 -8
  285. package/dist/types/Events/types.d.ts +311 -288
  286. package/dist/types/Events/types.d.ts.map +1 -1
  287. package/dist/types/Logger/index.d.ts +12 -12
  288. package/dist/types/Logger/types.d.ts +25 -25
  289. package/dist/types/Metrics/index.d.ts +5 -5
  290. package/dist/types/Metrics/types.d.ts +42 -42
  291. package/dist/types/SDKConnector/index.d.ts +12 -12
  292. package/dist/types/SDKConnector/types.d.ts +129 -128
  293. package/dist/types/SDKConnector/types.d.ts.map +1 -1
  294. package/dist/types/SDKConnector/utils.d.ts +5 -5
  295. package/dist/types/Voicemail/BroadworksBackendConnector.d.ts +27 -27
  296. package/dist/types/Voicemail/UcmBackendConnector.d.ts +34 -34
  297. package/dist/types/Voicemail/Voicemail.d.ts +27 -27
  298. package/dist/types/Voicemail/Voicemail.d.ts.map +1 -1
  299. package/dist/types/Voicemail/WxCallBackendConnector.d.ts +23 -23
  300. package/dist/types/Voicemail/constants.d.ts +29 -29
  301. package/dist/types/Voicemail/types.d.ts +133 -133
  302. package/dist/types/Voicemail/types.d.ts.map +1 -1
  303. package/dist/types/Voicemail/voicemailFixture.d.ts +417 -417
  304. package/dist/types/api.d.ts +16 -16
  305. package/dist/types/api.d.ts.map +1 -1
  306. package/dist/types/common/Utils.d.ts +34 -34
  307. package/dist/types/common/Utils.d.ts.map +1 -1
  308. package/dist/types/common/constants.d.ts +41 -41
  309. package/dist/types/common/index.d.ts +1 -1
  310. package/dist/types/common/testUtil.d.ts +3612 -3611
  311. package/dist/types/common/testUtil.d.ts.map +1 -1
  312. package/dist/types/common/types.d.ts +192 -191
  313. package/dist/types/common/types.d.ts.map +1 -1
  314. package/dist/types/index.d.ts +21 -8
  315. package/dist/types/index.d.ts.map +1 -1
  316. package/package.json +5 -3
@@ -1,1658 +1,1701 @@
1
- import { Event, LocalStreamEventNames, RoapMediaConnection, } from '@webex/internal-media-core';
2
- import { createMachine, interpret } from 'xstate';
3
- import { v4 as uuid } from 'uuid';
4
- import { ERROR_LAYER, ERROR_TYPE } from '../../Errors/types';
5
- import { handleCallErrors, parseMediaQualityStatistics } from '../../common/Utils';
6
- import { ALLOWED_SERVICES, CallDirection, HTTP_METHODS, } from '../../common/types';
7
- import { createCallError } from '../../Errors/catalog/CallError';
8
- import { CALL_ENDPOINT_RESOURCE, CALL_FILE, CALL_HOLD_SERVICE, CALL_STATUS_RESOURCE, CALL_TRANSFER_SERVICE, CALLING_USER_AGENT, CALLS_ENDPOINT_RESOURCE, CISCO_DEVICE_URL, DEFAULT_LOCAL_CALL_ID, DEFAULT_SESSION_TIMER, DEVICES_ENDPOINT_RESOURCE, HOLD_ENDPOINT, INITIAL_SEQ_NUMBER, MEDIA_ENDPOINT_RESOURCE, RESUME_ENDPOINT, SPARK_USER_AGENT, SUPPLEMENTARY_SERVICES_TIMEOUT, TRANSFER_ENDPOINT, } from '../constants';
9
- import SDKConnector from '../../SDKConnector';
10
- import { Eventing } from '../../Events/impl';
11
- import { CALL_EVENT_KEYS, MEDIA_CONNECTION_EVENT_KEYS, MOBIUS_MIDCALL_STATE, SUPPLEMENTARY_SERVICES, } from '../../Events/types';
12
- import { DisconnectCause, DisconnectCode, MidCallEventType, MobiusCallState, RoapScenario, TransferType, } from './types';
13
- import log from '../../Logger';
14
- import { createCallerId } from './CallerId';
15
- import { METRIC_TYPE, METRIC_EVENT, TRANSFER_ACTION } from '../../Metrics/types';
16
- import { getMetricManager } from '../../Metrics';
17
- import { SERVICES_ENDPOINT } from '../../common/constants';
18
- export class Call extends Eventing {
19
- sdkConnector;
20
- webex;
21
- destination;
22
- direction;
23
- callId;
24
- correlationId;
25
- deviceId;
26
- lineId;
27
- disconnectReason;
28
- callStateMachine;
29
- mediaStateMachine;
30
- seq;
31
- mediaConnection;
32
- earlyMedia;
33
- connected;
34
- callerInfo;
35
- localRoapMessage;
36
- mobiusUrl;
37
- remoteRoapMessage;
38
- deleteCb;
39
- callerId;
40
- sessionTimer;
41
- supplementaryServicesTimer;
42
- muted;
43
- held;
44
- metricManager;
45
- broadworksCorrelationInfo;
46
- serviceIndicator;
47
- mediaNegotiationCompleted;
48
- receivedRoapOKSeq;
49
- isMuted() {
50
- return this.muted;
51
- }
52
- isConnected() {
53
- return this.connected;
54
- }
55
- isHeld() {
56
- return this.held;
57
- }
58
- constructor(activeUrl, webex, destination, direction, deviceId, lineId, deleteCb, indicator) {
59
- super();
60
- this.destination = destination;
61
- this.direction = direction;
62
- this.sdkConnector = SDKConnector;
63
- this.deviceId = deviceId;
64
- this.serviceIndicator = indicator;
65
- this.lineId = lineId;
66
- if (!this.sdkConnector.getWebex()) {
67
- SDKConnector.setWebex(webex);
68
- }
69
- this.webex = this.sdkConnector.getWebex();
70
- this.metricManager = getMetricManager(this.webex, this.serviceIndicator);
71
- this.callId = `${DEFAULT_LOCAL_CALL_ID}_${uuid()}`;
72
- this.correlationId = uuid();
73
- this.deleteCb = deleteCb;
74
- this.connected = false;
75
- this.held = false;
76
- this.earlyMedia = false;
77
- this.callerInfo = {};
78
- this.localRoapMessage = {};
79
- this.mobiusUrl = activeUrl;
80
- this.receivedRoapOKSeq = 0;
81
- this.mediaNegotiationCompleted = false;
82
- log.info(`Mobius Url:- ${this.mobiusUrl}`, {
83
- file: CALL_FILE,
84
- method: 'constructor',
85
- });
86
- this.seq = INITIAL_SEQ_NUMBER;
87
- this.callerId = createCallerId(webex, (callerInfo) => {
88
- this.callerInfo = callerInfo;
89
- const emitObj = {
90
- correlationId: this.correlationId,
91
- callerId: this.callerInfo,
92
- };
93
- this.emit(CALL_EVENT_KEYS.CALLER_ID, emitObj);
94
- });
95
- this.remoteRoapMessage = null;
96
- this.disconnectReason = { code: DisconnectCode.NORMAL, cause: DisconnectCause.NORMAL };
97
- const callMachine = createMachine({
98
- schema: {
99
- context: {},
100
- events: {},
101
- },
102
- id: 'call-state',
103
- initial: 'S_IDLE',
104
- context: {},
105
- states: {
106
- S_IDLE: {
107
- on: {
108
- E_RECV_CALL_SETUP: {
109
- target: 'S_RECV_CALL_SETUP',
110
- actions: ['incomingCallSetup'],
111
- },
112
- E_SEND_CALL_SETUP: {
113
- target: 'S_SEND_CALL_SETUP',
114
- actions: ['outgoingCallSetup'],
115
- },
116
- E_RECV_CALL_DISCONNECT: {
117
- target: 'S_RECV_CALL_DISCONNECT',
118
- actions: ['incomingCallDisconnect'],
119
- },
120
- E_SEND_CALL_DISCONNECT: {
121
- target: 'S_SEND_CALL_DISCONNECT',
122
- actions: ['outgoingCallDisconnect'],
123
- },
124
- E_UNKNOWN: {
125
- target: 'S_UNKNOWN',
126
- actions: ['unknownState'],
127
- },
128
- },
129
- },
130
- S_RECV_CALL_SETUP: {
131
- on: {
132
- E_SEND_CALL_ALERTING: {
133
- target: 'S_SEND_CALL_PROGRESS',
134
- actions: ['outgoingCallAlerting'],
135
- },
136
- E_RECV_CALL_DISCONNECT: {
137
- target: 'S_RECV_CALL_DISCONNECT',
138
- actions: ['incomingCallDisconnect'],
139
- },
140
- E_SEND_CALL_DISCONNECT: {
141
- target: 'S_SEND_CALL_DISCONNECT',
142
- actions: ['outgoingCallDisconnect'],
143
- },
144
- E_UNKNOWN: {
145
- target: 'S_UNKNOWN',
146
- actions: ['unknownState'],
147
- },
148
- },
149
- },
150
- S_SEND_CALL_SETUP: {
151
- on: {
152
- E_RECV_CALL_PROGRESS: {
153
- target: 'S_RECV_CALL_PROGRESS',
154
- actions: ['incomingCallProgress'],
155
- },
156
- E_RECV_CALL_DISCONNECT: {
157
- target: 'S_RECV_CALL_DISCONNECT',
158
- actions: ['incomingCallDisconnect'],
159
- },
160
- E_SEND_CALL_DISCONNECT: {
161
- target: 'S_SEND_CALL_DISCONNECT',
162
- actions: ['outgoingCallDisconnect'],
163
- },
164
- E_UNKNOWN: {
165
- target: 'S_UNKNOWN',
166
- actions: ['unknownState'],
167
- },
168
- },
169
- },
170
- S_RECV_CALL_PROGRESS: {
171
- on: {
172
- E_RECV_CALL_CONNECT: {
173
- target: 'S_RECV_CALL_CONNECT',
174
- actions: ['incomingCallConnect'],
175
- },
176
- E_RECV_CALL_DISCONNECT: {
177
- target: 'S_RECV_CALL_DISCONNECT',
178
- actions: ['incomingCallDisconnect'],
179
- },
180
- E_SEND_CALL_DISCONNECT: {
181
- target: 'S_SEND_CALL_DISCONNECT',
182
- actions: ['outgoingCallDisconnect'],
183
- },
184
- E_RECV_CALL_PROGRESS: {
185
- target: 'S_RECV_CALL_PROGRESS',
186
- actions: ['incomingCallProgress'],
187
- },
188
- E_UNKNOWN: {
189
- target: 'S_UNKNOWN',
190
- actions: ['unknownState'],
191
- },
192
- },
193
- },
194
- S_SEND_CALL_PROGRESS: {
195
- on: {
196
- E_SEND_CALL_CONNECT: {
197
- target: 'S_SEND_CALL_CONNECT',
198
- actions: ['outgoingCallConnect'],
199
- },
200
- E_RECV_CALL_DISCONNECT: {
201
- target: 'S_RECV_CALL_DISCONNECT',
202
- actions: ['incomingCallDisconnect'],
203
- },
204
- E_SEND_CALL_DISCONNECT: {
205
- target: 'S_SEND_CALL_DISCONNECT',
206
- actions: ['outgoingCallDisconnect'],
207
- },
208
- E_UNKNOWN: {
209
- target: 'S_UNKNOWN',
210
- actions: ['unknownState'],
211
- },
212
- },
213
- },
214
- S_RECV_CALL_CONNECT: {
215
- on: {
216
- E_CALL_ESTABLISHED: {
217
- target: 'S_CALL_ESTABLISHED',
218
- actions: ['callEstablished'],
219
- },
220
- E_RECV_CALL_DISCONNECT: {
221
- target: 'S_RECV_CALL_DISCONNECT',
222
- actions: ['incomingCallDisconnect'],
223
- },
224
- E_SEND_CALL_DISCONNECT: {
225
- target: 'S_SEND_CALL_DISCONNECT',
226
- actions: ['outgoingCallDisconnect'],
227
- },
228
- E_UNKNOWN: {
229
- target: 'S_UNKNOWN',
230
- actions: ['unknownState'],
231
- },
232
- },
233
- },
234
- S_SEND_CALL_CONNECT: {
235
- on: {
236
- E_CALL_ESTABLISHED: {
237
- target: 'S_CALL_ESTABLISHED',
238
- actions: ['callEstablished'],
239
- },
240
- E_RECV_CALL_DISCONNECT: {
241
- target: 'S_RECV_CALL_DISCONNECT',
242
- actions: ['incomingCallDisconnect'],
243
- },
244
- E_SEND_CALL_DISCONNECT: {
245
- target: 'S_SEND_CALL_DISCONNECT',
246
- actions: ['outgoingCallDisconnect'],
247
- },
248
- E_UNKNOWN: {
249
- target: 'S_UNKNOWN',
250
- actions: ['unknownState'],
251
- },
252
- },
253
- },
254
- S_CALL_HOLD: {
255
- on: {
256
- E_RECV_CALL_DISCONNECT: {
257
- target: 'S_RECV_CALL_DISCONNECT',
258
- actions: ['incomingCallDisconnect'],
259
- },
260
- E_SEND_CALL_DISCONNECT: {
261
- target: 'S_SEND_CALL_DISCONNECT',
262
- actions: ['outgoingCallDisconnect'],
263
- },
264
- E_CALL_ESTABLISHED: {
265
- target: 'S_CALL_ESTABLISHED',
266
- actions: ['callEstablished'],
267
- },
268
- E_UNKNOWN: {
269
- target: 'S_UNKNOWN',
270
- actions: ['unknownState'],
271
- },
272
- },
273
- },
274
- S_CALL_RESUME: {
275
- on: {
276
- E_RECV_CALL_DISCONNECT: {
277
- target: 'S_RECV_CALL_DISCONNECT',
278
- actions: ['incomingCallDisconnect'],
279
- },
280
- E_SEND_CALL_DISCONNECT: {
281
- target: 'S_SEND_CALL_DISCONNECT',
282
- actions: ['outgoingCallDisconnect'],
283
- },
284
- E_CALL_ESTABLISHED: {
285
- target: 'S_CALL_ESTABLISHED',
286
- actions: ['callEstablished'],
287
- },
288
- E_UNKNOWN: {
289
- target: 'S_UNKNOWN',
290
- actions: ['unknownState'],
291
- },
292
- },
293
- },
294
- S_CALL_ESTABLISHED: {
295
- on: {
296
- E_CALL_HOLD: {
297
- target: 'S_CALL_HOLD',
298
- actions: ['initiateCallHold'],
299
- },
300
- E_CALL_RESUME: {
301
- target: 'S_CALL_RESUME',
302
- actions: ['initiateCallResume'],
303
- },
304
- E_RECV_CALL_DISCONNECT: {
305
- target: 'S_RECV_CALL_DISCONNECT',
306
- actions: ['incomingCallDisconnect'],
307
- },
308
- E_SEND_CALL_DISCONNECT: {
309
- target: 'S_SEND_CALL_DISCONNECT',
310
- actions: ['outgoingCallDisconnect'],
311
- },
312
- E_CALL_ESTABLISHED: {
313
- target: 'S_CALL_ESTABLISHED',
314
- actions: ['callEstablished'],
315
- },
316
- E_UNKNOWN: {
317
- target: 'S_UNKNOWN',
318
- actions: ['unknownState'],
319
- },
320
- },
321
- },
322
- S_RECV_CALL_DISCONNECT: {
323
- on: {
324
- E_CALL_CLEARED: 'S_CALL_CLEARED',
325
- },
326
- },
327
- S_SEND_CALL_DISCONNECT: {
328
- on: {
329
- E_CALL_CLEARED: 'S_CALL_CLEARED',
330
- },
331
- },
332
- S_UNKNOWN: {
333
- on: {
334
- E_CALL_CLEARED: 'S_CALL_CLEARED',
335
- },
336
- },
337
- S_ERROR: {
338
- on: {
339
- E_CALL_CLEARED: 'S_CALL_CLEARED',
340
- },
341
- },
342
- S_CALL_CLEARED: {
343
- type: 'final',
344
- },
345
- },
346
- }, {
347
- actions: {
348
- incomingCallSetup: (context, event) => this.handleIncomingCallSetup(event),
349
- outgoingCallSetup: (context, event) => this.handleOutgoingCallSetup(event),
350
- incomingCallProgress: (context, event) => this.handleIncomingCallProgress(event),
351
- outgoingCallAlerting: (context, event) => this.handleOutgoingCallAlerting(event),
352
- incomingCallConnect: (context, event) => this.handleIncomingCallConnect(event),
353
- outgoingCallConnect: (context, event) => this.handleOutgoingCallConnect(event),
354
- callEstablished: (context, event) => this.handleCallEstablished(event),
355
- initiateCallHold: (context, event) => this.handleCallHold(event),
356
- initiateCallResume: (context, event) => this.handleCallResume(event),
357
- incomingCallDisconnect: (context, event) => this.handleIncomingCallDisconnect(event),
358
- outgoingCallDisconnect: (context, event) => this.handleOutgoingCallDisconnect(event),
359
- unknownState: (context, event) => this.handleUnknownState(event),
360
- },
361
- });
362
- const mediaMachine = createMachine({
363
- schema: {
364
- context: {},
365
- events: {},
366
- },
367
- id: 'roap-state',
368
- initial: 'S_ROAP_IDLE',
369
- context: {},
370
- states: {
371
- S_ROAP_IDLE: {
372
- on: {
373
- E_RECV_ROAP_OFFER_REQUEST: {
374
- target: 'S_RECV_ROAP_OFFER_REQUEST',
375
- actions: ['incomingRoapOfferRequest'],
376
- },
377
- E_RECV_ROAP_OFFER: {
378
- target: 'S_RECV_ROAP_OFFER',
379
- actions: ['incomingRoapOffer'],
380
- },
381
- E_SEND_ROAP_OFFER: {
382
- target: 'S_SEND_ROAP_OFFER',
383
- actions: ['outgoingRoapOffer'],
384
- },
385
- },
386
- },
387
- S_RECV_ROAP_OFFER_REQUEST: {
388
- on: {
389
- E_SEND_ROAP_OFFER: {
390
- target: 'S_SEND_ROAP_OFFER',
391
- actions: ['outgoingRoapOffer'],
392
- },
393
- E_ROAP_OK: {
394
- target: 'S_ROAP_OK',
395
- actions: ['roapEstablished'],
396
- },
397
- E_ROAP_ERROR: {
398
- target: 'S_ROAP_ERROR',
399
- actions: ['roapError'],
400
- },
401
- },
402
- },
403
- S_RECV_ROAP_OFFER: {
404
- on: {
405
- E_SEND_ROAP_ANSWER: {
406
- target: 'S_SEND_ROAP_ANSWER',
407
- actions: ['outgoingRoapAnswer'],
408
- },
409
- E_ROAP_OK: {
410
- target: 'S_ROAP_OK',
411
- actions: ['roapEstablished'],
412
- },
413
- E_ROAP_ERROR: {
414
- target: 'S_ROAP_ERROR',
415
- actions: ['roapError'],
416
- },
417
- },
418
- },
419
- S_SEND_ROAP_OFFER: {
420
- on: {
421
- E_RECV_ROAP_ANSWER: {
422
- target: 'S_RECV_ROAP_ANSWER',
423
- actions: ['incomingRoapAnswer'],
424
- },
425
- E_SEND_ROAP_ANSWER: {
426
- target: 'S_SEND_ROAP_ANSWER',
427
- actions: ['outgoingRoapAnswer'],
428
- },
429
- E_SEND_ROAP_OFFER: {
430
- target: 'S_SEND_ROAP_OFFER',
431
- actions: ['outgoingRoapOffer'],
432
- },
433
- E_ROAP_ERROR: {
434
- target: 'S_ROAP_ERROR',
435
- actions: ['roapError'],
436
- },
437
- },
438
- },
439
- S_RECV_ROAP_ANSWER: {
440
- on: {
441
- E_ROAP_OK: {
442
- target: 'S_ROAP_OK',
443
- actions: ['roapEstablished'],
444
- },
445
- E_ROAP_ERROR: {
446
- target: 'S_ROAP_ERROR',
447
- actions: ['roapError'],
448
- },
449
- },
450
- },
451
- S_SEND_ROAP_ANSWER: {
452
- on: {
453
- E_RECV_ROAP_OFFER_REQUEST: {
454
- target: 'S_RECV_ROAP_OFFER_REQUEST',
455
- actions: ['incomingRoapOfferRequest'],
456
- },
457
- E_RECV_ROAP_OFFER: {
458
- target: 'S_RECV_ROAP_OFFER',
459
- actions: ['incomingRoapOffer'],
460
- },
461
- E_ROAP_OK: {
462
- target: 'S_ROAP_OK',
463
- actions: ['roapEstablished'],
464
- },
465
- E_SEND_ROAP_ANSWER: {
466
- target: 'S_SEND_ROAP_ANSWER',
467
- actions: ['outgoingRoapAnswer'],
468
- },
469
- E_ROAP_ERROR: {
470
- target: 'S_ROAP_ERROR',
471
- actions: ['roapError'],
472
- },
473
- },
474
- },
475
- S_ROAP_OK: {
476
- on: {
477
- E_RECV_ROAP_OFFER_REQUEST: {
478
- target: 'S_RECV_ROAP_OFFER_REQUEST',
479
- actions: ['incomingRoapOfferRequest'],
480
- },
481
- E_RECV_ROAP_OFFER: {
482
- target: 'S_RECV_ROAP_OFFER',
483
- actions: ['incomingRoapOffer'],
484
- },
485
- E_ROAP_OK: {
486
- target: 'S_ROAP_OK',
487
- actions: ['roapEstablished'],
488
- },
489
- E_SEND_ROAP_OFFER: {
490
- target: 'S_SEND_ROAP_OFFER',
491
- actions: ['outgoingRoapOffer'],
492
- },
493
- E_ROAP_ERROR: {
494
- target: 'S_ROAP_ERROR',
495
- actions: ['roapError'],
496
- },
497
- E_ROAP_TEARDOWN: {
498
- target: 'S_ROAP_TEARDOWN',
499
- },
500
- },
501
- },
502
- S_ROAP_ERROR: {
503
- on: {
504
- E_ROAP_TEARDOWN: {
505
- target: 'S_ROAP_TEARDOWN',
506
- },
507
- E_RECV_ROAP_OFFER_REQUEST: {
508
- target: 'S_RECV_ROAP_OFFER_REQUEST',
509
- actions: ['incomingRoapOfferRequest'],
510
- },
511
- E_RECV_ROAP_OFFER: {
512
- target: 'S_RECV_ROAP_OFFER',
513
- actions: ['incomingRoapOffer'],
514
- },
515
- E_RECV_ROAP_ANSWER: {
516
- target: 'S_RECV_ROAP_ANSWER',
517
- actions: ['incomingRoapAnswer'],
518
- },
519
- E_ROAP_OK: {
520
- target: 'S_ROAP_OK',
521
- actions: ['roapEstablished'],
522
- },
523
- },
524
- },
525
- S_ROAP_TEARDOWN: {
526
- type: 'final',
527
- },
528
- },
529
- }, {
530
- actions: {
531
- incomingRoapOffer: (context, event) => this.handleIncomingRoapOffer(context, event),
532
- incomingRoapAnswer: (context, event) => this.handleIncomingRoapAnswer(context, event),
533
- incomingRoapOfferRequest: (context, event) => this.handleIncomingRoapOfferRequest(context, event),
534
- outgoingRoapOffer: (context, event) => this.handleOutgoingRoapOffer(context, event),
535
- outgoingRoapAnswer: (context, event) => this.handleOutgoingRoapAnswer(context, event),
536
- roapEstablished: (context, event) => this.handleRoapEstablished(context, event),
537
- roapError: (context, event) => this.handleRoapError(context, event),
538
- },
539
- });
540
- this.callStateMachine = interpret(callMachine)
541
- .onTransition((state, event) => {
542
- log.log(`Call StateMachine:- state=${state.value}, event=${JSON.stringify(event.type)}`, {});
543
- if (state.value !== 'S_UNKNOWN') {
544
- this.metricManager.submitCallMetric(METRIC_EVENT.CALL, state.value.toString(), METRIC_TYPE.BEHAVIORAL, this.callId, this.correlationId, undefined);
545
- }
546
- })
547
- .start();
548
- this.mediaStateMachine = interpret(mediaMachine)
549
- .onTransition((state, event) => {
550
- log.log(`Media StateMachine:- state=${state.value}, event=${JSON.stringify(event.type)}`, {});
551
- if (state.value !== 'S_ROAP_ERROR') {
552
- this.metricManager.submitMediaMetric(METRIC_EVENT.MEDIA, state.value.toString(), METRIC_TYPE.BEHAVIORAL, this.callId, this.correlationId, this.localRoapMessage.sdp, this.remoteRoapMessage?.sdp, undefined);
553
- }
554
- })
555
- .start();
556
- this.muted = false;
557
- }
558
- handleIncomingCallSetup(event) {
559
- log.info(`handleIncomingCallSetup: ${this.getCorrelationId()} `, {
560
- file: CALL_FILE,
561
- method: this.handleIncomingCallSetup.name,
562
- });
563
- this.sendCallStateMachineEvt({ type: 'E_SEND_CALL_ALERTING' });
564
- }
565
- async handleOutgoingCallSetup(event) {
566
- log.info(`handleOutgoingCallSetup: ${this.getCorrelationId()} `, {
567
- file: CALL_FILE,
568
- method: this.handleOutgoingCallSetup.name,
569
- });
570
- const message = event.data;
571
- try {
572
- const response = await this.post(message);
573
- log.log(`handleOutgoingCallSetup: Response code: ${response.statusCode}`, {
574
- file: CALL_FILE,
575
- method: this.handleOutgoingCallSetup.name,
576
- });
577
- this.setCallId(response.body.callId);
578
- }
579
- catch (e) {
580
- log.warn('Call setup failed with Mobius', {
581
- file: CALL_FILE,
582
- method: this.handleOutgoingCallSetup.name,
583
- });
584
- const errData = e;
585
- handleCallErrors((error) => {
586
- this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
587
- this.submitCallErrorMetric(error);
588
- this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: errData });
589
- }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallSetup.name, CALL_FILE);
590
- }
591
- }
592
- async handleCallHold(event) {
593
- log.info(`handleCallHold: ${this.getCorrelationId()} `, {
594
- file: CALL_FILE,
595
- method: this.handleCallHold.name,
596
- });
597
- try {
598
- const response = await this.postSSRequest(undefined, SUPPLEMENTARY_SERVICES.HOLD);
599
- log.log(`Response code: ${response.statusCode}`, {
600
- file: CALL_FILE,
601
- method: this.handleCallHold.name,
602
- });
603
- if (this.isHeld() === false) {
604
- this.supplementaryServicesTimer = setTimeout(async () => {
605
- const errorContext = { file: CALL_FILE, method: this.handleCallHold.name };
606
- log.warn('Hold response timed out', {
607
- file: CALL_FILE,
608
- method: this.handleCallHold.name,
609
- });
610
- const callError = createCallError('An error occurred while placing the call on hold. Wait a moment and try again.', errorContext, ERROR_TYPE.TIMEOUT, this.getCorrelationId(), ERROR_LAYER.CALL_CONTROL);
611
- this.emit(CALL_EVENT_KEYS.HOLD_ERROR, callError);
612
- this.submitCallErrorMetric(callError);
613
- }, SUPPLEMENTARY_SERVICES_TIMEOUT);
614
- }
615
- }
616
- catch (e) {
617
- log.warn('Call Hold failed with Mobius', {
618
- file: CALL_FILE,
619
- method: this.handleCallHold.name,
620
- });
621
- const errData = e;
622
- handleCallErrors((error) => {
623
- this.emit(CALL_EVENT_KEYS.HOLD_ERROR, error);
624
- this.submitCallErrorMetric(error);
625
- this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
626
- }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallSetup.name, CALL_FILE);
627
- }
628
- }
629
- async handleCallResume(event) {
630
- log.info(`handleCallResume: ${this.getCorrelationId()} `, {
631
- file: CALL_FILE,
632
- method: this.handleCallResume.name,
633
- });
634
- try {
635
- const response = await this.postSSRequest(undefined, SUPPLEMENTARY_SERVICES.RESUME);
636
- log.log(`Response code: ${response.statusCode}`, {
637
- file: CALL_FILE,
638
- method: this.handleCallResume.name,
639
- });
640
- if (this.isHeld() === true) {
641
- this.supplementaryServicesTimer = setTimeout(async () => {
642
- const errorContext = { file: CALL_FILE, method: this.handleCallResume.name };
643
- log.warn('Resume response timed out', {
644
- file: CALL_FILE,
645
- method: this.handleCallResume.name,
646
- });
647
- const callError = createCallError('An error occurred while resuming the call. Wait a moment and try again.', errorContext, ERROR_TYPE.TIMEOUT, this.getCorrelationId(), ERROR_LAYER.CALL_CONTROL);
648
- this.emit(CALL_EVENT_KEYS.RESUME_ERROR, callError);
649
- this.submitCallErrorMetric(callError);
650
- }, SUPPLEMENTARY_SERVICES_TIMEOUT);
651
- }
652
- }
653
- catch (e) {
654
- log.warn('Call Resume failed with Mobius', {
655
- file: CALL_FILE,
656
- method: this.handleCallResume.name,
657
- });
658
- const errData = e;
659
- handleCallErrors((error) => {
660
- this.emit(CALL_EVENT_KEYS.RESUME_ERROR, error);
661
- this.submitCallErrorMetric(error);
662
- this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
663
- }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallSetup.name, CALL_FILE);
664
- }
665
- }
666
- handleIncomingCallProgress(event) {
667
- log.info(`handleIncomingCallProgress: ${this.getCorrelationId()} `, {
668
- file: CALL_FILE,
669
- method: this.handleIncomingCallProgress.name,
670
- });
671
- const data = event.data;
672
- if (data?.callProgressData?.inbandMedia) {
673
- log.log('Inband media present. Setting Early Media flag', {
674
- file: CALL_FILE,
675
- method: this.handleIncomingCallProgress.name,
676
- });
677
- this.earlyMedia = true;
678
- }
679
- else {
680
- log.log('Inband media not present.', {
681
- file: CALL_FILE,
682
- method: this.handleIncomingCallProgress.name,
683
- });
684
- }
685
- if (data?.callerId) {
686
- log.info('Processing Caller-Id data', {
687
- file: CALL_FILE,
688
- method: this.handleIncomingCallProgress.name,
689
- });
690
- this.startCallerIdResolution(data.callerId);
691
- }
692
- this.emit(CALL_EVENT_KEYS.PROGRESS, this.correlationId);
693
- }
694
- handleIncomingRoapOfferRequest(context, event) {
695
- log.info(`handleIncomingRoapOfferRequest: ${this.getCorrelationId()} `, {
696
- file: CALL_FILE,
697
- method: this.handleIncomingRoapOfferRequest.name,
698
- });
699
- const message = event.data;
700
- if (!this.mediaConnection) {
701
- log.info('Media connection is not up, buffer the remote Offer Request for later handling', {
702
- file: CALL_FILE,
703
- method: this.handleIncomingRoapOfferRequest.name,
704
- });
705
- this.seq = message.seq;
706
- log.info(`Setting Sequence No: ${this.seq}`, {
707
- file: CALL_FILE,
708
- method: this.handleIncomingRoapOfferRequest.name,
709
- });
710
- this.remoteRoapMessage = message;
711
- }
712
- else if (this.receivedRoapOKSeq === message.seq - 2) {
713
- log.info('Waiting for Roap OK, buffer the remote Offer Request for later handling', {
714
- file: CALL_FILE,
715
- method: this.handleIncomingRoapOfferRequest.name,
716
- });
717
- this.remoteRoapMessage = message;
718
- }
719
- else {
720
- message.seq = this.seq + 1;
721
- this.seq = message.seq;
722
- this.mediaConnection.roapMessageReceived(message);
723
- }
724
- }
725
- async handleOutgoingCallAlerting(event) {
726
- log.info(`handleOutgoingCallAlerting: ${this.getCorrelationId()} `, {
727
- file: CALL_FILE,
728
- method: this.handleOutgoingCallAlerting.name,
729
- });
730
- try {
731
- const res = await this.patch(MobiusCallState.ALERTING);
732
- log.log(`PATCH response: ${res.statusCode}`, {
733
- file: CALL_FILE,
734
- method: this.handleOutgoingCallAlerting.name,
735
- });
736
- }
737
- catch (err) {
738
- log.warn('Call Progress failed with Mobius', {
739
- file: CALL_FILE,
740
- method: this.handleOutgoingCallAlerting.name,
741
- });
742
- const errData = err;
743
- handleCallErrors((error) => {
744
- this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
745
- this.submitCallErrorMetric(error);
746
- this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: errData });
747
- }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallAlerting.name, CALL_FILE);
748
- }
749
- }
750
- handleIncomingCallConnect(event) {
751
- log.info(`handleIncomingCallConnect: ${this.getCorrelationId()} `, {
752
- file: CALL_FILE,
753
- method: this.handleIncomingCallConnect.name,
754
- });
755
- this.emit(CALL_EVENT_KEYS.CONNECT, this.correlationId);
756
- if (this.earlyMedia || this.mediaNegotiationCompleted) {
757
- this.mediaNegotiationCompleted = false;
758
- this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED' });
759
- }
760
- }
761
- async handleOutgoingCallConnect(event) {
762
- log.info(`handleOutgoingCallConnect: ${this.getCorrelationId()} `, {
763
- file: CALL_FILE,
764
- method: this.handleOutgoingCallConnect.name,
765
- });
766
- if (!this.remoteRoapMessage) {
767
- log.warn('Offer not yet received from remote end... Exiting', {
768
- file: CALL_FILE,
769
- method: this.handleOutgoingCallConnect.name,
770
- });
771
- return;
772
- }
773
- try {
774
- const res = await this.patch(MobiusCallState.CONNECTED);
775
- log.log(`PATCH response: ${res.statusCode}`, {
776
- file: CALL_FILE,
777
- method: this.handleOutgoingCallConnect.name,
778
- });
779
- this.mediaConnection.roapMessageReceived(this.remoteRoapMessage);
780
- }
781
- catch (err) {
782
- log.warn('Call Connect failed with Mobius', {
783
- file: CALL_FILE,
784
- method: this.handleOutgoingCallConnect.name,
785
- });
786
- const errData = err;
787
- handleCallErrors((error) => {
788
- this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
789
- this.submitCallErrorMetric(error);
790
- this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: errData });
791
- }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallConnect.name, CALL_FILE);
792
- }
793
- }
794
- async handleIncomingCallDisconnect(event) {
795
- log.info(`handleIncomingCallDisconnect: ${this.getCorrelationId()} `, {
796
- file: CALL_FILE,
797
- method: this.handleIncomingCallDisconnect.name,
798
- });
799
- this.setDisconnectReason();
800
- try {
801
- const response = await this.delete();
802
- log.log(`handleOutgoingCallDisconnect: Response code: ${response.statusCode}`, {
803
- file: CALL_FILE,
804
- method: this.handleIncomingCallDisconnect.name,
805
- });
806
- }
807
- catch (e) {
808
- log.warn('Delete Call failed with Mobius', {
809
- file: CALL_FILE,
810
- method: this.handleIncomingCallDisconnect.name,
811
- });
812
- }
813
- this.deleteCb(this.correlationId);
814
- if (this.sessionTimer) {
815
- clearInterval(this.sessionTimer);
816
- }
817
- if (this.mediaConnection) {
818
- this.mediaConnection.close();
819
- log.info('Closing media channel', { file: CALL_FILE, method: 'handleIncomingCallDisconnect' });
820
- }
821
- this.sendMediaStateMachineEvt({ type: 'E_ROAP_TEARDOWN' });
822
- this.sendCallStateMachineEvt({ type: 'E_CALL_CLEARED' });
823
- this.emit(CALL_EVENT_KEYS.DISCONNECT, this.correlationId);
824
- }
825
- async handleOutgoingCallDisconnect(event) {
826
- this.setDisconnectReason();
827
- try {
828
- const response = await this.delete();
829
- log.log(`handleOutgoingCallDisconnect: Response code: ${response.statusCode}`, {
830
- file: CALL_FILE,
831
- method: this.handleOutgoingCallDisconnect.name,
832
- });
833
- }
834
- catch (e) {
835
- log.warn('Delete Call failed with Mobius', {
836
- file: CALL_FILE,
837
- method: this.handleOutgoingCallDisconnect.name,
838
- });
839
- }
840
- this.deleteCb(this.correlationId);
841
- if (this.sessionTimer) {
842
- clearInterval(this.sessionTimer);
843
- }
844
- if (this.mediaConnection) {
845
- this.mediaConnection.close();
846
- log.info('Closing media channel', { file: CALL_FILE, method: 'handleOutgoingCallDisconnect' });
847
- }
848
- this.sendMediaStateMachineEvt({ type: 'E_ROAP_TEARDOWN' });
849
- this.sendCallStateMachineEvt({ type: 'E_CALL_CLEARED' });
850
- }
851
- handleCallEstablished(event) {
852
- log.info(`handleCallEstablished: ${this.getCorrelationId()} `, {
853
- file: CALL_FILE,
854
- method: this.handleCallEstablished.name,
855
- });
856
- this.emit(CALL_EVENT_KEYS.ESTABLISHED, this.correlationId);
857
- this.earlyMedia = false;
858
- this.connected = true;
859
- if (this.sessionTimer) {
860
- log.log('Resetting session timer', {
861
- file: CALL_FILE,
862
- method: 'handleCallEstablished',
863
- });
864
- clearInterval(this.sessionTimer);
865
- }
866
- this.sessionTimer = setInterval(async () => {
867
- try {
868
- const res = await this.postStatus();
869
- log.info(`Session refresh successful`, {
870
- file: CALL_FILE,
871
- method: 'handleCallEstablished',
872
- });
873
- }
874
- catch (err) {
875
- const error = err;
876
- if (this.sessionTimer) {
877
- clearInterval(this.sessionTimer);
878
- }
879
- handleCallErrors((callError) => {
880
- this.emit(CALL_EVENT_KEYS.CALL_ERROR, callError);
881
- this.submitCallErrorMetric(callError);
882
- }, ERROR_LAYER.CALL_CONTROL, (interval) => {
883
- setTimeout(() => {
884
- this.postStatus();
885
- this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED' });
886
- }, interval * 1000);
887
- }, this.getCorrelationId(), error, this.handleCallEstablished.name, CALL_FILE);
888
- }
889
- }, DEFAULT_SESSION_TIMER);
890
- }
891
- async handleUnknownState(event) {
892
- log.info(`handleUnknownState: ${this.getCorrelationId()} `, {
893
- file: CALL_FILE,
894
- method: this.handleUnknownState.name,
895
- });
896
- const eventData = event.data;
897
- if (!eventData?.media) {
898
- log.warn('Call failed due to signalling issue', {
899
- file: CALL_FILE,
900
- method: this.handleUnknownState.name,
901
- });
902
- }
903
- try {
904
- this.setDisconnectReason();
905
- const response = await this.delete();
906
- log.log(`handleOutgoingCallDisconnect: Response code: ${response.statusCode}`, {
907
- file: CALL_FILE,
908
- method: this.handleUnknownState.name,
909
- });
910
- }
911
- catch (e) {
912
- log.warn('Delete Call failed with Mobius', {
913
- file: CALL_FILE,
914
- method: this.handleUnknownState.name,
915
- });
916
- }
917
- this.deleteCb(this.correlationId);
918
- if (this.sessionTimer) {
919
- clearInterval(this.sessionTimer);
920
- }
921
- if (this.mediaConnection) {
922
- this.mediaConnection.close();
923
- log.info('Closing media channel', {
924
- file: CALL_FILE,
925
- method: this.handleUnknownState.name,
926
- });
927
- }
928
- this.sendMediaStateMachineEvt({ type: 'E_ROAP_TEARDOWN' });
929
- this.sendCallStateMachineEvt({ type: 'E_CALL_CLEARED' });
930
- }
931
- getEmitterCallback(errData) {
932
- return (error) => {
933
- switch (this.callStateMachine.state.value) {
934
- case 'S_CALL_HOLD':
935
- this.emit(CALL_EVENT_KEYS.HOLD_ERROR, error);
936
- if (this.supplementaryServicesTimer) {
937
- clearTimeout(this.supplementaryServicesTimer);
938
- this.supplementaryServicesTimer = undefined;
939
- }
940
- this.submitCallErrorMetric(error);
941
- this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
942
- return;
943
- case 'S_CALL_RESUME':
944
- this.emit(CALL_EVENT_KEYS.RESUME_ERROR, error);
945
- this.submitCallErrorMetric(error);
946
- this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
947
- return;
948
- default:
949
- this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
950
- this.submitCallErrorMetric(error);
951
- if (!this.connected) {
952
- this.sendMediaStateMachineEvt({ type: 'E_ROAP_ERROR', data: errData });
953
- }
954
- }
955
- };
956
- }
957
- async handleRoapEstablished(context, event) {
958
- log.info(`handleRoapEstablished: ${this.getCorrelationId()} `, {
959
- file: CALL_FILE,
960
- method: 'handleRoapEstablished',
961
- });
962
- const { received, message } = event.data;
963
- this.receivedRoapOKSeq = message.seq;
964
- if (!received) {
965
- log.info('Sending Media Ok to the remote End', {
966
- file: CALL_FILE,
967
- method: 'handleRoapEstablished',
968
- });
969
- try {
970
- if (this.callStateMachine.state.value === 'S_RECV_CALL_PROGRESS') {
971
- log.info('Media negotiation completed before call connect. Setting media negotiation completed flag.', {
972
- file: CALL_FILE,
973
- method: 'handleRoapEstablished',
974
- });
975
- this.mediaNegotiationCompleted = true;
976
- }
977
- message.seq = this.seq;
978
- const res = await this.postMedia(message);
979
- log.log(`handleRoapEstablished: Response code: ${res.statusCode}`, {
980
- file: CALL_FILE,
981
- method: 'handleRoapEstablished',
982
- });
983
- if (!this.earlyMedia && !this.mediaNegotiationCompleted) {
984
- this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED' });
985
- }
986
- }
987
- catch (err) {
988
- log.warn('MediaOk failed with Mobius', {
989
- file: CALL_FILE,
990
- method: 'handleRoapEstablished',
991
- });
992
- const errData = err;
993
- handleCallErrors(this.getEmitterCallback(errData), ERROR_LAYER.MEDIA, (interval) => {
994
- if (this.connected) {
995
- setTimeout(() => {
996
- this.sendMediaStateMachineEvt({ type: 'E_ROAP_OK', data: event.data });
997
- }, interval * 1000);
998
- }
999
- }, this.getCorrelationId(), errData, this.handleRoapEstablished.name, CALL_FILE);
1000
- }
1001
- }
1002
- else {
1003
- log.info('Notifying internal-media-core about ROAP OK message', {
1004
- file: CALL_FILE,
1005
- method: 'handleRoapEstablished',
1006
- });
1007
- message.seq = this.seq;
1008
- if (this.mediaConnection) {
1009
- this.mediaConnection.roapMessageReceived(message);
1010
- }
1011
- if (!this.earlyMedia) {
1012
- this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED' });
1013
- }
1014
- if (this.remoteRoapMessage && this.remoteRoapMessage.seq > this.seq) {
1015
- if (this.remoteRoapMessage.messageType === 'OFFER_REQUEST') {
1016
- this.sendMediaStateMachineEvt({
1017
- type: 'E_RECV_ROAP_OFFER_REQUEST',
1018
- data: this.remoteRoapMessage,
1019
- });
1020
- }
1021
- else if (this.remoteRoapMessage.messageType === 'OFFER') {
1022
- this.sendMediaStateMachineEvt({ type: 'E_RECV_ROAP_OFFER', data: this.remoteRoapMessage });
1023
- }
1024
- }
1025
- }
1026
- }
1027
- async handleRoapError(context, event) {
1028
- log.info(`handleRoapError: ${this.getCorrelationId()}`, {
1029
- file: CALL_FILE,
1030
- method: this.handleRoapError.name,
1031
- });
1032
- const message = event.data;
1033
- if (message) {
1034
- try {
1035
- const res = await this.postMedia(message);
1036
- log.info(`Response code: ${res.statusCode}`, {
1037
- file: CALL_FILE,
1038
- method: this.handleRoapError.name,
1039
- });
1040
- }
1041
- catch (err) {
1042
- log.warn('Failed to communicate ROAP error with Mobius', {
1043
- file: CALL_FILE,
1044
- method: this.handleRoapError.name,
1045
- });
1046
- const errData = err;
1047
- handleCallErrors((error) => {
1048
- this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
1049
- this.submitCallErrorMetric(error);
1050
- }, ERROR_LAYER.MEDIA, (interval) => undefined, this.getCorrelationId(), errData, this.handleRoapError.name, CALL_FILE);
1051
- }
1052
- }
1053
- if (!this.connected) {
1054
- log.warn('Call failed due to media issue', {
1055
- file: CALL_FILE,
1056
- method: 'handleRoapError',
1057
- });
1058
- this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: { media: true } });
1059
- }
1060
- }
1061
- async handleOutgoingRoapOffer(context, event) {
1062
- log.info(`handleOutgoingRoapOffer: ${this.getCorrelationId()}`, {
1063
- file: CALL_FILE,
1064
- method: this.handleOutgoingRoapOffer.name,
1065
- });
1066
- const message = event.data;
1067
- if (!message?.sdp) {
1068
- log.info('Initializing Offer...', {
1069
- file: CALL_FILE,
1070
- method: this.handleOutgoingRoapOffer.name,
1071
- });
1072
- this.mediaConnection.initiateOffer();
1073
- return;
1074
- }
1075
- try {
1076
- const res = await this.postMedia(message);
1077
- log.log(`handleOutgoingRoapOffer: Response code: ${res.statusCode}`, {
1078
- file: CALL_FILE,
1079
- method: this.handleOutgoingRoapOffer.name,
1080
- });
1081
- }
1082
- catch (err) {
1083
- log.warn('MediaOk failed with Mobius', {
1084
- file: CALL_FILE,
1085
- method: this.handleOutgoingRoapOffer.name,
1086
- });
1087
- const errData = err;
1088
- handleCallErrors(this.getEmitterCallback(errData), ERROR_LAYER.MEDIA, (interval) => {
1089
- if (this.connected) {
1090
- setTimeout(() => {
1091
- this.sendMediaStateMachineEvt({ type: 'E_SEND_ROAP_OFFER', data: event.data });
1092
- }, interval * 1000);
1093
- }
1094
- }, this.getCorrelationId(), errData, this.handleOutgoingRoapOffer.name, CALL_FILE);
1095
- }
1096
- }
1097
- async handleOutgoingRoapAnswer(context, event) {
1098
- log.info(`handleOutgoingRoapAnswer: ${this.getCorrelationId()}`, {
1099
- file: CALL_FILE,
1100
- method: this.handleOutgoingRoapAnswer.name,
1101
- });
1102
- const message = event.data;
1103
- try {
1104
- message.seq = this.seq;
1105
- const res = await this.postMedia(message);
1106
- log.log(`handleOutgoingRoapAnswer: Response code: ${res.statusCode}`, {
1107
- file: CALL_FILE,
1108
- method: this.handleOutgoingRoapAnswer.name,
1109
- });
1110
- }
1111
- catch (err) {
1112
- log.warn('MediaAnswer failed with Mobius', {
1113
- file: CALL_FILE,
1114
- method: this.handleOutgoingRoapAnswer.name,
1115
- });
1116
- const errData = err;
1117
- handleCallErrors(this.getEmitterCallback(errData), ERROR_LAYER.MEDIA, (interval) => {
1118
- if (this.connected) {
1119
- setTimeout(() => {
1120
- this.sendMediaStateMachineEvt({ type: 'E_SEND_ROAP_ANSWER', data: event.data });
1121
- }, interval * 1000);
1122
- }
1123
- }, this.getCorrelationId(), errData, this.handleOutgoingRoapAnswer.name, CALL_FILE);
1124
- }
1125
- }
1126
- handleIncomingRoapOffer(context, event) {
1127
- log.info(`handleIncomingRoapOffer: ${this.getCorrelationId()}`, {
1128
- file: CALL_FILE,
1129
- method: this.handleIncomingRoapOffer.name,
1130
- });
1131
- const message = event.data;
1132
- this.remoteRoapMessage = message;
1133
- if (!this.mediaConnection) {
1134
- log.info('Media connection is not up, buffer the remote offer for later handling', {
1135
- file: CALL_FILE,
1136
- method: this.handleIncomingRoapOffer.name,
1137
- });
1138
- this.seq = message.seq;
1139
- log.info(`Setting Sequence No: ${this.seq}`, {
1140
- file: CALL_FILE,
1141
- method: this.handleIncomingRoapOffer.name,
1142
- });
1143
- }
1144
- else if (this.receivedRoapOKSeq === message.seq - 2) {
1145
- log.info('Waiting for Roap OK, buffer the remote offer for later handling', {
1146
- file: CALL_FILE,
1147
- method: this.handleIncomingRoapOffer.name,
1148
- });
1149
- this.remoteRoapMessage = message;
1150
- }
1151
- else {
1152
- log.info('Handling new offer...', {
1153
- file: CALL_FILE,
1154
- method: this.handleIncomingRoapOffer.name,
1155
- });
1156
- this.seq = message.seq;
1157
- if (this.mediaConnection) {
1158
- this.mediaConnection.roapMessageReceived(message);
1159
- }
1160
- }
1161
- }
1162
- handleIncomingRoapAnswer(context, event) {
1163
- log.info(`handleIncomingRoapAnswer: ${this.getCorrelationId()}`, {
1164
- file: CALL_FILE,
1165
- method: this.handleIncomingRoapAnswer.name,
1166
- });
1167
- const message = event.data;
1168
- this.remoteRoapMessage = message;
1169
- message.seq = this.seq;
1170
- if (this.mediaConnection) {
1171
- this.mediaConnection.roapMessageReceived(message);
1172
- }
1173
- }
1174
- initMediaConnection(localAudioTrack, debugId) {
1175
- const mediaConnection = new RoapMediaConnection({
1176
- skipInactiveTransceivers: true,
1177
- iceServers: [],
1178
- sdpMunging: {
1179
- convertPort9to0: true,
1180
- addContentSlides: false,
1181
- },
1182
- }, {
1183
- localTracks: { audio: localAudioTrack },
1184
- direction: {
1185
- audio: 'sendrecv',
1186
- video: 'inactive',
1187
- screenShareVideo: 'inactive',
1188
- },
1189
- }, debugId || `WebexCallSDK-${this.correlationId}`);
1190
- this.mediaConnection = mediaConnection;
1191
- }
1192
- getDirection = () => this.direction;
1193
- getCallId = () => this.callId;
1194
- getCorrelationId = () => this.correlationId;
1195
- sendCallStateMachineEvt(event) {
1196
- this.callStateMachine.send(event);
1197
- }
1198
- sendMediaStateMachineEvt(event) {
1199
- this.mediaStateMachine.send(event);
1200
- }
1201
- setCallId = (callId) => {
1202
- this.callId = callId;
1203
- log.info(`Setting callId : ${this.callId} for correlationId: ${this.correlationId}`, {
1204
- file: CALL_FILE,
1205
- method: this.setCallId.name,
1206
- });
1207
- };
1208
- setDisconnectReason() {
1209
- if (this.connected || this.direction === CallDirection.OUTBOUND) {
1210
- this.disconnectReason.code = DisconnectCode.NORMAL;
1211
- this.disconnectReason.cause = DisconnectCause.NORMAL;
1212
- }
1213
- else {
1214
- this.disconnectReason.code = DisconnectCode.BUSY;
1215
- this.disconnectReason.cause = DisconnectCause.BUSY;
1216
- }
1217
- }
1218
- getDisconnectReason = () => {
1219
- return this.disconnectReason;
1220
- };
1221
- async answer(localAudioStream) {
1222
- const localAudioTrack = localAudioStream.outputStream.getAudioTracks()[0];
1223
- localAudioTrack.enabled = true;
1224
- if (!this.mediaConnection) {
1225
- this.initMediaConnection(localAudioTrack);
1226
- this.mediaRoapEventsListener();
1227
- this.mediaTrackListener();
1228
- this.outputTrackUpdateListener(localAudioStream);
1229
- }
1230
- if (this.callStateMachine.state.value === 'S_SEND_CALL_PROGRESS') {
1231
- this.sendCallStateMachineEvt({ type: 'E_SEND_CALL_CONNECT' });
1232
- }
1233
- else {
1234
- log.warn(`Call cannot be answered because the state is : ${this.callStateMachine.state.value}`, { file: CALL_FILE, method: 'answer' });
1235
- }
1236
- }
1237
- async dial(localAudioStream) {
1238
- const localAudioTrack = localAudioStream.outputStream.getAudioTracks()[0];
1239
- localAudioTrack.enabled = true;
1240
- if (!this.mediaConnection) {
1241
- this.initMediaConnection(localAudioTrack);
1242
- this.mediaRoapEventsListener();
1243
- this.mediaTrackListener();
1244
- this.outputTrackUpdateListener(localAudioStream);
1245
- }
1246
- if (this.mediaStateMachine.state.value === 'S_ROAP_IDLE') {
1247
- this.sendMediaStateMachineEvt({ type: 'E_SEND_ROAP_OFFER' });
1248
- }
1249
- else {
1250
- log.warn(`Call cannot be dialed because the state is already : ${this.mediaStateMachine.state.value}`, { file: CALL_FILE, method: 'dial' });
1251
- }
1252
- }
1253
- post = async (roapMessage) => {
1254
- return this.webex.request({
1255
- uri: `${this.mobiusUrl}${DEVICES_ENDPOINT_RESOURCE}/${this.deviceId}/${CALL_ENDPOINT_RESOURCE}`,
1256
- method: HTTP_METHODS.POST,
1257
- service: ALLOWED_SERVICES.MOBIUS,
1258
- headers: {
1259
- [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1260
- [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1261
- },
1262
- body: {
1263
- device: {
1264
- deviceId: this.deviceId,
1265
- correlationId: this.correlationId,
1266
- },
1267
- callee: {
1268
- type: this.destination.type,
1269
- address: this.destination.address,
1270
- },
1271
- localMedia: {
1272
- roap: roapMessage,
1273
- mediaId: uuid(),
1274
- },
1275
- },
1276
- });
1277
- };
1278
- async patch(state) {
1279
- log.info(`Send a PATCH for ${state} to mobius`, {
1280
- file: CALL_FILE,
1281
- method: this.patch.name,
1282
- });
1283
- return this.webex.request({
1284
- uri: `${this.mobiusUrl}${DEVICES_ENDPOINT_RESOURCE}/${this.deviceId}/${CALLS_ENDPOINT_RESOURCE}/${this.callId}`,
1285
- method: HTTP_METHODS.PATCH,
1286
- service: ALLOWED_SERVICES.MOBIUS,
1287
- headers: {
1288
- [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1289
- [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1290
- },
1291
- body: {
1292
- device: {
1293
- deviceId: this.deviceId,
1294
- correlationId: this.correlationId,
1295
- },
1296
- callId: this.callId,
1297
- callState: state,
1298
- inbandMedia: false,
1299
- },
1300
- });
1301
- }
1302
- async postSSRequest(context, type) {
1303
- const request = {
1304
- uri: `${this.mobiusUrl}${SERVICES_ENDPOINT}`,
1305
- method: HTTP_METHODS.POST,
1306
- service: ALLOWED_SERVICES.MOBIUS,
1307
- headers: {
1308
- [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1309
- [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1310
- },
1311
- body: {
1312
- device: {
1313
- deviceId: this.deviceId,
1314
- correlationId: this.correlationId,
1315
- },
1316
- callId: this.callId,
1317
- },
1318
- };
1319
- switch (type) {
1320
- case SUPPLEMENTARY_SERVICES.HOLD: {
1321
- request.uri = `${request.uri}/${CALL_HOLD_SERVICE}/${HOLD_ENDPOINT}`;
1322
- break;
1323
- }
1324
- case SUPPLEMENTARY_SERVICES.RESUME: {
1325
- request.uri = `${request.uri}/${CALL_HOLD_SERVICE}/${RESUME_ENDPOINT}`;
1326
- break;
1327
- }
1328
- case SUPPLEMENTARY_SERVICES.TRANSFER: {
1329
- request.uri = `${request.uri}/${CALL_TRANSFER_SERVICE}/${TRANSFER_ENDPOINT}`;
1330
- const transferContext = context;
1331
- if (transferContext.destination) {
1332
- Object.assign(request.body, { blindTransferContext: transferContext });
1333
- Object.assign(request.body, { transferType: TransferType.BLIND });
1334
- }
1335
- else if (transferContext.transferToCallId) {
1336
- Object.assign(request.body, { consultTransferContext: transferContext });
1337
- Object.assign(request.body, { transferType: TransferType.CONSULT });
1338
- }
1339
- break;
1340
- }
1341
- default: {
1342
- log.warn(`Unknown type for PUT request: ${type}`, {
1343
- file: CALL_FILE,
1344
- method: this.postSSRequest.name,
1345
- });
1346
- }
1347
- }
1348
- return this.webex.request(request);
1349
- }
1350
- async postStatus() {
1351
- return this.webex.request({
1352
- uri: `${this.mobiusUrl}${DEVICES_ENDPOINT_RESOURCE}/${this.deviceId}/${CALLS_ENDPOINT_RESOURCE}/${this.callId}/${CALL_STATUS_RESOURCE}`,
1353
- method: HTTP_METHODS.POST,
1354
- service: ALLOWED_SERVICES.MOBIUS,
1355
- headers: {
1356
- [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1357
- [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1358
- },
1359
- body: {
1360
- device: {
1361
- deviceId: this.deviceId,
1362
- correlationId: this.correlationId,
1363
- },
1364
- callId: this.callId,
1365
- },
1366
- });
1367
- }
1368
- async completeTransfer(transferType, transferCallId, transferTarget) {
1369
- if (transferType === TransferType.BLIND && transferTarget) {
1370
- log.info(`Initiating Blind transfer with : ${transferTarget}`, {
1371
- file: CALL_FILE,
1372
- method: this.completeTransfer.name,
1373
- });
1374
- const context = {
1375
- transferorCallId: this.getCallId(),
1376
- destination: transferTarget,
1377
- };
1378
- try {
1379
- await this.postSSRequest(context, SUPPLEMENTARY_SERVICES.TRANSFER);
1380
- this.metricManager.submitCallMetric(METRIC_EVENT.CALL, TRANSFER_ACTION.BLIND, METRIC_TYPE.BEHAVIORAL, this.getCallId(), this.getCorrelationId(), undefined);
1381
- }
1382
- catch (e) {
1383
- log.warn(`Blind Transfer failed for correlationId ${this.getCorrelationId()}`, {
1384
- file: CALL_FILE,
1385
- method: this.completeTransfer.name,
1386
- });
1387
- const errData = e;
1388
- handleCallErrors((error) => {
1389
- this.emit(CALL_EVENT_KEYS.TRANSFER_ERROR, error);
1390
- this.submitCallErrorMetric(error, TRANSFER_ACTION.BLIND);
1391
- }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.completeTransfer.name, CALL_FILE);
1392
- }
1393
- }
1394
- else if (transferType === TransferType.CONSULT && transferCallId) {
1395
- log.info(`Initiating Consult transfer between : ${this.callId} and ${transferCallId}`, {
1396
- file: CALL_FILE,
1397
- method: this.completeTransfer.name,
1398
- });
1399
- const context = {
1400
- transferorCallId: this.getCallId(),
1401
- transferToCallId: transferCallId,
1402
- };
1403
- try {
1404
- await this.postSSRequest(context, SUPPLEMENTARY_SERVICES.TRANSFER);
1405
- this.metricManager.submitCallMetric(METRIC_EVENT.CALL, TRANSFER_ACTION.CONSULT, METRIC_TYPE.BEHAVIORAL, this.getCallId(), this.getCorrelationId(), undefined);
1406
- }
1407
- catch (e) {
1408
- log.warn(`Consult Transfer failed for correlationId ${this.getCorrelationId()}`, {
1409
- file: CALL_FILE,
1410
- method: this.completeTransfer.name,
1411
- });
1412
- const errData = e;
1413
- handleCallErrors((error) => {
1414
- this.emit(CALL_EVENT_KEYS.TRANSFER_ERROR, error);
1415
- this.submitCallErrorMetric(error, TRANSFER_ACTION.CONSULT);
1416
- }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.completeTransfer.name, CALL_FILE);
1417
- }
1418
- }
1419
- else {
1420
- log.warn(`Invalid information received, transfer failed for correlationId: ${this.getCorrelationId()}`, {
1421
- file: CALL_FILE,
1422
- method: this.completeTransfer.name,
1423
- });
1424
- }
1425
- }
1426
- async getCallStats() {
1427
- let stats;
1428
- try {
1429
- stats = await this.mediaConnection.getStats();
1430
- }
1431
- catch (err) {
1432
- log.warn('Stats collection failed, using dummy stats', {
1433
- file: CALL_FILE,
1434
- method: this.getCallStats.name,
1435
- });
1436
- }
1437
- return parseMediaQualityStatistics(stats);
1438
- }
1439
- async postMedia(roapMessage) {
1440
- log.log('Posting message to mobius', {
1441
- file: CALL_FILE,
1442
- method: this.postMedia.name,
1443
- });
1444
- return this.webex.request({
1445
- uri: `${this.mobiusUrl}${DEVICES_ENDPOINT_RESOURCE}/${this.deviceId}/${CALLS_ENDPOINT_RESOURCE}/${this.callId}/${MEDIA_ENDPOINT_RESOURCE}`,
1446
- method: HTTP_METHODS.POST,
1447
- service: ALLOWED_SERVICES.MOBIUS,
1448
- headers: {
1449
- [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1450
- [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1451
- },
1452
- body: {
1453
- device: {
1454
- deviceId: this.deviceId,
1455
- correlationId: this.correlationId,
1456
- },
1457
- callId: this.callId,
1458
- localMedia: {
1459
- roap: roapMessage,
1460
- mediaId: uuid(),
1461
- },
1462
- },
1463
- });
1464
- }
1465
- mediaRoapEventsListener() {
1466
- this.mediaConnection.on(Event.ROAP_MESSAGE_TO_SEND, async (event) => {
1467
- log.info(`ROAP message to send (rcv from MEDIA-SDK) :
1468
- \n type: ${event.roapMessage?.messageType}, seq: ${event.roapMessage.seq} , version: ${event.roapMessage.version}`, {});
1469
- log.info(`SDP message to send : \n ${event.roapMessage?.sdp}`, {
1470
- file: CALL_FILE,
1471
- method: this.mediaRoapEventsListener.name,
1472
- });
1473
- switch (event.roapMessage.messageType) {
1474
- case RoapScenario.OK: {
1475
- const mediaOk = {
1476
- received: false,
1477
- message: event.roapMessage,
1478
- };
1479
- this.sendMediaStateMachineEvt({ type: 'E_ROAP_OK', data: mediaOk });
1480
- break;
1481
- }
1482
- case RoapScenario.OFFER: {
1483
- const sdpVideoPortZero = event.roapMessage.sdp.replace(/^m=(video) (?:\d+) /gim, 'm=$1 0 ');
1484
- event.roapMessage.sdp = sdpVideoPortZero;
1485
- this.localRoapMessage = event.roapMessage;
1486
- this.sendCallStateMachineEvt({ type: 'E_SEND_CALL_SETUP', data: event.roapMessage });
1487
- break;
1488
- }
1489
- case RoapScenario.ANSWER:
1490
- this.localRoapMessage = event.roapMessage;
1491
- this.sendMediaStateMachineEvt({ type: 'E_SEND_ROAP_ANSWER', data: event.roapMessage });
1492
- break;
1493
- case RoapScenario.ERROR:
1494
- this.sendMediaStateMachineEvt({ type: 'E_ROAP_ERROR', data: event.roapMessage });
1495
- break;
1496
- case RoapScenario.OFFER_RESPONSE:
1497
- this.localRoapMessage = event.roapMessage;
1498
- this.sendMediaStateMachineEvt({ type: 'E_SEND_ROAP_OFFER', data: event.roapMessage });
1499
- break;
1500
- default:
1501
- }
1502
- });
1503
- }
1504
- mediaTrackListener() {
1505
- this.mediaConnection.on(Event.REMOTE_TRACK_ADDED, (e) => {
1506
- if (e.type === MEDIA_CONNECTION_EVENT_KEYS.MEDIA_TYPE_AUDIO) {
1507
- this.emit(CALL_EVENT_KEYS.REMOTE_MEDIA, e.track);
1508
- }
1509
- });
1510
- }
1511
- outputTrackUpdateListener(localAudioStream) {
1512
- localAudioStream.on(LocalStreamEventNames.OutputTrackChange, (track) => {
1513
- this.mediaConnection.updateLocalTracks({ audio: track });
1514
- });
1515
- }
1516
- async delete() {
1517
- const disconnectMetrics = await this.getCallStats();
1518
- return this.webex.request({
1519
- uri: `${this.mobiusUrl}${DEVICES_ENDPOINT_RESOURCE}/${this.deviceId}/${CALLS_ENDPOINT_RESOURCE}/${this.callId}`,
1520
- method: HTTP_METHODS.DELETE,
1521
- service: ALLOWED_SERVICES.MOBIUS,
1522
- headers: {
1523
- [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1524
- [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1525
- },
1526
- body: {
1527
- device: {
1528
- deviceId: this.deviceId,
1529
- correlationId: this.correlationId,
1530
- },
1531
- callId: this.callId,
1532
- metrics: disconnectMetrics,
1533
- causecode: this.disconnectReason.code,
1534
- cause: this.disconnectReason.cause,
1535
- },
1536
- });
1537
- }
1538
- submitCallErrorMetric(error, transferMetricAction) {
1539
- if (error.getCallError().errorLayer === ERROR_LAYER.CALL_CONTROL) {
1540
- this.metricManager.submitCallMetric(METRIC_EVENT.CALL_ERROR, transferMetricAction || this.callStateMachine.state.value.toString(), METRIC_TYPE.BEHAVIORAL, this.callId, this.correlationId, error);
1541
- }
1542
- else {
1543
- this.metricManager.submitMediaMetric(METRIC_EVENT.MEDIA_ERROR, this.mediaStateMachine.state.value.toString(), METRIC_TYPE.BEHAVIORAL, this.callId, this.correlationId, this.localRoapMessage.sdp, this.remoteRoapMessage?.sdp, error);
1544
- }
1545
- }
1546
- handleMidCallEvent(event) {
1547
- const { eventType, eventData } = event;
1548
- switch (eventType) {
1549
- case MidCallEventType.CALL_INFO: {
1550
- log.log(`Received Midcall CallInfo Event for correlationId : ${this.correlationId}`, {
1551
- file: CALL_FILE,
1552
- method: 'handleMidCallEvent',
1553
- });
1554
- const callerData = eventData;
1555
- this.startCallerIdResolution(callerData.callerId);
1556
- break;
1557
- }
1558
- case MidCallEventType.CALL_STATE: {
1559
- log.log(`Received Midcall call event for correlationId : ${this.correlationId}`, {
1560
- file: CALL_FILE,
1561
- method: 'handleMidCallEvent',
1562
- });
1563
- const data = eventData;
1564
- switch (data.callState) {
1565
- case MOBIUS_MIDCALL_STATE.HELD: {
1566
- log.log(`Call is successfully held : ${this.correlationId}`, {
1567
- file: CALL_FILE,
1568
- method: 'handleMidCallEvent',
1569
- });
1570
- this.emit(CALL_EVENT_KEYS.HELD, this.correlationId);
1571
- this.held = true;
1572
- if (this.supplementaryServicesTimer) {
1573
- clearTimeout(this.supplementaryServicesTimer);
1574
- this.supplementaryServicesTimer = undefined;
1575
- }
1576
- break;
1577
- }
1578
- case MOBIUS_MIDCALL_STATE.CONNECTED: {
1579
- log.log(`Call is successfully resumed : ${this.correlationId}`, {
1580
- file: CALL_FILE,
1581
- method: 'handleMidCallEvent',
1582
- });
1583
- this.emit(CALL_EVENT_KEYS.RESUMED, this.correlationId);
1584
- this.held = false;
1585
- if (this.supplementaryServicesTimer) {
1586
- clearTimeout(this.supplementaryServicesTimer);
1587
- this.supplementaryServicesTimer = undefined;
1588
- }
1589
- break;
1590
- }
1591
- default: {
1592
- log.warn(`Unknown Supplementary service state: ${data.callState} for correlationId : ${this.correlationId}`, {
1593
- file: CALL_FILE,
1594
- method: 'handleMidCallEvent',
1595
- });
1596
- }
1597
- }
1598
- break;
1599
- }
1600
- default: {
1601
- log.warn(`Unknown Midcall type: ${eventType} for correlationId : ${this.correlationId}`, {
1602
- file: CALL_FILE,
1603
- method: 'handleMidCallEvent',
1604
- });
1605
- }
1606
- }
1607
- }
1608
- getCallerInfo = () => this.callerInfo;
1609
- end = () => {
1610
- this.sendCallStateMachineEvt({ type: 'E_SEND_CALL_DISCONNECT' });
1611
- };
1612
- doHoldResume = () => {
1613
- if (this.held) {
1614
- this.sendCallStateMachineEvt({ type: 'E_CALL_RESUME' });
1615
- }
1616
- else {
1617
- this.sendCallStateMachineEvt({ type: 'E_CALL_HOLD' });
1618
- }
1619
- };
1620
- startCallerIdResolution(callerInfo) {
1621
- this.callerInfo = this.callerId.fetchCallerDetails(callerInfo);
1622
- }
1623
- sendDigit(tone) {
1624
- if (!this.connected) {
1625
- log.warn(`Can't send DTMF as call is not yet connected`, {
1626
- file: CALL_FILE,
1627
- method: 'sendDigit',
1628
- });
1629
- return;
1630
- }
1631
- log.info(`Sending digit : ${tone}`, {
1632
- file: CALL_FILE,
1633
- method: 'sendDigit',
1634
- });
1635
- this.mediaConnection.insertDTMF(tone);
1636
- }
1637
- mute = (localAudioStream) => {
1638
- const localAudioTrack = localAudioStream.outputStream.getAudioTracks()[0];
1639
- if (this.muted) {
1640
- localAudioTrack.enabled = true;
1641
- this.muted = false;
1642
- }
1643
- else {
1644
- localAudioTrack.enabled = false;
1645
- this.muted = true;
1646
- }
1647
- };
1648
- setBroadworksCorrelationInfo(broadworksCorrelationInfo) {
1649
- this.broadworksCorrelationInfo = broadworksCorrelationInfo;
1650
- }
1651
- getBroadworksCorrelationInfo() {
1652
- return this.broadworksCorrelationInfo;
1653
- }
1654
- getCallRtpStats() {
1655
- return this.getCallStats();
1656
- }
1657
- }
1658
- export const createCall = (activeUrl, webex, dest, dir, deviceId, lineId, deleteCb, indicator) => new Call(activeUrl, webex, dest, dir, deviceId, lineId, deleteCb, indicator);
1
+ import { Event, LocalStreamEventNames, RoapMediaConnection, } from '@webex/internal-media-core';
2
+ import { createMachine, interpret } from 'xstate';
3
+ import { v4 as uuid } from 'uuid';
4
+ import { ERROR_LAYER, ERROR_TYPE } from '../../Errors/types';
5
+ import { handleCallErrors, parseMediaQualityStatistics } from '../../common/Utils';
6
+ import { ALLOWED_SERVICES, CallDirection, HTTP_METHODS, } from '../../common/types';
7
+ import { createCallError } from '../../Errors/catalog/CallError';
8
+ import { CALL_ENDPOINT_RESOURCE, CALL_FILE, CALL_HOLD_SERVICE, CALL_STATUS_RESOURCE, CALL_TRANSFER_SERVICE, CALLING_USER_AGENT, CALLS_ENDPOINT_RESOURCE, CISCO_DEVICE_URL, DEFAULT_LOCAL_CALL_ID, DEFAULT_SESSION_TIMER, DEVICES_ENDPOINT_RESOURCE, HOLD_ENDPOINT, INITIAL_SEQ_NUMBER, MEDIA_ENDPOINT_RESOURCE, RESUME_ENDPOINT, SPARK_USER_AGENT, SUPPLEMENTARY_SERVICES_TIMEOUT, TRANSFER_ENDPOINT, } from '../constants';
9
+ import SDKConnector from '../../SDKConnector';
10
+ import { Eventing } from '../../Events/impl';
11
+ import { CALL_EVENT_KEYS, MEDIA_CONNECTION_EVENT_KEYS, MOBIUS_MIDCALL_STATE, SUPPLEMENTARY_SERVICES, } from '../../Events/types';
12
+ import { DisconnectCause, DisconnectCode, MidCallEventType, MobiusCallState, RoapScenario, TransferType, } from './types';
13
+ import log from '../../Logger';
14
+ import { createCallerId } from './CallerId';
15
+ import { METRIC_TYPE, METRIC_EVENT, TRANSFER_ACTION } from '../../Metrics/types';
16
+ import { getMetricManager } from '../../Metrics';
17
+ import { SERVICES_ENDPOINT } from '../../common/constants';
18
+ export class Call extends Eventing {
19
+ sdkConnector;
20
+ webex;
21
+ destination;
22
+ direction;
23
+ callId;
24
+ correlationId;
25
+ deviceId;
26
+ lineId;
27
+ disconnectReason;
28
+ callStateMachine;
29
+ mediaStateMachine;
30
+ seq;
31
+ mediaConnection;
32
+ earlyMedia;
33
+ connected;
34
+ mediaInactivity;
35
+ callerInfo;
36
+ localRoapMessage;
37
+ mobiusUrl;
38
+ remoteRoapMessage;
39
+ deleteCb;
40
+ callerId;
41
+ sessionTimer;
42
+ supplementaryServicesTimer;
43
+ muted;
44
+ held;
45
+ metricManager;
46
+ broadworksCorrelationInfo;
47
+ serviceIndicator;
48
+ mediaNegotiationCompleted;
49
+ receivedRoapOKSeq;
50
+ isMuted() {
51
+ return this.muted;
52
+ }
53
+ isConnected() {
54
+ return this.connected;
55
+ }
56
+ isHeld() {
57
+ return this.held;
58
+ }
59
+ constructor(activeUrl, webex, destination, direction, deviceId, lineId, deleteCb, indicator) {
60
+ super();
61
+ this.destination = destination;
62
+ this.direction = direction;
63
+ this.sdkConnector = SDKConnector;
64
+ this.deviceId = deviceId;
65
+ this.serviceIndicator = indicator;
66
+ this.lineId = lineId;
67
+ if (!this.sdkConnector.getWebex()) {
68
+ SDKConnector.setWebex(webex);
69
+ }
70
+ this.webex = this.sdkConnector.getWebex();
71
+ this.metricManager = getMetricManager(this.webex, this.serviceIndicator);
72
+ this.callId = `${DEFAULT_LOCAL_CALL_ID}_${uuid()}`;
73
+ this.correlationId = uuid();
74
+ this.deleteCb = deleteCb;
75
+ this.connected = false;
76
+ this.mediaInactivity = false;
77
+ this.held = false;
78
+ this.earlyMedia = false;
79
+ this.callerInfo = {};
80
+ this.localRoapMessage = {};
81
+ this.mobiusUrl = activeUrl;
82
+ this.receivedRoapOKSeq = 0;
83
+ this.mediaNegotiationCompleted = false;
84
+ log.info(`Webex Calling Url:- ${this.mobiusUrl}`, {
85
+ file: CALL_FILE,
86
+ method: 'constructor',
87
+ });
88
+ this.seq = INITIAL_SEQ_NUMBER;
89
+ this.callerId = createCallerId(webex, (callerInfo) => {
90
+ this.callerInfo = callerInfo;
91
+ const emitObj = {
92
+ correlationId: this.correlationId,
93
+ callerId: this.callerInfo,
94
+ };
95
+ this.emit(CALL_EVENT_KEYS.CALLER_ID, emitObj);
96
+ });
97
+ this.remoteRoapMessage = null;
98
+ this.disconnectReason = { code: DisconnectCode.NORMAL, cause: DisconnectCause.NORMAL };
99
+ const callMachine = createMachine({
100
+ schema: {
101
+ context: {},
102
+ events: {},
103
+ },
104
+ id: 'call-state',
105
+ initial: 'S_IDLE',
106
+ context: {},
107
+ states: {
108
+ S_IDLE: {
109
+ on: {
110
+ E_RECV_CALL_SETUP: {
111
+ target: 'S_RECV_CALL_SETUP',
112
+ actions: ['incomingCallSetup'],
113
+ },
114
+ E_SEND_CALL_SETUP: {
115
+ target: 'S_SEND_CALL_SETUP',
116
+ actions: ['outgoingCallSetup'],
117
+ },
118
+ E_RECV_CALL_DISCONNECT: {
119
+ target: 'S_RECV_CALL_DISCONNECT',
120
+ actions: ['incomingCallDisconnect'],
121
+ },
122
+ E_SEND_CALL_DISCONNECT: {
123
+ target: 'S_SEND_CALL_DISCONNECT',
124
+ actions: ['outgoingCallDisconnect'],
125
+ },
126
+ E_UNKNOWN: {
127
+ target: 'S_UNKNOWN',
128
+ actions: ['unknownState'],
129
+ },
130
+ },
131
+ },
132
+ S_RECV_CALL_SETUP: {
133
+ on: {
134
+ E_SEND_CALL_ALERTING: {
135
+ target: 'S_SEND_CALL_PROGRESS',
136
+ actions: ['outgoingCallAlerting'],
137
+ },
138
+ E_RECV_CALL_DISCONNECT: {
139
+ target: 'S_RECV_CALL_DISCONNECT',
140
+ actions: ['incomingCallDisconnect'],
141
+ },
142
+ E_SEND_CALL_DISCONNECT: {
143
+ target: 'S_SEND_CALL_DISCONNECT',
144
+ actions: ['outgoingCallDisconnect'],
145
+ },
146
+ E_UNKNOWN: {
147
+ target: 'S_UNKNOWN',
148
+ actions: ['unknownState'],
149
+ },
150
+ },
151
+ },
152
+ S_SEND_CALL_SETUP: {
153
+ on: {
154
+ E_RECV_CALL_PROGRESS: {
155
+ target: 'S_RECV_CALL_PROGRESS',
156
+ actions: ['incomingCallProgress'],
157
+ },
158
+ E_RECV_CALL_CONNECT: {
159
+ target: 'S_RECV_CALL_CONNECT',
160
+ actions: ['incomingCallConnect'],
161
+ },
162
+ E_RECV_CALL_DISCONNECT: {
163
+ target: 'S_RECV_CALL_DISCONNECT',
164
+ actions: ['incomingCallDisconnect'],
165
+ },
166
+ E_SEND_CALL_DISCONNECT: {
167
+ target: 'S_SEND_CALL_DISCONNECT',
168
+ actions: ['outgoingCallDisconnect'],
169
+ },
170
+ E_UNKNOWN: {
171
+ target: 'S_UNKNOWN',
172
+ actions: ['unknownState'],
173
+ },
174
+ },
175
+ },
176
+ S_RECV_CALL_PROGRESS: {
177
+ on: {
178
+ E_RECV_CALL_CONNECT: {
179
+ target: 'S_RECV_CALL_CONNECT',
180
+ actions: ['incomingCallConnect'],
181
+ },
182
+ E_RECV_CALL_DISCONNECT: {
183
+ target: 'S_RECV_CALL_DISCONNECT',
184
+ actions: ['incomingCallDisconnect'],
185
+ },
186
+ E_SEND_CALL_DISCONNECT: {
187
+ target: 'S_SEND_CALL_DISCONNECT',
188
+ actions: ['outgoingCallDisconnect'],
189
+ },
190
+ E_RECV_CALL_PROGRESS: {
191
+ target: 'S_RECV_CALL_PROGRESS',
192
+ actions: ['incomingCallProgress'],
193
+ },
194
+ E_UNKNOWN: {
195
+ target: 'S_UNKNOWN',
196
+ actions: ['unknownState'],
197
+ },
198
+ },
199
+ },
200
+ S_SEND_CALL_PROGRESS: {
201
+ on: {
202
+ E_SEND_CALL_CONNECT: {
203
+ target: 'S_SEND_CALL_CONNECT',
204
+ actions: ['outgoingCallConnect'],
205
+ },
206
+ E_RECV_CALL_DISCONNECT: {
207
+ target: 'S_RECV_CALL_DISCONNECT',
208
+ actions: ['incomingCallDisconnect'],
209
+ },
210
+ E_SEND_CALL_DISCONNECT: {
211
+ target: 'S_SEND_CALL_DISCONNECT',
212
+ actions: ['outgoingCallDisconnect'],
213
+ },
214
+ E_UNKNOWN: {
215
+ target: 'S_UNKNOWN',
216
+ actions: ['unknownState'],
217
+ },
218
+ },
219
+ },
220
+ S_RECV_CALL_CONNECT: {
221
+ on: {
222
+ E_CALL_ESTABLISHED: {
223
+ target: 'S_CALL_ESTABLISHED',
224
+ actions: ['callEstablished'],
225
+ },
226
+ E_RECV_CALL_DISCONNECT: {
227
+ target: 'S_RECV_CALL_DISCONNECT',
228
+ actions: ['incomingCallDisconnect'],
229
+ },
230
+ E_SEND_CALL_DISCONNECT: {
231
+ target: 'S_SEND_CALL_DISCONNECT',
232
+ actions: ['outgoingCallDisconnect'],
233
+ },
234
+ E_UNKNOWN: {
235
+ target: 'S_UNKNOWN',
236
+ actions: ['unknownState'],
237
+ },
238
+ },
239
+ },
240
+ S_SEND_CALL_CONNECT: {
241
+ on: {
242
+ E_CALL_ESTABLISHED: {
243
+ target: 'S_CALL_ESTABLISHED',
244
+ actions: ['callEstablished'],
245
+ },
246
+ E_RECV_CALL_DISCONNECT: {
247
+ target: 'S_RECV_CALL_DISCONNECT',
248
+ actions: ['incomingCallDisconnect'],
249
+ },
250
+ E_SEND_CALL_DISCONNECT: {
251
+ target: 'S_SEND_CALL_DISCONNECT',
252
+ actions: ['outgoingCallDisconnect'],
253
+ },
254
+ E_UNKNOWN: {
255
+ target: 'S_UNKNOWN',
256
+ actions: ['unknownState'],
257
+ },
258
+ },
259
+ },
260
+ S_CALL_HOLD: {
261
+ on: {
262
+ E_RECV_CALL_DISCONNECT: {
263
+ target: 'S_RECV_CALL_DISCONNECT',
264
+ actions: ['incomingCallDisconnect'],
265
+ },
266
+ E_SEND_CALL_DISCONNECT: {
267
+ target: 'S_SEND_CALL_DISCONNECT',
268
+ actions: ['outgoingCallDisconnect'],
269
+ },
270
+ E_CALL_ESTABLISHED: {
271
+ target: 'S_CALL_ESTABLISHED',
272
+ actions: ['callEstablished'],
273
+ },
274
+ E_UNKNOWN: {
275
+ target: 'S_UNKNOWN',
276
+ actions: ['unknownState'],
277
+ },
278
+ },
279
+ },
280
+ S_CALL_RESUME: {
281
+ on: {
282
+ E_RECV_CALL_DISCONNECT: {
283
+ target: 'S_RECV_CALL_DISCONNECT',
284
+ actions: ['incomingCallDisconnect'],
285
+ },
286
+ E_SEND_CALL_DISCONNECT: {
287
+ target: 'S_SEND_CALL_DISCONNECT',
288
+ actions: ['outgoingCallDisconnect'],
289
+ },
290
+ E_CALL_ESTABLISHED: {
291
+ target: 'S_CALL_ESTABLISHED',
292
+ actions: ['callEstablished'],
293
+ },
294
+ E_UNKNOWN: {
295
+ target: 'S_UNKNOWN',
296
+ actions: ['unknownState'],
297
+ },
298
+ },
299
+ },
300
+ S_CALL_ESTABLISHED: {
301
+ on: {
302
+ E_CALL_HOLD: {
303
+ target: 'S_CALL_HOLD',
304
+ actions: ['initiateCallHold'],
305
+ },
306
+ E_CALL_RESUME: {
307
+ target: 'S_CALL_RESUME',
308
+ actions: ['initiateCallResume'],
309
+ },
310
+ E_RECV_CALL_DISCONNECT: {
311
+ target: 'S_RECV_CALL_DISCONNECT',
312
+ actions: ['incomingCallDisconnect'],
313
+ },
314
+ E_SEND_CALL_DISCONNECT: {
315
+ target: 'S_SEND_CALL_DISCONNECT',
316
+ actions: ['outgoingCallDisconnect'],
317
+ },
318
+ E_CALL_ESTABLISHED: {
319
+ target: 'S_CALL_ESTABLISHED',
320
+ actions: ['callEstablished'],
321
+ },
322
+ E_UNKNOWN: {
323
+ target: 'S_UNKNOWN',
324
+ actions: ['unknownState'],
325
+ },
326
+ },
327
+ },
328
+ S_RECV_CALL_DISCONNECT: {
329
+ on: {
330
+ E_CALL_CLEARED: 'S_CALL_CLEARED',
331
+ },
332
+ },
333
+ S_SEND_CALL_DISCONNECT: {
334
+ on: {
335
+ E_CALL_CLEARED: 'S_CALL_CLEARED',
336
+ },
337
+ },
338
+ S_UNKNOWN: {
339
+ on: {
340
+ E_CALL_CLEARED: 'S_CALL_CLEARED',
341
+ },
342
+ },
343
+ S_ERROR: {
344
+ on: {
345
+ E_CALL_CLEARED: 'S_CALL_CLEARED',
346
+ },
347
+ },
348
+ S_CALL_CLEARED: {
349
+ type: 'final',
350
+ },
351
+ },
352
+ }, {
353
+ actions: {
354
+ incomingCallSetup: (context, event) => this.handleIncomingCallSetup(event),
355
+ outgoingCallSetup: (context, event) => this.handleOutgoingCallSetup(event),
356
+ incomingCallProgress: (context, event) => this.handleIncomingCallProgress(event),
357
+ outgoingCallAlerting: (context, event) => this.handleOutgoingCallAlerting(event),
358
+ incomingCallConnect: (context, event) => this.handleIncomingCallConnect(event),
359
+ outgoingCallConnect: (context, event) => this.handleOutgoingCallConnect(event),
360
+ callEstablished: (context, event) => this.handleCallEstablished(event),
361
+ initiateCallHold: (context, event) => this.handleCallHold(event),
362
+ initiateCallResume: (context, event) => this.handleCallResume(event),
363
+ incomingCallDisconnect: (context, event) => this.handleIncomingCallDisconnect(event),
364
+ outgoingCallDisconnect: (context, event) => this.handleOutgoingCallDisconnect(event),
365
+ unknownState: (context, event) => this.handleUnknownState(event),
366
+ },
367
+ });
368
+ const mediaMachine = createMachine({
369
+ schema: {
370
+ context: {},
371
+ events: {},
372
+ },
373
+ id: 'roap-state',
374
+ initial: 'S_ROAP_IDLE',
375
+ context: {},
376
+ states: {
377
+ S_ROAP_IDLE: {
378
+ on: {
379
+ E_RECV_ROAP_OFFER_REQUEST: {
380
+ target: 'S_RECV_ROAP_OFFER_REQUEST',
381
+ actions: ['incomingRoapOfferRequest'],
382
+ },
383
+ E_RECV_ROAP_OFFER: {
384
+ target: 'S_RECV_ROAP_OFFER',
385
+ actions: ['incomingRoapOffer'],
386
+ },
387
+ E_SEND_ROAP_OFFER: {
388
+ target: 'S_SEND_ROAP_OFFER',
389
+ actions: ['outgoingRoapOffer'],
390
+ },
391
+ },
392
+ },
393
+ S_RECV_ROAP_OFFER_REQUEST: {
394
+ on: {
395
+ E_SEND_ROAP_OFFER: {
396
+ target: 'S_SEND_ROAP_OFFER',
397
+ actions: ['outgoingRoapOffer'],
398
+ },
399
+ E_ROAP_OK: {
400
+ target: 'S_ROAP_OK',
401
+ actions: ['roapEstablished'],
402
+ },
403
+ E_ROAP_ERROR: {
404
+ target: 'S_ROAP_ERROR',
405
+ actions: ['roapError'],
406
+ },
407
+ },
408
+ },
409
+ S_RECV_ROAP_OFFER: {
410
+ on: {
411
+ E_SEND_ROAP_ANSWER: {
412
+ target: 'S_SEND_ROAP_ANSWER',
413
+ actions: ['outgoingRoapAnswer'],
414
+ },
415
+ E_ROAP_OK: {
416
+ target: 'S_ROAP_OK',
417
+ actions: ['roapEstablished'],
418
+ },
419
+ E_ROAP_ERROR: {
420
+ target: 'S_ROAP_ERROR',
421
+ actions: ['roapError'],
422
+ },
423
+ },
424
+ },
425
+ S_SEND_ROAP_OFFER: {
426
+ on: {
427
+ E_RECV_ROAP_ANSWER: {
428
+ target: 'S_RECV_ROAP_ANSWER',
429
+ actions: ['incomingRoapAnswer'],
430
+ },
431
+ E_SEND_ROAP_ANSWER: {
432
+ target: 'S_SEND_ROAP_ANSWER',
433
+ actions: ['outgoingRoapAnswer'],
434
+ },
435
+ E_SEND_ROAP_OFFER: {
436
+ target: 'S_SEND_ROAP_OFFER',
437
+ actions: ['outgoingRoapOffer'],
438
+ },
439
+ E_ROAP_ERROR: {
440
+ target: 'S_ROAP_ERROR',
441
+ actions: ['roapError'],
442
+ },
443
+ },
444
+ },
445
+ S_RECV_ROAP_ANSWER: {
446
+ on: {
447
+ E_ROAP_OK: {
448
+ target: 'S_ROAP_OK',
449
+ actions: ['roapEstablished'],
450
+ },
451
+ E_ROAP_ERROR: {
452
+ target: 'S_ROAP_ERROR',
453
+ actions: ['roapError'],
454
+ },
455
+ },
456
+ },
457
+ S_SEND_ROAP_ANSWER: {
458
+ on: {
459
+ E_RECV_ROAP_OFFER_REQUEST: {
460
+ target: 'S_RECV_ROAP_OFFER_REQUEST',
461
+ actions: ['incomingRoapOfferRequest'],
462
+ },
463
+ E_RECV_ROAP_OFFER: {
464
+ target: 'S_RECV_ROAP_OFFER',
465
+ actions: ['incomingRoapOffer'],
466
+ },
467
+ E_ROAP_OK: {
468
+ target: 'S_ROAP_OK',
469
+ actions: ['roapEstablished'],
470
+ },
471
+ E_SEND_ROAP_ANSWER: {
472
+ target: 'S_SEND_ROAP_ANSWER',
473
+ actions: ['outgoingRoapAnswer'],
474
+ },
475
+ E_ROAP_ERROR: {
476
+ target: 'S_ROAP_ERROR',
477
+ actions: ['roapError'],
478
+ },
479
+ },
480
+ },
481
+ S_ROAP_OK: {
482
+ on: {
483
+ E_RECV_ROAP_OFFER_REQUEST: {
484
+ target: 'S_RECV_ROAP_OFFER_REQUEST',
485
+ actions: ['incomingRoapOfferRequest'],
486
+ },
487
+ E_RECV_ROAP_OFFER: {
488
+ target: 'S_RECV_ROAP_OFFER',
489
+ actions: ['incomingRoapOffer'],
490
+ },
491
+ E_ROAP_OK: {
492
+ target: 'S_ROAP_OK',
493
+ actions: ['roapEstablished'],
494
+ },
495
+ E_SEND_ROAP_OFFER: {
496
+ target: 'S_SEND_ROAP_OFFER',
497
+ actions: ['outgoingRoapOffer'],
498
+ },
499
+ E_ROAP_ERROR: {
500
+ target: 'S_ROAP_ERROR',
501
+ actions: ['roapError'],
502
+ },
503
+ E_ROAP_TEARDOWN: {
504
+ target: 'S_ROAP_TEARDOWN',
505
+ },
506
+ },
507
+ },
508
+ S_ROAP_ERROR: {
509
+ on: {
510
+ E_ROAP_TEARDOWN: {
511
+ target: 'S_ROAP_TEARDOWN',
512
+ },
513
+ E_RECV_ROAP_OFFER_REQUEST: {
514
+ target: 'S_RECV_ROAP_OFFER_REQUEST',
515
+ actions: ['incomingRoapOfferRequest'],
516
+ },
517
+ E_RECV_ROAP_OFFER: {
518
+ target: 'S_RECV_ROAP_OFFER',
519
+ actions: ['incomingRoapOffer'],
520
+ },
521
+ E_RECV_ROAP_ANSWER: {
522
+ target: 'S_RECV_ROAP_ANSWER',
523
+ actions: ['incomingRoapAnswer'],
524
+ },
525
+ E_ROAP_OK: {
526
+ target: 'S_ROAP_OK',
527
+ actions: ['roapEstablished'],
528
+ },
529
+ },
530
+ },
531
+ S_ROAP_TEARDOWN: {
532
+ type: 'final',
533
+ },
534
+ },
535
+ }, {
536
+ actions: {
537
+ incomingRoapOffer: (context, event) => this.handleIncomingRoapOffer(context, event),
538
+ incomingRoapAnswer: (context, event) => this.handleIncomingRoapAnswer(context, event),
539
+ incomingRoapOfferRequest: (context, event) => this.handleIncomingRoapOfferRequest(context, event),
540
+ outgoingRoapOffer: (context, event) => this.handleOutgoingRoapOffer(context, event),
541
+ outgoingRoapAnswer: (context, event) => this.handleOutgoingRoapAnswer(context, event),
542
+ roapEstablished: (context, event) => this.handleRoapEstablished(context, event),
543
+ roapError: (context, event) => this.handleRoapError(context, event),
544
+ },
545
+ });
546
+ this.callStateMachine = interpret(callMachine)
547
+ .onTransition((state, event) => {
548
+ log.log(`Call StateMachine:- state=${state.value}, event=${JSON.stringify(event.type)}`, {});
549
+ if (state.value !== 'S_UNKNOWN') {
550
+ this.metricManager.submitCallMetric(METRIC_EVENT.CALL, state.value.toString(), METRIC_TYPE.BEHAVIORAL, this.callId, this.correlationId, undefined);
551
+ }
552
+ })
553
+ .start();
554
+ this.mediaStateMachine = interpret(mediaMachine)
555
+ .onTransition((state, event) => {
556
+ log.log(`Media StateMachine:- state=${state.value}, event=${JSON.stringify(event.type)}`, {});
557
+ if (state.value !== 'S_ROAP_ERROR') {
558
+ this.metricManager.submitMediaMetric(METRIC_EVENT.MEDIA, state.value.toString(), METRIC_TYPE.BEHAVIORAL, this.callId, this.correlationId, this.localRoapMessage.sdp, this.remoteRoapMessage?.sdp, undefined);
559
+ }
560
+ })
561
+ .start();
562
+ this.muted = false;
563
+ }
564
+ handleIncomingCallSetup(event) {
565
+ log.info(`handleIncomingCallSetup: ${this.getCorrelationId()} `, {
566
+ file: CALL_FILE,
567
+ method: this.handleIncomingCallSetup.name,
568
+ });
569
+ this.sendCallStateMachineEvt({ type: 'E_SEND_CALL_ALERTING' });
570
+ }
571
+ async handleOutgoingCallSetup(event) {
572
+ log.info(`handleOutgoingCallSetup: ${this.getCorrelationId()} `, {
573
+ file: CALL_FILE,
574
+ method: this.handleOutgoingCallSetup.name,
575
+ });
576
+ const message = event.data;
577
+ try {
578
+ const response = await this.post(message);
579
+ log.log(`handleOutgoingCallSetup: Response code: ${response.statusCode}`, {
580
+ file: CALL_FILE,
581
+ method: this.handleOutgoingCallSetup.name,
582
+ });
583
+ this.setCallId(response.body.callId);
584
+ }
585
+ catch (e) {
586
+ log.warn('Failed to setup the call', {
587
+ file: CALL_FILE,
588
+ method: this.handleOutgoingCallSetup.name,
589
+ });
590
+ const errData = e;
591
+ handleCallErrors((error) => {
592
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
593
+ this.submitCallErrorMetric(error);
594
+ this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: errData });
595
+ }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallSetup.name, CALL_FILE);
596
+ }
597
+ }
598
+ async handleCallHold(event) {
599
+ log.info(`handleCallHold: ${this.getCorrelationId()} `, {
600
+ file: CALL_FILE,
601
+ method: this.handleCallHold.name,
602
+ });
603
+ try {
604
+ const response = await this.postSSRequest(undefined, SUPPLEMENTARY_SERVICES.HOLD);
605
+ log.log(`Response code: ${response.statusCode}`, {
606
+ file: CALL_FILE,
607
+ method: this.handleCallHold.name,
608
+ });
609
+ if (this.isHeld() === false) {
610
+ this.supplementaryServicesTimer = setTimeout(async () => {
611
+ const errorContext = { file: CALL_FILE, method: this.handleCallHold.name };
612
+ log.warn('Hold response timed out', {
613
+ file: CALL_FILE,
614
+ method: this.handleCallHold.name,
615
+ });
616
+ const callError = createCallError('An error occurred while placing the call on hold. Wait a moment and try again.', errorContext, ERROR_TYPE.TIMEOUT, this.getCorrelationId(), ERROR_LAYER.CALL_CONTROL);
617
+ this.emit(CALL_EVENT_KEYS.HOLD_ERROR, callError);
618
+ this.submitCallErrorMetric(callError);
619
+ }, SUPPLEMENTARY_SERVICES_TIMEOUT);
620
+ }
621
+ }
622
+ catch (e) {
623
+ log.warn('Failed to put the call on hold', {
624
+ file: CALL_FILE,
625
+ method: this.handleCallHold.name,
626
+ });
627
+ const errData = e;
628
+ handleCallErrors((error) => {
629
+ this.emit(CALL_EVENT_KEYS.HOLD_ERROR, error);
630
+ this.submitCallErrorMetric(error);
631
+ this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
632
+ }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallSetup.name, CALL_FILE);
633
+ }
634
+ }
635
+ async handleCallResume(event) {
636
+ log.info(`handleCallResume: ${this.getCorrelationId()} `, {
637
+ file: CALL_FILE,
638
+ method: this.handleCallResume.name,
639
+ });
640
+ try {
641
+ const response = await this.postSSRequest(undefined, SUPPLEMENTARY_SERVICES.RESUME);
642
+ log.log(`Response code: ${response.statusCode}`, {
643
+ file: CALL_FILE,
644
+ method: this.handleCallResume.name,
645
+ });
646
+ if (this.isHeld() === true) {
647
+ this.supplementaryServicesTimer = setTimeout(async () => {
648
+ const errorContext = { file: CALL_FILE, method: this.handleCallResume.name };
649
+ log.warn('Resume response timed out', {
650
+ file: CALL_FILE,
651
+ method: this.handleCallResume.name,
652
+ });
653
+ const callError = createCallError('An error occurred while resuming the call. Wait a moment and try again.', errorContext, ERROR_TYPE.TIMEOUT, this.getCorrelationId(), ERROR_LAYER.CALL_CONTROL);
654
+ this.emit(CALL_EVENT_KEYS.RESUME_ERROR, callError);
655
+ this.submitCallErrorMetric(callError);
656
+ }, SUPPLEMENTARY_SERVICES_TIMEOUT);
657
+ }
658
+ }
659
+ catch (e) {
660
+ log.warn('Failed to resume the call', {
661
+ file: CALL_FILE,
662
+ method: this.handleCallResume.name,
663
+ });
664
+ const errData = e;
665
+ handleCallErrors((error) => {
666
+ this.emit(CALL_EVENT_KEYS.RESUME_ERROR, error);
667
+ this.submitCallErrorMetric(error);
668
+ this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
669
+ }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallSetup.name, CALL_FILE);
670
+ }
671
+ }
672
+ handleIncomingCallProgress(event) {
673
+ log.info(`handleIncomingCallProgress: ${this.getCorrelationId()} `, {
674
+ file: CALL_FILE,
675
+ method: this.handleIncomingCallProgress.name,
676
+ });
677
+ const data = event.data;
678
+ if (data?.callProgressData?.inbandMedia) {
679
+ log.log('Inband media present. Setting Early Media flag', {
680
+ file: CALL_FILE,
681
+ method: this.handleIncomingCallProgress.name,
682
+ });
683
+ this.earlyMedia = true;
684
+ }
685
+ else {
686
+ log.log('Inband media not present.', {
687
+ file: CALL_FILE,
688
+ method: this.handleIncomingCallProgress.name,
689
+ });
690
+ }
691
+ if (data?.callerId) {
692
+ log.info('Processing Caller-Id data', {
693
+ file: CALL_FILE,
694
+ method: this.handleIncomingCallProgress.name,
695
+ });
696
+ this.startCallerIdResolution(data.callerId);
697
+ }
698
+ this.emit(CALL_EVENT_KEYS.PROGRESS, this.correlationId);
699
+ }
700
+ handleIncomingRoapOfferRequest(context, event) {
701
+ log.info(`handleIncomingRoapOfferRequest: ${this.getCorrelationId()} `, {
702
+ file: CALL_FILE,
703
+ method: this.handleIncomingRoapOfferRequest.name,
704
+ });
705
+ const message = event.data;
706
+ if (!this.mediaConnection) {
707
+ log.info('Media connection is not up, buffer the remote Offer Request for later handling', {
708
+ file: CALL_FILE,
709
+ method: this.handleIncomingRoapOfferRequest.name,
710
+ });
711
+ this.seq = message.seq;
712
+ log.info(`Setting Sequence No: ${this.seq}`, {
713
+ file: CALL_FILE,
714
+ method: this.handleIncomingRoapOfferRequest.name,
715
+ });
716
+ this.remoteRoapMessage = message;
717
+ }
718
+ else if (this.receivedRoapOKSeq === message.seq - 2) {
719
+ log.info('Waiting for Roap OK, buffer the remote Offer Request for later handling', {
720
+ file: CALL_FILE,
721
+ method: this.handleIncomingRoapOfferRequest.name,
722
+ });
723
+ this.remoteRoapMessage = message;
724
+ }
725
+ else {
726
+ message.seq = this.seq + 1;
727
+ this.seq = message.seq;
728
+ this.mediaConnection.roapMessageReceived(message);
729
+ }
730
+ }
731
+ async handleOutgoingCallAlerting(event) {
732
+ log.info(`handleOutgoingCallAlerting: ${this.getCorrelationId()} `, {
733
+ file: CALL_FILE,
734
+ method: this.handleOutgoingCallAlerting.name,
735
+ });
736
+ try {
737
+ const res = await this.patch(MobiusCallState.ALERTING);
738
+ log.log(`PATCH response: ${res.statusCode}`, {
739
+ file: CALL_FILE,
740
+ method: this.handleOutgoingCallAlerting.name,
741
+ });
742
+ }
743
+ catch (err) {
744
+ log.warn('Failed to signal call progression', {
745
+ file: CALL_FILE,
746
+ method: this.handleOutgoingCallAlerting.name,
747
+ });
748
+ const errData = err;
749
+ handleCallErrors((error) => {
750
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
751
+ this.submitCallErrorMetric(error);
752
+ this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: errData });
753
+ }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallAlerting.name, CALL_FILE);
754
+ }
755
+ }
756
+ handleIncomingCallConnect(event) {
757
+ log.info(`handleIncomingCallConnect: ${this.getCorrelationId()} `, {
758
+ file: CALL_FILE,
759
+ method: this.handleIncomingCallConnect.name,
760
+ });
761
+ this.emit(CALL_EVENT_KEYS.CONNECT, this.correlationId);
762
+ if (this.earlyMedia || this.mediaNegotiationCompleted) {
763
+ this.mediaNegotiationCompleted = false;
764
+ this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED' });
765
+ }
766
+ }
767
+ async handleOutgoingCallConnect(event) {
768
+ log.info(`handleOutgoingCallConnect: ${this.getCorrelationId()} `, {
769
+ file: CALL_FILE,
770
+ method: this.handleOutgoingCallConnect.name,
771
+ });
772
+ if (!this.remoteRoapMessage) {
773
+ log.warn('Offer not yet received from remote end... Exiting', {
774
+ file: CALL_FILE,
775
+ method: this.handleOutgoingCallConnect.name,
776
+ });
777
+ return;
778
+ }
779
+ try {
780
+ this.mediaConnection.roapMessageReceived(this.remoteRoapMessage);
781
+ const res = await this.patch(MobiusCallState.CONNECTED);
782
+ log.log(`PATCH response: ${res.statusCode}`, {
783
+ file: CALL_FILE,
784
+ method: this.handleOutgoingCallConnect.name,
785
+ });
786
+ }
787
+ catch (err) {
788
+ log.warn('Failed to connect the call', {
789
+ file: CALL_FILE,
790
+ method: this.handleOutgoingCallConnect.name,
791
+ });
792
+ const errData = err;
793
+ handleCallErrors((error) => {
794
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
795
+ this.submitCallErrorMetric(error);
796
+ this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: errData });
797
+ }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallConnect.name, CALL_FILE);
798
+ }
799
+ }
800
+ async handleIncomingCallDisconnect(event) {
801
+ log.info(`handleIncomingCallDisconnect: ${this.getCorrelationId()} `, {
802
+ file: CALL_FILE,
803
+ method: this.handleIncomingCallDisconnect.name,
804
+ });
805
+ this.setDisconnectReason();
806
+ try {
807
+ const response = await this.delete();
808
+ log.log(`handleOutgoingCallDisconnect: Response code: ${response.statusCode}`, {
809
+ file: CALL_FILE,
810
+ method: this.handleIncomingCallDisconnect.name,
811
+ });
812
+ }
813
+ catch (e) {
814
+ log.warn('Failed to delete the call', {
815
+ file: CALL_FILE,
816
+ method: this.handleIncomingCallDisconnect.name,
817
+ });
818
+ }
819
+ this.deleteCb(this.correlationId);
820
+ if (this.sessionTimer) {
821
+ clearInterval(this.sessionTimer);
822
+ }
823
+ if (this.mediaConnection) {
824
+ this.mediaConnection.close();
825
+ log.info('Closing media channel', { file: CALL_FILE, method: 'handleIncomingCallDisconnect' });
826
+ }
827
+ this.sendMediaStateMachineEvt({ type: 'E_ROAP_TEARDOWN' });
828
+ this.sendCallStateMachineEvt({ type: 'E_CALL_CLEARED' });
829
+ this.emit(CALL_EVENT_KEYS.DISCONNECT, this.correlationId);
830
+ }
831
+ async handleOutgoingCallDisconnect(event) {
832
+ this.setDisconnectReason();
833
+ try {
834
+ const response = await this.delete();
835
+ log.log(`handleOutgoingCallDisconnect: Response code: ${response.statusCode}`, {
836
+ file: CALL_FILE,
837
+ method: this.handleOutgoingCallDisconnect.name,
838
+ });
839
+ }
840
+ catch (e) {
841
+ log.warn('Failed to delete the call', {
842
+ file: CALL_FILE,
843
+ method: this.handleOutgoingCallDisconnect.name,
844
+ });
845
+ }
846
+ this.deleteCb(this.correlationId);
847
+ if (this.sessionTimer) {
848
+ clearInterval(this.sessionTimer);
849
+ }
850
+ if (this.mediaConnection) {
851
+ this.mediaConnection.close();
852
+ log.info('Closing media channel', { file: CALL_FILE, method: 'handleOutgoingCallDisconnect' });
853
+ }
854
+ this.sendMediaStateMachineEvt({ type: 'E_ROAP_TEARDOWN' });
855
+ this.sendCallStateMachineEvt({ type: 'E_CALL_CLEARED' });
856
+ }
857
+ handleCallEstablished(event) {
858
+ log.info(`handleCallEstablished: ${this.getCorrelationId()} `, {
859
+ file: CALL_FILE,
860
+ method: this.handleCallEstablished.name,
861
+ });
862
+ this.emit(CALL_EVENT_KEYS.ESTABLISHED, this.correlationId);
863
+ this.earlyMedia = false;
864
+ this.connected = true;
865
+ if (this.sessionTimer) {
866
+ log.log('Resetting session timer', {
867
+ file: CALL_FILE,
868
+ method: 'handleCallEstablished',
869
+ });
870
+ clearInterval(this.sessionTimer);
871
+ }
872
+ this.sessionTimer = setInterval(async () => {
873
+ try {
874
+ const res = await this.postStatus();
875
+ log.info(`Session refresh successful`, {
876
+ file: CALL_FILE,
877
+ method: 'handleCallEstablished',
878
+ });
879
+ }
880
+ catch (err) {
881
+ const error = err;
882
+ if (this.sessionTimer) {
883
+ clearInterval(this.sessionTimer);
884
+ }
885
+ handleCallErrors((callError) => {
886
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, callError);
887
+ this.submitCallErrorMetric(callError);
888
+ }, ERROR_LAYER.CALL_CONTROL, (interval) => {
889
+ setTimeout(() => {
890
+ this.postStatus();
891
+ this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED' });
892
+ }, interval * 1000);
893
+ }, this.getCorrelationId(), error, this.handleCallEstablished.name, CALL_FILE);
894
+ }
895
+ }, DEFAULT_SESSION_TIMER);
896
+ }
897
+ async handleUnknownState(event) {
898
+ log.info(`handleUnknownState: ${this.getCorrelationId()} `, {
899
+ file: CALL_FILE,
900
+ method: this.handleUnknownState.name,
901
+ });
902
+ const eventData = event.data;
903
+ if (!eventData?.media) {
904
+ log.warn('Call failed due to signalling issue', {
905
+ file: CALL_FILE,
906
+ method: this.handleUnknownState.name,
907
+ });
908
+ }
909
+ try {
910
+ this.setDisconnectReason();
911
+ const response = await this.delete();
912
+ log.log(`handleOutgoingCallDisconnect: Response code: ${response.statusCode}`, {
913
+ file: CALL_FILE,
914
+ method: this.handleUnknownState.name,
915
+ });
916
+ }
917
+ catch (e) {
918
+ log.warn('Failed to delete the call', {
919
+ file: CALL_FILE,
920
+ method: this.handleUnknownState.name,
921
+ });
922
+ }
923
+ this.deleteCb(this.correlationId);
924
+ if (this.sessionTimer) {
925
+ clearInterval(this.sessionTimer);
926
+ }
927
+ if (this.mediaConnection) {
928
+ this.mediaConnection.close();
929
+ log.info('Closing media channel', {
930
+ file: CALL_FILE,
931
+ method: this.handleUnknownState.name,
932
+ });
933
+ }
934
+ this.sendMediaStateMachineEvt({ type: 'E_ROAP_TEARDOWN' });
935
+ this.sendCallStateMachineEvt({ type: 'E_CALL_CLEARED' });
936
+ }
937
+ getEmitterCallback(errData) {
938
+ return (error) => {
939
+ switch (this.callStateMachine.state.value) {
940
+ case 'S_CALL_HOLD':
941
+ this.emit(CALL_EVENT_KEYS.HOLD_ERROR, error);
942
+ if (this.supplementaryServicesTimer) {
943
+ clearTimeout(this.supplementaryServicesTimer);
944
+ this.supplementaryServicesTimer = undefined;
945
+ }
946
+ this.submitCallErrorMetric(error);
947
+ this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
948
+ return;
949
+ case 'S_CALL_RESUME':
950
+ this.emit(CALL_EVENT_KEYS.RESUME_ERROR, error);
951
+ this.submitCallErrorMetric(error);
952
+ this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
953
+ return;
954
+ default:
955
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
956
+ this.submitCallErrorMetric(error);
957
+ if (!this.connected) {
958
+ this.sendMediaStateMachineEvt({ type: 'E_ROAP_ERROR', data: errData });
959
+ }
960
+ }
961
+ };
962
+ }
963
+ async handleRoapEstablished(context, event) {
964
+ log.info(`handleRoapEstablished: ${this.getCorrelationId()} `, {
965
+ file: CALL_FILE,
966
+ method: 'handleRoapEstablished',
967
+ });
968
+ const { received, message } = event.data;
969
+ this.receivedRoapOKSeq = message.seq;
970
+ if (!received) {
971
+ log.info('Sending Media Ok to the remote End', {
972
+ file: CALL_FILE,
973
+ method: 'handleRoapEstablished',
974
+ });
975
+ try {
976
+ if (this.callStateMachine.state.value === 'S_RECV_CALL_PROGRESS' ||
977
+ this.callStateMachine.state.value === 'S_SEND_CALL_SETUP') {
978
+ log.info('Media negotiation completed before call connect. Setting media negotiation completed flag.', {
979
+ file: CALL_FILE,
980
+ method: 'handleRoapEstablished',
981
+ });
982
+ this.mediaNegotiationCompleted = true;
983
+ }
984
+ message.seq = this.seq;
985
+ const res = await this.postMedia(message);
986
+ log.log(`handleRoapEstablished: Response code: ${res.statusCode}`, {
987
+ file: CALL_FILE,
988
+ method: 'handleRoapEstablished',
989
+ });
990
+ if (!this.earlyMedia && !this.mediaNegotiationCompleted) {
991
+ this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED' });
992
+ }
993
+ }
994
+ catch (err) {
995
+ log.warn('Failed to process MediaOk request', {
996
+ file: CALL_FILE,
997
+ method: 'handleRoapEstablished',
998
+ });
999
+ const errData = err;
1000
+ handleCallErrors(this.getEmitterCallback(errData), ERROR_LAYER.MEDIA, (interval) => {
1001
+ if (this.connected) {
1002
+ setTimeout(() => {
1003
+ this.sendMediaStateMachineEvt({ type: 'E_ROAP_OK', data: event.data });
1004
+ }, interval * 1000);
1005
+ }
1006
+ }, this.getCorrelationId(), errData, this.handleRoapEstablished.name, CALL_FILE);
1007
+ }
1008
+ }
1009
+ else {
1010
+ log.info('Notifying internal-media-core about ROAP OK message', {
1011
+ file: CALL_FILE,
1012
+ method: 'handleRoapEstablished',
1013
+ });
1014
+ message.seq = this.seq;
1015
+ if (this.mediaConnection) {
1016
+ this.mediaConnection.roapMessageReceived(message);
1017
+ }
1018
+ if (!this.earlyMedia) {
1019
+ this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED' });
1020
+ }
1021
+ if (this.remoteRoapMessage && this.remoteRoapMessage.seq > this.seq) {
1022
+ if (this.remoteRoapMessage.messageType === 'OFFER_REQUEST') {
1023
+ this.sendMediaStateMachineEvt({
1024
+ type: 'E_RECV_ROAP_OFFER_REQUEST',
1025
+ data: this.remoteRoapMessage,
1026
+ });
1027
+ }
1028
+ else if (this.remoteRoapMessage.messageType === 'OFFER') {
1029
+ this.sendMediaStateMachineEvt({ type: 'E_RECV_ROAP_OFFER', data: this.remoteRoapMessage });
1030
+ }
1031
+ }
1032
+ }
1033
+ }
1034
+ async handleRoapError(context, event) {
1035
+ log.info(`handleRoapError: ${this.getCorrelationId()}`, {
1036
+ file: CALL_FILE,
1037
+ method: this.handleRoapError.name,
1038
+ });
1039
+ const message = event.data;
1040
+ if (message) {
1041
+ try {
1042
+ const res = await this.postMedia(message);
1043
+ log.info(`Response code: ${res.statusCode}`, {
1044
+ file: CALL_FILE,
1045
+ method: this.handleRoapError.name,
1046
+ });
1047
+ }
1048
+ catch (err) {
1049
+ log.warn('Failed to communicate ROAP error to Webex Calling', {
1050
+ file: CALL_FILE,
1051
+ method: this.handleRoapError.name,
1052
+ });
1053
+ const errData = err;
1054
+ handleCallErrors((error) => {
1055
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
1056
+ this.submitCallErrorMetric(error);
1057
+ }, ERROR_LAYER.MEDIA, (interval) => undefined, this.getCorrelationId(), errData, this.handleRoapError.name, CALL_FILE);
1058
+ }
1059
+ }
1060
+ if (!this.connected) {
1061
+ log.warn('Call failed due to media issue', {
1062
+ file: CALL_FILE,
1063
+ method: 'handleRoapError',
1064
+ });
1065
+ this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: { media: true } });
1066
+ }
1067
+ }
1068
+ async handleOutgoingRoapOffer(context, event) {
1069
+ log.info(`handleOutgoingRoapOffer: ${this.getCorrelationId()}`, {
1070
+ file: CALL_FILE,
1071
+ method: this.handleOutgoingRoapOffer.name,
1072
+ });
1073
+ const message = event.data;
1074
+ if (!message?.sdp) {
1075
+ log.info('Initializing Offer...', {
1076
+ file: CALL_FILE,
1077
+ method: this.handleOutgoingRoapOffer.name,
1078
+ });
1079
+ this.mediaConnection.initiateOffer();
1080
+ return;
1081
+ }
1082
+ try {
1083
+ const res = await this.postMedia(message);
1084
+ log.log(`handleOutgoingRoapOffer: Response code: ${res.statusCode}`, {
1085
+ file: CALL_FILE,
1086
+ method: this.handleOutgoingRoapOffer.name,
1087
+ });
1088
+ }
1089
+ catch (err) {
1090
+ log.warn('Failed to process MediaOk request', {
1091
+ file: CALL_FILE,
1092
+ method: this.handleOutgoingRoapOffer.name,
1093
+ });
1094
+ const errData = err;
1095
+ handleCallErrors(this.getEmitterCallback(errData), ERROR_LAYER.MEDIA, (interval) => {
1096
+ if (this.connected) {
1097
+ setTimeout(() => {
1098
+ this.sendMediaStateMachineEvt({ type: 'E_SEND_ROAP_OFFER', data: event.data });
1099
+ }, interval * 1000);
1100
+ }
1101
+ }, this.getCorrelationId(), errData, this.handleOutgoingRoapOffer.name, CALL_FILE);
1102
+ }
1103
+ }
1104
+ async handleOutgoingRoapAnswer(context, event) {
1105
+ log.info(`handleOutgoingRoapAnswer: ${this.getCorrelationId()}`, {
1106
+ file: CALL_FILE,
1107
+ method: this.handleOutgoingRoapAnswer.name,
1108
+ });
1109
+ const message = event.data;
1110
+ try {
1111
+ message.seq = this.seq;
1112
+ const res = await this.postMedia(message);
1113
+ log.log(`handleOutgoingRoapAnswer: Response code: ${res.statusCode}`, {
1114
+ file: CALL_FILE,
1115
+ method: this.handleOutgoingRoapAnswer.name,
1116
+ });
1117
+ }
1118
+ catch (err) {
1119
+ log.warn('Failed to send MediaAnswer request', {
1120
+ file: CALL_FILE,
1121
+ method: this.handleOutgoingRoapAnswer.name,
1122
+ });
1123
+ const errData = err;
1124
+ handleCallErrors(this.getEmitterCallback(errData), ERROR_LAYER.MEDIA, (interval) => {
1125
+ if (this.connected) {
1126
+ setTimeout(() => {
1127
+ this.sendMediaStateMachineEvt({ type: 'E_SEND_ROAP_ANSWER', data: event.data });
1128
+ }, interval * 1000);
1129
+ }
1130
+ }, this.getCorrelationId(), errData, this.handleOutgoingRoapAnswer.name, CALL_FILE);
1131
+ }
1132
+ }
1133
+ handleIncomingRoapOffer(context, event) {
1134
+ log.info(`handleIncomingRoapOffer: ${this.getCorrelationId()}`, {
1135
+ file: CALL_FILE,
1136
+ method: this.handleIncomingRoapOffer.name,
1137
+ });
1138
+ const message = event.data;
1139
+ this.remoteRoapMessage = message;
1140
+ if (!this.mediaConnection) {
1141
+ log.info('Media connection is not up, buffer the remote offer for later handling', {
1142
+ file: CALL_FILE,
1143
+ method: this.handleIncomingRoapOffer.name,
1144
+ });
1145
+ this.seq = message.seq;
1146
+ log.info(`Setting Sequence No: ${this.seq}`, {
1147
+ file: CALL_FILE,
1148
+ method: this.handleIncomingRoapOffer.name,
1149
+ });
1150
+ }
1151
+ else if (this.receivedRoapOKSeq === message.seq - 2) {
1152
+ log.info('Waiting for Roap OK, buffer the remote offer for later handling', {
1153
+ file: CALL_FILE,
1154
+ method: this.handleIncomingRoapOffer.name,
1155
+ });
1156
+ this.remoteRoapMessage = message;
1157
+ }
1158
+ else {
1159
+ log.info('Handling new offer...', {
1160
+ file: CALL_FILE,
1161
+ method: this.handleIncomingRoapOffer.name,
1162
+ });
1163
+ this.seq = message.seq;
1164
+ if (this.mediaConnection) {
1165
+ this.mediaConnection.roapMessageReceived(message);
1166
+ }
1167
+ }
1168
+ }
1169
+ handleIncomingRoapAnswer(context, event) {
1170
+ log.info(`handleIncomingRoapAnswer: ${this.getCorrelationId()}`, {
1171
+ file: CALL_FILE,
1172
+ method: this.handleIncomingRoapAnswer.name,
1173
+ });
1174
+ const message = event.data;
1175
+ this.remoteRoapMessage = message;
1176
+ message.seq = this.seq;
1177
+ if (this.mediaConnection) {
1178
+ this.mediaConnection.roapMessageReceived(message);
1179
+ }
1180
+ }
1181
+ initMediaConnection(localAudioTrack, debugId) {
1182
+ const mediaConnection = new RoapMediaConnection({
1183
+ skipInactiveTransceivers: true,
1184
+ iceServers: [],
1185
+ sdpMunging: {
1186
+ convertPort9to0: true,
1187
+ addContentSlides: false,
1188
+ },
1189
+ }, {
1190
+ localTracks: { audio: localAudioTrack },
1191
+ direction: {
1192
+ audio: 'sendrecv',
1193
+ video: 'inactive',
1194
+ screenShareVideo: 'inactive',
1195
+ },
1196
+ }, debugId || `WebexCallSDK-${this.correlationId}`);
1197
+ this.mediaConnection = mediaConnection;
1198
+ }
1199
+ getDirection = () => this.direction;
1200
+ getCallId = () => this.callId;
1201
+ getCorrelationId = () => this.correlationId;
1202
+ sendCallStateMachineEvt(event) {
1203
+ this.callStateMachine.send(event);
1204
+ }
1205
+ sendMediaStateMachineEvt(event) {
1206
+ this.mediaStateMachine.send(event);
1207
+ }
1208
+ setCallId = (callId) => {
1209
+ this.callId = callId;
1210
+ log.info(`Setting callId : ${this.callId} for correlationId: ${this.correlationId}`, {
1211
+ file: CALL_FILE,
1212
+ method: this.setCallId.name,
1213
+ });
1214
+ };
1215
+ setDisconnectReason() {
1216
+ if (this.mediaInactivity) {
1217
+ this.disconnectReason.code = DisconnectCode.MEDIA_INACTIVITY;
1218
+ this.disconnectReason.cause = DisconnectCause.MEDIA_INACTIVITY;
1219
+ }
1220
+ else if (this.connected || this.direction === CallDirection.OUTBOUND) {
1221
+ this.disconnectReason.code = DisconnectCode.NORMAL;
1222
+ this.disconnectReason.cause = DisconnectCause.NORMAL;
1223
+ }
1224
+ else {
1225
+ this.disconnectReason.code = DisconnectCode.BUSY;
1226
+ this.disconnectReason.cause = DisconnectCause.BUSY;
1227
+ }
1228
+ }
1229
+ getDisconnectReason = () => {
1230
+ return this.disconnectReason;
1231
+ };
1232
+ async answer(localAudioStream) {
1233
+ const localAudioTrack = localAudioStream.outputStream.getAudioTracks()[0];
1234
+ if (!localAudioTrack) {
1235
+ log.warn(`Did not find a local track while answering the call ${this.getCorrelationId()}`, {
1236
+ file: CALL_FILE,
1237
+ method: 'answer',
1238
+ });
1239
+ this.mediaInactivity = true;
1240
+ this.sendCallStateMachineEvt({ type: 'E_SEND_CALL_DISCONNECT' });
1241
+ return;
1242
+ }
1243
+ localAudioTrack.enabled = true;
1244
+ if (!this.mediaConnection) {
1245
+ this.initMediaConnection(localAudioTrack);
1246
+ this.mediaRoapEventsListener();
1247
+ this.mediaTrackListener();
1248
+ this.outputTrackUpdateListener(localAudioStream);
1249
+ }
1250
+ if (this.callStateMachine.state.value === 'S_SEND_CALL_PROGRESS') {
1251
+ this.sendCallStateMachineEvt({ type: 'E_SEND_CALL_CONNECT' });
1252
+ }
1253
+ else {
1254
+ log.warn(`Call cannot be answered because the state is : ${this.callStateMachine.state.value}`, { file: CALL_FILE, method: 'answer' });
1255
+ }
1256
+ }
1257
+ async dial(localAudioStream) {
1258
+ const localAudioTrack = localAudioStream.outputStream.getAudioTracks()[0];
1259
+ if (!localAudioTrack) {
1260
+ log.warn(`Did not find a local track while dialing the call ${this.getCorrelationId()}`, {
1261
+ file: CALL_FILE,
1262
+ method: 'dial',
1263
+ });
1264
+ this.deleteCb(this.getCorrelationId());
1265
+ this.emit(CALL_EVENT_KEYS.DISCONNECT, this.getCorrelationId());
1266
+ return;
1267
+ }
1268
+ localAudioTrack.enabled = true;
1269
+ if (!this.mediaConnection) {
1270
+ this.initMediaConnection(localAudioTrack);
1271
+ this.mediaRoapEventsListener();
1272
+ this.mediaTrackListener();
1273
+ this.outputTrackUpdateListener(localAudioStream);
1274
+ }
1275
+ if (this.mediaStateMachine.state.value === 'S_ROAP_IDLE') {
1276
+ this.sendMediaStateMachineEvt({ type: 'E_SEND_ROAP_OFFER' });
1277
+ }
1278
+ else {
1279
+ log.warn(`Call cannot be dialed because the state is already : ${this.mediaStateMachine.state.value}`, { file: CALL_FILE, method: 'dial' });
1280
+ }
1281
+ }
1282
+ post = async (roapMessage) => {
1283
+ return this.webex.request({
1284
+ uri: `${this.mobiusUrl}${DEVICES_ENDPOINT_RESOURCE}/${this.deviceId}/${CALL_ENDPOINT_RESOURCE}`,
1285
+ method: HTTP_METHODS.POST,
1286
+ service: ALLOWED_SERVICES.MOBIUS,
1287
+ headers: {
1288
+ [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1289
+ [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1290
+ },
1291
+ body: {
1292
+ device: {
1293
+ deviceId: this.deviceId,
1294
+ correlationId: this.correlationId,
1295
+ },
1296
+ callee: {
1297
+ type: this.destination.type,
1298
+ address: this.destination.address,
1299
+ },
1300
+ localMedia: {
1301
+ roap: roapMessage,
1302
+ mediaId: uuid(),
1303
+ },
1304
+ },
1305
+ });
1306
+ };
1307
+ async patch(state) {
1308
+ log.info(`Send a PATCH for ${state} to Webex Calling`, {
1309
+ file: CALL_FILE,
1310
+ method: this.patch.name,
1311
+ });
1312
+ return this.webex.request({
1313
+ uri: `${this.mobiusUrl}${DEVICES_ENDPOINT_RESOURCE}/${this.deviceId}/${CALLS_ENDPOINT_RESOURCE}/${this.callId}`,
1314
+ method: HTTP_METHODS.PATCH,
1315
+ service: ALLOWED_SERVICES.MOBIUS,
1316
+ headers: {
1317
+ [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1318
+ [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1319
+ },
1320
+ body: {
1321
+ device: {
1322
+ deviceId: this.deviceId,
1323
+ correlationId: this.correlationId,
1324
+ },
1325
+ callId: this.callId,
1326
+ callState: state,
1327
+ inbandMedia: false,
1328
+ },
1329
+ });
1330
+ }
1331
+ async postSSRequest(context, type) {
1332
+ const request = {
1333
+ uri: `${this.mobiusUrl}${SERVICES_ENDPOINT}`,
1334
+ method: HTTP_METHODS.POST,
1335
+ service: ALLOWED_SERVICES.MOBIUS,
1336
+ headers: {
1337
+ [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1338
+ [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1339
+ },
1340
+ body: {
1341
+ device: {
1342
+ deviceId: this.deviceId,
1343
+ correlationId: this.correlationId,
1344
+ },
1345
+ callId: this.callId,
1346
+ },
1347
+ };
1348
+ switch (type) {
1349
+ case SUPPLEMENTARY_SERVICES.HOLD: {
1350
+ request.uri = `${request.uri}/${CALL_HOLD_SERVICE}/${HOLD_ENDPOINT}`;
1351
+ break;
1352
+ }
1353
+ case SUPPLEMENTARY_SERVICES.RESUME: {
1354
+ request.uri = `${request.uri}/${CALL_HOLD_SERVICE}/${RESUME_ENDPOINT}`;
1355
+ break;
1356
+ }
1357
+ case SUPPLEMENTARY_SERVICES.TRANSFER: {
1358
+ request.uri = `${request.uri}/${CALL_TRANSFER_SERVICE}/${TRANSFER_ENDPOINT}`;
1359
+ const transferContext = context;
1360
+ if (transferContext.destination) {
1361
+ Object.assign(request.body, { blindTransferContext: transferContext });
1362
+ Object.assign(request.body, { transferType: TransferType.BLIND });
1363
+ }
1364
+ else if (transferContext.transferToCallId) {
1365
+ Object.assign(request.body, { consultTransferContext: transferContext });
1366
+ Object.assign(request.body, { transferType: TransferType.CONSULT });
1367
+ }
1368
+ break;
1369
+ }
1370
+ default: {
1371
+ log.warn(`Unknown type for PUT request: ${type}`, {
1372
+ file: CALL_FILE,
1373
+ method: this.postSSRequest.name,
1374
+ });
1375
+ }
1376
+ }
1377
+ return this.webex.request(request);
1378
+ }
1379
+ async postStatus() {
1380
+ return this.webex.request({
1381
+ uri: `${this.mobiusUrl}${DEVICES_ENDPOINT_RESOURCE}/${this.deviceId}/${CALLS_ENDPOINT_RESOURCE}/${this.callId}/${CALL_STATUS_RESOURCE}`,
1382
+ method: HTTP_METHODS.POST,
1383
+ service: ALLOWED_SERVICES.MOBIUS,
1384
+ headers: {
1385
+ [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1386
+ [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1387
+ },
1388
+ body: {
1389
+ device: {
1390
+ deviceId: this.deviceId,
1391
+ correlationId: this.correlationId,
1392
+ },
1393
+ callId: this.callId,
1394
+ },
1395
+ });
1396
+ }
1397
+ async completeTransfer(transferType, transferCallId, transferTarget) {
1398
+ if (transferType === TransferType.BLIND && transferTarget) {
1399
+ log.info(`Initiating Blind transfer with : ${transferTarget}`, {
1400
+ file: CALL_FILE,
1401
+ method: this.completeTransfer.name,
1402
+ });
1403
+ const context = {
1404
+ transferorCallId: this.getCallId(),
1405
+ destination: transferTarget,
1406
+ };
1407
+ try {
1408
+ await this.postSSRequest(context, SUPPLEMENTARY_SERVICES.TRANSFER);
1409
+ this.metricManager.submitCallMetric(METRIC_EVENT.CALL, TRANSFER_ACTION.BLIND, METRIC_TYPE.BEHAVIORAL, this.getCallId(), this.getCorrelationId(), undefined);
1410
+ }
1411
+ catch (e) {
1412
+ log.warn(`Blind Transfer failed for correlationId ${this.getCorrelationId()}`, {
1413
+ file: CALL_FILE,
1414
+ method: this.completeTransfer.name,
1415
+ });
1416
+ const errData = e;
1417
+ handleCallErrors((error) => {
1418
+ this.emit(CALL_EVENT_KEYS.TRANSFER_ERROR, error);
1419
+ this.submitCallErrorMetric(error, TRANSFER_ACTION.BLIND);
1420
+ }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.completeTransfer.name, CALL_FILE);
1421
+ }
1422
+ }
1423
+ else if (transferType === TransferType.CONSULT && transferCallId) {
1424
+ log.info(`Initiating Consult transfer between : ${this.callId} and ${transferCallId}`, {
1425
+ file: CALL_FILE,
1426
+ method: this.completeTransfer.name,
1427
+ });
1428
+ const context = {
1429
+ transferorCallId: this.getCallId(),
1430
+ transferToCallId: transferCallId,
1431
+ };
1432
+ try {
1433
+ await this.postSSRequest(context, SUPPLEMENTARY_SERVICES.TRANSFER);
1434
+ this.metricManager.submitCallMetric(METRIC_EVENT.CALL, TRANSFER_ACTION.CONSULT, METRIC_TYPE.BEHAVIORAL, this.getCallId(), this.getCorrelationId(), undefined);
1435
+ }
1436
+ catch (e) {
1437
+ log.warn(`Consult Transfer failed for correlationId ${this.getCorrelationId()}`, {
1438
+ file: CALL_FILE,
1439
+ method: this.completeTransfer.name,
1440
+ });
1441
+ const errData = e;
1442
+ handleCallErrors((error) => {
1443
+ this.emit(CALL_EVENT_KEYS.TRANSFER_ERROR, error);
1444
+ this.submitCallErrorMetric(error, TRANSFER_ACTION.CONSULT);
1445
+ }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.completeTransfer.name, CALL_FILE);
1446
+ }
1447
+ }
1448
+ else {
1449
+ log.warn(`Invalid information received, transfer failed for correlationId: ${this.getCorrelationId()}`, {
1450
+ file: CALL_FILE,
1451
+ method: this.completeTransfer.name,
1452
+ });
1453
+ }
1454
+ }
1455
+ async getCallStats() {
1456
+ let stats;
1457
+ try {
1458
+ stats = await this.mediaConnection.getStats();
1459
+ }
1460
+ catch (err) {
1461
+ log.warn('Stats collection failed, using dummy stats', {
1462
+ file: CALL_FILE,
1463
+ method: this.getCallStats.name,
1464
+ });
1465
+ }
1466
+ return parseMediaQualityStatistics(stats);
1467
+ }
1468
+ async postMedia(roapMessage) {
1469
+ log.log('Posting message to Webex Calling', {
1470
+ file: CALL_FILE,
1471
+ method: this.postMedia.name,
1472
+ });
1473
+ return this.webex.request({
1474
+ uri: `${this.mobiusUrl}${DEVICES_ENDPOINT_RESOURCE}/${this.deviceId}/${CALLS_ENDPOINT_RESOURCE}/${this.callId}/${MEDIA_ENDPOINT_RESOURCE}`,
1475
+ method: HTTP_METHODS.POST,
1476
+ service: ALLOWED_SERVICES.MOBIUS,
1477
+ headers: {
1478
+ [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1479
+ [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1480
+ },
1481
+ body: {
1482
+ device: {
1483
+ deviceId: this.deviceId,
1484
+ correlationId: this.correlationId,
1485
+ },
1486
+ callId: this.callId,
1487
+ localMedia: {
1488
+ roap: roapMessage,
1489
+ mediaId: uuid(),
1490
+ },
1491
+ },
1492
+ });
1493
+ }
1494
+ mediaRoapEventsListener() {
1495
+ this.mediaConnection.on(Event.ROAP_MESSAGE_TO_SEND, async (event) => {
1496
+ log.info(`ROAP message to send (rcv from MEDIA-SDK) :
1497
+ \n type: ${event.roapMessage?.messageType}, seq: ${event.roapMessage.seq} , version: ${event.roapMessage.version}`, {});
1498
+ log.info(`SDP message to send : \n ${event.roapMessage?.sdp}`, {
1499
+ file: CALL_FILE,
1500
+ method: this.mediaRoapEventsListener.name,
1501
+ });
1502
+ switch (event.roapMessage.messageType) {
1503
+ case RoapScenario.OK: {
1504
+ const mediaOk = {
1505
+ received: false,
1506
+ message: event.roapMessage,
1507
+ };
1508
+ this.sendMediaStateMachineEvt({ type: 'E_ROAP_OK', data: mediaOk });
1509
+ break;
1510
+ }
1511
+ case RoapScenario.OFFER: {
1512
+ const sdpVideoPortZero = event.roapMessage.sdp.replace(/^m=(video) (?:\d+) /gim, 'm=$1 0 ');
1513
+ event.roapMessage.sdp = sdpVideoPortZero;
1514
+ this.localRoapMessage = event.roapMessage;
1515
+ this.sendCallStateMachineEvt({ type: 'E_SEND_CALL_SETUP', data: event.roapMessage });
1516
+ break;
1517
+ }
1518
+ case RoapScenario.ANSWER:
1519
+ this.localRoapMessage = event.roapMessage;
1520
+ this.sendMediaStateMachineEvt({ type: 'E_SEND_ROAP_ANSWER', data: event.roapMessage });
1521
+ break;
1522
+ case RoapScenario.ERROR:
1523
+ this.sendMediaStateMachineEvt({ type: 'E_ROAP_ERROR', data: event.roapMessage });
1524
+ break;
1525
+ case RoapScenario.OFFER_RESPONSE:
1526
+ this.localRoapMessage = event.roapMessage;
1527
+ this.sendMediaStateMachineEvt({ type: 'E_SEND_ROAP_OFFER', data: event.roapMessage });
1528
+ break;
1529
+ default:
1530
+ }
1531
+ });
1532
+ }
1533
+ mediaTrackListener() {
1534
+ this.mediaConnection.on(Event.REMOTE_TRACK_ADDED, (e) => {
1535
+ if (e.type === MEDIA_CONNECTION_EVENT_KEYS.MEDIA_TYPE_AUDIO) {
1536
+ this.emit(CALL_EVENT_KEYS.REMOTE_MEDIA, e.track);
1537
+ }
1538
+ });
1539
+ }
1540
+ outputTrackUpdateListener(localAudioStream) {
1541
+ localAudioStream.on(LocalStreamEventNames.OutputTrackChange, (track) => {
1542
+ this.mediaConnection.updateLocalTracks({ audio: track });
1543
+ });
1544
+ }
1545
+ async delete() {
1546
+ const disconnectMetrics = await this.getCallStats();
1547
+ return this.webex.request({
1548
+ uri: `${this.mobiusUrl}${DEVICES_ENDPOINT_RESOURCE}/${this.deviceId}/${CALLS_ENDPOINT_RESOURCE}/${this.callId}`,
1549
+ method: HTTP_METHODS.DELETE,
1550
+ service: ALLOWED_SERVICES.MOBIUS,
1551
+ headers: {
1552
+ [CISCO_DEVICE_URL]: this.webex.internal.device.url,
1553
+ [SPARK_USER_AGENT]: CALLING_USER_AGENT,
1554
+ },
1555
+ body: {
1556
+ device: {
1557
+ deviceId: this.deviceId,
1558
+ correlationId: this.correlationId,
1559
+ },
1560
+ callId: this.callId,
1561
+ metrics: disconnectMetrics,
1562
+ causecode: this.disconnectReason.code,
1563
+ cause: this.disconnectReason.cause,
1564
+ },
1565
+ });
1566
+ }
1567
+ submitCallErrorMetric(error, transferMetricAction) {
1568
+ if (error.getCallError().errorLayer === ERROR_LAYER.CALL_CONTROL) {
1569
+ this.metricManager.submitCallMetric(METRIC_EVENT.CALL_ERROR, transferMetricAction || this.callStateMachine.state.value.toString(), METRIC_TYPE.BEHAVIORAL, this.callId, this.correlationId, error);
1570
+ }
1571
+ else {
1572
+ this.metricManager.submitMediaMetric(METRIC_EVENT.MEDIA_ERROR, this.mediaStateMachine.state.value.toString(), METRIC_TYPE.BEHAVIORAL, this.callId, this.correlationId, this.localRoapMessage.sdp, this.remoteRoapMessage?.sdp, error);
1573
+ }
1574
+ }
1575
+ handleMidCallEvent(event) {
1576
+ const { eventType, eventData } = event;
1577
+ switch (eventType) {
1578
+ case MidCallEventType.CALL_INFO: {
1579
+ log.log(`Received Midcall CallInfo Event for correlationId : ${this.correlationId}`, {
1580
+ file: CALL_FILE,
1581
+ method: 'handleMidCallEvent',
1582
+ });
1583
+ const callerData = eventData;
1584
+ this.startCallerIdResolution(callerData.callerId);
1585
+ break;
1586
+ }
1587
+ case MidCallEventType.CALL_STATE: {
1588
+ log.log(`Received Midcall call event for correlationId : ${this.correlationId}`, {
1589
+ file: CALL_FILE,
1590
+ method: 'handleMidCallEvent',
1591
+ });
1592
+ const data = eventData;
1593
+ switch (data.callState) {
1594
+ case MOBIUS_MIDCALL_STATE.HELD: {
1595
+ log.log(`Call is successfully held : ${this.correlationId}`, {
1596
+ file: CALL_FILE,
1597
+ method: 'handleMidCallEvent',
1598
+ });
1599
+ this.emit(CALL_EVENT_KEYS.HELD, this.correlationId);
1600
+ this.held = true;
1601
+ if (this.supplementaryServicesTimer) {
1602
+ clearTimeout(this.supplementaryServicesTimer);
1603
+ this.supplementaryServicesTimer = undefined;
1604
+ }
1605
+ break;
1606
+ }
1607
+ case MOBIUS_MIDCALL_STATE.CONNECTED: {
1608
+ log.log(`Call is successfully resumed : ${this.correlationId}`, {
1609
+ file: CALL_FILE,
1610
+ method: 'handleMidCallEvent',
1611
+ });
1612
+ this.emit(CALL_EVENT_KEYS.RESUMED, this.correlationId);
1613
+ this.held = false;
1614
+ if (this.supplementaryServicesTimer) {
1615
+ clearTimeout(this.supplementaryServicesTimer);
1616
+ this.supplementaryServicesTimer = undefined;
1617
+ }
1618
+ break;
1619
+ }
1620
+ default: {
1621
+ log.warn(`Unknown Supplementary service state: ${data.callState} for correlationId : ${this.correlationId}`, {
1622
+ file: CALL_FILE,
1623
+ method: 'handleMidCallEvent',
1624
+ });
1625
+ }
1626
+ }
1627
+ break;
1628
+ }
1629
+ default: {
1630
+ log.warn(`Unknown Midcall type: ${eventType} for correlationId : ${this.correlationId}`, {
1631
+ file: CALL_FILE,
1632
+ method: 'handleMidCallEvent',
1633
+ });
1634
+ }
1635
+ }
1636
+ }
1637
+ getCallerInfo = () => this.callerInfo;
1638
+ end = () => {
1639
+ this.sendCallStateMachineEvt({ type: 'E_SEND_CALL_DISCONNECT' });
1640
+ };
1641
+ doHoldResume = () => {
1642
+ if (this.held) {
1643
+ this.sendCallStateMachineEvt({ type: 'E_CALL_RESUME' });
1644
+ }
1645
+ else {
1646
+ this.sendCallStateMachineEvt({ type: 'E_CALL_HOLD' });
1647
+ }
1648
+ };
1649
+ startCallerIdResolution(callerInfo) {
1650
+ this.callerInfo = this.callerId.fetchCallerDetails(callerInfo);
1651
+ }
1652
+ sendDigit(tone) {
1653
+ try {
1654
+ log.info(`Sending digit : ${tone}`, {
1655
+ file: CALL_FILE,
1656
+ method: 'sendDigit',
1657
+ });
1658
+ this.mediaConnection.insertDTMF(tone);
1659
+ }
1660
+ catch (e) {
1661
+ log.warn(`Unable to send digit on call: ${e.message}`, {
1662
+ file: CALL_FILE,
1663
+ method: 'sendDigit',
1664
+ });
1665
+ }
1666
+ }
1667
+ mute = (localAudioStream) => {
1668
+ const localAudioTrack = localAudioStream.outputStream.getAudioTracks()[0];
1669
+ if (this.muted) {
1670
+ localAudioTrack.enabled = true;
1671
+ this.muted = false;
1672
+ }
1673
+ else {
1674
+ localAudioTrack.enabled = false;
1675
+ this.muted = true;
1676
+ }
1677
+ };
1678
+ updateMedia = (newAudioStream) => {
1679
+ const localAudioTrack = newAudioStream.outputStream.getAudioTracks()[0];
1680
+ if (!localAudioTrack) {
1681
+ log.warn(`Did not find a local track while updating media for call ${this.getCorrelationId()}. Will not update media`, {
1682
+ file: CALL_FILE,
1683
+ method: 'updateMedia',
1684
+ });
1685
+ return;
1686
+ }
1687
+ this.mediaConnection.updateLocalTracks({
1688
+ audio: localAudioTrack,
1689
+ });
1690
+ };
1691
+ setBroadworksCorrelationInfo(broadworksCorrelationInfo) {
1692
+ this.broadworksCorrelationInfo = broadworksCorrelationInfo;
1693
+ }
1694
+ getBroadworksCorrelationInfo() {
1695
+ return this.broadworksCorrelationInfo;
1696
+ }
1697
+ getCallRtpStats() {
1698
+ return this.getCallStats();
1699
+ }
1700
+ }
1701
+ export const createCall = (activeUrl, webex, dest, dir, deviceId, lineId, deleteCb, indicator) => new Call(activeUrl, webex, dest, dir, deviceId, lineId, deleteCb, indicator);