Refactored run_command functions.

Back in the day, dumpstate.c had a simple run_command() function. Then on
Android N, dumpstate.c became dumpstate.cpp and that function multiplied into:

- run_command()
- run_command_as_shell()
- run_command_always()

Not only these 3 commands were pretty much copy-and-pasted, but they
didn't take advantage of C++ features (such as std::vector and
std::string).

This CL refactor them into a single runCommand() function that takes an
optional CommandOptions argument to set its behavior. Examples:

// Run as shell
   runCommand("DUMPSYS MEMINFO", {"meminfo", "-a"},
              CommandOptions::WithTimeout(90).DropRoot().Build());

// Run always, as shell
   runCommand(nullptr, am, CommandOptions::WithTimeout(20).Build());

The legacy run_command() is still available since it's used by
device-specific dumpstate_board() implementations, but it will
eventually go away as well.

This change also:
- Refactored run_dumpsys() into runDumpsys().
- Added a .clang-format file (initially equals to dumpsys's).
- Renamed the variable names on those commands according to the style guide.

BUG: 26379932
Test: manual

Change-Id: Ie045eb2fb825e68088d231129044c59e61450d99
diff --git a/cmds/dumpstate/.clang-format b/cmds/dumpstate/.clang-format
new file mode 100644
index 0000000..fc4eb1b
--- /dev/null
+++ b/cmds/dumpstate/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+AccessModifierOffset: -2
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 6d90b97..7d73b0f 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -247,7 +247,8 @@
 
         // send SIGUSR1 to the anrd to generate a trace.
         sprintf(buf, "%u", pid);
-        if (run_command("ANRD_DUMP", 1, "kill", "-SIGUSR1", buf, NULL)) {
+        if (runCommand("ANRD_DUMP", {"kill", "-SIGUSR1", buf},
+                       CommandOptions::WithTimeout(1).Build())) {
             MYLOGE("anrd signal timed out. Please manually collect trace\n");
             return false;
         }
@@ -337,15 +338,15 @@
 
     MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes",
             systrace_path.c_str());
-    if (run_command("SYSTRACE", 120, "/system/bin/atrace", "--async_dump", "-o",
-            systrace_path.c_str(), NULL)) {
+    if (runCommand("SYSTRACE", {"/system/bin/atrace", "--async_dump", "-o", systrace_path},
+                   CommandOptions::WithTimeout(120).Build())) {
         MYLOGE("systrace timed out, its zip entry will be incomplete\n");
-        // TODO: run_command tries to kill the process, but atrace doesn't die peacefully; ideally,
-        // we should call strace to stop itself, but there is no such option yet (just a
-        // --async_stop, which stops and dump
-        //        if (run_command("SYSTRACE", 10, "/system/bin/atrace", "--kill", NULL)) {
-        //            MYLOGE("could not stop systrace ");
-        //        }
+        // TODO: run_command tries to kill the process, but atrace doesn't die
+        // peacefully; ideally, we should call strace to stop itself, but there is no such option
+        // yet (just a --async_stop, which stops and dump
+        // if (runCommand("SYSTRACE", {"/system/bin/atrace", "--kill"})) {
+        //   MYLOGE("could not stop systrace ");
+        // }
     }
     if (!add_zip_entry("systrace.txt", systrace_path)) {
         MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
@@ -373,14 +374,14 @@
         return;
     }
 
+    CommandOptions options = CommandOptions::WithTimeout(600).Build();
     if (!zip_writer) {
         // Write compressed and encoded raft logs to stdout if not zip_writer.
-        run_command("RAFT LOGS", 600, "logcompressor", "-r", RAFT_DIR, NULL);
+        runCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options);
         return;
     }
 
-    run_command("RAFT LOGS", 600, "logcompressor", "-n", "-r", RAFT_DIR,
-            "-o", raft_log_path.c_str(), NULL);
+    runCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_log_path}, options);
     if (!add_zip_entry("raft_log.txt", raft_log_path)) {
         MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str());
     } else {
@@ -403,49 +404,6 @@
     return false;
 }
 
-static void _run_dumpsys(const std::string& title, RootMode root_mode, int timeout_seconds,
-        const std::vector<std::string>& args) {
-    DurationReporter duration_reporter(title.c_str());
-
-    std::string timeout_string = std::to_string(timeout_seconds);
-
-    const char *dumpsys_args[MAX_ARGS_ARRAY_SIZE] =
-        { "/system/bin/dumpsys", "-t", timeout_string.c_str()};
-
-    int index = 3; // 'dumpsys' '-t' 'TIMEOUT'
-    for (const std::string& arg : args) {
-        if (index > MAX_ARGS_ARRAY_SIZE - 2) {
-            MYLOGE("Too many arguments for '%s': %d\n", title.c_str(), (int) args.size());
-            return;
-        }
-        dumpsys_args[index++] = arg.c_str();
-    }
-    // Always terminate with nullptr.
-    dumpsys_args[index] = nullptr;
-
-    std::string args_string;
-    format_args(index, dumpsys_args, &args_string);
-    printf("------ %s (%s) ------\n", title.c_str(), args_string.c_str());
-    fflush(stdout);
-
-    if (is_dry_run()) {
-        update_progress(timeout_seconds);
-        return;
-    }
-
-    run_command_always(title.c_str(), root_mode, NORMAL_STDOUT, timeout_seconds, dumpsys_args);
-}
-
-static void run_dumpsys(const std::string& title, int timeout_seconds,
-        const std::vector<std::string>& args) {
-    _run_dumpsys(title, DONT_DROP_ROOT, timeout_seconds, args);
-}
-
-static void run_dumpsys_as_shell(const std::string& title, int timeout_seconds,
-        const std::vector<std::string>& args) {
-    _run_dumpsys(title, DROP_ROOT, timeout_seconds, args);
-}
-
 static const char mmcblk0[] = "/sys/block/mmcblk0/";
 unsigned long worst_write_perf = 20000; /* in KB/s */
 
@@ -741,7 +699,7 @@
     dump_file(NULL, "/proc/version");
     printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
     printf("Bugreport format version: %s\n", version.c_str());
-    printf("Dumpstate info: id=%lu pid=%d\n", id, getpid());
+    printf("Dumpstate info: id=%lu pid=%d dry_run=%d\n", id, getpid(), dry_run);
     printf("\n");
 }
 
@@ -865,14 +823,14 @@
 }
 
 static void dump_iptables() {
-    run_command("IPTABLES", 10, "iptables", "-L", "-nvx", NULL);
-    run_command("IP6TABLES", 10, "ip6tables", "-L", "-nvx", NULL);
-    run_command("IPTABLE NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL);
+    runCommand("IPTABLES", {"iptables", "-L", "-nvx"});
+    runCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
+    runCommand("IPTABLE NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
     /* no ip6 nat */
-    run_command("IPTABLE MANGLE", 10, "iptables", "-t", "mangle", "-L", "-nvx", NULL);
-    run_command("IP6TABLE MANGLE", 10, "ip6tables", "-t", "mangle", "-L", "-nvx", NULL);
-    run_command("IPTABLE RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL);
-    run_command("IP6TABLE RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
+    runCommand("IPTABLE MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"});
+    runCommand("IP6TABLE MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"});
+    runCommand("IPTABLE RAW", {"iptables", "-t", "raw", "-L", "-nvx"});
+    runCommand("IP6TABLE RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
 }
 
 static void dumpstate(const std::string& screenshot_path, const std::string& version) {
@@ -880,13 +838,13 @@
     unsigned long timeout;
 
     dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
-    run_command("UPTIME", 10, "uptime", NULL);
+    runCommand("UPTIME", {"uptime"});
     dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd);
     dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd");
     dump_file("MEMORY INFO", "/proc/meminfo");
-    run_command("CPU INFO", 10, "top", "-b", "-n", "1", "-H", "-s", "6",
-                "-o", "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name", NULL);
-    run_command("PROCRANK", 20, SU_PATH, "root", "procrank", NULL);
+    runCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
+                            "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
+    runCommand("PROCRANK", {"procrank"}, CommandOptions::AS_ROOT_20);
     dump_file("VIRTUAL MEMORY STATS", "/proc/vmstat");
     dump_file("VMALLOC INFO", "/proc/vmallocinfo");
     dump_file("SLAB INFO", "/proc/slabinfo");
@@ -899,22 +857,22 @@
     dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
     dump_file("KERNEL SYNC", "/d/sync");
 
-    run_command("PROCESSES AND THREADS", 10, "ps", "-A", "-T", "-Z",
-                "-O", "pri,nice,rtprio,sched,pcy", NULL);
-    run_command("LIBRANK", 10, SU_PATH, "root", "librank", NULL);
+    runCommand("PROCESSES AND THREADS",
+               {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"});
+    runCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT_10);
 
-    run_command("PRINTENV", 10, "printenv", NULL);
-    run_command("NETSTAT", 10, "netstat", "-n", NULL);
+    runCommand("PRINTENV", {"printenv"});
+    runCommand("NETSTAT", {"netstat", "-n"});
     struct stat s;
     if (stat("/proc/modules", &s) != 0) {
         MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
     } else {
-        run_command("LSMOD", 10, "lsmod", NULL);
+        runCommand("LSMOD", {"lsmod"});
     }
 
     do_dmesg();
 
-    run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
+    runCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT_10);
     for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
     for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
     for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
@@ -931,30 +889,24 @@
     if (timeout < 20000) {
         timeout = 20000;
     }
-    run_command("SYSTEM LOG", timeout / 1000, "logcat", "-v", "threadtime",
-                                                        "-v", "printable",
-                                                        "-d",
-                                                        "*:v", NULL);
+    runCommand("SYSTEM LOG", {"logcat", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
+               CommandOptions::WithTimeout(timeout / 1000).Build());
     timeout = logcat_timeout("events");
     if (timeout < 20000) {
         timeout = 20000;
     }
-    run_command("EVENT LOG", timeout / 1000, "logcat", "-b", "events",
-                                                       "-v", "threadtime",
-                                                       "-v", "printable",
-                                                       "-d",
-                                                       "*:v", NULL);
+    runCommand("EVENT LOG",
+               {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
+               CommandOptions::WithTimeout(timeout / 1000).Build());
     timeout = logcat_timeout("radio");
     if (timeout < 20000) {
         timeout = 20000;
     }
-    run_command("RADIO LOG", timeout / 1000, "logcat", "-b", "radio",
-                                                       "-v", "threadtime",
-                                                       "-v", "printable",
-                                                       "-d",
-                                                       "*:v", NULL);
+    runCommand("RADIO LOG",
+               {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
+               CommandOptions::WithTimeout(timeout / 1000).Build());
 
-    run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
+    runCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
 
     /* show the traces we collected in main(), if that was done */
     if (dump_traces_path != NULL) {
@@ -1033,65 +985,57 @@
     }
 
     /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
-    run_command("LAST LOGCAT", 10, "logcat", "-L",
-                                             "-b", "all",
-                                             "-v", "threadtime",
-                                             "-v", "printable",
-                                             "-d",
-                                             "*:v", NULL);
+    runCommand("LAST LOGCAT",
+               {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-d", "*:v"});
 
     /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
 
-    run_command("NETWORK INTERFACES", 10, "ip", "link", NULL);
+    runCommand("NETWORK INTERFACES", {"ip", "link"});
 
-    run_command("IPv4 ADDRESSES", 10, "ip", "-4", "addr", "show", NULL);
-    run_command("IPv6 ADDRESSES", 10, "ip", "-6", "addr", "show", NULL);
+    runCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
+    runCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
 
-    run_command("IP RULES", 10, "ip", "rule", "show", NULL);
-    run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL);
+    runCommand("IP RULES", {"ip", "rule", "show"});
+    runCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
 
     dump_route_tables();
 
-    run_command("ARP CACHE", 10, "ip", "-4", "neigh", "show", NULL);
-    run_command("IPv6 ND CACHE", 10, "ip", "-6", "neigh", "show", NULL);
-    run_command("MULTICAST ADDRESSES", 10, "ip", "maddr", NULL);
-    run_command("WIFI NETWORKS", 20, "wpa_cli", "IFNAME=wlan0", "list_networks", NULL);
+    runCommand("ARP CACHE", {"ip", "-4", "neigh", "show"});
+    runCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"});
+    runCommand("MULTICAST ADDRESSES", {"ip", "maddr"});
+    runCommand("WIFI NETWORKS", {"wpa_cli", "IFNAME=wlan0", "list_networks"},
+               CommandOptions::WithTimeout(20).Build());
 
 #ifdef FWDUMP_bcmdhd
-    run_command("ND OFFLOAD TABLE", 5,
-            SU_PATH, "root", WLUTIL, "nd_hostip", NULL);
+    runCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT_5);
 
-    run_command("DUMP WIFI INTERNAL COUNTERS (1)", 20,
-            SU_PATH, "root", WLUTIL, "counters", NULL);
+    runCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20);
 
-    run_command("ND OFFLOAD STATUS (1)", 5,
-            SU_PATH, "root", WLUTIL, "nd_status", NULL);
+    runCommand("ND OFFLOAD STATUS (1)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
 
 #endif
     dump_file("INTERRUPTS (1)", "/proc/interrupts");
 
-    run_dumpsys("NETWORK DIAGNOSTICS", 10, {"connectivity", "--diag"});
+    runDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
+               CommandOptions::WithTimeout(10).Build());
 
 #ifdef FWDUMP_bcmdhd
-    run_command("DUMP WIFI STATUS", 20,
-            SU_PATH, "root", "dhdutil", "-i", "wlan0", "dump", NULL);
+    runCommand("DUMP WIFI STATUS", {"dhdutil", "-i", "wlan0", "dump"}, CommandOptions::AS_ROOT_20);
 
-    run_command("DUMP WIFI INTERNAL COUNTERS (2)", 20,
-            SU_PATH, "root", WLUTIL, "counters", NULL);
+    runCommand("DUMP WIFI INTERNAL COUNTERS (2)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20);
 
-    run_command("ND OFFLOAD STATUS (2)", 5,
-            SU_PATH, "root", WLUTIL, "nd_status", NULL);
+    runCommand("ND OFFLOAD STATUS (2)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
 #endif
     dump_file("INTERRUPTS (2)", "/proc/interrupts");
 
     print_properties();
 
-    run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
-    run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
+    runCommand("VOLD DUMP", {"vdc", "dump"});
+    runCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
 
-    run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
+    runCommand("FILESYSTEMS & FREE SPACE", {"df"});
 
-    run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
+    runCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
 
     printf("------ BACKLIGHTS ------\n");
     printf("LCD brightness=");
@@ -1124,53 +1068,51 @@
     char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0};
     property_get("ril.dumpstate.timeout", ril_dumpstate_timeout, "30");
     if (strnlen(ril_dumpstate_timeout, PROPERTY_VALUE_MAX - 1) > 0) {
-        if (is_user_build()) {
-            // su does not exist on user builds, so try running without it.
-            // This way any implementations of vril-dump that do not require
-            // root can run on user builds.
-            run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
-                    "vril-dump", NULL);
-        } else {
-            run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
-                    SU_PATH, "root", "vril-dump", NULL);
+        // su does not exist on user builds, so try running without it.
+        // This way any implementations of vril-dump that do not require
+        // root can run on user builds.
+        CommandOptions::CommandOptionsBuilder options =
+            CommandOptions::WithTimeout(atoi(ril_dumpstate_timeout));
+        if (!is_user_build()) {
+            options.AsRoot();
         }
+        runCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
     }
 
     printf("========================================================\n");
     printf("== Android Framework Services\n");
     printf("========================================================\n");
 
-    run_dumpsys("DUMPSYS", 60, {"--skip", "meminfo", "cpuinfo"});
+    runDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(60).Build());
 
     printf("========================================================\n");
     printf("== Checkins\n");
     printf("========================================================\n");
 
-    run_dumpsys("CHECKIN BATTERYSTATS", 30, {"batterystats", "-c"});
-    run_dumpsys("CHECKIN MEMINFO", 30, {"meminfo", "--checkin"});
-    run_dumpsys("CHECKIN NETSTATS", 30, {"netstats", "--checkin"});
-    run_dumpsys("CHECKIN PROCSTATS", 30, {"procstats", "-c"});
-    run_dumpsys("CHECKIN USAGESTATS", 30, {"usagestats", "-c"});
-    run_dumpsys("CHECKIN PACKAGE", 30, {"package", "--checkin"});
+    runDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
+    runDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"});
+    runDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
+    runDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
+    runDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
+    runDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
 
     printf("========================================================\n");
     printf("== Running Application Activities\n");
     printf("========================================================\n");
 
-    run_dumpsys("APP ACTIVITIES", 30, {"activity", "all"});
+    runDumpsys("APP ACTIVITIES", {"activity", "all"});
 
     printf("========================================================\n");
     printf("== Running Application Services\n");
     printf("========================================================\n");
 
-    run_dumpsys("APP SERVICES", 30, {"activity", "service", "all"});
+    runDumpsys("APP SERVICES", {"activity", "service", "all"});
 
     printf("========================================================\n");
     printf("== Running Application Providers\n");
     printf("========================================================\n");
 
-    run_dumpsys("APP PROVIDERS", 30, {"activity", "provider", "all"});
-
+    runDumpsys("APP PROVIDERS", {"activity", "provider", "all"});
 
     printf("========================================================\n");
     printf("== Final progress (pid %d): %d/%d (originally %d)\n",
@@ -1315,6 +1257,13 @@
         MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
     }
 
+    std::string args;
+    for (int i = 0; i < argc; i++) {
+        args += argv[i];
+        args += " ";
+    }
+    MYLOGD("Dumpstate command line: %s\n", args.c_str());
+
     /* gets the sequential id */
     char last_id[PROPERTY_VALUE_MAX];
     property_get("dumpstate.last_id", last_id, "0");
@@ -1345,9 +1294,6 @@
     }
 
     /* parse arguments */
-    std::string args;
-    format_args(argc, const_cast<const char **>(argv), &args);
-    MYLOGD("Dumpstate command line: %s\n", args.c_str());
     int c;
     while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
         switch (c) {
@@ -1569,8 +1515,10 @@
 
     // Invoking the following dumpsys calls before dump_traces() to try and
     // keep the system stats as close to its initial state as possible.
-    run_dumpsys_as_shell("DUMPSYS MEMINFO", 90, {"meminfo", "-a"});
-    run_dumpsys_as_shell("DUMPSYS CPUINFO", 10, {"cpuinfo", "-a"});
+    runDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"},
+               CommandOptions::WithTimeout(90).DropRoot().Build());
+    runDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"},
+               CommandOptions::WithTimeout(10).DropRoot().Build());
 
     /* collect stack traces from Dalvik and native processes (needs root) */
     dump_traces_path = dump_traces();
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 11b0c3e..978abb8 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -33,19 +33,151 @@
 #include <unistd.h>
 #include <stdbool.h>
 #include <stdio.h>
+
+#include <string>
 #include <vector>
 
 #define SU_PATH "/system/xbin/su"
 
 // Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
 // std::vector<std::string>
+// TODO: remove once not used
 #define MAX_ARGS_ARRAY_SIZE 1000
 
-
+// TODO: remove once moved to HAL
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/*
+ * Defines the Linux user that should be executing a command.
+ */
+enum RootMode {
+    /* Explicitly change the `uid` and `gid` to be `shell`.*/
+    DROP_ROOT,
+    /* Don't change the `uid` and `gid`. */
+    DONT_DROP_ROOT,
+    /* Prefix the command with `/PATH/TO/su root`. Won't work non user builds. */
+    SU_ROOT
+};
+
+/*
+ * Defines what should happen with the `stdout` stream of a command.
+ */
+enum StdoutMode {
+    /* Don't change `stdout`. */
+    NORMAL_STDOUT,
+    /* Redirect `stdout` to `stderr`. */
+    REDIRECT_TO_STDERR
+};
+
+/*
+ * Helper class used to report how long it takes for a section to finish.
+ *
+ * Typical usage:
+ *
+ *    DurationReporter duration_reporter(title);
+ *
+ */
+class DurationReporter {
+  public:
+    DurationReporter(const char* title);
+    DurationReporter(const char* title, FILE* out);
+
+    ~DurationReporter();
+
+    static uint64_t nanotime();
+
+  private:
+    // TODO: use std::string for title, once dump_files() and other places that pass a char* are
+    // refactored as well.
+    const char* mTitle;
+    FILE* mOut;
+    uint64_t mStarted;
+};
+
+/*
+ * Value object used to set command options.
+ *
+ * Typically constructed using a builder with chained setters. Examples:
+ *
+ *  CommandOptions::WithTimeout(20).AsRoot().Build();
+ *  CommandOptions::WithTimeout(10).Always().RedirectStderr().Build();
+ *
+ * Although the builder could be used to dynamically set values. Example:
+ *
+ *  CommandOptions::CommandOptionsBuilder options =
+ *  CommandOptions::WithTimeout(10);
+ *  if (!is_user_build()) {
+ *    options.AsRoot();
+ *  }
+ *  runCommand("command", {"args"}, options.Build());
+ */
+class CommandOptions {
+  private:
+    class CommandOptionsValues {
+      private:
+        CommandOptionsValues(long timeout);
+
+        long mTimeout;
+        bool mAlways;
+        RootMode mRootMode;
+        StdoutMode mStdoutMode;
+        std::string mLoggingMessage;
+
+        friend class CommandOptions;
+        friend class CommandOptionsBuilder;
+    };
+
+    CommandOptions(const CommandOptionsValues& values);
+
+    const CommandOptionsValues mValues;
+
+  public:
+    class CommandOptionsBuilder {
+      public:
+        /* Sets the command to always run, even on `dry-run` mode. */
+        CommandOptionsBuilder& Always();
+        /* Sets the command's RootMode as `SU_ROOT` */
+        CommandOptionsBuilder& AsRoot();
+        /* Sets the command's RootMode as `DROP_ROOT` */
+        CommandOptionsBuilder& DropRoot();
+        /* Sets the command's StdoutMode `REDIRECT_TO_STDERR` */
+        CommandOptionsBuilder& RedirectStderr();
+        /* When not empty, logs a message before executing the command.
+         * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */
+        CommandOptionsBuilder& Log(const std::string& message);
+        /* Builds the command options. */
+        CommandOptions Build();
+
+      private:
+        CommandOptionsBuilder(long timeout);
+        CommandOptionsValues mValues;
+        friend class CommandOptions;
+    };
+
+    /** Gets the command timeout, in seconds. */
+    long Timeout() const;
+    /* Checks whether the command should always be run, even on dry-run mode. */
+    bool Always() const;
+    /** Gets the RootMode of the command. */
+    RootMode RootMode() const;
+    /** Gets the StdoutMode of the command. */
+    StdoutMode StdoutMode() const;
+    /** Gets the logging message header, it any. */
+    std::string LoggingMessage() const;
+
+    /** Creates a builder with the requied timeout. */
+    static CommandOptionsBuilder WithTimeout(long timeout);
+
+    // Common options.
+    static CommandOptions DEFAULT;
+    static CommandOptions DEFAULT_DUMPSYS;
+    static CommandOptions AS_ROOT_5;
+    static CommandOptions AS_ROOT_10;
+    static CommandOptions AS_ROOT_20;
+};
+
 typedef void (for_each_pid_func)(int, const char *);
 typedef void (for_each_tid_func)(int, int, const char *);
 
@@ -110,21 +242,35 @@
         bool (*skip)(const char *path),
         int (*dump_from_fd)(const char *title, const char *path, int fd));
 
-// TODO: need to refactor all those run_command variations; there shold be just one, receiving an
-// optional CommandOptions objects with values such as run_always, drop_root, etc...
-
-/* forks a command and waits for it to finish -- terminate args with NULL */
-int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...);
+/* forks a command and waits for it to finish -- terminate args with NULL
+ * DEPRECATED: will be removed once device-specific implementations use
+ * runCommand */
 int run_command(const char *title, int timeout_seconds, const char *command, ...);
 
-enum RootMode { DROP_ROOT, DONT_DROP_ROOT };
-enum StdoutMode { NORMAL_STDOUT, REDIRECT_TO_STDERR };
+/*
+ * Forks a command, waits for it to finish, and returns its status.
+ *
+ * |title| description of the command printed on `stdout` (or `nullptr` to skip
+ * description).
+ * |full_command| array containing the command (first entry) and its arguments.
+ * Must contain at least one element.
+ * |options| optional argument defining the command's behavior.
+ */
+// TODO: use std::string for title once other char* title references are refactored.
+int runCommand(const char* title, const std::vector<std::string>& fullCommand,
+               const CommandOptions& options = CommandOptions::DEFAULT);
 
-/* forks a command and waits for it to finish
-   first element of args is the command, and last must be NULL.
-   command is always ran, even when _DUMPSTATE_DRY_RUN_ is defined. */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
-        int timeout_seconds, const char *args[]);
+/*
+ * Runs `dumpsys` with the given arguments, automatically setting its timeout
+ * (`-t` argument)
+ * according to the command options.
+ *
+ * |title| description of the command printed on `stdout`.
+ * |dumpsys_args| `dumpsys` arguments (except `-t`).
+ * |options| optional argument defining the command's behavior.
+ */
+void runDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+                const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS);
 
 /* switch to non-root user and group */
 bool drop_root_user();
@@ -213,29 +359,6 @@
  */
 bool is_dry_run();
 
-/*
- * Helper class used to report how long it takes for a section to finish.
- *
- * Typical usage:
- *
- *    DurationReporter duration_reporter(title);
- *
- */
-class DurationReporter {
-public:
-    explicit DurationReporter(const char *title);
-    DurationReporter(const char *title, FILE* out);
-
-    ~DurationReporter();
-
-    static uint64_t nanotime();
-
-private:
-    const char* title_;
-    FILE* out_;
-    uint64_t started_;
-};
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 3e4d343..5c59bae 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -69,25 +69,99 @@
         NULL,
 };
 
+CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
+CommandOptions CommandOptions::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
+CommandOptions CommandOptions::AS_ROOT_5 = CommandOptions::WithTimeout(5).AsRoot().Build();
+CommandOptions CommandOptions::AS_ROOT_10 = CommandOptions::WithTimeout(10).AsRoot().Build();
+CommandOptions CommandOptions::AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
+
+CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(long timeout) : mValues(timeout) {
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
+    mValues.mAlways = true;
+    return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
+    mValues.mRootMode = SU_ROOT;
+    return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
+    mValues.mRootMode = DROP_ROOT;
+    return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
+    mValues.mStdoutMode = REDIRECT_TO_STDERR;
+    return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
+    const std::string& message) {
+    mValues.mLoggingMessage = message;
+    return *this;
+}
+
+CommandOptions CommandOptions::CommandOptionsBuilder::Build() {
+    return CommandOptions(mValues);
+}
+
+CommandOptions::CommandOptionsValues::CommandOptionsValues(long timeout)
+    : mTimeout(timeout),
+      mAlways(false),
+      mRootMode(DONT_DROP_ROOT),
+      mStdoutMode(NORMAL_STDOUT),
+      mLoggingMessage("") {
+}
+
+CommandOptions::CommandOptions(const CommandOptionsValues& values) : mValues(values) {
+}
+
+long CommandOptions::Timeout() const {
+    return mValues.mTimeout;
+}
+
+bool CommandOptions::Always() const {
+    return mValues.mAlways;
+}
+
+RootMode CommandOptions::RootMode() const {
+    return mValues.mRootMode;
+}
+
+StdoutMode CommandOptions::StdoutMode() const {
+    return mValues.mStdoutMode;
+}
+
+std::string CommandOptions::LoggingMessage() const {
+    return mValues.mLoggingMessage;
+}
+
+CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(long timeout) {
+    return CommandOptions::CommandOptionsBuilder(timeout);
+}
+
 DurationReporter::DurationReporter(const char *title) : DurationReporter(title, stdout) {}
 
 DurationReporter::DurationReporter(const char *title, FILE *out) {
-    title_ = title;
-    if (title) {
-        started_ = DurationReporter::nanotime();
+    mTitle = title;
+    if (title != nullptr) {
+        mStarted = DurationReporter::nanotime();
     }
-    out_ = out;
+    mOut = out;
 }
 
 DurationReporter::~DurationReporter() {
-    if (title_) {
-        uint64_t elapsed = DurationReporter::nanotime() - started_;
+    if (mTitle != nullptr) {
+        uint64_t elapsed = DurationReporter::nanotime() - mStarted;
         // Use "Yoda grammar" to make it easier to grep|sort sections.
-        if (out_) {
-            fprintf(out_, "------ %.3fs was the duration of '%s' ------\n",
-                   (float) elapsed / NANOS_PER_SEC, title_);
+        if (mOut != nullptr) {
+            fprintf(mOut, "------ %.3fs was the duration of '%s' ------\n",
+                    (float)elapsed / NANOS_PER_SEC, mTitle);
         } else {
-            MYLOGD("Duration of '%s': %.3fs\n", title_, (float) elapsed / NANOS_PER_SEC);
+            MYLOGD("Duration of '%s': %.3fs\n", mTitle, (float)elapsed / NANOS_PER_SEC);
         }
     }
 }
@@ -408,7 +482,7 @@
 
     snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name);
     snprintf(arg, sizeof(arg), "%d", pid);
-    run_command(title, 10, SU_PATH, "root", "showmap", "-q", arg, NULL);
+    runCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT_10);
 }
 
 static int _dump_file_from_fd(const char *title, const char *path, int fd) {
@@ -648,103 +722,82 @@
     return true;
 }
 
-// TODO: refactor all those commands that convert args
-void format_args(const char* command, const char *args[], std::string *string);
-
-int run_command(const char *title, int timeout_seconds, const char *command, ...) {
-    DurationReporter duration_reporter(title);
-    fflush(stdout);
-
-    const char *args[MAX_ARGS_ARRAY_SIZE] = {command};
+int run_command(const char* title, int timeout_seconds, const char* command, ...) {
+    std::vector<std::string> fullCommand = {command};
     size_t arg;
     va_list ap;
     va_start(ap, command);
-    if (title) printf("------ %s (%s", title, command);
-    bool null_terminated = false;
-    for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
-        args[arg] = va_arg(ap, const char *);
-        if (args[arg] == nullptr) {
-            null_terminated = true;
+    for (arg = 0; arg < MAX_ARGS_ARRAY_SIZE; ++arg) {
+        const char* ptr = va_arg(ap, const char*);
+        if (ptr == nullptr) {
             break;
         }
-        // TODO: null_terminated check is not really working; line below would crash dumpstate if
-        // nullptr is missing
-        if (title) printf(" %s", args[arg]);
-    }
-    if (title) printf(") ------\n");
-    fflush(stdout);
-    if (!null_terminated) {
-        // Fail now, otherwise execvp() call on run_command_always() might hang.
-        std::string cmd;
-        format_args(command, args, &cmd);
-        MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
-        va_end(ap);
-        return -1;
-    }
-
-    int status = 0;
-    if (is_dry_run()) {
-        update_progress(timeout_seconds);
-    } else {
-        status = run_command_always(title, DONT_DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
+        fullCommand.push_back(ptr);
     }
     va_end(ap);
-    return status;
+
+    return runCommand(title, fullCommand, CommandOptions::WithTimeout(timeout_seconds).Build());
 }
 
-int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...) {
-    DurationReporter duration_reporter(title);
-    fflush(stdout);
+int runCommand(const char* title, const std::vector<std::string>& fullCommand,
+               const CommandOptions& options) {
+    if (fullCommand.empty()) {
+        MYLOGE("No arguments on command '%s'\n", title);
+        return -1;
+    }
+    DurationReporter durationReporter(title);
 
-    const char *args[MAX_ARGS_ARRAY_SIZE] = {command};
-    size_t arg;
-    va_list ap;
-    va_start(ap, command);
-    if (title) printf("------ %s (%s", title, command);
-    bool null_terminated = false;
-    for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
-        args[arg] = va_arg(ap, const char *);
-        if (args[arg] == nullptr) {
-            null_terminated = true;
-            break;
+    int size = fullCommand.size() + 1;  // null terminated
+    if (options.RootMode() == SU_ROOT) {
+        size += 2;  // "su" "root"
+    }
+
+    const char* args[size];
+
+    printf("------");
+    if (title) printf(" %s", title);
+    printf(" (");
+
+    std::string commandString;
+    int i = 0;
+    if (options.RootMode() == SU_ROOT) {
+        args[0] = SU_PATH;
+        commandString += SU_PATH;
+        args[1] = "root";
+        commandString += " root ";
+    }
+    for (auto arg = fullCommand.begin(); arg < fullCommand.end(); arg++) {
+        args[i++] = arg->c_str();
+        commandString += arg->c_str();
+        if (arg != fullCommand.end() - 1) {
+            commandString += " ";
         }
-        // TODO: null_terminated check is not really working; line below would crash dumpstate if
-        // nullptr is missing
-        if (title) printf(" %s", args[arg]);
     }
-    if (title) printf(") ------\n");
+    args[i] = nullptr;
+    const char* path = args[0];
+    const char* command = commandString.c_str();
+    printf("%s)\n", command);
+
     fflush(stdout);
-    if (!null_terminated) {
-        // Fail now, otherwise execvp() call on run_command_always() might hang.
-        std::string cmd;
-        format_args(command, args, &cmd);
-        MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
-        va_end(ap);
-        return -1;
+
+    const std::string& loggingMessage = options.LoggingMessage();
+    if (!loggingMessage.empty()) {
+        MYLOGI(loggingMessage.c_str(), commandString.c_str());
     }
 
-    int status = 0;
-    if (is_dry_run()) {
-        update_progress(timeout_seconds);
-    } else {
-        status = run_command_always(title, DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
+    if (is_dry_run() && !options.Always()) {
+        update_progress(options.Timeout());
+        return 0;
     }
-    va_end(ap);
-    return status;
-}
 
-/* forks a command and waits for it to finish */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
-        int timeout_seconds, const char *args[]) {
-    bool silent = (stdout_mode == REDIRECT_TO_STDERR);
-    // TODO: need to check if args is null-terminated, otherwise execvp will crash dumpstate
+    bool silent = (options.StdoutMode() == REDIRECT_TO_STDERR);
 
-    /* TODO: for now we're simplifying the progress calculation by using the timeout as the weight.
-     * It's a good approximation for most cases, except when calling dumpsys, where its weight
-     * should be much higher proportionally to its timeout. */
-    int weight = timeout_seconds;
+    /* TODO: for now we're simplifying the progress calculation by using the
+     * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
+     * where its weight should be much higher proportionally to its timeout.
+     * Ideally, it should use a options.EstimatedDuration() instead...*/
+    int weight = options.Timeout();
 
-    const char *command = args[0];
     uint64_t start = DurationReporter::nanotime();
     pid_t pid = fork();
 
@@ -757,9 +810,9 @@
 
     /* handle child case */
     if (pid == 0) {
-        if (root_mode == DROP_ROOT && !drop_root_user()) {
-        if (!silent) printf("*** fail todrop root before running %s: %s\n", command,
-                strerror(errno));
+        if (options.RootMode() == DROP_ROOT && !drop_root_user()) {
+            if (!silent)
+                printf("*** failed to drop root before running %s: %s\n", command, strerror(errno));
             MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
             return -1;
         }
@@ -778,49 +831,45 @@
         sigact.sa_handler = SIG_IGN;
         sigaction(SIGPIPE, &sigact, NULL);
 
-        execvp(command, (char**) args);
-        // execvp's result will be handled after waitpid_with_timeout() below, but if it failed,
-        // it's safer to exit dumpstate.
-        MYLOGD("execvp on command '%s' failed (error: %s)", command, strerror(errno));
+        execvp(path, (char**)args);
+        // execvp's result will be handled after waitpid_with_timeout() below, but
+        // if it failed, it's safer to exit dumpstate.
+        MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno));
         fflush(stdout);
-        // Must call _exit (instead of exit), otherwise it will corrupt the zip file.
+        // Must call _exit (instead of exit), otherwise it will corrupt the zip
+        // file.
         _exit(EXIT_FAILURE);
     }
 
     /* handle parent case */
     int status;
-    bool ret = waitpid_with_timeout(pid, timeout_seconds, &status);
+    bool ret = waitpid_with_timeout(pid, options.Timeout(), &status);
     uint64_t elapsed = DurationReporter::nanotime() - start;
-    std::string cmd; // used to log command and its args
     if (!ret) {
         if (errno == ETIMEDOUT) {
-            format_args(command, args, &cmd);
-            if (!silent) printf("*** command '%s' timed out after %.3fs (killing pid %d)\n",
-            cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
-            MYLOGE("command '%s' timed out after %.3fs (killing pid %d)\n", cmd.c_str(),
-                   (float) elapsed / NANOS_PER_SEC, pid);
+            if (!silent)
+                printf("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
+                       (float)elapsed / NANOS_PER_SEC, pid);
+            MYLOGE("command '%s' timed out after %.3fs (killing pid %d)\n", command,
+                   (float)elapsed / NANOS_PER_SEC, pid);
         } else {
-            format_args(command, args, &cmd);
-            if (!silent) printf("*** command '%s': Error after %.4fs (killing pid %d)\n",
-            cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
-            MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", cmd.c_str(),
-                   (float) elapsed / NANOS_PER_SEC, pid);
+            if (!silent)
+                printf("*** command '%s': Error after %.4fs (killing pid %d)\n", command,
+                       (float)elapsed / NANOS_PER_SEC, pid);
+            MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command,
+                   (float)elapsed / NANOS_PER_SEC, pid);
         }
         kill(pid, SIGTERM);
         if (!waitpid_with_timeout(pid, 5, NULL)) {
             kill(pid, SIGKILL);
             if (!waitpid_with_timeout(pid, 5, NULL)) {
-                if (!silent) printf("could not kill command '%s' (pid %d) even with SIGKILL.\n",
-                        command, pid);
+                if (!silent)
+                    printf("could not kill command '%s' (pid %d) even with SIGKILL.\n", command,
+                           pid);
                 MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
             }
         }
         return -1;
-    } else if (status) {
-        format_args(command, args, &cmd);
-        if (!silent) printf("*** command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
-        MYLOGE("command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
-        return -2;
     }
 
     if (WIFSIGNALED(status)) {
@@ -837,6 +886,14 @@
     return status;
 }
 
+void runDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+                const CommandOptions& options) {
+    std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-t",
+                                        std::to_string(options.Timeout())};
+    dumpsys.insert(dumpsys.end(), dumpsysArgs.begin(), dumpsysArgs.end());
+    runCommand(title.c_str(), dumpsys, options);
+}
+
 bool drop_root_user() {
     if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
         MYLOGD("drop_root_user(): already running as Shell");
@@ -884,26 +941,16 @@
 }
 
 void send_broadcast(const std::string& action, const std::vector<std::string>& args) {
-    if (args.size() > 1000) {
-        MYLOGE("send_broadcast: too many arguments (%d)\n", (int) args.size());
-        return;
-    }
-    const char *am_args[MAX_ARGS_ARRAY_SIZE] = { "/system/bin/am", "broadcast", "--user", "0", "-a",
-                                     action.c_str() };
-    size_t am_index = 5; // Starts at the index of last initial value above.
-    for (const std::string& arg : args) {
-        if (am_index > MAX_ARGS_ARRAY_SIZE - 2) {
-            MYLOGE("send_broadcast: too many arguments (%d)\n", (int) args.size());
-            return;
-        }
-        am_args[++am_index] = arg.c_str();
-    }
-    // Always terminate with nullptr.
-    am_args[am_index + 1] = nullptr;
-    std::string args_string;
-    format_args(am_index + 1, am_args, &args_string);
-    MYLOGD("send_broadcast command: %s\n", args_string.c_str());
-    run_command_always(NULL, DROP_ROOT, REDIRECT_TO_STDERR, 20, am_args);
+    std::vector<std::string> am = {"/system/bin/am", "broadcast", "--user", "0", "-a", action};
+
+    am.insert(am.end(), args.begin(), args.end());
+
+    runCommand(nullptr, am, CommandOptions::WithTimeout(20)
+                                .Log("Sending broadcast: '%s'\n")
+                                .Always()
+                                .DropRoot()
+                                .RedirectStderr()
+                                .Build());
 }
 
 size_t num_props = 0;
@@ -1202,8 +1249,8 @@
     // need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name.
     // Add a fixed max limit so this doesn't go awry.
     for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) {
-        run_command("ROUTE TABLE IPv4", 10, "ip", "-4", "route", "show", "table", table, NULL);
-        run_command("ROUTE TABLE IPv6", 10, "ip", "-6", "route", "show", "table", table, NULL);
+        runCommand("ROUTE TABLE IPv4", {"ip", "-4", "route", "show", "table", table});
+        runCommand("ROUTE TABLE IPv6", {"ip", "-6", "route", "show", "table", table});
     }
     fclose(fp);
 }
@@ -1261,8 +1308,8 @@
 }
 
 void take_screenshot(const std::string& path) {
-    const char *args[] = { "/system/bin/screencap", "-p", path.c_str(), NULL };
-    run_command_always(NULL, DONT_DROP_ROOT, REDIRECT_TO_STDERR, 10, args);
+    runCommand(nullptr, {"/system/bin/screencap", "-p", path},
+               CommandOptions::WithTimeout(10).Always().RedirectStderr().Build());
 }
 
 void vibrate(FILE* vibrator, int ms) {
@@ -1399,30 +1446,3 @@
 
     printf("\n");
 }
-
-// TODO: refactor all those commands that convert args
-void format_args(int argc, const char *argv[], std::string *args) {
-    LOG_ALWAYS_FATAL_IF(args == nullptr);
-    for (int i = 0; i < argc; i++) {
-        args->append(argv[i]);
-        if (i < argc -1) {
-          args->append(" ");
-        }
-    }
-}
-void format_args(const char* command, const char *args[], std::string *string) {
-    LOG_ALWAYS_FATAL_IF(args == nullptr || command == nullptr);
-    string->append(command);
-    if (args[1] == nullptr) return;
-    string->append(" ");
-
-    for (int arg = 1; arg <= MAX_ARGS_ARRAY_SIZE; ++arg) {
-        if (args[arg] == nullptr) return;
-        string->append(args[arg]);
-        if (args[arg+1] != nullptr) {
-            string->append(" ");
-        }
-    }
-    // TODO: not really working: if NULL is missing, it will crash dumpstate.
-    MYLOGE("internal error: missing NULL entry on %s", string->c_str());
-}