From 58ba00fa060c9a1f439120f8869ed106e1c935f9 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 29 Oct 2024 00:45:26 +0100 Subject: [PATCH] Adapt "turn screen off" for Android 15 Android 15 introduced an easy way to set the display power: Refs #3927 Refs PR #5418 --- .../java/com/genymobile/scrcpy/CleanUp.java | 2 +- .../genymobile/scrcpy/control/Controller.java | 10 ++++----- .../com/genymobile/scrcpy/device/Device.java | 8 ++++++- .../scrcpy/wrappers/DisplayManager.java | 21 +++++++++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java index f561a10f..c8ee3ef4 100644 --- a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java +++ b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java @@ -143,7 +143,7 @@ public final class CleanUp { Device.powerOffScreen(displayId); } else if (restoreDisplayPower) { Ln.i("Restoring display power"); - Device.setDisplayPower(true); + Device.setDisplayPower(displayId, true); } } diff --git a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java index fbe0691e..7add4ea9 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java @@ -273,7 +273,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener { case ControlMessage.TYPE_SET_DISPLAY_POWER: if (supportsInputEvents && displayId != Device.DISPLAY_ID_NONE) { boolean on = msg.getOn(); - boolean setDisplayPowerOk = Device.setDisplayPower(on); + boolean setDisplayPowerOk = Device.setDisplayPower(displayId, on); if (setDisplayPowerOk) { keepDisplayPowerOff = !on; Ln.i("Device display turned " + (on ? "on" : "off")); @@ -312,7 +312,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener { private boolean injectKeycode(int action, int keycode, int repeat, int metaState) { if (keepDisplayPowerOff && action == KeyEvent.ACTION_UP && (keycode == KeyEvent.KEYCODE_POWER || keycode == KeyEvent.KEYCODE_WAKEUP)) { assert displayId != Device.DISPLAY_ID_NONE; - scheduleDisplayPowerOff(); + scheduleDisplayPowerOff(displayId); } return injectKeyEvent(action, keycode, repeat, metaState, Device.INJECT_MODE_ASYNC); } @@ -491,10 +491,10 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener { /** * Schedule a call to set display power to off after a small delay. */ - private static void scheduleDisplayPowerOff() { + private static void scheduleDisplayPowerOff(int displayId) { EXECUTOR.schedule(() -> { Ln.i("Forcing display off"); - Device.setDisplayPower(false); + Device.setDisplayPower(displayId, false); }, 200, TimeUnit.MILLISECONDS); } @@ -512,7 +512,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener { if (keepDisplayPowerOff) { assert displayId != Device.DISPLAY_ID_NONE; - scheduleDisplayPowerOff(); + scheduleDisplayPowerOff(displayId); } return pressReleaseKeycode(KeyEvent.KEYCODE_POWER, Device.INJECT_MODE_ASYNC); } diff --git a/server/src/main/java/com/genymobile/scrcpy/device/Device.java b/server/src/main/java/com/genymobile/scrcpy/device/Device.java index cbc1bc81..1cf96714 100644 --- a/server/src/main/java/com/genymobile/scrcpy/device/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/device/Device.java @@ -126,7 +126,13 @@ public final class Device { return clipboardManager.setText(text); } - public static boolean setDisplayPower(boolean on) { + public static boolean setDisplayPower(int displayId, boolean on) { + assert displayId != Device.DISPLAY_ID_NONE; + + if (Build.VERSION.SDK_INT >= AndroidVersions.API_35_ANDROID_15) { + return ServiceManager.getDisplayManager().requestDisplayPower(displayId, on); + } + boolean applyToMultiPhysicalDisplays = Build.VERSION.SDK_INT >= AndroidVersions.API_29_ANDROID_10; if (applyToMultiPhysicalDisplays diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java index c8c405bb..37d82c33 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java @@ -1,5 +1,6 @@ package com.genymobile.scrcpy.wrappers; +import com.genymobile.scrcpy.AndroidVersions; import com.genymobile.scrcpy.FakeContext; import com.genymobile.scrcpy.device.DisplayInfo; import com.genymobile.scrcpy.device.Size; @@ -7,6 +8,7 @@ import com.genymobile.scrcpy.util.Command; import com.genymobile.scrcpy.util.Ln; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.content.Context; import android.hardware.display.VirtualDisplay; import android.view.Display; @@ -22,6 +24,7 @@ import java.util.regex.Pattern; public final class DisplayManager { private final Object manager; // instance of hidden class android.hardware.display.DisplayManagerGlobal private Method createVirtualDisplayMethod; + private Method requestDisplayPowerMethod; static DisplayManager create() { try { @@ -137,4 +140,22 @@ public final class DisplayManager { android.hardware.display.DisplayManager dm = ctor.newInstance(FakeContext.get()); return dm.createVirtualDisplay(name, width, height, dpi, surface, flags); } + + private Method getRequestDisplayPowerMethod() throws NoSuchMethodException { + if (requestDisplayPowerMethod == null) { + requestDisplayPowerMethod = manager.getClass().getMethod("requestDisplayPower", int.class, boolean.class); + } + return requestDisplayPowerMethod; + } + + @TargetApi(AndroidVersions.API_35_ANDROID_15) + public boolean requestDisplayPower(int displayId, boolean on) { + try { + Method method = getRequestDisplayPowerMethod(); + return (boolean) method.invoke(manager, displayId, on); + } catch (ReflectiveOperationException e) { + Ln.e("Could not invoke method", e); + return false; + } + } }