SystemUI
The status bar, the shade, quick settings, the lock screen and the navigation bar are all drawn by one privileged app. SystemUI is not part of the framework — it is an APK that holds special powers and can crash without taking the system down.
SystemUI is the part of Android that feels like the operating system and is, in fact, an application. It draws the status bar, the shade, the lock screen, the volume panel and the navigation bar, and it does so from a process that can be killed and restarted.
TL;DR#
- SystemUI is a privileged system app, not a framework component. It lives in
frameworks/base/packages/SystemUI/. - It draws the status bar, notification shade, quick settings, lock screen, volume panel, navigation bar and, in some versions, recents.
- It receives notifications through a privileged listener connection and renders them from
RemoteViews. - Quick Settings tiles have a public API since Android 7.0; apps can add their own.
- Its plugins system lets OEMs customise it without forking the whole app.
- When it crashes, the status bar disappears while the rest of the system keeps running — which is the clearest proof that it is an app.
Mental model#
Think of a hotel's front desk.
The building is the framework and system_server: lifts, plumbing, locks. The front desk does
not maintain any of it.
The front desk is SystemUI. It is where guests see their messages, where the lift status is displayed, and where the concierge takes requests. It is staffed by employees who can be replaced without closing the hotel.
The concierge's authority is delegated: the desk can open doors because the building gave it a master key, not because it owns the locks.
Where the analogy breaks#
A front desk is a place. SystemUI is a process with a window that sits above every other window, and
its "master key" is a set of signature|privileged permissions plus a direct Binder connection to
system_server. The delegation is technical and enumerable, not social.
The big picture#
Text description of this diagram
A three-column map of every surface attributed to its real owner. The SystemUI column holds the status bar icons and clock, the notification shade, the Quick Settings panel since API 17 and third-party tiles since API 24, the lock screen keyguard since API 21, the volume panel, and the navigation bar. The system_server column holds NotificationManagerService, which validates and ranks notifications; ActivityManagerService, which owns tasks and processes; and WindowManagerService, which treats the shade and status bar as windows. The launcher column holds the recents and overview gesture since API 28 through Quickstep, and widget hosting since API 3. Three notes below: SystemUI is not in system_server and runs in its own process, so when it ANRs the status bar freezes while the rest of the phone works; its powers come from signature privileged permissions and a build-time listener grant; and it renders app content as RemoteViews descriptions rather than running app code.
The modularity is what allows an OEM to ship a different shade without forking the framework, and what allows Google to update parts of SystemUI through Play on supported devices.
Deep dive#
What SystemUI actually draws#
The list is longer than most people expect:
| Surface | Notes |
|---|---|
| Status bar | Icons, clock, battery, privacy indicators |
| Notification shade | The list of notifications, grouped and ranked |
| Quick Settings | The tile grid behind the shade |
| Lock screen | On many devices, including the notification presentation |
| Volume panel | The system volume UI |
| Navigation bar | Gesture and button navigation, in most versions |
| Recents | Historically in SystemUI; on modern devices it is part of the launcher (Quickstep) |
| Keyguard | The lock screen state machine, not the lock screen's visual only |
The recents entry is the one that moves. On modern Android, the recents/overview gesture is implemented by the launcher through the Quickstep integration, while SystemUI still owns parts of the transition. That split is a frequent source of confusion when debugging.
The privileged connection#
SystemUI holds a NotificationListenerService grant — the same API available to third-party apps,
but granted at build time rather than by the user. Through it, it receives every notification as a
StatusBarNotification and renders the content.1
It also holds permissions such as STATUS_BAR, EXPAND_STATUS_BAR and STATUS_BAR_SERVICE, which
are signature|privileged and unavailable to ordinary apps.
Quick Settings tiles#
The tile API has been public since Android 7.0 (API 24). A tile is a TileService declared in the manifest
with android.service.quicksettings.action.QS_TILE:2
class FocusTileService : TileService() {
override fun onStartListening() {
qsTile?.apply {
state = if (isFocusOn()) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
label = "Focus"
updateTile()
}
}
override fun onClick() {
toggleFocus()
qsTile?.apply {
state = if (isFocusOn()) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
updateTile()
}
}
}The system binds to the service, calls onStartListening when the shade opens, and onClick when
the user taps. The app does not draw the tile; it supplies a state, a label, an icon and optionally
a subtitle or a dialog.
The constraint worth knowing: a tile is not a widget. You cannot put arbitrary content in it, which is why tiles are limited to an icon, a label and a state — and why media controls in Quick Settings are a system feature, not a tile.
The plugin architecture#
OEMs need to change SystemUI without forking the whole app, and Google needs to ship a single SystemUI that can look different on different devices. The answer is a plugin system: SystemUI loads implementation classes by interface, and the device's overlay or a plugin APK supplies the implementation.
This is why the AOSP source has a plugins/ directory containing interfaces with names like
GlobalActions, NotificationUI, NavBar and Keyguard. The interfaces are the contract; the
implementations are replaceable.
Rendering other apps' content safely#
The security problem SystemUI faces is unusual: it must render content supplied by untrusted apps, at high priority, above other windows.1
Three defences matter:
- RemoteViews allowlist. Only a fixed set of view classes and methods can be inflated from a notification's custom layout.
- Size limits. A notification's view hierarchy is bounded; the platform rejects layouts that exceed the limits, which is what Android 17's tightening continues.
- No code execution. The app's process is not involved in rendering. Only a
PendingIntentfires app code, and it fires in the app's process with the app's identity.
The lock screen and privacy#
The lock screen is SystemUI's most privacy-sensitive surface. The visibility set on a notification
decides whether its content appears there:1
VISIBILITY_PUBLIC— the full content is shown.VISIBILITY_PRIVATE— the content is replaced by the app's public version.VISIBILITY_SECRET— nothing is shown.
A notification that sets VISIBILITY_PUBLIC on a messaging app puts message text in front of anyone
holding the phone. That is a decision the app makes, and it is frequently the wrong one.
Why SystemUI is a separate app at all#
The separation is not an accident of history; it is a deliberate boundary with three benefits.
Failure isolation. A bug in the shade should not take down the window manager. Because SystemUI is a process, an ANR there freezes the status bar and leaves everything else running. That is a degraded experience, not a dead phone.
Independent updateability. Parts of SystemUI can be updated without a full platform release, which matters more as the module structure matures. The shade is a place where a security fix should not require an OTA.
Replaceability for OEMs. A vendor can ship a different shade, a different quick settings layout or a different lock screen without forking the framework. The plugin architecture exists to make that possible without a fork of the whole app.
The cost is the one every process boundary has: every piece of data SystemUI displays must cross Binder, and every cross-boundary interaction is a place where a size limit or a permission check can intervene.
Debugging the shade#
SystemUI has its own dump and its own logs, and they are the fastest way to understand a rendering problem.
adb shell dumpsys activity service com.android.systemui/.SystemUIService prints the state of the
status bar, the shade and the keyguard. Logcat tags beginning with SystemUI cover the row
inflation path. When a notification appears in dumpsys notification but not on screen, the
problem is almost always in SystemUI's rendering rather than in the notification service, and the
two dumps together localise it in a minute.
The difference between "system" and "framework"#
The vocabulary is worth fixing because it explains a lot of Android's behaviour.
The framework is the code in the bootclasspath and system_server: the APIs and the services.
It is what an app calls.
The system apps are APKs that ship on the image and hold privileged permissions: SystemUI, Settings, the launcher, the phone app. They call the framework like any app, with a bigger set of permissions.
"System" is therefore a deployment category, not an architectural layer. SystemUI is a system app; the notification service it reads from is a framework service. Confusing the two leads to the wrong mental model of where a bug lives, and to the wrong place to look for a fix.
How it evolved#
| API | Version | What changed |
|---|---|---|
| 1 | 1.0 |
|
| 3 | 1.5 Cupcake |
|
| 9 | 2.3 Gingerbread |
|
| 11 | 3.0 Honeycomb |
|
| 12 | 3.1 Honeycomb |
|
| 16 | 4.1 Jelly Bean |
|
| 20 | 4.4W KitKat (Wear) |
|
| 21 | 5.0 Lollipop |
|
| 24 | 7.0 Nougat |
|
| 26 | 8.0 Oreo |
|
| 28 | 9 Pie |
|
| 29 | 10 |
|
| 30 | 11 |
|
| 31 | 12 |
|
| 33 | 13 Tiramisu |
|
| 36 | 16 Baklava |
|
Under the hood#
- the whole app: status bar, shade, keyguard, QS
- the notification row inflation and ranking presentation
- the Quick Settings implementation
- the plugin interfaces OEMs implement
- the lock screen state machine
Curiosity corner#
- SystemUI has its own ANR dialog. "System UI isn't responding" is the system reporting that the app drawing your status bar has stopped responding, which is why the status bar freezes but the rest of the phone works.
- The privacy indicators are SystemUI, reading app op state rather than a dedicated privacy signal.
- Some "system" UI is not SystemUI. The power menu, the volume dialog and the recents gesture
have moved between SystemUI,
system_serverand the launcher across releases.
Try it#
adb shell ps -A | grep systemui
Example output
u0_a123 1234 567 com.android.systemuiadb shell dumpsys activity service com.android.systemui/.SystemUIService | head -40
Example output
StatusBar: mState=1 mVisible=trueadb shell dumpsys package | grep -i QS_TILE
Example output
com.example.app/.FocusTileServiceBuild it#
Write a TileService that toggles something real — a focus mode, a VPN, a Do Not Disturb state —
and add it to Quick Settings. The interesting part is the lifecycle: the system binds the service
when the shade opens and unbinds when it closes, so the tile must be able to render from persisted
state without a network call.
Check yourself#
Check yourself
Put these notification features in the order they were introduced.
Drag, or use the arrows to reorder.
- 1Lock screen notifications and heads-up peeks
- 2POST_NOTIFICATIONS as a runtime permission
- 3Direct reply and bundled groups
- 4Expandable styles and actions
- 5Mandatory notification channels
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.
- docsNotifications overviewdeveloper.android.comaccessed 2026-10-27↩
- docsModular system componentssource.android.comaccessed 2026-10-27↩
- docsCreate and manage notification channelsdeveloper.android.comaccessed 2026-10-27↩
- docsPerubahan perilaku: aplikasi yang menargetkan API level 28+developer.android.comaccessed 2026-10-27↩
- docsBehavior changes: Apps targeting Android 14developer.android.comaccessed 2026-10-27↩