Merge "tests: Add the missing dependency on libhidlbase." into oc-mr1-dev
diff --git a/tools/recovery_l10n/res/values-en-rXC/strings.xml b/tools/recovery_l10n/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..2d528b3
--- /dev/null
+++ b/tools/recovery_l10n/res/values-en-rXC/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="recovery_installing" msgid="2013591905463558223">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎Installing system update‎‏‎‎‏‎"</string>
+    <string name="recovery_erasing" msgid="7334826894904037088">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎Erasing‎‏‎‎‏‎"</string>
+    <string name="recovery_no_command" msgid="4465476568623024327">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎No command‎‏‎‎‏‎"</string>
+    <string name="recovery_error" msgid="5748178989622716736">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎Error!‎‏‎‎‏‎"</string>
+    <string name="recovery_installing_security" msgid="9184031299717114342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎Installing security update‎‏‎‎‏‎"</string>
+</resources>
diff --git a/ui.cpp b/ui.cpp
index eadcdd4..e80d7ed 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -69,6 +69,7 @@
       has_down_key(false),
       has_touch_screen(false),
       touch_slot_(0),
+      is_bootreason_recovery_ui_(false),
       screensaver_state_(ScreensaverState::DISABLED) {
   pthread_mutex_init(&key_queue_mutex, nullptr);
   pthread_cond_init(&key_queue_cond, nullptr);
@@ -142,6 +143,19 @@
 
   if (touch_screen_allowed_) {
     ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
+
+    // Parse /proc/cmdline to determine if it's booting into recovery with a bootreason of
+    // "recovery_ui". This specific reason is set by some (wear) bootloaders, to allow an easier way
+    // to turn on text mode. It will only be set if the recovery boot is triggered from fastboot, or
+    // with 'adb reboot recovery'. Note that this applies to all build variants. Otherwise the text
+    // mode will be turned on automatically on debuggable builds, even without a swipe.
+    std::string cmdline;
+    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+      is_bootreason_recovery_ui_ = cmdline.find("bootreason=recovery_ui") != std::string::npos;
+    } else {
+      // Non-fatal, and won't affect Init() result.
+      PLOG(WARNING) << "Failed to read /proc/cmdline";
+    }
   }
 
   if (!InitScreensaver()) {
@@ -168,6 +182,12 @@
     return;
   }
 
+  // Allow turning on text mode with any swipe, if bootloader has set a bootreason of recovery_ui.
+  if (is_bootreason_recovery_ui_ && !IsTextVisible()) {
+    ShowText(true);
+    return;
+  }
+
   LOG(DEBUG) << "Swipe direction=" << direction;
   switch (direction) {
     case SwipeDirection::UP:
diff --git a/ui.h b/ui.h
index 5cda7af..3d9afec 100644
--- a/ui.h
+++ b/ui.h
@@ -170,6 +170,7 @@
   int touch_start_Y_;
   bool touch_finger_down_;
   bool touch_swiping_;
+  bool is_bootreason_recovery_ui_;
 
   struct key_timer_t {
     RecoveryUI* ui;
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index b49011a..ceb3ec9 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -45,6 +45,7 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <future>
 #include <string>
 #include <vector>
 
@@ -123,11 +124,6 @@
     LOG(ERROR) << "Failed to find dm block device for " << partition;
     return false;
   }
-  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
-  if (fd.get() == -1) {
-    PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition;
-    return false;
-  }
 
   // For block range string, first integer 'count' equals 2 * total number of valid ranges,
   // followed by 'count' number comma separated integers. Every two integers reprensent a
@@ -142,37 +138,61 @@
     return false;
   }
 
-  size_t blk_count = 0;
-  for (size_t i = 1; i < ranges.size(); i += 2) {
-    unsigned int range_start, range_end;
-    bool parse_status = android::base::ParseUint(ranges[i], &range_start);
-    parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end);
-    if (!parse_status || range_start >= range_end) {
-      LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1];
-      return false;
-    }
+  std::vector<std::future<bool>> threads;
+  size_t thread_num = std::thread::hardware_concurrency() ?: 4;
+  thread_num = std::min(thread_num, range_count / 2);
+  size_t group_range_count = range_count / thread_num;
 
-    static constexpr size_t BLOCKSIZE = 4096;
-    if (lseek64(fd.get(), static_cast<off64_t>(range_start) * BLOCKSIZE, SEEK_SET) == -1) {
-      PLOG(ERROR) << "lseek to " << range_start << " failed";
-      return false;
-    }
-
-    size_t remain = (range_end - range_start) * BLOCKSIZE;
-    while (remain > 0) {
-      size_t to_read = std::min(remain, 1024 * BLOCKSIZE);
-      std::vector<uint8_t> buf(to_read);
-      if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) {
-        PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end;
+  for (size_t t = 0; t < thread_num; t++) {
+    auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() {
+      size_t blk_count = 0;
+      static constexpr size_t kBlockSize = 4096;
+      std::vector<uint8_t> buf(1024 * kBlockSize);
+      android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
+      if (fd.get() == -1) {
+        PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition;
         return false;
       }
-      remain -= to_read;
-    }
-    blk_count += (range_end - range_start);
+
+      for (size_t i = 1 + group_range_count * t; i < group_range_count * (t + 1) + 1; i += 2) {
+        unsigned int range_start, range_end;
+        bool parse_status = android::base::ParseUint(ranges[i], &range_start);
+        parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end);
+        if (!parse_status || range_start >= range_end) {
+          LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1];
+          return false;
+        }
+
+        if (lseek64(fd.get(), static_cast<off64_t>(range_start) * kBlockSize, SEEK_SET) == -1) {
+          PLOG(ERROR) << "lseek to " << range_start << " failed";
+          return false;
+        }
+
+        size_t remain = (range_end - range_start) * kBlockSize;
+        while (remain > 0) {
+          size_t to_read = std::min(remain, 1024 * kBlockSize);
+          if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) {
+            PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end;
+            return false;
+          }
+          remain -= to_read;
+        }
+        blk_count += (range_end - range_start);
+      }
+      LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device;
+      return true;
+    };
+
+    threads.emplace_back(std::async(std::launch::async, thread_func));
   }
 
-  LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device;
-  return true;
+  bool ret = true;
+  for (auto& t : threads) {
+    ret = t.get() && ret;
+  }
+  LOG(INFO) << "Finished reading blocks on " << dm_block_device << " with " << thread_num
+            << " threads.";
+  return ret;
 }
 
 // Returns true to indicate a passing verification (or the error should be ignored); Otherwise