From c0af2f25a1cb9a1d18f2943f600b9016342967fb Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 19 Apr 2021 20:28:42 +0100 Subject: [PATCH 1/7] Support MSC3086 asserted identity --- src/@types/event.ts | 2 ++ src/webrtc/call.ts | 23 +++++++++++++++++++++++ src/webrtc/callEventHandler.ts | 17 ++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/@types/event.ts b/src/@types/event.ts index 92f90ea62..a908088d7 100644 --- a/src/@types/event.ts +++ b/src/@types/event.ts @@ -53,6 +53,8 @@ export enum EventType { CallSelectAnswer = "m.call.select_answer", CallNegotiate = "m.call.negotiate", CallReplaces = "m.call.replaces", + CallAssertedIdentity = "m.call.asserted_identity", + CallAssertedIdentityPrefix = "org.matrix.call.asserted_identity", KeyVerificationRequest = "m.key.verification.request", KeyVerificationStart = "m.key.verification.start", KeyVerificationCancel = "m.key.verification.cancel", diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index 53680702a..4dabf2984 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -62,6 +62,11 @@ interface TurnServer { ttl?: number, } +interface AssertedIdentity { + id: string, + displayName: string, +} + export enum CallState { Fledgling = 'fledgling', InviteSent = 'invite_sent', @@ -101,6 +106,8 @@ export enum CallEvent { RemoteHoldUnhold = 'remote_hold_unhold', // backwards compat alias for LocalHoldUnhold: remove in a major version bump HoldUnhold = 'hold_unhold', + + AssertedIdentityChanged = 'asserted_identity_changed', } export enum CallErrorCode { @@ -292,6 +299,8 @@ export class MatrixCall extends EventEmitter { // the call) we buffer them up here so we can then add the ones from the party we pick private remoteCandidateBuffer = new Map(); + private remoteAssertedIdentity: AssertedIdentity; + constructor(opts: CallOpts) { super(); this.roomId = opts.roomId; @@ -419,6 +428,10 @@ export class MatrixCall extends EventEmitter { return Boolean(this.opponentCaps && this.opponentCaps["m.call.transferee"]); } + public getRemoteAssertedIdentity(): AssertedIdentity { + return this.remoteAssertedIdentity; + } + /** * Retrieve the local <video> DOM element. * @return {Element} The dom element @@ -1199,6 +1212,16 @@ export class MatrixCall extends EventEmitter { } } + async onAssertedIdentityReceived(event: MatrixEvent) { + if (!event.getContent().asserted_identity) return; + + this.remoteAssertedIdentity = { + id: event.getContent().asserted_identity.id, + displayName: event.getContent().asserted_identity.displayName, + }; + this.emit(CallEvent.AssertedIdentityChanged); + } + private callHasEnded(): boolean { // This exists as workaround to typescript trying to be clever and erroring // when putting if (this.state === CallState.Ended) return; twice in the same diff --git a/src/webrtc/callEventHandler.ts b/src/webrtc/callEventHandler.ts index c45bf62b3..58145724e 100644 --- a/src/webrtc/callEventHandler.ts +++ b/src/webrtc/callEventHandler.ts @@ -87,7 +87,11 @@ export class CallEventHandler { private onEvent = (event: MatrixEvent) => { // any call events or ones that might be once they're decrypted - if (event.getType().indexOf("m.call.") === 0 || event.isBeingDecrypted()) { + if ( + event.getType().indexOf("m.call.") === 0 || + event.getType().indexOf("org.matrix.call.") === 0 + || event.isBeingDecrypted() + ) { // queue up for processing once all events from this sync have been // processed (see above). this.callEventBuffer.push(event); @@ -271,6 +275,17 @@ export class CallEventHandler { } call.onNegotiateReceived(event); + } else if ( + event.getType() === EventType.CallAssertedIdentity || event.getType() === EventType.CallAssertedIdentityPrefix + ) { + if (!call) return; + + if (event.getContent().party_id === call.ourPartyId) { + // Ignore remote echo (not that we send asserted identity, but still...) + return; + } + + call.onAssertedIdentityReceived(event); } } } From 56797948afc4c09055313c3ef13d6551772a5b1e Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 19 Apr 2021 20:36:00 +0100 Subject: [PATCH 2/7] lint --- src/webrtc/callEventHandler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webrtc/callEventHandler.ts b/src/webrtc/callEventHandler.ts index 58145724e..9c4c38b26 100644 --- a/src/webrtc/callEventHandler.ts +++ b/src/webrtc/callEventHandler.ts @@ -276,7 +276,8 @@ export class CallEventHandler { call.onNegotiateReceived(event); } else if ( - event.getType() === EventType.CallAssertedIdentity || event.getType() === EventType.CallAssertedIdentityPrefix + event.getType() === EventType.CallAssertedIdentity || + event.getType() === EventType.CallAssertedIdentityPrefix ) { if (!call) return; From 1b31d0622e6a216b66e8cebc8ff43676c40da5bf Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 20 Apr 2021 11:08:06 +0100 Subject: [PATCH 3/7] Unit test for asserted identity messages --- spec/unit/webrtc/call.spec.ts | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 0e7b67547..6a9d0cd52 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -254,4 +254,43 @@ describe('Call', function() { sdpMid: '', }); }); + + it('should map asserted identity messages to remoteAssertedIdentity', async function() { + const callPromise = call.placeVoiceCall(); + await client.httpBackend.flush(); + await callPromise; + await call.onAnswerReceived({ + getContent: () => { + return { + version: 1, + call_id: call.callId, + party_id: 'party_id', + answer: { + sdp: DUMMY_SDP, + }, + }; + }, + }); + + await call.onAssertedIdentityReceived({ + getContent: () => { + return { + version: 1, + call_id: call.callId, + party_id: 'party_id', + asserted_identity: { + id: "@steve:example.com", + display_name: "Steve Gibbons", + }, + }; + }, + }); + + const ident = call.getRemoteAssertedIdentity(); + expect(ident.id).toEqual("@steve:example.com"); + expect(ident.displayName).toEqual("Steve Gibbons"); + + // Hangup to stop timers + call.hangup(CallErrorCode.UserHangup, true); + }); }); From 74ef760591a3e316c4aadcdeb95db5573ae667ca Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 20 Apr 2021 12:48:54 +0100 Subject: [PATCH 4/7] Tests: They find bugs --- src/webrtc/call.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index 4dabf2984..12b62c660 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -1217,7 +1217,7 @@ export class MatrixCall extends EventEmitter { this.remoteAssertedIdentity = { id: event.getContent().asserted_identity.id, - displayName: event.getContent().asserted_identity.displayName, + displayName: event.getContent().asserted_identity.display_name, }; this.emit(CallEvent.AssertedIdentityChanged); } From 54077175341e297a45178e05fa6c623c2f639ba2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 20 Apr 2021 12:57:11 +0100 Subject: [PATCH 5/7] Assert event emitted --- spec/unit/webrtc/call.spec.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 6a9d0cd52..46102c30d 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -15,7 +15,7 @@ limitations under the License. */ import {TestClient} from '../../TestClient'; -import {MatrixCall, CallErrorCode} from '../../../src/webrtc/call'; +import {MatrixCall, CallErrorCode, CallEvent} from '../../../src/webrtc/call'; const DUMMY_SDP = ( "v=0\r\n" + @@ -272,6 +272,9 @@ describe('Call', function() { }, }); + const identChangedCallback = jest.fn(); + call.on(CallEvent.AssertedIdentityChanged, identChangedCallback) + await call.onAssertedIdentityReceived({ getContent: () => { return { @@ -286,6 +289,8 @@ describe('Call', function() { }, }); + expect(identChangedCallback).toHaveBeenCalled(); + const ident = call.getRemoteAssertedIdentity(); expect(ident.id).toEqual("@steve:example.com"); expect(ident.displayName).toEqual("Steve Gibbons"); From bca8568d64e4964fafdc5772cf14eec10bd8662e Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 20 Apr 2021 12:58:33 +0100 Subject: [PATCH 6/7] missing semicolon --- spec/unit/webrtc/call.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 46102c30d..e5ef6de87 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -273,7 +273,7 @@ describe('Call', function() { }); const identChangedCallback = jest.fn(); - call.on(CallEvent.AssertedIdentityChanged, identChangedCallback) + call.on(CallEvent.AssertedIdentityChanged, identChangedCallback); await call.onAssertedIdentityReceived({ getContent: () => { From 4a51ac7a7499cca7aa774b1ecf6c50dd5d9b8341 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 23 Apr 2021 14:36:56 +0100 Subject: [PATCH 7/7] Move createNewMatrixCall to the client object So we can mock it out it tests (and also I'm not sure why it was like this in the first place: we passed the client in anyway...) Deprecate createNewMatrixCall --- src/client.js | 11 +++++++++++ src/webrtc/call.ts | 3 +++ 2 files changed, 14 insertions(+) diff --git a/src/client.js b/src/client.js index f344bce87..e99896792 100644 --- a/src/client.js +++ b/src/client.js @@ -729,6 +729,17 @@ MatrixClient.prototype.setSupportsCallTransfer = function(supportsCallTransfer) this._supportsCallTransfer = supportsCallTransfer; }; +/** + * Creates a new call. + * The place*Call methods on the returned call can be used to actually place a call + * + * @param {string} roomId The room the call is to be placed in. + * @return {MatrixCall} the call or null if the browser doesn't support calling. + */ +MatrixClient.prototype.createCall = function(roomId) { + return createNewMatrixCall(this, roomId); +}; + /** * Get the current sync state. * @return {?string} the sync state, which may be null. diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index 12b62c660..233c6e1fa 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -1936,6 +1936,9 @@ export function setAudioInput(deviceId: string) { audioInput = deviceId; } export function setVideoInput(deviceId: string) { videoInput = deviceId; } /** + * DEPRECATED + * Use client.createCall() + * * Create a new Matrix call for the browser. * @param {MatrixClient} client The client instance to use. * @param {string} roomId The room the call is in.