MoreDisplay is an Xposed/LSPosed module that runs only inside system_server. It manages
virtual displays from the system side and lets you decide, per app, which displays that app can
see, which display it records, and which display its accessibility gestures land on.
It never hooks the target app. Every decision and rewrite happens in system_server, so the
module scope stays a single package.
| Feature | What it does |
|---|---|
| Managed displays | Create / list / remove virtual displays from system_server (uid 1000), including flags such as PUBLIC, TRUSTED, OWN_FOCUS. |
| Launch bypass | Relaxes ActivityTaskSupervisor#isCallerAllowedToLaunchOnDisplay so apps can launch themselves onto a managed display. |
| Per-uid visibility | Hide a display from a specific app (blacklist) or show it only to that app (whitelist). Covers the list API, the by-id lookup API and display add/remove events. |
| Recording redirection | When a target app starts a MediaProjection capture, the virtual display's mirror source is switched from display 0 to a managed display. |
| Accessibility redirection | dispatchGesture calls from a configured accessibility service are rewritten so the gesture is injected into a managed display instead of the default one. |
- LSPosed 1.9.3+ (LibXposed API 102).
- Android 10 (API 29) or above. Developed and verified on Android 16 (API 36).
- Recommended scope:
system(system server) only. Nothing else needs to be checked.
The app talks to the in-system-server engine over a channel that needs no new SELinux rules and no
root: the module hooks ContentProvider$Transport#call and answers only the method
moredisplay.rpc; every other call is passed through untouched. Everything else is plain reflection
against DisplayManagerService, matched dynamically by method name and parameter shape so ROM
differences degrade to a log line instead of a boot loop.
Enable the module in LSPosed, reboot, then either use the app UI or drive it over adb:
# create a managed display (flags=1 -> PUBLIC)
adb shell am start -n io.github.ynkcc.moredisplay/.CommandActivity \
--es action create --ei width 1280 --ei height 720 --ei dpi 240 --ei flags 1
# list / remove
adb shell am start -n io.github.ynkcc.moredisplay/.CommandActivity --es action list
adb shell am start -n io.github.ynkcc.moredisplay/.CommandActivity --es action remove --ei display_id 2
# hide display 2 from uid 10123
adb shell am start -n io.github.ynkcc.moredisplay/.CommandActivity \
--es action policy-set --ei uid 10123 --es mode hide --eia display_ids 2
# send that app's recordings / gestures to display 2
adb shell am start -n io.github.ynkcc.moredisplay/.CommandActivity \
--es action policy-set --ei uid 10123 --es mode all --ei record_display_id 2
adb shell am start -n io.github.ynkcc.moredisplay/.CommandActivity --es action policy-list- Display 0 is never filtered; hiding it breaks the target app's
Contextcreation. - Policies are keyed by uid (package name is display-only). On multi-user devices compute
uid = userId * 100000 + appId. - The engine lives in
system_servermemory, so policies take effect without a reboot, but a client process may still hold a cachedDisplayInfountil it restarts. - Recording redirection only rewrites the mirror source of displays that really mirror display 0
with
AUTO_MIRROR;OWN_CONTENT_ONLYvirtual displays are left alone. - Accessibility redirection only rewrites gestures aimed at the default display.