Merge "Avoid to call UI functions in child process"
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index 3951506..2f0e165 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -198,6 +198,7 @@
   if (fread(img, 1, sz, f) != sz) {
     printf("failed to read \"%s\" %s\n", filename, strerror(errno));
     fclose(f);
+    free(img);
     return NULL;
   }
   fclose(f);
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index 34ea81c..c0c67f9 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -16,21 +16,9 @@
 
 #include "graphics.h"
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-
-#include <fcntl.h>
-#include <stdio.h>
-
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-
-#include <linux/fb.h>
-#include <linux/kd.h>
-
-#include <time.h>
 
 #include "font_10x18.h"
 #include "minui/minui.h"
@@ -319,55 +307,6 @@
     gr_font->char_height = font.char_height;
 }
 
-#if 0
-// Exercises many of the gr_*() functions; useful for testing.
-static void gr_test() {
-    GRSurface** images;
-    int frames;
-    int result = res_create_multi_surface("icon_installing", &frames, &images);
-    if (result < 0) {
-        printf("create surface %d\n", result);
-        gr_exit();
-        return;
-    }
-
-    time_t start = time(NULL);
-    int x;
-    for (x = 0; x <= 1200; ++x) {
-        if (x < 400) {
-            gr_color(0, 0, 0, 255);
-        } else {
-            gr_color(0, (x-400)%128, 0, 255);
-        }
-        gr_clear();
-
-        gr_color(255, 0, 0, 255);
-        GRSurface* frame = images[x%frames];
-        gr_blit(frame, 0, 0, frame->width, frame->height, x, 0);
-
-        gr_color(255, 0, 0, 128);
-        gr_fill(400, 150, 600, 350);
-
-        gr_color(255, 255, 255, 255);
-        gr_text(500, 225, "hello, world!", 0);
-        gr_color(255, 255, 0, 128);
-        gr_text(300+x, 275, "pack my box with five dozen liquor jugs", 1);
-
-        gr_color(0, 0, 255, 128);
-        gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500);
-
-        gr_draw = gr_backend->flip(gr_backend);
-    }
-    printf("getting end time\n");
-    time_t end = time(NULL);
-    printf("got end time\n");
-    printf("start %ld end %ld\n", (long)start, (long)end);
-    if (end > start) {
-        printf("%.2f fps\n", ((double)x) / (end-start));
-    }
-}
-#endif
-
 void gr_flip() {
     gr_draw = gr_backend->flip(gr_backend);
 }
diff --git a/ui.cpp b/ui.cpp
index a0f741e..3ecd6d1 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -32,7 +32,11 @@
 
 #include <string>
 
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/strings.h>
 #include <cutils/android_reboot.h>
 #include <minui/minui.h>
 
@@ -40,11 +44,15 @@
 #include "roots.h"
 #include "device.h"
 
-#define UI_WAIT_KEY_TIMEOUT_SEC    120
+static constexpr int UI_WAIT_KEY_TIMEOUT_SEC = 120;
+static constexpr const char* BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/brightness";
+static constexpr const char* MAX_BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/max_brightness";
 
 RecoveryUI::RecoveryUI()
     : locale_(""),
       rtl_locale_(false),
+      brightness_normal_(50),
+      brightness_dimmed_(25),
       key_queue_len(0),
       key_last_down(-1),
       key_long_press(false),
@@ -54,7 +62,8 @@
       last_key(-1),
       has_power_key(false),
       has_up_key(false),
-      has_down_key(false) {
+      has_down_key(false),
+      screensaver_state_(ScreensaverState::DISABLED) {
   pthread_mutex_init(&key_queue_mutex, nullptr);
   pthread_cond_init(&key_queue_cond, nullptr);
   memset(key_pressed, 0, sizeof(key_pressed));
@@ -80,6 +89,40 @@
     return nullptr;
 }
 
+bool RecoveryUI::InitScreensaver() {
+  // Disabled.
+  if (brightness_normal_ == 0 || brightness_dimmed_ > brightness_normal_) {
+    return false;
+  }
+
+  // Set the initial brightness level based on the max brightness. Note that reading the initial
+  // value from BRIGHTNESS_FILE doesn't give the actual brightness value (bullhead, sailfish), so
+  // we don't have a good way to query the default value.
+  std::string content;
+  if (!android::base::ReadFileToString(MAX_BRIGHTNESS_FILE, &content)) {
+    PLOG(WARNING) << "Failed to read max brightness";
+    return false;
+  }
+
+  unsigned int max_value;
+  if (!android::base::ParseUint(android::base::Trim(content), &max_value)) {
+    LOG(WARNING) << "Failed to parse max brightness: " << content;
+    return false;
+  }
+
+  brightness_normal_value_ = max_value * brightness_normal_ / 100.0;
+  brightness_dimmed_value_ = max_value * brightness_dimmed_ / 100.0;
+  if (!android::base::WriteStringToFile(std::to_string(brightness_normal_value_),
+                                        BRIGHTNESS_FILE)) {
+    PLOG(WARNING) << "Failed to set brightness";
+    return false;
+  }
+
+  LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_ << "%)";
+  screensaver_state_ = ScreensaverState::NORMAL;
+  return true;
+}
+
 bool RecoveryUI::Init(const std::string& locale) {
   // Set up the locale info.
   SetLocale(locale);
@@ -88,6 +131,10 @@
 
   ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
 
+  if (!InitScreensaver()) {
+    LOG(INFO) << "Screensaver disabled";
+  }
+
   pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
   return true;
 }
@@ -220,31 +267,65 @@
 }
 
 int RecoveryUI::WaitKey() {
-    pthread_mutex_lock(&key_queue_mutex);
+  pthread_mutex_lock(&key_queue_mutex);
 
-    // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
-    // plugged in.
-    do {
-        struct timeval now;
-        struct timespec timeout;
-        gettimeofday(&now, nullptr);
-        timeout.tv_sec = now.tv_sec;
-        timeout.tv_nsec = now.tv_usec * 1000;
-        timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
+  // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
+  // plugged in.
+  do {
+    struct timeval now;
+    struct timespec timeout;
+    gettimeofday(&now, nullptr);
+    timeout.tv_sec = now.tv_sec;
+    timeout.tv_nsec = now.tv_usec * 1000;
+    timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
 
-        int rc = 0;
-        while (key_queue_len == 0 && rc != ETIMEDOUT) {
-            rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout);
-        }
-    } while (IsUsbConnected() && key_queue_len == 0);
-
-    int key = -1;
-    if (key_queue_len > 0) {
-        key = key_queue[0];
-        memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
+    int rc = 0;
+    while (key_queue_len == 0 && rc != ETIMEDOUT) {
+      rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout);
     }
-    pthread_mutex_unlock(&key_queue_mutex);
-    return key;
+
+    if (screensaver_state_ != ScreensaverState::DISABLED) {
+      if (rc == ETIMEDOUT) {
+        // Lower the brightness level: NORMAL -> DIMMED; DIMMED -> OFF.
+        if (screensaver_state_ == ScreensaverState::NORMAL) {
+          if (android::base::WriteStringToFile(std::to_string(brightness_dimmed_value_),
+                                               BRIGHTNESS_FILE)) {
+            LOG(INFO) << "Brightness: " << brightness_dimmed_value_ << " (" << brightness_dimmed_
+                      << "%)";
+            screensaver_state_ = ScreensaverState::DIMMED;
+          }
+        } else if (screensaver_state_ == ScreensaverState::DIMMED) {
+          if (android::base::WriteStringToFile("0", BRIGHTNESS_FILE)) {
+            LOG(INFO) << "Brightness: 0 (off)";
+            screensaver_state_ = ScreensaverState::OFF;
+          }
+        }
+      } else if (screensaver_state_ != ScreensaverState::NORMAL) {
+        // Drop the first key if it's changing from OFF to NORMAL.
+        if (screensaver_state_ == ScreensaverState::OFF) {
+          if (key_queue_len > 0) {
+            memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
+          }
+        }
+
+        // Reset the brightness to normal.
+        if (android::base::WriteStringToFile(std::to_string(brightness_normal_value_),
+                                             BRIGHTNESS_FILE)) {
+          screensaver_state_ = ScreensaverState::NORMAL;
+          LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_
+                    << "%)";
+        }
+      }
+    }
+  } while (IsUsbConnected() && key_queue_len == 0);
+
+  int key = -1;
+  if (key_queue_len > 0) {
+    key = key_queue[0];
+    memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
+  }
+  pthread_mutex_unlock(&key_queue_mutex);
+  return key;
 }
 
 bool RecoveryUI::IsUsbConnected() {
@@ -330,7 +411,7 @@
     }
 
     last_key = key;
-    return IsTextVisible() ? ENQUEUE : IGNORE;
+    return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
 }
 
 void RecoveryUI::KeyLongPress(int) {
diff --git a/ui.h b/ui.h
index 53ce060..823eb65 100644
--- a/ui.h
+++ b/ui.h
@@ -130,6 +130,13 @@
     std::string locale_;
     bool rtl_locale_;
 
+    // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25%
+    // of the max_brightness). Because the absolute values may vary across devices. These two
+    // values can be configured via subclassing. Setting brightness_normal_ to 0 to disable
+    // screensaver.
+    unsigned int brightness_normal_;
+    unsigned int brightness_dimmed_;
+
   private:
     // Key event input queue
     pthread_mutex_t key_queue_mutex;
@@ -167,6 +174,14 @@
     void time_key(int key_code, int count);
 
     void SetLocale(const std::string&);
+
+    enum class ScreensaverState { DISABLED, NORMAL, DIMMED, OFF };
+    ScreensaverState screensaver_state_;
+    // The following two contain the absolute values computed from brightness_normal_ and
+    // brightness_dimmed_ respectively.
+    unsigned int brightness_normal_value_;
+    unsigned int brightness_dimmed_value_;
+    bool InitScreensaver();
 };
 
 #endif  // RECOVERY_UI_H
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index 1c9be2d..a4799cc 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -30,6 +30,7 @@
  * verifier reaches the end after the verification.
  */
 
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -52,14 +53,71 @@
 using android::hardware::boot::V1_0::CommandResult;
 
 constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt";
+constexpr auto DM_PATH_PREFIX = "/sys/block/";
+constexpr auto DM_PATH_SUFFIX = "/dm/name";
+constexpr auto DEV_PATH = "/dev/block/";
 constexpr int BLOCKSIZE = 4096;
 
-static bool read_blocks(const std::string& blk_device_prefix, const std::string& range_str) {
-  std::string slot_suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
-  std::string blk_device = blk_device_prefix + slot_suffix;
-  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY)));
+// Find directories in format of "/sys/block/dm-X".
+static int dm_name_filter(const dirent* de) {
+  if (android::base::StartsWith(de->d_name, "dm-")) {
+    return 1;
+  }
+  return 0;
+}
+
+static bool read_blocks(const std::string& blk_device, const std::string& range_str) {
+  // Parse the partition in the end of the block_device string.
+  // Here is one example: "/dev/block/bootdevice/by-name/system"
+  std::string partition;
+  if (android::base::EndsWith(blk_device, "system")) {
+    partition = "system";
+  } else if (android::base::EndsWith(blk_device, "vendor")) {
+    partition = "vendor";
+  } else {
+    LOG(ERROR) << "Failed to parse partition string in " << blk_device;
+    return false;
+  }
+
+  // Iterate the content of "/sys/block/dm-X/dm/name". If it matches "system"
+  // (or "vendor"), then dm-X is a dm-wrapped system/vendor partition.
+  // Afterwards, update_verifier will read every block on the care_map_file of
+  // "/dev/block/dm-X" to ensure the partition's integrity.
+  dirent** namelist;
+  int n = scandir(DM_PATH_PREFIX, &namelist, dm_name_filter, alphasort);
+  if (n == -1) {
+    PLOG(ERROR) << "Failed to scan dir " << DM_PATH_PREFIX;
+    return false;
+  }
+  if (n == 0) {
+    LOG(ERROR) << "dm block device not found for " << partition;
+    return false;
+  }
+
+  std::string dm_block_device;
+  while (n--) {
+    std::string path = DM_PATH_PREFIX + std::string(namelist[n]->d_name) + DM_PATH_SUFFIX;
+    std::string content;
+    if (!android::base::ReadFileToString(path, &content)) {
+      PLOG(WARNING) << "Failed to read " << path;
+    } else if (android::base::Trim(content) == partition) {
+      dm_block_device = DEV_PATH + std::string(namelist[n]->d_name);
+      while (n--) {
+        free(namelist[n]);
+      }
+      break;
+    }
+    free(namelist[n]);
+  }
+  free(namelist);
+
+  if (dm_block_device.empty()) {
+    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 partition " << blk_device;
+    PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition;
     return false;
   }
 
@@ -100,7 +158,7 @@
     blk_count += (range_end - range_start);
   }
 
-  LOG(INFO) << "Finished reading " << blk_count << " blocks on " << blk_device;
+  LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device;
   return true;
 }