scrcpy icon indicating copy to clipboard operation
scrcpy copied to clipboard

Restart capture when screen resolution changes

Open yume-chan opened this issue 7 months ago • 8 comments

Fixes #1918 Fixes #161 Might affect #4152

Found this API by accident when researching https://github.com/Genymobile/scrcpy/pull/4446#issuecomment-1827164429.

Some devices have a resolution option in their Settings app, and on all devices the resolution can be changed by adb shell wm size 1080x1920. This event will react to both cases.

Tested on Xiaomi Mi 11 (Android 13, both system settings and wm size), Samsung Galaxy S9 (Android 10, both system settings and wm size), Onyx BOOX Nova Pro (Android 6.0.1, only wm size).

yume-chan avatar Nov 27 '23 09:11 yume-chan

For consistency with the other listeners where we use the aidl directly, maybe we could use IDisplayManagerCallback.aidl. This is what the wrapper DisplayListeners use internally (with an additional Handler).

https://github.com/aosp-mirror/platform_frameworks_base/blob/ad73c57d691ad2cfd48b201db6cf8b053d2207ba/core/java/android/hardware/display/DisplayManagerGlobal.java#L1099-L1108

rom1v avatar Nov 27 '23 11:11 rom1v

maybe we could use IDisplayManagerCallback.aidl

I created a new branch to use IDisplayManager.registerCallback(IDisplayManagerCallback): https://github.com/Genymobile/scrcpy/compare/master...yume-chan:scrcpy:feat/resolution-change-2?expand=1

It throws an error when called:

C:\Users\simon\Downloads\scrcpy-win64-v2.3> ./scrcpy -s 0123456789ABCDEF
scrcpy 2.3 <https://github.com/Genymobile/scrcpy>
INFO: ADB device found:
INFO:     -->   (usb)      0123456789ABCDEF            device  NovaPro
INFO:           (usb)              9719052f            device  ONEPLUS_A6000
C:\Users\simon\Downloads\scrcpy-win64-v2.3\scrcpy-server: 1 file pushed, 0 skipped. 10.9 MB/s (66675 bytes in 0.006s)
[server] INFO: Device: [Onyx] Onyx NovaPro (Android 6.0.1)
[server] ERROR: java.lang.reflect.InvocationTargetException
java.lang.AssertionError: java.lang.reflect.InvocationTargetException
        at com.genymobile.scrcpy.wrappers.DisplayManager.registerCallback(DisplayManager.java:112)
        at com.genymobile.scrcpy.Device.<init>(Device.java:94)
        at com.genymobile.scrcpy.Server.scrcpy(Server.java:114)
        at com.genymobile.scrcpy.Server.internalMain(Server.java:244)
        at com.genymobile.scrcpy.Server.main(Server.java:199)
        at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
        at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:251)
Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.genymobile.scrcpy.wrappers.DisplayManager.registerCallback(DisplayManager.java:110)
        ... 6 more
Caused by: java.lang.SecurityException: The calling process has already registered an IDisplayManagerCallback.
        at android.os.Parcel.readException(Parcel.java:1620)
        at android.os.Parcel.readException(Parcel.java:1573)
        at android.hardware.display.IDisplayManager$Stub$Proxy.registerCallback(IDisplayManager.java:305)
        ... 8 more
ERROR: Server connection failed

Until Android 12, one process can only have one IDisplayManagerCallback: https://cs.android.com/android/platform/superproject/+/android-11.0.0_r1:frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java;l=660-665;drc=b28fb721f5be06818bedc8601e02118ddcbd4739

yume-chan avatar Nov 27 '23 14:11 yume-chan

Oops, it seems this has changed in Android 14:

[server] INFO: Device: [Google] google Pixel 8 (Android 14)
[server] ERROR: java.lang.NoSuchMethodException: android.hardware.display.DisplayManagerGlobal.registerDisplayListener [interface android.hardware.display.DisplayManager$DisplayListener, class android.os.Handler]
java.lang.AssertionError: java.lang.NoSuchMethodException: android.hardware.display.DisplayManagerGlobal.registerDisplayListener [interface android.hardware.display.DisplayManager$DisplayListener, class android.os.Handler]
	at com.genymobile.scrcpy.wrappers.DisplayManager.registerDisplayListener(DisplayManager.java:127)
	at com.genymobile.scrcpy.Device.<init>(Device.java:96)
	at com.genymobile.scrcpy.Server.scrcpy(Server.java:114)
	at com.genymobile.scrcpy.Server.internalMain(Server.java:244)
	at com.genymobile.scrcpy.Server.main(Server.java:199)
	at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
	at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:359)
Caused by: java.lang.NoSuchMethodException: android.hardware.display.DisplayManagerGlobal.registerDisplayListener [interface android.hardware.display.DisplayManager$DisplayListener, class android.os.Handler]
	at java.lang.Class.getMethod(Class.java:2937)
	at java.lang.Class.getMethod(Class.java:2449)
	at com.genymobile.scrcpy.wrappers.DisplayManager.registerDisplayListener(DisplayManager.java:124)
	... 6 more

EDIT: https://github.com/aosp-mirror/platform_frameworks_base/commit/204eba6cdc4ff465a8f457008e2783c8b5f67c01

rom1v avatar Nov 30 '23 12:11 rom1v

Oh, this callback is called also on device rotation (and probably also on fold, although I have no foldable device to test).

So we can probably remove the other callbacks completely.

rom1v avatar Nov 30 '23 17:11 rom1v

@AdoPi (as author or #3979), could you check that with this PR + this diff:

diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java
index 756af86e3..c75e2a8d7 100644
--- a/server/src/main/java/com/genymobile/scrcpy/Device.java
+++ b/server/src/main/java/com/genymobile/scrcpy/Device.java
@@ -106,6 +106,7 @@ public final class Device {
 
             @Override
             public void onDisplayChanged(int displayId) {
+                Ln.i("=== onDisplayChanged(" + displayId + ")");
                 if (Device.this.displayId != displayId) {
                     return;
                 }

If you fold the device, is the log printed?

rom1v avatar Nov 30 '23 17:11 rom1v

Oops, it seems this has changed in Android 14:

Oh, I was originally using DeviceManager.registerDisplayListener, it always has an overload with only two parameters: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/hardware/display/DisplayManager.java;l=726-761;drc=b3691fab2356133dfc7e11c213732ffef9a85315

Higher level API changes less, but is more prone to OEM modifications. It's hard to balance.

So we can probably remove the other callbacks completely.

I removed rotation listener, and will wait for test results from foldable devices.

yume-chan avatar Dec 01 '23 06:12 yume-chan

Thank you :+1:

For clarity, I extracted the rotation listener (and fold listener) to separate commits: reschange.3.

I also made minor changes, like removing unused onDisplayAdded() and onDisplayRemoved().

However, I just noticed that it does not work at all on Android 14: it builds, but the onDisplayChanged() event is never received, either on wm size changes or on rotation (on your branch or after my refactors). :disappointed:

It works correctly on Android <= 13.

rom1v avatar Dec 01 '23 18:12 rom1v

However, I just noticed that it does not work at all on Android 14

The reason is Android 14 no longer sends display events to "dead" (we don't have any window or service so we are considered as dead) apps: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java;l=2878-2891;drc=d4035b6eb1192f71af7adae47bf17d22c7356fa1

It works if I enable foreground workaround (and keeping it running like camera). https://github.com/Genymobile/scrcpy/pull/4213#issuecomment-1817562149 doesn't work.

In my app I'm experimenting showing foreground workaround on virtual displays (https://github.com/Genymobile/scrcpy/issues/1887#issuecomment-1694356024) to get camera capturing working on Android 11. Maybe it will help this on Android 14.

yume-chan avatar Dec 03 '23 12:12 yume-chan