Revert "Revert "Add Fastboot support for flashing secondary images""

This reverts commit 520573f2bb8a27e72c0727f250cee574e473b8ea.

Bug: 29278988
Change-Id: I5bd2de1c1e0e6224a195b566f7dcbe383555a80a
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7e3d212..a821ddc 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -49,6 +49,7 @@
 
 #include <android-base/parseint.h>
 #include <android-base/parsenetaddress.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
@@ -99,22 +100,41 @@
 };
 
 static struct {
-    char img_name[13];
-    char sig_name[13];
+    char img_name[17];
+    char sig_name[17];
     char part_name[9];
     bool is_optional;
+    bool is_secondary;
 } images[] = {
-    {"boot.img", "boot.sig", "boot", false},
-    {"recovery.img", "recovery.sig", "recovery", true},
-    {"system.img", "system.sig", "system", false},
-    {"vendor.img", "vendor.sig", "vendor", true},
+    {"boot.img", "boot.sig", "boot", false, false},
+    {"boot_other.img", "boot.sig", "boot", true, true},
+    {"recovery.img", "recovery.sig", "recovery", true, false},
+    {"system.img", "system.sig", "system", false, false},
+    {"system_other.img", "system.sig", "system", true, true},
+    {"vendor.img", "vendor.sig", "vendor", true, false},
+    {"vendor_other.img", "vendor.sig", "vendor", true, true},
 };
 
-static char* find_item(const char* item, const char* product) {
+static std::string find_item_given_name(const char* img_name, const char* product) {
     char *dir;
-    const char *fn;
     char path[PATH_MAX + 128];
 
+    if(product) {
+        get_my_path(path);
+        return android::base::StringPrintf("../../../target/product/%s/%s", product, img_name);
+    }
+
+    dir = getenv("ANDROID_PRODUCT_OUT");
+    if((dir == 0) || (dir[0] == 0)) {
+        die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
+    }
+
+    return android::base::StringPrintf("%s/%s", dir, img_name);
+}
+
+std::string find_item(const char* item, const char* product) {
+    const char *fn;
+
     if(!strcmp(item,"boot")) {
         fn = "boot.img";
     } else if(!strcmp(item,"recovery")) {
@@ -131,24 +151,10 @@
         fn = "android-info.txt";
     } else {
         fprintf(stderr,"unknown partition '%s'\n", item);
-        return 0;
+        return "";
     }
 
-    if(product) {
-        get_my_path(path);
-        sprintf(path + strlen(path),
-                "../../../target/product/%s/%s", product, fn);
-        return strdup(path);
-    }
-
-    dir = getenv("ANDROID_PRODUCT_OUT");
-    if((dir == 0) || (dir[0] == 0)) {
-        die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
-        return 0;
-    }
-
-    sprintf(path, "%s/%s", dir, fn);
-    return strdup(path);
+    return find_item_given_name(fn, product);
 }
 
 static int64_t get_file_size(int fd) {
@@ -319,6 +325,11 @@
             "                                           if found -- recovery. If the device\n"
             "                                           supports slots, the slot that has\n"
             "                                           been flashed to is set as active.\n"
+            "                                           Secondary images may be flashed to\n"
+            "                                           an inactive slot.\n"
+            "  flash-primary                            Same as flashall, but do not flash\n"
+            "                                           secondary images.\n"
+            "  flash-secondary                          Only flashes the secondary images.\n"
             "  flash <partition> [ <filename> ]         Write a file to a flash partition.\n"
             "  flashing lock                            Locks the device. Prevents flashing.\n"
             "  flashing unlock                          Unlocks the device. Allows flashing\n"
@@ -867,8 +878,12 @@
     return suffixes;
 }
 
+static bool supports_AB(Transport* transport) {
+  return !get_suffixes(transport).empty();
+}
+
 // Given a current slot, this returns what the 'other' slot is.
-static std::string get_other_slot(Transport* transport, std::string& current_slot) {
+static std::string get_other_slot(Transport* transport, const std::string& current_slot) {
     std::vector<std::string> suffixes = get_suffixes(transport);
 
     if (!suffixes.empty()) {
@@ -881,6 +896,12 @@
     return "";
 }
 
+static std::string get_other_slot(Transport* transport) {
+    std::string current_slot;
+    fb_getvar(transport, "current-slot", &current_slot);
+    return get_other_slot(transport, current_slot);
+}
+
 static std::string verify_slot(Transport* transport, const char *slot, bool allow_all) {
     if (strcmp(slot, "all") == 0) {
         if (allow_all) {
@@ -1003,9 +1024,11 @@
 
 // Sets slot_override as the active slot. If slot_override is blank,
 // set current slot as active instead. This clears slot-unbootable.
-static void set_active(Transport* transport, const char* slot_override) {
-    if (slot_override && slot_override[0]) {
-        fb_set_active(slot_override);
+static void set_active(Transport* transport, const std::string& slot_override) {
+    if (!supports_AB(transport)) {
+      return;
+    } else if (slot_override != "") {
+        fb_set_active(slot_override.c_str());
     } else {
         std::string current_slot;
         if (fb_getvar(transport, "current-slot", &current_slot)) {
@@ -1015,7 +1038,7 @@
     }
 }
 
-static void do_update(Transport* transport, const char* filename, const char* slot_override, bool erase_first) {
+static void do_update(Transport* transport, const char* filename, const std::string& slot_override, bool erase_first) {
     queue_info_dump();
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -1036,7 +1059,31 @@
 
     setup_requirements(reinterpret_cast<char*>(data), sz);
 
+    std::string secondary;
+    bool update_secondary = slot_override != "all";
+    if (update_secondary) {
+        if (slot_override != "") {
+            secondary = get_other_slot(transport, slot_override);
+        } else {
+            secondary = get_other_slot(transport);
+        }
+        if (secondary == "") {
+            if (supports_AB(transport)) {
+                fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
+            }
+            update_secondary = false;
+        }
+    }
     for (size_t i = 0; i < ARRAY_SIZE(images); ++i) {
+        const char* slot = slot_override.c_str();
+        if (images[i].is_secondary) {
+            if (update_secondary) {
+                slot = secondary.c_str();
+            } else {
+                continue;
+            }
+        }
+
         int fd = unzip_to_file(zip, images[i].img_name);
         if (fd == -1) {
             if (images[i].is_optional) {
@@ -1060,49 +1107,69 @@
              * program exits.
              */
         };
-        do_for_partitions(transport, images[i].part_name, slot_override, update, false);
+        do_for_partitions(transport, images[i].part_name, slot, update, false);
     }
 
     CloseArchive(zip);
     set_active(transport, slot_override);
 }
 
-static void do_send_signature(char* fn) {
-    char* xtn = strrchr(fn, '.');
-    if (!xtn) return;
+static void do_send_signature(const std::string& fn) {
+    std::size_t extension_loc = fn.find(".img");
+    if (extension_loc == std::string::npos) return;
 
-    if (strcmp(xtn, ".img")) return;
-
-    strcpy(xtn, ".sig");
+    std::string fs_sig = fn.substr(0, extension_loc) + ".sig";
 
     int64_t sz;
-    void* data = load_file(fn, &sz);
-    strcpy(xtn, ".img");
+    void* data = load_file(fs_sig.c_str(), &sz);
     if (data == nullptr) return;
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
 }
 
-static void do_flashall(Transport* transport, const char* slot_override, int erase_first) {
-    queue_info_dump();
+static void do_flashall(Transport* transport, const std::string& slot_override, int erase_first, bool flash_primary, bool flash_secondary) {
+    std::string fname;
+    if (flash_primary) {
+        queue_info_dump();
 
-    fb_queue_query_save("product", cur_product, sizeof(cur_product));
+        fb_queue_query_save("product", cur_product, sizeof(cur_product));
 
-    char* fname = find_item("info", product);
-    if (fname == nullptr) die("cannot find android-info.txt");
+        fname = find_item("info", product);
+        if (fname == "") die("cannot find android-info.txt");
 
-    int64_t sz;
-    void* data = load_file(fname, &sz);
-    if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
+        int64_t sz;
+        void* data = load_file(fname.c_str(), &sz);
+        if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
 
-    setup_requirements(reinterpret_cast<char*>(data), sz);
+        setup_requirements(reinterpret_cast<char*>(data), sz);
+    }
+    std::string secondary;
+    if (flash_secondary) {
+        if (slot_override != "") {
+            secondary = get_other_slot(transport, slot_override);
+        } else {
+            secondary = get_other_slot(transport);
+        }
+        if (secondary == "") {
+            if (supports_AB(transport)) {
+                fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
+            }
+            flash_secondary = false;
+        }
+    }
 
     for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
-        fname = find_item(images[i].part_name, product);
+        const char* slot = NULL;
+        if (images[i].is_secondary) {
+            if (flash_secondary) slot = secondary.c_str();
+        } else {
+            if (flash_primary) slot = slot_override.c_str();
+        }
+        if (!slot) continue;
+        fname = find_item_given_name(images[i].img_name, product);
         fastboot_buffer buf;
-        if (load_buf(transport, fname, &buf)) {
-            if (images[i].is_optional)
-                continue;
+        if (load_buf(transport, fname.c_str(), &buf)) {
+            if (images[i].is_optional) continue;
             die("could not load %s\n", images[i].img_name);
         }
 
@@ -1113,10 +1180,10 @@
             }
             flash_buf(partition.c_str(), &buf);
         };
-        do_for_partitions(transport, images[i].part_name, slot_override, flashall, false);
+        do_for_partitions(transport, images[i].part_name, slot, flashall, false);
     }
 
-    set_active(transport, slot_override);
+    if (flash_primary) set_active(transport, slot_override);
 }
 
 #define skip(n) do { argc -= (n); argv += (n); } while (0)
@@ -1443,10 +1510,8 @@
         return 1;
     }
 
-    if (slot_override != "")
-        slot_override = verify_slot(transport, slot_override.c_str());
-    if (next_active != "")
-        next_active = verify_slot(transport, next_active.c_str(), false);
+    if (slot_override != "") slot_override = verify_slot(transport, slot_override.c_str());
+    if (next_active != "") next_active = verify_slot(transport, next_active.c_str(), false);
 
     if (wants_set_active) {
         if (next_active == "") {
@@ -1567,7 +1632,7 @@
             fb_queue_command("boot", "booting");
         } else if(!strcmp(*argv, "flash")) {
             char *pname = argv[1];
-            char *fname = 0;
+            std::string fname;
             require(2);
             if (argc > 2) {
                 fname = argv[2];
@@ -1576,13 +1641,13 @@
                 fname = find_item(pname, product);
                 skip(2);
             }
-            if (fname == 0) die("cannot determine image filename for '%s'", pname);
+            if (fname == "") die("cannot determine image filename for '%s'", pname);
 
             auto flash = [&](const std::string &partition) {
                 if (erase_first && needs_erase(transport, partition.c_str())) {
                     fb_queue_erase(partition.c_str());
                 }
-                do_flash(transport, partition.c_str(), fname);
+                do_flash(transport, partition.c_str(), fname.c_str());
             };
             do_for_partitions(transport, pname, slot_override.c_str(), flash, true);
         } else if(!strcmp(*argv, "flash:raw")) {
@@ -1607,14 +1672,27 @@
             do_for_partitions(transport, argv[1], slot_override.c_str(), flashraw, true);
         } else if(!strcmp(*argv, "flashall")) {
             skip(1);
-            do_flashall(transport, slot_override.c_str(), erase_first);
+            if (slot_override == "all") {
+                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.");
+                 do_flashall(transport, slot_override, erase_first, true, false);
+            } else {
+                do_flashall(transport, slot_override, erase_first, true, true);
+            }
+            wants_reboot = true;
+        } else if(!strcmp(*argv, "flash-primary")) {
+            skip(1);
+            do_flashall(transport, slot_override, erase_first, true, false);
+            wants_reboot = true;
+        } else if(!strcmp(*argv, "flash-secondary")) {
+            skip(1);
+            do_flashall(transport, slot_override, erase_first, false, true);
             wants_reboot = true;
         } else if(!strcmp(*argv, "update")) {
             if (argc > 1) {
-                do_update(transport, argv[1], slot_override.c_str(), erase_first);
+                do_update(transport, argv[1], slot_override, erase_first);
                 skip(2);
             } else {
-                do_update(transport, "update.zip", slot_override.c_str(), erase_first);
+                do_update(transport, "update.zip", slot_override, erase_first);
                 skip(1);
             }
             wants_reboot = 1;