Add Fastboot support for flashing secondary images

Fastboot update and flashall will now flash secondary
images if provided, and if the --slot flag is not set
to 'all'. Also added flash-primary to preserve the
previous behavior, and flash-secondary to just flash
the secondary images.

Fixes from: I5bd2de1c1e0e6224a195b566f7dcbe383555a80a

Bug: 29278988
Change-Id: Ia870e4be55617c0eefa4e1381287f8cd14affe7e
(cherry-picked from commit 6c98509c34d152007cf953c2cacb5750745dd464)
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3c69964..8714471 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -97,19 +97,40 @@
 };
 
 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 std::string find_item(const char* item, const char* product) {
+static std::string find_item_given_name(const char* img_name, const char* product) {
+    if(product) {
+        std::string path = get_my_path();
+        path.erase(path.find_last_of('/'));
+        return android::base::StringPrintf("%s/../../../target/product/%s/%s",
+                                           path.c_str(), product, img_name);
+    }
+
+    char *dir = getenv("ANDROID_PRODUCT_OUT");
+    if (dir == nullptr || 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")) {
@@ -127,21 +148,10 @@
     } else {
         fprintf(stderr,"unknown partition '%s'\n", item);
         return "";
+
     }
 
-    if (product) {
-        std::string path = get_my_path();
-        path.erase(path.find_last_of('/'));
-        return android::base::StringPrintf("%s/../../../target/product/%s/%s",
-                                           path.c_str(), product, fn);
-    }
-
-    char* dir = getenv("ANDROID_PRODUCT_OUT");
-    if (dir == nullptr || dir[0] == '\0') {
-        die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
-    }
-
-    return android::base::StringPrintf("%s/%s", dir, fn);
+    return find_item_given_name(fn, product);
 }
 
 static int64_t get_file_size(int fd) {
@@ -312,6 +322,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"
@@ -801,8 +816,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()) {
@@ -815,6 +834,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) {
@@ -937,9 +962,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)) {
@@ -949,7 +976,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));
@@ -970,7 +997,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 < arraysize(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) {
@@ -995,50 +1046,71 @@
              * 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(const char* filename) {
-    if (android::base::EndsWith(filename, ".img") == false) {
-        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;
 
-    std::string sig_path = filename;
-    sig_path.erase(sig_path.size() - 4);
-    sig_path += ".sig";
+    std::string fs_sig = fn.substr(0, extension_loc) + ".sig";
 
     int64_t sz;
-    void* data = load_file(sig_path, &sz);
+    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));
 
-    std::string fname = find_item("info", product);
-    if (fname.empty()) die("cannot find android-info.txt");
+        fname = find_item("info", product);
+        if (fname.empty()) 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 < arraysize(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.c_str(), &buf)) {
             if (images[i].is_optional) continue;
-            die("could not load '%s': %s", images[i].img_name, strerror(errno));
+            die("could not load '%s': %s\n", images[i].img_name, strerror(errno));
         }
 
         auto flashall = [&](const std::string &partition) {
@@ -1048,10 +1120,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)
@@ -1364,10 +1436,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 == "") {
@@ -1527,14 +1597,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;