Ravenwood support for `wtf()`, wrap logging.

There are some tests triggering `wtf()` during the normal course of
operation, and we haven't been able to reproduce them yet.  This
change gives us a valid `wtf()` implementation to aid debugging.

It's also a bit jarring to have log-style messages interleaved with
raw `System.out`, so also add `redirectLogStreams()` to match the
logging behavior that developers observe on physical devices.

Bug: 322805216
Test: atest FrameworksCoreTestsRavenwood FrameworksUtilTestsRavenwood
Change-Id: I8ea2d12638cd998e3962aaa8af09a3335fcd0d51
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index c0ceb9ea..9ecb4cb 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -217,7 +217,6 @@
      * @see Log#wtf(String, String)
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @android.ravenwood.annotation.RavenwoodThrow
     public static int wtf(@Nullable String tag, @NonNull String msg) {
         return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, false, true);
     }
@@ -263,7 +262,6 @@
      *
      * @see Log#wtf(String, Throwable)
      */
-    @android.ravenwood.annotation.RavenwoodThrow
     public static int wtf(@Nullable String tag, @Nullable Throwable tr) {
         return Log.wtf(Log.LOG_ID_SYSTEM, tag, tr.getMessage(), tr, false, true);
     }
@@ -284,7 +282,6 @@
      * @see Log#wtf(String, String, Throwable)
      */
     @UnsupportedAppUsage
-    @android.ravenwood.annotation.RavenwoodThrow
     public static int wtf(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) {
         return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, tr, false, true);
     }
diff --git a/core/java/com/android/internal/os/AndroidPrintStream.java b/core/java/com/android/internal/os/AndroidPrintStream.java
index a6e41ff..bb388bb 100644
--- a/core/java/com/android/internal/os/AndroidPrintStream.java
+++ b/core/java/com/android/internal/os/AndroidPrintStream.java
@@ -24,6 +24,7 @@
  *
  * {@hide}
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 class AndroidPrintStream extends LoggingPrintStream {
 
     private final int priority;
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 28b98d6..cdac097 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -39,6 +39,7 @@
 
 import libcore.content.type.MimeMap;
 
+import java.io.PrintStream;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -50,6 +51,7 @@
  * public consumption.
  * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
 public class RuntimeInit {
     final static String TAG = "AndroidRuntime";
     final static boolean DEBUG = false;
@@ -67,7 +69,15 @@
 
     private static volatile ApplicationWtfHandler sDefaultApplicationWtfHandler;
 
+    /**
+     * Stored values of System.out and System.err before they've been replaced by
+     * redirectLogStreams(). Kept open here for other Ravenwood internals to use.
+     */
+    public static PrintStream sOut$ravenwood;
+    public static PrintStream sErr$ravenwood;
+
     private static final native void nativeFinishInit();
+
     private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
 
     private static int Clog_e(String tag, String msg, Throwable tr) {
@@ -385,6 +395,7 @@
     /**
      * Redirect System.out and System.err to the Android log.
      */
+    @android.ravenwood.annotation.RavenwoodReplace
     public static void redirectLogStreams() {
         System.out.close();
         System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
@@ -392,6 +403,17 @@
         System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
     }
 
+    public static void redirectLogStreams$ravenwood() {
+        if (sOut$ravenwood == null) {
+            sOut$ravenwood = System.out;
+            System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
+        }
+        if (sErr$ravenwood == null) {
+            sErr$ravenwood = System.err;
+            System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
+        }
+    }
+
     /**
      * Report a serious error in the current process.  May or may not cause
      * the process to terminate (depends on system settings).
@@ -399,6 +421,7 @@
      * @param tag to record with the error
      * @param t exception describing the error site and conditions
      */
+    @android.ravenwood.annotation.RavenwoodReplace
     public static void wtf(String tag, Throwable t, boolean system) {
         try {
             boolean exit = false;
@@ -436,6 +459,11 @@
         }
     }
 
+    public static void wtf$ravenwood(String tag, Throwable t, boolean system) {
+        // We've already emitted to logs, so there's nothing more to do here,
+        // as we don't have a DropBox pipeline configured
+    }
+
     /**
      * Set the default {@link ApplicationWtfHandler}, in case the ActivityManager is not ready yet.
      */
diff --git a/core/tests/coretests/src/android/util/LogTest.java b/core/tests/coretests/src/android/util/LogTest.java
index f9966a1..15caac9 100644
--- a/core/tests/coretests/src/android/util/LogTest.java
+++ b/core/tests/coretests/src/android/util/LogTest.java
@@ -37,6 +37,13 @@
     private static final String LOG_TAG = "LogTest";
 
     @Test
+    public void testWtf() {
+        Log.wtf(LOG_TAG, "Message");
+        Log.wtf(LOG_TAG, "Message", new Throwable("Throwable"));
+        Log.wtf(LOG_TAG, new Throwable("Throwable"));
+    }
+
+    @Test
     @Ignore
     public void testIsLoggable() {
         // First clear any SystemProperty setting for our test key.
diff --git a/core/tests/utiltests/src/android/util/SlogTest.java b/core/tests/utiltests/src/android/util/SlogTest.java
index 6f761e3..738c668 100644
--- a/core/tests/utiltests/src/android/util/SlogTest.java
+++ b/core/tests/utiltests/src/android/util/SlogTest.java
@@ -44,4 +44,11 @@
         Slog.w(TAG, MSG, THROWABLE);
         Slog.e(TAG, MSG, THROWABLE);
     }
+
+    @Test
+    public void testWtf() {
+        Slog.wtf(TAG, MSG);
+        Slog.wtf(TAG, MSG, THROWABLE);
+        Slog.wtf(TAG, THROWABLE);
+    }
 }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 5588f4f..3670459 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -24,6 +24,8 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.internal.os.RuntimeInit;
+
 import org.junit.runner.Description;
 
 import java.io.PrintStream;
@@ -53,6 +55,8 @@
     }
 
     public static void init(RavenwoodRule rule) {
+        RuntimeInit.redirectLogStreams();
+
         android.os.Process.init$ravenwood(rule.mUid, rule.mPid);
         android.os.Binder.init$ravenwood();
         android.os.SystemProperties.init$ravenwood(
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index b775f9a..e49b64e 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -5,6 +5,7 @@
 com.android.internal.logging.MetricsLogger
 com.android.internal.logging.testing.FakeMetricsLogger
 com.android.internal.logging.testing.UiEventLoggerFake
+com.android.internal.os.AndroidPrintStream
 com.android.internal.os.BatteryStatsHistory
 com.android.internal.os.BatteryStatsHistory$TraceDelegate
 com.android.internal.os.BatteryStatsHistory$VarintParceler
@@ -16,6 +17,7 @@
 com.android.internal.os.PowerProfile
 com.android.internal.os.PowerStats
 com.android.internal.os.PowerStats$Descriptor
+com.android.internal.os.RuntimeInit
 com.android.internal.power.ModemPowerProfile
 
 android.util.AtomicFile
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java
index 292e8da..6480cfc 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java
@@ -15,9 +15,9 @@
  */
 package com.android.hoststubgen.nativesubstitution;
 
-import android.util.Log;
-import android.util.Log.Level;
+import com.android.internal.os.RuntimeInit;
 
+import java.io.PrintStream;
 import java.util.Collection;
 
 public class EventLog_host {
@@ -54,7 +54,7 @@
             }
         }
         sb.append(']');
-        System.out.println(sb.toString());
+        getRealOut().println(sb.toString());
         return sb.length();
     }
 
@@ -66,4 +66,16 @@
             Collection<android.util.EventLog.Event> output) {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * Return the "real" {@code System.out} if it's been swapped by {@code RavenwoodRuleImpl}, so
+     * that we don't end up in a recursive loop.
+     */
+    private static PrintStream getRealOut() {
+        if (RuntimeInit.sOut$ravenwood != null) {
+            return RuntimeInit.sOut$ravenwood;
+        } else {
+            return System.out;
+        }
+    }
 }
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java
index ee55c7a..cdfa302 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java
@@ -18,6 +18,8 @@
 import android.util.Log;
 import android.util.Log.Level;
 
+import com.android.internal.os.RuntimeInit;
+
 import java.io.PrintStream;
 
 public class Log_host {
@@ -27,7 +29,6 @@
     }
 
     public static int println_native(int bufID, int priority, String tag, String msg) {
-        final PrintStream out = System.out;
         final String buffer;
         switch (bufID) {
             case Log.LOG_ID_MAIN: buffer = "main"; break;
@@ -50,7 +51,7 @@
         };
 
         for (String s : msg.split("\\n")) {
-            out.println(String.format("logd: [%s] %s %s: %s", buffer, prio, tag, s));
+            getRealOut().println(String.format("logd: [%s] %s %s: %s", buffer, prio, tag, s));
         }
         return msg.length();
     }
@@ -58,4 +59,16 @@
     public static int logger_entry_max_payload_native() {
         return 4068; // [ravenwood] This is what people use in various places.
     }
+
+    /**
+     * Return the "real" {@code System.out} if it's been swapped by {@code RavenwoodRuleImpl}, so
+     * that we don't end up in a recursive loop.
+     */
+    private static PrintStream getRealOut() {
+        if (RuntimeInit.sOut$ravenwood != null) {
+            return RuntimeInit.sOut$ravenwood;
+        } else {
+            return System.out;
+        }
+    }
 }