How a bug in Android and Microsoft Teams could have caused this user’s 911 call to fail

Mishaal Rahman on 2021-12-10

Written by Mishaal Rahman (@MishaalRahman) and edited by @linuxct, Kuba Wojciechowski (@Za_Raczke), and Al Sutton (@alsutton).

With contributions from: Kieron Quinn (@Quinny898), Luca Stefani (@luca020400), Matheus Kiser (@matheuskiser), and James Woglom (@jbwoglom)

On the 29th of November, Reddit user KitchenPicture 5849 posted a harrowing story about how their Pixel phone failed to connect to emergency services. The user, who owns a Google Pixel 3 running Android 11, attempted to dial 911 to get their grandmother medical attention for a suspected stroke. After placing the call, the user said the phone call “got stuck immediately after one ring” and that they were “unable to do anything other than click through apps with an emergency phone call running in the background.”

Thankfully, the user’s grandmother had landline service, so the user was able to call 911 in the end, but the situation was nonetheless very troubling for the user. The public clearly agrees, seeing as the post on /r/GooglePixel became the subreddit’s most upvoted post of all time. After the post blew up, the Reddit user reproduced the bug and stated that, five minutes after initiating the call, there was no response from emergency services or evidence that 911 had been called — both the on-device phone log and the carrier (Verizon) phone log came up empty.

Google’s support team contacted the Reddit user to diagnose the issue, and on December 8th, a representative for the company posting to the PixelCommunity account finally publicly addressed the matter. According to Google’s investigation, the issue can occur when the user has the Microsoft Teams app installed but not logged in, a circumstance that can lead to an “unintended interaction between the Microsoft Teams app and the underlying Android operating system.” Here’s Google’s full statement shared by the PixelCommunity account:

“Based on our investigation we have been able to reproduce the issue under a limited set of circumstances. We believe the issue is only present on a small number of devices with the Microsoft Teams app installed when the user is not logged in, and we are currently only aware of one user report related to the occurrence of this bug. We determined that the issue was being caused by unintended interaction between the Microsoft Teams app and the underlying Android operating system. Because this issue impacts emergency calling, both Google and Microsoft are heavily prioritizing the issue, and we expect a Microsoft Teams app update to be rolled out soon — as always we suggest users keep an eye out for app updates to ensure they are running the latest version. We will also be providing an Android platform update to the Android ecosystem on January 4.

Out of an abundance of caution, in the meantime, we suggest users with Microsoft Teams installed on any Android device running Android 10 and above take the following steps:

If you are unsure what Android version you are on, confirm you are running Android 10 or above by following the steps here. If you are not running Android 10 or above, you are not impacted by this issue.

If you have the Microsoft Teams app downloaded, check to see if you are signed in. If you have been signed in, you are not impacted by this issue, and we suggest you remain signed in until you’ve received the Microsoft Teams app update.

If you have the Microsoft Teams app downloaded, but are not signed in, uninstall and reinstall the app. While this will address the problem in the interim, a Microsoft Teams app update is still required to fully resolve the issue.

We advise users to keep an eye out for an update to the Microsoft Teams app, and ensure it is applied as soon as available. We will update this post once the new version of Microsoft Teams is available to 100% of users.

We take issues like this extremely seriously, and want to thank u/KitchenPicture5849 for bringing it to our attention.”

Understandably, the revelation that a third-party app like Microsoft Teams was able to interfere with a process as critical as emergency dialing had many users upset and confused. Under no circumstance should emergency dialing be blocked on a device; carriers are required by law to route emergency calls to the local emergency services even if the device doesn’t have a SIM card provisioned on their network.

So what actually happened in this case and what exactly are Google and Microsoft doing to mitigate this problem? Thanks to my friend Kuba Wojciechowski (@Za_Raczke on Twitter), I’ve found the clues to piece things together. With his help, I was able to find the code changes that mitigate this issue and work backwards from there to find out what the Microsoft Teams app was doing wrong and how that affected Android’s emergency calling service.

Too many c̶o̶o̶k̶s̶ PhoneAccounts

The issue can be traced to the methods adjustAttemptsforEmergency and sortSimPhoneAccountsForEmergency in the CreateConnectionProcessor class. The adjustAttemptsforEmergency method decides which PhoneAccount instance should handle the emergency call. According to Google’s documentation, “apps which can place calls and want those calls to be integrated into the dialer and in-call UI should build an instance of [the PhoneAccount] class and register it with the system using TelecomManager.” Basically, many Android apps with phone calling functionality — whether that’s through the stock dialer framework or a custom service connecting to a remote endpoint — will want to build an instance of PhoneAccount. This includes the Microsoft Teams app, which uses the Skype backend to make voice calls.

Since Microsoft Teams manages its own Connection in lieu of the default phone app, the app registers a PhoneAccount with the CAPABILITY_SELF_MANAGED flag. However, since the Teams app cannot handle emergency calling, it does not register with the CAPABILITY_PLACE_EMERGENCY_CALLS flag. The importance of these two constants will become clear shortly.

If we go back to the adjustAttemptsForEmergency class, the first thing we’ll notice is that the class gets a list of all registered instances of PhoneAccount.

This includes instances without CAPABILITY_PLACE_EMERGENCY_CALLS defined, which includes the instances built by the Microsoft Teams app. The next block of code adds a fallback emergency phone account in case the list of PhoneAccount instances is empty, so we can move on. We can also skip the next block which deals with testing emergency calls through a test connection service.

Next, though, adjustAttemptsForEmergency attempts to get the “user-preferred” PhoneAccount instance, which is the PhoneAccount instance that the Telephony service thinks should handle emergency calls.

However, it doesn’t end there, as Android will next sort the list of PhoneAccounts in case there are multiple instances with CAPABILITY_PLACE_EMERGENCY_CALLS defined and one of them ranks higher than the “user-preferred” PhoneAccount.

You may have figured out one problem here: sortSimPhoneAccountsforEmergency is called with a list that contains all PhoneAccount instances, even those without CAPABILITY_PLACE_EMERGENCY_CALLS. Microsoft Teams should not even be in the list to begin with, and yet it is. If that was the root problem of this bug, though, then every Android phone with Microsoft Teams installed wouldn’t be able to dial 911, and that’s clearly not the case.

Within the sortSimPhoneAccountsforEmergency method, though, is code that can result in an integer overflow/underflow error under the right circumstances. This method sorts the list of PhoneAccounts in ascending order by performing a few comparisons between every Phone Account in the list. These comparisons, in order, include checking which PhoneAccount supports CAPABILITY_SIM_SUBSCRIPTION, which is the aforementioned “user-preferred” account, and which account is associated with a valid subscription ID and SIM slot index. If the two PhoneAccount instances being compared are the same, the method will then order the list by package name, label, and finally, by hashcode. Even if the two PhoneAccount instances being compared have the same capabilities, package name, and label, they may have different hashcode values.

The problem with this line as it’s written is it can result in an integer overflow if the value of account1.hashCode() — account2.hashCode() is less than Integer.MIN_VALUE or greater than Integer.MAX_VALUE. There usually aren’t that many PhoneAccounts to sort through, and the method usually never reaches this block of code anyway since the other comparisons have likely succeeded at ranking the two PhoneAccount instances, so the chances of this mathematical calculation actually resulting in an integer overflow/underflow is really, really low.

But what if there were potentially dozens of duplicate PhoneAccount instances for this method to sort through? If that’s the case, then the chances of an integer overflow/underflow triggering go up. Even without analyzing the decompiled code of the Microsoft Teams app, it’s easy to confirm that there’s a bug in the app that results in the excessive registration of PhoneAccount instances. Whenever the Microsoft Teams app is installed but the user has not signed in, every cold launch of the app results in the creation of another PhoneAccount instance.

Prior to installing Microsoft Teams, there are only two registered instances of PhoneAccount on my Google Pixel 4 running Android 12 SQ1A.211205.008. One instance was built by TelephonyService while the other by Google Duo.
After installing Microsoft Teams and launching it (but not signing in), a third instance of PhoneAccount appears. This instance was built by Microsoft Teams.
After force closing Microsoft Teams and relaunching it, it built another instance of PhoneAccount
After repeating the last step 15 more times, I ended up with 17 basically duplicate PhoneAccount instances built by Microsoft Teams.
After uninstalling then reinstalling Microsoft Teams, I chose to login immediately after launching the app. Then, I force closed the app and re-launched it multiple times. While a second PhoneAccount instance was made, no more were created after that.

I do not know which release of the Microsoft Teams app first introduced this behavior, but I can confirm that versions 1416/1.0.0.2021163901 and 1416/1.0.0.2021183702 of the app exhibit it. The former was uploaded to APKMirror on October 28th, 2021, approximately one month before the Reddit user encountered the bug. The latter is the latest Play Store release.

If you’re interested in reproducing this behavior yourself, install one of the versions of Teams that I mentioned and run the following ADB shell command to see the list of PhoneAccounts:

dumpsys telecom | sed -n ‘/PhoneAccountRegistrar/,/Analytics/p’

Uninstalling and then reinstalling the app will clear all of the PhoneAccount instances it creates, which is one of the mitigation steps that Google recommends.

I also do not know how exactly the Reddit user got so many Microsoft Teams-built PhoneAccount instances on their device. I do not use Microsoft Teams that often, but from what I’ve read online, there have been problems where it frequently logs the user out. I have also read reports that enterprises can set a policy to log the user out from time to time for security reasons.

A series of unfortunate events

After inspecting a decompiled version of the Microsoft Teams application, we were able to determine why a new PhoneAccount instance appears every time the app restarts. We found that when the user is not signed in, a new, randomly generated UUID is used to create the PhoneAccount instance that gets added to Android’s TelecomManager. This means that every time the Teams app restarts or crashes, a new UUID is generated for users that are not logged in, and thus a new PhoneAccount is added to Android’s TelecomManager. Because Teams has a boot broadcast receiver, this also happens every time the phone is rebooted.

Relevant pseudo-code:

String userId = MS_Teams_Current_User_ID;

if (StringUtils.isEmptyOrWhiteSpace(userId)) {

userId = UUID.randomUUID().toString();

}

TelecomManager.registerPhoneAccount(PhoneAccount.builder(new PhoneAccountHandle(componentName, userId), applicationName).setCapabilities(capabilities).build());

(Here, the userId + componentName make up the PhoneAccountHandle which is a unique id used to distinguish between different PhoneAccounts in AOSP. userId is used within Teams to identify the Teams session and is not explicitly used outside of the app.)

Users that have logged into the Teams app won’t have duplicate PhoneAccount instances created because the userId will be the same as the already registered PhoneAccount. This also explains why, in the case where I installed and immediately signed into Teams, there were still two PhoneAccount instances built by Teams: One was built with the randomly generated UUID, while the other is with the Teams account’s userId.

Here is the stack trace showing Teams calling the registerPhoneAccount method in the PhoneAccountRegistrar when the app restarts.

Whatever the case, an abundance of duplicate PhoneAccount instances raises the possibility of triggering an integer overflow/underflow error when the user attempts to make an emergency call. Whether or not the integer overflow/underflow error is the exact step where everything goes wrong is something I can’t determine without examining how that error affects the rest of the Telephony stack. This requires further analysis.

Two patches, with more surely to come

I am fairly certain that this integer overflow/underflow bug is where things start to go wrong, though, as it’s exactly what this code change addresses. This code change, titled “fix the integer overflow/underflow caused by sorting of duplicate phoneaccounts during emergency call attempt”, was interestingly submitted by a Samsung engineer on November 25th, four days before the Reddit thread went up. The linked bug report sadly isn’t public, so we can’t see how the engineers discovered this issue. Thus, we don’t know for sure if the bug report was prompted by the same Microsoft Teams duplicate PhoneAccount issue, but it’d be one hell of a coincidence if it wasn’t.

In any case, this code change fixes the integer overflow/underflow issue in a simple way. It replaces the simple subtraction used to compare hashcodes with Integer.compare, which only returns -1, 0, or 1. It’s worth noting that the Teams app only registers PhoneAccounts if it’s running on API level 28+ (Android 9 Pie and later), but the code change that implemented the sortSimPhoneAccountsforEmergency method as it was originally written was committed to AOSP in mid-2019 and was included in the android10-release branch, hence why this bug only affects Android devices running Android 10+.

That’s not the only code change addressing this issue, though. Another one was merged to the public AOSP master branch just the other day. This code change “is a clean cherry-pick from internal gerrit”, according to the Googler who submitted the patch. It adds new lines to the adjustAttemptsForEmergency method to filter out PhoneAccount instances that are self-managed, ie. with CAPABILITY_SELF_MANAGED defined. This will prevent PhoneAccount instances like the ones built by Microsoft Teams from ever being passed to the sortSimPhoneAccountsforEmergency method, which is sensible since only the default phone service should handle emergency calls.

Google says they will be providing “an Android platform update to the Android ecosystem on January 4th.” The Telecomm service is not contained within one of the 23 Mainline modules listed by Google, so I do not think it will be patched via a Google Play System Update. As such, the patches may be delivered to devices as part of the 2022–01–01 security patch level, which should start rolling out to devices when the January 2022 Android Security Bulletin is made public on January 3rd, 2022, the first Monday of the month. (I’m not sure why January 4th is listed as that’s a Tuesday.)

In any case, an update is coming soon, and not just from Google. Microsoft will be rolling out a new version of Teams that will likely resolve the issue with duplicate PhoneAccount instances being created. Microsoft can resolve this bug on their end by generating a UUID once and reading it from SharedPreferences.

I don’t think most users will need to fear this bug, because it requires a very specific set of circumstances to trigger. And even when those circumstances are met, it’s basically bad luck if it gets triggered. If you’re concerned, though, be sure to follow the steps outlined by Google in this post.

Update 1 (12/10/2021 @ 10:19 PM ET): Microsoft has started to roll out version 1416/1.0.0.2021194504 of the Teams app. This version resolves the issue with the spawning of too many duplicate PhoneAccounts, and it also calls the clearPhoneAccounts method of TelecomManager at first launch to clear all PhoneAccounts it previously created. This means you don’t need to uninstall and reinstall Teams — just update to the new version!

Update 2 (12/10/2021 @ 11:40 PM ET): If you would like to check if any apps on your device are registering too many instances of PhoneAccount (which is a possible sign of abuse), @linuxct has developed a simple open-source app that lists all PhoneAccounts and evaluates if too many have been registered for a certain application. You can read more details on Twitter or download the app from GitHub.