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

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 (283) hide show
  1. package/CHANGELOG.md +1403 -0
  2. package/README.md +74 -74
  3. package/dist/module/CallHistory/CallHistory.js +81 -81
  4. package/dist/module/CallHistory/callHistoryFixtures.js +307 -307
  5. package/dist/module/CallHistory/constants.js +9 -9
  6. package/dist/module/CallHistory/types.js +1 -1
  7. package/dist/module/CallSettings/CallSettings.js +65 -65
  8. package/dist/module/CallSettings/UcmBackendConnector.js +100 -100
  9. package/dist/module/CallSettings/WxCallBackendConnector.js +287 -287
  10. package/dist/module/CallSettings/constants.js +11 -11
  11. package/dist/module/CallSettings/testFixtures.js +62 -62
  12. package/dist/module/CallSettings/types.js +1 -1
  13. package/dist/module/CallingClient/CallingClient.js +248 -248
  14. package/dist/module/CallingClient/callRecordFixtures.js +93 -93
  15. package/dist/module/CallingClient/calling/CallerId/index.js +169 -169
  16. package/dist/module/CallingClient/calling/CallerId/types.js +1 -1
  17. package/dist/module/CallingClient/calling/call.js +1658 -1658
  18. package/dist/module/CallingClient/calling/callManager.js +284 -284
  19. package/dist/module/CallingClient/calling/index.js +2 -2
  20. package/dist/module/CallingClient/calling/types.js +53 -53
  21. package/dist/module/CallingClient/callingClientFixtures.js +38 -38
  22. package/dist/module/CallingClient/constants.js +122 -122
  23. package/dist/module/CallingClient/line/index.js +158 -158
  24. package/dist/module/CallingClient/line/types.js +15 -15
  25. package/dist/module/CallingClient/registration/index.js +1 -1
  26. package/dist/module/CallingClient/registration/register.js +507 -507
  27. package/dist/module/CallingClient/registration/registerFixtures.js +28 -28
  28. package/dist/module/CallingClient/registration/types.js +1 -1
  29. package/dist/module/CallingClient/types.js +1 -1
  30. package/dist/module/Contacts/ContactsClient.js +484 -484
  31. package/dist/module/Contacts/constants.js +20 -20
  32. package/dist/module/Contacts/contactFixtures.js +284 -284
  33. package/dist/module/Contacts/types.js +10 -10
  34. package/dist/module/Errors/catalog/CallError.js +26 -26
  35. package/dist/module/Errors/catalog/CallingDeviceError.js +18 -18
  36. package/dist/module/Errors/catalog/ExtendedError.js +10 -10
  37. package/dist/module/Errors/catalog/LineError.js +24 -24
  38. package/dist/module/Errors/index.js +2 -2
  39. package/dist/module/Errors/types.js +48 -48
  40. package/dist/module/Events/impl/index.js +19 -19
  41. package/dist/module/Events/types.js +83 -83
  42. package/dist/module/Logger/index.js +114 -114
  43. package/dist/module/Logger/types.js +25 -25
  44. package/dist/module/Metrics/index.js +232 -232
  45. package/dist/module/Metrics/types.js +37 -37
  46. package/dist/module/SDKConnector/index.js +39 -39
  47. package/dist/module/SDKConnector/types.js +1 -1
  48. package/dist/module/SDKConnector/utils.js +12 -12
  49. package/dist/module/Voicemail/BroadworksBackendConnector.js +291 -291
  50. package/dist/module/Voicemail/UcmBackendConnector.js +279 -279
  51. package/dist/module/Voicemail/Voicemail.js +110 -110
  52. package/dist/module/Voicemail/WxCallBackendConnector.js +279 -279
  53. package/dist/module/Voicemail/constants.js +29 -29
  54. package/dist/module/Voicemail/types.js +1 -1
  55. package/dist/module/Voicemail/voicemailFixture.js +510 -510
  56. package/dist/module/api.js +6 -6
  57. package/dist/module/common/Utils.js +802 -802
  58. package/dist/module/common/constants.js +41 -41
  59. package/dist/module/common/index.js +1 -1
  60. package/dist/module/common/testUtil.js +938 -938
  61. package/dist/module/common/types.js +57 -57
  62. package/dist/module/index.js +8 -8
  63. package/dist/types/CallHistory/CallHistory.d.ts +17 -17
  64. package/dist/types/CallHistory/callHistoryFixtures.d.ts +94 -94
  65. package/dist/types/CallHistory/constants.d.ts +9 -9
  66. package/dist/types/CallHistory/types.d.ts +18 -18
  67. package/dist/types/CallSettings/CallSettings.d.ts +19 -19
  68. package/dist/types/CallSettings/UcmBackendConnector.d.ts +19 -19
  69. package/dist/types/CallSettings/WxCallBackendConnector.d.ts +21 -21
  70. package/dist/types/CallSettings/constants.d.ts +11 -11
  71. package/dist/types/CallSettings/testFixtures.d.ts +15 -15
  72. package/dist/types/CallSettings/types.d.ts +107 -107
  73. package/dist/types/CallingClient/CallingClient.d.ts +35 -35
  74. package/dist/types/CallingClient/callRecordFixtures.d.ts +3 -3
  75. package/dist/types/CallingClient/calling/CallerId/index.d.ts +17 -17
  76. package/dist/types/CallingClient/calling/CallerId/types.d.ts +41 -41
  77. package/dist/types/CallingClient/calling/call.d.ts +96 -96
  78. package/dist/types/CallingClient/calling/callManager.d.ts +25 -25
  79. package/dist/types/CallingClient/calling/index.d.ts +2 -2
  80. package/dist/types/CallingClient/calling/types.d.ts +206 -206
  81. package/dist/types/CallingClient/callingClientFixtures.d.ts +18 -18
  82. package/dist/types/CallingClient/constants.d.ts +122 -122
  83. package/dist/types/CallingClient/line/index.d.ts +43 -43
  84. package/dist/types/CallingClient/line/types.d.ts +55 -55
  85. package/dist/types/CallingClient/registration/index.d.ts +1 -1
  86. package/dist/types/CallingClient/registration/register.d.ts +64 -64
  87. package/dist/types/CallingClient/registration/registerFixtures.d.ts +28 -28
  88. package/dist/types/CallingClient/registration/types.d.ts +20 -20
  89. package/dist/types/CallingClient/types.d.ts +29 -29
  90. package/dist/types/Contacts/ContactsClient.d.ts +26 -26
  91. package/dist/types/Contacts/constants.d.ts +19 -19
  92. package/dist/types/Contacts/contactFixtures.d.ts +280 -280
  93. package/dist/types/Contacts/types.d.ts +72 -72
  94. package/dist/types/Errors/catalog/CallError.d.ts +11 -11
  95. package/dist/types/Errors/catalog/CallingDeviceError.d.ts +10 -10
  96. package/dist/types/Errors/catalog/ExtendedError.d.ts +6 -6
  97. package/dist/types/Errors/catalog/LineError.d.ts +10 -10
  98. package/dist/types/Errors/index.d.ts +2 -2
  99. package/dist/types/Errors/types.d.ts +60 -60
  100. package/dist/types/Events/impl/index.d.ts +8 -8
  101. package/dist/types/Events/types.d.ts +288 -288
  102. package/dist/types/Logger/index.d.ts +12 -12
  103. package/dist/types/Logger/types.d.ts +25 -25
  104. package/dist/types/Metrics/index.d.ts +5 -5
  105. package/dist/types/Metrics/types.d.ts +42 -42
  106. package/dist/types/SDKConnector/index.d.ts +12 -12
  107. package/dist/types/SDKConnector/types.d.ts +128 -128
  108. package/dist/types/SDKConnector/utils.d.ts +5 -5
  109. package/dist/types/Voicemail/BroadworksBackendConnector.d.ts +27 -27
  110. package/dist/types/Voicemail/UcmBackendConnector.d.ts +34 -34
  111. package/dist/types/Voicemail/Voicemail.d.ts +27 -27
  112. package/dist/types/Voicemail/WxCallBackendConnector.d.ts +23 -23
  113. package/dist/types/Voicemail/constants.d.ts +29 -29
  114. package/dist/types/Voicemail/types.d.ts +133 -133
  115. package/dist/types/Voicemail/voicemailFixture.d.ts +417 -417
  116. package/dist/types/api.d.ts +16 -16
  117. package/dist/types/common/Utils.d.ts +34 -34
  118. package/dist/types/common/constants.d.ts +41 -41
  119. package/dist/types/common/index.d.ts +1 -1
  120. package/dist/types/common/testUtil.d.ts +3611 -3611
  121. package/dist/types/common/types.d.ts +191 -191
  122. package/dist/types/index.d.ts +8 -8
  123. package/package.json +4 -4
  124. package/dist/CallHistory/CallHistory.js +0 -203
  125. package/dist/CallHistory/CallHistory.js.map +0 -1
  126. package/dist/CallHistory/CallHistory.test.js +0 -133
  127. package/dist/CallHistory/CallHistory.test.js.map +0 -1
  128. package/dist/CallHistory/callHistoryFixtures.js +0 -310
  129. package/dist/CallHistory/callHistoryFixtures.js.map +0 -1
  130. package/dist/CallHistory/constants.js +0 -26
  131. package/dist/CallHistory/constants.js.map +0 -1
  132. package/dist/CallHistory/types.js +0 -7
  133. package/dist/CallHistory/types.js.map +0 -1
  134. package/dist/CallSettings/CallSettings.js +0 -276
  135. package/dist/CallSettings/CallSettings.js.map +0 -1
  136. package/dist/CallSettings/CallSettings.test.js +0 -122
  137. package/dist/CallSettings/CallSettings.test.js.map +0 -1
  138. package/dist/CallSettings/UcmBackendConnector.js +0 -217
  139. package/dist/CallSettings/UcmBackendConnector.js.map +0 -1
  140. package/dist/CallSettings/UcmBackendConnector.test.js +0 -227
  141. package/dist/CallSettings/UcmBackendConnector.test.js.map +0 -1
  142. package/dist/CallSettings/WxCallBackendConnector.js +0 -541
  143. package/dist/CallSettings/WxCallBackendConnector.js.map +0 -1
  144. package/dist/CallSettings/WxCallBackendConnector.test.js +0 -802
  145. package/dist/CallSettings/WxCallBackendConnector.test.js.map +0 -1
  146. package/dist/CallSettings/constants.js +0 -30
  147. package/dist/CallSettings/constants.js.map +0 -1
  148. package/dist/CallSettings/testFixtures.js +0 -72
  149. package/dist/CallSettings/testFixtures.js.map +0 -1
  150. package/dist/CallSettings/types.js +0 -7
  151. package/dist/CallSettings/types.js.map +0 -1
  152. package/dist/CallingClient/CallingClient.js +0 -572
  153. package/dist/CallingClient/CallingClient.js.map +0 -1
  154. package/dist/CallingClient/CallingClient.test.js +0 -931
  155. package/dist/CallingClient/CallingClient.test.js.map +0 -1
  156. package/dist/CallingClient/callRecordFixtures.js +0 -103
  157. package/dist/CallingClient/callRecordFixtures.js.map +0 -1
  158. package/dist/CallingClient/calling/CallerId/index.js +0 -287
  159. package/dist/CallingClient/calling/CallerId/index.js.map +0 -1
  160. package/dist/CallingClient/calling/CallerId/index.test.js +0 -274
  161. package/dist/CallingClient/calling/CallerId/index.test.js.map +0 -1
  162. package/dist/CallingClient/calling/CallerId/types.js +0 -7
  163. package/dist/CallingClient/calling/CallerId/types.js.map +0 -1
  164. package/dist/CallingClient/calling/call.js +0 -2995
  165. package/dist/CallingClient/calling/call.js.map +0 -1
  166. package/dist/CallingClient/calling/call.test.js +0 -2614
  167. package/dist/CallingClient/calling/call.test.js.map +0 -1
  168. package/dist/CallingClient/calling/callManager.js +0 -443
  169. package/dist/CallingClient/calling/callManager.js.map +0 -1
  170. package/dist/CallingClient/calling/callManager.test.js +0 -683
  171. package/dist/CallingClient/calling/callManager.test.js.map +0 -1
  172. package/dist/CallingClient/calling/index.js +0 -30
  173. package/dist/CallingClient/calling/index.js.map +0 -1
  174. package/dist/CallingClient/calling/types.js +0 -71
  175. package/dist/CallingClient/calling/types.js.map +0 -1
  176. package/dist/CallingClient/callingClientFixtures.js +0 -53
  177. package/dist/CallingClient/callingClientFixtures.js.map +0 -1
  178. package/dist/CallingClient/constants.js +0 -226
  179. package/dist/CallingClient/constants.js.map +0 -1
  180. package/dist/CallingClient/line/index.js +0 -312
  181. package/dist/CallingClient/line/index.js.map +0 -1
  182. package/dist/CallingClient/line/line.test.js +0 -270
  183. package/dist/CallingClient/line/line.test.js.map +0 -1
  184. package/dist/CallingClient/line/types.js +0 -25
  185. package/dist/CallingClient/line/types.js.map +0 -1
  186. package/dist/CallingClient/registration/index.js +0 -19
  187. package/dist/CallingClient/registration/index.js.map +0 -1
  188. package/dist/CallingClient/registration/register.js +0 -1295
  189. package/dist/CallingClient/registration/register.js.map +0 -1
  190. package/dist/CallingClient/registration/register.test.js +0 -838
  191. package/dist/CallingClient/registration/register.test.js.map +0 -1
  192. package/dist/CallingClient/registration/registerFixtures.js +0 -41
  193. package/dist/CallingClient/registration/registerFixtures.js.map +0 -1
  194. package/dist/CallingClient/registration/types.js +0 -7
  195. package/dist/CallingClient/registration/types.js.map +0 -1
  196. package/dist/CallingClient/types.js +0 -7
  197. package/dist/CallingClient/types.js.map +0 -1
  198. package/dist/Contacts/ContactsClient.js +0 -1139
  199. package/dist/Contacts/ContactsClient.js.map +0 -1
  200. package/dist/Contacts/ContactsClient.test.js +0 -725
  201. package/dist/Contacts/ContactsClient.test.js.map +0 -1
  202. package/dist/Contacts/constants.js +0 -36
  203. package/dist/Contacts/constants.js.map +0 -1
  204. package/dist/Contacts/contactFixtures.js +0 -340
  205. package/dist/Contacts/contactFixtures.js.map +0 -1
  206. package/dist/Contacts/types.js +0 -20
  207. package/dist/Contacts/types.js.map +0 -1
  208. package/dist/Errors/catalog/CallError.js +0 -94
  209. package/dist/Errors/catalog/CallError.js.map +0 -1
  210. package/dist/Errors/catalog/CallingDeviceError.js +0 -88
  211. package/dist/Errors/catalog/CallingDeviceError.js.map +0 -1
  212. package/dist/Errors/catalog/ExtendedError.js +0 -45
  213. package/dist/Errors/catalog/ExtendedError.js.map +0 -1
  214. package/dist/Errors/catalog/LineError.js +0 -90
  215. package/dist/Errors/catalog/LineError.js.map +0 -1
  216. package/dist/Errors/index.js +0 -21
  217. package/dist/Errors/index.js.map +0 -1
  218. package/dist/Errors/types.js +0 -62
  219. package/dist/Errors/types.js.map +0 -1
  220. package/dist/Events/impl/index.js +0 -82
  221. package/dist/Events/impl/index.js.map +0 -1
  222. package/dist/Events/types.js +0 -111
  223. package/dist/Events/types.js.map +0 -1
  224. package/dist/Logger/index.js +0 -221
  225. package/dist/Logger/index.js.map +0 -1
  226. package/dist/Logger/index.test.js +0 -62
  227. package/dist/Logger/index.test.js.map +0 -1
  228. package/dist/Logger/types.js +0 -37
  229. package/dist/Logger/types.js.map +0 -1
  230. package/dist/Metrics/index.js +0 -329
  231. package/dist/Metrics/index.js.map +0 -1
  232. package/dist/Metrics/index.test.js +0 -291
  233. package/dist/Metrics/index.test.js.map +0 -1
  234. package/dist/Metrics/types.js +0 -50
  235. package/dist/Metrics/types.js.map +0 -1
  236. package/dist/SDKConnector/index.js +0 -105
  237. package/dist/SDKConnector/index.js.map +0 -1
  238. package/dist/SDKConnector/index.test.js +0 -9
  239. package/dist/SDKConnector/index.test.js.map +0 -1
  240. package/dist/SDKConnector/types.js +0 -7
  241. package/dist/SDKConnector/types.js.map +0 -1
  242. package/dist/SDKConnector/utils.js +0 -40
  243. package/dist/SDKConnector/utils.js.map +0 -1
  244. package/dist/SDKConnector/utils.test.js +0 -9
  245. package/dist/SDKConnector/utils.test.js.map +0 -1
  246. package/dist/Voicemail/BroadworksBackendConnector.js +0 -632
  247. package/dist/Voicemail/BroadworksBackendConnector.js.map +0 -1
  248. package/dist/Voicemail/BroadworksBackendConnector.test.js +0 -733
  249. package/dist/Voicemail/BroadworksBackendConnector.test.js.map +0 -1
  250. package/dist/Voicemail/UcmBackendConnector.js +0 -566
  251. package/dist/Voicemail/UcmBackendConnector.js.map +0 -1
  252. package/dist/Voicemail/UcmBackendConnector.test.js +0 -628
  253. package/dist/Voicemail/UcmBackendConnector.test.js.map +0 -1
  254. package/dist/Voicemail/Voicemail.js +0 -378
  255. package/dist/Voicemail/Voicemail.js.map +0 -1
  256. package/dist/Voicemail/Voicemail.test.js +0 -226
  257. package/dist/Voicemail/Voicemail.test.js.map +0 -1
  258. package/dist/Voicemail/WxCallBackendConnector.js +0 -540
  259. package/dist/Voicemail/WxCallBackendConnector.js.map +0 -1
  260. package/dist/Voicemail/WxCallBackendConnector.test.js +0 -942
  261. package/dist/Voicemail/WxCallBackendConnector.test.js.map +0 -1
  262. package/dist/Voicemail/constants.js +0 -66
  263. package/dist/Voicemail/constants.js.map +0 -1
  264. package/dist/Voicemail/types.js +0 -7
  265. package/dist/Voicemail/types.js.map +0 -1
  266. package/dist/Voicemail/voicemailFixture.js +0 -559
  267. package/dist/Voicemail/voicemailFixture.js.map +0 -1
  268. package/dist/api.js +0 -134
  269. package/dist/api.js.map +0 -1
  270. package/dist/common/Utils.js +0 -1264
  271. package/dist/common/Utils.js.map +0 -1
  272. package/dist/common/Utils.test.js +0 -1227
  273. package/dist/common/Utils.test.js.map +0 -1
  274. package/dist/common/constants.js +0 -90
  275. package/dist/common/constants.js.map +0 -1
  276. package/dist/common/index.js +0 -19
  277. package/dist/common/index.js.map +0 -1
  278. package/dist/common/testUtil.js +0 -968
  279. package/dist/common/testUtil.js.map +0 -1
  280. package/dist/common/types.js +0 -75
  281. package/dist/common/types.js.map +0 -1
  282. package/dist/index.js +0 -63
  283. package/dist/index.js.map +0 -1
@@ -1,1658 +1,1658 @@
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
+ 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);