In this post, we’ll be exploring the default blocklist provided on the native Phone app. We’ll discuss how apps can access the blocklist, restrictions on the blocklist and how it’s been implemented.
What is the default blocklist?
The default blocklist provides a light UI to add and remove numbers that you don’t want to receive calls or texts from. Users can add numbers by typing in a number, or by directly blocking/unblocking a number from the caller details page.
Prior to Android Nougat (7.0), Android users relied on downloaded apps to restrict calls and texts from nuisance phone numbers.
According to Google, “Many of those apps either do not work as desired or provide a less-than ideal experience because there are no proper APIs for blocking calls and messages.”
With the introduction of a BlockedNumberProvider in Android Nougat, users can add numbers to a list of phone numbers that they don’t want to receive telephony communications from (calls, SMS, MMS).
Perhaps there are finally proper APIs for blocking calls and messages! Let’s explore this further:
Accessing the default blocklist
Block phone numbers data flow
The diagram above shows the flow of how applications can access the default blocklist.
- Any application can launch an intent to the Telecom UI to access the default blocklist. This Telecom UI is the same UI as shown in the screenshot above.
- Only the default messaging app, default dialer app, or any carrier apps can access the default blocklist.
Huh? So only a few applications can read and write directly to the BlockedNumberProvider? Doesn’t sound like an open API to me. Let’s see if there are any permissions to circumvent this data flow:
In Android access to system APIs and user sensitive data usually requires permissions. Permissions are divided into several protection levels:
- Normal: These permissions are granted automatically (accessing internet for example)
- Dangerous: These permissions require explicit user approval (accessing the camera, or microphone)
- Signature: These permissions are automatically granted, but only if the app that is using this permission is signed with the same certificate as the app that defines the permission. These permissions are typically not granted for apps that can be downloaded from Google Play.
After looking over the permissions overview on the Android developer site, there doesn’t seem to be a permission that allows an application to read/write to BlockedNumberProvider. However, according to the Block phone numbers data flow, there must be a way for certain applications to read/write directly to this provider.
Let’s dig deeper.
Down. Down. Down.
The Android platform is open source. AOSP allows device manufacturers to implement their own custom versions of Android, tailored to their needs. This is why every Android device has a slightly different UI flavor. The underlying functionality for all devices is the same, but presented in a different way per device.
What does this mean?
We can browse the source code and figure out how only specific applications are accessing the blocklist.
In AOSP, we can find a list of all the permissions on the Android platform. Here are the permissions to read/write to BlockedNumberProvider:
- READ_BLOCKED_NUMBERS: A signature level permission
- WRITE_BLOCKED_NUMBERS: A signature level permission
Huh. Usually, signature level permissions are only granted to apps that come shipped with devices by default, not downloaded apps.
It appears there is no way for us to get access to these permissions through normal means.
For exploratory purposes, let’s assume we can somehow get these permissions. What other capabilities can BlockedNumberProvider offer?
Since BlockedNumberProvider is a ContentProvider, we can use a ContentObserver to monitor the provider for onChange() events. These events are fired when the content provider changes, so that apps can perform actions based on the change.
One possible use case for ContentObserver is to show after-call notifications after a user ends a call. When the phone call ends, we can monitor CallLog.Calls.CONTENT_URI to receive an onChange() event indicating that a change has occurred. From there, we can query the ContentProvider for call logs and figure out the change and display the appropriate notification.
Can we receive onChange events for BlockedNumberProvider by monitoring BlockedNumbers.CONTENT_URI? Sadly, no we cannot.
Usage of the API
The insert() method returns a modified URI, that is not the URI passed in as an argument. The notifyChange() is called with this modified URI.
The delete() method uses this same modified URI. The notifyChange() is called with this modified URI.
Therefore, it’s not possible to use a ContentObserver to monitor the BlockedNumbers.CONTENT_URI for onChange() events, which seems like an intentional decision to limit usage of the API to third-party apps.
For third-party apps, BlockedNumberProvider simply does not provide an open API for developers to block calls and messages, which is a shame considering the open source nature of the Android platform. Developers have been waiting for this feature for quite some time and although Google provides a solution, it has limited use cases and is not meant for other applications to use.
With Android P on the horizon, perhaps this will change. At Hiya, we certainly hope so!