Skip to contentSkip to navigationSkip to topbar
On this page

Troubleshoot Call Issues with Voice Mobile SDK PreflightTest


The PreflightTest for Voice Mobile SDKs allows you to anticipate and troubleshoot end users' connectivity and bandwidth issues before or during Twilio Voice calls.

You can run a PreflightTest before a mobile Twilio Voice call. The PreflightTest performs a test call to Twilio and provides a [JSON-serialized report at the end. The report includes information about the end user's network connection (including jitter, packet loss, and round trip time) and connection settings.


Run a PreflightTest

run-a-preflighttest page anchor

Below is an example of creating a PreflightTest in the iOS and Android SDKs and listening for the events that the test emits. Note that the test requires an Access Token; learn more about how to generate the Access Token below.

(information)

Info

You do not instantiate a TVOPreflightTest (iOS) or PreflightTest (Android) instance directly. Instead, the SDK returns a PreflightTest object when you call runPreflightTestWithAccessToken (iOS) or runPreflight (Android).

iOS SDKAndroid SDK
1
2
class ViewController: UIViewController {
3
var preflightTest: PreflightTest? = nil
4
5
func performPreflight() {
6
let preflightOptions = PreflightOptions(accessToken: accessToken, block: { builder in
7
builder.preferredAudioCodecs = [OpusCodec()]
8
})
9
10
preflightTest = TwilioVoiceSDK.runPreflightTest(options: preflightOptions, delegate: self)
11
}
12
}
13
14
extension ViewController: PreflightDelegate {
15
func preflightDidComplete(preflightTest: PreflightTest, report: PreflightReport) {
16
// Check the result in the report
17
}
18
19
func preflightDidFail(preflightTest: PreflightTest, error: any Error) {
20
// Check the failure reason in the error
21
}
22
23
func preflightDidConnect(preflightTest: PreflightTest) {
24
// preflight test has connected
25
}
26
}

You need an Access Token and a Voice application (either a TwiML app(link takes you to an external page) or your own backend server that generates TwiML) to initiate the test call. The endpoint you've configured in your Voice application for the test call should be able to record audio from a microphone and play it back to the browser.

See Create a TwiML App and Access Token for the PreflightTest for an example application that can handle the PreflightTest test call and more information.


The PreflightTest emits the following events that your frontend application can listen for:

completedEvent

completedevent page anchor

Raised when the PreflightTest has successfully completed and the TestFlight object's status has transitioned to completed. It provides a JSON-serialized report object. This event does not occur if the test encountered a fatal error.

The following code shows how to listen for this event. The event passes the completed JSON-serialized report to the event listener.

iOS SDKAndroid
preflightDidComplete(preflightTest:report:)

See the Report section below to view an example of this completed report.

Raised when the PreflightTest has transitioned to the connected state. This indicates that the connection to Twilio has been established.

The following code shows how to listen for this event.

iOS SDKAndroid
preflightDidConnect(preflightTest:)

Raised when the Preflight test has failed. This happens the test cannot establish a connection to Twilio, a test call encounters a fatal error, or stop() is called while the test is in progress. It passes an error object to the event handler.

The following code shows how to listen for this event. The event passes the error to the event listener.

iOS SDKAndroid
preflightDidFail(preflightTest:error:)

Raised when the test call gets a WebRTC sample object. The test publishes this event every second.

The following code shows how to listen for this event. The event passes the test stats to the event listener.

iOS SDKAndroid
preflightDidReceiveStatsSample(preflightTest:statsSample:)

Raised whenever the test call encounters a warning, or when a previously raised warning clear. The warnings relate to call or network quality issues.

The following code shows how to listen for this event. The event passes the current set of quality warnings to the event listener, along with a set of previously raised warnings. You can use these two sets to see if there is a new warning or previous warnings have cleared.

iOS SDKAndroid
preflightDidReceiveQualityWarnings(preflightTest:currentWarnings:previousWarnings:)

Method and property reference

method-and-property-reference page anchor
iOS SDKAndroid

(void) stop

Calling this will result in preflight test to be in TVOPreflightTestStatusFailed state and will raise a failed event with an error code 31008 indicating that the call has been cancelled.

TVOPreflightTest.callSid

The call SID for the preflight test call.

TVOPreflightTest.startTime

Preflight test start time. Unix timestamp in milliseconds.

TVOPreflightTest.endTime

Preflight test end time. Unix timestamp in milliseconds.

TVOPreflightTest.preflightReport

The preflight test report. If accessed when the preflight status is anything other than TVOPreflightTestStatusCompleted or COMPLETED, an empty report object will be returned.

TVOPreflightTest.latestSample

Returns the latest stats sample for the preflight test.

TVOPreflightTest.status

The current state of the preflight test. Below are the possible values for this property.

ValueDescription
TVOPreflightTestStatusCompletedThe connection to Twilio has been disconnected and the test call has completed.
TVOPreflightTestStatusConnectedThe connection to Twilio has been established.
TVOPreflightTestStatusConnectingConnecting to Twilio has started.
TVOPreflightTestStatusFailedThe test has stopped and failed.

Below is an example report returned to the completed event handler or accessed via TVOPreflightTest.preflightReport (iOS) or PreflightTest.getReport() (Android). See below for an explanation of the report's fields.

1
{
2
"edge":"US_WEST_OREGON",
3
"callSid":"CAxxxxx....xxxxxx",
4
"selectedIceCandidatePair":{
5
"remoteCandidate":{
6
"candidateType":"local",
7
"transportId":"0",
8
"protocol":"udp",
9
"networkCost":0,
10
"networkId":0,
11
"url":"",
12
"priority":2130706431,
13
"networkType":"Unknown",
14
"relatedAddress":"",
15
"deleted":false,
16
"ip":"168.86.145.92",
17
"tcpType":"",
18
"isRemote":true,
19
"relatedPort":0,
20
"port":12074
21
},
22
"localCandidate":{
23
"candidateType":"relay",
24
"transportId":"0",
25
"protocol":"udp",
26
"networkCost":10,
27
"networkId":1,
28
"url":"turn:global.turn.twilio.com:3478?transport=udp",
29
"priority":41820671,
30
"networkType":"Wifi",
31
"relatedAddress":"0.0.0.0",
32
"deleted":false,
33
"ip":"54.244.51.46",
34
"tcpType":"",
35
"isRemote":false,
36
"relatedPort":0,
37
"port":54397
38
}
39
},
40
"networkStats":{
41
"jitter":{
42
"average":1.4090909090909092,
43
"min":0,
44
"max":5
45
},
46
"mos":{
47
"average":4.4161981818181824,
48
"min":4.4081799999999998,
49
"max":4.4195489999999999
50
},
51
"rtt":{
52
"average":27.454545454545453,
53
"min":0,
54
"max":40
55
}
56
},
57
"iceCandidates":[
58
{
59
"candidateType":"relay",
60
"transportId":"0",
61
"protocol":"udp",
62
"networkCost":10,
63
"networkId":1,
64
"url":"turn:global.turn.twilio.com:3478?transport=udp",
65
"priority":41820671,
66
"networkType":"Wifi",
67
"relatedAddress":"0.0.0.0",
68
"deleted":false,
69
"ip":"54.244.51.46",
70
"tcpType":"",
71
"isRemote":false,
72
"relatedPort":0,
73
"port":54397
74
},
75
{
76
"candidateType":"relay",
77
"transportId":"0",
78
"protocol":"udp",
79
"networkCost":10,
80
"networkId":1,
81
"url":"turn:global.turn.twilio.com:443?transport=tcp",
82
"priority":25042943,
83
"networkType":"Wifi",
84
"relatedAddress":"0.0.0.0",
85
"deleted":false,
86
"ip":"54.244.51.46",
87
"tcpType":"",
88
"isRemote":false,
89
"relatedPort":0,
90
"port":39989
91
}
92
],
93
"selectedEdge":"roaming",
94
"statsSamples":[
95
{
96
"packetsReceived":0,
97
"rtt":0,
98
"audioInputLevel":0,
99
"packetsSent":0,
100
"timestamp":"1731699119243.266113",
101
"jitter":0,
102
"mos":4.4081799999999998,
103
"audioOutputLevel":0,
104
"bytesReceived":0,
105
"packetsLost":0,
106
"packetsLostFraction":0,
107
"codec":"",
108
"bytesSent":0
109
},
110
{
111
"packetsReceived":1048,
112
"rtt":34,
113
"audioInputLevel":36,
114
"packetsSent":1026,
115
"timestamp":"1731699140337.642090",
116
"jitter":1,
117
"mos":4.4139400000000002,
118
"audioOutputLevel":924,
119
"bytesReceived":166942,
120
"packetsLost":0,
121
"packetsLostFraction":0,
122
"codec":"PCMU",
123
"bytesSent":164160
124
}
125
],
126
"callQuality":0,
127
"warnings":[
128
{
129
"values":"0.305590",
130
"timestamp":1731699128298,
131
"name":"constant-audio-input-level",
132
"threshold":"1.000000"
133
}
134
],
135
"networkTiming":{
136
"iceConnection":{
137
"endTime":1731699119809,
138
"startTime":1731699119090,
139
"duration":719
140
},
141
"preflightTest":{
142
"endTime":1731699141306,
143
"startTime":1731699117268,
144
"duration":24038
145
},
146
"signaling":{
147
"endTime":1731699117884,
148
"startTime":1731699117268,
149
"duration":616
150
},
151
"peerConnection":{
152
"endTime":1731699119243,
153
"startTime":1731699119135,
154
"duration":108
155
}
156
},
157
"isTurnRequired":"true",
158
"warningsCleared":[
159
{
160
"name":"constant-audio-output-level",
161
"timestamp":1731699132313
162
}
163
]
164
}
165
NameDescription
callSidThe Call SID for the test call.
edgeThe Twilio Edge location that the test call connected to.
iceCandidatesAn array of WebRTC stats for the ICE candidates gathered when connecting to media. Each item is an RTCIceCandidateStats(link takes you to an external page) object which provides information related to an ICE candidate.
networkTiming- ice: Measurements for establishing ICE connection. This is measured from ICE connection checking to connected state. See the documentation for RTCPeerConnection.iceConnectionState(link takes you to an external page) for more information.
- peerConnection: Measurements for establishing a Peer Connection. This is measured from PeerConnection connecting to connected state. See the documentation for RTCPeerConnection.connectionState(link takes you to an external page) for more information.
- signaling: Measurements for establishing Signaling connection. This is measured from initiating a connection using device.connect() up to when RTCPeerConnection.signalingState transitions to stable state. See the documentation for RTCPeerConnection.signalingState(link takes you to an external page) for more information.
statsSamplesWebRTC samples collected during the test. See the object format on the RTCSample Interface reference page(link takes you to an external page).
selectedEdgeThe edge you set via the TwilioVoiceSDK before placing the call. If you did not explicitly set the edge, it defaults to gll.
networkTimingTiming measurements from the test. Includes millisecond timestamps and duration.
- iceConnection: How long the ICE connection took to establish.
- preflightTest: The total duration of the PreflightTest.
-signaling: How long it took to establish the signaling connection.
- peerConnection: How long it took to establish the peer connection.
warningsAll warnings detected during the test, including warnings that clea.
warningsClearedWarnings cleared during the test.
selectedIceCandidatePairA WebRTC stats object for the ICE candidate pair used to connect to media, if candidates were selected. Each item is an RTCIceCandidatePairStats(link takes you to an external page) object which provides information related to an ICE candidate.
isTurnRequiredWhether a TURN server is required to connect to media. This is dependent on the selected ICE candidates, and will be true if either is of type "relay", false if both are of another type, or undefined if there are no selected ICE candidates.
callQualityThe quality of the call, determined by the MOS (Mean Opinion Score) of the audio stream. Possible values include:
- 0: If the average mos is over 4.2.
- 1: If the average mos is between 4.1 and 4.2 both inclusive.
- 2: If the average mos is between 3.7 and 4.0 both inclusive.
- 3: If the average mos is between 3.1 and 3.6 both inclusive.
- 4: If the average mos is 3.0 or below.

Create a TwiML App and Access Token for the PreflightTest

create-a-twiml-app-and-access-token-for-the-preflighttest page anchor

The PreflightTest requires a Twilio Access Token to run the test. It also requires a backend application that can generate TwiML to handle incoming phone calls from the PreflightTest. The app should be able to record audio from a microphone or an audio file and play it back to the browser.

Below are instructions for creating an TwiML App to handle PreflightTest calls and generate an Access Token.

Create TwiML Bins to record and playback the PreflightTest call

create-twiml-bins-to-record-and-playback-the-preflighttest-call page anchor

The PreflightTest API requires a server-side application that can record audio from a microphone or an audio file and play the recorded audio back to the browser. This is how the PreflightTest verifies your connection to Twilio and the end user's network quality.

To accomplish this via a TwiML App, you need two TwiML endpoints: one to capture and record the audio, and another one to play the recorded audio.

This example uses TwiML Bins, which are serverless functions that Twilio hosts where you can provide TwiML instructions for the TwiML app. Start by going to the TwiML Bin(link takes you to an external page) page in the Twilio Console.

Playback TwiML Bin

playback-twiml-bin page anchor

Create a new TwiML Bin with the "+" button and use "Playback" as the Bin's friendly name.

Then paste the following TwiML under the "TwiML" section:

1
<?xml version="1.0" encoding="UTF-8"?>
2
3
<Response>
4
<Say>You said:</Say>
5
<Play loop="1">{{RecordingUrl}}</Play>
6
<Say>Now waiting for a few seconds to gather audio performance metrics.</Say>
7
<Pause length="3"/>
8
<Say>Hanging up now.</Say>
9
</Response>

This TwiML tells Twilio to do the following when it receives an incoming call:

  • Say "You said".
  • Play the recording of the call that the PreflightTest makes in the next TwiML Bin.
  • Say "Now waiting for a few seconds to gather audio performance metrics."
  • Pause for three seconds.
  • Say "Hanging up now."

Using the TwiML Bin(link takes you to an external page) page, create another TwiML Bin by clicking the plus button on that screen and use "Record" as the friendly name.

Replace the action URL in the following template (action=) with your "Playback" TwiML Bin's URL that you created previously.

1
<?xml version="1.0" encoding="UTF-8"?>
2
3
<Response>
4
<Say>Record a message in 3, 2, 1</Say>
5
<Record maxLength="5" action="<your Playback TwiML Bin's URL>"></Record>
6
<Say>Did not detect a message to record</Say>
7
</Response>

This TwiML tells Twilio to do the following when it receives an incoming call:

  • Say "Record a message in 3, 2, 1."
  • Record the call for five seconds and then make an HTTP request to your Playback TwiML Bin's URL, which will run the TwiML you created in the step above.
    • Twilio's request to your Playback TwiML Bin includes a RecordingUrl parameter, which the Playback TwiML Bin uses to play the recording of this call.
  • Say "Did not detect a message to record" if the step above doesn't occur.

Create a TwiML Application

create-a-twiml-application page anchor

Now that you have created the two TwiML Bins to record and playback a Voice call, create a TwiML app to connect the TwiML Bin to a Twilio phone number.

Go to the TwiML Apps(link takes you to an external page) page of the Twilio Console.

  1. Click Create new TwiML App.
  2. Enter a friendly name for the application, such as "PreflightTest".
  3. For the Request URL under Voice Configuration, enter the Record TwiML Bin's URL that you created in the previous section, and then click Create.
  4. On that same page, open the TwiML app that you just created by clicking on it and make note of the SID.

Generate an Access Token for the PreflightTest

generate-an-access-token-for-the-preflighttest page anchor

You can now use this TwiML app SID to create an Access Token to use when creating the PreflightTest.

Follow the instructions on this page for creating a Voice Access Token. Substitute the outgoingApplicationSid value with your new TwiML App's SID. Then, you can use the generated Access Token to make PreflightTest calls.

When the PreflightTest initiates a call using the Access Token, Twilio makes an HTTP request to the TwiML App that you associated with the token. That TwiML App then runs the TwiML Bins you created to record and playback the call.

Need some help?

Terms of service

Copyright © 2024 Twilio Inc.