Merge "update_verifier: Support AVB."
diff --git a/Android.mk b/Android.mk
index e619db0..5348e36 100644
--- a/Android.mk
+++ b/Android.mk
@@ -76,6 +76,7 @@
     rotate_logs.cpp \
     screen_ui.cpp \
     ui.cpp \
+    vr_ui.cpp \
     wear_ui.cpp \
     wear_touch.cpp \
 
@@ -182,6 +183,17 @@
 LOCAL_CFLAGS := -Werror
 include $(BUILD_STATIC_LIBRARY)
 
+# vr headset default device
+# ===============================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := vr_device.cpp
+
+# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk
+LOCAL_MODULE := librecovery_ui_vr
+
+include $(BUILD_STATIC_LIBRARY)
+
 include \
     $(LOCAL_PATH)/applypatch/Android.mk \
     $(LOCAL_PATH)/boot_control/Android.mk \
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..09754c6
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+enh+aosp-gerrit@google.com
+tbao@google.com
+xunchang@google.com
diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp
index 702a624..df75f98 100644
--- a/applypatch/imgpatch.cpp
+++ b/applypatch/imgpatch.cpp
@@ -26,12 +26,14 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
+#include <android-base/logging.h>
+#include <android-base/memory.h>
 #include <applypatch/applypatch.h>
 #include <applypatch/imgdiff.h>
-#include <android-base/memory.h>
 #include <openssl/sha.h>
 #include <zlib.h>
 
@@ -43,6 +45,86 @@
   return android::base::get_unaligned<int32_t>(address);
 }
 
+// This function is a wrapper of ApplyBSDiffPatch(). It has a custom sink function to deflate the
+// patched data and stream the deflated data to output.
+static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_len,
+                                            const Value* patch, size_t patch_offset,
+                                            const char* deflate_header, SinkFn sink, SHA_CTX* ctx) {
+  size_t expected_target_length = static_cast<size_t>(Read8(deflate_header + 32));
+  int level = Read4(deflate_header + 40);
+  int method = Read4(deflate_header + 44);
+  int window_bits = Read4(deflate_header + 48);
+  int mem_level = Read4(deflate_header + 52);
+  int strategy = Read4(deflate_header + 56);
+
+  std::unique_ptr<z_stream, decltype(&deflateEnd)> strm(new z_stream(), deflateEnd);
+  strm->zalloc = Z_NULL;
+  strm->zfree = Z_NULL;
+  strm->opaque = Z_NULL;
+  strm->avail_in = 0;
+  strm->next_in = nullptr;
+  int ret = deflateInit2(strm.get(), level, method, window_bits, mem_level, strategy);
+  if (ret != Z_OK) {
+    LOG(ERROR) << "Failed to init uncompressed data deflation: " << ret;
+    return false;
+  }
+
+  // Define a custom sink wrapper that feeds to bspatch. It deflates the available patch data on
+  // the fly and outputs the compressed data to the given sink.
+  size_t actual_target_length = 0;
+  size_t total_written = 0;
+  static constexpr size_t buffer_size = 32768;
+  auto compression_sink = [&](const uint8_t* data, size_t len) -> size_t {
+    // The input patch length for an update never exceeds INT_MAX.
+    strm->avail_in = len;
+    strm->next_in = data;
+    do {
+      std::vector<uint8_t> buffer(buffer_size);
+      strm->avail_out = buffer_size;
+      strm->next_out = buffer.data();
+      if (actual_target_length + len < expected_target_length) {
+        ret = deflate(strm.get(), Z_NO_FLUSH);
+      } else {
+        ret = deflate(strm.get(), Z_FINISH);
+      }
+      if (ret != Z_OK && ret != Z_STREAM_END) {
+        LOG(ERROR) << "Failed to deflate stream: " << ret;
+        // zero length indicates an error in the sink function of bspatch().
+        return 0;
+      }
+
+      size_t have = buffer_size - strm->avail_out;
+      total_written += have;
+      if (sink(buffer.data(), have) != have) {
+        LOG(ERROR) << "Failed to write " << have << " compressed bytes to output.";
+        return 0;
+      }
+      if (ctx) SHA1_Update(ctx, buffer.data(), have);
+    } while ((strm->avail_in != 0 || strm->avail_out == 0) && ret != Z_STREAM_END);
+
+    actual_target_length += len;
+    return len;
+  };
+
+  if (ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink, nullptr) != 0) {
+    return false;
+  }
+
+  if (ret != Z_STREAM_END) {
+    LOG(ERROR) << "ret is expected to be Z_STREAM_END, but it's " << ret;
+    return false;
+  }
+
+  if (expected_target_length != actual_target_length) {
+    LOG(ERROR) << "target length is expected to be " << expected_target_length << ", but it's "
+               << actual_target_length;
+    return false;
+  }
+  LOG(DEBUG) << "bspatch writes " << total_written << " bytes in total to streaming output.";
+
+  return true;
+}
+
 int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data,
                     size_t patch_size, SinkFn sink) {
   Value patch(VAL_BLOB, std::string(reinterpret_cast<const char*>(patch_data), patch_size));
@@ -137,12 +219,6 @@
       size_t src_len = static_cast<size_t>(Read8(deflate_header + 8));
       size_t patch_offset = static_cast<size_t>(Read8(deflate_header + 16));
       size_t expanded_len = static_cast<size_t>(Read8(deflate_header + 24));
-      size_t target_len = static_cast<size_t>(Read8(deflate_header + 32));
-      int level = Read4(deflate_header + 40);
-      int method = Read4(deflate_header + 44);
-      int windowBits = Read4(deflate_header + 48);
-      int memLevel = Read4(deflate_header + 52);
-      int strategy = Read4(deflate_header + 56);
 
       if (src_start + src_len > old_size) {
         printf("source data too short\n");
@@ -199,60 +275,12 @@
         }
       }
 
-      // Next, apply the bsdiff patch (in memory) to the uncompressed data.
-      std::vector<uint8_t> uncompressed_target_data;
-      // TODO: replace the custom sink function passed into ApplyBSDiffPatch so that it wraps the
-      // given sink function to stream output to save memory.
-      if (ApplyBSDiffPatch(expanded_source.data(), expanded_len, patch, patch_offset,
-        [&uncompressed_target_data](const uint8_t* data, size_t len) {
-          uncompressed_target_data.insert(uncompressed_target_data.end(), data, data + len);
-          return len;
-        }, nullptr) != 0) {
-        return -1;
-      }
-      if (uncompressed_target_data.size() != target_len) {
-        printf("expected target len to be %zu, but it's %zu\n", target_len,
-               uncompressed_target_data.size());
+      if (!ApplyBSDiffPatchAndStreamOutput(expanded_source.data(), expanded_len, patch,
+                                           patch_offset, deflate_header, sink, ctx)) {
+        LOG(ERROR) << "Fail to apply streaming bspatch.";
         return -1;
       }
 
-      // Now compress the target data and append it to the output.
-
-      // we're done with the expanded_source data buffer, so we'll
-      // reuse that memory to receive the output of deflate.
-      if (expanded_source.size() < 32768U) {
-        expanded_source.resize(32768U);
-      }
-
-      {
-        std::vector<unsigned char>& temp_data = expanded_source;
-
-        // now the deflate stream
-        z_stream strm;
-        strm.zalloc = Z_NULL;
-        strm.zfree = Z_NULL;
-        strm.opaque = Z_NULL;
-        strm.avail_in = uncompressed_target_data.size();
-        strm.next_in = uncompressed_target_data.data();
-        int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
-        if (ret != Z_OK) {
-          printf("failed to init uncompressed data deflation: %d\n", ret);
-          return -1;
-        }
-        do {
-          strm.avail_out = temp_data.size();
-          strm.next_out = temp_data.data();
-          ret = deflate(&strm, Z_FINISH);
-          size_t have = temp_data.size() - strm.avail_out;
-
-          if (sink(temp_data.data(), have) != have) {
-            printf("failed to write %zd compressed bytes to output\n", have);
-            return -1;
-          }
-          if (ctx) SHA1_Update(ctx, temp_data.data(), have);
-        } while (ret != Z_STREAM_END);
-        deflateEnd(&strm);
-      }
     } else {
       printf("patch chunk %d is unknown type %d\n", i, type);
       return -1;
diff --git a/error_code.h b/error_code.h
index 0e79c87..9fe047c 100644
--- a/error_code.h
+++ b/error_code.h
@@ -44,6 +44,7 @@
   kTune2FsFailure,
   kRebootFailure,
   kPackageExtractFileFailure,
+  kPatchApplicationFailure,
   kVendorFailure = 200
 };
 
diff --git a/install.cpp b/install.cpp
index a1f2e4f..db4ba93 100644
--- a/install.cpp
+++ b/install.cpp
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <atomic>
 #include <chrono>
 #include <condition_variable>
 #include <functional>
@@ -294,11 +295,12 @@
 }
 #endif  // !AB_OTA_UPDATER
 
-static void log_max_temperature(int* max_temperature) {
+static void log_max_temperature(int* max_temperature, const std::atomic<bool>& logger_finished) {
   CHECK(max_temperature != nullptr);
   std::mutex mtx;
   std::unique_lock<std::mutex> lck(mtx);
-  while (finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) {
+  while (!logger_finished.load() &&
+         finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) {
     *max_temperature = std::max(*max_temperature, GetMaxValueFromThermalZone());
   }
 }
@@ -403,7 +405,8 @@
   }
   close(pipefd[1]);
 
-  std::thread temperature_logger(log_max_temperature, max_temperature);
+  std::atomic<bool> logger_finished(false);
+  std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));
 
   *wipe_cache = false;
   bool retry_update = false;
@@ -467,6 +470,7 @@
   int status;
   waitpid(pid, &status, 0);
 
+  logger_finished.store(true);
   finish_log_temperature.notify_one();
   temperature_logger.join();
 
diff --git a/otautil/Android.bp b/otautil/Android.bp
index 0b23143..a2eaa04 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -18,7 +18,6 @@
     srcs: [
         "SysUtil.cpp",
         "DirUtil.cpp",
-        "ZipUtil.cpp",
         "ThermalUtil.cpp",
     ],
 
diff --git a/otautil/ZipUtil.cpp b/otautil/ZipUtil.cpp
deleted file mode 100644
index 9cc97e4..0000000
--- a/otautil/ZipUtil.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ZipUtil.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <utime.h>
-
-#include <string>
-
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <selinux/label.h>
-#include <selinux/selinux.h>
-#include <ziparchive/zip_archive.h>
-
-#include "DirUtil.h"
-
-static constexpr mode_t UNZIP_DIRMODE = 0755;
-static constexpr mode_t UNZIP_FILEMODE = 0644;
-
-bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path,
-                             const std::string& dest_path, const struct utimbuf* timestamp,
-                             struct selabel_handle* sehnd) {
-    if (!zip_path.empty() && zip_path[0] == '/') {
-        LOG(ERROR) << "ExtractPackageRecursive(): zip_path must be a relative path " << zip_path;
-        return false;
-    }
-    if (dest_path.empty() || dest_path[0] != '/') {
-        LOG(ERROR) << "ExtractPackageRecursive(): dest_path must be an absolute path " << dest_path;
-        return false;
-    }
-
-    void* cookie;
-    std::string target_dir(dest_path);
-    if (dest_path.back() != '/') {
-        target_dir += '/';
-    }
-    std::string prefix_path(zip_path);
-    if (!zip_path.empty() && zip_path.back() != '/') {
-        prefix_path += '/';
-    }
-    const ZipString zip_prefix(prefix_path.c_str());
-
-    int ret = StartIteration(zip, &cookie, &zip_prefix, nullptr);
-    if (ret != 0) {
-        LOG(ERROR) << "failed to start iterating zip entries.";
-        return false;
-    }
-
-    std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
-    ZipEntry entry;
-    ZipString name;
-    int extractCount = 0;
-    while (Next(cookie, &entry, &name) == 0) {
-        std::string entry_name(name.name, name.name + name.name_length);
-        CHECK_LE(prefix_path.size(), entry_name.size());
-        std::string path = target_dir + entry_name.substr(prefix_path.size());
-        // Skip dir.
-        if (path.back() == '/') {
-            continue;
-        }
-
-        if (dirCreateHierarchy(path.c_str(), UNZIP_DIRMODE, timestamp, true, sehnd) != 0) {
-            LOG(ERROR) << "failed to create dir for " << path;
-            return false;
-        }
-
-        char *secontext = NULL;
-        if (sehnd) {
-            selabel_lookup(sehnd, &secontext, path.c_str(), UNZIP_FILEMODE);
-            setfscreatecon(secontext);
-        }
-        android::base::unique_fd fd(open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, UNZIP_FILEMODE));
-        if (fd == -1) {
-            PLOG(ERROR) << "Can't create target file \"" << path << "\"";
-            return false;
-        }
-        if (secontext) {
-            freecon(secontext);
-            setfscreatecon(NULL);
-        }
-
-        int err = ExtractEntryToFile(zip, &entry, fd);
-        if (err != 0) {
-            LOG(ERROR) << "Error extracting \"" << path << "\" : " << ErrorCodeString(err);
-            return false;
-        }
-
-        if (fsync(fd) != 0) {
-            PLOG(ERROR) << "Error syncing file descriptor when extracting \"" << path << "\"";
-            return false;
-        }
-
-        if (timestamp != nullptr && utime(path.c_str(), timestamp)) {
-            PLOG(ERROR) << "Error touching \"" << path << "\"";
-            return false;
-        }
-
-        LOG(INFO) << "Extracted file \"" << path << "\"";
-        ++extractCount;
-    }
-
-    LOG(INFO) << "Extracted " << extractCount << " file(s)";
-    return true;
-}
diff --git a/otautil/ZipUtil.h b/otautil/ZipUtil.h
deleted file mode 100644
index cda405c..0000000
--- a/otautil/ZipUtil.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _OTAUTIL_ZIPUTIL_H
-#define _OTAUTIL_ZIPUTIL_H
-
-#include <utime.h>
-
-#include <string>
-
-#include <selinux/label.h>
-#include <ziparchive/zip_archive.h>
-
-/*
- * Inflate all files under zip_path to the directory specified by
- * dest_path, which must exist and be a writable directory. The zip_path
- * is allowed to be an empty string, in which case the whole package
- * will be extracted.
- *
- * Directory entries are not extracted.
- *
- * The immediate children of zip_path will become the immediate
- * children of dest_path; e.g., if the archive contains the entries
- *
- *     a/b/c/one
- *     a/b/c/two
- *     a/b/c/d/three
- *
- * and ExtractPackageRecursive(a, "a/b/c", "/tmp", ...) is called, the resulting
- * files will be
- *
- *     /tmp/one
- *     /tmp/two
- *     /tmp/d/three
- *
- * If timestamp is non-NULL, file timestamps will be set accordingly.
- *
- * Returns true on success, false on failure.
- */
-bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path,
-                             const std::string& dest_path, const struct utimbuf* timestamp,
-                             struct selabel_handle* sehnd);
-
-#endif // _OTAUTIL_ZIPUTIL_H
diff --git a/recovery.cpp b/recovery.cpp
index dfae7f0..122b89d 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -112,8 +112,9 @@
 static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
 static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
 static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
-// We will try to apply the update package 5 times at most in case of an I/O error.
-static const int EIO_RETRY_COUNT = 4;
+// We will try to apply the update package 5 times at most in case of an I/O error or
+// bspatch | imgpatch error.
+static const int RETRY_LIMIT = 4;
 static const int BATTERY_READ_TIMEOUT_IN_SEC = 10;
 // GmsCore enters recovery mode to install package when having enough battery
 // percentage. Normally, the threshold is 40% without charger and 20% with charger.
@@ -1530,9 +1531,9 @@
             }
             if (status != INSTALL_SUCCESS) {
                 ui->Print("Installation aborted.\n");
-                // When I/O error happens, reboot and retry installation EIO_RETRY_COUNT
+                // When I/O error happens, reboot and retry installation RETRY_LIMIT
                 // times before we abandon this OTA update.
-                if (status == INSTALL_RETRY && retry_count < EIO_RETRY_COUNT) {
+                if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) {
                     copy_logs();
                     set_retry_bootloader_message(retry_count, args);
                     // Print retry count on screen.
diff --git a/screen_ui.cpp b/screen_ui.cpp
index bb2772d..61ef591 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -256,6 +256,10 @@
     *y += 4;
 }
 
+void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
+    gr_fill(x, y, x + width, y + height);
+}
+
 void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const {
     gr_text(gr_sys_font(), x, *y, line, bold);
     *y += char_height_ + 4;
@@ -310,15 +314,14 @@
                 if (i == menu_sel) {
                     // Draw the highlight bar.
                     SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG);
-                    gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2);
+                    DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4);
                     // Bold white text for the selected item.
                     SetColor(MENU_SEL_FG);
-                    gr_text(gr_sys_font(), 4, y, menu_[i], true);
+                    DrawTextLine(TEXT_INDENT, &y, menu_[i], true);
                     SetColor(MENU);
                 } else {
-                    gr_text(gr_sys_font(), 4, y, menu_[i], false);
+                    DrawTextLine(TEXT_INDENT, &y, menu_[i], false);
                 }
-                y += char_height_ + 4;
             }
             DrawHorizontalRule(&y);
         }
@@ -329,10 +332,11 @@
         SetColor(LOG);
         int row = (text_top_ + text_rows_ - 1) % text_rows_;
         size_t count = 0;
-        for (int ty = gr_fb_height() - char_height_;
+        for (int ty = gr_fb_height() - char_height_ - log_bottom_offset_;
              ty >= y && count < text_rows_;
              ty -= char_height_, ++count) {
-            gr_text(gr_sys_font(), 0, ty, text_[row], false);
+            int temp_y = ty;
+            DrawTextLine(0, &temp_y, text_[row], false);
             --row;
             if (row < 0) row = text_rows_ - 1;
         }
@@ -453,6 +457,7 @@
     gr_font_size(gr_sys_font(), &char_width_, &char_height_);
     text_rows_ = gr_fb_height() / char_height_;
     text_cols_ = gr_fb_width() / char_width_;
+    log_bottom_offset_ = 0;
     return true;
 }
 
diff --git a/screen_ui.h b/screen_ui.h
index a2322c3..bd99254 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -107,6 +107,7 @@
     // Log text overlay, displayed when a magic key is pressed.
     char** text_;
     size_t text_col_, text_row_, text_top_;
+    int log_bottom_offset_;
 
     bool show_text;
     bool show_text_ever;   // has show_text ever been true?
@@ -165,8 +166,9 @@
     virtual int GetProgressBaseline();
     virtual int GetTextBaseline();
 
-    void DrawHorizontalRule(int* y);
-    void DrawTextLine(int x, int* y, const char* line, bool bold) const;
+    virtual void DrawHorizontalRule(int* y);
+    virtual void DrawHighlightBar(int x, int y, int width, int height) const;
+    virtual void DrawTextLine(int x, int* y, const char* line, bool bold) const;
     void DrawTextLines(int x, int* y, const char* const* lines) const;
 };
 
diff --git a/tests/Android.mk b/tests/Android.mk
index 4ee59b5..346873d 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -39,7 +39,6 @@
     unit/rangeset_test.cpp \
     unit/sysutil_test.cpp \
     unit/zip_test.cpp \
-    unit/ziputil_test.cpp
 
 LOCAL_C_INCLUDES := bootable/recovery
 LOCAL_SHARED_LIBRARIES := liblog
diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp
index df4e38c..8276685 100644
--- a/tests/unit/zip_test.cpp
+++ b/tests/unit/zip_test.cpp
@@ -24,47 +24,10 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 #include <otautil/SysUtil.h>
-#include <otautil/ZipUtil.h>
 #include <ziparchive/zip_archive.h>
 
 #include "common/test_constants.h"
 
-TEST(ZipTest, ExtractPackageRecursive) {
-  std::string zip_path = from_testdata_base("ziptest_valid.zip");
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
-
-  // Extract the whole package into a temp directory.
-  TemporaryDir td;
-  ASSERT_NE(nullptr, td.path);
-  ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr);
-
-  // Make sure all the files are extracted correctly.
-  std::string path(td.path);
-  ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK));
-  ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK));
-  ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK));
-  ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK));
-
-  // The content of the file is the same as expected.
-  std::string content1;
-  ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1));
-  ASSERT_EQ(kATxtContents, content1);
-
-  std::string content2;
-  ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2));
-  ASSERT_EQ(kDTxtContents, content2);
-
-  CloseArchive(handle);
-
-  // Clean up.
-  ASSERT_EQ(0, unlink((path + "/a.txt").c_str()));
-  ASSERT_EQ(0, unlink((path + "/b.txt").c_str()));
-  ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str()));
-  ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str()));
-  ASSERT_EQ(0, rmdir((path + "/b").c_str()));
-}
-
 TEST(ZipTest, OpenFromMemory) {
   std::string zip_path = from_testdata_base("ziptest_dummy-update.zip");
   MemMapping map;
diff --git a/tests/unit/ziputil_test.cpp b/tests/unit/ziputil_test.cpp
deleted file mode 100644
index 14e5416..0000000
--- a/tests/unit/ziputil_test.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/test_utils.h>
-#include <gtest/gtest.h>
-#include <otautil/ZipUtil.h>
-#include <ziparchive/zip_archive.h>
-
-#include "common/test_constants.h"
-
-TEST(ZipUtilTest, invalid_args) {
-  std::string zip_path = from_testdata_base("ziptest_valid.zip");
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
-
-  // zip_path must be a relative path.
-  ASSERT_FALSE(ExtractPackageRecursive(handle, "/a/b", "/tmp", nullptr, nullptr));
-
-  // dest_path must be an absolute path.
-  ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "tmp", nullptr, nullptr));
-  ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "", nullptr, nullptr));
-
-  CloseArchive(handle);
-}
-
-TEST(ZipUtilTest, extract_all) {
-  std::string zip_path = from_testdata_base("ziptest_valid.zip");
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
-
-  // Extract the whole package into a temp directory.
-  TemporaryDir td;
-  ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr);
-
-  // Make sure all the files are extracted correctly.
-  std::string path(td.path);
-  ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK));
-  ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK));
-  ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK));
-  ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK));
-
-  // The content of the file is the same as expected.
-  std::string content1;
-  ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1));
-  ASSERT_EQ(kATxtContents, content1);
-
-  std::string content2;
-  ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2));
-  ASSERT_EQ(kDTxtContents, content2);
-
-  // Clean up the temp files under td.
-  ASSERT_EQ(0, unlink((path + "/a.txt").c_str()));
-  ASSERT_EQ(0, unlink((path + "/b.txt").c_str()));
-  ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str()));
-  ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str()));
-  ASSERT_EQ(0, rmdir((path + "/b").c_str()));
-
-  CloseArchive(handle);
-}
-
-TEST(ZipUtilTest, extract_prefix_with_slash) {
-  std::string zip_path = from_testdata_base("ziptest_valid.zip");
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
-
-  // Extract all the entries starting with "b/".
-  TemporaryDir td;
-  ExtractPackageRecursive(handle, "b/", td.path, nullptr, nullptr);
-
-  // Make sure all the files with "b/" prefix are extracted correctly.
-  std::string path(td.path);
-  ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK));
-  ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK));
-
-  // And the rest are not extracted.
-  ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK));
-  ASSERT_EQ(ENOENT, errno);
-  ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK));
-  ASSERT_EQ(ENOENT, errno);
-
-  // The content of the file is the same as expected.
-  std::string content1;
-  ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1));
-  ASSERT_EQ(kCTxtContents, content1);
-
-  std::string content2;
-  ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2));
-  ASSERT_EQ(kDTxtContents, content2);
-
-  // Clean up the temp files under td.
-  ASSERT_EQ(0, unlink((path + "/c.txt").c_str()));
-  ASSERT_EQ(0, unlink((path + "/d.txt").c_str()));
-
-  CloseArchive(handle);
-}
-
-TEST(ZipUtilTest, extract_prefix_without_slash) {
-  std::string zip_path = from_testdata_base("ziptest_valid.zip");
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
-
-  // Extract all the file entries starting with "b/".
-  TemporaryDir td;
-  ExtractPackageRecursive(handle, "b", td.path, nullptr, nullptr);
-
-  // Make sure all the files with "b/" prefix are extracted correctly.
-  std::string path(td.path);
-  ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK));
-  ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK));
-
-  // And the rest are not extracted.
-  ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK));
-  ASSERT_EQ(ENOENT, errno);
-  ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK));
-  ASSERT_EQ(ENOENT, errno);
-
-  // The content of the file is the same as expected.
-  std::string content1;
-  ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1));
-  ASSERT_EQ(kCTxtContents, content1);
-
-  std::string content2;
-  ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2));
-  ASSERT_EQ(kDTxtContents, content2);
-
-  // Clean up the temp files under td.
-  ASSERT_EQ(0, unlink((path + "/c.txt").c_str()));
-  ASSERT_EQ(0, unlink((path + "/d.txt").c_str()));
-
-  CloseArchive(handle);
-}
-
-TEST(ZipUtilTest, set_timestamp) {
-  std::string zip_path = from_testdata_base("ziptest_valid.zip");
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
-
-  // Set the timestamp to 8/1/2008.
-  constexpr struct utimbuf timestamp = { 1217592000, 1217592000 };
-
-  // Extract all the entries starting with "b/".
-  TemporaryDir td;
-  ExtractPackageRecursive(handle, "b", td.path, &timestamp, nullptr);
-
-  // Make sure all the files with "b/" prefix are extracted correctly.
-  std::string path(td.path);
-  std::string file_c = path + "/c.txt";
-  std::string file_d = path + "/d.txt";
-  ASSERT_EQ(0, access(file_c.c_str(), F_OK));
-  ASSERT_EQ(0, access(file_d.c_str(), F_OK));
-
-  // Verify the timestamp.
-  timespec time;
-  time.tv_sec = 1217592000;
-  time.tv_nsec = 0;
-
-  struct stat sb;
-  ASSERT_EQ(0, stat(file_c.c_str(), &sb)) << strerror(errno);
-  ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
-  ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
-
-  ASSERT_EQ(0, stat(file_d.c_str(), &sb)) << strerror(errno);
-  ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
-  ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
-
-  // Clean up the temp files under td.
-  ASSERT_EQ(0, unlink(file_c.c_str()));
-  ASSERT_EQ(0, unlink(file_d.c_str()));
-
-  CloseArchive(handle);
-}
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index d5c1704..df366b0 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -1213,6 +1213,7 @@
                                       std::placeholders::_2),
                             nullptr, nullptr) != 0) {
           LOG(ERROR) << "Failed to apply image patch.";
+          failure_type = kPatchApplicationFailure;
           return -1;
         }
       } else {
@@ -1221,6 +1222,7 @@
                                        std::placeholders::_2),
                              nullptr) != 0) {
           LOG(ERROR) << "Failed to apply bsdiff patch.";
+          failure_type = kPatchApplicationFailure;
           return -1;
         }
       }
diff --git a/updater/install.cpp b/updater/install.cpp
index c5f9a89..ff79edc 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -61,7 +61,6 @@
 #include "mounts.h"
 #include "ota_io.h"
 #include "otautil/DirUtil.h"
-#include "otautil/ZipUtil.h"
 #include "print_sha1.h"
 #include "tune2fs.h"
 #include "updater/updater.h"
@@ -389,36 +388,6 @@
   return StringValue(frac_str);
 }
 
-// package_extract_dir(package_dir, dest_dir)
-//   Extracts all files from the package underneath package_dir and writes them to the
-//   corresponding tree beneath dest_dir. Any existing files are overwritten.
-//   Example: package_extract_dir("system", "/system")
-//
-//   Note: package_dir needs to be a relative path; dest_dir needs to be an absolute path.
-Value* PackageExtractDirFn(const char* name, State* state,
-                           const std::vector<std::unique_ptr<Expr>>&argv) {
-  if (argv.size() != 2) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
-                      argv.size());
-  }
-
-  std::vector<std::string> args;
-  if (!ReadArgs(state, argv, &args)) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
-  }
-  const std::string& zip_path = args[0];
-  const std::string& dest_path = args[1];
-
-  ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
-
-  // To create a consistent system image, never use the clock for timestamps.
-  constexpr struct utimbuf timestamp = { 1217592000, 1217592000 };  // 8/1/2008 default
-
-  bool success = ExtractPackageRecursive(za, zip_path, dest_path, &timestamp, sehandle);
-
-  return StringValue(success ? "t" : "");
-}
-
 // package_extract_file(package_file[, dest_file])
 //   Extracts a single package_file from the update package and writes it to dest_file,
 //   overwriting existing files if necessary. Without the dest_file argument, returns the
@@ -1037,7 +1006,6 @@
   RegisterFunction("format", FormatFn);
   RegisterFunction("show_progress", ShowProgressFn);
   RegisterFunction("set_progress", SetProgressFn);
-  RegisterFunction("package_extract_dir", PackageExtractDirFn);
   RegisterFunction("package_extract_file", PackageExtractFileFn);
 
   RegisterFunction("getprop", GetPropFn);
diff --git a/updater/updater.cpp b/updater/updater.cpp
index 1be8b60..f5ff6df 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -202,6 +202,10 @@
     // Cause code should provide additional information about the abort.
     if (state.cause_code != kNoCause) {
       fprintf(cmd_pipe, "log cause: %d\n", state.cause_code);
+      if (state.cause_code == kPatchApplicationFailure) {
+        LOG(INFO) << "Patch application failed, retry update.";
+        fprintf(cmd_pipe, "retry_update\n");
+      }
     }
 
     if (updater_info.package_zip) {
diff --git a/vr_device.cpp b/vr_device.cpp
new file mode 100644
index 0000000..61e15cb
--- /dev/null
+++ b/vr_device.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "device.h"
+#include "vr_ui.h"
+
+Device* make_device() {
+    return new Device(new VrRecoveryUI);
+}
+
diff --git a/vr_ui.cpp b/vr_ui.cpp
new file mode 100644
index 0000000..b2c65e3
--- /dev/null
+++ b/vr_ui.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vr_ui.h"
+
+#include <minui/minui.h>
+
+VrRecoveryUI::VrRecoveryUI() :
+  x_offset(400),
+  y_offset(400),
+  stereo_offset(100) {
+}
+
+bool VrRecoveryUI::InitTextParams() {
+  if (gr_init() < 0) {
+    return false;
+  }
+
+  gr_font_size(gr_sys_font(), &char_width_, &char_height_);
+  int mid_divide = gr_fb_width() / 2;
+  text_rows_ = (gr_fb_height() - 2 * y_offset) / char_height_;
+  text_cols_ = (mid_divide - x_offset - stereo_offset) / char_width_;
+  log_bottom_offset_ = gr_fb_height() - 2 * y_offset;
+  return true;
+}
+
+void VrRecoveryUI::DrawHorizontalRule(int* y) {
+  SetColor(MENU);
+  *y += 4;
+  gr_fill(0, *y + y_offset, gr_fb_width(), *y + y_offset + 2);
+  *y += 4;
+}
+
+void VrRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
+  gr_fill(x, y + y_offset, x + width, y + y_offset + height);
+}
+
+void VrRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const {
+  int mid_divide = gr_fb_width() / 2;
+  gr_text(gr_sys_font(), x + x_offset + stereo_offset, *y + y_offset, line, bold);
+  gr_text(gr_sys_font(), x + x_offset - stereo_offset + mid_divide, *y + y_offset, line, bold);
+  *y += char_height_ + 4;
+}
diff --git a/vr_ui.h b/vr_ui.h
new file mode 100644
index 0000000..85c5708
--- /dev/null
+++ b/vr_ui.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RECOVERY_VR_UI_H
+#define RECOVERY_VR_UI_H
+
+#include "screen_ui.h"
+
+class VrRecoveryUI : public ScreenRecoveryUI {
+  public:
+    VrRecoveryUI();
+
+  protected:
+    // Pixel offsets to move drawing functions to visible range.
+    // Can vary per device depending on screen size and lens distortion.
+    int x_offset, y_offset, stereo_offset;
+
+    bool InitTextParams() override;
+
+    void DrawHorizontalRule(int* y) override;
+    void DrawHighlightBar(int x, int y, int width, int height) const override;
+    void DrawTextLine(int x, int* y, const char* line, bool bold) const override;
+};
+
+#endif  // RECOVERY_VR_UI_H