Fix virtual display activity routing for Android desktop mode#1
Merged
Merged
Conversation
…ay desktop mode - Add VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS unconditionally for API 34+ alongside OWN_FOCUS to ensure desktop mode compatibility - Add WindowContainerTransaction-based setDisplayWindowingMode() to WindowManager using reflection to set WINDOWING_MODE_FREEFORM on the virtual display's default task area after creation - Call setDisplayWindowingMode after virtual display creation on API 34+ Co-authored-by: lmichaelwar <[email protected]>
Co-authored-by: lmichaelwar <[email protected]>
Copilot
AI
changed the title
[WIP] Fix routing of activity launches for virtual displays
Fix virtual display activity routing for Android desktop mode
Mar 13, 2026
There was a problem hiding this comment.
Pull request overview
Fixes app/activity launch routing for --new-display virtual displays on Android 16 QPR1+/17 when desktop mode is enabled by ensuring the virtual display is treated as a proper “desktop-like” environment (system decorations + freeform windowing mode).
Changes:
- Always add
VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONSon API 34+ for new virtual displays. - Add
WindowManager#setDisplayWindowingMode()(reflection-based) to apply aWindowContainerTransactionsettingWINDOWING_MODE_FREEFORMon the virtual display’s default task area. - Invoke
setDisplayWindowingMode()after virtual display creation on API 34+.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java |
Adds reflection logic to set freeform windowing mode on a display via Window Organizer APIs. |
server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java |
Adjusts virtual display flags for API 34+ and calls the new windowing-mode setter after creation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+291
to
+304
| // Get the IWindowOrganizerController | ||
| Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager"); | ||
| Method getServiceMethod = serviceManagerClass.getDeclaredMethod("getService", String.class); | ||
| IBinder wocBinder = (IBinder) getServiceMethod.invoke(null, "window_organizer"); | ||
| if (wocBinder == null) { | ||
| Ln.w("window_organizer service not available"); | ||
| return; | ||
| } | ||
|
|
||
| Class<?> iWocStubClass = Class.forName("android.window.IWindowOrganizerController$Stub"); | ||
| Object windowOrganizerController = iWocStubClass.getDeclaredMethod("asInterface", IBinder.class).invoke(null, wocBinder); | ||
|
|
||
| // Get the IDisplayAreaOrganizerController | ||
| daoController = windowOrganizerController.getClass().getMethod("getDisplayAreaOrganizerController").invoke(windowOrganizerController); |
Comment on lines
+306
to
+316
| // Create a no-op IDisplayAreaOrganizer proxy for registration; callbacks are ignored since we unregister immediately | ||
| Class<?> idaoClass = Class.forName("android.window.IDisplayAreaOrganizer"); | ||
| organizerProxy = Proxy.newProxyInstance( | ||
| ClassLoader.getSystemClassLoader(), | ||
| new Class[]{idaoClass}, | ||
| (proxy, method, args) -> { | ||
| if ("asBinder".equals(method.getName())) { | ||
| return organizerBinder; | ||
| } | ||
| return null; | ||
| } |
Comment on lines
+191
to
+192
| | VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP | ||
| | VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; |
Owner
|
@copilot apply changes based on the comments in this thread |
Copilot AI
added a commit
that referenced
this pull request
Mar 14, 2026
… Binder descriptor, respect vdSystemDecorations flag 1. WindowManager.java: Replace manual reflection for window_organizer service with ServiceManager.getService() to avoid duplicating reflection logic. 2. WindowManager.java: Attach correct AIDL interface descriptor to the organizer Binder and override onTransact to safely handle callbacks. 3. NewDisplayCapture.java: Remove unconditional SHOULD_SHOW_SYSTEM_DECORATIONS from API 34+ block so --no-vd-system-decorations remains effective. Co-authored-by: lmichaelwar <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Virtual displays created with
--new-displayon Android 16 QPR1+/17 with desktop mode enabled route activity launches to display 0 instead of the virtual display. The launcher renders correctly but tapping app icons opens them on the phone screen.Flag changes (
NewDisplayCapture.java)VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONSunconditionally in the API 34+ block, ensuring the system treats the display as a full windowing environment regardless of thevdSystemDecorationsoption.OWN_FOCUSwas already present here.Freeform windowing mode (
WindowManager.java)setDisplayWindowingMode()using reflection to apply aWindowContainerTransactionthat setsWINDOWING_MODE_FREEFORMon the virtual display's default task display area.DisplayAreaOrganizerforFEATURE_DEFAULT_TASK_CONTAINERto obtain the display area'sWindowContainerToken, applies the transaction, then unregisters in afinallyblock.startNew()on API 34+ after virtual display creation, alongside the existing IME policy configuration.All reflection failures are caught and logged as warnings — the virtual display still functions without freeform mode, just without proper desktop-mode activity routing.
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
dl.google.com/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -Xmx1536m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.14.3-bin/cv11ve7ro1n3o1j4so8xd9n66/gradle-8.14.3/lib/gradle-daemon-main-8.14.3.jar(dns block)www.puppycrawl.com/opt/hostedtoolcache/CodeQL/2.24.2/x64/codeql/tools/linux64/java/bin/java /opt/hostedtoolcache/CodeQL/2.24.2/x64/codeql/tools/linux64/java/bin/java -jar /opt/hostedtoolcache/CodeQL/2.24.2/x64/codeql/xml/tools/xml-extractor.jar --fileList=/tmp/codeql-scratch-6269c6174ccbfd86/dbs/java/working/files-to-index16506125763911101300.list --sourceArchiveDir=/tmp/codeql-scratch-6269c6174ccbfd86/dbs/java/src --outputDir=/tmp/codeql-scratch-6269c6174ccbfd86/dbs/java/trap/java java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java k/_temp/ghcca-node/node/bin/git(dns block)If you need me to access, download, or install something from one of these locations, you can either:
💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.