[adb data server] wait for installation results before terminates

Currently the server often quits before installation finishes. As a
result, there is no difference in the commandline output between a
successful installation and a failed one.

Let adb client wait till installation fails or succeeds by parsing the
output from the inc-server process.

Test: $ adb install --incremental ~/Downloads/base.apk
Test: Performing Incremental Install
Test: Serving...
Test: All files should be loaded. Notifying the device.
Test: Failure [INSTALL_PARSE_FAILED_NOT_APK: Failed to parse /data/app/vmdl749343150.tmp/base.apk: Failed to load asset path /data/app/vmdl749343150.tmp/base.apk]
Test: Install command complete (ms: 91 total, 0 apk prep, 91 install)

BUG: b/150865433
Change-Id: Ie33505f9cc08fc6d60ad4a5d709526e7aa9a0ad1
Merged-In: Ie33505f9cc08fc6d60ad4a5d709526e7aa9a0ad1
diff --git a/client/incremental.cpp b/client/incremental.cpp
index 3ceb374..fd608cc 100644
--- a/client/incremental.cpp
+++ b/client/incremental.cpp
@@ -193,20 +193,72 @@
     auto fd_param = std::to_string(osh);
 #endif
 
+    // pipe for child process to write output
+    int print_fds[2];
+    if (adb_socketpair(print_fds) != 0) {
+        fprintf(stderr, "Failed to create socket pair for child to print to parent\n");
+        return {};
+    }
+    auto [pipe_read_fd, pipe_write_fd] = print_fds;
+    auto pipe_write_fd_param = std::to_string(pipe_write_fd);
+    close_on_exec(pipe_read_fd);
+
     std::vector<std::string> args(std::move(files));
-    args.insert(args.begin(), {"inc-server", fd_param});
-    auto child = adb_launch_process(adb_path, std::move(args), {connection_fd.get()});
+    args.insert(args.begin(), {"inc-server", fd_param, pipe_write_fd_param});
+    auto child =
+            adb_launch_process(adb_path, std::move(args), {connection_fd.get(), pipe_write_fd});
     if (!child) {
         fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
         return {};
     }
 
+    adb_close(pipe_write_fd);
+
     auto killOnExit = [](Process* p) { p->kill(); };
     std::unique_ptr<Process, decltype(killOnExit)> serverKiller(&child, killOnExit);
-    // TODO: Terminate server process if installation fails.
-    serverKiller.release();
 
+    Result result = wait_for_installation(pipe_read_fd);
+    adb_close(pipe_read_fd);
+
+    if (result == Result::Success) {
+        // adb client exits now but inc-server can continue
+        serverKiller.release();
+    }
     return child;
 }
 
+Result wait_for_installation(int read_fd) {
+    static constexpr int maxMessageSize = 256;
+    std::vector<char> child_stdout(CHUNK_SIZE);
+    int bytes_read;
+    int buf_size = 0;
+    // TODO(b/150865433): optimize child's output parsing
+    while ((bytes_read = adb_read(read_fd, child_stdout.data() + buf_size,
+                                  child_stdout.size() - buf_size)) > 0) {
+        // print to parent's stdout
+        fprintf(stdout, "%.*s", bytes_read, child_stdout.data() + buf_size);
+
+        buf_size += bytes_read;
+        const std::string_view stdout_str(child_stdout.data(), buf_size);
+        // wait till installation either succeeds or fails
+        if (stdout_str.find("Success") != std::string::npos) {
+            return Result::Success;
+        }
+        // on failure, wait for full message
+        static constexpr auto failure_msg_head = "Failure ["sv;
+        if (const auto begin_itr = stdout_str.find(failure_msg_head);
+            begin_itr != std::string::npos) {
+            if (buf_size >= maxMessageSize) {
+                return Result::Failure;
+            }
+            const auto end_itr = stdout_str.rfind("]");
+            if (end_itr != std::string::npos && end_itr >= begin_itr + failure_msg_head.size()) {
+                return Result::Failure;
+            }
+        }
+        child_stdout.resize(buf_size + CHUNK_SIZE);
+    }
+    return Result::None;
+}
+
 }  // namespace incremental