diff --git a/server/src/main/java/com/genymobile/scrcpy/Ln.java b/server/src/main/java/com/genymobile/scrcpy/Ln.java index 199c29be..564901b4 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Ln.java +++ b/server/src/main/java/com/genymobile/scrcpy/Ln.java @@ -8,6 +8,11 @@ import android.util.Log; */ public final class Ln { + public interface ConsolePrinter { + void printOut(String message); + void printErr(String message, Throwable throwable); + } + private static final String TAG = "scrcpy"; private static final String PREFIX = "[server] "; @@ -15,12 +20,17 @@ public final class Ln { VERBOSE, DEBUG, INFO, WARN, ERROR } + private static ConsolePrinter consolePrinter = new DefaultConsolePrinter(); private static Level threshold = Level.INFO; private Ln() { // not instantiable } + public static void setConsolePrinter(ConsolePrinter consolePrinter) { + Ln.consolePrinter = consolePrinter; + } + /** * Initialize the log level. *

@@ -39,31 +49,28 @@ public final class Ln { public static void v(String message) { if (isEnabled(Level.VERBOSE)) { Log.v(TAG, message); - System.out.print(PREFIX + "VERBOSE: " + message + '\n'); + consolePrinter.printOut(PREFIX + "VERBOSE: " + message + '\n'); } } public static void d(String message) { if (isEnabled(Level.DEBUG)) { Log.d(TAG, message); - System.out.print(PREFIX + "DEBUG: " + message + '\n'); + consolePrinter.printOut(PREFIX + "DEBUG: " + message + '\n'); } } public static void i(String message) { if (isEnabled(Level.INFO)) { Log.i(TAG, message); - System.out.print(PREFIX + "INFO: " + message + '\n'); + consolePrinter.printOut(PREFIX + "INFO: " + message + '\n'); } } public static void w(String message, Throwable throwable) { if (isEnabled(Level.WARN)) { Log.w(TAG, message, throwable); - System.err.print(PREFIX + "WARN: " + message + '\n'); - if (throwable != null) { - throwable.printStackTrace(); - } + consolePrinter.printErr(PREFIX + "WARN: " + message + '\n', throwable); } } @@ -74,14 +81,26 @@ public final class Ln { public static void e(String message, Throwable throwable) { if (isEnabled(Level.ERROR)) { Log.e(TAG, message, throwable); - System.err.print(PREFIX + "ERROR: " + message + "\n"); - if (throwable != null) { - throwable.printStackTrace(); - } + consolePrinter.printErr(PREFIX + "ERROR: " + message + '\n', throwable); } } public static void e(String message) { e(message, null); } + + public static class DefaultConsolePrinter implements ConsolePrinter { + @Override + public void printOut(String message) { + System.out.print(message); + } + + @Override + public void printErr(String message, Throwable throwable) { + System.err.print(message); + if (throwable != null) { + throwable.printStackTrace(); + } + } + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java index b8ee68ca..4fbe5962 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java +++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java @@ -14,6 +14,12 @@ import android.os.Build; import android.os.Looper; import android.os.Parcel; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -55,6 +61,12 @@ public final class Workarounds { mustFillAppInfo = true; mustFillBaseContext = true; mustFillAppContext = true; + } else if (Build.MANUFACTURER.equalsIgnoreCase("xiaomi")) { + // Trash the messages printed to the console by direct calls to System.out and System.err. + // Xiaomi device ROMs may print internal errors using e.printStackTrace(), flooding the console with irrelevant errors. + ExclusiveConsolePrinter exclusiveConsolePrinter = new ExclusiveConsolePrinter(); + exclusiveConsolePrinter.installNullSystemStreams(); + Ln.setConsolePrinter(new ExclusiveConsolePrinter()); } if (audio && Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { @@ -306,4 +318,48 @@ public final class Workarounds { throw new RuntimeException("Cannot create AudioRecord"); } } + + static class ExclusiveConsolePrinter implements Ln.ConsolePrinter { + + static class NullOutputStream extends OutputStream { + @Override + public void write(byte[] b) { + // ignore + } + + @Override + public void write(byte[] b, int off, int len) { + // ignore + } + + @Override + public void write(int b) { + // ignore + } + } + + private final PrintStream realOut = new PrintStream(new FileOutputStream(FileDescriptor.out)); + private final PrintStream realErr = new PrintStream(new FileOutputStream(FileDescriptor.err)); + + void installNullSystemStreams() { + PrintStream nullStream = new PrintStream(new NullOutputStream()); + System.setOut(nullStream); + System.setErr(nullStream); + } + + @Override + public void printOut(String message) { + realOut.print(message); + } + + @Override + public void printErr(String message, Throwable throwable) { + realErr.print(message); + if (throwable != null) { + StringWriter errors = new StringWriter(); + throwable.printStackTrace(new PrintWriter(errors)); + realErr.print(errors); + } + } + } }