Better handle group call ring race conditions

This commit is contained in:
Evan Hahn 2022-11-16 20:52:04 -06:00 committed by GitHub
parent 629b5c3f6a
commit a88243f26d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 169 additions and 116 deletions

View file

@ -38,7 +38,7 @@ import {
import { uniqBy, noop } from 'lodash';
import type {
ActionsType as UxActionsType,
ActionsType as CallingReduxActionsType,
GroupCallParticipantInfoType,
GroupCallPeekInfoType,
} from '../state/ducks/calling';
@ -55,7 +55,6 @@ import {
CallMode,
GroupCallConnectionState,
GroupCallJoinState,
ProcessGroupCallRingRequestResult,
} from '../types/Calling';
import {
AudioDeviceModule,
@ -102,9 +101,9 @@ import * as log from '../logging/log';
import { assertDev } from '../util/assert';
const {
processGroupCallRingRequest,
processGroupCallRingCancelation,
cleanExpiredGroupCallRings,
processGroupCallRingCancellation,
cleanExpiredGroupCallRingCancellations,
wasGroupCallRingPreviouslyCanceled,
} = dataInterface;
const RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD: Map<
@ -129,6 +128,24 @@ enum GroupCallUpdateMessageState {
SentLeft,
}
type CallingReduxInterface = Pick<
CallingReduxActionsType,
| 'callStateChange'
| 'cancelIncomingGroupCallRing'
| 'groupCallAudioLevelsChange'
| 'groupCallStateChange'
| 'outgoingCall'
| 'receiveIncomingDirectCall'
| 'receiveIncomingGroupCall'
| 'refreshIODevices'
| 'remoteSharingScreenChange'
| 'remoteVideoChange'
| 'setPresenting'
| 'startCallingLobby'
> & {
areAnyCallsActiveOrRinging(): boolean;
};
function isScreenSource(source: PresentedSource): boolean {
return source.id.startsWith('screen');
}
@ -254,7 +271,7 @@ export class CallingClass {
readonly videoRenderer: CanvasVideoRenderer;
private uxActions?: UxActionsType;
private reduxInterface?: CallingReduxInterface;
private sfuUrl?: string;
@ -281,9 +298,9 @@ export class CallingClass {
this.callsByConversation = {};
}
initialize(uxActions: UxActionsType, sfuUrl: string): void {
this.uxActions = uxActions;
if (!uxActions) {
initialize(reduxInterface: CallingReduxInterface, sfuUrl: string): void {
this.reduxInterface = reduxInterface;
if (!reduxInterface) {
throw new Error('CallingClass.initialize: Invalid uxActions.');
}
@ -321,7 +338,7 @@ export class CallingClass {
});
ipcRenderer.on('stop-screen-share', () => {
uxActions.setPresenting();
reduxInterface.setPresenting();
});
ipcRenderer.on('quit', () => {
@ -390,7 +407,7 @@ export class CallingClass {
throw missingCaseError(callMode);
}
if (!this.uxActions) {
if (!this.reduxInterface) {
log.error('Missing uxActions, new call not allowed.');
return;
}
@ -492,7 +509,7 @@ export class CallingClass {
): Promise<void> {
log.info('CallingClass.startOutgoingDirectCall()');
if (!this.uxActions) {
if (!this.reduxInterface) {
throw new Error('Redux actions not available');
}
@ -544,7 +561,7 @@ export class CallingClass {
RingRTC.setVideoRenderer(call.callId, this.videoRenderer);
this.attachToCall(conversation, call);
this.uxActions.outgoingCall({
this.reduxInterface.outgoingCall({
conversationId: conversation.id,
hasLocalAudio,
hasLocalVideo,
@ -715,7 +732,7 @@ export class CallingClass {
}
const localAudioLevel = groupCall.getLocalDeviceState().audioLevel;
this.uxActions?.groupCallAudioLevelsChange({
this.reduxInterface?.groupCallAudioLevelsChange({
conversationId,
localAudioLevel,
remoteDeviceStates,
@ -985,7 +1002,7 @@ export class CallingClass {
conversationId: string,
groupCall: GroupCall
): void {
this.uxActions?.groupCallStateChange({
this.reduxInterface?.groupCallStateChange({
conversationId,
...this.formatGroupCallForRedux(groupCall),
});
@ -1247,8 +1264,8 @@ export class CallingClass {
icon: 'images/icons/v2/video-solid-24.svg',
message: window.i18n('calling__presenting--notification-body'),
onNotificationClick: () => {
if (this.uxActions) {
this.uxActions.setPresenting();
if (this.reduxInterface) {
this.reduxInterface.setPresenting();
}
},
silent: true,
@ -1365,7 +1382,7 @@ export class CallingClass {
await this.selectPreferredDevices(newSettings);
this.lastMediaDeviceSettings = newSettings;
this.uxActions?.refreshIODevices(newSettings);
this.reduxInterface?.refreshIODevices(newSettings);
}
}
@ -1703,34 +1720,31 @@ export class CallingClass {
let shouldRing = false;
if (update === RingUpdate.Requested) {
const processResult = await processGroupCallRingRequest(ringId);
switch (processResult) {
case ProcessGroupCallRingRequestResult.ShouldRing:
shouldRing = true;
break;
case ProcessGroupCallRingRequestResult.RingWasPreviouslyCanceled:
RingRTC.cancelGroupRing(groupIdBytes, ringId, null);
break;
case ProcessGroupCallRingRequestResult.ThereIsAnotherActiveRing:
RingRTC.cancelGroupRing(groupIdBytes, ringId, RingCancelReason.Busy);
break;
default:
throw missingCaseError(processResult);
if (await wasGroupCallRingPreviouslyCanceled(ringId)) {
RingRTC.cancelGroupRing(groupIdBytes, ringId, null);
} else if (this.areAnyCallsActiveOrRinging()) {
RingRTC.cancelGroupRing(groupIdBytes, ringId, RingCancelReason.Busy);
} else if (window.Events.getIncomingCallNotification()) {
shouldRing = true;
} else {
log.info(
'Incoming calls are disabled. Ignoring group call ring request'
);
}
} else {
await processGroupCallRingCancelation(ringId);
await processGroupCallRingCancellation(ringId);
}
if (shouldRing) {
log.info('handleGroupCallRingUpdate: ringing');
this.uxActions?.receiveIncomingGroupCall({
this.reduxInterface?.receiveIncomingGroupCall({
conversationId,
ringId,
ringerUuid,
});
} else {
log.info('handleGroupCallRingUpdate: canceling any existing ring');
this.uxActions?.cancelIncomingGroupCallRing({
this.reduxInterface?.cancelIncomingGroupCallRing({
conversationId,
ringId,
});
@ -1782,7 +1796,7 @@ export class CallingClass {
private async handleIncomingCall(call: Call): Promise<CallSettings | null> {
log.info('CallingClass.handleIncomingCall()');
if (!this.uxActions || !this.localDeviceId) {
if (!this.reduxInterface || !this.localDeviceId) {
log.error('Missing required objects, ignoring incoming call.');
return null;
}
@ -1815,7 +1829,7 @@ export class CallingClass {
this.attachToCall(conversation, call);
this.uxActions.receiveIncomingDirectCall({
this.reduxInterface.receiveIncomingDirectCall({
conversationId: conversation.id,
isVideoCall: call.isVideoCall,
});
@ -1866,8 +1880,8 @@ export class CallingClass {
private attachToCall(conversation: ConversationModel, call: Call): void {
this.callsByConversation[conversation.id] = call;
const { uxActions } = this;
if (!uxActions) {
const { reduxInterface } = this;
if (!reduxInterface) {
return;
}
@ -1883,7 +1897,7 @@ export class CallingClass {
this.lastMediaDeviceSettings = undefined;
delete this.callsByConversation[conversation.id];
}
uxActions.callStateChange({
reduxInterface.callStateChange({
conversationId: conversation.id,
acceptedTime,
callState: call.state,
@ -1896,7 +1910,7 @@ export class CallingClass {
// eslint-disable-next-line no-param-reassign
call.handleRemoteVideoEnabled = () => {
uxActions.remoteVideoChange({
reduxInterface.remoteVideoChange({
conversationId: conversation.id,
hasVideo: call.remoteVideoEnabled,
});
@ -1904,7 +1918,7 @@ export class CallingClass {
// eslint-disable-next-line no-param-reassign
call.handleRemoteSharingScreen = () => {
uxActions.remoteSharingScreenChange({
reduxInterface.remoteSharingScreenChange({
conversationId: conversation.id,
isSharingScreen: Boolean(call.remoteSharingScreen),
});
@ -2189,7 +2203,7 @@ export class CallingClass {
icon: 'images/icons/v2/video-solid-24.svg',
message: notificationMessage,
onNotificationClick: () => {
this.uxActions?.startCallingLobby({
this.reduxInterface?.startCallingLobby({
conversationId: conversation.id,
isVideoCall: true,
});
@ -2199,9 +2213,13 @@ export class CallingClass {
});
}
private areAnyCallsActiveOrRinging(): boolean {
return this.reduxInterface?.areAnyCallsActiveOrRinging() ?? false;
}
private async cleanExpiredGroupCallRingsAndLoop(): Promise<void> {
try {
await cleanExpiredGroupCallRings();
await cleanExpiredGroupCallRingCancellations();
} catch (err: unknown) {
// These errors are ignored here. They should be logged elsewhere and it's okay if
// we don't do a cleanup this time.