Merge "Move menu headers/items to std::vector<std::string>." am: fb86bb2a07 am: 0d9ed29541
am: 3777c4b859

Change-Id: I208d2804c2fc6634c8464fdb700756ade977b908
diff --git a/device.cpp b/device.cpp
index 3b0942c..5cf9cc2 100644
--- a/device.cpp
+++ b/device.cpp
@@ -16,9 +16,13 @@
 
 #include "device.h"
 
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
 #include "ui.h"
 
-static const char* MENU_ITEMS[] = {
+// clang-format off
+static constexpr const char* kItems[]{
   "Reboot system now",
   "Reboot to bootloader",
   "Apply update from ADB",
@@ -32,10 +36,11 @@
   "Run graphics test",
   "Run locale test",
   "Power off",
-  nullptr,
 };
+// clang-format on
 
-static const Device::BuiltinAction MENU_ACTIONS[] = {
+// clang-format off
+static constexpr Device::BuiltinAction kMenuActions[] {
   Device::REBOOT,
   Device::REBOOT_BOOTLOADER,
   Device::APPLY_ADB_SIDELOAD,
@@ -50,18 +55,20 @@
   Device::RUN_LOCALE_TEST,
   Device::SHUTDOWN,
 };
+// clang-format on
 
-static_assert(sizeof(MENU_ITEMS) / sizeof(MENU_ITEMS[0]) ==
-              sizeof(MENU_ACTIONS) / sizeof(MENU_ACTIONS[0]) + 1,
-              "MENU_ITEMS and MENU_ACTIONS should have the same length, "
-              "except for the extra NULL entry in MENU_ITEMS.");
+static_assert(arraysize(kItems) == arraysize(kMenuActions),
+              "kItems and kMenuActions should have the same length.");
 
-const char* const* Device::GetMenuItems() {
-  return MENU_ITEMS;
+static const std::vector<std::string> kMenuItems(kItems, kItems + arraysize(kItems));
+
+const std::vector<std::string>& Device::GetMenuItems() {
+  return kMenuItems;
 }
 
-Device::BuiltinAction Device::InvokeMenuItem(int menu_position) {
-  return menu_position < 0 ? NO_ACTION : MENU_ACTIONS[menu_position];
+Device::BuiltinAction Device::InvokeMenuItem(size_t menu_position) {
+  // CHECK_LT(menu_position, );
+  return kMenuActions[menu_position];
 }
 
 int Device::HandleMenuKey(int key, bool visible) {
diff --git a/device.h b/device.h
index 4ea3159..8788b2d 100644
--- a/device.h
+++ b/device.h
@@ -17,11 +17,37 @@
 #ifndef _RECOVERY_DEVICE_H
 #define _RECOVERY_DEVICE_H
 
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
 // Forward declaration to avoid including "ui.h".
 class RecoveryUI;
 
 class Device {
  public:
+  static constexpr const int kNoAction = -1;
+  static constexpr const int kHighlightUp = -2;
+  static constexpr const int kHighlightDown = -3;
+  static constexpr const int kInvokeItem = -4;
+
+  enum BuiltinAction {
+    NO_ACTION = 0,
+    REBOOT = 1,
+    APPLY_SDCARD = 2,
+    // APPLY_CACHE was 3.
+    APPLY_ADB_SIDELOAD = 4,
+    WIPE_DATA = 5,
+    WIPE_CACHE = 6,
+    REBOOT_BOOTLOADER = 7,
+    SHUTDOWN = 8,
+    VIEW_RECOVERY_LOGS = 9,
+    MOUNT_SYSTEM = 10,
+    RUN_GRAPHICS_TEST = 11,
+    RUN_LOCALE_TEST = 12,
+  };
+
   explicit Device(RecoveryUI* ui) : ui_(ui) {}
   virtual ~Device() {}
 
@@ -48,44 +74,23 @@
   //
   // Returns one of the defined constants below in order to:
   //
-  //   - move the menu highlight (kHighlight{Up,Down})
-  //   - invoke the highlighted item (kInvokeItem)
-  //   - do nothing (kNoAction)
-  //   - invoke a specific action (a menu position: any non-negative number)
+  //   - move the menu highlight (kHighlight{Up,Down}: negative value)
+  //   - invoke the highlighted item (kInvokeItem: negative value)
+  //   - do nothing (kNoAction: negative value)
+  //   - invoke a specific action (a menu position: non-negative value)
   virtual int HandleMenuKey(int key, bool visible);
 
-  enum BuiltinAction {
-    NO_ACTION = 0,
-    REBOOT = 1,
-    APPLY_SDCARD = 2,
-    // APPLY_CACHE was 3.
-    APPLY_ADB_SIDELOAD = 4,
-    WIPE_DATA = 5,
-    WIPE_CACHE = 6,
-    REBOOT_BOOTLOADER = 7,
-    SHUTDOWN = 8,
-    VIEW_RECOVERY_LOGS = 9,
-    MOUNT_SYSTEM = 10,
-    RUN_GRAPHICS_TEST = 11,
-    RUN_LOCALE_TEST = 12,
-  };
+  // Returns the list of menu items (a vector of strings). The menu_position passed to
+  // InvokeMenuItem will correspond to the indexes into this array.
+  virtual const std::vector<std::string>& GetMenuItems();
 
-  // Return the list of menu items (an array of strings, NULL-terminated). The menu_position passed
-  // to InvokeMenuItem will correspond to the indexes into this array.
-  virtual const char* const* GetMenuItems();
-
-  // Perform a recovery action selected from the menu. 'menu_position' will be the item number of
-  // the selected menu item, or a non-negative number returned from HandleMenuKey(). The menu will
-  // be hidden when this is called; implementations can call ui_print() to print information to the
+  // Performs a recovery action selected from the menu. 'menu_position' will be the index of the
+  // selected menu item, or a non-negative value returned from HandleMenuKey(). The menu will be
+  // hidden when this is called; implementations can call ui_print() to print information to the
   // screen. If the menu position is one of the builtin actions, you can just return the
   // corresponding enum value. If it is an action specific to your device, you actually perform it
   // here and return NO_ACTION.
-  virtual BuiltinAction InvokeMenuItem(int menu_position);
-
-  static const int kNoAction = -1;
-  static const int kHighlightUp = -2;
-  static const int kHighlightDown = -3;
-  static const int kInvokeItem = -4;
+  virtual BuiltinAction InvokeMenuItem(size_t menu_position);
 
   // Called before and after we do a wipe data/factory reset operation, either via a reboot from the
   // main system with the --wipe_data flag, or when the user boots into recovery image manually and
diff --git a/recovery.cpp b/recovery.cpp
index f820373..d7ece4e 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -507,7 +507,7 @@
   }
 
   std::vector<std::string> dirs;
-  std::vector<std::string> zips = { "../" };  // "../" is always the first entry.
+  std::vector<std::string> entries{ "../" };  // "../" is always the first entry.
 
   dirent* de;
   while ((de = readdir(d.get())) != nullptr) {
@@ -518,31 +518,25 @@
       if (name == "." || name == "..") continue;
       dirs.push_back(name + "/");
     } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
-      zips.push_back(name);
+      entries.push_back(name);
     }
   }
 
   std::sort(dirs.begin(), dirs.end());
-  std::sort(zips.begin(), zips.end());
+  std::sort(entries.begin(), entries.end());
 
-  // Append dirs to the zips list.
-  zips.insert(zips.end(), dirs.begin(), dirs.end());
+  // Append dirs to the entries list.
+  entries.insert(entries.end(), dirs.begin(), dirs.end());
 
-  const char* entries[zips.size() + 1];
-  entries[zips.size()] = nullptr;
-  for (size_t i = 0; i < zips.size(); i++) {
-    entries[i] = zips[i].c_str();
-  }
+  std::vector<std::string> headers{ "Choose a package to install:", path };
 
-  const char* headers[] = { "Choose a package to install:", path.c_str(), nullptr };
-
-  int chosen_item = 0;
+  size_t chosen_item = 0;
   while (true) {
     chosen_item = ui->ShowMenu(
         headers, entries, chosen_item, true,
         std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
 
-    const std::string& item = zips[chosen_item];
+    const std::string& item = entries[chosen_item];
     if (chosen_item == 0) {
       // Go up but continue browsing (if the caller is browse_directory).
       return "";
@@ -564,10 +558,10 @@
 }
 
 static bool yes_no(Device* device, const char* question1, const char* question2) {
-  const char* headers[] = { question1, question2, NULL };
-  const char* items[] = { " No", " Yes", NULL };
+  std::vector<std::string> headers{ question1, question2 };
+  std::vector<std::string> items{ " No", " Yes" };
 
-  int chosen_item = ui->ShowMenu(
+  size_t chosen_item = ui->ShowMenu(
       headers, items, 0, true,
       std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
   return (chosen_item == 1);
@@ -601,20 +595,20 @@
 
 static bool prompt_and_wipe_data(Device* device) {
   // Use a single string and let ScreenRecoveryUI handles the wrapping.
-  const char* const headers[] = {
+  std::vector<std::string> headers{
     "Can't load Android system. Your data may be corrupt. "
     "If you continue to get this message, you may need to "
     "perform a factory data reset and erase all user data "
     "stored on this device.",
-    nullptr
   };
-  const char* const items[] = {
+  // clang-format off
+  std::vector<std::string> items {
     "Try again",
     "Factory data reset",
-    NULL
   };
+  // clang-format on
   for (;;) {
-    int chosen_item = ui->ShowMenu(
+    size_t chosen_item = ui->ShowMenu(
         headers, items, 0, true,
         std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
     if (chosen_item != 1) {
@@ -806,17 +800,12 @@
 
   entries.push_back("Back");
 
-  std::vector<const char*> menu_entries(entries.size());
-  std::transform(entries.cbegin(), entries.cend(), menu_entries.begin(),
-                 [](const std::string& entry) { return entry.c_str(); });
-  menu_entries.push_back(nullptr);
+  std::vector<std::string> headers{ "Select file to view" };
 
-  const char* headers[] = { "Select file to view", nullptr };
-
-  int chosen_item = 0;
+  size_t chosen_item = 0;
   while (true) {
     chosen_item = ui->ShowMenu(
-        headers, menu_entries.data(), chosen_item, true,
+        headers, entries, chosen_item, true,
         std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
     if (entries[chosen_item] == "Back") break;
 
@@ -963,14 +952,15 @@
     }
     ui->SetProgressType(RecoveryUI::EMPTY);
 
-    int chosen_item = ui->ShowMenu(
-        nullptr, device->GetMenuItems(), 0, false,
+    size_t chosen_item = ui->ShowMenu(
+        {}, device->GetMenuItems(), 0, false,
         std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
 
     // Device-specific code may take some action here. It may return one of the core actions
     // handled in the switch statement below.
-    Device::BuiltinAction chosen_action =
-        (chosen_item == -1) ? Device::REBOOT : device->InvokeMenuItem(chosen_item);
+    Device::BuiltinAction chosen_action = (chosen_item == static_cast<size_t>(-1))
+                                              ? Device::REBOOT
+                                              : device->InvokeMenuItem(chosen_item);
 
     bool should_wipe_cache = false;
     switch (chosen_action) {
diff --git a/screen_ui.cpp b/screen_ui.cpp
index 56ca48e..7ae81e5 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -31,6 +31,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -52,8 +53,9 @@
   return tv.tv_sec + tv.tv_usec / 1000000.0;
 }
 
-Menu::Menu(bool scrollable, size_t max_items, size_t max_length, const char* const* headers,
-           const char* const* items, int initial_selection)
+Menu::Menu(bool scrollable, size_t max_items, size_t max_length,
+           const std::vector<std::string>& headers, const std::vector<std::string>& items,
+           size_t initial_selection)
     : scrollable_(scrollable),
       max_display_items_(max_items),
       max_item_length_(max_length),
@@ -63,15 +65,15 @@
   CHECK_LE(max_items, static_cast<size_t>(std::numeric_limits<int>::max()));
 
   // It's fine to have more entries than text_rows_ if scrollable menu is supported.
-  size_t max_items_count = scrollable_ ? std::numeric_limits<int>::max() : max_display_items_;
-  for (size_t i = 0; i < max_items_count && items[i] != nullptr; ++i) {
-    text_items_.emplace_back(items[i], strnlen(items[i], max_item_length_));
+  size_t items_count = scrollable_ ? items.size() : std::min(items.size(), max_display_items_);
+  for (size_t i = 0; i < items_count; ++i) {
+    text_items_.emplace_back(items[i].substr(0, max_item_length_));
   }
 
   CHECK(!text_items_.empty());
 }
 
-const char* const* Menu::text_headers() const {
+const std::vector<std::string>& Menu::text_headers() const {
   return text_headers_;
 }
 
@@ -99,7 +101,7 @@
   }
 
   *cur_selection_str =
-      android::base::StringPrintf("Current item: %d/%zu", selection_ + 1, ItemsCount());
+      android::base::StringPrintf("Current item: %zu/%zu", selection_ + 1, ItemsCount());
   return true;
 }
 
@@ -503,10 +505,10 @@
   gr_clear();
 
   // clang-format off
-  static std::vector<std::string> REGULAR_HELP = {
+  static std::vector<std::string> REGULAR_HELP{
     "Use volume up/down and power.",
   };
-  static std::vector<std::string> LONG_PRESS_HELP = {
+  static std::vector<std::string> LONG_PRESS_HELP{
     "Any button cycles highlight.",
     "Long-press activates.",
   };
@@ -532,22 +534,12 @@
 
     y += DrawTextLines(x, y, help_message);
 
-    auto convert_to_vector = [](const char* const* items) -> std::vector<std::string> {
-      if (items == nullptr) return {};
-
-      std::vector<std::string> result;
-      for (size_t i = 0; items[i] != nullptr; ++i) {
-        result.emplace_back(items[i]);
-      }
-      return result;
-    };
-
     // Draw menu header.
     SetColor(HEADER);
     if (!menu_->scrollable()) {
-      y += DrawWrappedTextLines(x, y, convert_to_vector(menu_->text_headers()));
+      y += DrawWrappedTextLines(x, y, menu_->text_headers());
     } else {
-      y += DrawTextLines(x, y, convert_to_vector(menu_->text_headers()));
+      y += DrawTextLines(x, y, menu_->text_headers());
       // Show the current menu item number in relation to total number if items don't fit on the
       // screen.
       std::string cur_selection_str;
@@ -979,8 +971,8 @@
   text_row_ = old_text_row;
 }
 
-void ScreenRecoveryUI::StartMenu(const char* const* headers, const char* const* items,
-                                 int initial_selection) {
+void ScreenRecoveryUI::StartMenu(const std::vector<std::string>& headers,
+                                 const std::vector<std::string>& items, size_t initial_selection) {
   pthread_mutex_lock(&updateMutex);
   if (text_rows_ > 0 && text_cols_ > 1) {
     menu_ = std::make_unique<Menu>(scrollable_menu_, text_rows_, text_cols_ - 1, headers, items,
@@ -1013,9 +1005,10 @@
   pthread_mutex_unlock(&updateMutex);
 }
 
-int ScreenRecoveryUI::ShowMenu(const char* const* headers, const char* const* items,
-                               int initial_selection, bool menu_only,
-                               const std::function<int(int, bool)>& key_handler) {
+size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
+                                  const std::vector<std::string>& items, size_t initial_selection,
+                                  bool menu_only,
+                                  const std::function<int(int, bool)>& key_handler) {
   // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
   FlushKeys();
 
@@ -1031,7 +1024,7 @@
       } else {
         LOG(INFO) << "Timed out waiting for key input; rebooting.";
         EndMenu();
-        return -1;
+        return static_cast<size_t>(-1);
       }
     }
 
diff --git a/screen_ui.h b/screen_ui.h
index 3b309fb..fb811ce 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -35,14 +35,15 @@
  public:
   // Constructs a Menu instance with the given |headers|, |items| and properties. Sets the initial
   // selection to |initial_selection|.
-  Menu(bool scrollable, size_t max_items, size_t max_length, const char* const* headers,
-       const char* const* items, int initial_selection);
+  Menu(bool scrollable, size_t max_items, size_t max_length,
+       const std::vector<std::string>& headers, const std::vector<std::string>& items,
+       size_t initial_selection);
 
   bool scrollable() const {
     return scrollable_;
   }
 
-  int selection() const {
+  size_t selection() const {
     return selection_;
   }
 
@@ -66,7 +67,7 @@
   //                                 /cache/recovery/last_log.1
   //                                 /cache/recovery/last_log.2
   //                                 ...
-  const char* const* text_headers() const;
+  const std::vector<std::string>& text_headers() const;
   std::string TextItem(size_t index) const;
 
   // Checks if the menu items fit vertically on the screen. Returns true and set the
@@ -84,15 +85,14 @@
   const size_t max_display_items_;
   // The length of each item to fit horizontally on a screen.
   const size_t max_item_length_;
-
-  // Internal storage for the menu headers and items in text.
-  const char* const* text_headers_;
+  // The menu headers.
+  std::vector<std::string> text_headers_;
+  // The actual menu items trimmed to fit the given properties.
   std::vector<std::string> text_items_;
-
   // The first item to display on the screen.
   size_t menu_start_;
   // Current menu selection.
-  int selection_;
+  size_t selection_;
 };
 
 // Implementation of RecoveryUI appropriate for devices with a screen
@@ -137,8 +137,9 @@
   void ShowFile(const std::string& filename) override;
 
   // menu display
-  int ShowMenu(const char* const* headers, const char* const* items, int initial_selection,
-               bool menu_only, const std::function<int(int, bool)>& key_handler) override;
+  size_t ShowMenu(const std::vector<std::string>& headers, const std::vector<std::string>& items,
+                  size_t initial_selection, bool menu_only,
+                  const std::function<int(int, bool)>& key_handler) override;
 
   void KeyLongPress(int) override;
 
@@ -166,8 +167,8 @@
 
   // Displays some header text followed by a menu of items, which appears at the top of the screen
   // (in place of any scrolling ui_print() output, if necessary).
-  virtual void StartMenu(const char* const* headers, const char* const* items,
-                         int initial_selection);
+  virtual void StartMenu(const std::vector<std::string>& headers,
+                         const std::vector<std::string>& items, size_t initial_selection);
 
   // Sets the menu highlight to the given index, wrapping if necessary. Returns the actual item
   // selected.
diff --git a/stub_ui.h b/stub_ui.h
index 362aab4..2ccd491 100644
--- a/stub_ui.h
+++ b/stub_ui.h
@@ -19,6 +19,7 @@
 
 #include <functional>
 #include <string>
+#include <vector>
 
 #include "ui.h"
 
@@ -57,9 +58,10 @@
   void ShowFile(const std::string& /* filename */) override {}
 
   // menu display
-  int ShowMenu(const char* const* /* headers */, const char* const* /* items */,
-               int initial_selection, bool /* menu_only */,
-               const std::function<int(int, bool)>& /* key_handler */) override {
+  size_t ShowMenu(const std::vector<std::string>& /* headers */,
+                  const std::vector<std::string>& /* items */, size_t initial_selection,
+                  bool /* menu_only */,
+                  const std::function<int(int, bool)>& /* key_handler */) override {
     return initial_selection;
   }
 };
diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp
index 9c123e8..e47d705 100644
--- a/tests/unit/screen_ui_test.cpp
+++ b/tests/unit/screen_ui_test.cpp
@@ -14,19 +14,22 @@
  * limitations under the License.
  */
 
-#include "screen_ui.h"
+#include <stddef.h>
 
 #include <string>
+#include <vector>
 
 #include <gtest/gtest.h>
 
-constexpr const char* HEADER[] = { "header", nullptr };
-constexpr const char* ITEMS[] = { "items1", "items2", "items3", "items4", "1234567890", nullptr };
+#include "screen_ui.h"
+
+static const std::vector<std::string> HEADERS{ "header" };
+static const std::vector<std::string> ITEMS{ "item1", "item2", "item3", "item4", "1234567890" };
 
 TEST(ScreenUITest, StartPhoneMenuSmoke) {
-  Menu menu(false, 10, 20, HEADER, ITEMS, 0);
+  Menu menu(false, 10, 20, HEADERS, ITEMS, 0);
   ASSERT_FALSE(menu.scrollable());
-  ASSERT_EQ(HEADER[0], menu.text_headers()[0]);
+  ASSERT_EQ(HEADERS[0], menu.text_headers()[0]);
   ASSERT_EQ(5u, menu.ItemsCount());
 
   std::string message;
@@ -39,9 +42,9 @@
 }
 
 TEST(ScreenUITest, StartWearMenuSmoke) {
-  Menu menu(true, 10, 8, HEADER, ITEMS, 1);
+  Menu menu(true, 10, 8, HEADERS, ITEMS, 1);
   ASSERT_TRUE(menu.scrollable());
-  ASSERT_EQ(HEADER[0], menu.text_headers()[0]);
+  ASSERT_EQ(HEADERS[0], menu.text_headers()[0]);
   ASSERT_EQ(5u, menu.ItemsCount());
 
   std::string message;
@@ -55,7 +58,7 @@
 }
 
 TEST(ScreenUITest, StartPhoneMenuItemsOverflow) {
-  Menu menu(false, 1, 20, HEADER, ITEMS, 0);
+  Menu menu(false, 1, 20, HEADERS, ITEMS, 0);
   ASSERT_FALSE(menu.scrollable());
   ASSERT_EQ(1u, menu.ItemsCount());
 
@@ -70,7 +73,7 @@
 }
 
 TEST(ScreenUITest, StartWearMenuItemsOverflow) {
-  Menu menu(true, 1, 20, HEADER, ITEMS, 0);
+  Menu menu(true, 1, 20, HEADERS, ITEMS, 0);
   ASSERT_TRUE(menu.scrollable());
   ASSERT_EQ(5u, menu.ItemsCount());
 
@@ -88,7 +91,7 @@
 
 TEST(ScreenUITest, PhoneMenuSelectSmoke) {
   int sel = 0;
-  Menu menu(false, 10, 20, HEADER, ITEMS, sel);
+  Menu menu(false, 10, 20, HEADERS, ITEMS, sel);
   // Mimic down button 10 times (2 * items size)
   for (int i = 0; i < 10; i++) {
     sel = menu.Select(++sel);
@@ -117,7 +120,7 @@
 
 TEST(ScreenUITest, WearMenuSelectSmoke) {
   int sel = 0;
-  Menu menu(true, 10, 20, HEADER, ITEMS, sel);
+  Menu menu(true, 10, 20, HEADERS, ITEMS, sel);
   // Mimic pressing down button 10 times (2 * items size)
   for (int i = 0; i < 10; i++) {
     sel = menu.Select(++sel);
@@ -146,7 +149,7 @@
 
 TEST(ScreenUITest, WearMenuSelectItemsOverflow) {
   int sel = 1;
-  Menu menu(true, 3, 20, HEADER, ITEMS, sel);
+  Menu menu(true, 3, 20, HEADERS, ITEMS, sel);
   ASSERT_EQ(5u, menu.ItemsCount());
 
   // Scroll the menu to the end, and check the start & end of menu.
diff --git a/ui.h b/ui.h
index c468992..35cc36e 100644
--- a/ui.h
+++ b/ui.h
@@ -23,6 +23,7 @@
 
 #include <functional>
 #include <string>
+#include <vector>
 
 // Abstract class for controlling the user interface during recovery.
 class RecoveryUI {
@@ -139,10 +140,11 @@
   // key_handler, which may be beyond the range of menu items. This could be used to trigger a
   // device-specific action, even without that being listed in the menu. Caller needs to handle
   // such a case accordingly (e.g. by calling Device::InvokeMenuItem() to process the action).
-  // Returns a non-negative value (the chosen item number or device-specific action code), or -1 if
-  // timed out waiting for input.
-  virtual int ShowMenu(const char* const* headers, const char* const* items, int initial_selection,
-                       bool menu_only, const std::function<int(int, bool)>& key_handler) = 0;
+  // Returns a non-negative value (the chosen item number or device-specific action code), or
+  // static_cast<size_t>(-1) if timed out waiting for input.
+  virtual size_t ShowMenu(const std::vector<std::string>& headers,
+                          const std::vector<std::string>& items, size_t initial_selection,
+                          bool menu_only, const std::function<int(int, bool)>& key_handler) = 0;
 
  protected:
   void EnqueueKey(int key_code);
diff --git a/wear_ui.cpp b/wear_ui.cpp
index d21f835..f157d3c 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -20,6 +20,7 @@
 #include <string.h>
 
 #include <string>
+#include <vector>
 
 #include <android-base/properties.h>
 #include <android-base/strings.h>
@@ -88,13 +89,12 @@
 
 void WearRecoveryUI::SetStage(int /* current */, int /* max */) {}
 
-void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* items,
-                               int initial_selection) {
+void WearRecoveryUI::StartMenu(const std::vector<std::string>& headers,
+                               const std::vector<std::string>& items, size_t initial_selection) {
   pthread_mutex_lock(&updateMutex);
   if (text_rows_ > 0 && text_cols_ > 0) {
     menu_ = std::make_unique<Menu>(scrollable_menu_, text_rows_ - kMenuUnusableRows - 1,
                                    text_cols_ - 1, headers, items, initial_selection);
-
     update_screen_locked();
   }
   pthread_mutex_unlock(&updateMutex);
diff --git a/wear_ui.h b/wear_ui.h
index fcbbee2..c9a9f0e 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -17,6 +17,9 @@
 #ifndef RECOVERY_WEAR_UI_H
 #define RECOVERY_WEAR_UI_H
 
+#include <string>
+#include <vector>
+
 #include "screen_ui.h"
 
 class WearRecoveryUI : public ScreenRecoveryUI {
@@ -33,8 +36,8 @@
   // Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen.
   const int kMenuUnusableRows;
 
-  void StartMenu(const char* const* headers, const char* const* items,
-                 int initial_selection) override;
+  void StartMenu(const std::vector<std::string>& headers, const std::vector<std::string>& items,
+                 size_t initial_selection) override;
 
   int GetProgressBaseline() const override;