Telephony
The radio is the most vendor-specific part of Android, and the RIL is the seam that tries to hide it. It is also the area where the platform's default-app rules and the law intersect.
Telephony is where the abstract Android stack meets the least standardised hardware on the phone. Every modem speaks a different dialect, every carrier has different requirements, and the result is a stack that is unusually deep and unusually vendor-shaped. It is also the area where the platform makes decisions on behalf of the network operator — and where the default-SMS-app rule lives.
TL;DR#
- The app-facing surface is
TelephonyManager,SmsManagerand the subscription APIs; the decisions are made byTelephonyServiceinsystem_server. - Below it, the RIL (Radio Interface Layer) translates Android's radio requests into commands to the vendor's modem, and the actual modem implementation is entirely vendor-specific.
- The RIL moved from a socket protocol to a versioned HAL as part of Project Treble, which is what allowed the radio stack to become less entangled with the framework.
- SMS and MMS are default-app gated: only the user's chosen SMS app can send and observe them.
- Dual-SIM turned "the phone" into "zero or more subscriptions", which leaked into almost every API.
Mental model#
Think of a phone call as a diplomatic conversation.
Your app is a member of the public writing a request to a ministry. The ministry
(TelephonyService) decides whether the request is legal and which consulate handles it. The
consulate (the RIL) speaks the local language of whichever country it is in — and each country's
language (modem) is different. The ministry has a standard request form; the consulate has to
translate it into whatever the local bureaucracy accepts.
Where the analogy breaks#
A consulate is one building. A phone can have two SIMs, so there may be two consulates, and a request must name which one it is for. There are also cases where the app is not the writer of the request: an incoming call is initiated by the network, and the framework's job is to route it to the right app rather than to originate it.
The big picture#
Text description of this diagram
A sequence diagram of a call or an SMS. The app calls TelephonyManager or SmsManager, and the request crosses Binder to the telephony service in system_server, which checks the caller's role. The request then goes to the RIL, which translates it into the modem's protocol, across the radio HAL to the baseband. Calls and SMS travel in the signalling channel, so they can arrive with mobile data off. An incoming call or message comes back as an unsolicited event through the RIL and is dispatched to the app that holds the relevant default role.
The stack from top to bottom:
| Layer | What it is | Who writes it |
|---|---|---|
| App APIs | TelephonyManager, SmsManager, SubscriptionManager | Google/AOSP |
| Telephony framework | TelephonyService, Phone, SMSDispatcher | AOSP |
| RIL (Java side) | request/response plumbing over the HAL | AOSP |
| Radio HAL | versioned AIDL interface | AOSP defines, vendor implements |
| Vendor RIL + modem | the actual radio firmware | SoC vendor / modem vendor |
The lower two rows are where "most vendor-specific" comes from. The HAL interface is standard; the implementation behind it is not, and the modem firmware may be decades of accumulated vendor code.
Deep dive#
The RIL's history in one paragraph#
Originally the RIL was a socket protocol: the framework's Java code spoke a line-based protocol to a vendor daemon over a local socket, and the vendor daemon talked to the modem. It worked, but it meant the protocol was a de-facto ABI with no versioning discipline. Treble replaced it with a versioned HAL — first HIDL, later AIDL — so the framework and the vendor implementation could be versioned independently.1
That is the same move Treble made everywhere: turn an implicit contract into an explicit one. The practical benefit is that a phone's radio stack can be updated without matching the framework exactly, and a framework update does not require a new modem.
Why telephony is the most vendor-specific area#
Three forces push in the same direction:
- The modem is a black box. Baseband firmware is usually supplied by a modem vendor and runs on its own processor with its own OS. Android cannot inspect it.
- Carrier requirements are real and varied. VoLTE, Wi-Fi calling, carrier-specific APN configuration, emergency call behaviour and SMS encoding all differ between operators.
- Certification is expensive. A change that breaks a carrier's certification is worse than a change that leaves a feature unimplemented, so vendors are conservative.
The result is a stack where the framework is standard, the HAL interface is standard, and almost everything below the HAL varies.
SIM, subscription, and the dual-SIM problem#
Modern Android separates the SIM (a physical or embedded card) from the subscription (an
account with a carrier). SubscriptionManager is the API for the latter. On a dual-SIM phone there
are two subscriptions, and an operation like sending an SMS must specify which one.
This separation is why the APIs look more complicated than "the phone number": there may be no SIM, one SIM, two SIMs, an eSIM profile that is downloaded and activated later, or a subscription that is present but not the default for data. Every one of those is a supported state.
Default apps: SMS and the dialer#
Android's default-app model is unusually strict here, for legal and privacy reasons.
- Only the user's chosen default SMS app can send SMS and receive the SMS delivery broadcast. On Android 4.4 (API 19) this moved from "any app with the permission" to an explicit default-app role.2
- Only a default dialer or a privileged app can place calls without the user seeing the dialer.
- Emergency calls are treated specially and are not simply a
CALL_PHONEinvocation.
The design intent: an app that can silently send premium SMS is a fraud vector. Gating it behind a user choice the user can see, and revoking that choice, makes the attack require social engineering.
What apps can and cannot do#
The API surface is narrower than people expect:
- Allowed: read the device's phone number (if the carrier exposes it and permission is granted),
observe signal strength and network type, place calls through the dialer, send SMS if default, use
TelephonyManagerfor carrier and network metadata. - Not allowed for ordinary apps: change radio state, read other SIMs' messages, control the modem, place calls silently without the default-dialer role.
That shape exists because telephony has direct billing and emergency implications, so the platform optimises for preventing silent actions rather than for app convenience.
Where Mainline and Treble meet#
The radio HAL is a Treble HAL, which means it is versioned against the vendor interface. Parts of
the telephony framework can also ship as Mainline modules. The combination is what gives a phone the
ability to receive a telephony bug fix without a full OS upgrade — and what makes the version
reporting in getprop confusing, because the "Android version" and the "module version" are different
things.
How it evolved#
| API | Version | What changed |
|---|---|---|
| 1 | 1.0 |
|
| 17 | 4.2 Jelly Bean |
|
| 19 | 4.4 KitKat |
|
| 23 | 6.0 Marshmallow |
|
| 24 | 7.0 Nougat |
|
| 26 | 8.0 Oreo |
|
| 28 | 9 Pie |
|
| 29 | 10 |
|
| 30 | 11 |
|
Under the hood#
- the telephony framework: Phone, SMS dispatcher, subscription handling
- the radio HAL contract vendors implement
- the phone app and the default-dialer/SMS behaviour
- the app-facing telephony APIs
Curiosity corner#
- A phone can have no phone number. With eSIM and data-only plans, a valid subscription may have
no MSISDN at all, which is why
getLine1Number()is allowed to return null. - The radio often runs its own OS. Baseband processors frequently run a small real-time OS, with its own memory and its own bugs, isolated from Android by design.
- SMS is not a data service. It travels in the signalling channel, which is why it can arrive when mobile data is off.
Try it#
adb shell dumpsys telephony.registry | head -40
adb shell dumpsys isub
adb shell settings get global airplane_mode_on
Example output
mServiceState=IN_SERVICE
mSignalStrength=SignalStrength: 28 -95 ...
mNetworkType=LTE
SubscriptionInfo: id=1, slotIndex=0, carrierName=Exampledumpsys telephony.registry reports service state and signal; dumpsys isub reports subscriptions.
Comparing them is how you tell "no SIM" from "SIM present but not registered".
Check yourself#
Check yourself
Which component decides which network an app's socket should use?
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docsConnectivity overviewsource.android.comaccessed 2026-10-27↩
- docsAndroid 4.4 APIsdeveloper.android.comaccessed 2026-10-27↩
- docsTelephonyManagerdeveloper.android.comaccessed 2026-10-27↩
- docsManifest.permission | API referencedeveloper.android.comaccessed 2026-10-27↩
- docsApp manifest overviewdeveloper.android.comaccessed 2026-10-27↩