diff --git a/server/src/main/java/com/genymobile/scrcpy/DisplayPowerMode.java b/server/src/main/java/com/genymobile/scrcpy/DisplayPowerMode.java index 68bef2b830..3ffadf79a2 100644 --- a/server/src/main/java/com/genymobile/scrcpy/DisplayPowerMode.java +++ b/server/src/main/java/com/genymobile/scrcpy/DisplayPowerMode.java @@ -4,13 +4,19 @@ import android.os.IBinder; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.reflect.Method; /** * On Android 14, the methods used to turn the device screen off have been moved from SurfaceControl (in framework.jar) to DisplayControl (a system * server class). As a consequence, they could not be called directly. See {@url https://github.com/Genymobile/scrcpy/issues/3927}. *
- * Instead, run a separate process with a different classpath and LD_PRELOAD just to set the display power mode. + * Instead, run a separate process with a different classpath and LD_PRELOAD just to set the display power mode. The scrcpy server can request to + * this process to set the display mode by writing the mode (a single byte, the value of one of the SurfaceControl.POWER_MODE_* constants, + * typically 0=off, 2=on) to the process stdin. In return, it receives the status of the request (0=ok, 1=error) on the process stdout. + *
+ * This separate process is started on the first display mode request. *
* Since the client must not block, and calling/joining a process is blocking (this specific one takes a few hundred milliseconds to complete), * this class uses an internal thread to execute the requests asynchronously, and serialize them (so that two successive requests are guaranteed to @@ -23,18 +29,26 @@ public final class DisplayPowerMode { private static final class Proxy implements Runnable { + private Process process; private Thread thread; private int requestedMode = -1; private boolean stopped; - synchronized void requestMode(int mode) { - if (thread == null) { - thread = new Thread(this, "DisplayPowerModeProxy"); - thread.setDaemon(true); - thread.start(); + synchronized boolean requestMode(int mode) { + try { + if (process == null) { + process = executeDisplayPowerModeDaemon(); + thread = new Thread(this, "DisplayPowerModeProxy"); + thread.setDaemon(true); + thread.start(); + } + requestedMode = mode; + notify(); + return true; + } catch (IOException e) { + Ln.e("Could not start display power mode daemon", e); + return false; } - requestedMode = mode; - notify(); } void stopAndJoin() { @@ -60,8 +74,10 @@ void stopAndJoin() { @Override public void run() { try { - int mode; + OutputStream out = process.getOutputStream(); + InputStream in = process.getInputStream(); while (true) { + int mode; synchronized (this) { while (!stopped && requestedMode == -1) { wait(); @@ -76,13 +92,14 @@ public void run() { } try { - Process process = executeSystemServerSetDisplayPowerMode(mode); - int status = process.waitFor(); + out.write(mode); + out.flush(); + int status = in.read(); if (status != 0) { Ln.e("Set display power mode failed remotely: status=" + status); } - } catch (Exception e) { - Ln.e("Failed to execute process", e); + } catch (IOException e) { + Ln.e("Could not request display power mode", e); } } } catch (InterruptedException e) { @@ -96,8 +113,8 @@ private DisplayPowerMode() { } // Called from the scrcpy process - public static void setRemoteDisplayPowerMode(int mode) { - PROXY.requestMode(mode); + public static boolean setRemoteDisplayPowerMode(int mode) { + return PROXY.requestMode(mode); } public static void stopAndJoin() { @@ -105,9 +122,9 @@ public static void stopAndJoin() { } // Called from the proxy thread in the scrcpy process - private static Process executeSystemServerSetDisplayPowerMode(int mode) throws IOException { + private static Process executeDisplayPowerModeDaemon() throws IOException { String[] ldPreloadLibs = {"/system/lib64/libandroid_servers.so"}; - String[] cmd = {"app_process", "/", DisplayPowerMode.class.getName(), String.valueOf(mode)}; + String[] cmd = {"app_process", "/", DisplayPowerMode.class.getName()}; ProcessBuilder builder = new ProcessBuilder(cmd); builder.environment().put("LD_PRELOAD", String.join(" ", ldPreloadLibs)); @@ -139,23 +156,25 @@ public static void main(String... args) { Ln.disableSystemStreams(); Ln.initLogLevel(Ln.Level.DEBUG); - int status = run(args) ? 0 : 1; - System.exit(status); - } - - private static boolean run(String... args) { - if (args.length != 1) { - Ln.e("Exactly one argument expected: the value of one of the SurfaceControl.POWER_MODE_* constants (typically 0 or 2)"); - return false; - } - try { - int mode = Integer.parseInt(args[0]); - setDisplayPowerModeUsingDisplayControl(mode); - return true; - } catch (Throwable e) { - Ln.e("Could not set display power mode", e); - return false; + while (true) { + // Wait for requests + int request = System.in.read(); + if (request == -1) { + // EOF + return; + } + try { + setDisplayPowerModeUsingDisplayControl(request); + System.out.write(0); // ok + } catch (Throwable e) { + Ln.e("Could not set display power mode", e); + System.out.write(1); // error + } + System.out.flush(); + } + } catch (IOException e) { + // Expected when the server is dead } } }