Skip to content

Case Study: GroupMe‐ACS 1:1 Calling

jamchengms edited this page May 6, 2024 · 3 revisions

Introduction

GroupMe is a highly acclaimed group chat application, particularly used by United States college students, that has an impressive monthly active user (MAU) base around 31M with around 22M in-apps MAU (Android +iOS). GroupMe offers a multitude of features, including seamless group and event calling options which have recently recorded an astounding 80k unique calling MAU and 3.5 million calling minutes per month. GroupMe group and event call facilitated through the ACS UI SDK integration, GroupMe users have expressed their interest in the addition of 1:1 calling functionality to further enhance their communication experience. The 1:1 Calling experience provided by the ACS Calling NativeUI SDK will be familiar for end users that’s accustomed to the native phone interfaces, and this made possible through the use of CallKit and TelecomManager frameworks.

GroupMe, like many other native applications, already has a full-fledged custom push notification backend system setup already. They were able to reference Push Notification Recommendations on GitHub Wiki and utilized Azure Event Grid events to trigger notifications for sending/receiving 1:1 calling push notifications through CallKit (iOS) and TelecomManager (Android). We discovered many challenges along the journey from both the GroupMe and ACS team as we collaborated to enable the 1:1 Calling experience for the end-users in the GroupMe application. We hope to share these 1:1 calling scenarios and learnings with developers to reference.

1:1 Calling Scenarios Supported in GroupMe-ACS integration

Below are the business scenarios GroupMe application considered while implementing 1:1 Calling for their end users.


Scenario: 1. Callee Accepts the call

Behaviour: Happy path, both caller and receiver join the ACS call.

Technical Details: Azure Event Grid will have Call started event, on receipt of which GM server will send VOIP or FCM


Scenario: 2. Callee rejects the call

Behaviour: Call will end on both sides.

Technical Details: Event Grid will have Call ended event. Call will end on both clients via ACS UI SDK.


Scenario: 3. Callee ignores/does not pick

Behaviour: Missed call system message and notification

Technical Details: Management of missed call states will involve coordination from two sources

Disconnect API sent by caller on receiving Disconnect in below scenarios.

  • Caller ends calls after dials -> Callee will immediately receive call ended with 487.

  • Caller dials, callee do nothing on notification -> Caller will get 487, Callee 603.

  • Caller dials, callee app is crashed or notification not received -> Caller will get 408.

  • Server will maintain a validation timer to dispose the call if not connected state received.


Scenario: 4. Callee does not have internet

Behaviour: Calle will receive a missed call notification and system message.


Scenario: 5. Callee is signed out

Behaviour: Same as #4

Technical Details: Same as #4


Scenario: 6. Callee has blocked the caller

Behaviour: Callee will not receive incoming call notification and call will end after timeout at caller end (from connecting state)

Technical Details: GM Server will receive incoming call event from Azure Event Grid, and no notification will be triggered to Callee as caller is blocked. To callee it seems call is going.


Scenario: 7. Call is ended by callee or caller after connected

Behaviour: Call is ended for both the users.

Technical Details: ACS SDK itself handles state for closing the calling SDK for both users. No GroupMe server intervention is needed. GroupMe server will only send the call end system message with call duration.


Scenario: 8. Receiver is on another 1-1 call(Phone call, Whatspp call, or GroupMe app)

Behaviour: Missed call will be generated

Technical Details: On receiving incoming call notification, GroupMe will ignore the call and it will result in timeout on the caller side after some time and callee will receive a missed call.


Scenario: 9. Receiver is on another group call

Behaviour: Same as #8

Technical Details: Same as #8


Scenario: 10. Receiver is on DND/Focus Mode

Behaviour: Missed Call

Technical Details: Same as #4.


Scenario: 11. Caller hangs up before receiver picks

Behaviour: Missed call

Technical Details: Same as #4


Scenario: 12. Both users call each other at the same time.

Behaviour: One user call will go in.

Technical Details: Explained below in the Challenges and Learnings from GroupMe #2.


Scenario: 13. Caller ends the call when call is still in starting state

Behaviour: Call should end immediately both at caller and callee side.

Challenges/Learning Discovered by GroupMe

Here are the top challenges/learnings from the GroupMe team while enabling the 1:1 Calling experience.

1. Call Notification in Background/Terminated State

In 1:1 calling, no matter in which state the callee app is, user should be informed about the incoming call. To achieve this Apple provides one special type of notification VOIP push notification. VoIP push notifications in iOS are different from regular push notifications in that they are intended specifically for initiating calls or other real-time communication features within an app.

GroupMe server listens to Azure Event Grid and on initiation of call and generates the VOIP notification. When a VoIP push notification is received, the app can wake up in the background or in terminated stated, allowing the user to respond to the call promptly.

Main learning while implementing this we got is that we have to report each and every VOIP notification. If for any scenario if we skip reporting the VOIP notification after 4-5 notifications, Apple will stop sending the VOIP notifications irrespective of the development and production environment. Also, immediately after receiving the notification we need to invoke Callkit for this we used SDK method handlePushNotification. If there is any delay in invoking CallKit, Apple will crash the app immediately.

Note For Android there is no specific FCM for 1:1 calling.

2. Network Latency Between Backend Systems

Problem: We observed a significant delay of approximately 8 seconds in transitioning from the call starting state to the ringing state, raising concerns regarding user experience. Further analysis identified that the server required 4-5 seconds to process events received from the Azure Event Hub, which listens for events from the Azure Event Grid and sends push notifications to the callee. Additionally, 1-2 seconds were attributed to fetching the ACS token via API, with an additional 2-3 seconds consumed by the ACS SDK to process the push notification and initiate Telecom Manager or Call Kit experience.

Solution: To mitigate the observed latency and improve user experience, we implemented the following measures:

  1. Early Start of Caller Ringtone: Recognizing that users perceived a prolonged wait time during the transition from the starting state to the ringing state, we modified our approach to initiate the caller ringtone from the starting state itself. By doing so, users are provided with immediate indication, that the call process has commenced, thus reducing perceived waiting time.
  2. ACS Token Caching: Given that ACS tokens have a lifespan of 24 hours, we implemented token caching. This involved storing the token locally and sending the cached token whenever launching a call composite. By doing this, the SDK only needs to fetch a new token if the cached one has expired, effectively saving approximately 2 seconds in the process.

Result: Through the implementation of these strategies, we successfully reduced the overall latency from 8-9 seconds to 6-7 seconds. This optimization has significantly enhanced the user experience during call initiation.

3. Two callers calling each other at the same time.

Problem: There is a case when two callers calling each other at the same time. We observed that both the users receiving calling notification and call is failing for both the users.

Solution: To handle the same, we took an approach where we allow a call from one user to get in and end the call initiated by the other user, resulting in one call being made. We make use of GroupMe backend identifiers to help determine this scenario and create logic to determine which caller gets precedence.

When user start the 1:1 call we maintain a value for which user a call is started. On receiving VOIP, we check whether caller is a same user to whom user is trying to call. If yes, then we allow a call of user with smaller user id to get in. For instance, User A with GroupMe ID -121 calling User B with GroupMe ID -234 and User B is also calling user A at the same time.

User A side: In our case when User A receives VOIP/FCM, since his user id is small, we supress the notification so that he keeps on calling user B.

User B side: User B will also receives VOIP/FCM, since his user id is greater, we end his call he is making to user A and report his push notification to SDK.

Note: The solution works on both the platforms Android and iOS.

4. App received delayed FCM or VOIP

Problem: There are cases when user receives push notification of call when there is actually no call. For instance callee is offline when caller try to call or caller start the call and immediately end the call.

Solution: The SDK addressed both scenarios. It checks if the notification is outdated and suppresses it accordingly. In Android, upon receiving an expired FCM notification, the SDK doesn't display any call UI to the user.

In iOS, because every VoIP notification must be reported, upon receiving expired VoIP notifications, the SDK briefly displays the CallKit UI for one second and immediately end the call. This is how whats app also handle the expired notifications.

Challenges/Learnings Discovered by ACS Calling

Here are the top challenges/learnings from the ACS Calling team while enabling the 1:1 Calling experience. These learning will be incorporated into the 1:1 Calling functionality for GA in May 2024.

1. Handling call state and composite exit events from UI Library

Introduction: UI Library provides events to Contoso to listen to call state changes (connecting, connected, ringing, disconnected, disconnecting, ...) as well as events when the call composite exited completely. Contoso subscribes to these events for logging or updating UI state of the application; for example, showing call in progress or call ended indicator.

Problem: Contoso is executing Contoso-specific logic on call state change and composite exit events, considering that events like composite exit will be received only after the disconnected event is completed. However, the UI Library does not wait for disconnected event notifications to complete before firing the composite exit event. The UI Library fires events in order but does not track the handling of these events by Contoso.

Solution: It would be ideal for Contoso not to run UI updates or make API calls on call state change or composite exit events. Contoso can implement its own event handling for these events following an asynchronous pattern so that event completion is not interrupted by complex computations.

Additionally, Contoso should track all events by CallId. In cases where ending and accepting calls occur, it is possible that the connected event for a new call is received before the disconnected event of the existing call, as ending a connecting call involves REST API calls and the order cannot be guaranteed.

2. Android ringtone integration in Contoso application

Introduction: The UI Library does not provide support to play a ringtone for incoming calls. However, Contoso can manage playing a ringtone following call state changes. To implement this feature, Contoso needs to learn AudioManager implementation. It's worth noting that the UI Library also provides options to change audio selection during calls.

Problem: The audio device selection by Contoso could impact the UI Library's device selection if the device changes during the "Connected" call state. For example, in the connected state, the UI Library provides an option to select a device using a UI dropdown. If, on call state change, Contoso implements custom logic, then the selection made by Contoso on the UI may not be applied.

Solution: When receiving a push notification and an incoming call notification event, Contoso can play a ringtone and apply the previous audio state before accepting the call. These events are sufficient for playing a ringtone on the callee's side.

3. Handling incoming call notifications

Introduction: The Contoso application will receive incoming call notifications from APNS or EventGrid, depending on the Contoso backend setup. The payload received will be passed to the UI Library to receive incoming call notifications. Apart from push notifications, Contoso will also receive incoming call notifications if CallComposite is running, meaning the current call or application is in the foreground.

Problem: The UI Library supports only one call at a time. It is not possible to accept a new call while a current call is in progress.

Solution: On iOS, CallKit will manage incoming calls, and Contoso will see options to End or Accept the call. When End is selected, the current call will be terminated, and when Accept is chosen, the current call will end, and the new call will be accepted, or the incoming call can be declined.

For Android, as the Telecom manager does not provide options to end and accept calls, Contoso either needs to ignore incoming calls by showing them as missed calls or needs to end the current call before accepting a new incoming call.

Summary

We outlined the various scenarios discovered and solutions explored for 1:1 Calling feature between GroupMe-ACS integrations. Hopefully, these shared knowledge will provide insight on how to best integrate 1:1 Calling feature into your mobile application as well as backend system. If you would like to try the 1:1 Calling experience on GroupMe application, here are the links to their native experience - it is free to try!

The 1:1 Calling feature is targeted to be released by end of May 2024. You can try the public beta version of 1:1 Calling feature in 1.6.0-beta1 on iOS and Android. To read more about 1:1 Calling feature, please visit Set up one-to-one calling in the UI Library.

Clone this wiki locally