Merge "Admit that there's more `adb install` help available."
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 054cbac..bd5508c 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -501,6 +501,197 @@
     return EXIT_FAILURE;
 }
 
+int install_multi_package(int argc, const char** argv) {
+    // Find all APK arguments starting at end.
+    // All other arguments passed through verbatim.
+    int first_apk = -1;
+    for (int i = argc - 1; i >= 0; i--) {
+        const char* file = argv[i];
+        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+            first_apk = i;
+        } else {
+            break;
+        }
+    }
+
+    if (first_apk == -1) error_exit("need APK file on command line");
+
+    if (use_legacy_install()) {
+        fprintf(stderr, "adb: multi-package install is not supported on this device\n");
+        return EXIT_FAILURE;
+    }
+    std::string install_cmd = "exec:cmd package";
+
+    std::string multi_package_cmd =
+            android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
+
+    // Create multi-package install session
+    std::string error;
+    int fd = adb_connect(multi_package_cmd, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+    char buf[BUFSIZ];
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    int parent_session_id = -1;
+    if (!strncmp("Success", buf, 7)) {
+        char* start = strrchr(buf, '[');
+        char* end = strrchr(buf, ']');
+        if (start && end) {
+            *end = '\0';
+            parent_session_id = strtol(start + 1, nullptr, 10);
+        }
+    }
+    if (parent_session_id < 0) {
+        fprintf(stderr, "adb: failed to create multi-package session\n");
+        fputs(buf, stderr);
+        return EXIT_FAILURE;
+    }
+
+    fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
+
+    std::vector<int> session_ids;
+
+    // Valid session, now create the individual sessions and stream the APKs
+    int success = EXIT_FAILURE;
+    std::string individual_cmd =
+            android::base::StringPrintf("%s install-create", install_cmd.c_str());
+    std::string all_session_ids = "";
+    for (int i = 1; i < first_apk; i++) {
+        individual_cmd += " " + escape_arg(argv[i]);
+    }
+    std::string cmd = "";
+    for (int i = first_apk; i < argc; i++) {
+        // Create individual install session
+        fd = adb_connect(individual_cmd, &error);
+        if (fd < 0) {
+            fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+            goto finalize_multi_package_session;
+        }
+        char buf[BUFSIZ];
+        read_status_line(fd, buf, sizeof(buf));
+        adb_close(fd);
+
+        int session_id = -1;
+        if (!strncmp("Success", buf, 7)) {
+            char* start = strrchr(buf, '[');
+            char* end = strrchr(buf, ']');
+            if (start && end) {
+                *end = '\0';
+                session_id = strtol(start + 1, nullptr, 10);
+            }
+        }
+        if (session_id < 0) {
+            fprintf(stderr, "adb: failed to create multi-package session\n");
+            fputs(buf, stderr);
+            goto finalize_multi_package_session;
+        }
+
+        fprintf(stdout, "Created child session ID %d.\n", session_id);
+        session_ids.push_back(session_id);
+
+        const char* file = argv[i];
+        struct stat sb;
+        if (stat(file, &sb) == -1) {
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
+            goto finalize_multi_package_session;
+        }
+
+        std::string cmd =
+                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
+                                            install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
+                                            session_id, i, android::base::Basename(file).c_str());
+
+        int localFd = adb_open(file, O_RDONLY);
+        if (localFd < 0) {
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
+            goto finalize_multi_package_session;
+        }
+
+        std::string error;
+        int remoteFd = adb_connect(cmd, &error);
+        if (remoteFd < 0) {
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+            adb_close(localFd);
+            goto finalize_multi_package_session;
+        }
+
+        copy_to_file(localFd, remoteFd);
+        read_status_line(remoteFd, buf, sizeof(buf));
+
+        adb_close(localFd);
+        adb_close(remoteFd);
+
+        if (strncmp("Success", buf, 7)) {
+            fprintf(stderr, "adb: failed to write %s\n", file);
+            fputs(buf, stderr);
+            goto finalize_multi_package_session;
+        }
+
+        all_session_ids += android::base::StringPrintf(" %d", session_id);
+    }
+
+    cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
+                                      parent_session_id, all_session_ids.c_str());
+    fd = adb_connect(cmd, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+        goto finalize_multi_package_session;
+    }
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    if (strncmp("Success", buf, 7)) {
+        fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
+        fputs(buf, stderr);
+        goto finalize_multi_package_session;
+    }
+
+    // no failures means we can proceed with the assumption of success
+    success = 0;
+
+finalize_multi_package_session:
+    // Commit session if we streamed everything okay; otherwise abandon
+    std::string service =
+            android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
+                                        success == 0 ? "commit" : "abandon", parent_session_id);
+    fd = adb_connect(service, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        if (success == 0) {
+            return 0;
+        }
+    } else {
+        fprintf(stderr, "adb: failed to finalize session\n");
+        fputs(buf, stderr);
+    }
+
+    // try to abandon all remaining sessions
+    for (std::size_t i = 0; i < session_ids.size(); i++) {
+        service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
+                                              session_ids[i]);
+        fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
+        fd = adb_connect(service, &error);
+        if (fd < 0) {
+            fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+            continue;
+        }
+        read_status_line(fd, buf, sizeof(buf));
+        adb_close(fd);
+    }
+    return EXIT_FAILURE;
+}
+
 int delete_device_file(const std::string& filename) {
     std::string cmd = "rm -f " + escape_arg(filename);
     return send_shell_command(cmd);
diff --git a/adb/client/adb_install.h b/adb/client/adb_install.h
index 5b6c4cb..9946604 100644
--- a/adb/client/adb_install.h
+++ b/adb/client/adb_install.h
@@ -20,6 +20,7 @@
 
 int install_app(int argc, const char** argv);
 int install_multiple_app(int argc, const char** argv);
+int install_multi_package(int argc, const char** argv);
 int uninstall_app(int argc, const char** argv);
 
 int delete_device_file(const std::string& filename);
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 98cd40f..9bc42e1 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -144,8 +144,11 @@
         "\n"
         "app installation (see also `adb shell cmd package help`):\n"
         " install [-lrtsdg] [--instant] PACKAGE\n"
+        "     push a single package to the device and install it\n"
         " install-multiple [-lrtsdpg] [--instant] PACKAGE...\n"
-        "     push package(s) to the device and install them\n"
+        "     push multiple APKs to the device for a single package and install them\n"
+        " install-multi-package [-lrtsdpg] [--instant] PACKAGE...\n"
+        "     push one or more packages to the device and install them atomically\n"
         "     -r: replace existing application\n"
         "     -t: allow test packages\n"
         "     -d: allow version code downgrade (debuggable packages only)\n"
@@ -1734,6 +1737,9 @@
     } else if (!strcmp(argv[0], "install-multiple")) {
         if (argc < 2) error_exit("install-multiple requires an argument");
         return install_multiple_app(argc, argv);
+    } else if (!strcmp(argv[0], "install-multi-package")) {
+        if (argc < 3) error_exit("install-multi-package requires an argument");
+        return install_multi_package(argc, argv);
     } else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) error_exit("uninstall requires an argument");
         return uninstall_app(argc, argv);
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 89d4fc0..e267f58 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -221,7 +221,6 @@
         "liblog",
         "liblzma",
         "libunwindstack",
-        "libdexfile",
         "libdexfile_support",
     ],
 
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 21ca47b..0149a42 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -21,7 +21,6 @@
 #include <unordered_map>
 
 #include <android-base/file.h>
-#include <dex/dex_file.h>
 #include <gtest/gtest.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
@@ -40,17 +39,15 @@
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
 
-  ASSERT_EQ(sizeof(art::DexFile::Header) - 1,
-            static_cast<size_t>(
-                TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header) - 1))));
+  ASSERT_EQ(size_t{10}, static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, 10))));
 
   // Header too small.
   EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
 
   // Header correct, file too small.
   ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
-  ASSERT_EQ(sizeof(art::DexFile::Header), static_cast<size_t>(TEMP_FAILURE_RETRY(write(
-                                              tf.fd, kDexData, sizeof(art::DexFile::Header)))));
+  ASSERT_EQ(sizeof(kDexData) - 1,
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 1))));
   EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
 }
 
@@ -78,7 +75,7 @@
 TEST(DexFileTest, from_memory_fail_too_small_for_header) {
   MemoryFake memory;
 
-  memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1);
+  memory.SetMemory(0x1000, kDexData, 10);
 
   EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
 }
@@ -187,15 +184,6 @@
   ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset));
   EXPECT_EQ("Main.main", method);
   EXPECT_EQ(0U, method_offset);
-
-  // Make sure that any data that is cached is still retrievable.
-  ASSERT_TRUE(dex_file->GetMethodInformation(0x104, &method, &method_offset));
-  EXPECT_EQ("Main.<init>", method);
-  EXPECT_EQ(4U, method_offset);
-
-  ASSERT_TRUE(dex_file->GetMethodInformation(0x119, &method, &method_offset));
-  EXPECT_EQ("Main.main", method);
-  EXPECT_EQ(1U, method_offset);
 }
 
 TEST(DexFileTest, get_method_empty) {
@@ -210,12 +198,6 @@
   EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset));
 
   EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
-
-  // Make sure that once the whole dex file has been cached, no problems occur.
-  EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
-
-  // Choose a value that is in the cached map, but not in a valid method.
-  EXPECT_FALSE(dex_file->GetMethodInformation(0x110, &method, &method_offset));
 }
 
 }  // namespace unwindstack