A PC-class SystemUI for boringdroid. BoringdroidSystemUI is loaded into the stock AOSP SystemUI process as a SystemUI plugin, so it inherits SystemUI's permissions, UID and signature without forking SystemUI itself. The plugin replaces the navigation bar with a desktop taskbar and layers a notch of desktop-class surfaces on top of it — Start menu, Action Center, Calendar, Overview.
flowchart TB
subgraph Host["com.android.systemui host (AOSP, uid 1000)"]
Overlay["SystemUIOverlay (plugin entry point)"]
Taskbar["TaskbarWindow<br>TYPE_NAVIGATION_BAR<br>providedInsets = 72dp"]
AllApps["AllAppsWindow<br>start menu"]
Action["ActionCenterWindow<br>notifications + QS"]
Calendar["CalendarClockWindow<br>clock + calendar"]
A11y["AccessibilityManager<br>GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS<br>Meta key opens Start menu"]
Overlay --- Taskbar
Overlay --- AllApps
Overlay --- Action
Overlay --- Calendar
Overlay --- A11y
end
subgraph Plugin["com.boringdroid.systemui (own process)"]
Overview["BoringdroidOverviewService<br>bound by OverviewProxyService<br>owns OverviewWindow (TYPE_APPLICATION_OVERLAY)"]
Mirror["BoringdroidNotificationMirror<br>NotificationListenerService"]
end
Overlay -- "ACTION_TOGGLE_OVERVIEW broadcast" --> Overview
Mirror -- "NotificationFeedIpc broadcast" --> Action
Two processes are involved:
-
SystemUI host (
com.android.systemui, uid 1000). The plugin runs here. Everything that needs to draw on the taskbar/status-bar layer — the taskbar itself, Start menu, Action Center, Calendar — is owned bySystemUIOverlayin this process. It talks to the framework via system-signature APIs (WindowManager, AccessibilityManager, NotificationListenerService bindings). -
Plugin package (
com.boringdroid.systemui). Hosts the Overview binder (BoringdroidOverviewService, the IOverviewProxy that SystemUI'sOverviewProxyServicebinds to) and the notification mirror. Runs in its own process becauseOverviewProxyServicerequires the recents component to live outside SystemUI.
Keep that split in mind when wiring new flows: a broadcast that needs to reach the overview window must be addressed to the plugin package, not the SystemUI host.
- Taskbar (
taskbar/Taskbar.kt). Compose-first, pinned viaTaskbarWindowas aTYPE_NAVIGATION_BAR— chosen so WMS propagates theprovidedInsetsit carries. Advertises the taskbar height plus thepanel_taskbar_gapas a navigation-bar inset so Launcher3 and other full-height apps leave room above it. - Start menu (
AllAppsLayout.kt,AllAppsWindow.kt). Search pill, pinned grid, user rail (lock / sign out / power). Toggled by clicking the start button, tapping the taskbar search pill, or pressing the Meta (Windows) key. - Action Center (
actioncenter/ActionCenterLayout.kt). Big clock header, 3x3 QS tile grid backed byQsController/QsTileStore, a media card, and aLazyColumnof notifications fed byNotificationFeed.ACTION_CLEAR_ALLbroadcast + in-panel "Clear all" button. - Calendar panel (
calendar/CalendarClockLayout.kt). Clock + month grid + agenda. Mutually exclusive with Action Center. Backed byCalendarContract.Instancesin a background loader. - Overview (
overview/OverviewLayout.kt). macOS-Mission-Control-style expo — thumbnails fly from their real window bounds to a common-scale grid and back. The window itself is owned byBoringdroidOverviewServicein the plugin process (see above);SystemUIOverlaytriggers it viaACTION_TOGGLE_OVERVIEWbroadcast.
BoringdroidSystemUI is almost pure plugin. On AOSP 14 it still depends on one four-line edit inside the stock framework:
- File:
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java - What it does: early-returns from
createNavigationBar(Display)whenSystemProperties.getBoolean("persist.sys.systemuiplugin.enabled", false)is true (the prop is set byvendor/boringdroid/boringdroid.mk). - Why it has to exist: BoringdroidSystemUI renders its own
TYPE_NAVIGATION_BARtaskbar window. Without this guard, AOSP also creates its own NavigationBar ondisplayId=0, producing double-bar visual collisions and inset-accounting bugs. - Why it can't be an overlay: there is no product-overlay or RRO
mechanism in AOSP 14 that suppresses NavigationBar creation per-display
from outside the framework.
config_showNavigationBaris read by WMS atDisplayPolicyconstruction time — before product overlays are guaranteed to be applied — so flipping it via overlay is unreliable for our use case. AWindowManager-side suppression hook would itself be a forked-AOSP patch, and strictly larger than the current one. - Maintainer note: do not delete this patch as part of a
"framework-patch-free" cleanup. It is intentionally the smallest
survivable diff. The edit is wrapped in
// region boringdroid/// endregionso it's trivial to locate. If a future AOSP release adds a per-display NavigationBar suppression hook (or makesconfig_showNavigationBaroverlay-honoring at the right lifecycle point), this patch can be retired — until then, keep it.
BoringdroidSystemUI ships as an AOSP module — the build is Soong. From
the AOSP root:
source build/envsetup.sh
lunch boringdroid_x86_64-userdebug
m BoringdroidSystemUI BoringdroidSystemUITestsAndroid.bp declares both the plugin APK and its instrumentation APK
via the standard android_app / android_test module types, with
platform_apis: true and certificate: "platform" so the plugin loads
inside SystemUI's UID on the signed image.
For IDE iteration Android Studio can import the module directly via
Soong's IntelliJ integration (idegen); the in-tree Gradle project
has been retired — running the full tree build is the source of truth
for both the plugin APK and its test APK.
Instrumentation suite (UiAutomator) lives under app/src/androidTest
and builds as BoringdroidSystemUITests.apk:
m BoringdroidSystemUI BoringdroidSystemUITests
bash .claude/scripts/run-boringdroid-tests.shThe script builds the APKs, installs them against a running
boringdroid_x86_64-userdebug emulator, restarts SystemUI to pick up
the plugin, drops adb to shell uid (so cmd notification post works),
restarts Launcher so it re-reads the taskbar's navigation-bar inset,
and runs every class in com.boringdroid.systemui.test.