Refactor dumpsys to expose helper apis

- allow dumpstate to interact with services without executing dumpsys binary
- Remove "NORMAL" priority from section name for backwards compatibility when switching to version 2.0

Bug: 67716082

Test: mmm -j56 frameworks/native/cmds/dumpsys && \
      adb sync data && \
      adb shell /data/nativetest/dumpsys_test/dumpsys_test && \
      adb shell /data/nativetest64/dumpsys_test/dumpsys_test && \
      printf "\n\n#### ALL TESTS PASSED ####\n"

Test: manual tests with "adb bugreport"
Change-Id: I4198333a58ffe6cb521b5cb7066520c7a3ef0675
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 0862a40..ae0cc01 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -43,9 +43,11 @@
 #include "dumpsys.h"
 
 using namespace android;
-using android::base::StringPrintf;
-using android::base::unique_fd;
-using android::base::WriteFully;
+using ::android::base::StringAppendF;
+using ::android::base::StringPrintf;
+using ::android::base::unique_fd;
+using ::android::base::WriteFully;
+using ::android::base::WriteStringToFd;
 
 static int sort_func(const String16* lhs, const String16* rhs)
 {
@@ -96,6 +98,19 @@
     return false;
 }
 
+String16 ConvertBitmaskToPriorityType(int bitmask) {
+    if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) {
+        return String16(PriorityDumper::PRIORITY_ARG_CRITICAL);
+    }
+    if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) {
+        return String16(PriorityDumper::PRIORITY_ARG_HIGH);
+    }
+    if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+        return String16(PriorityDumper::PRIORITY_ARG_NORMAL);
+    }
+    return String16("");
+}
+
 int Dumpsys::main(int argc, char* const argv[]) {
     Vector<String16> services;
     Vector<String16> args;
@@ -104,9 +119,9 @@
     Vector<String16> protoServices;
     bool showListOnly = false;
     bool skipServices = false;
-    bool filterByProto = false;
+    bool asProto = false;
     int timeoutArgMs = 10000;
-    int dumpPriorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
+    int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
     static struct option longOptions[] = {{"priority", required_argument, 0, 0},
                                           {"proto", no_argument, 0, 0},
                                           {"skip", no_argument, 0, 0},
@@ -131,13 +146,13 @@
             if (!strcmp(longOptions[optionIndex].name, "skip")) {
                 skipServices = true;
             } else if (!strcmp(longOptions[optionIndex].name, "proto")) {
-                filterByProto = true;
+                asProto = true;
             } else if (!strcmp(longOptions[optionIndex].name, "help")) {
                 usage();
                 return 0;
             } else if (!strcmp(longOptions[optionIndex].name, "priority")) {
                 priorityType = String16(String8(optarg));
-                if (!ConvertPriorityTypeToBitmask(priorityType, dumpPriorityFlags)) {
+                if (!ConvertPriorityTypeToBitmask(priorityType, priorityFlags)) {
                     fprintf(stderr, "\n");
                     usage();
                     return -1;
@@ -198,28 +213,11 @@
     }
 
     if (services.empty() || showListOnly) {
-        // gets all services
-        services = sm_->listServices(dumpPriorityFlags);
-        services.sort(sort_func);
-        if (filterByProto) {
-            protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
-            protoServices.sort(sort_func);
-            Vector<String16> intersection;
-            std::set_intersection(services.begin(), services.end(), protoServices.begin(),
-                                  protoServices.end(), std::back_inserter(intersection));
-            services = std::move(intersection);
-            args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
-        }
-        if (dumpPriorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
-            args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
-            args.insertAt(priorityType, 1);
-        } else {
-            args.add(String16("-a"));
-        }
+        services = listServices(priorityFlags, asProto);
+        setServiceArgs(args, asProto, priorityFlags);
     }
 
     const size_t N = services.size();
-
     if (N > 1) {
         // first print a list of the current services
         aout << "Currently running services:" << endl;
@@ -239,129 +237,204 @@
     }
 
     for (size_t i = 0; i < N; i++) {
-        const String16& service_name = std::move(services[i]);
-        if (IsSkipped(skippedServices, service_name)) continue;
+        const String16& serviceName = services[i];
+        if (IsSkipped(skippedServices, serviceName)) continue;
 
-        sp<IBinder> service = sm_->checkService(service_name);
-        if (service != nullptr) {
-            int sfd[2];
-
-            if (pipe(sfd) != 0) {
-                aerr << "Failed to create pipe to dump service info for " << service_name
-                     << ": " << strerror(errno) << endl;
-                continue;
+        if (startDumpThread(serviceName, args) == OK) {
+            bool addSeparator = (N > 1);
+            if (addSeparator) {
+                writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
             }
+            std::chrono::duration<double> elapsedDuration;
+            size_t bytesWritten = 0;
+            status_t status =
+                writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs),
+                          asProto, elapsedDuration, bytesWritten);
 
-            unique_fd local_end(sfd[0]);
-            unique_fd remote_end(sfd[1]);
-            sfd[0] = sfd[1] = -1;
-
-            if (N > 1) {
-                aout << "------------------------------------------------------------"
-                        "-------------------" << endl;
-                if (dumpPriorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
-                    aout << "DUMP OF SERVICE " << service_name << ":" << endl;
-                } else {
-                    aout << "DUMP OF SERVICE " << priorityType << " " << service_name << ":" << endl;
-                }
-            }
-
-            // dump blocks until completion, so spawn a thread..
-            std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
-                int err = service->dump(remote_end.get(), args);
-
-                // It'd be nice to be able to close the remote end of the socketpair before the dump
-                // call returns, to terminate our reads if the other end closes their copy of the
-                // file descriptor, but then hangs for some reason. There doesn't seem to be a good
-                // way to do this, though.
-                remote_end.reset();
-
-                if (err != 0) {
-                    aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
-                         << endl;
-                }
-            });
-
-            auto timeout = std::chrono::milliseconds(timeoutArgMs);
-            auto start = std::chrono::steady_clock::now();
-            auto end = start + timeout;
-
-            struct pollfd pfd = {
-                .fd = local_end.get(),
-                .events = POLLIN
-            };
-
-            bool timed_out = false;
-            bool error = false;
-            while (true) {
-                // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
-                auto time_left_ms = [end]() {
-                    auto now = std::chrono::steady_clock::now();
-                    auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
-                    return std::max(diff.count(), 0ll);
-                };
-
-                int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
-                if (rc < 0) {
-                    aerr << "Error in poll while dumping service " << service_name << " : "
-                         << strerror(errno) << endl;
-                    error = true;
-                    break;
-                } else if (rc == 0) {
-                    timed_out = true;
-                    break;
-                }
-
-                char buf[4096];
-                rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
-                if (rc < 0) {
-                    aerr << "Failed to read while dumping service " << service_name << ": "
-                         << strerror(errno) << endl;
-                    error = true;
-                    break;
-                } else if (rc == 0) {
-                    // EOF.
-                    break;
-                }
-
-                if (!WriteFully(STDOUT_FILENO, buf, rc)) {
-                    aerr << "Failed to write while dumping service " << service_name << ": "
-                         << strerror(errno) << endl;
-                    error = true;
-                    break;
-                }
-            }
-
-            if (timed_out) {
+            if (status == TIMED_OUT) {
                 aout << endl
-                     << "*** SERVICE '" << service_name << "' DUMP TIMEOUT (" << timeoutArgMs
+                     << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
                      << "ms) EXPIRED ***" << endl
                      << endl;
             }
 
-            if (timed_out || error) {
-                dump_thread.detach();
-            } else {
-                dump_thread.join();
+            if (addSeparator) {
+                writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration);
             }
-
-            if (N > 1) {
-              std::chrono::duration<double> elapsed_seconds =
-                  std::chrono::steady_clock::now() - start;
-              aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str()
-                   << "was the duration of dumpsys " << service_name;
-
-              using std::chrono::system_clock;
-              const auto finish = system_clock::to_time_t(system_clock::now());
-              std::tm finish_tm;
-              localtime_r(&finish, &finish_tm);
-              aout << ", ending at: " << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S")
-                   << endl;
-            }
-        } else {
-            aerr << "Can't find service: " << service_name << endl;
+            bool dumpComplete = (status == OK);
+            stopDumpThread(dumpComplete);
         }
     }
 
     return 0;
 }
+
+Vector<String16> Dumpsys::listServices(int priorityFilterFlags, bool filterByProto) const {
+    Vector<String16> services = sm_->listServices(priorityFilterFlags);
+    services.sort(sort_func);
+    if (filterByProto) {
+        Vector<String16> protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
+        protoServices.sort(sort_func);
+        Vector<String16> intersection;
+        std::set_intersection(services.begin(), services.end(), protoServices.begin(),
+                              protoServices.end(), std::back_inserter(intersection));
+        services = std::move(intersection);
+    }
+    return services;
+}
+
+void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const {
+    if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) ||
+        (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL)) {
+        args.add(String16("-a"));
+    }
+    if (asProto) {
+        args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
+    }
+    if (priorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
+        String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
+        args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
+        args.insertAt(priorityType, 1);
+    }
+}
+
+status_t Dumpsys::startDumpThread(const String16& serviceName, const Vector<String16>& args) {
+    sp<IBinder> service = sm_->checkService(serviceName);
+    if (service == nullptr) {
+        aerr << "Can't find service: " << serviceName << endl;
+        return NAME_NOT_FOUND;
+    }
+
+    int sfd[2];
+    if (pipe(sfd) != 0) {
+        aerr << "Failed to create pipe to dump service info for " << serviceName << ": "
+             << strerror(errno) << endl;
+        return -errno;
+    }
+
+    redirectFd_ = unique_fd(sfd[0]);
+    unique_fd remote_end(sfd[1]);
+    sfd[0] = sfd[1] = -1;
+
+    // dump blocks until completion, so spawn a thread..
+    activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable {
+        int err = service->dump(remote_end.get(), args);
+
+        // It'd be nice to be able to close the remote end of the socketpair before the dump
+        // call returns, to terminate our reads if the other end closes their copy of the
+        // file descriptor, but then hangs for some reason. There doesn't seem to be a good
+        // way to do this, though.
+        remote_end.reset();
+
+        if (err != 0) {
+            aerr << "Error dumping service info: (" << strerror(err) << ") "
+                 << serviceName << endl;
+        }
+    });
+    return OK;
+}
+
+void Dumpsys::stopDumpThread(bool dumpComplete) {
+    if (dumpComplete) {
+        activeThread_.join();
+    } else {
+        activeThread_.detach();
+    }
+    /* close read end of the dump output redirection pipe */
+    redirectFd_.reset();
+}
+
+void Dumpsys::writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const {
+    std::string msg(
+        "----------------------------------------"
+        "---------------------------------------\n");
+    if (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL ||
+        priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+        StringAppendF(&msg, "DUMP OF SERVICE %s:\n", String8(serviceName).c_str());
+    } else {
+        String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
+        StringAppendF(&msg, "DUMP OF SERVICE %s %s:\n", String8(priorityType).c_str(),
+                      String8(serviceName).c_str());
+    }
+    WriteStringToFd(msg, fd);
+}
+
+status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
+                            bool asProto, std::chrono::duration<double>& elapsedDuration,
+                            size_t& bytesWritten) const {
+    status_t status = OK;
+    size_t totalBytes = 0;
+    auto start = std::chrono::steady_clock::now();
+    auto end = start + timeout;
+
+    int serviceDumpFd = redirectFd_.get();
+    if (serviceDumpFd == -1) {
+        return INVALID_OPERATION;
+    }
+
+    struct pollfd pfd = {.fd = serviceDumpFd, .events = POLLIN};
+
+    while (true) {
+        // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
+        auto time_left_ms = [end]() {
+            auto now = std::chrono::steady_clock::now();
+            auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
+            return std::max(diff.count(), 0ll);
+        };
+
+        int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
+        if (rc < 0) {
+            aerr << "Error in poll while dumping service " << serviceName << " : "
+                 << strerror(errno) << endl;
+            status = -errno;
+            break;
+        } else if (rc == 0) {
+            status = TIMED_OUT;
+            break;
+        }
+
+        char buf[4096];
+        rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf)));
+        if (rc < 0) {
+            aerr << "Failed to read while dumping service " << serviceName << ": "
+                 << strerror(errno) << endl;
+            status = -errno;
+            break;
+        } else if (rc == 0) {
+            // EOF.
+            break;
+        }
+
+        if (!WriteFully(fd, buf, rc)) {
+            aerr << "Failed to write while dumping service " << serviceName << ": "
+                 << strerror(errno) << endl;
+            status = -errno;
+            break;
+        }
+        totalBytes += rc;
+    }
+
+    if ((status == TIMED_OUT) && (!asProto)) {
+        std::string msg = StringPrintf("\n*** SERVICE '%s' DUMP TIMEOUT (%llums) EXPIRED ***\n\n",
+                                       String8(serviceName).string(), timeout.count());
+        WriteStringToFd(msg, fd);
+    }
+
+    elapsedDuration = std::chrono::steady_clock::now() - start;
+    bytesWritten = totalBytes;
+    return status;
+}
+
+void Dumpsys::writeDumpFooter(int fd, const String16& serviceName,
+                              const std::chrono::duration<double>& elapsedDuration) const {
+    using std::chrono::system_clock;
+    const auto finish = system_clock::to_time_t(system_clock::now());
+    std::tm finish_tm;
+    localtime_r(&finish, &finish_tm);
+    std::stringstream oss;
+    oss << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S");
+    std::string msg =
+        StringPrintf("--------- %.3fs was the duration of dumpsys %s, ending at: %s\n",
+                     elapsedDuration.count(), String8(serviceName).string(), oss.str().c_str());
+    WriteStringToFd(msg, fd);
+}