add reboot-to-bootloader and power down options to recovery menu

Useful when debugging or developing for recovery.

Change-Id: Ic3ab42d5e848ad3488f1c575339b55e45c8a024b
diff --git a/default_device.cpp b/default_device.cpp
index 1f18131..a25f05f 100644
--- a/default_device.cpp
+++ b/default_device.cpp
@@ -29,6 +29,8 @@
                                "apply update from ADB",
                                "wipe data/factory reset",
                                "wipe cache partition",
+                               "reboot to bootloader",
+                               "power down",
                                NULL };
 
 class DefaultDevice : public Device {
@@ -65,6 +67,8 @@
           case 1: return APPLY_ADB_SIDELOAD;
           case 2: return WIPE_DATA;
           case 3: return WIPE_CACHE;
+          case 4: return REBOOT_BOOTLOADER;
+          case 5: return SHUTDOWN;
           default: return NO_ACTION;
         }
     }
diff --git a/device.h b/device.h
index 583de75..df71377 100644
--- a/device.h
+++ b/device.h
@@ -66,7 +66,8 @@
     virtual int HandleMenuKey(int key, int visible) = 0;
 
     enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, APPLY_CACHE,
-                         APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE };
+                         APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE,
+                         REBOOT_BOOTLOADER, SHUTDOWN };
 
     // Perform a recovery action selected from the menu.
     // 'menu_position' will be the item number of the selected menu
diff --git a/recovery.cpp b/recovery.cpp
index 8d37315..09af5f8 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -771,7 +771,10 @@
     ui->Print("Data wipe complete.\n");
 }
 
-static void
+// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER.  Returning NO_ACTION
+// means to take the default, which is to reboot or shutdown depending
+// on if the --shutdown_after flag was passed to recovery.
+static Device::BuiltinAction
 prompt_and_wait(Device* device, int status) {
     const char* const* headers = prepend_title(device->GetMenuHeaders());
 
@@ -795,23 +798,28 @@
         // device-specific code may take some action here.  It may
         // return one of the core actions handled in the switch
         // statement below.
-        chosen_item = device->InvokeMenuItem(chosen_item);
+        Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);
 
         int wipe_cache;
-        switch (chosen_item) {
+        switch (chosen_action) {
+            case Device::NO_ACTION:
+                break;
+
             case Device::REBOOT:
-                return;
+            case Device::SHUTDOWN:
+            case Device::REBOOT_BOOTLOADER:
+                return chosen_action;
 
             case Device::WIPE_DATA:
                 wipe_data(ui->IsTextVisible(), device);
-                if (!ui->IsTextVisible()) return;
+                if (!ui->IsTextVisible()) return Device::NO_ACTION;
                 break;
 
             case Device::WIPE_CACHE:
                 ui->Print("\n-- Wiping cache...\n");
                 erase_volume("/cache");
                 ui->Print("Cache wipe complete.\n");
-                if (!ui->IsTextVisible()) return;
+                if (!ui->IsTextVisible()) return Device::NO_ACTION;
                 break;
 
             case Device::APPLY_EXT:
@@ -829,7 +837,7 @@
                         ui->SetBackground(RecoveryUI::ERROR);
                         ui->Print("Installation aborted.\n");
                     } else if (!ui->IsTextVisible()) {
-                        return;  // reboot if logs aren't visible
+                        return Device::NO_ACTION;  // reboot if logs aren't visible
                     } else {
                         ui->Print("\nInstall from sdcard complete.\n");
                     }
@@ -852,7 +860,7 @@
                         ui->SetBackground(RecoveryUI::ERROR);
                         ui->Print("Installation aborted.\n");
                     } else if (!ui->IsTextVisible()) {
-                        return;  // reboot if logs aren't visible
+                        return Device::NO_ACTION;  // reboot if logs aren't visible
                     } else {
                         ui->Print("\nInstall from cache complete.\n");
                     }
@@ -867,7 +875,7 @@
                         ui->Print("Installation aborted.\n");
                         copy_logs();
                     } else if (!ui->IsTextVisible()) {
-                        return;  // reboot if logs aren't visible
+                        return Device::NO_ACTION;  // reboot if logs aren't visible
                     } else {
                         ui->Print("\nInstall from ADB complete.\n");
                     }
@@ -1074,18 +1082,31 @@
         copy_logs();
         ui->SetBackground(RecoveryUI::ERROR);
     }
+    Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
     if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
-        prompt_and_wait(device, status);
+        Device::BuiltinAction temp = prompt_and_wait(device, status);
+        if (temp != Device::NO_ACTION) after = temp;
     }
 
-    // Otherwise, get ready to boot the main system...
+    // Save logs and clean up before rebooting or shutting down.
     finish_recovery(send_intent);
-    if (shutdown_after) {
-        ui->Print("Shutting down...\n");
-        property_set(ANDROID_RB_PROPERTY, "shutdown,");
-    } else {
-        ui->Print("Rebooting...\n");
-        property_set(ANDROID_RB_PROPERTY, "reboot,");
+
+    switch (after) {
+        case Device::SHUTDOWN:
+            ui->Print("Shutting down...\n");
+            property_set(ANDROID_RB_PROPERTY, "shutdown,");
+            break;
+
+        case Device::REBOOT_BOOTLOADER:
+            ui->Print("Rebooting to bootloader...\n");
+            property_set(ANDROID_RB_PROPERTY, "reboot,bootloader");
+            break;
+
+        default:
+            ui->Print("Rebooting...\n");
+            property_set(ANDROID_RB_PROPERTY, "reboot,");
+            break;
     }
+    sleep(5); // should reboot before this finishes
     return EXIT_SUCCESS;
 }