Add bugreport pre-dump functionality

Allow apps to trigger the dump of certain critical data, e.g. data stored in short
ring buffers that might get lost by the time a bugreport is requested.

Then, a bugreport request can specify whether the pre-dumped data should be used.

Fixes: 205138504
Test: atest com.android.os.bugreports.tests.BugreportManagerTest
Ignore-AOSP-First: depends on changes (surfaceflinger) that cannot go into AOSP
Change-Id: I976f2ed3189e83f5bd71dc81e20306527c411d10
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index a60972b..8620a2e 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -154,7 +154,10 @@
         "dumpstate.cpp",
         "tests/dumpstate_test.cpp",
     ],
-    static_libs: ["libgmock"],
+    static_libs: [
+        "libc++fs",
+        "libgmock",
+    ],
     test_config: "dumpstate_test.xml",
     data: [
         ":dumpstate_test_fixture",
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index e42ee05..42e9e0f 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -51,7 +51,7 @@
 
 // Creates a bugreport and exits, thus preserving the oneshot nature of the service.
 // Note: takes ownership of data.
-[[noreturn]] static void* dumpstate_thread_main(void* data) {
+[[noreturn]] static void* dumpstate_thread_bugreport(void* data) {
     std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
     ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package);
     MYLOGD("Finished taking a bugreport. Exiting.\n");
@@ -84,11 +84,28 @@
     return android::OK;
 }
 
+binder::Status DumpstateService::preDumpUiData(const std::string&) {
+    std::lock_guard<std::mutex> lock(lock_);
+    MYLOGI("preDumpUiData()");
+
+    if (ds_ != nullptr) {
+        MYLOGE("Error! DumpstateService is currently already being used. Returning.");
+        return exception(binder::Status::EX_SERVICE_SPECIFIC,
+                         "DumpstateService is already being used");
+    }
+
+    ds_ = &(Dumpstate::GetInstance());
+    ds_->PreDumpUiData();
+
+    return binder::Status::ok();
+}
+
 binder::Status DumpstateService::startBugreport(int32_t calling_uid,
                                                 const std::string& calling_package,
                                                 android::base::unique_fd bugreport_fd,
                                                 android::base::unique_fd screenshot_fd,
                                                 int bugreport_mode,
+                                                int bugreport_flags,
                                                 const sp<IDumpstateListener>& listener,
                                                 bool is_screenshot_requested) {
     MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
@@ -96,12 +113,12 @@
     // Ensure there is only one bugreport in progress at a time.
     std::lock_guard<std::mutex> lock(lock_);
     if (ds_ != nullptr) {
-        MYLOGE("Error! There is already a bugreport in progress. Returning.");
+        MYLOGE("Error! DumpstateService is currently already being used. Returning.");
         if (listener != nullptr) {
             listener->onError(IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
         }
         return exception(binder::Status::EX_SERVICE_SPECIFIC,
-                         "There is already a bugreport in progress");
+                         "DumpstateService is already being used");
     }
 
     // From here on, all conditions that indicate we are done with this incoming request should
@@ -123,8 +140,8 @@
     }
 
     std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
-    options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
-                        screenshot_fd, is_screenshot_requested);
+    options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_flags,
+                        bugreport_fd, screenshot_fd, is_screenshot_requested);
 
     if (bugreport_fd.get() == -1 || (options->do_screenshot && screenshot_fd.get() == -1)) {
         MYLOGE("Invalid filedescriptor");
@@ -148,10 +165,9 @@
     pthread_t thread;
     // Initialize dumpstate
     ds_->Initialize();
-    status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info);
+    status_t err = pthread_create(&thread, nullptr, dumpstate_thread_bugreport, ds_info);
     if (err != 0) {
         delete ds_info;
-        ds_info = nullptr;
         MYLOGE("Could not create a thread");
         signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
     }
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 3ec8471..997999c 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -38,13 +38,16 @@
 
     status_t dump(int fd, const Vector<String16>& args) override;
 
+    binder::Status preDumpUiData(const std::string& callingPackage) override;
+
     binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
                                   android::base::unique_fd bugreport_fd,
                                   android::base::unique_fd screenshot_fd, int bugreport_mode,
-                                  const sp<IDumpstateListener>& listener,
+                                  int bugreport_flags, const sp<IDumpstateListener>& listener,
                                   bool is_screenshot_requested) override;
 
-    binder::Status cancelBugreport(int32_t calling_uid, const std::string& calling_package);
+    binder::Status cancelBugreport(int32_t calling_uid,
+                                   const std::string& calling_package) override;
 
   private:
     // Dumpstate object which contains all the bugreporting logic.
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 0793f0b..d4323af 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -49,6 +49,23 @@
     // Default mode.
     const int BUGREPORT_MODE_DEFAULT = 6;
 
+    // Use pre-dumped data.
+    const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1;
+
+    /**
+     * Speculatively pre-dumps UI data for a bugreport request that might come later.
+     *
+     * <p>Triggers the dump of certain critical UI data, e.g. traces stored in short
+     * ring buffers that might get lost by the time the actual bugreport is requested.
+     *
+     * <p>{@code startBugreport} will then pick the pre-dumped data if:
+     * - {@link BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA} is specified.
+     * - {@code preDumpUiData} and {@code startBugreport} were called by the same UID.
+     *
+     * @param callingPackage package of the original application that requested the report.
+     */
+    void preDumpUiData(@utf8InCpp String callingPackage);
+
     /**
      * Starts a bugreport in the background.
      *
@@ -63,13 +80,14 @@
      * @param bugreportFd the file to which the zipped bugreport should be written
      * @param screenshotFd the file to which screenshot should be written
      * @param bugreportMode the mode that specifies other run time options; must be one of above
+     * @param bugreportFlags flags to customize the bugreport generation
      * @param listener callback for updates; optional
      * @param isScreenshotRequested indicates screenshot is requested or not
      */
     void startBugreport(int callingUid, @utf8InCpp String callingPackage,
                         FileDescriptor bugreportFd, FileDescriptor screenshotFd,
-                        int bugreportMode, IDumpstateListener listener,
-                        boolean isScreenshotRequested);
+                        int bugreportMode, int bugreportFlags,
+                        IDumpstateListener listener, boolean isScreenshotRequested);
 
     /**
      * Cancels the bugreport currently in progress.
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 0b69829..7d9bdcb 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -237,6 +237,7 @@
 static const std::string DUMP_HALS_TASK = "DUMP HALS";
 static const std::string DUMP_BOARD_TASK = "dumpstate_board()";
 static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
+static const std::string POST_PROCESS_UI_TRACES_TASK = "POST-PROCESS UI TRACES";
 
 namespace android {
 namespace os {
@@ -1588,16 +1589,16 @@
 // via the consent they are shown. Ignores other errors that occur while running various
 // commands. The consent checking is currently done around long running tasks, which happen to
 // be distributed fairly evenly throughout the function.
-static Dumpstate::RunStatus dumpstate() {
+Dumpstate::RunStatus Dumpstate::dumpstate() {
     DurationReporter duration_reporter("DUMPSTATE");
 
     // Enqueue slow functions into the thread pool, if the parallel run is enabled.
     std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins,
-            dump_netstats_report;
+            dump_netstats_report, post_process_ui_traces;
     if (ds.dump_pool_) {
         // Pool was shutdown in DumpstateDefaultAfterCritical method in order to
-        // drop root user. Restarts it with two threads for the parallel run.
-        ds.dump_pool_->start(/* thread_counts = */2);
+        // drop root user. Restarts it.
+        ds.dump_pool_->start(/* thread_counts = */3);
 
         dump_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
         dump_incident_report = ds.dump_pool_->enqueueTask(
@@ -1607,6 +1608,8 @@
         dump_board = ds.dump_pool_->enqueueTaskWithFd(
             DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
         dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
+        post_process_ui_traces = ds.dump_pool_->enqueueTask(
+            POST_PROCESS_UI_TRACES_TASK, &Dumpstate::MaybePostProcessUiTraces, &ds);
     }
 
     // Dump various things. Note that anything that takes "long" (i.e. several seconds) should
@@ -1731,11 +1734,6 @@
     DumpFile("BINDER STATS", binder_logs_dir + "/stats");
     DumpFile("BINDER STATE", binder_logs_dir + "/state");
 
-    /* Add window and surface trace files. */
-    if (!PropertiesHelper::IsUserBuild()) {
-        ds.AddDir(WMTRACE_DATA_DIR, false);
-    }
-
     ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
 
     if (ds.dump_pool_) {
@@ -1815,6 +1813,14 @@
                 DumpIncidentReport);
     }
 
+    if (ds.dump_pool_) {
+        WaitForTask(std::move(post_process_ui_traces));
+    } else {
+        RUN_SLOW_FUNCTION_AND_LOG(POST_PROCESS_UI_TRACES_TASK, MaybePostProcessUiTraces);
+    }
+
+    MaybeAddUiTracesToZip();
+
     return Dumpstate::RunStatus::OK;
 }
 
@@ -2783,9 +2789,11 @@
 }
 
 void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
+                                        int bugreport_flags,
                                         const android::base::unique_fd& bugreport_fd_in,
                                         const android::base::unique_fd& screenshot_fd_in,
                                         bool is_screenshot_requested) {
+    this->use_predumped_ui_data = bugreport_flags & BugreportFlag::BUGREPORT_USE_PREDUMPED_UI_DATA;
     // Duplicate the fds because the passed in fds don't outlive the binder transaction.
     bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0));
     screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0));
@@ -2912,6 +2920,10 @@
     }
 }
 
+void Dumpstate::PreDumpUiData() {
+    MaybeSnapshotUiTraces();
+}
+
 /*
  * Dumps relevant information to a bugreport based on the given options.
  *
@@ -3103,9 +3115,9 @@
         // The trace file is added to the zip by MaybeAddSystemTraceToZip().
         MaybeSnapshotSystemTrace();
 
-        // If a winscope trace is running, snapshot it now. It will be pulled into bugreport later
-        // from WMTRACE_DATA_DIR.
-        MaybeSnapshotWinTrace();
+        // Snapshot the UI traces now (if running).
+        // The trace files will be added to bugreport later.
+        MaybeSnapshotUiTraces();
     }
     onUiIntensiveBugreportDumpsFinished(calling_uid);
     MaybeCheckUserConsent(calling_uid, calling_package);
@@ -3219,15 +3231,53 @@
     // file in the later stages.
 }
 
-void Dumpstate::MaybeSnapshotWinTrace() {
+void Dumpstate::MaybeSnapshotUiTraces() {
+    if (PropertiesHelper::IsUserBuild() || options_->use_predumped_ui_data) {
+        return;
+    }
+
     // Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol.
-    for (const auto& service : {"window", "input_method"}) {
+    for (const auto& service : {"input_method", "window"}) {
         RunCommand(
             // Empty name because it's not intended to be classified as a bugreport section.
             // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
             "", {"cmd", service, "tracing", "save-for-bugreport"},
             CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
     }
+
+    static const auto SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES = std::vector<std::string> {
+        "service", "call", "SurfaceFlinger", "1042"
+    };
+    // Empty name because it's not intended to be classified as a bugreport section.
+    // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
+    RunCommand(
+        "", SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES,
+        CommandOptions::WithTimeout(10).Always().AsRoot().RedirectStderr().Build());
+}
+
+void Dumpstate::MaybePostProcessUiTraces() {
+    if (PropertiesHelper::IsUserBuild()) {
+        return;
+    }
+
+    RunCommand(
+        // Empty name because it's not intended to be classified as a bugreport section.
+        // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
+        "", {
+            "/system/xbin/su", "system",
+            "/system/bin/layertracegenerator",
+            "/data/misc/wmtrace/transactions_trace.winscope",
+            "/data/misc/wmtrace/layers_trace_from_transactions.winscope"
+        },
+        CommandOptions::WithTimeout(120).Always().RedirectStderr().Build());
+}
+
+void Dumpstate::MaybeAddUiTracesToZip() {
+    if (PropertiesHelper::IsUserBuild()) {
+        return;
+    }
+
+    ds.AddDir(WMTRACE_DATA_DIR, false);
 }
 
 void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index ee6b1ae..1ab46c8 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -204,6 +204,12 @@
         BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT
     };
 
+    // The flags used to customize bugreport requests.
+    enum BugreportFlag {
+        BUGREPORT_USE_PREDUMPED_UI_DATA =
+          android::os::IDumpstate::BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA
+    };
+
     static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS;
 
     static Dumpstate& GetInstance();
@@ -333,6 +339,12 @@
 
     struct DumpOptions;
 
+    /**
+     * Pre-dump critical UI data, e.g. data stored in short ring buffers that might get lost
+     * by the time the actual bugreport is requested.
+     */
+    void PreDumpUiData();
+
     /*
      * Main entry point for running a complete bugreport.
      *
@@ -396,6 +408,8 @@
         // TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead.
         // The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
         BugreportMode bugreport_mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT;
+        // Will use data collected through a previous call to PreDumpUiData().
+        bool use_predumped_ui_data;
         // File descriptor to output zip file. Takes precedence over out_dir.
         android::base::unique_fd bugreport_fd;
         // File descriptor to screenshot file.
@@ -414,7 +428,8 @@
         RunStatus Initialize(int argc, char* argv[]);
 
         /* Initializes options from the requested mode. */
-        void Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd,
+        void Initialize(BugreportMode bugreport_mode, int bugreport_flags,
+                        const android::base::unique_fd& bugreport_fd,
                         const android::base::unique_fd& screenshot_fd,
                         bool is_screenshot_requested);
 
@@ -532,10 +547,13 @@
     RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
 
     RunStatus DumpstateDefaultAfterCritical();
+    RunStatus dumpstate();
 
     void MaybeTakeEarlyScreenshot();
     void MaybeSnapshotSystemTrace();
-    void MaybeSnapshotWinTrace();
+    void MaybeSnapshotUiTraces();
+    void MaybePostProcessUiTraces();
+    void MaybeAddUiTracesToZip();
 
     void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
 
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
index ec89c0d..a634f93 100644
--- a/cmds/dumpstate/main.cpp
+++ b/cmds/dumpstate/main.cpp
@@ -56,7 +56,7 @@
             MYLOGE("Unable to start 'dumpstate' service: %d", ret);
             exit(1);
         }
-        MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()");
+        MYLOGI("'dumpstate' service started and will wait for a call");
 
         // Waits forever for an incoming connection.
         // TODO(b/111441001): should this time out?
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 28e5ee2..b091c8e 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -497,14 +497,17 @@
     // Prepare arguments
     unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip"));
     unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png"));
+    int flags = 0;
 
     EXPECT_NE(bugreport_fd.get(), -1);
     EXPECT_NE(screenshot_fd.get(), -1);
 
     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
     android::binder::Status status =
-        ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
-                                  Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener, true);
+        ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
+                                  std::move(screenshot_fd),
+                                  Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener,
+                                  true);
     // startBugreport is an async call. Verify binder call succeeded first, then wait till listener
     // gets expected callbacks.
     EXPECT_TRUE(status.isOk());
@@ -532,6 +535,7 @@
     // Prepare arguments
     unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
+    int flags = 0;
 
     EXPECT_NE(bugreport_fd.get(), -1);
     EXPECT_NE(screenshot_fd.get(), -1);
@@ -539,9 +543,9 @@
     // Call startBugreport with bad arguments.
     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
     android::binder::Status status =
-        ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
-                                  2000,  // invalid bugreport mode
-                                  listener, false);
+        ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
+                                  std::move(screenshot_fd), 2000,  // invalid bugreport mode
+                                  flags, listener, false);
     EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
 
     // The service should have died, freeing itself up for a new invocation.
@@ -563,6 +567,7 @@
     unique_fd bugreport_fd2(dup(bugreport_fd.get()));
     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
     unique_fd screenshot_fd2(dup(screenshot_fd.get()));
+    int flags = 0;
 
     EXPECT_NE(bugreport_fd.get(), -1);
     EXPECT_NE(bugreport_fd2.get(), -1);
@@ -571,14 +576,18 @@
 
     sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
     android::binder::Status status =
-        ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
-                                  Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1, true);
+        ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
+                                  std::move(screenshot_fd),
+                                  Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener1,
+                                  true);
     EXPECT_TRUE(status.isOk());
 
     // try to make another call to startBugreport. This should fail.
     sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
-    status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2),
-                                       Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2, true);
+    status = ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd2),
+                                        std::move(screenshot_fd2),
+                                       Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags,
+                                       listener2, true);
     EXPECT_FALSE(status.isOk());
     WaitTillExecutionComplete(listener2.get());
     EXPECT_EQ(listener2->getErrorCode(),
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 70b4e5c..1ffcafa 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -31,6 +31,7 @@
 #include <signal.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <filesystem>
 #include <thread>
 
 #include <aidl/android/hardware/dumpstate/IDumpstateDevice.h>
@@ -237,7 +238,7 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeFullBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, 0, fd, fd, true);
     EXPECT_TRUE(options_.do_screenshot);
 
     // Other options retain default values
@@ -251,7 +252,7 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, 0, fd, fd, true);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_screenshot);
 
@@ -265,7 +266,7 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd, false);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, 0, fd, fd, false);
     EXPECT_TRUE(options_.is_remote_mode);
     EXPECT_FALSE(options_.do_vibrate);
     EXPECT_FALSE(options_.do_screenshot);
@@ -279,7 +280,7 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeWearBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, 0, fd, fd, true);
     EXPECT_TRUE(options_.do_screenshot);
     EXPECT_TRUE(options_.do_progress_updates);
 
@@ -294,7 +295,7 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd, false);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, 0, fd, fd, false);
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_TRUE(options_.telephony_only);
     EXPECT_TRUE(options_.do_progress_updates);
@@ -309,7 +310,7 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, 0, fd, fd, false);
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_TRUE(options_.wifi_only);
 
@@ -982,6 +983,19 @@
     EXPECT_FALSE(ds.dump_pool_);
 }
 
+TEST_F(DumpstateBaseTest, PreDumpUiData) {
+    // SurfaceFlinger's transactions trace is always enabled, i.e. it is always pre-dumped
+    static const auto kTransactionsTrace =
+            std::filesystem::path {"/data/misc/wmtrace/transactions_trace.winscope"};
+
+    std::system(("rm " + kTransactionsTrace.string()).c_str());
+    EXPECT_FALSE(std::filesystem::exists(kTransactionsTrace));
+
+    Dumpstate& ds_ = Dumpstate::GetInstance();
+    ds_.PreDumpUiData();
+    EXPECT_TRUE(std::filesystem::exists(kTransactionsTrace));
+}
+
 class ZippedBugReportStreamTest : public DumpstateBaseTest {
   public:
     void SetUp() {
@@ -1045,11 +1059,6 @@
     VerifyEntry(handle_, bugreport_txt_name, &entry);
 }
 
-class DumpstateServiceTest : public DumpstateBaseTest {
-  public:
-    DumpstateService dss;
-};
-
 class ProgressTest : public DumpstateBaseTest {
   public:
     Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") {