Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67fa766f09 |
@@ -4,20 +4,13 @@ import android.annotation.SuppressLint;
|
||||
import android.os.IBinder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
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}.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* This separate process is started on the first display mode request.
|
||||
* Instead, run a separate process with a different classpath and LD_PRELOAD just to set the display power mode.
|
||||
* <p>
|
||||
* 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
|
||||
@@ -30,26 +23,18 @@ 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 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;
|
||||
synchronized void requestMode(int mode) {
|
||||
if (thread == null) {
|
||||
thread = new Thread(this, "DisplayPowerModeProxy");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
requestedMode = mode;
|
||||
notify();
|
||||
}
|
||||
|
||||
void stopAndJoin() {
|
||||
@@ -75,10 +60,8 @@ public final class DisplayPowerMode {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
OutputStream out = process.getOutputStream();
|
||||
InputStream in = process.getInputStream();
|
||||
int mode;
|
||||
while (true) {
|
||||
int mode;
|
||||
synchronized (this) {
|
||||
while (!stopped && requestedMode == -1) {
|
||||
wait();
|
||||
@@ -93,14 +76,13 @@ public final class DisplayPowerMode {
|
||||
}
|
||||
|
||||
try {
|
||||
out.write(mode);
|
||||
out.flush();
|
||||
int status = in.read();
|
||||
Process process = executeSystemServerSetDisplayPowerMode(mode);
|
||||
int status = process.waitFor();
|
||||
if (status != 0) {
|
||||
Ln.e("Set display power mode failed remotely: status=" + status);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Ln.e("Could not request display power mode", e);
|
||||
} catch (Exception e) {
|
||||
Ln.e("Failed to execute process", e);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
@@ -114,8 +96,8 @@ public final class DisplayPowerMode {
|
||||
}
|
||||
|
||||
// Called from the scrcpy process
|
||||
public static boolean setRemoteDisplayPowerMode(int mode) {
|
||||
return PROXY.requestMode(mode);
|
||||
public static void setRemoteDisplayPowerMode(int mode) {
|
||||
PROXY.requestMode(mode);
|
||||
}
|
||||
|
||||
public static void stopAndJoin() {
|
||||
@@ -123,9 +105,9 @@ public final class DisplayPowerMode {
|
||||
}
|
||||
|
||||
// Called from the proxy thread in the scrcpy process
|
||||
private static Process executeDisplayPowerModeDaemon() throws IOException {
|
||||
private static Process executeSystemServerSetDisplayPowerMode(int mode) throws IOException {
|
||||
String[] ldPreloadLibs = {"/system/lib64/libandroid_servers.so"};
|
||||
String[] cmd = {"app_process", "/", DisplayPowerMode.class.getName()};
|
||||
String[] cmd = {"app_process", "/", DisplayPowerMode.class.getName(), String.valueOf(mode)};
|
||||
|
||||
ProcessBuilder builder = new ProcessBuilder(cmd);
|
||||
builder.environment().put("LD_PRELOAD", String.join(" ", ldPreloadLibs));
|
||||
@@ -154,31 +136,26 @@ public final class DisplayPowerMode {
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
// This process uses stdin/stdout to communicate with the caller, make sure nothing else writes to stdout
|
||||
// (and never use Ln methods other than Ln.w() and Ln.e()).
|
||||
PrintStream nullStream = new PrintStream(new Ln.NullOutputStream());
|
||||
System.setOut(nullStream);
|
||||
PrintStream stdout = Ln.CONSOLE_OUT;
|
||||
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 {
|
||||
while (true) {
|
||||
// Wait for requests
|
||||
int request = System.in.read();
|
||||
if (request == -1) {
|
||||
// EOF
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setDisplayPowerModeUsingDisplayControl(request);
|
||||
stdout.write(0); // ok
|
||||
} catch (Throwable e) {
|
||||
Ln.e("Could not set display power mode", e);
|
||||
stdout.write(1); // error
|
||||
}
|
||||
stdout.flush();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Expected when the server is dead
|
||||
int mode = Integer.parseInt(args[0]);
|
||||
setDisplayPowerModeUsingDisplayControl(mode);
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
Ln.e("Could not set display power mode", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ public final class Ln {
|
||||
private static final String TAG = "scrcpy";
|
||||
private static final String PREFIX = "[server] ";
|
||||
|
||||
public static final PrintStream CONSOLE_OUT = new PrintStream(new FileOutputStream(FileDescriptor.out));
|
||||
public static final PrintStream CONSOLE_ERR = new PrintStream(new FileOutputStream(FileDescriptor.err));
|
||||
private static final PrintStream CONSOLE_OUT = new PrintStream(new FileOutputStream(FileDescriptor.out));
|
||||
private static final PrintStream CONSOLE_ERR = new PrintStream(new FileOutputStream(FileDescriptor.err));
|
||||
|
||||
enum Level {
|
||||
VERBOSE, DEBUG, INFO, WARN, ERROR
|
||||
|
||||
Reference in New Issue
Block a user