Update to AOSP 8.0 base

Change-Id: I29fe722b4eb9718765327902779046840a01433e
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..5322788
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,15 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: true
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+PointerAlignment: Left
+TabWidth: 2
+UseTab: Never
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/Android.mk b/Android.mk
index 85cdd32..7338adf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -104,24 +104,25 @@
 
 LOCAL_C_INCLUDES += \
     system/vold \
-    system/extras/ext4_utils \
+    system/extras \
     system/core/adb \
     system/core/libsparse \
-    external/zlib
+    external/zlib \
+    $(LOCAL_PATH)/bootloader_message_twrp/include
 
 LOCAL_C_INCLUDES += bionic
 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
     LOCAL_C_INCLUDES += external/stlport/stlport external/openssl/include
 else
-    LOCAL_C_INCLUDES += external/boringssl/include
+    LOCAL_C_INCLUDES += external/boringssl/include external/libcxx/include
 endif
 
 LOCAL_STATIC_LIBRARIES :=
 LOCAL_SHARED_LIBRARIES :=
 
 LOCAL_STATIC_LIBRARIES += libguitwrp
-LOCAL_SHARED_LIBRARIES += libaosprecovery libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libtwadbbu libbootloader_message
-LOCAL_SHARED_LIBRARIES += libcrecovery libtwadbbu libtwrpdigest
+LOCAL_SHARED_LIBRARIES += libaosprecovery libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libtwadbbu libbootloader_message_twrp
+LOCAL_SHARED_LIBRARIES += libcrecovery libtwadbbu libtwrpdigest libc++
 
 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
     LOCAL_SHARED_LIBRARIES += libstlport
@@ -132,13 +133,20 @@
     LOCAL_C_INCLUDES += $(LOCAL_PATH)/libmincrypt/includes
     LOCAL_CFLAGS += -DUSE_OLD_VERIFIER
 else
-    LOCAL_SHARED_LIBRARIES += libc++ libcrypto
+    LOCAL_SHARED_LIBRARIES += libcrypto
 endif
 
 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0)
     LOCAL_SHARED_LIBRARIES += libbase
 endif
 
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+   LOCAL_SHARED_LIBRARIES += libziparchive
+else
+    LOCAL_SHARED_LIBRARIES += libminzip
+    LOCAL_CFLAGS += -DUSE_MINZIP
+endif
+
 ifneq ($(wildcard system/core/libsparse/Android.mk),)
 LOCAL_SHARED_LIBRARIES += libsparse
 endif
@@ -161,14 +169,14 @@
 endif
 LOCAL_C_INCLUDES += external/libselinux/include
 LOCAL_SHARED_LIBRARIES += libselinux
-ifneq ($(TARGET_USERIMAGES_USE_EXT4), true)
-    LOCAL_CFLAGS += -DUSE_EXT4
-    LOCAL_C_INCLUDES += system/extras/ext4_utils
-    LOCAL_SHARED_LIBRARIES += libext4_utils
-    ifneq ($(wildcard external/lz4/Android.mk),)
-        LOCAL_STATIC_LIBRARIES += liblz4
-    endif
-endif
+#ifneq ($(TARGET_USERIMAGES_USE_EXT4), true)
+#    LOCAL_CFLAGS += -DUSE_EXT4
+#    LOCAL_C_INCLUDES += system/extras/ext4_utils
+#    LOCAL_SHARED_LIBRARIES += libext4_utils
+#    ifneq ($(wildcard external/lz4/Android.mk),)
+#        LOCAL_STATIC_LIBRARIES += liblz4
+#    endif
+#endif
 
 ifeq ($(AB_OTA_UPDATER),true)
     LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
@@ -304,7 +312,7 @@
     endif
 endif
 WITH_CRYPTO_UTILS := \
-    $(if $(wildcard system/core/libcrypto_utils/Android.mk),true)
+    $(if $(wildcard system/core/libcrypto_utils/android_pubkey.c),true)
 ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true)
     LOCAL_CFLAGS += -DTW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
 endif
@@ -370,7 +378,8 @@
     mkfs.fat \
     permissive.sh \
     simg2img_twrp \
-    libbootloader_message \
+    libbootloader_message_twrp \
+    init.recovery.hlthchrg.rc \
     init.recovery.service.rc
 
 ifneq ($(TARGET_ARCH), arm64)
@@ -589,6 +598,37 @@
 endif
 include $(BUILD_SHARED_LIBRARY)
 
+# libmounts (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := mounts.cpp
+LOCAL_CLANG := true
+LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
+LOCAL_MODULE := libmounts
+include $(BUILD_STATIC_LIBRARY)
+
+# librecovery (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    install.cpp
+LOCAL_CFLAGS := -Wno-unused-parameter -Werror
+LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+
+ifeq ($(AB_OTA_UPDATER),true)
+    LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
+endif
+
+LOCAL_MODULE := librecovery
+LOCAL_STATIC_LIBRARIES := \
+    libminui \
+    libvintf_recovery \
+    libcrypto_utils \
+    libcrypto \
+    libbase
+
+include $(BUILD_STATIC_LIBRARY)
+
 # shared libaosprecovery for Apache code
 # ===============================
 include $(CLEAR_VARS)
@@ -596,7 +636,7 @@
 LOCAL_MODULE := libaosprecovery
 LOCAL_MODULE_TAGS := eng optional
 LOCAL_CFLAGS := -std=gnu++0x
-LOCAL_SRC_FILES := adb_install.cpp asn1_decoder.cpp legacy_property_service.cpp set_metadata.cpp tw_atomic.cpp installcommand.cpp
+LOCAL_SRC_FILES := adb_install.cpp legacy_property_service.cpp set_metadata.cpp tw_atomic.cpp installcommand.cpp zipwrap.cpp
 LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils libfusesideload libselinux libminzip
 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
@@ -608,15 +648,23 @@
 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0)
     LOCAL_SHARED_LIBRARIES += libmincrypttwrp
     LOCAL_C_INCLUDES += $(LOCAL_PATH)/libmincrypt/includes
-    LOCAL_SRC_FILES += verifier24/verifier.cpp
+    LOCAL_SRC_FILES += verifier24/verifier.cpp verifier24/asn1_decoder.cpp
     LOCAL_CFLAGS += -DUSE_OLD_VERIFIER
 else
     LOCAL_SHARED_LIBRARIES += libcrypto libbase
-    LOCAL_SRC_FILES += verifier.cpp
+    LOCAL_SRC_FILES += verifier.cpp asn1_decoder.cpp
 endif
 ifeq ($(AB_OTA_UPDATER),true)
     LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
 endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+    LOCAL_SRC_FILES += otautil/ZipUtil.cpp otautil/SysUtil.cpp
+    LOCAL_SHARED_LIBRARIES += libziparchive libext4_utils libcrypto libcrypto_utils
+    LOCAL_STATIC_LIBRARIES += libvintf_recovery libfs_mgr liblogwrap libavb libvintf libtinyxml2 libz
+    LOCAL_WHOLE_STATIC_LIBRARIES +=
+else
+    LOCAL_CFLAGS += -DUSE_MINZIP
+endif
 
 include $(BUILD_SHARED_LIBRARY)
 
@@ -638,6 +686,7 @@
     $(LOCAL_PATH)/edify/Android.mk \
     $(LOCAL_PATH)/otafault/Android.mk \
     $(LOCAL_PATH)/bootloader_message/Android.mk \
+    $(LOCAL_PATH)/bootloader_message_twrp/Android.mk \
     $(LOCAL_PATH)/updater/Android.mk \
     $(LOCAL_PATH)/update_verifier/Android.mk \
     $(LOCAL_PATH)/applypatch/Android.mk
@@ -647,6 +696,10 @@
 endif
 
 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0)
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0)
+        TARGET_GLOBAL_CFLAGS += -DTW_USE_MINUI_WITH_DATA
+        CLANG_TARGET_GLOBAL_CFLAGS += -DTW_USE_MINUI_WITH_DATA
+    endif
     include $(commands_recovery_local_path)/minadbd/Android.mk \
         $(commands_recovery_local_path)/minui/Android.mk
 else
@@ -669,6 +722,7 @@
     $(commands_recovery_local_path)/libcrecovery/Android.mk \
     $(commands_recovery_local_path)/libblkid/Android.mk \
     $(commands_recovery_local_path)/minuitwrp/Android.mk \
+    $(commands_recovery_local_path)/otautil/Android.mk \
     $(commands_recovery_local_path)/openaes/Android.mk \
     $(commands_recovery_local_path)/toolbox/Android.mk \
     $(commands_recovery_local_path)/twrpTarMain/Android.mk \
diff --git a/README.md b/README.md
index 96f3789..820a8b7 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,3 @@
 **Team Win Recovery Project (TWRP)**
 
 You can find a compiling guide [here](http://forum.xda-developers.com/showthread.php?t=1943625 "Guide").
-
diff --git a/adb_install.cpp b/adb_install.cpp
index 448d617..771994c 100644
--- a/adb_install.cpp
+++ b/adb_install.cpp
@@ -28,6 +28,8 @@
 
 #include "ui.h"
 #include "cutils/properties.h"
+#include "install.h"
+#include "common.h"
 #include "adb_install.h"
 #include "minadbd/fuse_adb_provider.h"
 #include "fuse_sideload.h"
@@ -37,10 +39,7 @@
 #include "verifier.h"
 #endif
 
-static RecoveryUI* ui = NULL;
-
-void
-set_usb_driver(bool enabled) {
+static void set_usb_driver(bool enabled) {
     int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY);
     if (fd < 0) {
 /* These error messages show when built in older Android branches (e.g. Gingerbread)
@@ -65,20 +64,18 @@
     }
 }
 
-static void
-stop_adbd() {
+static void stop_adbd() {
+    printf("Stopping adbd...\n");
     property_set("ctl.stop", "adbd");
     set_usb_driver(false);
 }
 
-bool is_ro_debuggable() {
+static bool is_ro_debuggable() {
     char value[PROPERTY_VALUE_MAX+1];
     return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1');
 }
 
-void
-maybe_restart_adbd() {
-    char value[PROPERTY_VALUE_MAX+1];
+static void maybe_restart_adbd() {
     if (is_ro_debuggable()) {
         printf("Restarting adbd...\n");
         set_usb_driver(true);
@@ -96,6 +93,12 @@
     stop_adbd();
     set_usb_driver(true);
 /*
+int apply_from_adb(RecoveryUI* ui, bool* wipe_cache, const char* install_file) {
+    modified_flash = true;
+
+    stop_adbd(ui);
+    set_usb_driver(ui, true);
+
     ui->Print("\n\nNow send the package you want to apply\n"
               "to the device with \"adb sideload <filename>\"...\n");
 */
diff --git a/adb_install.h b/adb_install.h
index 24e9e21..e9e88f7 100644
--- a/adb_install.h
+++ b/adb_install.h
@@ -19,8 +19,8 @@
 
 //class RecoveryUI;
 
-void set_usb_driver(bool enabled);
-void maybe_restart_adbd();
+static void set_usb_driver(bool enabled);
+static void maybe_restart_adbd();
 int apply_from_adb(const char* install_file, pid_t* child_pid);
 
 #endif
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index 2f96f0a..9cbe3e2 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -31,6 +31,7 @@
   )
 
 LOCAL_C_INCLUDES += \
+    $(LOCAL_PATH)/include \
     external/bzip2 \
     external/zlib \
     $(commands_recovery_local_path)
@@ -42,27 +43,99 @@
 LOCAL_C_INCLUDES += $(RECOVERY_PATH)
 LOCAL_STATIC_LIBRARIES += libbase libotafault libmtdutils libcrypto_static libbz libz
 
+# libapplypatch (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    applypatch.cpp \
+    bspatch.cpp \
+    freecache.cpp \
+    imgpatch.cpp
+LOCAL_MODULE := libapplypatch
+LOCAL_MODULE_TAGS := eng
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+    libotafault \
+    libbase \
+    libcrypto \
+    libbspatch \
+    libbz \
+    libz
+LOCAL_WHOLE_STATIC_LIBRARIES += libmtdutils
+LOCAL_CFLAGS := \
+    -DZLIB_CONST \
+    -Werror
 include $(BUILD_STATIC_LIBRARY)
 
+# libimgpatch (static library)
+# ===============================
 include $(CLEAR_VARS)
 
-ifeq ($(HOST_OS),linux)
-include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    bspatch.cpp \
+    imgpatch.cpp
+LOCAL_MODULE := libimgpatch
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+    libcrypto \
+    libbspatch \
+    libbase \
+    libbz \
+    libz
+LOCAL_CFLAGS := \
+    -DZLIB_CONST \
+    -Werror
+include $(BUILD_STATIC_LIBRARY)
 
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp
+# libimgpatch (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    bspatch.cpp \
+    imgpatch.cpp
 LOCAL_MODULE := libimgpatch
 LOCAL_C_INCLUDES += $(RECOVERY_PATH)
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    $(commands_recovery_local_path)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz
-
+LOCAL_STATIC_LIBRARIES := \
+    libcrypto \
+    libbspatch \
+    libbase \
+    libbz \
+    libz
+LOCAL_CFLAGS := \
+    -DZLIB_CONST \
+    -Werror
 include $(BUILD_HOST_STATIC_LIBRARY)
-endif  # HOST_OS == linux
 
+# libapplypatch_modes (static library)
+# ===============================
 include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    applypatch_modes.cpp
+LOCAL_MODULE := libapplypatch_modes
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+    libapplypatch \
+    libbase \
+    libedify \
+    libcrypto
+LOCAL_CFLAGS := -Werror
+include $(BUILD_STATIC_LIBRARY)
 
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := main.cpp
+# applypatch (target executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := applypatch_main.cpp
 LOCAL_MODULE := applypatch
 LOCAL_C_INCLUDES += $(RECOVERY_PATH)
 LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libmtdutils libcrypto_static libbz \
@@ -70,15 +143,78 @@
 
 LOCAL_SHARED_LIBRARIES += libz libcutils libc
 
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+    libapplypatch_modes \
+    libapplypatch \
+    libbase \
+    libedify \
+    libotafault \
+    libcrypto \
+    libbspatch \
+    libbz
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libz \
+    libcutils
+LOCAL_CFLAGS := -Werror
 include $(BUILD_EXECUTABLE)
 
+libimgdiff_src_files := imgdiff.cpp
+
+# libbsdiff is compiled with -D_FILE_OFFSET_BITS=64.
+libimgdiff_cflags := \
+    -Werror \
+    -D_FILE_OFFSET_BITS=64
+
+libimgdiff_static_libraries := \
+    libbsdiff \
+    libdivsufsort \
+    libdivsufsort64 \
+    libziparchive \
+    libutils \
+    liblog \
+    libbase \
+    libz
+
+# libimgdiff (static library)
+# ===============================
 include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+    $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+    $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
 
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := imgdiff.cpp utils.cpp bsdiff.cpp
+# libimgdiff (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+    $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+    $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# imgdiff (host static executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := imgdiff_main.cpp
 LOCAL_MODULE := imgdiff
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_C_INCLUDES += external/zlib external/bzip2
-LOCAL_STATIC_LIBRARIES += libz libbz
-
+LOCAL_CFLAGS := -Werror
+LOCAL_STATIC_LIBRARIES := \
+    libimgdiff \
+    $(libimgdiff_static_libraries) \
+    libbz
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/applypatch/Makefile b/applypatch/Makefile
new file mode 100644
index 0000000..fb49843
--- /dev/null
+++ b/applypatch/Makefile
@@ -0,0 +1,33 @@
+# 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.
+
+# This file is for building imgdiff in Chrome OS.
+
+CPPFLAGS += -iquote.. -Iinclude
+CXXFLAGS += -std=c++11 -O3 -Wall -Werror
+LDLIBS += -lbz2 -lz
+
+.PHONY: all clean
+
+all: imgdiff libimgpatch.a
+
+clean:
+	rm -f *.o imgdiff libimgpatch.a
+
+imgdiff: imgdiff.o bsdiff.o utils.o
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDLIBS) -o $@ $^
+
+libimgpatch.a utils.o: CXXFLAGS += -fPIC
+libimgpatch.a: imgpatch.o bspatch.o utils.o
+	${AR} rcs $@ $^
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index c2fe11b..54c37eb 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "applypatch/applypatch.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <libgen.h>
@@ -27,72 +29,65 @@
 
 #include <memory>
 #include <string>
+#include <utility>
+#include <vector>
 
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/strings.h>
+#include <openssl/sha.h>
 
-#include "openssl/sha.h"
-#include "applypatch.h"
 #include "bmlutils/bmlutils.h"
 #include "mtdutils/mtdutils.h"
+
 #include "edify/expr.h"
 #include "ota_io.h"
 #include "print_sha1.h"
 
-static int LoadPartitionContents(const char* filename, FileContents* file);
+static int LoadPartitionContents(const std::string& filename, FileContents* file);
 static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token);
-static int GenerateTarget(FileContents* source_file,
-                          const Value* source_patch_value,
-                          FileContents* copy_file,
-                          const Value* copy_patch_value,
-                          const char* source_filename,
-                          const char* target_filename,
-                          const uint8_t target_sha1[SHA_DIGEST_LENGTH],
-                          size_t target_size,
-                          const Value* bonus_data);
+static int GenerateTarget(const FileContents& source_file, const std::unique_ptr<Value>& patch,
+                          const std::string& target_filename,
+                          const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data);
 
 static bool mtd_partitions_scanned = false;
 
-// Read a file into memory; store the file contents and associated
-// metadata in *file.
-//
+// Read a file into memory; store the file contents and associated metadata in *file.
 // Return 0 on success.
 int LoadFileContents(const char* filename, FileContents* file) {
-    // A special 'filename' beginning with "MTD:" or "EMMC:" means to
-    // load the contents of a partition.
-    if (strncmp(filename, "MTD:", 4) == 0 ||
-        strncmp(filename, "EMMC:", 5) == 0 ||
-        strncmp(filename, "BML:", 4) == 0) {
-        return LoadPartitionContents(filename, file);
-    }
+  // A special 'filename' beginning with "MTD:" or "EMMC:" means to
+  // load the contents of a partition.
+  if (strncmp(filename, "MTD:", 4) == 0 ||
+    strncmp(filename, "EMMC:", 5) == 0 ||
+    strncmp(filename, "BML:", 4) == 0) {
+    return LoadPartitionContents(filename, file);
+  }
 
-    if (stat(filename, &file->st) != 0) {
-        printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
-        return -1;
-    }
+  if (stat(filename, &file->st) == -1) {
+    printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
+    return -1;
+  }
 
-    std::vector<unsigned char> data(file->st.st_size);
-    FILE* f = ota_fopen(filename, "rb");
-    if (f == NULL) {
-        printf("failed to open \"%s\": %s\n", filename, strerror(errno));
-        return -1;
-    }
+  std::vector<unsigned char> data(file->st.st_size);
+  unique_file f(ota_fopen(filename, "rb"));
+  if (!f) {
+    printf("failed to open \"%s\": %s\n", filename, strerror(errno));
+    return -1;
+  }
 
-    size_t bytes_read = ota_fread(data.data(), 1, data.size(), f);
-    if (bytes_read != data.size()) {
-        printf("short read of \"%s\" (%zu bytes of %zd)\n", filename, bytes_read, data.size());
-        ota_fclose(f);
-        return -1;
-    }
-    ota_fclose(f);
-    file->data = std::move(data);
-    SHA1(file->data.data(), file->data.size(), file->sha1);
-    return 0;
+  size_t bytes_read = ota_fread(data.data(), 1, data.size(), f.get());
+  if (bytes_read != data.size()) {
+    printf("short read of \"%s\" (%zu bytes of %zu)\n", filename, bytes_read, data.size());
+    return -1;
+  }
+  file->data = std::move(data);
+  SHA1(file->data.data(), file->data.size(), file->sha1);
+  return 0;
 }
 
-// Load the contents of an MTD or EMMC partition into the provided
+// Load the contents of an EMMC partition into the provided
 // FileContents.  filename should be a string of the form
-// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:..."  (or
-// "EMMC:<partition_device>:...").  The smallest size_n bytes for
+// "EMMC:<partition_device>:...".  The smallest size_n bytes for
 // which that prefix of the partition contents has the corresponding
 // sha1 hash will be loaded.  It is acceptable for a size value to be
 // repeated with different sha1s.  Will return 0 on success.
@@ -106,223 +101,166 @@
 // to find one of those hashes.
 enum PartitionType { MTD, EMMC };
 
-static int LoadPartitionContents(const char* filename, FileContents* file) {
-    std::string copy(filename);
-    std::vector<std::string> pieces = android::base::Split(copy, ":");
-    if (pieces.size() < 4 || pieces.size() % 2 != 0) {
-        printf("LoadPartitionContents called with bad filename (%s)\n", filename);
-        return -1;
-    }
+static int LoadPartitionContents(const std::string& filename, FileContents* file) {
+  std::vector<std::string> pieces = android::base::Split(filename, ":");
+  if (pieces.size() < 4 || pieces.size() % 2 != 0) {
+    printf("LoadPartitionContents called with bad filename \"%s\"\n", filename.c_str());
+    return -1;
+  }
 
-    enum PartitionType type;
-    if (pieces[0] == "MTD") {
-        type = MTD;
-    } else if (pieces[0] == "EMMC") {
-        type = EMMC;
-    } else if (pieces[0] == "BML") {
-        type = EMMC;
-    } else {
-        printf("LoadPartitionContents called with bad filename (%s)\n", filename);
-        return -1;
+  enum PartitionType type;
+  if (pieces[0] == "MTD") {
+    type = MTD;
+  } else if (pieces[0] == "EMMC") {
+    type = EMMC;
+  } else if (pieces[0] == "BML") {
+    type = EMMC;
+  } else {
+    printf("LoadPartitionContents called with bad filename (%s)\n", filename.c_str());
+    return -1;
+  }
+
+  size_t pair_count = (pieces.size() - 2) / 2;  // # of (size, sha1) pairs in filename
+  std::vector<std::pair<size_t, std::string>> pairs;
+  for (size_t i = 0; i < pair_count; ++i) {
+    size_t size;
+    if (!android::base::ParseUint(pieces[i * 2 + 2], &size) || size == 0) {
+      printf("LoadPartitionContents called with bad size \"%s\"\n", pieces[i * 2 + 2].c_str());
+      return -1;
     }
-    const char* partition = pieces[1].c_str();
+    pairs.push_back({ size, pieces[i * 2 + 3] });
+  }
+
+  // Sort the pairs array so that they are in order of increasing size.
+  std::sort(pairs.begin(), pairs.end());
+
+  const char* partition = pieces[1].c_str();
+  unique_file dev(ota_fopen(partition, "rb"));
+  if (!dev) {
+    printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno));
+    return -1;
+  }
+
+  SHA_CTX sha_ctx;
+  SHA1_Init(&sha_ctx);
+
+  // Allocate enough memory to hold the largest size.
+  std::vector<unsigned char> buffer(pairs[pair_count - 1].first);
+  unsigned char* buffer_ptr = buffer.data();
+  size_t buffer_size = 0;  // # bytes read so far
+  bool found = false;
+
+  for (const auto& pair : pairs) {
+    size_t current_size = pair.first;
+    const std::string& current_sha1 = pair.second;
+
+    // Read enough additional bytes to get us up to the next size. (Again,
+    // we're trying the possibilities in order of increasing size).
+    size_t next = current_size - buffer_size;
+    if (next > 0) {
+      size_t read = ota_fread(buffer_ptr, 1, next, dev.get());
+      if (next != read) {
+        printf("short read (%zu bytes of %zu) for partition \"%s\"\n", read, next, partition);
+        return -1;
+      }
+      SHA1_Update(&sha_ctx, buffer_ptr, read);
+      buffer_size += read;
+      buffer_ptr += read;
+    }
 
     if (pieces[0] == "BML") {
-        if (strcmp(partition, "boot") == 0) {
-            partition = BOARD_BML_BOOT;
-        } else if (strcmp(partition, "recovery") == 0) {
-            partition = BOARD_BML_RECOVERY;
-        }
+      if (strcmp(partition, "boot") == 0) {
+        partition = BOARD_BML_BOOT;
+      } else if (strcmp(partition, "recovery") == 0) {
+        partition = BOARD_BML_RECOVERY;
+      }
     }
 
-    size_t pairs = (pieces.size() - 2) / 2;    // # of (size, sha1) pairs in filename
-    std::vector<size_t> index(pairs);
-    std::vector<size_t> size(pairs);
-    std::vector<std::string> sha1sum(pairs);
+    // Duplicate the SHA context and finalize the duplicate so we can
+    // check it against this pair's expected hash.
+    SHA_CTX temp_ctx;
+    memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX));
+    uint8_t sha_so_far[SHA_DIGEST_LENGTH];
+    SHA1_Final(sha_so_far, &temp_ctx);
 
-    for (size_t i = 0; i < pairs; ++i) {
-        size[i] = strtol(pieces[i*2+2].c_str(), NULL, 10);
-        if (size[i] == 0) {
-            printf("LoadPartitionContents called with bad size (%s)\n", filename);
-            return -1;
-        }
-        sha1sum[i] = pieces[i*2+3].c_str();
-        index[i] = i;
-    }
-
-    // Sort the index[] array so it indexes the pairs in order of increasing size.
-    sort(index.begin(), index.end(),
-        [&](const size_t& i, const size_t& j) {
-            return (size[i] < size[j]);
-        }
-    );
-
-    MtdReadContext* ctx = NULL;
-    FILE* dev = NULL;
-
-    switch (type) {
-        case MTD: {
-            if (!mtd_partitions_scanned) {
-                mtd_scan_partitions();
-                mtd_partitions_scanned = true;
-            }
-
-            const MtdPartition* mtd = mtd_find_partition_by_name(partition);
-            if (mtd == NULL) {
-                printf("mtd partition \"%s\" not found (loading %s)\n", partition, filename);
-                return -1;
-            }
-
-            ctx = mtd_read_partition(mtd);
-            if (ctx == NULL) {
-                printf("failed to initialize read of mtd partition \"%s\"\n", partition);
-                return -1;
-            }
-            break;
-        }
-
-        case EMMC:
-            dev = ota_fopen(partition, "rb");
-            if (dev == NULL) {
-                printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno));
-                return -1;
-            }
-    }
-
-    SHA_CTX sha_ctx;
-    SHA1_Init(&sha_ctx);
     uint8_t parsed_sha[SHA_DIGEST_LENGTH];
-
-    // Allocate enough memory to hold the largest size.
-    std::vector<unsigned char> data(size[index[pairs-1]]);
-    char* p = reinterpret_cast<char*>(data.data());
-    size_t data_size = 0;                // # bytes read so far
-    bool found = false;
-
-    for (size_t i = 0; i < pairs; ++i) {
-        // Read enough additional bytes to get us up to the next size. (Again,
-        // we're trying the possibilities in order of increasing size).
-        size_t next = size[index[i]] - data_size;
-        if (next > 0) {
-            size_t read = 0;
-            switch (type) {
-                case MTD:
-                    read = mtd_read_data(ctx, p, next);
-                    break;
-
-                case EMMC:
-                    read = ota_fread(p, 1, next, dev);
-                    break;
-            }
-            if (next != read) {
-                printf("short read (%zu bytes of %zu) for partition \"%s\"\n",
-                       read, next, partition);
-                return -1;
-            }
-            SHA1_Update(&sha_ctx, p, read);
-            data_size += read;
-            p += read;
-        }
-
-        // Duplicate the SHA context and finalize the duplicate so we can
-        // check it against this pair's expected hash.
-        SHA_CTX temp_ctx;
-        memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX));
-        uint8_t sha_so_far[SHA_DIGEST_LENGTH];
-        SHA1_Final(sha_so_far, &temp_ctx);
-
-        if (ParseSha1(sha1sum[index[i]].c_str(), parsed_sha) != 0) {
-            printf("failed to parse sha1 %s in %s\n", sha1sum[index[i]].c_str(), filename);
-            return -1;
-        }
-
-        if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_LENGTH) == 0) {
-            // we have a match.  stop reading the partition; we'll return
-            // the data we've read so far.
-            printf("partition read matched size %zu sha %s\n",
-                   size[index[i]], sha1sum[index[i]].c_str());
-            found = true;
-            break;
-        }
+    if (ParseSha1(current_sha1.c_str(), parsed_sha) != 0) {
+      printf("failed to parse SHA-1 %s in %s\n", current_sha1.c_str(), filename.c_str());
+      return -1;
     }
 
-    switch (type) {
-        case MTD:
-            mtd_read_close(ctx);
-            break;
-
-        case EMMC:
-            ota_fclose(dev);
-            break;
+    if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_LENGTH) == 0) {
+      // We have a match. Stop reading the partition; we'll return the data we've read so far.
+      printf("partition read matched size %zu SHA-1 %s\n", current_size, current_sha1.c_str());
+      found = true;
+      break;
     }
+  }
 
+  if (!found) {
+    // Ran off the end of the list of (size, sha1) pairs without finding a match.
+    printf("contents of partition \"%s\" didn't match %s\n", partition, filename.c_str());
+    return -1;
+  }
 
-    if (!found) {
-        // Ran off the end of the list of (size,sha1) pairs without finding a match.
-        printf("contents of partition \"%s\" didn't match %s\n", partition, filename);
-        return -1;
-    }
+  SHA1_Final(file->sha1, &sha_ctx);
 
-    SHA1_Final(file->sha1, &sha_ctx);
+  buffer.resize(buffer_size);
+  file->data = std::move(buffer);
+  // Fake some stat() info.
+  file->st.st_mode = 0644;
+  file->st.st_uid = 0;
+  file->st.st_gid = 0;
 
-    data.resize(data_size);
-    file->data = std::move(data);
-    // Fake some stat() info.
-    file->st.st_mode = 0644;
-    file->st.st_uid = 0;
-    file->st.st_gid = 0;
-
-    return 0;
+  return 0;
 }
 
-
 // Save the contents of the given FileContents object under the given
 // filename.  Return 0 on success.
 int SaveFileContents(const char* filename, const FileContents* file) {
-    int fd = ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR);
-    if (fd < 0) {
-        printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno));
-        return -1;
-    }
+  unique_fd fd(ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR));
+  if (fd == -1) {
+    printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno));
+    return -1;
+  }
 
-    ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd);
-    if (bytes_written != static_cast<ssize_t>(file->data.size())) {
-        printf("short write of \"%s\" (%zd bytes of %zu) (%s)\n",
-               filename, bytes_written, file->data.size(), strerror(errno));
-        ota_close(fd);
-        return -1;
-    }
-    if (ota_fsync(fd) != 0) {
-        printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
-        return -1;
-    }
-    if (ota_close(fd) != 0) {
-        printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
-        return -1;
-    }
+  ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd);
+  if (bytes_written != static_cast<ssize_t>(file->data.size())) {
+    printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written,
+           file->data.size(), strerror(errno));
+    return -1;
+  }
+  if (ota_fsync(fd) != 0) {
+    printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
+    return -1;
+  }
+  if (ota_close(fd) != 0) {
+    printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
+    return -1;
+  }
 
-    if (chmod(filename, file->st.st_mode) != 0) {
-        printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
-        return -1;
-    }
-    if (chown(filename, file->st.st_uid, file->st.st_gid) != 0) {
-        printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
-        return -1;
-    }
+  if (chmod(filename, file->st.st_mode) != 0) {
+    printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
+    return -1;
+  }
+  if (chown(filename, file->st.st_uid, file->st.st_gid) != 0) {
+    printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
+    return -1;
+  }
 
-    return 0;
+  return 0;
 }
 
 // Write a memory buffer to 'target' partition, a string of the form
-// "MTD:<partition>[:...]" or "EMMC:<partition_device>[:...]". The target name
+// "EMMC:<partition_device>[:...]". The target name
 // might contain multiple colons, but WriteToPartition() only uses the first
 // two and ignores the rest. Return 0 on success.
-int WriteToPartition(const unsigned char* data, size_t len, const char* target) {
+int WriteToPartition(const unsigned char* data, size_t len, const std::string& target) {
     std::string copy(target);
     std::vector<std::string> pieces = android::base::Split(copy, ":");
 
     if (pieces.size() < 2) {
-        printf("WriteToPartition called with bad target (%s)\n", target);
+        printf("WriteToPartition called with bad target (%s)\n", target.c_str());
         return -1;
     }
 
@@ -334,7 +272,7 @@
     } else if (pieces[0] == "BML") {
         type = EMMC;
     } else {
-        printf("WriteToPartition called with bad target (%s)\n", target);
+        printf("WriteToPartition called with bad target (%s)\n", target.c_str());
         return -1;
     }
 
@@ -358,7 +296,7 @@
     }
 
     if (partition == NULL) {
-        printf("bad partition target name \"%s\"\n", target);
+        printf("bad partition target name \"%s\"\n", target.c_str());
         return -1;
     }
 
@@ -404,7 +342,7 @@
         case EMMC: {
             size_t start = 0;
             bool success = false;
-            int fd = ota_open(partition, O_RDWR | O_SYNC);
+            unique_fd fd(ota_open(partition, O_RDWR | O_SYNC));
             if (fd < 0) {
                 printf("failed to open %s: %s\n", partition, strerror(errno));
                 return -1;
@@ -434,7 +372,7 @@
                    printf("failed to close %s (%s)\n", partition, strerror(errno));
                    return -1;
                 }
-                fd = ota_open(partition, O_RDONLY);
+                unique_fd fd(ota_open(partition, O_RDONLY));
                 if (fd < 0) {
                    printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno));
                    return -1;
@@ -443,7 +381,7 @@
                 // Drop caches so our subsequent verification read
                 // won't just be reading the cache.
                 sync();
-                int dc = ota_open("/proc/sys/vm/drop_caches", O_WRONLY);
+                unique_fd dc(ota_open("/proc/sys/vm/drop_caches", O_WRONLY));
                 if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) {
                     printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno));
                 } else {
@@ -513,7 +451,6 @@
     return 0;
 }
 
-
 // Take a string 'str' of 40 hex digits and parse it into the 20
 // byte array 'digest'.  'str' may contain only the digest or be of
 // the form "<digest>:<anything>".  Return 0 on success, -1 on any
@@ -546,52 +483,47 @@
 // Search an array of sha1 strings for one matching the given sha1.
 // Return the index of the match on success, or -1 if no match is
 // found.
-int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
-                      int num_patches) {
+static int FindMatchingPatch(uint8_t* sha1, const std::vector<std::string>& patch_sha1_str) {
+  for (size_t i = 0; i < patch_sha1_str.size(); ++i) {
     uint8_t patch_sha1[SHA_DIGEST_LENGTH];
-    for (int i = 0; i < num_patches; ++i) {
-        if (ParseSha1(patch_sha1_str[i], patch_sha1) == 0 &&
-            memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) {
-            return i;
-        }
+    if (ParseSha1(patch_sha1_str[i].c_str(), patch_sha1) == 0 &&
+        memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) {
+      return i;
     }
-    return -1;
+  }
+  return -1;
 }
 
 // Returns 0 if the contents of the file (argv[2]) or the cached file
 // match any of the sha1's on the command line (argv[3:]).  Returns
 // nonzero otherwise.
-int applypatch_check(const char* filename, int num_patches,
-                     char** const patch_sha1_str) {
-    FileContents file;
+int applypatch_check(const char* filename, const std::vector<std::string>& patch_sha1_str) {
+  FileContents file;
 
-    // It's okay to specify no sha1s; the check will pass if the
-    // LoadFileContents is successful.  (Useful for reading
-    // partitions, where the filename encodes the sha1s; no need to
-    // check them twice.)
-    if (LoadFileContents(filename, &file) != 0 ||
-        (num_patches > 0 &&
-         FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) {
-        printf("file \"%s\" doesn't have any of expected "
-               "sha1 sums; checking cache\n", filename);
+  // It's okay to specify no sha1s; the check will pass if the
+  // LoadFileContents is successful.  (Useful for reading
+  // partitions, where the filename encodes the sha1s; no need to
+  // check them twice.)
+  if (LoadFileContents(filename, &file) != 0 ||
+      (!patch_sha1_str.empty() && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) {
+    printf("file \"%s\" doesn't have any of expected sha1 sums; checking cache\n", filename);
 
-        // If the source file is missing or corrupted, it might be because
-        // we were killed in the middle of patching it.  A copy of it
-        // should have been made in CACHE_TEMP_SOURCE.  If that file
-        // exists and matches the sha1 we're looking for, the check still
-        // passes.
-
-        if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
-            printf("failed to load cache file\n");
-            return 1;
-        }
-
-        if (FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0) {
-            printf("cache bits don't match any sha1 for \"%s\"\n", filename);
-            return 1;
-        }
+    // If the source file is missing or corrupted, it might be because
+    // we were killed in the middle of patching it.  A copy of it
+    // should have been made in CACHE_TEMP_SOURCE.  If that file
+    // exists and matches the sha1 we're looking for, the check still
+    // passes.
+    if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
+      printf("failed to load cache file\n");
+      return 1;
     }
-    return 0;
+
+    if (FindMatchingPatch(file.sha1, patch_sha1_str) < 0) {
+      printf("cache bits don't match any sha1 for \"%s\"\n", filename);
+      return 1;
+    }
+  }
+  return 0;
 }
 
 int ShowLicenses() {
@@ -633,116 +565,97 @@
 
 int CacheSizeCheck(size_t bytes) {
     if (MakeFreeSpaceOnCache(bytes) < 0) {
-        printf("unable to make %ld bytes available on /cache\n", (long)bytes);
+        printf("unable to make %zu bytes available on /cache\n", bytes);
         return 1;
     } else {
         return 0;
     }
 }
 
-// This function applies binary patches to files in a way that is safe
-// (the original file is not touched until we have the desired
-// replacement for it) and idempotent (it's okay to run this program
-// multiple times).
+// This function applies binary patches to EMMC target files in a way that is safe (the original
+// file is not touched until we have the desired replacement for it) and idempotent (it's okay to
+// run this program multiple times).
 //
-// - if the sha1 hash of <target_filename> is <target_sha1_string>,
-//   does nothing and exits successfully.
+// - If the SHA-1 hash of <target_filename> is <target_sha1_string>, does nothing and exits
+//   successfully.
 //
-// - otherwise, if the sha1 hash of <source_filename> is one of the
-//   entries in <patch_sha1_str>, the corresponding patch from
-//   <patch_data> (which must be a VAL_BLOB) is applied to produce a
-//   new file (the type of patch is automatically detected from the
-//   blob data).  If that new file has sha1 hash <target_sha1_str>,
-//   moves it to replace <target_filename>, and exits successfully.
-//   Note that if <source_filename> and <target_filename> are not the
-//   same, <source_filename> is NOT deleted on success.
-//   <target_filename> may be the string "-" to mean "the same as
-//   source_filename".
+// - Otherwise, if the SHA-1 hash of <source_filename> is one of the entries in <patch_sha1_str>,
+//   the corresponding patch from <patch_data> (which must be a VAL_BLOB) is applied to produce a
+//   new file (the type of patch is automatically detected from the blob data). If that new file
+//   has SHA-1 hash <target_sha1_str>, moves it to replace <target_filename>, and exits
+//   successfully. Note that if <source_filename> and <target_filename> are not the same,
+//   <source_filename> is NOT deleted on success. <target_filename> may be the string "-" to mean
+//   "the same as <source_filename>".
 //
-// - otherwise, or if any error is encountered, exits with non-zero
-//   status.
+// - Otherwise, or if any error is encountered, exits with non-zero status.
 //
-// <source_filename> may refer to a partition to read the source data.
-// See the comments for the LoadPartitionContents() function above
-// for the format of such a filename.
+// <source_filename> must refer to an EMMC partition to read the source data. See the comments for
+// the LoadPartitionContents() function above for the format of such a filename. <target_size> has
+// become obsolete since we have dropped the support for patching non-EMMC targets (EMMC targets
+// have the size embedded in the filename).
+int applypatch(const char* source_filename, const char* target_filename,
+               const char* target_sha1_str, size_t target_size __unused,
+               const std::vector<std::string>& patch_sha1_str,
+               const std::vector<std::unique_ptr<Value>>& patch_data, const Value* bonus_data) {
+  printf("patch %s: ", source_filename);
 
-int applypatch(const char* source_filename,
-               const char* target_filename,
-               const char* target_sha1_str,
-               size_t target_size,
-               int num_patches,
-               char** const patch_sha1_str,
-               Value** patch_data,
-               Value* bonus_data) {
-    printf("patch %s: ", source_filename);
+  if (target_filename[0] == '-' && target_filename[1] == '\0') {
+    target_filename = source_filename;
+  }
 
-    if (target_filename[0] == '-' && target_filename[1] == '\0') {
-        target_filename = source_filename;
+  if (strncmp(target_filename, "EMMC:", 5) != 0) {
+    printf("Supporting patching EMMC targets only.\n");
+    return 1;
+  }
+
+  uint8_t target_sha1[SHA_DIGEST_LENGTH];
+  if (ParseSha1(target_sha1_str, target_sha1) != 0) {
+    printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
+    return 1;
+  }
+
+  // We try to load the target file into the source_file object.
+  FileContents source_file;
+  if (LoadFileContents(target_filename, &source_file) == 0) {
+    if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
+      // The early-exit case: the patch was already applied, this file has the desired hash, nothing
+      // for us to do.
+      printf("already %s\n", short_sha1(target_sha1).c_str());
+      return 0;
     }
+  }
 
-    uint8_t target_sha1[SHA_DIGEST_LENGTH];
-    if (ParseSha1(target_sha1_str, target_sha1) != 0) {
-        printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
-        return 1;
+  if (source_file.data.empty() ||
+      (target_filename != source_filename && strcmp(target_filename, source_filename) != 0)) {
+    // Need to load the source file: either we failed to load the target file, or we did but it's
+    // different from the expected.
+    source_file.data.clear();
+    LoadFileContents(source_filename, &source_file);
+  }
+
+  if (!source_file.data.empty()) {
+    int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str);
+    if (to_use != -1) {
+      return GenerateTarget(source_file, patch_data[to_use], target_filename, target_sha1,
+                            bonus_data);
     }
+  }
 
-    FileContents copy_file;
-    FileContents source_file;
-    const Value* source_patch_value = NULL;
-    const Value* copy_patch_value = NULL;
+  printf("source file is bad; trying copy\n");
 
-    // We try to load the target file into the source_file object.
-    if (LoadFileContents(target_filename, &source_file) == 0) {
-        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
-            // The early-exit case:  the patch was already applied, this file
-            // has the desired hash, nothing for us to do.
-            printf("already %s\n", short_sha1(target_sha1).c_str());
-            return 0;
-        }
-    }
+  FileContents copy_file;
+  if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
+    printf("failed to read copy file\n");
+    return 1;
+  }
 
-    if (source_file.data.empty() ||
-        (target_filename != source_filename &&
-         strcmp(target_filename, source_filename) != 0)) {
-        // Need to load the source file:  either we failed to load the
-        // target file, or we did but it's different from the source file.
-        source_file.data.clear();
-        LoadFileContents(source_filename, &source_file);
-    }
+  int to_use = FindMatchingPatch(copy_file.sha1, patch_sha1_str);
+  if (to_use == -1) {
+    printf("copy file doesn't match source SHA-1s either\n");
+    return 1;
+  }
 
-    if (!source_file.data.empty()) {
-        int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str, num_patches);
-        if (to_use >= 0) {
-            source_patch_value = patch_data[to_use];
-        }
-    }
-
-    if (source_patch_value == NULL) {
-        source_file.data.clear();
-        printf("source file is bad; trying copy\n");
-
-        if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
-            // fail.
-            printf("failed to read copy file\n");
-            return 1;
-        }
-
-        int to_use = FindMatchingPatch(copy_file.sha1, patch_sha1_str, num_patches);
-        if (to_use >= 0) {
-            copy_patch_value = patch_data[to_use];
-        }
-
-        if (copy_patch_value == NULL) {
-            // fail.
-            printf("copy file doesn't match source SHA-1s either\n");
-            return 1;
-        }
-    }
-
-    return GenerateTarget(&source_file, source_patch_value,
-                          &copy_file, copy_patch_value,
-                          source_filename, target_filename,
-                          target_sha1, target_size, bonus_data);
+  return GenerateTarget(copy_file, patch_data[to_use], target_filename, target_sha1, bonus_data);
 }
 
 /*
@@ -754,281 +667,124 @@
  */
 int applypatch_flash(const char* source_filename, const char* target_filename,
                      const char* target_sha1_str, size_t target_size) {
-    printf("flash %s: ", target_filename);
+  printf("flash %s: ", target_filename);
 
-    uint8_t target_sha1[SHA_DIGEST_LENGTH];
-    if (ParseSha1(target_sha1_str, target_sha1) != 0) {
-        printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
-        return 1;
-    }
+  uint8_t target_sha1[SHA_DIGEST_LENGTH];
+  if (ParseSha1(target_sha1_str, target_sha1) != 0) {
+    printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
+    return 1;
+  }
 
-    FileContents source_file;
-    std::string target_str(target_filename);
+  std::string target_str(target_filename);
+  std::vector<std::string> pieces = android::base::Split(target_str, ":");
+  if (pieces.size() != 2 || pieces[0] != "EMMC") {
+    printf("invalid target name \"%s\"", target_filename);
+    return 1;
+  }
 
-    std::vector<std::string> pieces = android::base::Split(target_str, ":");
-    if (pieces.size() != 2 || (pieces[0] != "MTD" && pieces[0] != "EMMC")) {
-        printf("invalid target name \"%s\"", target_filename);
-        return 1;
-    }
-
-    // Load the target into the source_file object to see if already applied.
-    pieces.push_back(std::to_string(target_size));
-    pieces.push_back(target_sha1_str);
-    std::string fullname = android::base::Join(pieces, ':');
-    if (LoadPartitionContents(fullname.c_str(), &source_file) == 0 &&
-        memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
-        // The early-exit case: the image was already applied, this partition
-        // has the desired hash, nothing for us to do.
-        printf("already %s\n", short_sha1(target_sha1).c_str());
-        return 0;
-    }
-
-    if (LoadFileContents(source_filename, &source_file) == 0) {
-        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
-            // The source doesn't have desired checksum.
-            printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename);
-            printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(),
-                    short_sha1(source_file.sha1).c_str());
-            return 1;
-        }
-    }
-
-    if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) {
-        printf("write of copied data to %s failed\n", target_filename);
-        return 1;
-    }
+  // Load the target into the source_file object to see if already applied.
+  pieces.push_back(std::to_string(target_size));
+  pieces.push_back(target_sha1_str);
+  std::string fullname = android::base::Join(pieces, ':');
+  FileContents source_file;
+  if (LoadPartitionContents(fullname, &source_file) == 0 &&
+      memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
+    // The early-exit case: the image was already applied, this partition
+    // has the desired hash, nothing for us to do.
+    printf("already %s\n", short_sha1(target_sha1).c_str());
     return 0;
+  }
+
+  if (LoadFileContents(source_filename, &source_file) == 0) {
+    if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
+      // The source doesn't have desired checksum.
+      printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename);
+      printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(),
+             short_sha1(source_file.sha1).c_str());
+      return 1;
+    }
+  }
+
+  if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) {
+    printf("write of copied data to %s failed\n", target_filename);
+    return 1;
+  }
+  return 0;
 }
 
-static int GenerateTarget(FileContents* source_file,
-                          const Value* source_patch_value,
-                          FileContents* copy_file,
-                          const Value* copy_patch_value,
-                          const char* source_filename,
-                          const char* target_filename,
-                          const uint8_t target_sha1[SHA_DIGEST_LENGTH],
-                          size_t target_size,
-                          const Value* bonus_data) {
-    int retry = 1;
-    SHA_CTX ctx;
-    std::string memory_sink_str;
-    FileContents* source_to_use;
-    int made_copy = 0;
+static int GenerateTarget(const FileContents& source_file, const std::unique_ptr<Value>& patch,
+                          const std::string& target_filename,
+                          const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data) {
+  if (patch->type != VAL_BLOB) {
+    printf("patch is not a blob\n");
+    return 1;
+  }
 
-    bool target_is_partition = (strncmp(target_filename, "MTD:", 4) == 0 ||
-                                strncmp(target_filename, "EMMC:", 5) == 0 ||
-                                strncmp(target_filename, "BML:", 4) == 0);
-    const std::string tmp_target_filename = std::string(target_filename) + ".patch";
+  const char* header = &patch->data[0];
+  size_t header_bytes_read = patch->data.size();
+  bool use_bsdiff = false;
+  if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) {
+    use_bsdiff = true;
+  } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) {
+    use_bsdiff = false;
+  } else {
+    printf("Unknown patch file format\n");
+    return 1;
+  }
 
-    // assume that target_filename (eg "/system/app/Foo.apk") is located
-    // on the same filesystem as its top-level directory ("/system").
-    // We need something that exists for calling statfs().
-    std::string target_fs = target_filename;
-    auto slash_pos = target_fs.find('/', 1);
-    if (slash_pos != std::string::npos) {
-        target_fs.resize(slash_pos);
-    }
+  CHECK(android::base::StartsWith(target_filename, "EMMC:"));
 
-    const Value* patch;
-    if (source_patch_value != NULL) {
-        source_to_use = source_file;
-        patch = source_patch_value;
-    } else {
-        source_to_use = copy_file;
-        patch = copy_patch_value;
-    }
-    if (patch->type != VAL_BLOB) {
-        printf("patch is not a blob\n");
-        return 1;
-    }
-    char* header = patch->data;
-    ssize_t header_bytes_read = patch->size;
-    bool use_bsdiff = false;
-    if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) {
-        use_bsdiff = true;
-    } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) {
-        use_bsdiff = false;
-    } else {
-        printf("Unknown patch file format\n");
-        return 1;
-    }
+  // We still write the original source to cache, in case the partition write is interrupted.
+  if (MakeFreeSpaceOnCache(source_file.data.size()) < 0) {
+    printf("not enough free space on /cache\n");
+    return 1;
+  }
+  if (SaveFileContents(CACHE_TEMP_SOURCE, &source_file) < 0) {
+    printf("failed to back up source file\n");
+    return 1;
+  }
 
-    do {
-        // Is there enough room in the target filesystem to hold the patched
-        // file?
+  // We store the decoded output in memory.
+  SinkFn sink = MemorySink;
+  std::string memory_sink_str;  // Don't need to reserve space.
+  void* token = &memory_sink_str;
 
-        if (target_is_partition) {
-            // If the target is a partition, we're actually going to
-            // write the output to /tmp and then copy it to the
-            // partition.  statfs() always returns 0 blocks free for
-            // /tmp, so instead we'll just assume that /tmp has enough
-            // space to hold the file.
+  SHA_CTX ctx;
+  SHA1_Init(&ctx);
 
-            // We still write the original source to cache, in case
-            // the partition write is interrupted.
-            if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
-                printf("not enough free space on /cache\n");
-                return 1;
-            }
-            if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
-                printf("failed to back up source file\n");
-                return 1;
-            }
-            made_copy = 1;
-            retry = 0;
-        } else {
-            int enough_space = 0;
-            if (retry > 0) {
-                size_t free_space = FreeSpaceForFile(target_fs.c_str());
-                enough_space =
-                    (free_space > (256 << 10)) &&          // 256k (two-block) minimum
-                    (free_space > (target_size * 3 / 2));  // 50% margin of error
-                if (!enough_space) {
-                    printf("target %zu bytes; free space %zu bytes; retry %d; enough %d\n",
-                           target_size, free_space, retry, enough_space);
-                }
-            }
+  int result;
+  if (use_bsdiff) {
+    result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch.get(), 0,
+                              sink, token, &ctx);
+  } else {
+    result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch.get(), sink,
+                             token, &ctx, bonus_data);
+  }
 
-            if (!enough_space) {
-                retry = 0;
-            }
+  if (result != 0) {
+    printf("applying patch failed\n");
+    return 1;
+  }
 
-            if (!enough_space && source_patch_value != NULL) {
-                // Using the original source, but not enough free space.  First
-                // copy the source file to cache, then delete it from the original
-                // location.
+  uint8_t current_target_sha1[SHA_DIGEST_LENGTH];
+  SHA1_Final(current_target_sha1, &ctx);
+  if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
+    printf("patch did not produce expected sha1\n");
+    return 1;
+  } else {
+    printf("now %s\n", short_sha1(target_sha1).c_str());
+  }
 
-                if (strncmp(source_filename, "MTD:", 4) == 0 ||
-                    strncmp(source_filename, "EMMC:", 5) == 0 ||
-                    strncmp(source_filename, "BML:", 4) == 0) {
-                    // It's impossible to free space on the target filesystem by
-                    // deleting the source if the source is a partition.  If
-                    // we're ever in a state where we need to do this, fail.
-                    printf("not enough free space for target but source is partition\n");
-                    return 1;
-                }
+  // Write back the temp file to the partition.
+  if (WriteToPartition(reinterpret_cast<const unsigned char*>(memory_sink_str.c_str()),
+                       memory_sink_str.size(), target_filename) != 0) {
+    printf("write of patched data to %s failed\n", target_filename.c_str());
+    return 1;
+  }
 
-                if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
-                    printf("not enough free space on /cache\n");
-                    return 1;
-                }
+  // Delete the backup copy of the source.
+  unlink(CACHE_TEMP_SOURCE);
 
-                if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
-                    printf("failed to back up source file\n");
-                    return 1;
-                }
-                made_copy = 1;
-                unlink(source_filename);
-
-                size_t free_space = FreeSpaceForFile(target_fs.c_str());
-                printf("(now %zu bytes free for target) ", free_space);
-            }
-        }
-
-
-        SinkFn sink = NULL;
-        void* token = NULL;
-
-        int output_fd = -1;
-        if (target_is_partition) {
-            // We store the decoded output in memory.
-            sink = MemorySink;
-            token = &memory_sink_str;
-        } else {
-            // We write the decoded output to "<tgt-file>.patch".
-            output_fd = ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC,
-                          S_IRUSR | S_IWUSR);
-            if (output_fd < 0) {
-                printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(),
-                       strerror(errno));
-                return 1;
-            }
-            sink = FileSink;
-            token = &output_fd;
-        }
-
-
-        SHA1_Init(&ctx);
-
-        int result;
-        if (use_bsdiff) {
-            result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(),
-                                      patch, 0, sink, token, &ctx);
-        } else {
-            result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(),
-                                     patch, sink, token, &ctx, bonus_data);
-        }
-
-        if (!target_is_partition) {
-            if (ota_fsync(output_fd) != 0) {
-                printf("failed to fsync file \"%s\" (%s)\n", tmp_target_filename.c_str(),
-                       strerror(errno));
-                result = 1;
-            }
-            if (ota_close(output_fd) != 0) {
-                printf("failed to close file \"%s\" (%s)\n", tmp_target_filename.c_str(),
-                       strerror(errno));
-                result = 1;
-            }
-        }
-
-        if (result != 0) {
-            if (retry == 0) {
-                printf("applying patch failed\n");
-                return result != 0;
-            } else {
-                printf("applying patch failed; retrying\n");
-            }
-            if (!target_is_partition) {
-                unlink(tmp_target_filename.c_str());
-            }
-        } else {
-            // succeeded; no need to retry
-            break;
-        }
-    } while (retry-- > 0);
-
-    uint8_t current_target_sha1[SHA_DIGEST_LENGTH];
-    SHA1_Final(current_target_sha1, &ctx);
-    if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
-        printf("patch did not produce expected sha1\n");
-        return 1;
-    } else {
-        printf("now %s\n", short_sha1(target_sha1).c_str());
-    }
-
-    if (target_is_partition) {
-        // Copy the temp file to the partition.
-        if (WriteToPartition(reinterpret_cast<const unsigned char*>(memory_sink_str.c_str()),
-                             memory_sink_str.size(), target_filename) != 0) {
-            printf("write of patched data to %s failed\n", target_filename);
-            return 1;
-        }
-    } else {
-        // Give the .patch file the same owner, group, and mode of the
-        // original source file.
-        if (chmod(tmp_target_filename.c_str(), source_to_use->st.st_mode) != 0) {
-            printf("chmod of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno));
-            return 1;
-        }
-        if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) {
-            printf("chown of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno));
-            return 1;
-        }
-
-        // Finally, rename the .patch file to replace the target file.
-        if (rename(tmp_target_filename.c_str(), target_filename) != 0) {
-            printf("rename of .patch to \"%s\" failed: %s\n", target_filename, strerror(errno));
-            return 1;
-        }
-    }
-
-    // If this run of applypatch created the copy, and we're here, we
-    // can delete it.
-    if (made_copy) {
-        unlink(CACHE_TEMP_SOURCE);
-    }
-
-    // Success!
-    return 0;
+  // Success!
+  return 0;
 }
diff --git a/applypatch/applypatch_main.cpp b/applypatch/applypatch_main.cpp
new file mode 100644
index 0000000..197077c
--- /dev/null
+++ b/applypatch/applypatch_main.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "applypatch_modes.h"
+
+// This program (applypatch) applies binary patches to files in a way that
+// is safe (the original file is not touched until we have the desired
+// replacement for it) and idempotent (it's okay to run this program
+// multiple times).
+//
+// See the comments to applypatch_modes() function.
+
+int main(int argc, char** argv) {
+    return applypatch_modes(argc, const_cast<const char**>(argv));
+}
diff --git a/applypatch/main.cpp b/applypatch/applypatch_modes.cpp
similarity index 65%
rename from applypatch/main.cpp
rename to applypatch/applypatch_modes.cpp
index 9013760..7b191a8 100644
--- a/applypatch/main.cpp
+++ b/applypatch/applypatch_modes.cpp
@@ -14,61 +14,72 @@
  * limitations under the License.
  */
 
+#include "applypatch_modes.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <memory>
+#include <string>
 #include <vector>
 
-#include "applypatch.h"
-#include "edify/expr.h"
-#include "openssl/sha.h"
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <openssl/sha.h>
 
-static int CheckMode(int argc, char** argv) {
+#include "applypatch/applypatch.h"
+#include "edify/expr.h"
+
+static int CheckMode(int argc, const char** argv) {
     if (argc < 3) {
         return 2;
     }
-    return applypatch_check(argv[2], argc-3, argv+3);
+    std::vector<std::string> sha1;
+    for (int i = 3; i < argc; i++) {
+        sha1.push_back(argv[i]);
+    }
+
+    return applypatch_check(argv[2], sha1);
 }
 
-static int SpaceMode(int argc, char** argv) {
+static int SpaceMode(int argc, const char** argv) {
     if (argc != 3) {
         return 2;
     }
-    char* endptr;
-    size_t bytes = strtol(argv[2], &endptr, 10);
-    if (bytes == 0 && endptr == argv[2]) {
+
+    size_t bytes;
+    if (!android::base::ParseUint(argv[2], &bytes) || bytes == 0) {
         printf("can't parse \"%s\" as byte count\n\n", argv[2]);
         return 1;
     }
     return CacheSizeCheck(bytes);
 }
 
-// Parse arguments (which should be of the form "<sha1>:<filename>"
-// into the new parallel arrays *sha1s and *files.Returns true on
-// success.
-static bool ParsePatchArgs(int argc, char** argv, std::vector<char*>* sha1s,
+// Parse arguments (which should be of the form "<sha1>:<filename>" into the
+// new parallel arrays *sha1s and *files. Returns true on success.
+static bool ParsePatchArgs(int argc, const char** argv, std::vector<std::string>* sha1s,
                            std::vector<FileContents>* files) {
-    uint8_t digest[SHA_DIGEST_LENGTH];
-
+    if (sha1s == nullptr) {
+        return false;
+    }
     for (int i = 0; i < argc; ++i) {
-        char* colon = strchr(argv[i], ':');
-        if (colon == nullptr) {
-            printf("no ':' in patch argument \"%s\"\n", argv[i]);
+        std::vector<std::string> pieces = android::base::Split(argv[i], ":");
+        if (pieces.size() != 2) {
+            printf("failed to parse patch argument \"%s\"\n", argv[i]);
             return false;
         }
-        *colon = '\0';
-        ++colon;
-        if (ParseSha1(argv[i], digest) != 0) {
+
+        uint8_t digest[SHA_DIGEST_LENGTH];
+        if (ParseSha1(pieces[0].c_str(), digest) != 0) {
             printf("failed to parse sha1 \"%s\"\n", argv[i]);
             return false;
         }
 
-        sha1s->push_back(argv[i]);
+        sha1s->push_back(pieces[0]);
         FileContents fc;
-        if (LoadFileContents(colon, &fc) != 0) {
+        if (LoadFileContents(pieces[1].c_str(), &fc) != 0) {
             return false;
         }
         files->push_back(std::move(fc));
@@ -81,20 +92,17 @@
     return applypatch_flash(src_filename, tgt_filename, tgt_sha1, tgt_size);
 }
 
-static int PatchMode(int argc, char** argv) {
+static int PatchMode(int argc, const char** argv) {
     FileContents bonusFc;
-    Value bonusValue;
-    Value* bonus = nullptr;
+    Value bonus(VAL_INVALID, "");
 
     if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
         if (LoadFileContents(argv[2], &bonusFc) != 0) {
             printf("failed to load bonus file %s\n", argv[2]);
             return 1;
         }
-        bonus = &bonusValue;
-        bonus->type = VAL_BLOB;
-        bonus->size = bonusFc.data.size();
-        bonus->data = reinterpret_cast<char*>(bonusFc.data.data());
+        bonus.type = VAL_BLOB;
+        bonus.data = std::string(bonusFc.data.cbegin(), bonusFc.data.cend());
         argc -= 2;
         argv += 2;
     }
@@ -103,42 +111,38 @@
         return 2;
     }
 
-    char* endptr;
-    size_t target_size = strtol(argv[4], &endptr, 10);
-    if (target_size == 0 && endptr == argv[4]) {
+    size_t target_size;
+    if (!android::base::ParseUint(argv[4], &target_size) || target_size == 0) {
         printf("can't parse \"%s\" as byte count\n\n", argv[4]);
         return 1;
     }
 
     // If no <src-sha1>:<patch> is provided, it is in flash mode.
     if (argc == 5) {
-        if (bonus != nullptr) {
+        if (bonus.type != VAL_INVALID) {
             printf("bonus file not supported in flash mode\n");
             return 1;
         }
         return FlashMode(argv[1], argv[2], argv[3], target_size);
     }
-    std::vector<char*> sha1s;
+
+    std::vector<std::string> sha1s;
     std::vector<FileContents> files;
     if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &files)) {
         printf("failed to parse patch args\n");
         return 1;
     }
-    std::vector<Value> patches(files.size());
-    std::vector<Value*> patch_ptrs(files.size());
+
+    std::vector<std::unique_ptr<Value>> patches;
     for (size_t i = 0; i < files.size(); ++i) {
-        patches[i].type = VAL_BLOB;
-        patches[i].size = files[i].data.size();
-        patches[i].data = reinterpret_cast<char*>(files[i].data.data());
-        patch_ptrs[i] = &patches[i];
+        patches.push_back(std::make_unique<Value>(
+                VAL_BLOB, std::string(files[i].data.cbegin(), files[i].data.cend())));
     }
-    return applypatch(argv[1], argv[2], argv[3], target_size,
-                      patch_ptrs.size(), sha1s.data(),
-                      patch_ptrs.data(), bonus);
+    return applypatch(argv[1], argv[2], argv[3], target_size, sha1s, patches, &bonus);
 }
 
-// This program applies binary patches to files in a way that is safe
-// (the original file is not touched until we have the desired
+// This program (applypatch) applies binary patches to files in a way that
+// is safe (the original file is not touched until we have the desired
 // replacement for it) and idempotent (it's okay to run this program
 // multiple times).
 //
@@ -160,11 +164,11 @@
 // - otherwise, or if any error is encountered, exits with non-zero
 //   status.
 //
-// <src-file> (or <file> in check mode) may refer to an MTD partition
+// <src-file> (or <file> in check mode) may refer to an EMMC partition
 // to read the source data.  See the comments for the
-// LoadMTDContents() function above for the format of such a filename.
+// LoadPartitionContents() function for the format of such a filename.
 
-int main(int argc, char** argv) {
+int applypatch_modes(int argc, const char** argv) {
     if (argc < 2) {
       usage:
         printf(
@@ -175,8 +179,8 @@
             "   or  %s -l\n"
             "\n"
             "Filenames may be of the form\n"
-            "  MTD:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
-            "to specify reading from or writing to an MTD partition.\n\n",
+            "  EMMC:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
+            "to specify reading from or writing to an EMMC partition.\n\n",
             argv[0], argv[0], argv[0], argv[0]);
         return 2;
     }
diff --git a/updater/install.h b/applypatch/applypatch_modes.h
similarity index 64%
copy from updater/install.h
copy to applypatch/applypatch_modes.h
index 70e3434..3d9d08d 100644
--- a/updater/install.h
+++ b/applypatch/applypatch_modes.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef _UPDATER_INSTALL_H_
-#define _UPDATER_INSTALL_H_
+#ifndef _APPLYPATCH_MODES_H
+#define _APPLYPATCH_MODES_H
 
-void RegisterInstallFunctions();
+int applypatch_modes(int argc, const char** argv);
 
-// uiPrintf function prints msg to screen as well as logs
-void uiPrintf(State* state, const char* format, ...);
-
-static int make_parents(char* name);
-
-#endif
+#endif // _APPLYPATCH_MODES_H
diff --git a/applypatch/bsdiff.cpp b/applypatch/bsdiff.cpp
deleted file mode 100644
index 55dbe5c..0000000
--- a/applypatch/bsdiff.cpp
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-/*
- * Most of this code comes from bsdiff.c from the bsdiff-4.3
- * distribution, which is:
- */
-
-/*-
- * Copyright 2003-2005 Colin Percival
- * All rights reserved
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted providing that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/types.h>
-
-#include <bzlib.h>
-#include <err.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define MIN(x,y) (((x)<(y)) ? (x) : (y))
-
-static void split(off_t *I,off_t *V,off_t start,off_t len,off_t h)
-{
-	off_t i,j,k,x,tmp,jj,kk;
-
-	if(len<16) {
-		for(k=start;k<start+len;k+=j) {
-			j=1;x=V[I[k]+h];
-			for(i=1;k+i<start+len;i++) {
-				if(V[I[k+i]+h]<x) {
-					x=V[I[k+i]+h];
-					j=0;
-				};
-				if(V[I[k+i]+h]==x) {
-					tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
-					j++;
-				};
-			};
-			for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
-			if(j==1) I[k]=-1;
-		};
-		return;
-	};
-
-	x=V[I[start+len/2]+h];
-	jj=0;kk=0;
-	for(i=start;i<start+len;i++) {
-		if(V[I[i]+h]<x) jj++;
-		if(V[I[i]+h]==x) kk++;
-	};
-	jj+=start;kk+=jj;
-
-	i=start;j=0;k=0;
-	while(i<jj) {
-		if(V[I[i]+h]<x) {
-			i++;
-		} else if(V[I[i]+h]==x) {
-			tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
-			j++;
-		} else {
-			tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
-			k++;
-		};
-	};
-
-	while(jj+j<kk) {
-		if(V[I[jj+j]+h]==x) {
-			j++;
-		} else {
-			tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
-			k++;
-		};
-	};
-
-	if(jj>start) split(I,V,start,jj-start,h);
-
-	for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
-	if(jj==kk-1) I[jj]=-1;
-
-	if(start+len>kk) split(I,V,kk,start+len-kk,h);
-}
-
-static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize)
-{
-	off_t buckets[256];
-	off_t i,h,len;
-
-	for(i=0;i<256;i++) buckets[i]=0;
-	for(i=0;i<oldsize;i++) buckets[old[i]]++;
-	for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
-	for(i=255;i>0;i--) buckets[i]=buckets[i-1];
-	buckets[0]=0;
-
-	for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
-	I[0]=oldsize;
-	for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
-	V[oldsize]=0;
-	for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
-	I[0]=-1;
-
-	for(h=1;I[0]!=-(oldsize+1);h+=h) {
-		len=0;
-		for(i=0;i<oldsize+1;) {
-			if(I[i]<0) {
-				len-=I[i];
-				i-=I[i];
-			} else {
-				if(len) I[i-len]=-len;
-				len=V[I[i]]+1-i;
-				split(I,V,i,len,h);
-				i+=len;
-				len=0;
-			};
-		};
-		if(len) I[i-len]=-len;
-	};
-
-	for(i=0;i<oldsize+1;i++) I[V[i]]=i;
-}
-
-static off_t matchlen(u_char *olddata,off_t oldsize,u_char *newdata,off_t newsize)
-{
-	off_t i;
-
-	for(i=0;(i<oldsize)&&(i<newsize);i++)
-		if(olddata[i]!=newdata[i]) break;
-
-	return i;
-}
-
-static off_t search(off_t *I,u_char *old,off_t oldsize,
-		u_char *newdata,off_t newsize,off_t st,off_t en,off_t *pos)
-{
-	off_t x,y;
-
-	if(en-st<2) {
-		x=matchlen(old+I[st],oldsize-I[st],newdata,newsize);
-		y=matchlen(old+I[en],oldsize-I[en],newdata,newsize);
-
-		if(x>y) {
-			*pos=I[st];
-			return x;
-		} else {
-			*pos=I[en];
-			return y;
-		}
-	};
-
-	x=st+(en-st)/2;
-	if(memcmp(old+I[x],newdata,MIN(oldsize-I[x],newsize))<0) {
-		return search(I,old,oldsize,newdata,newsize,x,en,pos);
-	} else {
-		return search(I,old,oldsize,newdata,newsize,st,x,pos);
-	};
-}
-
-static void offtout(off_t x,u_char *buf)
-{
-	off_t y;
-
-	if(x<0) y=-x; else y=x;
-
-		buf[0]=y%256;y-=buf[0];
-	y=y/256;buf[1]=y%256;y-=buf[1];
-	y=y/256;buf[2]=y%256;y-=buf[2];
-	y=y/256;buf[3]=y%256;y-=buf[3];
-	y=y/256;buf[4]=y%256;y-=buf[4];
-	y=y/256;buf[5]=y%256;y-=buf[5];
-	y=y/256;buf[6]=y%256;y-=buf[6];
-	y=y/256;buf[7]=y%256;
-
-	if(x<0) buf[7]|=0x80;
-}
-
-// This is main() from bsdiff.c, with the following changes:
-//
-//    - old, oldsize, newdata, newsize are arguments; we don't load this
-//      data from files.  old and newdata are owned by the caller; we
-//      don't free them at the end.
-//
-//    - the "I" block of memory is owned by the caller, who passes a
-//      pointer to *I, which can be NULL.  This way if we call
-//      bsdiff() multiple times with the same 'old' data, we only do
-//      the qsufsort() step the first time.
-//
-int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* newdata, off_t newsize,
-           const char* patch_filename)
-{
-	int fd;
-	off_t *I;
-	off_t scan,pos,len;
-	off_t lastscan,lastpos,lastoffset;
-	off_t oldscore,scsc;
-	off_t s,Sf,lenf,Sb,lenb;
-	off_t overlap,Ss,lens;
-	off_t i;
-	off_t dblen,eblen;
-	u_char *db,*eb;
-	u_char buf[8];
-	u_char header[32];
-	FILE * pf;
-	BZFILE * pfbz2;
-	int bz2err;
-
-        if (*IP == NULL) {
-            off_t* V;
-            *IP = reinterpret_cast<off_t*>(malloc((oldsize+1) * sizeof(off_t)));
-            V = reinterpret_cast<off_t*>(malloc((oldsize+1) * sizeof(off_t)));
-            qsufsort(*IP, V, old, oldsize);
-            free(V);
-        }
-        I = *IP;
-
-	if(((db=reinterpret_cast<u_char*>(malloc(newsize+1)))==NULL) ||
-		((eb=reinterpret_cast<u_char*>(malloc(newsize+1)))==NULL)) err(1,NULL);
-	dblen=0;
-	eblen=0;
-
-	/* Create the patch file */
-	if ((pf = fopen(patch_filename, "w")) == NULL)
-              err(1, "%s", patch_filename);
-
-	/* Header is
-		0	8	 "BSDIFF40"
-		8	8	length of bzip2ed ctrl block
-		16	8	length of bzip2ed diff block
-		24	8	length of new file */
-	/* File is
-		0	32	Header
-		32	??	Bzip2ed ctrl block
-		??	??	Bzip2ed diff block
-		??	??	Bzip2ed extra block */
-	memcpy(header,"BSDIFF40",8);
-	offtout(0, header + 8);
-	offtout(0, header + 16);
-	offtout(newsize, header + 24);
-	if (fwrite(header, 32, 1, pf) != 1)
-		err(1, "fwrite(%s)", patch_filename);
-
-	/* Compute the differences, writing ctrl as we go */
-	if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
-		errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
-	scan=0;len=0;
-	lastscan=0;lastpos=0;lastoffset=0;
-	while(scan<newsize) {
-		oldscore=0;
-
-		for(scsc=scan+=len;scan<newsize;scan++) {
-			len=search(I,old,oldsize,newdata+scan,newsize-scan,
-					0,oldsize,&pos);
-
-			for(;scsc<scan+len;scsc++)
-			if((scsc+lastoffset<oldsize) &&
-				(old[scsc+lastoffset] == newdata[scsc]))
-				oldscore++;
-
-			if(((len==oldscore) && (len!=0)) ||
-				(len>oldscore+8)) break;
-
-			if((scan+lastoffset<oldsize) &&
-				(old[scan+lastoffset] == newdata[scan]))
-				oldscore--;
-		};
-
-		if((len!=oldscore) || (scan==newsize)) {
-			s=0;Sf=0;lenf=0;
-			for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {
-				if(old[lastpos+i]==newdata[lastscan+i]) s++;
-				i++;
-				if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
-			};
-
-			lenb=0;
-			if(scan<newsize) {
-				s=0;Sb=0;
-				for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
-					if(old[pos-i]==newdata[scan-i]) s++;
-					if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
-				};
-			};
-
-			if(lastscan+lenf>scan-lenb) {
-				overlap=(lastscan+lenf)-(scan-lenb);
-				s=0;Ss=0;lens=0;
-				for(i=0;i<overlap;i++) {
-					if(newdata[lastscan+lenf-overlap+i]==
-					   old[lastpos+lenf-overlap+i]) s++;
-					if(newdata[scan-lenb+i]==
-					   old[pos-lenb+i]) s--;
-					if(s>Ss) { Ss=s; lens=i+1; };
-				};
-
-				lenf+=lens-overlap;
-				lenb-=lens;
-			};
-
-			for(i=0;i<lenf;i++)
-				db[dblen+i]=newdata[lastscan+i]-old[lastpos+i];
-			for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
-				eb[eblen+i]=newdata[lastscan+lenf+i];
-
-			dblen+=lenf;
-			eblen+=(scan-lenb)-(lastscan+lenf);
-
-			offtout(lenf,buf);
-			BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
-			if (bz2err != BZ_OK)
-				errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
-
-			offtout((scan-lenb)-(lastscan+lenf),buf);
-			BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
-			if (bz2err != BZ_OK)
-				errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
-
-			offtout((pos-lenb)-(lastpos+lenf),buf);
-			BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
-			if (bz2err != BZ_OK)
-				errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
-
-			lastscan=scan-lenb;
-			lastpos=pos-lenb;
-			lastoffset=pos-scan;
-		};
-	};
-	BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
-	if (bz2err != BZ_OK)
-		errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
-
-	/* Compute size of compressed ctrl data */
-	if ((len = ftello(pf)) == -1)
-		err(1, "ftello");
-	offtout(len-32, header + 8);
-
-	/* Write compressed diff data */
-	if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
-		errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
-	BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
-	if (bz2err != BZ_OK)
-		errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
-	BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
-	if (bz2err != BZ_OK)
-		errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
-
-	/* Compute size of compressed diff data */
-	if ((newsize = ftello(pf)) == -1)
-		err(1, "ftello");
-	offtout(newsize - len, header + 16);
-
-	/* Write compressed extra data */
-	if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
-		errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
-	BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
-	if (bz2err != BZ_OK)
-		errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
-	BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
-	if (bz2err != BZ_OK)
-		errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
-
-	/* Seek to the beginning, write the header, and close the file */
-	if (fseeko(pf, 0, SEEK_SET))
-		err(1, "fseeko");
-	if (fwrite(header, 32, 1, pf) != 1)
-		err(1, "fwrite(%s)", patch_filename);
-	if (fclose(pf))
-		err(1, "fclose");
-
-	/* Free the memory we used */
-	free(db);
-	free(eb);
-
-	return 0;
-}
diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp
index ebb55f1..9920c2b 100644
--- a/applypatch/bspatch.cpp
+++ b/applypatch/bspatch.cpp
@@ -21,16 +21,12 @@
 // notice.
 
 #include <stdio.h>
-#include <sys/stat.h>
 #include <sys/types.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
 
-#include <bzlib.h>
+#include <bspatch.h>
 
+#include "applypatch/applypatch.h"
 #include "openssl/sha.h"
-#include "applypatch.h"
 
 void ShowBSDiffLicense() {
     puts("The bsdiff library used herein is:\n"
@@ -64,184 +60,25 @@
         );
 }
 
-static off_t offtin(u_char *buf)
-{
-    off_t y;
-
-    y=buf[7]&0x7F;
-    y=y*256;y+=buf[6];
-    y=y*256;y+=buf[5];
-    y=y*256;y+=buf[4];
-    y=y*256;y+=buf[3];
-    y=y*256;y+=buf[2];
-    y=y*256;y+=buf[1];
-    y=y*256;y+=buf[0];
-
-    if(buf[7]&0x80) y=-y;
-
-    return y;
+int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Value* patch,
+                     ssize_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx) {
+  auto sha_sink = [&](const uint8_t* data, size_t len) {
+    len = sink(data, len, token);
+    if (ctx) SHA1_Update(ctx, data, len);
+    return len;
+  };
+  return bsdiff::bspatch(old_data, old_size,
+                         reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
+                         patch->data.size(), sha_sink);
 }
 
-int FillBuffer(unsigned char* buffer, int size, bz_stream* stream) {
-    stream->next_out = (char*)buffer;
-    stream->avail_out = size;
-    while (stream->avail_out > 0) {
-        int bzerr = BZ2_bzDecompress(stream);
-        if (bzerr != BZ_OK && bzerr != BZ_STREAM_END) {
-            printf("bz error %d decompressing\n", bzerr);
-            return -1;
-        }
-        if (stream->avail_out > 0) {
-            printf("need %d more bytes\n", stream->avail_out);
-        }
-    }
-    return 0;
-}
-
-int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
-                     const Value* patch, ssize_t patch_offset,
-                     SinkFn sink, void* token, SHA_CTX* ctx) {
-
-    std::vector<unsigned char> new_data;
-    if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset, &new_data) != 0) {
-        return -1;
-    }
-
-    if (sink(new_data.data(), new_data.size(), token) < static_cast<ssize_t>(new_data.size())) {
-        printf("short write of output: %d (%s)\n", errno, strerror(errno));
-        return 1;
-    }
-    if (ctx) SHA1_Update(ctx, new_data.data(), new_data.size());
-    return 0;
-}
-
-int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
-                        const Value* patch, ssize_t patch_offset,
-                        std::vector<unsigned char>* new_data) {
-    // Patch data format:
-    //   0       8       "BSDIFF40"
-    //   8       8       X
-    //   16      8       Y
-    //   24      8       sizeof(newfile)
-    //   32      X       bzip2(control block)
-    //   32+X    Y       bzip2(diff block)
-    //   32+X+Y  ???     bzip2(extra block)
-    // with control block a set of triples (x,y,z) meaning "add x bytes
-    // from oldfile to x bytes from the diff block; copy y bytes from the
-    // extra block; seek forwards in oldfile by z bytes".
-
-    unsigned char* header = (unsigned char*) patch->data + patch_offset;
-    if (memcmp(header, "BSDIFF40", 8) != 0) {
-        printf("corrupt bsdiff patch file header (magic number)\n");
-        return 1;
-    }
-
-    ssize_t ctrl_len, data_len, new_size;
-    ctrl_len = offtin(header+8);
-    data_len = offtin(header+16);
-    new_size = offtin(header+24);
-
-    if (ctrl_len < 0 || data_len < 0 || new_size < 0) {
-        printf("corrupt patch file header (data lengths)\n");
-        return 1;
-    }
-
-    int bzerr;
-
-    bz_stream cstream;
-    cstream.next_in = patch->data + patch_offset + 32;
-    cstream.avail_in = ctrl_len;
-    cstream.bzalloc = NULL;
-    cstream.bzfree = NULL;
-    cstream.opaque = NULL;
-    if ((bzerr = BZ2_bzDecompressInit(&cstream, 0, 0)) != BZ_OK) {
-        printf("failed to bzinit control stream (%d)\n", bzerr);
-    }
-
-    bz_stream dstream;
-    dstream.next_in = patch->data + patch_offset + 32 + ctrl_len;
-    dstream.avail_in = data_len;
-    dstream.bzalloc = NULL;
-    dstream.bzfree = NULL;
-    dstream.opaque = NULL;
-    if ((bzerr = BZ2_bzDecompressInit(&dstream, 0, 0)) != BZ_OK) {
-        printf("failed to bzinit diff stream (%d)\n", bzerr);
-    }
-
-    bz_stream estream;
-    estream.next_in = patch->data + patch_offset + 32 + ctrl_len + data_len;
-    estream.avail_in = patch->size - (patch_offset + 32 + ctrl_len + data_len);
-    estream.bzalloc = NULL;
-    estream.bzfree = NULL;
-    estream.opaque = NULL;
-    if ((bzerr = BZ2_bzDecompressInit(&estream, 0, 0)) != BZ_OK) {
-        printf("failed to bzinit extra stream (%d)\n", bzerr);
-    }
-
-    new_data->resize(new_size);
-
-    off_t oldpos = 0, newpos = 0;
-    off_t ctrl[3];
-    off_t len_read;
-    int i;
-    unsigned char buf[24];
-    while (newpos < new_size) {
-        // Read control data
-        if (FillBuffer(buf, 24, &cstream) != 0) {
-            printf("error while reading control stream\n");
-            return 1;
-        }
-        ctrl[0] = offtin(buf);
-        ctrl[1] = offtin(buf+8);
-        ctrl[2] = offtin(buf+16);
-
-        if (ctrl[0] < 0 || ctrl[1] < 0) {
-            printf("corrupt patch (negative byte counts)\n");
-            return 1;
-        }
-
-        // Sanity check
-        if (newpos + ctrl[0] > new_size) {
-            printf("corrupt patch (new file overrun)\n");
-            return 1;
-        }
-
-        // Read diff string
-        if (FillBuffer(new_data->data() + newpos, ctrl[0], &dstream) != 0) {
-            printf("error while reading diff stream\n");
-            return 1;
-        }
-
-        // Add old data to diff string
-        for (i = 0; i < ctrl[0]; ++i) {
-            if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
-                (*new_data)[newpos+i] += old_data[oldpos+i];
-            }
-        }
-
-        // Adjust pointers
-        newpos += ctrl[0];
-        oldpos += ctrl[0];
-
-        // Sanity check
-        if (newpos + ctrl[1] > new_size) {
-            printf("corrupt patch (new file overrun)\n");
-            return 1;
-        }
-
-        // Read extra string
-        if (FillBuffer(new_data->data() + newpos, ctrl[1], &estream) != 0) {
-            printf("error while reading extra stream\n");
-            return 1;
-        }
-
-        // Adjust pointers
-        newpos += ctrl[1];
-        oldpos += ctrl[2];
-    }
-
-    BZ2_bzDecompressEnd(&cstream);
-    BZ2_bzDecompressEnd(&dstream);
-    BZ2_bzDecompressEnd(&estream);
-    return 0;
+int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, const Value* patch,
+                        ssize_t patch_offset, std::vector<unsigned char>* new_data) {
+  auto vector_sink = [new_data](const uint8_t* data, size_t len) {
+    new_data->insert(new_data->end(), data, data + len);
+    return len;
+  };
+  return bsdiff::bspatch(old_data, old_size,
+                         reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
+                         patch->data.size(), vector_sink);
 }
diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp
index c84f427..331cae2 100644
--- a/applypatch/freecache.cpp
+++ b/applypatch/freecache.cpp
@@ -32,7 +32,7 @@
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 
-#include "applypatch.h"
+#include "applypatch/applypatch.h"
 
 static int EliminateOpenFiles(std::set<std::string>* files) {
   std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/proc"), closedir);
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index f22502e..41d73ab 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -121,618 +121,687 @@
  * information that is stored on the system partition.
  */
 
+#include "applypatch/imgdiff.h"
+
 #include <errno.h>
-#include <inttypes.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
-#include <unistd.h>
 #include <sys/types.h>
+#include <unistd.h>
 
-#include "zlib.h"
-#include "imgdiff.h"
-#include "utils.h"
+#include <algorithm>
+#include <string>
+#include <vector>
 
-typedef struct {
-  int type;             // CHUNK_NORMAL, CHUNK_DEFLATE
-  size_t start;         // offset of chunk in original image file
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/memory.h>
+#include <android-base/unique_fd.h>
+#include <ziparchive/zip_archive.h>
 
-  size_t len;
-  unsigned char* data;  // data to be patched (uncompressed, for deflate chunks)
+#include <bsdiff.h>
+#include <zlib.h>
 
-  size_t source_start;
-  size_t source_len;
+using android::base::get_unaligned;
 
-  off_t* I;             // used by bsdiff
+static constexpr auto BUFFER_SIZE = 0x8000;
+
+// If we use this function to write the offset and length (type size_t), their values should not
+// exceed 2^63; because the signed bit will be casted away.
+static inline bool Write8(int fd, int64_t value) {
+  return android::base::WriteFully(fd, &value, sizeof(int64_t));
+}
+
+// Similarly, the value should not exceed 2^31 if we are casting from size_t (e.g. target chunk
+// size).
+static inline bool Write4(int fd, int32_t value) {
+  return android::base::WriteFully(fd, &value, sizeof(int32_t));
+}
+
+class ImageChunk {
+ public:
+  static constexpr auto WINDOWBITS = -15;  // 32kb window; negative to indicate a raw stream.
+  static constexpr auto MEMLEVEL = 8;      // the default value.
+  static constexpr auto METHOD = Z_DEFLATED;
+  static constexpr auto STRATEGY = Z_DEFAULT_STRATEGY;
+
+  ImageChunk(int type, size_t start, const std::vector<uint8_t>* file_content, size_t raw_data_len)
+      : type_(type),
+        start_(start),
+        input_file_ptr_(file_content),
+        raw_data_len_(raw_data_len),
+        compress_level_(6),
+        source_start_(0),
+        source_len_(0),
+        source_uncompressed_len_(0) {
+    CHECK(file_content != nullptr) << "input file container can't be nullptr";
+  }
+
+  int GetType() const {
+    return type_;
+  }
+  size_t GetRawDataLength() const {
+    return raw_data_len_;
+  }
+  const std::string& GetEntryName() const {
+    return entry_name_;
+  }
+
+  // CHUNK_DEFLATE will return the uncompressed data for diff, while other types will simply return
+  // the raw data.
+  const uint8_t * DataForPatch() const;
+  size_t DataLengthForPatch() const;
+
+  void Dump() const {
+    printf("type %d start %zu len %zu\n", type_, start_, DataLengthForPatch());
+  }
+
+  void SetSourceInfo(const ImageChunk& other);
+  void SetEntryName(std::string entryname);
+  void SetUncompressedData(std::vector<uint8_t> data);
+  bool SetBonusData(const std::vector<uint8_t>& bonus_data);
+
+  bool operator==(const ImageChunk& other) const;
+  bool operator!=(const ImageChunk& other) const {
+    return !(*this == other);
+  }
+
+  size_t GetHeaderSize(size_t patch_size) const;
+  // Return the offset of the next patch into the patch data.
+  size_t WriteHeaderToFd(int fd, const std::vector<uint8_t>& patch, size_t offset);
+
+  /*
+   * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob
+   * of uninterpreted data).  The resulting patch will likely be about
+   * as big as the target file, but it lets us handle the case of images
+   * where some gzip chunks are reconstructible but others aren't (by
+   * treating the ones that aren't as normal chunks).
+   */
+  void ChangeDeflateChunkToNormal();
+  bool ChangeChunkToRaw(size_t patch_size);
+
+  /*
+   * Verify that we can reproduce exactly the same compressed data that
+   * we started with.  Sets the level, method, windowBits, memLevel, and
+   * strategy fields in the chunk to the encoding parameters needed to
+   * produce the right output.
+   */
+  bool ReconstructDeflateChunk();
+  bool IsAdjacentNormal(const ImageChunk& other) const;
+  void MergeAdjacentNormal(const ImageChunk& other);
+
+ private:
+  int type_;                                    // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW
+  size_t start_;                                // offset of chunk in the original input file
+  const std::vector<uint8_t>* input_file_ptr_;  // ptr to the full content of original input file
+  size_t raw_data_len_;
 
   // --- for CHUNK_DEFLATE chunks only: ---
-
-  // original (compressed) deflate data
-  size_t deflate_len;
-  unsigned char* deflate_data;
-
-  char* filename;       // used for zip entries
+  std::vector<uint8_t> uncompressed_data_;
+  std::string entry_name_;  // used for zip entries
 
   // deflate encoder parameters
-  int level, method, windowBits, memLevel, strategy;
+  int compress_level_;
 
-  size_t source_uncompressed_len;
-} ImageChunk;
+  size_t source_start_;
+  size_t source_len_;
+  size_t source_uncompressed_len_;
 
-typedef struct {
-  int data_offset;
-  int deflate_len;
-  int uncomp_len;
-  char* filename;
-} ZipFileEntry;
+  const uint8_t* GetRawData() const;
+  bool TryReconstruction(int level);
+};
 
-static int fileentry_compare(const void* a, const void* b) {
-  int ao = ((ZipFileEntry*)a)->data_offset;
-  int bo = ((ZipFileEntry*)b)->data_offset;
-  if (ao < bo) {
-    return -1;
-  } else if (ao > bo) {
-    return 1;
-  } else {
-    return 0;
+const uint8_t* ImageChunk::GetRawData() const {
+  CHECK_LE(start_ + raw_data_len_, input_file_ptr_->size());
+  return input_file_ptr_->data() + start_;
+}
+
+const uint8_t * ImageChunk::DataForPatch() const {
+  if (type_ == CHUNK_DEFLATE) {
+    return uncompressed_data_.data();
+  }
+  return GetRawData();
+}
+
+size_t ImageChunk::DataLengthForPatch() const {
+  if (type_ == CHUNK_DEFLATE) {
+    return uncompressed_data_.size();
+  }
+  return raw_data_len_;
+}
+
+bool ImageChunk::operator==(const ImageChunk& other) const {
+  if (type_ != other.type_) {
+    return false;
+  }
+  return (raw_data_len_ == other.raw_data_len_ &&
+          memcmp(GetRawData(), other.GetRawData(), raw_data_len_) == 0);
+}
+
+void ImageChunk::SetSourceInfo(const ImageChunk& src) {
+  source_start_ = src.start_;
+  if (type_ == CHUNK_NORMAL) {
+    source_len_ = src.raw_data_len_;
+  } else if (type_ == CHUNK_DEFLATE) {
+    source_len_ = src.raw_data_len_;
+    source_uncompressed_len_ = src.uncompressed_data_.size();
   }
 }
 
-// from bsdiff.c
-int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* newdata, off_t newsize,
-           const char* patch_filename);
+void ImageChunk::SetEntryName(std::string entryname) {
+  entry_name_ = std::move(entryname);
+}
 
-unsigned char* ReadZip(const char* filename,
-                       int* num_chunks, ImageChunk** chunks,
-                       int include_pseudo_chunk) {
-  struct stat st;
-  if (stat(filename, &st) != 0) {
-    printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
-    return NULL;
+void ImageChunk::SetUncompressedData(std::vector<uint8_t> data) {
+  uncompressed_data_ = std::move(data);
+}
+
+bool ImageChunk::SetBonusData(const std::vector<uint8_t>& bonus_data) {
+  if (type_ != CHUNK_DEFLATE) {
+    return false;
   }
+  uncompressed_data_.insert(uncompressed_data_.end(), bonus_data.begin(), bonus_data.end());
+  return true;
+}
 
-  size_t sz = static_cast<size_t>(st.st_size);
-  unsigned char* img = reinterpret_cast<unsigned char*>(malloc(sz));
-  FILE* f = fopen(filename, "rb");
-  if (fread(img, 1, sz, f) != sz) {
-    printf("failed to read \"%s\" %s\n", filename, strerror(errno));
-    fclose(f);
-    return NULL;
+// Convert CHUNK_NORMAL & CHUNK_DEFLATE to CHUNK_RAW if the target size is
+// smaller. Also take the header size into account during size comparison.
+bool ImageChunk::ChangeChunkToRaw(size_t patch_size) {
+  if (type_ == CHUNK_RAW) {
+    return true;
+  } else if (type_ == CHUNK_NORMAL && (raw_data_len_ <= 160 || raw_data_len_ < patch_size)) {
+    type_ = CHUNK_RAW;
+    return true;
   }
-  fclose(f);
+  return false;
+}
 
-  // look for the end-of-central-directory record.
+void ImageChunk::ChangeDeflateChunkToNormal() {
+  if (type_ != CHUNK_DEFLATE) return;
+  type_ = CHUNK_NORMAL;
+  entry_name_.clear();
+  uncompressed_data_.clear();
+}
 
-  int i;
-  for (i = st.st_size-20; i >= 0 && i > st.st_size - 65600; --i) {
-    if (img[i] == 0x50 && img[i+1] == 0x4b &&
-        img[i+2] == 0x05 && img[i+3] == 0x06) {
-      break;
-    }
+// Header size:
+// header_type    4 bytes
+// CHUNK_NORMAL   8*3 = 24 bytes
+// CHUNK_DEFLATE  8*5 + 4*5 = 60 bytes
+// CHUNK_RAW      4 bytes + patch_size
+size_t ImageChunk::GetHeaderSize(size_t patch_size) const {
+  switch (type_) {
+    case CHUNK_NORMAL:
+      return 4 + 8 * 3;
+    case CHUNK_DEFLATE:
+      return 4 + 8 * 5 + 4 * 5;
+    case CHUNK_RAW:
+      return 4 + 4 + patch_size;
+    default:
+      CHECK(false) << "unexpected chunk type: " << type_;  // Should not reach here.
+      return 0;
   }
-  // double-check: this archive consists of a single "disk"
-  if (!(img[i+4] == 0 && img[i+5] == 0 && img[i+6] == 0 && img[i+7] == 0)) {
-    printf("can't process multi-disk archive\n");
-    return NULL;
-  }
+}
 
-  int cdcount = Read2(img+i+8);
-  int cdoffset = Read4(img+i+16);
-
-  ZipFileEntry* temp_entries = reinterpret_cast<ZipFileEntry*>(malloc(
-      cdcount * sizeof(ZipFileEntry)));
-  int entrycount = 0;
-
-  unsigned char* cd = img+cdoffset;
-  for (i = 0; i < cdcount; ++i) {
-    if (!(cd[0] == 0x50 && cd[1] == 0x4b && cd[2] == 0x01 && cd[3] == 0x02)) {
-      printf("bad central directory entry %d\n", i);
-      return NULL;
-    }
-
-    int clen = Read4(cd+20);   // compressed len
-    int ulen = Read4(cd+24);   // uncompressed len
-    int nlen = Read2(cd+28);   // filename len
-    int xlen = Read2(cd+30);   // extra field len
-    int mlen = Read2(cd+32);   // file comment len
-    int hoffset = Read4(cd+42);   // local header offset
-
-    char* filename = reinterpret_cast<char*>(malloc(nlen+1));
-    memcpy(filename, cd+46, nlen);
-    filename[nlen] = '\0';
-
-    int method = Read2(cd+10);
-
-    cd += 46 + nlen + xlen + mlen;
-
-    if (method != 8) {  // 8 == deflate
-      free(filename);
-      continue;
-    }
-
-    unsigned char* lh = img + hoffset;
-
-    if (!(lh[0] == 0x50 && lh[1] == 0x4b && lh[2] == 0x03 && lh[3] == 0x04)) {
-      printf("bad local file header entry %d\n", i);
-      return NULL;
-    }
-
-    if (Read2(lh+26) != nlen || memcmp(lh+30, filename, nlen) != 0) {
-      printf("central dir filename doesn't match local header\n");
-      return NULL;
-    }
-
-    xlen = Read2(lh+28);   // extra field len; might be different from CD entry?
-
-    temp_entries[entrycount].data_offset = hoffset+30+nlen+xlen;
-    temp_entries[entrycount].deflate_len = clen;
-    temp_entries[entrycount].uncomp_len = ulen;
-    temp_entries[entrycount].filename = filename;
-    ++entrycount;
-  }
-
-  qsort(temp_entries, entrycount, sizeof(ZipFileEntry), fileentry_compare);
-
-#if 0
-  printf("found %d deflated entries\n", entrycount);
-  for (i = 0; i < entrycount; ++i) {
-    printf("off %10d  len %10d unlen %10d   %p %s\n",
-           temp_entries[i].data_offset,
-           temp_entries[i].deflate_len,
-           temp_entries[i].uncomp_len,
-           temp_entries[i].filename,
-           temp_entries[i].filename);
-  }
-#endif
-
-  *num_chunks = 0;
-  *chunks = reinterpret_cast<ImageChunk*>(malloc((entrycount*2+2) * sizeof(ImageChunk)));
-  ImageChunk* curr = *chunks;
-
-  if (include_pseudo_chunk) {
-    curr->type = CHUNK_NORMAL;
-    curr->start = 0;
-    curr->len = st.st_size;
-    curr->data = img;
-    curr->filename = NULL;
-    curr->I = NULL;
-    ++curr;
-    ++*num_chunks;
-  }
-
-  int pos = 0;
-  int nextentry = 0;
-
-  while (pos < st.st_size) {
-    if (nextentry < entrycount && pos == temp_entries[nextentry].data_offset) {
-      curr->type = CHUNK_DEFLATE;
-      curr->start = pos;
-      curr->deflate_len = temp_entries[nextentry].deflate_len;
-      curr->deflate_data = img + pos;
-      curr->filename = temp_entries[nextentry].filename;
-      curr->I = NULL;
-
-      curr->len = temp_entries[nextentry].uncomp_len;
-      curr->data = reinterpret_cast<unsigned char*>(malloc(curr->len));
-
-      z_stream strm;
-      strm.zalloc = Z_NULL;
-      strm.zfree = Z_NULL;
-      strm.opaque = Z_NULL;
-      strm.avail_in = curr->deflate_len;
-      strm.next_in = curr->deflate_data;
-
-      // -15 means we are decoding a 'raw' deflate stream; zlib will
-      // not expect zlib headers.
-      int ret = inflateInit2(&strm, -15);
-
-      strm.avail_out = curr->len;
-      strm.next_out = curr->data;
-      ret = inflate(&strm, Z_NO_FLUSH);
-      if (ret != Z_STREAM_END) {
-        printf("failed to inflate \"%s\"; %d\n", curr->filename, ret);
-        return NULL;
+size_t ImageChunk::WriteHeaderToFd(int fd, const std::vector<uint8_t>& patch, size_t offset) {
+  Write4(fd, type_);
+  switch (type_) {
+    case CHUNK_NORMAL:
+      printf("normal   (%10zu, %10zu)  %10zu\n", start_, raw_data_len_, patch.size());
+      Write8(fd, static_cast<int64_t>(source_start_));
+      Write8(fd, static_cast<int64_t>(source_len_));
+      Write8(fd, static_cast<int64_t>(offset));
+      return offset + patch.size();
+    case CHUNK_DEFLATE:
+      printf("deflate  (%10zu, %10zu)  %10zu  %s\n", start_, raw_data_len_, patch.size(),
+             entry_name_.c_str());
+      Write8(fd, static_cast<int64_t>(source_start_));
+      Write8(fd, static_cast<int64_t>(source_len_));
+      Write8(fd, static_cast<int64_t>(offset));
+      Write8(fd, static_cast<int64_t>(source_uncompressed_len_));
+      Write8(fd, static_cast<int64_t>(uncompressed_data_.size()));
+      Write4(fd, compress_level_);
+      Write4(fd, METHOD);
+      Write4(fd, WINDOWBITS);
+      Write4(fd, MEMLEVEL);
+      Write4(fd, STRATEGY);
+      return offset + patch.size();
+    case CHUNK_RAW:
+      printf("raw      (%10zu, %10zu)\n", start_, raw_data_len_);
+      Write4(fd, static_cast<int32_t>(patch.size()));
+      if (!android::base::WriteFully(fd, patch.data(), patch.size())) {
+        CHECK(false) << "failed to write " << patch.size() <<" bytes patch";
       }
+      return offset;
+    default:
+      CHECK(false) << "unexpected chunk type: " << type_;
+      return offset;
+  }
+}
 
-      inflateEnd(&strm);
+bool ImageChunk::IsAdjacentNormal(const ImageChunk& other) const {
+  if (type_ != CHUNK_NORMAL || other.type_ != CHUNK_NORMAL) {
+    return false;
+  }
+  return (other.start_ == start_ + raw_data_len_);
+}
 
-      pos += curr->deflate_len;
-      ++nextentry;
-      ++*num_chunks;
-      ++curr;
-      continue;
-    }
+void ImageChunk::MergeAdjacentNormal(const ImageChunk& other) {
+  CHECK(IsAdjacentNormal(other));
+  raw_data_len_ = raw_data_len_ + other.raw_data_len_;
+}
 
-    // use a normal chunk to take all the data up to the start of the
-    // next deflate section.
-
-    curr->type = CHUNK_NORMAL;
-    curr->start = pos;
-    if (nextentry < entrycount) {
-      curr->len = temp_entries[nextentry].data_offset - pos;
-    } else {
-      curr->len = st.st_size - pos;
-    }
-    curr->data = img + pos;
-    curr->filename = NULL;
-    curr->I = NULL;
-    pos += curr->len;
-
-    ++*num_chunks;
-    ++curr;
+bool ImageChunk::ReconstructDeflateChunk() {
+  if (type_ != CHUNK_DEFLATE) {
+    printf("attempt to reconstruct non-deflate chunk\n");
+    return false;
   }
 
-  free(temp_entries);
-  return img;
+  // We only check two combinations of encoder parameters:  level 6
+  // (the default) and level 9 (the maximum).
+  for (int level = 6; level <= 9; level += 3) {
+    if (TryReconstruction(level)) {
+      compress_level_ = level;
+      return true;
+    }
+  }
+
+  return false;
 }
 
 /*
- * Read the given file and break it up into chunks, putting the number
- * of chunks and their info in *num_chunks and **chunks,
- * respectively.  Returns a malloc'd block of memory containing the
- * contents of the file; various pointers in the output chunk array
- * will point into this block of memory.  The caller should free the
- * return value when done with all the chunks.  Returns NULL on
- * failure.
+ * Takes the uncompressed data stored in the chunk, compresses it
+ * using the zlib parameters stored in the chunk, and checks that it
+ * matches exactly the compressed data we started with (also stored in
+ * the chunk).
  */
-unsigned char* ReadImage(const char* filename,
-                         int* num_chunks, ImageChunk** chunks) {
+bool ImageChunk::TryReconstruction(int level) {
+  z_stream strm;
+  strm.zalloc = Z_NULL;
+  strm.zfree = Z_NULL;
+  strm.opaque = Z_NULL;
+  strm.avail_in = uncompressed_data_.size();
+  strm.next_in = uncompressed_data_.data();
+  int ret = deflateInit2(&strm, level, METHOD, WINDOWBITS, MEMLEVEL, STRATEGY);
+  if (ret < 0) {
+    printf("failed to initialize deflate: %d\n", ret);
+    return false;
+  }
+
+  std::vector<uint8_t> buffer(BUFFER_SIZE);
+  size_t offset = 0;
+  do {
+    strm.avail_out = buffer.size();
+    strm.next_out = buffer.data();
+    ret = deflate(&strm, Z_FINISH);
+    if (ret < 0) {
+      printf("failed to deflate: %d\n", ret);
+      return false;
+    }
+
+    size_t compressed_size = buffer.size() - strm.avail_out;
+    if (memcmp(buffer.data(), input_file_ptr_->data() + start_ + offset, compressed_size) != 0) {
+      // mismatch; data isn't the same.
+      deflateEnd(&strm);
+      return false;
+    }
+    offset += compressed_size;
+  } while (ret != Z_STREAM_END);
+  deflateEnd(&strm);
+
+  if (offset != raw_data_len_) {
+    // mismatch; ran out of data before we should have.
+    return false;
+  }
+  return true;
+}
+
+// EOCD record
+// offset 0: signature 0x06054b50, 4 bytes
+// offset 4: number of this disk, 2 bytes
+// ...
+// offset 20: comment length, 2 bytes
+// offset 22: comment, n bytes
+static bool GetZipFileSize(const std::vector<uint8_t>& zip_file, size_t* input_file_size) {
+  if (zip_file.size() < 22) {
+    printf("file is too small to be a zip file\n");
+    return false;
+  }
+
+  // Look for End of central directory record of the zip file, and calculate the actual
+  // zip_file size.
+  for (int i = zip_file.size() - 22; i >= 0; i--) {
+    if (zip_file[i] == 0x50) {
+      if (get_unaligned<uint32_t>(&zip_file[i]) == 0x06054b50) {
+        // double-check: this archive consists of a single "disk".
+        CHECK_EQ(get_unaligned<uint16_t>(&zip_file[i + 4]), 0);
+
+        uint16_t comment_length = get_unaligned<uint16_t>(&zip_file[i + 20]);
+        size_t file_size = i + 22 + comment_length;
+        CHECK_LE(file_size, zip_file.size());
+        *input_file_size = file_size;
+        return true;
+      }
+    }
+  }
+
+  // EOCD not found, this file is likely not a valid zip file.
+  return false;
+}
+
+static bool ReadZip(const char* filename, std::vector<ImageChunk>* chunks,
+                    std::vector<uint8_t>* zip_file, bool include_pseudo_chunk) {
+  CHECK(chunks != nullptr && zip_file != nullptr);
+
+  android::base::unique_fd fd(open(filename, O_RDONLY));
+  if (fd == -1) {
+    printf("failed to open \"%s\" %s\n", filename, strerror(errno));
+    return false;
+  }
   struct stat st;
-  if (stat(filename, &st) != 0) {
+  if (fstat(fd, &st) != 0) {
     printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
-    return NULL;
+    return false;
   }
 
   size_t sz = static_cast<size_t>(st.st_size);
-  unsigned char* img = reinterpret_cast<unsigned char*>(malloc(sz + 4));
-  FILE* f = fopen(filename, "rb");
-  if (fread(img, 1, sz, f) != sz) {
+  zip_file->resize(sz);
+  if (!android::base::ReadFully(fd, zip_file->data(), sz)) {
     printf("failed to read \"%s\" %s\n", filename, strerror(errno));
-    fclose(f);
-    return NULL;
+    return false;
   }
-  fclose(f);
+  fd.reset();
 
-  // append 4 zero bytes to the data so we can always search for the
-  // four-byte string 1f8b0800 starting at any point in the actual
-  // file data, without special-casing the end of the data.
-  memset(img+sz, 0, 4);
+  // Trim the trailing zeros before we pass the file to ziparchive handler.
+  size_t zipfile_size;
+  if (!GetZipFileSize(*zip_file, &zipfile_size)) {
+    printf("failed to parse the actual size of %s\n", filename);
+    return false;
+  }
+  ZipArchiveHandle handle;
+  int err = OpenArchiveFromMemory(zip_file->data(), zipfile_size, filename, &handle);
+  if (err != 0) {
+    printf("failed to open zip file %s: %s\n", filename, ErrorCodeString(err));
+    CloseArchive(handle);
+    return false;
+  }
+
+  // Create a list of deflated zip entries, sorted by offset.
+  std::vector<std::pair<std::string, ZipEntry>> temp_entries;
+  void* cookie;
+  int ret = StartIteration(handle, &cookie, nullptr, nullptr);
+  if (ret != 0) {
+    printf("failed to iterate over entries in %s: %s\n", filename, ErrorCodeString(ret));
+    CloseArchive(handle);
+    return false;
+  }
+
+  ZipString name;
+  ZipEntry entry;
+  while ((ret = Next(cookie, &entry, &name)) == 0) {
+    if (entry.method == kCompressDeflated) {
+      std::string entryname(name.name, name.name + name.name_length);
+      temp_entries.push_back(std::make_pair(entryname, entry));
+    }
+  }
+
+  if (ret != -1) {
+    printf("Error while iterating over zip entries: %s\n", ErrorCodeString(ret));
+    CloseArchive(handle);
+    return false;
+  }
+  std::sort(temp_entries.begin(), temp_entries.end(),
+            [](auto& entry1, auto& entry2) {
+              return entry1.second.offset < entry2.second.offset;
+            });
+
+  EndIteration(cookie);
+
+  if (include_pseudo_chunk) {
+    chunks->emplace_back(CHUNK_NORMAL, 0, zip_file, zip_file->size());
+  }
+
+  size_t pos = 0;
+  size_t nextentry = 0;
+  while (pos < zip_file->size()) {
+    if (nextentry < temp_entries.size() &&
+        static_cast<off64_t>(pos) == temp_entries[nextentry].second.offset) {
+      // compose the next deflate chunk.
+      std::string entryname = temp_entries[nextentry].first;
+      size_t uncompressed_len = temp_entries[nextentry].second.uncompressed_length;
+      std::vector<uint8_t> uncompressed_data(uncompressed_len);
+      if ((ret = ExtractToMemory(handle, &temp_entries[nextentry].second, uncompressed_data.data(),
+                                 uncompressed_len)) != 0) {
+        printf("failed to extract %s with size %zu: %s\n", entryname.c_str(), uncompressed_len,
+               ErrorCodeString(ret));
+        CloseArchive(handle);
+        return false;
+      }
+
+      size_t compressed_len = temp_entries[nextentry].second.compressed_length;
+      ImageChunk curr(CHUNK_DEFLATE, pos, zip_file, compressed_len);
+      curr.SetEntryName(std::move(entryname));
+      curr.SetUncompressedData(std::move(uncompressed_data));
+      chunks->push_back(curr);
+
+      pos += compressed_len;
+      ++nextentry;
+      continue;
+    }
+
+    // Use a normal chunk to take all the data up to the start of the next deflate section.
+    size_t raw_data_len;
+    if (nextentry < temp_entries.size()) {
+      raw_data_len = temp_entries[nextentry].second.offset - pos;
+    } else {
+      raw_data_len = zip_file->size() - pos;
+    }
+    chunks->emplace_back(CHUNK_NORMAL, pos, zip_file, raw_data_len);
+
+    pos += raw_data_len;
+  }
+
+  CloseArchive(handle);
+  return true;
+}
+
+// Read the given file and break it up into chunks, and putting the data in to a vector.
+static bool ReadImage(const char* filename, std::vector<ImageChunk>* chunks,
+                      std::vector<uint8_t>* img) {
+  CHECK(chunks != nullptr && img != nullptr);
+
+  android::base::unique_fd fd(open(filename, O_RDONLY));
+  if (fd == -1) {
+    printf("failed to open \"%s\" %s\n", filename, strerror(errno));
+    return false;
+  }
+  struct stat st;
+  if (fstat(fd, &st) != 0) {
+    printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
+    return false;
+  }
+
+  size_t sz = static_cast<size_t>(st.st_size);
+  img->resize(sz);
+  if (!android::base::ReadFully(fd, img->data(), sz)) {
+    printf("failed to read \"%s\" %s\n", filename, strerror(errno));
+    return false;
+  }
 
   size_t pos = 0;
 
-  *num_chunks = 0;
-  *chunks = NULL;
-
   while (pos < sz) {
-    unsigned char* p = img+pos;
-
-    bool processed_deflate = false;
-    if (sz - pos >= 4 &&
-        p[0] == 0x1f && p[1] == 0x8b &&
-        p[2] == 0x08 &&    // deflate compression
-        p[3] == 0x00) {    // no header flags
+    // 0x00 no header flags, 0x08 deflate compression, 0x1f8b gzip magic number
+    if (sz - pos >= 4 && get_unaligned<uint32_t>(img->data() + pos) == 0x00088b1f) {
       // 'pos' is the offset of the start of a gzip chunk.
       size_t chunk_offset = pos;
 
-      *num_chunks += 3;
-      *chunks = reinterpret_cast<ImageChunk*>(realloc(*chunks,
-          *num_chunks * sizeof(ImageChunk)));
-      ImageChunk* curr = *chunks + (*num_chunks-3);
+      // The remaining data is too small to be a gzip chunk; treat them as a normal chunk.
+      if (sz - pos < GZIP_HEADER_LEN + GZIP_FOOTER_LEN) {
+        chunks->emplace_back(CHUNK_NORMAL, pos, img, sz - pos);
+        break;
+      }
 
-      // create a normal chunk for the header.
-      curr->start = pos;
-      curr->type = CHUNK_NORMAL;
-      curr->len = GZIP_HEADER_LEN;
-      curr->data = p;
-      curr->I = NULL;
+      // We need three chunks for the deflated image in total, one normal chunk for the header,
+      // one deflated chunk for the body, and another normal chunk for the footer.
+      chunks->emplace_back(CHUNK_NORMAL, pos, img, GZIP_HEADER_LEN);
+      pos += GZIP_HEADER_LEN;
 
-      pos += curr->len;
-      p += curr->len;
-      ++curr;
-
-      curr->type = CHUNK_DEFLATE;
-      curr->filename = NULL;
-      curr->I = NULL;
-
-      // We must decompress this chunk in order to discover where it
-      // ends, and so we can put the uncompressed data and its length
-      // into curr->data and curr->len.
-
-      size_t allocated = 32768;
-      curr->len = 0;
-      curr->data = reinterpret_cast<unsigned char*>(malloc(allocated));
-      curr->start = pos;
-      curr->deflate_data = p;
+      // We must decompress this chunk in order to discover where it ends, and so we can update
+      // the uncompressed_data of the image body and its length.
 
       z_stream strm;
       strm.zalloc = Z_NULL;
       strm.zfree = Z_NULL;
       strm.opaque = Z_NULL;
       strm.avail_in = sz - pos;
-      strm.next_in = p;
+      strm.next_in = img->data() + pos;
 
       // -15 means we are decoding a 'raw' deflate stream; zlib will
       // not expect zlib headers.
       int ret = inflateInit2(&strm, -15);
+      if (ret < 0) {
+        printf("failed to initialize inflate: %d\n", ret);
+        return false;
+      }
 
+      size_t allocated = BUFFER_SIZE;
+      std::vector<uint8_t> uncompressed_data(allocated);
+      size_t uncompressed_len = 0, raw_data_len = 0;
       do {
-        strm.avail_out = allocated - curr->len;
-        strm.next_out = curr->data + curr->len;
+        strm.avail_out = allocated - uncompressed_len;
+        strm.next_out = uncompressed_data.data() + uncompressed_len;
         ret = inflate(&strm, Z_NO_FLUSH);
         if (ret < 0) {
-          if (!processed_deflate) {
-            // This is the first chunk, assume that it's just a spurious
-            // gzip header instead of a real one.
-            break;
-          }
-          printf("Error: inflate failed [%s] at file offset [%zu]\n"
-                 "imgdiff only supports gzip kernel compression,"
-                 " did you try CONFIG_KERNEL_LZO?\n",
+          printf("Warning: inflate failed [%s] at offset [%zu], treating as a normal chunk\n",
                  strm.msg, chunk_offset);
-          free(img);
-          return NULL;
+          break;
         }
-        curr->len = allocated - strm.avail_out;
+        uncompressed_len = allocated - strm.avail_out;
         if (strm.avail_out == 0) {
           allocated *= 2;
-          curr->data = reinterpret_cast<unsigned char*>(realloc(curr->data, allocated));
+          uncompressed_data.resize(allocated);
         }
-        processed_deflate = true;
       } while (ret != Z_STREAM_END);
 
-      curr->deflate_len = sz - strm.avail_in - pos;
+      raw_data_len = sz - strm.avail_in - pos;
       inflateEnd(&strm);
-      pos += curr->deflate_len;
-      p += curr->deflate_len;
-      ++curr;
+
+      if (ret < 0) {
+        continue;
+      }
+
+      ImageChunk body(CHUNK_DEFLATE, pos, img, raw_data_len);
+      uncompressed_data.resize(uncompressed_len);
+      body.SetUncompressedData(std::move(uncompressed_data));
+      chunks->push_back(body);
+
+      pos += raw_data_len;
 
       // create a normal chunk for the footer
+      chunks->emplace_back(CHUNK_NORMAL, pos, img, GZIP_FOOTER_LEN);
 
-      curr->type = CHUNK_NORMAL;
-      curr->start = pos;
-      curr->len = GZIP_FOOTER_LEN;
-      curr->data = img+pos;
-      curr->I = NULL;
-
-      pos += curr->len;
-      p += curr->len;
-      ++curr;
+      pos += GZIP_FOOTER_LEN;
 
       // The footer (that we just skipped over) contains the size of
       // the uncompressed data.  Double-check to make sure that it
       // matches the size of the data we got when we actually did
       // the decompression.
-      size_t footer_size = Read4(p-4);
-      if (footer_size != curr[-2].len) {
-        printf("Error: footer size %zu != decompressed size %zu\n",
-            footer_size, curr[-2].len);
-        free(img);
-        return NULL;
+      size_t footer_size = get_unaligned<uint32_t>(img->data() + pos - 4);
+      if (footer_size != body.DataLengthForPatch()) {
+        printf("Error: footer size %zu != decompressed size %zu\n", footer_size,
+               body.GetRawDataLength());
+        return false;
       }
     } else {
-      // Reallocate the list for every chunk; we expect the number of
-      // chunks to be small (5 for typical boot and recovery images).
-      ++*num_chunks;
-      *chunks = reinterpret_cast<ImageChunk*>(realloc(*chunks, *num_chunks * sizeof(ImageChunk)));
-      ImageChunk* curr = *chunks + (*num_chunks-1);
-      curr->start = pos;
-      curr->I = NULL;
+      // Use a normal chunk to take all the contents until the next gzip chunk (or EOF); we expect
+      // the number of chunks to be small (5 for typical boot and recovery images).
 
-      // 'pos' is not the offset of the start of a gzip chunk, so scan
-      // forward until we find a gzip header.
-      curr->type = CHUNK_NORMAL;
-      curr->data = p;
-
-      for (curr->len = 0; curr->len < (sz - pos); ++curr->len) {
-        if (p[curr->len] == 0x1f &&
-            p[curr->len+1] == 0x8b &&
-            p[curr->len+2] == 0x08 &&
-            p[curr->len+3] == 0x00) {
+      // Scan forward until we find a gzip header.
+      size_t data_len = 0;
+      while (data_len + pos < sz) {
+        if (data_len + pos + 4 <= sz &&
+            get_unaligned<uint32_t>(img->data() + pos + data_len) == 0x00088b1f) {
           break;
         }
+        data_len++;
       }
-      pos += curr->len;
+      chunks->emplace_back(CHUNK_NORMAL, pos, img, data_len);
+
+      pos += data_len;
     }
   }
 
-  return img;
+  return true;
 }
 
-#define BUFFER_SIZE 32768
-
 /*
- * Takes the uncompressed data stored in the chunk, compresses it
- * using the zlib parameters stored in the chunk, and checks that it
- * matches exactly the compressed data we started with (also stored in
- * the chunk).  Return 0 on success.
+ * Given source and target chunks, compute a bsdiff patch between them.
+ * Store the result in the patch_data.
+ * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk
+ * is used repeatedly, pass nullptr if not needed.
  */
-int TryReconstruction(ImageChunk* chunk, unsigned char* out) {
-  size_t p = 0;
+static bool MakePatch(const ImageChunk* src, ImageChunk* tgt, std::vector<uint8_t>* patch_data,
+                      saidx_t** bsdiff_cache) {
+  if (tgt->ChangeChunkToRaw(0)) {
+    size_t patch_size = tgt->DataLengthForPatch();
+    patch_data->resize(patch_size);
+    std::copy(tgt->DataForPatch(), tgt->DataForPatch() + patch_size, patch_data->begin());
+    return true;
+  }
 
-#if 0
-  printf("trying %d %d %d %d %d\n",
-          chunk->level, chunk->method, chunk->windowBits,
-          chunk->memLevel, chunk->strategy);
+#if defined(__ANDROID__)
+  char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX";
+#else
+  char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
 #endif
 
-  z_stream strm;
-  strm.zalloc = Z_NULL;
-  strm.zfree = Z_NULL;
-  strm.opaque = Z_NULL;
-  strm.avail_in = chunk->len;
-  strm.next_in = chunk->data;
-  int ret;
-  ret = deflateInit2(&strm, chunk->level, chunk->method, chunk->windowBits,
-                     chunk->memLevel, chunk->strategy);
-  do {
-    strm.avail_out = BUFFER_SIZE;
-    strm.next_out = out;
-    ret = deflate(&strm, Z_FINISH);
-    size_t have = BUFFER_SIZE - strm.avail_out;
-
-    if (memcmp(out, chunk->deflate_data+p, have) != 0) {
-      // mismatch; data isn't the same.
-      deflateEnd(&strm);
-      return -1;
-    }
-    p += have;
-  } while (ret != Z_STREAM_END);
-  deflateEnd(&strm);
-  if (p != chunk->deflate_len) {
-    // mismatch; ran out of data before we should have.
-    return -1;
-  }
-  return 0;
-}
-
-/*
- * Verify that we can reproduce exactly the same compressed data that
- * we started with.  Sets the level, method, windowBits, memLevel, and
- * strategy fields in the chunk to the encoding parameters needed to
- * produce the right output.  Returns 0 on success.
- */
-int ReconstructDeflateChunk(ImageChunk* chunk) {
-  if (chunk->type != CHUNK_DEFLATE) {
-    printf("attempt to reconstruct non-deflate chunk\n");
-    return -1;
-  }
-
-  size_t p = 0;
-  unsigned char* out = reinterpret_cast<unsigned char*>(malloc(BUFFER_SIZE));
-
-  // We only check two combinations of encoder parameters:  level 6
-  // (the default) and level 9 (the maximum).
-  for (chunk->level = 6; chunk->level <= 9; chunk->level += 3) {
-    chunk->windowBits = -15;  // 32kb window; negative to indicate a raw stream.
-    chunk->memLevel = 8;      // the default value.
-    chunk->method = Z_DEFLATED;
-    chunk->strategy = Z_DEFAULT_STRATEGY;
-
-    if (TryReconstruction(chunk, out) == 0) {
-      free(out);
-      return 0;
-    }
-  }
-
-  free(out);
-  return -1;
-}
-
-/*
- * Given source and target chunks, compute a bsdiff patch between them
- * by running bsdiff in a subprocess.  Return the patch data, placing
- * its length in *size.  Return NULL on failure.  We expect the bsdiff
- * program to be in the path.
- */
-unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
-  if (tgt->type == CHUNK_NORMAL) {
-    if (tgt->len <= 160) {
-      tgt->type = CHUNK_RAW;
-      *size = tgt->len;
-      return tgt->data;
-    }
-  }
-
-  char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
   int fd = mkstemp(ptemp);
-
   if (fd == -1) {
-    printf("MakePatch failed to create a temporary file: %s\n",
-           strerror(errno));
-    return NULL;
+    printf("MakePatch failed to create a temporary file: %s\n", strerror(errno));
+    return false;
   }
-  close(fd); // temporary file is created and we don't need its file
-             // descriptor
+  close(fd);
 
-  int r = bsdiff(src->data, src->len, &(src->I), tgt->data, tgt->len, ptemp);
+  int r = bsdiff::bsdiff(src->DataForPatch(), src->DataLengthForPatch(), tgt->DataForPatch(),
+                         tgt->DataLengthForPatch(), ptemp, bsdiff_cache);
   if (r != 0) {
     printf("bsdiff() failed: %d\n", r);
-    return NULL;
+    return false;
   }
 
+  android::base::unique_fd patch_fd(open(ptemp, O_RDONLY));
+  if (patch_fd == -1) {
+    printf("failed to open %s: %s\n", ptemp, strerror(errno));
+    return false;
+  }
   struct stat st;
-  if (stat(ptemp, &st) != 0) {
-    printf("failed to stat patch file %s: %s\n",
-            ptemp, strerror(errno));
-    return NULL;
+  if (fstat(patch_fd, &st) != 0) {
+    printf("failed to stat patch file %s: %s\n", ptemp, strerror(errno));
+    return false;
   }
 
   size_t sz = static_cast<size_t>(st.st_size);
-  // TODO: Memory leak on error return.
-  unsigned char* data = reinterpret_cast<unsigned char*>(malloc(sz));
-
-  if (tgt->type == CHUNK_NORMAL && tgt->len <= sz) {
+  // Change the chunk type to raw if the patch takes less space that way.
+  if (tgt->ChangeChunkToRaw(sz)) {
     unlink(ptemp);
-
-    tgt->type = CHUNK_RAW;
-    *size = tgt->len;
-    return tgt->data;
+    size_t patch_size = tgt->DataLengthForPatch();
+    patch_data->resize(patch_size);
+    std::copy(tgt->DataForPatch(), tgt->DataForPatch() + patch_size, patch_data->begin());
+    return true;
   }
-
-  *size = sz;
-
-  FILE* f = fopen(ptemp, "rb");
-  if (f == NULL) {
-    printf("failed to open patch %s: %s\n", ptemp, strerror(errno));
-    return NULL;
+  patch_data->resize(sz);
+  if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) {
+    printf("failed to read \"%s\" %s\n", ptemp, strerror(errno));
+    return false;
   }
-  if (fread(data, 1, sz, f) != sz) {
-    printf("failed to read patch %s: %s\n", ptemp, strerror(errno));
-    return NULL;
-  }
-  fclose(f);
 
   unlink(ptemp);
+  tgt->SetSourceInfo(*src);
 
-  tgt->source_start = src->start;
-  switch (tgt->type) {
-    case CHUNK_NORMAL:
-      tgt->source_len = src->len;
-      break;
-    case CHUNK_DEFLATE:
-      tgt->source_len = src->deflate_len;
-      tgt->source_uncompressed_len = src->len;
-      break;
-  }
-
-  return data;
-}
-
-/*
- * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob
- * of uninterpreted data).  The resulting patch will likely be about
- * as big as the target file, but it lets us handle the case of images
- * where some gzip chunks are reconstructible but others aren't (by
- * treating the ones that aren't as normal chunks).
- */
-void ChangeDeflateChunkToNormal(ImageChunk* ch) {
-  if (ch->type != CHUNK_DEFLATE) return;
-  ch->type = CHUNK_NORMAL;
-  free(ch->data);
-  ch->data = ch->deflate_data;
-  ch->len = ch->deflate_len;
-}
-
-/*
- * Return true if the data in the chunk is identical (including the
- * compressed representation, for gzip chunks).
- */
-int AreChunksEqual(ImageChunk* a, ImageChunk* b) {
-    if (a->type != b->type) return 0;
-
-    switch (a->type) {
-        case CHUNK_NORMAL:
-            return a->len == b->len && memcmp(a->data, b->data, a->len) == 0;
-
-        case CHUNK_DEFLATE:
-            return a->deflate_len == b->deflate_len &&
-                memcmp(a->deflate_data, b->deflate_data, a->deflate_len) == 0;
-
-        default:
-            printf("unknown chunk type %d\n", a->type);
-            return 0;
-    }
+  return true;
 }
 
 /*
@@ -740,137 +809,103 @@
  * a single chunk.  (Such runs can be produced when deflate chunks are
  * changed to normal chunks.)
  */
-void MergeAdjacentNormalChunks(ImageChunk* chunks, int* num_chunks) {
-  int out = 0;
-  int in_start = 0, in_end;
-  while (in_start < *num_chunks) {
-    if (chunks[in_start].type != CHUNK_NORMAL) {
-      in_end = in_start+1;
-    } else {
-      // in_start is a normal chunk.  Look for a run of normal chunks
-      // that constitute a solid block of data (ie, each chunk begins
-      // where the previous one ended).
-      for (in_end = in_start+1;
-           in_end < *num_chunks && chunks[in_end].type == CHUNK_NORMAL &&
-             (chunks[in_end].start ==
-              chunks[in_end-1].start + chunks[in_end-1].len &&
-              chunks[in_end].data ==
-              chunks[in_end-1].data + chunks[in_end-1].len);
-           ++in_end);
+static void MergeAdjacentNormalChunks(std::vector<ImageChunk>* chunks) {
+  size_t merged_last = 0, cur = 0;
+  while (cur < chunks->size()) {
+    // Look for normal chunks adjacent to the current one. If such chunk exists, extend the
+    // length of the current normal chunk.
+    size_t to_check = cur + 1;
+    while (to_check < chunks->size() && chunks->at(cur).IsAdjacentNormal(chunks->at(to_check))) {
+      chunks->at(cur).MergeAdjacentNormal(chunks->at(to_check));
+      to_check++;
     }
 
-    if (in_end == in_start+1) {
-#if 0
-      printf("chunk %d is now %d\n", in_start, out);
-#endif
-      if (out != in_start) {
-        memcpy(chunks+out, chunks+in_start, sizeof(ImageChunk));
-      }
-    } else {
-#if 0
-      printf("collapse normal chunks %d-%d into %d\n", in_start, in_end-1, out);
-#endif
-
-      // Merge chunks [in_start, in_end-1] into one chunk.  Since the
-      // data member of each chunk is just a pointer into an in-memory
-      // copy of the file, this can be done without recopying (the
-      // output chunk has the first chunk's start location and data
-      // pointer, and length equal to the sum of the input chunk
-      // lengths).
-      chunks[out].type = CHUNK_NORMAL;
-      chunks[out].start = chunks[in_start].start;
-      chunks[out].data = chunks[in_start].data;
-      chunks[out].len = chunks[in_end-1].len +
-        (chunks[in_end-1].start - chunks[in_start].start);
+    if (merged_last != cur) {
+      chunks->at(merged_last) = std::move(chunks->at(cur));
     }
-
-    ++out;
-    in_start = in_end;
+    merged_last++;
+    cur = to_check;
   }
-  *num_chunks = out;
+  if (merged_last < chunks->size()) {
+    chunks->erase(chunks->begin() + merged_last, chunks->end());
+  }
 }
 
-ImageChunk* FindChunkByName(const char* name,
-                            ImageChunk* chunks, int num_chunks) {
-  int i;
-  for (i = 0; i < num_chunks; ++i) {
-    if (chunks[i].type == CHUNK_DEFLATE && chunks[i].filename &&
-        strcmp(name, chunks[i].filename) == 0) {
-      return chunks+i;
+static ImageChunk* FindChunkByName(const std::string& name, std::vector<ImageChunk>& chunks) {
+  for (size_t i = 0; i < chunks.size(); ++i) {
+    if (chunks[i].GetType() == CHUNK_DEFLATE && chunks[i].GetEntryName() == name) {
+      return &chunks[i];
     }
   }
-  return NULL;
+  return nullptr;
 }
 
-void DumpChunks(ImageChunk* chunks, int num_chunks) {
-    for (int i = 0; i < num_chunks; ++i) {
-        printf("chunk %d: type %d start %zu len %zu\n",
-               i, chunks[i].type, chunks[i].start, chunks[i].len);
-    }
+static void DumpChunks(const std::vector<ImageChunk>& chunks) {
+  for (size_t i = 0; i < chunks.size(); ++i) {
+    printf("chunk %zu: ", i);
+    chunks[i].Dump();
+  }
 }
 
-int main(int argc, char** argv) {
-  int zip_mode = 0;
+int imgdiff(int argc, const char** argv) {
+  bool zip_mode = false;
 
   if (argc >= 2 && strcmp(argv[1], "-z") == 0) {
-    zip_mode = 1;
+    zip_mode = true;
     --argc;
     ++argv;
   }
 
-  size_t bonus_size = 0;
-  unsigned char* bonus_data = NULL;
+  std::vector<uint8_t> bonus_data;
   if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
-    struct stat st;
-    if (stat(argv[2], &st) != 0) {
-      printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno));
-      return 1;
-    }
-    bonus_size = st.st_size;
-    bonus_data = reinterpret_cast<unsigned char*>(malloc(bonus_size));
-    FILE* f = fopen(argv[2], "rb");
-    if (f == NULL) {
+    android::base::unique_fd fd(open(argv[2], O_RDONLY));
+    if (fd == -1) {
       printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno));
       return 1;
     }
-    if (fread(bonus_data, 1, bonus_size, f) != bonus_size) {
+    struct stat st;
+    if (fstat(fd, &st) != 0) {
+      printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno));
+      return 1;
+    }
+
+    size_t bonus_size = st.st_size;
+    bonus_data.resize(bonus_size);
+    if (!android::base::ReadFully(fd, bonus_data.data(), bonus_size)) {
       printf("failed to read bonus file %s: %s\n", argv[2], strerror(errno));
       return 1;
     }
-    fclose(f);
 
     argc -= 2;
     argv += 2;
   }
 
   if (argc != 4) {
-    usage:
     printf("usage: %s [-z] [-b <bonus-file>] <src-img> <tgt-img> <patch-file>\n",
             argv[0]);
     return 2;
   }
 
-  int num_src_chunks;
-  ImageChunk* src_chunks;
-  int num_tgt_chunks;
-  ImageChunk* tgt_chunks;
-  int i;
+  std::vector<ImageChunk> src_chunks;
+  std::vector<ImageChunk> tgt_chunks;
+  std::vector<uint8_t> src_file;
+  std::vector<uint8_t> tgt_file;
 
   if (zip_mode) {
-    if (ReadZip(argv[1], &num_src_chunks, &src_chunks, 1) == NULL) {
+    if (!ReadZip(argv[1], &src_chunks, &src_file, true)) {
       printf("failed to break apart source zip file\n");
       return 1;
     }
-    if (ReadZip(argv[2], &num_tgt_chunks, &tgt_chunks, 0) == NULL) {
+    if (!ReadZip(argv[2], &tgt_chunks, &tgt_file, false)) {
       printf("failed to break apart target zip file\n");
       return 1;
     }
   } else {
-    if (ReadImage(argv[1], &num_src_chunks, &src_chunks) == NULL) {
+    if (!ReadImage(argv[1], &src_chunks, &src_file)) {
       printf("failed to break apart source image\n");
       return 1;
     }
-    if (ReadImage(argv[2], &num_tgt_chunks, &tgt_chunks) == NULL) {
+    if (!ReadImage(argv[2], &tgt_chunks, &tgt_file)) {
       printf("failed to break apart target image\n");
       return 1;
     }
@@ -878,51 +913,47 @@
     // Verify that the source and target images have the same chunk
     // structure (ie, the same sequence of deflate and normal chunks).
 
-    if (!zip_mode) {
-        // Merge the gzip header and footer in with any adjacent
-        // normal chunks.
-        MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
-        MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
-    }
+    // Merge the gzip header and footer in with any adjacent normal chunks.
+    MergeAdjacentNormalChunks(&tgt_chunks);
+    MergeAdjacentNormalChunks(&src_chunks);
 
-    if (num_src_chunks != num_tgt_chunks) {
+    if (src_chunks.size() != tgt_chunks.size()) {
       printf("source and target don't have same number of chunks!\n");
       printf("source chunks:\n");
-      DumpChunks(src_chunks, num_src_chunks);
+      DumpChunks(src_chunks);
       printf("target chunks:\n");
-      DumpChunks(tgt_chunks, num_tgt_chunks);
+      DumpChunks(tgt_chunks);
       return 1;
     }
-    for (i = 0; i < num_src_chunks; ++i) {
-      if (src_chunks[i].type != tgt_chunks[i].type) {
-        printf("source and target don't have same chunk "
-                "structure! (chunk %d)\n", i);
+    for (size_t i = 0; i < src_chunks.size(); ++i) {
+      if (src_chunks[i].GetType() != tgt_chunks[i].GetType()) {
+        printf("source and target don't have same chunk structure! (chunk %zu)\n", i);
         printf("source chunks:\n");
-        DumpChunks(src_chunks, num_src_chunks);
+        DumpChunks(src_chunks);
         printf("target chunks:\n");
-        DumpChunks(tgt_chunks, num_tgt_chunks);
+        DumpChunks(tgt_chunks);
         return 1;
       }
     }
   }
 
-  for (i = 0; i < num_tgt_chunks; ++i) {
-    if (tgt_chunks[i].type == CHUNK_DEFLATE) {
+  for (size_t i = 0; i < tgt_chunks.size(); ++i) {
+    if (tgt_chunks[i].GetType() == CHUNK_DEFLATE) {
       // Confirm that given the uncompressed chunk data in the target, we
       // can recompress it and get exactly the same bits as are in the
       // input target image.  If this fails, treat the chunk as a normal
       // non-deflated chunk.
-      if (ReconstructDeflateChunk(tgt_chunks+i) < 0) {
-        printf("failed to reconstruct target deflate chunk %d [%s]; "
-               "treating as normal\n", i, tgt_chunks[i].filename);
-        ChangeDeflateChunkToNormal(tgt_chunks+i);
+      if (!tgt_chunks[i].ReconstructDeflateChunk()) {
+        printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i,
+               tgt_chunks[i].GetEntryName().c_str());
+        tgt_chunks[i].ChangeDeflateChunkToNormal();
         if (zip_mode) {
-          ImageChunk* src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks);
-          if (src) {
-            ChangeDeflateChunkToNormal(src);
+          ImageChunk* src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks);
+          if (src != nullptr) {
+            src->ChangeDeflateChunkToNormal();
           }
         } else {
-          ChangeDeflateChunkToNormal(src_chunks+i);
+          src_chunks[i].ChangeDeflateChunkToNormal();
         }
         continue;
       }
@@ -935,16 +966,16 @@
       // data.
       ImageChunk* src;
       if (zip_mode) {
-        src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks);
+        src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks);
       } else {
-        src = src_chunks+i;
+        src = &src_chunks[i];
       }
 
-      if (src == NULL || AreChunksEqual(tgt_chunks+i, src)) {
-        ChangeDeflateChunkToNormal(tgt_chunks+i);
-        if (src) {
-          ChangeDeflateChunkToNormal(src);
-        }
+      if (src == nullptr) {
+        tgt_chunks[i].ChangeDeflateChunkToNormal();
+      } else if (tgt_chunks[i] == *src) {
+        tgt_chunks[i].ChangeDeflateChunkToNormal();
+        src->ChangeDeflateChunkToNormal();
       }
     }
   }
@@ -954,14 +985,15 @@
     // For zips, we only need to do this to the target:  deflated
     // chunks are matched via filename, and normal chunks are patched
     // using the entire source file as the source.
-    MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
+    MergeAdjacentNormalChunks(&tgt_chunks);
+
   } else {
     // For images, we need to maintain the parallel structure of the
     // chunk lists, so do the merging in both the source and target
     // lists.
-    MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
-    MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
-    if (num_src_chunks != num_tgt_chunks) {
+    MergeAdjacentNormalChunks(&tgt_chunks);
+    MergeAdjacentNormalChunks(&src_chunks);
+    if (src_chunks.size() != tgt_chunks.size()) {
       // This shouldn't happen.
       printf("merging normal chunks went awry\n");
       return 1;
@@ -971,35 +1003,43 @@
   // Compute bsdiff patches for each chunk's data (the uncompressed
   // data, in the case of deflate chunks).
 
-  DumpChunks(src_chunks, num_src_chunks);
+  DumpChunks(src_chunks);
 
-  printf("Construct patches for %d chunks...\n", num_tgt_chunks);
-  unsigned char** patch_data = reinterpret_cast<unsigned char**>(malloc(
-      num_tgt_chunks * sizeof(unsigned char*)));
-  size_t* patch_size = reinterpret_cast<size_t*>(malloc(num_tgt_chunks * sizeof(size_t)));
-  for (i = 0; i < num_tgt_chunks; ++i) {
+  printf("Construct patches for %zu chunks...\n", tgt_chunks.size());
+  std::vector<std::vector<uint8_t>> patch_data(tgt_chunks.size());
+  saidx_t* bsdiff_cache = nullptr;
+  for (size_t i = 0; i < tgt_chunks.size(); ++i) {
     if (zip_mode) {
       ImageChunk* src;
-      if (tgt_chunks[i].type == CHUNK_DEFLATE &&
-          (src = FindChunkByName(tgt_chunks[i].filename, src_chunks,
-                                 num_src_chunks))) {
-        patch_data[i] = MakePatch(src, tgt_chunks+i, patch_size+i);
+      if (tgt_chunks[i].GetType() == CHUNK_DEFLATE &&
+          (src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks))) {
+        if (!MakePatch(src, &tgt_chunks[i], &patch_data[i], nullptr)) {
+          printf("Failed to generate patch for target chunk %zu: ", i);
+          return 1;
+        }
       } else {
-        patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i);
+        if (!MakePatch(&src_chunks[0], &tgt_chunks[i], &patch_data[i], &bsdiff_cache)) {
+          printf("Failed to generate patch for target chunk %zu: ", i);
+          return 1;
+        }
       }
     } else {
-      if (i == 1 && bonus_data) {
-        printf("  using %zu bytes of bonus data for chunk %d\n", bonus_size, i);
-        src_chunks[i].data = reinterpret_cast<unsigned char*>(realloc(src_chunks[i].data,
-            src_chunks[i].len + bonus_size));
-        memcpy(src_chunks[i].data+src_chunks[i].len, bonus_data, bonus_size);
-        src_chunks[i].len += bonus_size;
-     }
+      if (i == 1 && !bonus_data.empty()) {
+        printf("  using %zu bytes of bonus data for chunk %zu\n", bonus_data.size(), i);
+        src_chunks[i].SetBonusData(bonus_data);
+      }
 
-      patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i);
+      if (!MakePatch(&src_chunks[i], &tgt_chunks[i], &patch_data[i], nullptr)) {
+        printf("Failed to generate patch for target chunk %zu: ", i);
+        return 1;
+      }
     }
-    printf("patch %3d is %zu bytes (of %zu)\n",
-           i, patch_size[i], tgt_chunks[i].source_len);
+    printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(),
+           src_chunks[i].GetRawDataLength());
+  }
+
+  if (bsdiff_cache != nullptr) {
+    free(bsdiff_cache);
   }
 
   // Figure out how big the imgdiff file header is going to be, so
@@ -1007,77 +1047,38 @@
   // within the file.
 
   size_t total_header_size = 12;
-  for (i = 0; i < num_tgt_chunks; ++i) {
-    total_header_size += 4;
-    switch (tgt_chunks[i].type) {
-      case CHUNK_NORMAL:
-        total_header_size += 8*3;
-        break;
-      case CHUNK_DEFLATE:
-        total_header_size += 8*5 + 4*5;
-        break;
-      case CHUNK_RAW:
-        total_header_size += 4 + patch_size[i];
-        break;
-    }
+  for (size_t i = 0; i < tgt_chunks.size(); ++i) {
+    total_header_size += tgt_chunks[i].GetHeaderSize(patch_data[i].size());
   }
 
   size_t offset = total_header_size;
 
-  FILE* f = fopen(argv[3], "wb");
+  android::base::unique_fd patch_fd(open(argv[3], O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
+  if (patch_fd == -1) {
+    printf("failed to open \"%s\": %s\n", argv[3], strerror(errno));
+    return 1;
+  }
 
   // Write out the headers.
-
-  fwrite("IMGDIFF2", 1, 8, f);
-  Write4(num_tgt_chunks, f);
-  for (i = 0; i < num_tgt_chunks; ++i) {
-    Write4(tgt_chunks[i].type, f);
-
-    switch (tgt_chunks[i].type) {
-      case CHUNK_NORMAL:
-        printf("chunk %3d: normal   (%10zu, %10zu)  %10zu\n", i,
-               tgt_chunks[i].start, tgt_chunks[i].len, patch_size[i]);
-        Write8(tgt_chunks[i].source_start, f);
-        Write8(tgt_chunks[i].source_len, f);
-        Write8(offset, f);
-        offset += patch_size[i];
-        break;
-
-      case CHUNK_DEFLATE:
-        printf("chunk %3d: deflate  (%10zu, %10zu)  %10zu  %s\n", i,
-               tgt_chunks[i].start, tgt_chunks[i].deflate_len, patch_size[i],
-               tgt_chunks[i].filename);
-        Write8(tgt_chunks[i].source_start, f);
-        Write8(tgt_chunks[i].source_len, f);
-        Write8(offset, f);
-        Write8(tgt_chunks[i].source_uncompressed_len, f);
-        Write8(tgt_chunks[i].len, f);
-        Write4(tgt_chunks[i].level, f);
-        Write4(tgt_chunks[i].method, f);
-        Write4(tgt_chunks[i].windowBits, f);
-        Write4(tgt_chunks[i].memLevel, f);
-        Write4(tgt_chunks[i].strategy, f);
-        offset += patch_size[i];
-        break;
-
-      case CHUNK_RAW:
-        printf("chunk %3d: raw      (%10zu, %10zu)\n", i,
-               tgt_chunks[i].start, tgt_chunks[i].len);
-        Write4(patch_size[i], f);
-        fwrite(patch_data[i], 1, patch_size[i], f);
-        break;
-    }
+  if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) {
+    printf("failed to write \"IMGDIFF2\" to \"%s\": %s\n", argv[3], strerror(errno));
+    return 1;
+  }
+  Write4(patch_fd, static_cast<int32_t>(tgt_chunks.size()));
+  for (size_t i = 0; i < tgt_chunks.size(); ++i) {
+    printf("chunk %zu: ", i);
+    offset = tgt_chunks[i].WriteHeaderToFd(patch_fd, patch_data[i], offset);
   }
 
   // Append each chunk's bsdiff patch, in order.
-
-  for (i = 0; i < num_tgt_chunks; ++i) {
-    if (tgt_chunks[i].type != CHUNK_RAW) {
-      fwrite(patch_data[i], 1, patch_size[i], f);
+  for (size_t i = 0; i < tgt_chunks.size(); ++i) {
+    if (tgt_chunks[i].GetType() != CHUNK_RAW) {
+      if (!android::base::WriteFully(patch_fd, patch_data[i].data(), patch_data[i].size())) {
+        CHECK(false) << "failed to write " << patch_data[i].size() << " bytes patch for chunk "
+                     << i;
+      }
     }
   }
 
-  fclose(f);
-
   return 0;
 }
diff --git a/updater/install.h b/applypatch/imgdiff_main.cpp
similarity index 64%
copy from updater/install.h
copy to applypatch/imgdiff_main.cpp
index 70e3434..7d5bdf9 100644
--- a/updater/install.h
+++ b/applypatch/imgdiff_main.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _UPDATER_INSTALL_H_
-#define _UPDATER_INSTALL_H_
+#include "applypatch/imgdiff.h"
 
-void RegisterInstallFunctions();
-
-// uiPrintf function prints msg to screen as well as logs
-void uiPrintf(State* state, const char* format, ...);
-
-static int make_parents(char* name);
-
-#endif
+int main(int argc, char** argv) {
+  return imgdiff(argc, const_cast<const char**>(argv));
+}
diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp
index d175d63..adcc61f 100644
--- a/applypatch/imgpatch.cpp
+++ b/applypatch/imgpatch.cpp
@@ -14,31 +14,41 @@
  * limitations under the License.
  */
 
-// See imgdiff.c in this directory for a description of the patch file
+// See imgdiff.cpp in this directory for a description of the patch file
 // format.
 
+#include <applypatch/imgpatch.h>
+
+#include <errno.h>
 #include <stdio.h>
+#include <string.h>
 #include <sys/cdefs.h>
 #include <sys/stat.h>
-#include <errno.h>
 #include <unistd.h>
-#include <string.h>
 
+#include <string>
 #include <vector>
 
-#include "zlib.h"
-#include "openssl/sha.h"
-#include "applypatch.h"
-#include "imgdiff.h"
-#include "utils.h"
+#include <applypatch/applypatch.h>
+#include <applypatch/imgdiff.h>
+#include <android-base/memory.h>
+#include <openssl/sha.h>
+#include <zlib.h>
+
+static inline int64_t Read8(const void *address) {
+  return android::base::get_unaligned<int64_t>(address);
+}
+
+static inline int32_t Read4(const void *address) {
+  return android::base::get_unaligned<int32_t>(address);
+}
 
 int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
                     const unsigned char* patch_data, ssize_t patch_size,
                     SinkFn sink, void* token) {
-  Value patch = {VAL_BLOB, patch_size,
-      reinterpret_cast<char*>(const_cast<unsigned char*>(patch_data))};
-  return ApplyImagePatch(
-      old_data, old_size, &patch, sink, token, nullptr, nullptr);
+  Value patch(VAL_BLOB, std::string(reinterpret_cast<const char*>(patch_data), patch_size));
+
+  return ApplyImagePatch(old_data, old_size, &patch, sink, token, nullptr, nullptr);
 }
 
 /*
@@ -47,203 +57,204 @@
  * file, and update the SHA context with the output data as well.
  * Return 0 on success.
  */
-int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
-                    const Value* patch,
-                    SinkFn sink, void* token, SHA_CTX* ctx,
-                    const Value* bonus_data) {
-    ssize_t pos = 12;
-    char* header = patch->data;
-    if (patch->size < 12) {
-        printf("patch too short to contain header\n");
+int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch,
+                    SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data) {
+  if (patch->data.size() < 12) {
+    printf("patch too short to contain header\n");
+    return -1;
+  }
+
+  // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
+  // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
+  // CHUNK_GZIP.)
+  size_t pos = 12;
+  const char* header = &patch->data[0];
+  if (memcmp(header, "IMGDIFF2", 8) != 0) {
+    printf("corrupt patch file header (magic number)\n");
+    return -1;
+  }
+
+  int num_chunks = Read4(header + 8);
+
+  for (int i = 0; i < num_chunks; ++i) {
+    // each chunk's header record starts with 4 bytes.
+    if (pos + 4 > patch->data.size()) {
+      printf("failed to read chunk %d record\n", i);
+      return -1;
+    }
+    int type = Read4(&patch->data[pos]);
+    pos += 4;
+
+    if (type == CHUNK_NORMAL) {
+      const char* normal_header = &patch->data[pos];
+      pos += 24;
+      if (pos > patch->data.size()) {
+        printf("failed to read chunk %d normal header data\n", i);
         return -1;
-    }
+      }
 
-    // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
-    // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
-    // CHUNK_GZIP.)
-    if (memcmp(header, "IMGDIFF2", 8) != 0) {
-        printf("corrupt patch file header (magic number)\n");
+      size_t src_start = static_cast<size_t>(Read8(normal_header));
+      size_t src_len = static_cast<size_t>(Read8(normal_header + 8));
+      size_t patch_offset = static_cast<size_t>(Read8(normal_header + 16));
+
+      if (src_start + src_len > static_cast<size_t>(old_size)) {
+        printf("source data too short\n");
         return -1;
-    }
+      }
+      ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, token, ctx);
+    } else if (type == CHUNK_RAW) {
+      const char* raw_header = &patch->data[pos];
+      pos += 4;
+      if (pos > patch->data.size()) {
+        printf("failed to read chunk %d raw header data\n", i);
+        return -1;
+      }
 
-    int num_chunks = Read4(header+8);
+      ssize_t data_len = Read4(raw_header);
 
-    int i;
-    for (i = 0; i < num_chunks; ++i) {
-        // each chunk's header record starts with 4 bytes.
-        if (pos + 4 > patch->size) {
-            printf("failed to read chunk %d record\n", i);
-            return -1;
+      if (pos + data_len > patch->data.size()) {
+        printf("failed to read chunk %d raw data\n", i);
+        return -1;
+      }
+      if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len);
+      if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]), data_len, token) !=
+          data_len) {
+        printf("failed to write chunk %d raw data\n", i);
+        return -1;
+      }
+      pos += data_len;
+    } else if (type == CHUNK_DEFLATE) {
+      // deflate chunks have an additional 60 bytes in their chunk header.
+      const char* deflate_header = &patch->data[pos];
+      pos += 60;
+      if (pos > patch->data.size()) {
+        printf("failed to read chunk %d deflate header data\n", i);
+        return -1;
+      }
+
+      size_t src_start = static_cast<size_t>(Read8(deflate_header));
+      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 > static_cast<size_t>(old_size)) {
+        printf("source data too short\n");
+        return -1;
+      }
+
+      // Decompress the source data; the chunk header tells us exactly
+      // how big we expect it to be when decompressed.
+
+      // Note: expanded_len will include the bonus data size if
+      // the patch was constructed with bonus data.  The
+      // deflation will come up 'bonus_size' bytes short; these
+      // must be appended from the bonus_data value.
+      size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->data.size() : 0;
+
+      std::vector<unsigned char> expanded_source(expanded_len);
+
+      // inflate() doesn't like strm.next_out being a nullptr even with
+      // avail_out being zero (Z_STREAM_ERROR).
+      if (expanded_len != 0) {
+        z_stream strm;
+        strm.zalloc = Z_NULL;
+        strm.zfree = Z_NULL;
+        strm.opaque = Z_NULL;
+        strm.avail_in = src_len;
+        strm.next_in = old_data + src_start;
+        strm.avail_out = expanded_len;
+        strm.next_out = expanded_source.data();
+
+        int ret = inflateInit2(&strm, -15);
+        if (ret != Z_OK) {
+          printf("failed to init source inflation: %d\n", ret);
+          return -1;
         }
-        int type = Read4(patch->data + pos);
-        pos += 4;
 
-        if (type == CHUNK_NORMAL) {
-            char* normal_header = patch->data + pos;
-            pos += 24;
-            if (pos > patch->size) {
-                printf("failed to read chunk %d normal header data\n", i);
-                return -1;
-            }
-
-            size_t src_start = Read8(normal_header);
-            size_t src_len = Read8(normal_header+8);
-            size_t patch_offset = Read8(normal_header+16);
-
-            if (src_start + src_len > static_cast<size_t>(old_size)) {
-                printf("source data too short\n");
-                return -1;
-            }
-            ApplyBSDiffPatch(old_data + src_start, src_len,
-                             patch, patch_offset, sink, token, ctx);
-        } else if (type == CHUNK_RAW) {
-            char* raw_header = patch->data + pos;
-            pos += 4;
-            if (pos > patch->size) {
-                printf("failed to read chunk %d raw header data\n", i);
-                return -1;
-            }
-
-            ssize_t data_len = Read4(raw_header);
-
-            if (pos + data_len > patch->size) {
-                printf("failed to read chunk %d raw data\n", i);
-                return -1;
-            }
-            if (ctx) SHA1_Update(ctx, patch->data + pos, data_len);
-            if (sink((unsigned char*)patch->data + pos,
-                     data_len, token) != data_len) {
-                printf("failed to write chunk %d raw data\n", i);
-                return -1;
-            }
-            pos += data_len;
-        } else if (type == CHUNK_DEFLATE) {
-            // deflate chunks have an additional 60 bytes in their chunk header.
-            char* deflate_header = patch->data + pos;
-            pos += 60;
-            if (pos > patch->size) {
-                printf("failed to read chunk %d deflate header data\n", i);
-                return -1;
-            }
-
-            size_t src_start = Read8(deflate_header);
-            size_t src_len = Read8(deflate_header+8);
-            size_t patch_offset = Read8(deflate_header+16);
-            size_t expanded_len = Read8(deflate_header+24);
-            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 > static_cast<size_t>(old_size)) {
-                printf("source data too short\n");
-                return -1;
-            }
-
-            // Decompress the source data; the chunk header tells us exactly
-            // how big we expect it to be when decompressed.
-
-            // Note: expanded_len will include the bonus data size if
-            // the patch was constructed with bonus data.  The
-            // deflation will come up 'bonus_size' bytes short; these
-            // must be appended from the bonus_data value.
-            size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0;
-
-            std::vector<unsigned char> expanded_source(expanded_len);
-
-            // inflate() doesn't like strm.next_out being a nullptr even with
-            // avail_out being zero (Z_STREAM_ERROR).
-            if (expanded_len != 0) {
-                z_stream strm;
-                strm.zalloc = Z_NULL;
-                strm.zfree = Z_NULL;
-                strm.opaque = Z_NULL;
-                strm.avail_in = src_len;
-                strm.next_in = (unsigned char*)(old_data + src_start);
-                strm.avail_out = expanded_len;
-                strm.next_out = expanded_source.data();
-
-                int ret;
-                ret = inflateInit2(&strm, -15);
-                if (ret != Z_OK) {
-                    printf("failed to init source inflation: %d\n", ret);
-                    return -1;
-                }
-
-                // Because we've provided enough room to accommodate the output
-                // data, we expect one call to inflate() to suffice.
-                ret = inflate(&strm, Z_SYNC_FLUSH);
-                if (ret != Z_STREAM_END) {
-                    printf("source inflation returned %d\n", ret);
-                    return -1;
-                }
-                // We should have filled the output buffer exactly, except
-                // for the bonus_size.
-                if (strm.avail_out != bonus_size) {
-                    printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size);
-                    return -1;
-                }
-                inflateEnd(&strm);
-
-                if (bonus_size) {
-                    memcpy(expanded_source.data() + (expanded_len - bonus_size),
-                           bonus_data->data, bonus_size);
-                }
-            }
-
-            // Next, apply the bsdiff patch (in memory) to the uncompressed
-            // data.
-            std::vector<unsigned char> uncompressed_target_data;
-            if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len,
-                                    patch, patch_offset,
-                                    &uncompressed_target_data) != 0) {
-                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);
-                    ssize_t have = temp_data.size() - strm.avail_out;
-
-                    if (sink(temp_data.data(), have, token) != have) {
-                        printf("failed to write %ld compressed bytes to output\n",
-                               (long)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;
+        // Because we've provided enough room to accommodate the output
+        // data, we expect one call to inflate() to suffice.
+        ret = inflate(&strm, Z_SYNC_FLUSH);
+        if (ret != Z_STREAM_END) {
+          printf("source inflation returned %d\n", ret);
+          return -1;
         }
-    }
+        // We should have filled the output buffer exactly, except
+        // for the bonus_size.
+        if (strm.avail_out != bonus_size) {
+          printf("source inflation short by %zu bytes\n", strm.avail_out - bonus_size);
+          return -1;
+        }
+        inflateEnd(&strm);
 
-    return 0;
+        if (bonus_size) {
+          memcpy(expanded_source.data() + (expanded_len - bonus_size), &bonus_data->data[0],
+                 bonus_size);
+        }
+      }
+
+      // Next, apply the bsdiff patch (in memory) to the uncompressed data.
+      std::vector<unsigned char> uncompressed_target_data;
+      // TODO(senj): Remove the only usage of ApplyBSDiffPatchMem here,
+      // replace it with ApplyBSDiffPatch with a custom sink function that
+      // wraps the given sink function to stream output to save memory.
+      if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, patch, patch_offset,
+                              &uncompressed_target_data) != 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());
+        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);
+          ssize_t have = temp_data.size() - strm.avail_out;
+
+          if (sink(temp_data.data(), have, token) != 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;
+    }
+  }
+
+  return 0;
 }
diff --git a/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h
similarity index 85%
rename from applypatch/applypatch.h
rename to applypatch/include/applypatch/applypatch.h
index f392c55..4489dec 100644
--- a/applypatch/applypatch.h
+++ b/applypatch/include/applypatch/applypatch.h
@@ -17,11 +17,15 @@
 #ifndef _APPLYPATCH_H
 #define _APPLYPATCH_H
 
+#include <stdint.h>
 #include <sys/stat.h>
 
+#include <memory>
+#include <string>
 #include <vector>
 
-#include "openssl/sha.h"
+#include <openssl/sha.h>
+
 #include "edify/expr.h"
 
 struct FileContents {
@@ -39,33 +43,28 @@
 
 typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
 
-// applypatch.c
+// applypatch.cpp
 int ShowLicenses();
 size_t FreeSpaceForFile(const char* filename);
 int CacheSizeCheck(size_t bytes);
 int ParseSha1(const char* str, uint8_t* digest);
 
-int applypatch_flash(const char* source_filename, const char* target_filename,
-                     const char* target_sha1_str, size_t target_size);
 int applypatch(const char* source_filename,
                const char* target_filename,
                const char* target_sha1_str,
                size_t target_size,
-               int num_patches,
-               char** const patch_sha1_str,
-               Value** patch_data,
-               Value* bonus_data);
+               const std::vector<std::string>& patch_sha1_str,
+               const std::vector<std::unique_ptr<Value>>& patch_data,
+               const Value* bonus_data);
 int applypatch_check(const char* filename,
-                     int num_patches,
-                     char** const patch_sha1_str);
+                     const std::vector<std::string>& patch_sha1_str);
+int applypatch_flash(const char* source_filename, const char* target_filename,
+                     const char* target_sha1_str, size_t target_size);
 
 int LoadFileContents(const char* filename, FileContents* file);
 int SaveFileContents(const char* filename, const FileContents* file);
-void FreeFileContents(FileContents* file);
-int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
-                      int num_patches);
 
-// bsdiff.cpp
+// bspatch.cpp
 void ShowBSDiffLicense();
 int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
                      const Value* patch, ssize_t patch_offset,
diff --git a/applypatch/imgdiff.h b/applypatch/include/applypatch/imgdiff.h
similarity index 69%
rename from applypatch/imgdiff.h
rename to applypatch/include/applypatch/imgdiff.h
index f2069b4..22cbd4f 100644
--- a/applypatch/imgdiff.h
+++ b/applypatch/include/applypatch/imgdiff.h
@@ -14,17 +14,26 @@
  * limitations under the License.
  */
 
+#ifndef _APPLYPATCH_IMGDIFF_H
+#define _APPLYPATCH_IMGDIFF_H
+
+#include <stddef.h>
+
 // Image patch chunk types
-#define CHUNK_NORMAL   0
-#define CHUNK_GZIP     1   // version 1 only
-#define CHUNK_DEFLATE  2   // version 2 only
-#define CHUNK_RAW      3   // version 2 only
+#define CHUNK_NORMAL 0
+#define CHUNK_GZIP 1     // version 1 only
+#define CHUNK_DEFLATE 2  // version 2 only
+#define CHUNK_RAW 3      // version 2 only
 
 // The gzip header size is actually variable, but we currently don't
 // support gzipped data with any of the optional fields, so for now it
 // will always be ten bytes.  See RFC 1952 for the definition of the
 // gzip format.
-#define GZIP_HEADER_LEN   10
+static constexpr size_t GZIP_HEADER_LEN = 10;
 
 // The gzip footer size really is fixed.
-#define GZIP_FOOTER_LEN   8
+static constexpr size_t GZIP_FOOTER_LEN = 8;
+
+int imgdiff(int argc, const char** argv);
+
+#endif  // _APPLYPATCH_IMGDIFF_H
diff --git a/applypatch/include/applypatch/imgpatch.h b/applypatch/include/applypatch/imgpatch.h
index 64d9aa9..6549f79 100644
--- a/applypatch/include/applypatch/imgpatch.h
+++ b/applypatch/include/applypatch/imgpatch.h
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef _IMGPATCH_H
-#define _IMGPATCH_H
+#ifndef _APPLYPATCH_IMGPATCH_H
+#define _APPLYPATCH_IMGPATCH_H
 
-typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
+#include <sys/types.h>
+
+using SinkFn = ssize_t (*)(const unsigned char*, ssize_t, void*);
 
 int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
                     const unsigned char* patch_data, ssize_t patch_size,
                     SinkFn sink, void* token);
 
-#endif  //_IMGPATCH_H
+#endif  // _APPLYPATCH_IMGPATCH_H
diff --git a/applypatch/libimgpatch.pc b/applypatch/libimgpatch.pc
new file mode 100644
index 0000000..e500293
--- /dev/null
+++ b/applypatch/libimgpatch.pc
@@ -0,0 +1,6 @@
+# This file is for libimgpatch in Chrome OS.
+
+Name: libimgpatch
+Description: Apply imgdiff patch
+Version: 0.0.1
+Libs: -limgpatch -lbz2 -lz
diff --git a/applypatch/utils.cpp b/applypatch/utils.cpp
deleted file mode 100644
index 4a80be7..0000000
--- a/applypatch/utils.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2009 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 <stdio.h>
-
-#include "utils.h"
-
-/** Write a 4-byte value to f in little-endian order. */
-void Write4(int value, FILE* f) {
-  fputc(value & 0xff, f);
-  fputc((value >> 8) & 0xff, f);
-  fputc((value >> 16) & 0xff, f);
-  fputc((value >> 24) & 0xff, f);
-}
-
-/** Write an 8-byte value to f in little-endian order. */
-void Write8(long long value, FILE* f) {
-  fputc(value & 0xff, f);
-  fputc((value >> 8) & 0xff, f);
-  fputc((value >> 16) & 0xff, f);
-  fputc((value >> 24) & 0xff, f);
-  fputc((value >> 32) & 0xff, f);
-  fputc((value >> 40) & 0xff, f);
-  fputc((value >> 48) & 0xff, f);
-  fputc((value >> 56) & 0xff, f);
-}
-
-int Read2(void* pv) {
-    unsigned char* p = reinterpret_cast<unsigned char*>(pv);
-    return (int)(((unsigned int)p[1] << 8) |
-                 (unsigned int)p[0]);
-}
-
-int Read4(void* pv) {
-    unsigned char* p = reinterpret_cast<unsigned char*>(pv);
-    return (int)(((unsigned int)p[3] << 24) |
-                 ((unsigned int)p[2] << 16) |
-                 ((unsigned int)p[1] << 8) |
-                 (unsigned int)p[0]);
-}
-
-long long Read8(void* pv) {
-    unsigned char* p = reinterpret_cast<unsigned char*>(pv);
-    return (long long)(((unsigned long long)p[7] << 56) |
-                       ((unsigned long long)p[6] << 48) |
-                       ((unsigned long long)p[5] << 40) |
-                       ((unsigned long long)p[4] << 32) |
-                       ((unsigned long long)p[3] << 24) |
-                       ((unsigned long long)p[2] << 16) |
-                       ((unsigned long long)p[1] << 8) |
-                       (unsigned long long)p[0]);
-}
diff --git a/applypatch/utils.h b/applypatch/utils.h
deleted file mode 100644
index bc97f17..0000000
--- a/applypatch/utils.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2009 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 _BUILD_TOOLS_APPLYPATCH_UTILS_H
-#define _BUILD_TOOLS_APPLYPATCH_UTILS_H
-
-#include <stdio.h>
-
-// Read and write little-endian values of various sizes.
-
-void Write4(int value, FILE* f);
-void Write8(long long value, FILE* f);
-int Read2(void* p);
-int Read4(void* p);
-long long Read8(void* p);
-
-#endif //  _BUILD_TOOLS_APPLYPATCH_UTILS_H
diff --git a/asn1_decoder.cpp b/asn1_decoder.cpp
index e7aef78..285214f 100644
--- a/asn1_decoder.cpp
+++ b/asn1_decoder.cpp
@@ -14,178 +14,145 @@
  * limitations under the License.
  */
 
-#include <malloc.h>
-#include <stdint.h>
-#include <string.h>
-
 #include "asn1_decoder.h"
 
+#include <stdint.h>
 
-typedef struct asn1_context {
-    size_t length;
-    uint8_t* p;
-    int app_type;
-} asn1_context_t;
-
-
-static const int kMaskConstructed = 0xE0;
-static const int kMaskTag = 0x7F;
-static const int kMaskAppType = 0x1F;
-
-static const int kTagOctetString = 0x04;
-static const int kTagOid = 0x06;
-static const int kTagSequence = 0x30;
-static const int kTagSet = 0x31;
-static const int kTagConstructed = 0xA0;
-
-asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length) {
-    asn1_context_t* ctx = (asn1_context_t*) calloc(1, sizeof(asn1_context_t));
-    if (ctx == NULL) {
-        return NULL;
-    }
-    ctx->p = buffer;
-    ctx->length = length;
-    return ctx;
+int asn1_context::peek_byte() const {
+  if (length_ == 0) {
+    return -1;
+  }
+  return *p_;
 }
 
-void asn1_context_free(asn1_context_t* ctx) {
-    free(ctx);
+int asn1_context::get_byte() {
+  if (length_ == 0) {
+    return -1;
+  }
+
+  int byte = *p_;
+  p_++;
+  length_--;
+  return byte;
 }
 
-static inline int peek_byte(asn1_context_t* ctx) {
-    if (ctx->length <= 0) {
-        return -1;
-    }
-    return *ctx->p;
+bool asn1_context::skip_bytes(size_t num_skip) {
+  if (length_ < num_skip) {
+    return false;
+  }
+  p_ += num_skip;
+  length_ -= num_skip;
+  return true;
 }
 
-static inline int get_byte(asn1_context_t* ctx) {
-    if (ctx->length <= 0) {
-        return -1;
-    }
-    int byte = *ctx->p;
-    ctx->p++;
-    ctx->length--;
-    return byte;
-}
-
-static inline bool skip_bytes(asn1_context_t* ctx, size_t num_skip) {
-    if (ctx->length < num_skip) {
-        return false;
-    }
-    ctx->p += num_skip;
-    ctx->length -= num_skip;
+bool asn1_context::decode_length(size_t* out_len) {
+  int num_octets = get_byte();
+  if (num_octets == -1) {
+    return false;
+  }
+  if ((num_octets & 0x80) == 0x00) {
+    *out_len = num_octets;
     return true;
-}
-
-static bool decode_length(asn1_context_t* ctx, size_t* out_len) {
-    int num_octets = get_byte(ctx);
-    if (num_octets == -1) {
-        return false;
+  }
+  num_octets &= kMaskTag;
+  if (static_cast<size_t>(num_octets) >= sizeof(size_t)) {
+    return false;
+  }
+  size_t length = 0;
+  for (int i = 0; i < num_octets; ++i) {
+    int byte = get_byte();
+    if (byte == -1) {
+      return false;
     }
-    if ((num_octets & 0x80) == 0x00) {
-        *out_len = num_octets;
-        return 1;
-    }
-    num_octets &= kMaskTag;
-    if ((size_t)num_octets >= sizeof(size_t)) {
-        return false;
-    }
-    size_t length = 0;
-    for (int i = 0; i < num_octets; ++i) {
-        int byte = get_byte(ctx);
-        if (byte == -1) {
-            return false;
-        }
-        length <<= 8;
-        length += byte;
-    }
-    *out_len = length;
-    return true;
+    length <<= 8;
+    length += byte;
+  }
+  *out_len = length;
+  return true;
 }
 
 /**
  * Returns the constructed type and advances the pointer. E.g. A0 -> 0
  */
-asn1_context_t* asn1_constructed_get(asn1_context_t* ctx) {
-    int type = get_byte(ctx);
-    if (type == -1 || (type & kMaskConstructed) != kTagConstructed) {
-        return NULL;
-    }
+asn1_context* asn1_context::asn1_constructed_get() {
+  int type = get_byte();
+  if (type == -1 || (type & kMaskConstructed) != kTagConstructed) {
+    return nullptr;
+  }
+  size_t length;
+  if (!decode_length(&length) || length > length_) {
+    return nullptr;
+  }
+  asn1_context* app_ctx = new asn1_context(p_, length);
+  app_ctx->app_type_ = type & kMaskAppType;
+  return app_ctx;
+}
+
+bool asn1_context::asn1_constructed_skip_all() {
+  int byte = peek_byte();
+  while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) {
+    skip_bytes(1);
     size_t length;
-    if (!decode_length(ctx, &length) || length > ctx->length) {
-        return NULL;
+    if (!decode_length(&length) || !skip_bytes(length)) {
+      return false;
     }
-    asn1_context_t* app_ctx = asn1_context_new(ctx->p, length);
-    app_ctx->app_type = type & kMaskAppType;
-    return app_ctx;
+    byte = peek_byte();
+  }
+  return byte != -1;
 }
 
-bool asn1_constructed_skip_all(asn1_context_t* ctx) {
-    int byte = peek_byte(ctx);
-    while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) {
-        skip_bytes(ctx, 1);
-        size_t length;
-        if (!decode_length(ctx, &length) || !skip_bytes(ctx, length)) {
-            return false;
-        }
-        byte = peek_byte(ctx);
-    }
-    return byte != -1;
+int asn1_context::asn1_constructed_type() const {
+  return app_type_;
 }
 
-int asn1_constructed_type(asn1_context_t* ctx) {
-    return ctx->app_type;
+asn1_context* asn1_context::asn1_sequence_get() {
+  if ((get_byte() & kMaskTag) != kTagSequence) {
+    return nullptr;
+  }
+  size_t length;
+  if (!decode_length(&length) || length > length_) {
+    return nullptr;
+  }
+  return new asn1_context(p_, length);
 }
 
-asn1_context_t* asn1_sequence_get(asn1_context_t* ctx) {
-    if ((get_byte(ctx) & kMaskTag) != kTagSequence) {
-        return NULL;
-    }
-    size_t length;
-    if (!decode_length(ctx, &length) || length > ctx->length) {
-        return NULL;
-    }
-    return asn1_context_new(ctx->p, length);
+asn1_context* asn1_context::asn1_set_get() {
+  if ((get_byte() & kMaskTag) != kTagSet) {
+    return nullptr;
+  }
+  size_t length;
+  if (!decode_length(&length) || length > length_) {
+    return nullptr;
+  }
+  return new asn1_context(p_, length);
 }
 
-asn1_context_t* asn1_set_get(asn1_context_t* ctx) {
-    if ((get_byte(ctx) & kMaskTag) != kTagSet) {
-        return NULL;
-    }
-    size_t length;
-    if (!decode_length(ctx, &length) || length > ctx->length) {
-        return NULL;
-    }
-    return asn1_context_new(ctx->p, length);
+bool asn1_context::asn1_sequence_next() {
+  size_t length;
+  if (get_byte() == -1 || !decode_length(&length) || !skip_bytes(length)) {
+    return false;
+  }
+  return true;
 }
 
-bool asn1_sequence_next(asn1_context_t* ctx) {
-    size_t length;
-    if (get_byte(ctx) == -1 || !decode_length(ctx, &length) || !skip_bytes(ctx, length)) {
-        return false;
-    }
-    return true;
+bool asn1_context::asn1_oid_get(const uint8_t** oid, size_t* length) {
+  if (get_byte() != kTagOid) {
+    return false;
+  }
+  if (!decode_length(length) || *length == 0 || *length > length_) {
+    return false;
+  }
+  *oid = p_;
+  return true;
 }
 
-bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length) {
-    if (get_byte(ctx) != kTagOid) {
-        return false;
-    }
-    if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) {
-        return false;
-    }
-    *oid = ctx->p;
-    return true;
-}
-
-bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length) {
-    if (get_byte(ctx) != kTagOctetString) {
-        return false;
-    }
-    if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) {
-        return false;
-    }
-    *octet_string = ctx->p;
-    return true;
+bool asn1_context::asn1_octet_string_get(const uint8_t** octet_string, size_t* length) {
+  if (get_byte() != kTagOctetString) {
+    return false;
+  }
+  if (!decode_length(length) || *length == 0 || *length > length_) {
+    return false;
+  }
+  *octet_string = p_;
+  return true;
 }
diff --git a/asn1_decoder.h b/asn1_decoder.h
index b17141c..3e99211 100644
--- a/asn1_decoder.h
+++ b/asn1_decoder.h
@@ -14,23 +14,42 @@
  * limitations under the License.
  */
 
-
 #ifndef ASN1_DECODER_H_
 #define ASN1_DECODER_H_
 
 #include <stdint.h>
 
-typedef struct asn1_context asn1_context_t;
+class asn1_context {
+ public:
+  asn1_context(const uint8_t* buffer, size_t length) : p_(buffer), length_(length), app_type_(0) {}
+  int asn1_constructed_type() const;
+  asn1_context* asn1_constructed_get();
+  bool asn1_constructed_skip_all();
+  asn1_context* asn1_sequence_get();
+  asn1_context* asn1_set_get();
+  bool asn1_sequence_next();
+  bool asn1_oid_get(const uint8_t** oid, size_t* length);
+  bool asn1_octet_string_get(const uint8_t** octet_string, size_t* length);
 
-asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length);
-void asn1_context_free(asn1_context_t* ctx);
-asn1_context_t* asn1_constructed_get(asn1_context_t* ctx);
-bool asn1_constructed_skip_all(asn1_context_t* ctx);
-int asn1_constructed_type(asn1_context_t* ctx);
-asn1_context_t* asn1_sequence_get(asn1_context_t* ctx);
-asn1_context_t* asn1_set_get(asn1_context_t* ctx);
-bool asn1_sequence_next(asn1_context_t* seq);
-bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length);
-bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length);
+ private:
+  static constexpr int kMaskConstructed = 0xE0;
+  static constexpr int kMaskTag = 0x7F;
+  static constexpr int kMaskAppType = 0x1F;
+
+  static constexpr int kTagOctetString = 0x04;
+  static constexpr int kTagOid = 0x06;
+  static constexpr int kTagSequence = 0x30;
+  static constexpr int kTagSet = 0x31;
+  static constexpr int kTagConstructed = 0xA0;
+
+  int peek_byte() const;
+  int get_byte();
+  bool skip_bytes(size_t num_skip);
+  bool decode_length(size_t* out_len);
+
+  const uint8_t* p_;
+  size_t length_;
+  int app_type_;
+};
 
 #endif /* ASN1_DECODER_H_ */
diff --git a/bootloader_message/Android.mk b/bootloader_message/Android.mk
index 1d5c85f..0d84713 100644
--- a/bootloader_message/Android.mk
+++ b/bootloader_message/Android.mk
@@ -14,32 +14,16 @@
 
 LOCAL_PATH := $(call my-dir)
 
-ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 25; echo $$?),0)
-    include $(CLEAR_VARS)
-    LOCAL_CLANG := true
-    LOCAL_SRC_FILES := bootloader_message.cpp
-    LOCAL_MODULE := libbootloader_message
-    LOCAL_STATIC_LIBRARIES := libfs_mgr
-    LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-    LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-    include $(BUILD_STATIC_LIBRARY)
-endif
-
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_SRC_FILES := bootloader_message.cpp
 LOCAL_MODULE := libbootloader_message
-LOCAL_C_INCLUDES += bionic $(LOCAL_PATH)/include
-ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
-    LOCAL_C_INCLUDES += external/stlport/stlport
-    LOCAL_SHARED_LIBRARIES += libstlport
-else
-    LOCAL_SHARED_LIBRARIES += libc++
+LOCAL_STATIC_LIBRARIES := libbase libfs_mgr
+LOCAL_CFLAGS := -Werror
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0)
+    TARGET_GLOBAL_CFLAGS += -DUSE_OLD_BOOTLOADER_MESSAGE
+    CLANG_TARGET_GLOBAL_CFLAGS += -DUSE_OLD_BOOTLOADER_MESSAGE
 endif
-LOCAL_CFLAGS := -DEXCLUDE_FS_MGR
-# ignore bootloader's factory reset command even when written to /misc
-ifeq ($(TW_IGNORE_MISC_WIPE_DATA), true)
-    LOCAL_CFLAGS += -DIGNORE_MISC_WIPE_DATA
-endif
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index 4d1ce5b..dcaeb79 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -19,31 +19,18 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <string.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/system_properties.h>
 
 #include <string>
 #include <vector>
 
-/*
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-*/
-#ifndef EXCLUDE_FS_MGR
 #include <fs_mgr.h>
-#endif
 
-static std::string misc_blkdev;
+#ifdef USE_OLD_BOOTLOADER_MESSAGE
+#include <sys/system_properties.h>
 
-void set_misc_device(std::string name) {
-    misc_blkdev = name;
-}
-
-#ifndef EXCLUDE_FS_MGR
 static struct fstab* read_fstab(std::string* err) {
   // The fstab path is always "/fstab.${ro.hardware}".
   std::string fstab_path = "/fstab.";
@@ -62,23 +49,28 @@
 #endif
 
 static std::string get_misc_blk_device(std::string* err) {
-#ifdef EXCLUDE_FS_MGR
-  return misc_blkdev;
-#else
+#ifdef USE_OLD_BOOTLOADER_MESSAGE
   struct fstab* fstab = read_fstab(err);
-  if (fstab == nullptr) {
+#else
+  std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                             fs_mgr_free_fstab);
+#endif
+  if (!fstab) {
+    *err = "failed to read default fstab";
     return "";
   }
+#ifdef USE_OLD_BOOTLOADER_MESSAGE
   fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+#else
+  fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab.get(), "/misc");
+#endif
   if (record == nullptr) {
     *err = "failed to find /misc partition";
     return "";
   }
   return record->blk_device;
-#endif
 }
 
-
 // In recovery mode, recovery can get started and try to access the misc
 // device before the kernel has actually created it.
 static bool wait_for_device(const std::string& blk_device, std::string* err) {
@@ -90,127 +82,94 @@
     struct stat buf;
     ret = stat(blk_device.c_str(), &buf);
     if (ret == -1) {
-      char buffer[2048];
-      sprintf(buffer, "failed to stat %s try %d: %s\n",
-                                          blk_device.c_str(), tries, strerror(errno));
-      *err += buffer;
-      /*
       *err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
                                           blk_device.c_str(), tries, strerror(errno));
-      */
       sleep(1);
     }
   } while (ret && tries < 10);
 
   if (ret) {
-    *err += "failed to stat " + blk_device + "\n";
-    /*
     *err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
-    */
   }
   return ret == 0;
 }
 
-static bool read_misc_partition(void* p, size_t size, size_t offset, std::string* err) {
-  std::string misc_blk_device = get_misc_blk_device(err);
-  if (misc_blk_device.empty()) {
-    return false;
-  }
+static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
+                                size_t offset, std::string* err) {
   if (!wait_for_device(misc_blk_device, err)) {
     return false;
   }
-  int fd(open(misc_blk_device.c_str(), O_RDONLY));
-  if (fd < 0) {
-    *err = "failed to open " + misc_blk_device + ": ";
-    *err += strerror(errno);
-    /*
+  android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
+  if (fd == -1) {
     *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
-    */
     return false;
   }
   if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
-    *err = "failed to lseek " + misc_blk_device + ": ";
-    *err += strerror(errno);
-    close(fd);
-    /*
     *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
-    */
     return false;
   }
-  if (read(fd, p, size) != size) {
-    *err = "failed to read " + misc_blk_device + ": ";
-    *err += strerror(errno);
-    close(fd);
-    /*
+  if (!android::base::ReadFully(fd, p, size)) {
     *err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
-    */
     return false;
   }
-  close(fd);
   return true;
 }
 
-static bool write_misc_partition(const void* p, size_t size, size_t offset, std::string* err) {
-  std::string misc_blk_device = get_misc_blk_device(err);
-  if (misc_blk_device.empty()) {
-    *err = "no misc device set";
-    return false;
-  }
-  int fd = (open(misc_blk_device.c_str(), O_WRONLY | O_SYNC));
+static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
+                                 size_t offset, std::string* err) {
+  android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
   if (fd == -1) {
-    *err = "failed to open " + misc_blk_device + ": ";
-    *err += strerror(errno);
-    /*
     *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
-    */
     return false;
   }
   if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
-    *err = "failed to lseek " + misc_blk_device + ": ";
-    *err += strerror(errno);
-    close(fd);
-    /*
     *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
-    */
     return false;
   }
-  if (write(fd, p, size) != size) {
-    *err = "failed to write " + misc_blk_device + ": ";
-    *err += strerror(errno);
-    close(fd);
-    /*
+  if (!android::base::WriteFully(fd, p, size)) {
     *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
-    */
     return false;
   }
-
-  // TODO: O_SYNC and fsync duplicates each other?
   if (fsync(fd) == -1) {
-    *err = "failed to fsync " + misc_blk_device + ": ";
-    *err += strerror(errno);
-    close(fd);
-    /*
     *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
-    */
     return false;
   }
-  close(fd);
   return true;
 }
 
+bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
+                                  std::string* err) {
+  return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
+                             BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+}
+
 bool read_bootloader_message(bootloader_message* boot, std::string* err) {
-  return read_misc_partition(boot, sizeof(*boot), BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
+  return read_bootloader_message_from(boot, misc_blk_device, err);
+}
+
+bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device,
+                                 std::string* err) {
+  return write_misc_partition(&boot, sizeof(boot), misc_blk_device,
+                              BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
 }
 
 bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
-  return write_misc_partition(&boot, sizeof(boot), BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
+  return write_bootloader_message_to(boot, misc_blk_device, err);
 }
 
 bool clear_bootloader_message(std::string* err) {
@@ -224,120 +183,72 @@
   strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
   for (const auto& s : options) {
     strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
-    if (s.substr(s.size() - 1) != "\n") {
+    if (s.back() != '\n') {
       strlcat(boot.recovery, "\n", sizeof(boot.recovery));
     }
   }
   return write_bootloader_message(boot, err);
 }
 
+bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
+  bootloader_message boot;
+  if (!read_bootloader_message(&boot, err)) {
+    return false;
+  }
+
+  // Zero out the entire fields.
+  memset(boot.command, 0, sizeof(boot.command));
+  memset(boot.recovery, 0, sizeof(boot.recovery));
+
+  strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+  strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+  for (const auto& s : options) {
+    strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
+    if (s.back() != '\n') {
+      strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+    }
+  }
+  return write_bootloader_message(boot, err);
+}
+
+bool write_reboot_bootloader(std::string* err) {
+  bootloader_message boot;
+  if (!read_bootloader_message(&boot, err)) {
+    return false;
+  }
+  if (boot.command[0] != '\0') {
+    *err = "Bootloader command pending.";
+    return false;
+  }
+  strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
+  return write_bootloader_message(boot, err);
+}
+
 bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
   package_data->resize(size);
-  return read_misc_partition(&(*package_data)[0], size, WIPE_PACKAGE_OFFSET_IN_MISC, err);
+  return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
+                             WIPE_PACKAGE_OFFSET_IN_MISC, err);
 }
 
 bool write_wipe_package(const std::string& package_data, std::string* err) {
-  return write_misc_partition(package_data.data(), package_data.size(),
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
+  return write_misc_partition(package_data.data(), package_data.size(), misc_blk_device,
                               WIPE_PACKAGE_OFFSET_IN_MISC, err);
 }
 
+extern "C" bool write_reboot_bootloader(void) {
+  std::string err;
+  return write_reboot_bootloader(&err);
+}
+
 extern "C" bool write_bootloader_message(const char* options) {
   std::string err;
-  bootloader_message boot = {};
-  memcpy(&boot, options, sizeof(boot));
-  return write_bootloader_message(boot, &err);
-}
-
-static const char *COMMAND_FILE = "/cache/recovery/command";
-static const int MAX_ARG_LENGTH = 4096;
-static const int MAX_ARGS = 100;
-
-// command line args come from, in decreasing precedence:
-//   - the actual command line
-//   - the bootloader control block (one per line, after "recovery")
-//   - the contents of COMMAND_FILE (one per line)
-void
-get_args(int *argc, char ***argv) {
-    bootloader_message boot = {};
-    std::string err;
-    if (!read_bootloader_message(&boot, &err)) {
-        printf("%s\n", err.c_str());
-        // If fails, leave a zeroed bootloader_message.
-        memset(&boot, 0, sizeof(boot));
-    }
-    //stage = strndup(boot.stage, sizeof(boot.stage));
-
-    if (boot.command[0] != 0 && boot.command[0] != 255) {
-        printf("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
-    }
-
-    if (boot.status[0] != 0 && boot.status[0] != 255) {
-        printf("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
-    }
-
-    // --- if arguments weren't supplied, look in the bootloader control block
-    if (*argc <= 1) {
-        boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination
-        const char *arg = strtok(boot.recovery, "\n");
-        if (arg != NULL && !strcmp(arg, "recovery")) {
-            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
-            (*argv)[0] = strdup(arg);
-            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
-                if ((arg = strtok(NULL, "\n")) == NULL) break;
-
-// if the device does not have an own recovery key combo we just want to open TWRP after
-// walking through the factory reset screen - without actually doing a factory reset
-#ifdef IGNORE_MISC_WIPE_DATA
-                if (!strcmp(arg, "--wipe_data")) {
-                    (*argv)[*argc] = "";
-                    *argc = *argc -1;
-                    printf("Bootloader arg \"%s\" ignored because TWRP was compiled with TW_IGNORE_MISC_WIPE_DATA\n", arg);
-                    continue;
-                }
-#endif
-                (*argv)[*argc] = strdup(arg);
-            }
-            printf("Got arguments from boot message\n");
-        } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
-            printf("Bad boot message\n\"%.20s\"\n", boot.recovery);
-        }
-    }
-
-    // --- if that doesn't work, try the command file (if we have /cache).
-    if (*argc <= 1/* && has_cache*/) {
-        FILE *fp = fopen(COMMAND_FILE, "r");
-        if (fp != NULL) {
-            char *token;
-            char *argv0 = (*argv)[0];
-            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
-            (*argv)[0] = argv0;  // use the same program name
-
-            char buf[MAX_ARG_LENGTH];
-            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
-                if (!fgets(buf, sizeof(buf), fp)) break;
-                token = strtok(buf, "\r\n");
-                if (token != NULL) {
-                    (*argv)[*argc] = strdup(token);  // Strip newline.
-                } else {
-                    --*argc;
-                }
-            }
-
-            fclose(fp);
-            printf("Got arguments from %s\n", COMMAND_FILE);
-        }
-    }
-
-    // --> write the arguments we have back into the bootloader control block
-    // always boot into recovery after this (until finish_recovery() is called)
-    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
-    strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
-    int i;
-    for (i = 1; i < *argc; ++i) {
-        strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
-        strlcat(boot.recovery, "\n", sizeof(boot.recovery));
-    }
-    if (!write_bootloader_message(boot, &err)) {
-        printf("%s\n", err.c_str());
-    }
+  return write_bootloader_message({options}, &err);
 }
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h
index e0fc2cd..4ad18f2 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -17,23 +17,21 @@
 #ifndef _BOOTLOADER_MESSAGE_H
 #define _BOOTLOADER_MESSAGE_H
 
+#include <assert.h>
 #include <stddef.h>
+#include <stdint.h>
 
 // Spaces used by misc partition are as below:
-// 0   - 2K     Bootloader Message
-// 2K  - 16K    Used by Vendor's bootloader
+// 0   - 2K     For bootloader_message
+// 2K  - 16K    Used by Vendor's bootloader (the 2K - 4K range may be optionally used
+//              as bootloader_message_ab struct)
 // 16K - 64K    Used by uncrypt and recovery to store wipe_package for A/B devices
 // Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
 // are not configurable without changing all of them.
-#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
-static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = BOARD_RECOVERY_BLDRMSG_OFFSET;
-static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024 + BOOTLOADER_MESSAGE_OFFSET_IN_MISC;
-#else
 static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
 static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
-#endif
 
-/* Bootloader Message
+/* Bootloader Message (2-KiB)
  *
  * This structure describes the content of a block in flash
  * that is used for recovery and the bootloader to talk to
@@ -44,8 +42,9 @@
  * It is also updated by the bootloader when firmware update
  * is complete (to boot into recovery for any final cleanup)
  *
- * The status field is written by the bootloader after the
- * completion of an "update-radio" or "update-hboot" command.
+ * The status field was used by the bootloader after the completion
+ * of an "update-radio" or "update-hboot" command, which has been
+ * deprecated since Froyo.
  *
  * The recovery field is only written by linux and used
  * for the system to send a message to recovery or the
@@ -56,18 +55,17 @@
  * package it is.  If the value is of the format "#/#" (eg, "1/3"),
  * the UI will add a simple indicator of that status.
  *
- * The slot_suffix field is used for A/B implementations where the
- * bootloader does not set the androidboot.ro.boot.slot_suffix kernel
- * commandline parameter. This is used by fs_mgr to mount /system and
- * other partitions with the slotselect flag set in fstab. A/B
- * implementations are free to use all 32 bytes and may store private
- * data past the first NUL-byte in this field.
+ * We used to have slot_suffix field for A/B boot control metadata in
+ * this struct, which gets unintentionally cleared by recovery or
+ * uncrypt. Move it into struct bootloader_message_ab to avoid the
+ * issue.
  */
 struct bootloader_message {
     char command[32];
     char status[32];
     char recovery[768];
 
+#ifdef USE_OLD_BOOTLOADER_MESSAGE
     // The 'recovery' field used to be 1024 bytes.  It has only ever
     // been used to store the recovery command line, so 768 bytes
     // should be plenty.  We carve off the last 256 bytes to store the
@@ -76,23 +74,156 @@
     char stage[32];
     char slot_suffix[32];
     char reserved[192];
+#else
+    // The 'recovery' field used to be 1024 bytes.  It has only ever
+    // been used to store the recovery command line, so 768 bytes
+    // should be plenty.  We carve off the last 256 bytes to store the
+    // stage string (for multistage packages) and possible future
+    // expansion.
+    char stage[32];
+
+    // The 'reserved' field used to be 224 bytes when it was initially
+    // carved off from the 1024-byte recovery field. Bump it up to
+    // 1184-byte so that the entire bootloader_message struct rounds up
+    // to 2048-byte.
+    char reserved[1184];
+#endif
 };
 
+/**
+ * We must be cautious when changing the bootloader_message struct size,
+ * because A/B-specific fields may end up with different offsets.
+ */
+#if !defined(USE_OLD_BOOTLOADER_MESSAGE) && ((__STDC_VERSION__ >= 201112L) || defined(__cplusplus))
+static_assert(sizeof(struct bootloader_message) == 2048,
+              "struct bootloader_message size changes, which may break A/B devices");
+#endif
+
+/**
+ * The A/B-specific bootloader message structure (4-KiB).
+ *
+ * We separate A/B boot control metadata from the regular bootloader
+ * message struct and keep it here. Everything that's A/B-specific
+ * stays after struct bootloader_message, which should be managed by
+ * the A/B-bootloader or boot control HAL.
+ *
+ * The slot_suffix field is used for A/B implementations where the
+ * bootloader does not set the androidboot.ro.boot.slot_suffix kernel
+ * commandline parameter. This is used by fs_mgr to mount /system and
+ * other partitions with the slotselect flag set in fstab. A/B
+ * implementations are free to use all 32 bytes and may store private
+ * data past the first NUL-byte in this field. It is encouraged, but
+ * not mandatory, to use 'struct bootloader_control' described below.
+ */
+struct bootloader_message_ab {
+    struct bootloader_message message;
+    char slot_suffix[32];
+
+    // Round up the entire struct to 4096-byte.
+    char reserved[2016];
+};
+
+/**
+ * Be cautious about the struct size change, in case we put anything post
+ * bootloader_message_ab struct (b/29159185).
+ */
+#if !defined(USE_OLD_BOOTLOADER_MESSAGE) && ((__STDC_VERSION__ >= 201112L) || defined(__cplusplus))
+static_assert(sizeof(struct bootloader_message_ab) == 4096,
+              "struct bootloader_message_ab size changes");
+#endif
+
+#define BOOT_CTRL_MAGIC   0x42414342 /* Bootloader Control AB */
+#define BOOT_CTRL_VERSION 1
+
+struct slot_metadata {
+    // Slot priority with 15 meaning highest priority, 1 lowest
+    // priority and 0 the slot is unbootable.
+    uint8_t priority : 4;
+    // Number of times left attempting to boot this slot.
+    uint8_t tries_remaining : 3;
+    // 1 if this slot has booted successfully, 0 otherwise.
+    uint8_t successful_boot : 1;
+    // 1 if this slot is corrupted from a dm-verity corruption, 0
+    // otherwise.
+    uint8_t verity_corrupted : 1;
+    // Reserved for further use.
+    uint8_t reserved : 7;
+} __attribute__((packed));
+
+/* Bootloader Control AB
+ *
+ * This struct can be used to manage A/B metadata. It is designed to
+ * be put in the 'slot_suffix' field of the 'bootloader_message'
+ * structure described above. It is encouraged to use the
+ * 'bootloader_control' structure to store the A/B metadata, but not
+ * mandatory.
+ */
+struct bootloader_control {
+    // NUL terminated active slot suffix.
+    char slot_suffix[4];
+    // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
+    uint32_t magic;
+    // Version of struct being used (see BOOT_CTRL_VERSION).
+    uint8_t version;
+    // Number of slots being managed.
+    uint8_t nb_slot : 3;
+    // Number of times left attempting to boot recovery.
+    uint8_t recovery_tries_remaining : 3;
+    // Ensure 4-bytes alignment for slot_info field.
+    uint8_t reserved0[2];
+    // Per-slot information.  Up to 4 slots.
+    struct slot_metadata slot_info[4];
+    // Reserved for further use.
+    uint8_t reserved1[8];
+    // CRC32 of all 28 bytes preceding this field (little endian
+    // format).
+    uint32_t crc32_le;
+} __attribute__((packed));
+
+#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
+static_assert(sizeof(struct bootloader_control) ==
+              sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
+              "struct bootloader_control has wrong size");
+#endif
+
 #ifdef __cplusplus
 
 #include <string>
 #include <vector>
 
+// Read bootloader message into boot. Error message will be set in err.
 bool read_bootloader_message(bootloader_message* boot, std::string* err);
+
+// Read bootloader message from the specified misc device into boot.
+bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
+                                  std::string* err);
+
+// Write bootloader message to BCB.
 bool write_bootloader_message(const bootloader_message& boot, std::string* err);
+
+// Write bootloader message to the specified BCB device.
+bool write_bootloader_message_to(const bootloader_message& boot,
+                                 const std::string& misc_blk_device, std::string* err);
+
+// Write bootloader message (boots into recovery with the options) to BCB. Will
+// set the command and recovery fields, and reset the rest.
 bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
+
+// Update bootloader message (boots into recovery with the options) to BCB. Will
+// only update the command and recovery fields.
+bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
+
+// Clear BCB.
 bool clear_bootloader_message(std::string* err);
 
-bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
-bool write_wipe_package(const std::string& package_data, std::string* err);
+// Writes the reboot-bootloader reboot reason to the bootloader_message.
+bool write_reboot_bootloader(std::string* err);
 
-void set_misc_device(std::string name);
-void get_args(int *argc, char ***argv);
+// Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC).
+bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
+
+// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC).
+bool write_wipe_package(const std::string& package_data, std::string* err);
 
 #else
 
@@ -100,6 +231,7 @@
 
 // C Interface.
 bool write_bootloader_message(const char* options);
+bool write_reboot_bootloader(void);
 
 #endif  // ifdef __cplusplus
 
diff --git a/bootloader_message_twrp/Android.mk b/bootloader_message_twrp/Android.mk
new file mode 100644
index 0000000..5a24dc4
--- /dev/null
+++ b/bootloader_message_twrp/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := bootloader_message.cpp
+LOCAL_MODULE := libbootloader_message_twrp
+LOCAL_C_INCLUDES += bionic $(LOCAL_PATH)/include
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 22; echo $$?),0)
+    LOCAL_C_INCLUDES += external/stlport/stlport
+    LOCAL_SHARED_LIBRARIES += libstlport
+else
+    LOCAL_C_INCLUDES += external/libcxx/include
+    LOCAL_SHARED_LIBRARIES += libc++
+endif
+LOCAL_CFLAGS := -Werror -std=c++11
+# ignore bootloader's factory reset command even when written to /misc
+ifeq ($(TW_IGNORE_MISC_WIPE_DATA), true)
+    LOCAL_CFLAGS += -DIGNORE_MISC_WIPE_DATA
+endif
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_SHARED_LIBRARY)
diff --git a/bootloader_message_twrp/bootloader_message.cpp b/bootloader_message_twrp/bootloader_message.cpp
new file mode 100644
index 0000000..55d3a5d
--- /dev/null
+++ b/bootloader_message_twrp/bootloader_message.cpp
@@ -0,0 +1,386 @@
+/*
+ * 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 <bootloader_message_twrp/bootloader_message.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/system_properties.h>
+
+#include <string>
+#include <vector>
+
+static std::string misc_blkdev;
+
+void set_misc_device(const char* name) {
+    misc_blkdev = name;
+}
+
+static std::string get_misc_blk_device(std::string* err) {
+  *err = "";
+  return misc_blkdev;
+}
+
+// In recovery mode, recovery can get started and try to access the misc
+// device before the kernel has actually created it.
+static bool wait_for_device(const std::string& blk_device, std::string* err) {
+  int tries = 0;
+  int ret;
+  err->clear();
+  do {
+    ++tries;
+    struct stat buf;
+    ret = stat(blk_device.c_str(), &buf);
+    if (ret == -1) {
+      char buffer[2048];
+      sprintf(buffer, "failed to stat %s try %d: %s\n",
+                                          blk_device.c_str(), tries, strerror(errno));
+      *err += buffer;
+      /*
+      *err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
+                                          blk_device.c_str(), tries, strerror(errno));
+      */
+      sleep(1);
+    }
+  } while (ret && tries < 10);
+
+  if (ret) {
+    *err += "failed to stat " + blk_device + "\n";
+    /*
+    *err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
+    */
+  }
+  return ret == 0;
+}
+
+static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
+                                size_t offset, std::string* err) {
+  if (!wait_for_device(misc_blk_device, err)) {
+    return false;
+  }
+
+  int fd(open(misc_blk_device.c_str(), O_RDONLY));
+  if (fd < 0) {
+    *err = "failed to open " + misc_blk_device + ": ";
+    *err += strerror(errno);
+    /*
+  android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
+  if (fd == -1) {
+    *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
+                                       strerror(errno));
+    */
+    return false;
+  }
+  if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
+    *err = "failed to lseek " + misc_blk_device + ": ";
+    *err += strerror(errno);
+    close(fd);
+    /*
+
+    *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
+                                       strerror(errno));
+    */
+    return false;
+  }
+
+  if ((size_t)read(fd, p, size) != size) {
+    *err = "failed to read " + misc_blk_device + ": ";
+    *err += strerror(errno);
+    close(fd);
+    /*
+  if (!android::base::ReadFully(fd, p, size)) {
+    *err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
+                                       strerror(errno));
+    */
+    return false;
+  }
+  close(fd);
+  return true;
+}
+
+static bool write_misc_partition(const void* p, size_t size, size_t offset, std::string* err) {
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    *err = "no misc device set";
+    return false;
+  }
+  int fd = (open(misc_blk_device.c_str(), O_WRONLY | O_SYNC));
+  if (fd == -1) {
+    *err = "failed to open " + misc_blk_device + ": ";
+    *err += strerror(errno);
+    /*
+static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
+                                 size_t offset, std::string* err) {
+  android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
+  if (fd == -1) {
+    *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
+                                       strerror(errno));
+    */
+    return false;
+  }
+  if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
+    *err = "failed to lseek " + misc_blk_device + ": ";
+    *err += strerror(errno);
+    close(fd);
+    /*
+    *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
+                                       strerror(errno));
+    */
+    return false;
+  }
+  if ((size_t)write(fd, p, size) != size) {
+    *err = "failed to write " + misc_blk_device + ": ";
+    *err += strerror(errno);
+    close(fd);
+    /*
+  if (!android::base::WriteFully(fd, p, size)) {
+    *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
+                                       strerror(errno));
+    */
+    return false;
+  }
+
+  // TODO: O_SYNC and fsync duplicates each other?
+  if (fsync(fd) == -1) {
+    *err = "failed to fsync " + misc_blk_device + ": ";
+    *err += strerror(errno);
+    close(fd);
+    /*
+  if (fsync(fd) == -1) {
+    *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
+                                       strerror(errno));
+    */
+    return false;
+  }
+  close(fd);
+  return true;
+}
+
+bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
+                                  std::string* err) {
+  return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
+                             BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+}
+
+bool read_bootloader_message(bootloader_message* boot, std::string* err) {
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
+  return read_bootloader_message_from(boot, misc_blk_device, err);
+}
+
+bool write_bootloader_message_to(const bootloader_message& boot, __unused const std::string& misc_blk_device,
+                                 std::string* err) {
+  return write_misc_partition(&boot, sizeof(boot),
+                              BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+}
+
+bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
+  return write_bootloader_message_to(boot, misc_blk_device, err);
+}
+
+// libc++ in 5.1 does not know how to handle a std::string* so this craziness is needed
+bool clear_bootloader_message(void* err) {
+  std::string &s = *(static_cast<std::string*>(err));
+  return clear_bootloader_message(&s);
+}
+
+bool clear_bootloader_message(std::string* err) {
+  bootloader_message boot = {};
+  return write_bootloader_message(boot, err);
+}
+
+bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
+  bootloader_message boot = {};
+  strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+  strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+  for (const auto& s : options) {
+    strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
+    if (s.substr(s.size() - 1) != "\n") {
+      strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+    }
+  }
+  return write_bootloader_message(boot, err);
+}
+
+bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
+  bootloader_message boot;
+  if (!read_bootloader_message(&boot, err)) {
+    return false;
+  }
+
+  // Zero out the entire fields.
+  memset(boot.command, 0, sizeof(boot.command));
+  memset(boot.recovery, 0, sizeof(boot.recovery));
+
+  strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+  strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+  for (const auto& s : options) {
+    strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
+    if (s.back() != '\n') {
+      strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+    }
+  }
+  return write_bootloader_message(boot, err);
+}
+
+bool write_reboot_bootloader(std::string* err) {
+  bootloader_message boot;
+  if (!read_bootloader_message(&boot, err)) {
+    return false;
+  }
+  if (boot.command[0] != '\0') {
+    *err = "Bootloader command pending.";
+    return false;
+  }
+  strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
+  return write_bootloader_message(boot, err);
+}
+
+bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
+  package_data->resize(size);
+  return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
+                             WIPE_PACKAGE_OFFSET_IN_MISC, err);
+}
+
+bool write_wipe_package(const std::string& package_data, std::string* err) {
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
+  return write_misc_partition(package_data.data(), package_data.size(),
+                              WIPE_PACKAGE_OFFSET_IN_MISC, err);
+}
+
+extern "C" bool write_reboot_bootloader(void) {
+  std::string err;
+  return write_reboot_bootloader(&err);
+}
+
+extern "C" bool write_bootloader_message(const char* options) {
+  std::string err;
+  bootloader_message boot = {};
+  memcpy(&boot, options, sizeof(boot));
+  return write_bootloader_message(boot, &err);
+}
+
+static const char *COMMAND_FILE = "/cache/recovery/command";
+static const int MAX_ARG_LENGTH = 4096;
+static const int MAX_ARGS = 100;
+
+// command line args come from, in decreasing precedence:
+//   - the actual command line
+//   - the bootloader control block (one per line, after "recovery")
+//   - the contents of COMMAND_FILE (one per line)
+void
+get_args(int *argc, char ***argv) {
+    bootloader_message boot = {};
+    std::string err;
+    if (!read_bootloader_message(&boot, &err)) {
+        printf("%s\n", err.c_str());
+        // If fails, leave a zeroed bootloader_message.
+        memset(&boot, 0, sizeof(boot));
+    }
+    //stage = strndup(boot.stage, sizeof(boot.stage));
+
+    if (boot.command[0] != 0 && boot.command[0] != 255) {
+        printf("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
+    }
+
+    if (boot.status[0] != 0 && boot.status[0] != 255) {
+        printf("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
+    }
+
+    // --- if arguments weren't supplied, look in the bootloader control block
+    if (*argc <= 1) {
+        boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination
+        const char *arg = strtok(boot.recovery, "\n");
+        if (arg != NULL && !strcmp(arg, "recovery")) {
+            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
+            (*argv)[0] = strdup(arg);
+            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
+                if ((arg = strtok(NULL, "\n")) == NULL) break;
+
+// if the device does not have an own recovery key combo we just want to open TWRP after
+// walking through the factory reset screen - without actually doing a factory reset
+#ifdef IGNORE_MISC_WIPE_DATA
+                if (!strcmp(arg, "--wipe_data")) {
+                    (*argv)[*argc] = "";
+                    *argc = *argc -1;
+                    printf("Bootloader arg \"%s\" ignored because TWRP was compiled with TW_IGNORE_MISC_WIPE_DATA\n", arg);
+                    continue;
+                }
+#endif
+                (*argv)[*argc] = strdup(arg);
+            }
+            printf("Got arguments from boot message\n");
+        } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
+            printf("Bad boot message\n\"%.20s\"\n", boot.recovery);
+        }
+    }
+
+    // --- if that doesn't work, try the command file (if we have /cache).
+    if (*argc <= 1/* && has_cache*/) {
+        FILE *fp = fopen(COMMAND_FILE, "r");
+        if (fp != NULL) {
+            char *token;
+            char *argv0 = (*argv)[0];
+            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
+            (*argv)[0] = argv0;  // use the same program name
+
+            char buf[MAX_ARG_LENGTH];
+            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
+                if (!fgets(buf, sizeof(buf), fp)) break;
+                token = strtok(buf, "\r\n");
+                if (token != NULL) {
+                    (*argv)[*argc] = strdup(token);  // Strip newline.
+                } else {
+                    --*argc;
+                }
+            }
+
+            fclose(fp);
+            printf("Got arguments from %s\n", COMMAND_FILE);
+        }
+    }
+
+    // --> write the arguments we have back into the bootloader control block
+    // always boot into recovery after this (until finish_recovery() is called)
+    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+    strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+    int i;
+    for (i = 1; i < *argc; ++i) {
+        strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
+        strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+    }
+    if (!write_bootloader_message(boot, &err)) {
+        printf("%s\n", err.c_str());
+    }
+}
diff --git a/bootloader_message_twrp/include/bootloader_message_twrp/bootloader_message.h b/bootloader_message_twrp/include/bootloader_message_twrp/bootloader_message.h
new file mode 100644
index 0000000..52c1b86
--- /dev/null
+++ b/bootloader_message_twrp/include/bootloader_message_twrp/bootloader_message.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2008 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 _BOOTLOADER_MESSAGE_TWRP_H
+#define _BOOTLOADER_MESSAGE_TWRP_H
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// Spaces used by misc partition are as below:
+// 0   - 2K     For bootloader_message
+// 2K  - 16K    Used by Vendor's bootloader (the 2K - 4K range may be optionally used
+//              as bootloader_message_ab struct)
+// 16K - 64K    Used by uncrypt and recovery to store wipe_package for A/B devices
+// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
+// are not configurable without changing all of them.
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = BOARD_RECOVERY_BLDRMSG_OFFSET;
+static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024 + BOOTLOADER_MESSAGE_OFFSET_IN_MISC;
+#else
+static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
+static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
+#endif
+
+/* Bootloader Message (2-KiB)
+ *
+ * This structure describes the content of a block in flash
+ * that is used for recovery and the bootloader to talk to
+ * each other.
+ *
+ * The command field is updated by linux when it wants to
+ * reboot into recovery or to update radio or bootloader firmware.
+ * It is also updated by the bootloader when firmware update
+ * is complete (to boot into recovery for any final cleanup)
+ *
+ * The status field was used by the bootloader after the completion
+ * of an "update-radio" or "update-hboot" command, which has been
+ * deprecated since Froyo.
+ *
+ * The recovery field is only written by linux and used
+ * for the system to send a message to recovery or the
+ * other way around.
+ *
+ * The stage field is written by packages which restart themselves
+ * multiple times, so that the UI can reflect which invocation of the
+ * package it is.  If the value is of the format "#/#" (eg, "1/3"),
+ * the UI will add a simple indicator of that status.
+ *
+ * We used to have slot_suffix field for A/B boot control metadata in
+ * this struct, which gets unintentionally cleared by recovery or
+ * uncrypt. Move it into struct bootloader_message_ab to avoid the
+ * issue.
+ */
+struct bootloader_message {
+    char command[32];
+    char status[32];
+    char recovery[768];
+
+    // The 'recovery' field used to be 1024 bytes.  It has only ever
+    // been used to store the recovery command line, so 768 bytes
+    // should be plenty.  We carve off the last 256 bytes to store the
+    // stage string (for multistage packages) and possible future
+    // expansion.
+    char stage[32];
+
+    // The 'reserved' field used to be 224 bytes when it was initially
+    // carved off from the 1024-byte recovery field. Bump it up to
+    // 1184-byte so that the entire bootloader_message struct rounds up
+    // to 2048-byte.
+    char reserved[1184];
+};
+
+/**
+ * We must be cautious when changing the bootloader_message struct size,
+ * because A/B-specific fields may end up with different offsets.
+ */
+/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
+static_assert(sizeof(struct bootloader_message) == 2048,
+              "struct bootloader_message size changes, which may break A/B devices");
+#endif*/
+
+/**
+ * The A/B-specific bootloader message structure (4-KiB).
+ *
+ * We separate A/B boot control metadata from the regular bootloader
+ * message struct and keep it here. Everything that's A/B-specific
+ * stays after struct bootloader_message, which should be managed by
+ * the A/B-bootloader or boot control HAL.
+ *
+ * The slot_suffix field is used for A/B implementations where the
+ * bootloader does not set the androidboot.ro.boot.slot_suffix kernel
+ * commandline parameter. This is used by fs_mgr to mount /system and
+ * other partitions with the slotselect flag set in fstab. A/B
+ * implementations are free to use all 32 bytes and may store private
+ * data past the first NUL-byte in this field. It is encouraged, but
+ * not mandatory, to use 'struct bootloader_control' described below.
+ */
+struct bootloader_message_ab {
+    struct bootloader_message message;
+    char slot_suffix[32];
+
+    // Round up the entire struct to 4096-byte.
+    char reserved[2016];
+};
+
+/**
+ * Be cautious about the struct size change, in case we put anything post
+ * bootloader_message_ab struct (b/29159185).
+ */
+/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
+static_assert(sizeof(struct bootloader_message_ab) == 4096,
+              "struct bootloader_message_ab size changes");
+#endif*/
+
+#define BOOT_CTRL_MAGIC   0x42414342 /* Bootloader Control AB */
+#define BOOT_CTRL_VERSION 1
+
+struct slot_metadata {
+    // Slot priority with 15 meaning highest priority, 1 lowest
+    // priority and 0 the slot is unbootable.
+    uint8_t priority : 4;
+    // Number of times left attempting to boot this slot.
+    uint8_t tries_remaining : 3;
+    // 1 if this slot has booted successfully, 0 otherwise.
+    uint8_t successful_boot : 1;
+    // 1 if this slot is corrupted from a dm-verity corruption, 0
+    // otherwise.
+    uint8_t verity_corrupted : 1;
+    // Reserved for further use.
+    uint8_t reserved : 7;
+} __attribute__((packed));
+
+/* Bootloader Control AB
+ *
+ * This struct can be used to manage A/B metadata. It is designed to
+ * be put in the 'slot_suffix' field of the 'bootloader_message'
+ * structure described above. It is encouraged to use the
+ * 'bootloader_control' structure to store the A/B metadata, but not
+ * mandatory.
+ */
+struct bootloader_control {
+    // NUL terminated active slot suffix.
+    char slot_suffix[4];
+    // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
+    uint32_t magic;
+    // Version of struct being used (see BOOT_CTRL_VERSION).
+    uint8_t version;
+    // Number of slots being managed.
+    uint8_t nb_slot : 3;
+    // Number of times left attempting to boot recovery.
+    uint8_t recovery_tries_remaining : 3;
+    // Ensure 4-bytes alignment for slot_info field.
+    uint8_t reserved0[2];
+    // Per-slot information.  Up to 4 slots.
+    struct slot_metadata slot_info[4];
+    // Reserved for further use.
+    uint8_t reserved1[8];
+    // CRC32 of all 28 bytes preceding this field (little endian
+    // format).
+    uint32_t crc32_le;
+} __attribute__((packed));
+
+/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
+static_assert(sizeof(struct bootloader_control) ==
+              sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
+              "struct bootloader_control has wrong size");
+#endif*/
+
+#ifdef __cplusplus
+
+#include <string.h>
+#include <vector>
+
+// Read bootloader message into boot. Error message will be set in err.
+bool read_bootloader_message(bootloader_message* boot, std::string* err);
+
+// Read bootloader message from the specified misc device into boot.
+bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
+                                  std::string* err);
+
+// Write bootloader message to BCB.
+bool write_bootloader_message(const bootloader_message& boot, std::string* err);
+
+// Write bootloader message to the specified BCB device.
+bool write_bootloader_message_to(const bootloader_message& boot,
+                                 const std::string& misc_blk_device, std::string* err);
+
+// Write bootloader message (boots into recovery with the options) to BCB. Will
+// set the command and recovery fields, and reset the rest.
+bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
+
+// Update bootloader message (boots into recovery with the options) to BCB. Will
+// only update the command and recovery fields.
+bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
+
+// Clear BCB.
+bool clear_bootloader_message(void* err);
+bool clear_bootloader_message(std::string* err);
+
+// Writes the reboot-bootloader reboot reason to the bootloader_message.
+bool write_reboot_bootloader(std::string* err);
+
+// Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC).
+bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
+
+void set_misc_device(const char* name);
+void get_args(int *argc, char ***argv);
+
+// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC).
+bool write_wipe_package(const std::string& package_data, std::string* err);
+
+#else
+
+#include <stdbool.h>
+
+// C Interface.
+bool write_bootloader_message(const char* options);
+bool write_reboot_bootloader(void);
+
+#endif  // ifdef __cplusplus
+
+#endif  // _BOOTLOADER_MESSAGE_TWRP_H
diff --git a/common.h b/common.h
index 3afb633..b0901f4 100644
--- a/common.h
+++ b/common.h
@@ -17,9 +17,9 @@
 #ifndef RECOVERY_COMMON_H
 #define RECOVERY_COMMON_H
 
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdarg.h>
+#include <string>
 
 #ifdef __cplusplus
 extern "C" {
@@ -40,18 +40,29 @@
 #define STRINGIFY(x) #x
 #define EXPAND(x) STRINGIFY(x)
 
+class RecoveryUI;
+
+extern RecoveryUI* ui;
 extern bool modified_flash;
 //typedef struct fstab_rec Volume;
 
+// The current stage, e.g. "1/2".
+extern std::string stage;
+
+// The reason argument provided in "--reason=".
+extern const char* reason;
+
 // fopen a file, mounting volumes and making parent dirs as necessary.
 FILE* fopen_path(const char *path, const char *mode);
 
 void ui_print(const char* format, ...);
 
-bool is_ro_debuggable();
+static bool is_ro_debuggable();
 
 #ifdef __cplusplus
 }
 #endif
 
+bool reboot(const std::string& command);
+
 #endif  // RECOVERY_COMMON_H
diff --git a/device.cpp b/device.cpp
index e717ddd..6150186 100644
--- a/device.cpp
+++ b/device.cpp
@@ -60,7 +60,7 @@
   return menu_position < 0 ? NO_ACTION : MENU_ACTIONS[menu_position];
 }
 
-int Device::HandleMenuKey(int key, int visible) {
+int Device::HandleMenuKey(int key, bool visible) {
   if (!visible) {
     return kNoAction;
   }
diff --git a/device.h b/device.h
index 5017782..639e2bf 100644
--- a/device.h
+++ b/device.h
@@ -20,96 +20,90 @@
 #include "ui.h"
 
 class Device {
-  public:
-    Device(RecoveryUI* ui) : ui_(ui) { }
-    virtual ~Device() { }
+ public:
+  explicit Device(RecoveryUI* ui) : ui_(ui) {}
+  virtual ~Device() {}
 
-    // Called to obtain the UI object that should be used to display
-    // the recovery user interface for this device.  You should not
-    // have called Init() on the UI object already, the caller will do
-    // that after this method returns.
-    virtual RecoveryUI* GetUI() { return ui_; }
+  // Called to obtain the UI object that should be used to display the recovery user interface for
+  // this device. You should not have called Init() on the UI object already, the caller will do
+  // that after this method returns.
+  virtual RecoveryUI* GetUI() {
+    return ui_;
+  }
 
-    // Called when recovery starts up (after the UI has been obtained
-    // and initialized and after the arguments have been parsed, but
-    // before anything else).
-    virtual void StartRecovery() { };
+  // Called when recovery starts up (after the UI has been obtained and initialized and after the
+  // arguments have been parsed, but before anything else).
+  virtual void StartRecovery() {};
 
-    // Called from the main thread when recovery is at the main menu
-    // and waiting for input, and a key is pressed.  (Note that "at"
-    // the main menu does not necessarily mean the menu is visible;
-    // recovery will be at the main menu with it invisible after an
-    // unsuccessful operation [ie OTA package failure], or if recovery
-    // is started with no command.)
-    //
-    // key is the code of the key just pressed.  (You can call
-    // IsKeyPressed() on the RecoveryUI object you returned from GetUI
-    // if you want to find out if other keys are held down.)
-    //
-    // visible is true if the menu is visible.
-    //
-    // Return 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)
-    virtual int HandleMenuKey(int key, int visible);
+  // Called from the main thread when recovery is at the main menu and waiting for input, and a key
+  // is pressed. (Note that "at" the main menu does not necessarily mean the menu is visible;
+  // recovery will be at the main menu with it invisible after an unsuccessful operation [ie OTA
+  // package failure], or if recovery is started with no command.)
+  //
+  // 'key' is the code of the key just pressed. (You can call IsKeyPressed() on the RecoveryUI
+  // object you returned from GetUI if you want to find out if other keys are held down.)
+  //
+  // 'visible' is true if the menu is visible.
+  //
+  // 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)
+  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,
-    };
+  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,
+  };
 
-    // 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();
+  // 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
-    // device_handle_key().  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);
+  // 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
+  // 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;
+  static const int kNoAction = -1;
+  static const int kHighlightUp = -2;
+  static const int kHighlightDown = -3;
+  static const int kInvokeItem = -4;
 
-    // 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 selects the
-    // option from the menu, to perform whatever device-specific wiping
-    // actions are needed.
-    // Return true on success; returning false from PreWipeData will prevent
-    // the regular wipe, and returning false from PostWipeData will cause
-    // the wipe to be considered a failure.
-    virtual bool PreWipeData() { return true; }
-    virtual bool PostWipeData() { return true; }
+  // 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
+  // selects the option from the menu, to perform whatever device-specific wiping actions as needed.
+  // Returns true on success; returning false from PreWipeData will prevent the regular wipe, and
+  // returning false from PostWipeData will cause the wipe to be considered a failure.
+  virtual bool PreWipeData() {
+    return true;
+  }
 
-  private:
-    RecoveryUI* ui_;
+  virtual bool PostWipeData() {
+    return true;
+  }
+
+ private:
+  RecoveryUI* ui_;
 };
 
-// The device-specific library must define this function (or the
-// default one will be used, if there is no device-specific library).
-// It returns the Device object that recovery should use.
+// The device-specific library must define this function (or the default one will be used, if there
+// is no device-specific library). It returns the Device object that recovery should use.
 Device* make_device();
 
 #endif  // _DEVICE_H
diff --git a/edify/Android.mk b/edify/Android.mk
index 71cf765..d8058c1 100644
--- a/edify/Android.mk
+++ b/edify/Android.mk
@@ -1,23 +1,36 @@
 # Copyright 2009 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.
 
 LOCAL_PATH := $(call my-dir)
 
 edify_src_files := \
-	lexer.ll \
-	parser.yy \
-	expr.cpp
+    lexer.ll \
+    parser.yy \
+    expr.cpp
 
 #
-# Build the host-side command line tool
+# Build the host-side command line tool (host executable)
 #
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-		$(edify_src_files) \
-		main.cpp
+    $(edify_src_files) \
+    edify_parser.cpp
 
+LOCAL_CFLAGS := -Werror
 LOCAL_CPPFLAGS := -g -O0
-LOCAL_MODULE := edify
+LOCAL_MODULE := edify_parser
 LOCAL_YACCFLAGS := -v
 LOCAL_CPPFLAGS += -Wno-unused-parameter
 LOCAL_CPPFLAGS += -Wno-deprecated-register
@@ -28,12 +41,13 @@
 include $(BUILD_HOST_EXECUTABLE)
 
 #
-# Build the device-side library
+# Build the device-side library (static library)
 #
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := $(edify_src_files)
 
+LOCAL_CFLAGS := -Werror
 LOCAL_CPPFLAGS := -Wno-unused-parameter
 LOCAL_CPPFLAGS += -Wno-deprecated-register
 LOCAL_MODULE := libedify
diff --git a/edify/README b/edify/README.md
similarity index 99%
rename from edify/README
rename to edify/README.md
index 810455c..b3330e2 100644
--- a/edify/README
+++ b/edify/README.md
@@ -1,3 +1,6 @@
+edify
+=====
+
 Update scripts (from donut onwards) are written in a new little
 scripting language ("edify") that is superficially somewhat similar to
 the old one ("amend").  This is a brief overview of the new language.
diff --git a/edify/edify_parser.cpp b/edify/edify_parser.cpp
new file mode 100644
index 0000000..f1b5628
--- /dev/null
+++ b/edify/edify_parser.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/**
+ * This is a host-side tool for validating a given edify script file.
+ *
+ * We used to have edify test cases here, which have been moved to
+ * tests/component/edify_test.cpp.
+ *
+ * Caveat: It doesn't recognize functions defined through updater, which
+ * makes the tool less useful. We should either extend the tool or remove it.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+
+#include "expr.h"
+
+static void ExprDump(int depth, const std::unique_ptr<Expr>& n, const std::string& script) {
+    printf("%*s", depth*2, "");
+    printf("%s %p (%d-%d) \"%s\"\n",
+           n->name.c_str(), n->fn, n->start, n->end,
+           script.substr(n->start, n->end - n->start).c_str());
+    for (size_t i = 0; i < n->argv.size(); ++i) {
+        ExprDump(depth+1, n->argv[i], script);
+    }
+}
+
+int main(int argc, char** argv) {
+    RegisterBuiltins();
+
+    if (argc != 2) {
+        printf("Usage: %s <edify script>\n", argv[0]);
+        return 1;
+    }
+
+    std::string buffer;
+    if (!android::base::ReadFileToString(argv[1], &buffer)) {
+        printf("%s: failed to read %s: %s\n", argv[0], argv[1], strerror(errno));
+        return 1;
+    }
+
+    std::unique_ptr<Expr> root;
+    int error_count = 0;
+    int error = parse_string(buffer.data(), &root, &error_count);
+    printf("parse returned %d; %d errors encountered\n", error, error_count);
+    if (error == 0 || error_count > 0) {
+
+        ExprDump(0, root, buffer);
+
+        State state(buffer, nullptr);
+        std::string result;
+        if (!Evaluate(&state, root, &result)) {
+            printf("result was NULL, message is: %s\n",
+                   (state.errmsg.empty() ? "(NULL)" : state.errmsg.c_str()));
+        } else {
+            printf("result is [%s]\n", result.c_str());
+        }
+    }
+    return 0;
+}
diff --git a/edify/expr.cpp b/edify/expr.cpp
index cc14fbe..54ab332 100644
--- a/edify/expr.cpp
+++ b/edify/expr.cpp
@@ -14,201 +14,172 @@
  * limitations under the License.
  */
 
-#include <string.h>
-#include <stdbool.h>
+#include "expr.h"
+
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
+#include <string.h>
 #include <unistd.h>
 
+#include <memory>
 #include <string>
+#include <unordered_map>
+#include <vector>
 
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-#include "expr.h"
-
 // Functions should:
 //
 //    - return a malloc()'d string
-//    - if Evaluate() on any argument returns NULL, return NULL.
+//    - if Evaluate() on any argument returns nullptr, return nullptr.
 
-int BooleanString(const char* s) {
-    return s[0] != '\0';
+static bool BooleanString(const std::string& s) {
+    return !s.empty();
 }
 
-char* Evaluate(State* state, Expr* expr) {
-    Value* v = expr->fn(expr->name, state, expr->argc, expr->argv);
-    if (v == NULL) return NULL;
+bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result) {
+    if (result == nullptr) {
+        return false;
+    }
+
+    std::unique_ptr<Value> v(expr->fn(expr->name.c_str(), state, expr->argv));
+    if (!v) {
+        return false;
+    }
     if (v->type != VAL_STRING) {
         ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type);
-        FreeValue(v);
-        return NULL;
+        return false;
     }
-    char* result = v->data;
-    free(v);
-    return result;
+
+    *result = v->data;
+    return true;
 }
 
-Value* EvaluateValue(State* state, Expr* expr) {
-    return expr->fn(expr->name, state, expr->argc, expr->argv);
+Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr) {
+    return expr->fn(expr->name.c_str(), state, expr->argv);
 }
 
-Value* StringValue(char* str) {
-    if (str == NULL) return NULL;
-    Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value)));
-    v->type = VAL_STRING;
-    v->size = strlen(str);
-    v->data = str;
-    return v;
-}
-
-void FreeValue(Value* v) {
-    if (v == NULL) return;
-    free(v->data);
-    free(v);
-}
-
-Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc == 0) {
-        return StringValue(strdup(""));
+Value* StringValue(const char* str) {
+    if (str == nullptr) {
+        return nullptr;
     }
-    char** strings = reinterpret_cast<char**>(malloc(argc * sizeof(char*)));
-    int i;
-    for (i = 0; i < argc; ++i) {
-        strings[i] = NULL;
+    return new Value(VAL_STRING, str);
+}
+
+Value* StringValue(const std::string& str) {
+    return StringValue(str.c_str());
+}
+
+Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    if (argv.empty()) {
+        return StringValue("");
     }
-    char* result = NULL;
-    int length = 0;
-    for (i = 0; i < argc; ++i) {
-        strings[i] = Evaluate(state, argv[i]);
-        if (strings[i] == NULL) {
-            goto done;
+    std::string result;
+    for (size_t i = 0; i < argv.size(); ++i) {
+        std::string str;
+        if (!Evaluate(state, argv[i], &str)) {
+            return nullptr;
         }
-        length += strlen(strings[i]);
+        result += str;
     }
 
-    result = reinterpret_cast<char*>(malloc(length+1));
-    int p;
-    p = 0;
-    for (i = 0; i < argc; ++i) {
-        strcpy(result+p, strings[i]);
-        p += strlen(strings[i]);
-    }
-    result[p] = '\0';
-
-  done:
-    for (i = 0; i < argc; ++i) {
-        free(strings[i]);
-    }
-    free(strings);
     return StringValue(result);
 }
 
-Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc != 2 && argc != 3) {
-        free(state->errmsg);
-        state->errmsg = strdup("ifelse expects 2 or 3 arguments");
-        return NULL;
-    }
-    char* cond = Evaluate(state, argv[0]);
-    if (cond == NULL) {
-        return NULL;
+Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    if (argv.size() != 2 && argv.size() != 3) {
+        state->errmsg = "ifelse expects 2 or 3 arguments";
+        return nullptr;
     }
 
-    if (BooleanString(cond) == true) {
-        free(cond);
-        return EvaluateValue(state, argv[1]);
-    } else {
-        if (argc == 3) {
-            free(cond);
-            return EvaluateValue(state, argv[2]);
-        } else {
-            return StringValue(cond);
-        }
+    std::string cond;
+    if (!Evaluate(state, argv[0], &cond)) {
+        return nullptr;
     }
+
+    if (!cond.empty()) {
+        return EvaluateValue(state, argv[1]);
+    } else if (argv.size() == 3) {
+        return EvaluateValue(state, argv[2]);
+    }
+
+    return StringValue("");
 }
 
-Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
-    char* msg = NULL;
-    if (argc > 0) {
-        msg = Evaluate(state, argv[0]);
-    }
-    free(state->errmsg);
-    if (msg) {
+Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    std::string msg;
+    if (!argv.empty() && Evaluate(state, argv[0], &msg)) {
         state->errmsg = msg;
     } else {
-        state->errmsg = strdup("called abort()");
+        state->errmsg = "called abort()";
     }
-    return NULL;
+    return nullptr;
 }
 
-Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
-    int i;
-    for (i = 0; i < argc; ++i) {
-        char* v = Evaluate(state, argv[i]);
-        if (v == NULL) {
-            return NULL;
+Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    for (size_t i = 0; i < argv.size(); ++i) {
+        std::string result;
+        if (!Evaluate(state, argv[i], &result)) {
+            return nullptr;
         }
-        int b = BooleanString(v);
-        free(v);
-        if (!b) {
-            int prefix_len;
+        if (result.empty()) {
             int len = argv[i]->end - argv[i]->start;
-            char* err_src = reinterpret_cast<char*>(malloc(len + 20));
-            strcpy(err_src, "assert failed: ");
-            prefix_len = strlen(err_src);
-            memcpy(err_src + prefix_len, state->script + argv[i]->start, len);
-            err_src[prefix_len + len] = '\0';
-            free(state->errmsg);
-            state->errmsg = err_src;
-            return NULL;
+            state->errmsg = "assert failed: " + state->script.substr(argv[i]->start, len);
+            return nullptr;
         }
     }
-    return StringValue(strdup(""));
+    return StringValue("");
 }
 
-Value* SleepFn(const char* name, State* state, int argc, Expr* argv[]) {
-    char* val = Evaluate(state, argv[0]);
-    if (val == NULL) {
-        return NULL;
+Value* SleepFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    std::string val;
+    if (!Evaluate(state, argv[0], &val)) {
+        return nullptr;
     }
-    int v = strtol(val, NULL, 10);
+
+    int v;
+    if (!android::base::ParseInt(val.c_str(), &v, 0)) {
+        return nullptr;
+    }
     sleep(v);
+
     return StringValue(val);
 }
 
-Value* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
-    int i;
-    for (i = 0; i < argc; ++i) {
-        char* v = Evaluate(state, argv[i]);
-        if (v == NULL) {
-            return NULL;
+Value* StdoutFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    for (size_t i = 0; i < argv.size(); ++i) {
+        std::string v;
+        if (!Evaluate(state, argv[i], &v)) {
+            return nullptr;
         }
-        fputs(v, stdout);
-        free(v);
+        fputs(v.c_str(), stdout);
     }
-    return StringValue(strdup(""));
+    return StringValue("");
 }
 
 Value* LogicalAndFn(const char* name, State* state,
-                   int argc, Expr* argv[]) {
-    char* left = Evaluate(state, argv[0]);
-    if (left == NULL) return NULL;
-    if (BooleanString(left) == true) {
-        free(left);
+                    const std::vector<std::unique_ptr<Expr>>& argv) {
+    std::string left;
+    if (!Evaluate(state, argv[0], &left)) {
+        return nullptr;
+    }
+    if (BooleanString(left)) {
         return EvaluateValue(state, argv[1]);
     } else {
-        return StringValue(left);
+        return StringValue("");
     }
 }
 
 Value* LogicalOrFn(const char* name, State* state,
-                   int argc, Expr* argv[]) {
-    char* left = Evaluate(state, argv[0]);
-    if (left == NULL) return NULL;
-    if (BooleanString(left) == false) {
-        free(left);
+                   const std::vector<std::unique_ptr<Expr>>& argv) {
+    std::string left;
+    if (!Evaluate(state, argv[0], &left)) {
+        return nullptr;
+    }
+    if (!BooleanString(left)) {
         return EvaluateValue(state, argv[1]);
     } else {
         return StringValue(left);
@@ -216,174 +187,144 @@
 }
 
 Value* LogicalNotFn(const char* name, State* state,
-                    int argc, Expr* argv[]) {
-    char* val = Evaluate(state, argv[0]);
-    if (val == NULL) return NULL;
-    bool bv = BooleanString(val);
-    free(val);
-    return StringValue(strdup(bv ? "" : "t"));
+                    const std::vector<std::unique_ptr<Expr>>& argv) {
+    std::string val;
+    if (!Evaluate(state, argv[0], &val)) {
+        return nullptr;
+    }
+
+    return StringValue(BooleanString(val) ? "" : "t");
 }
 
 Value* SubstringFn(const char* name, State* state,
-                   int argc, Expr* argv[]) {
-    char* needle = Evaluate(state, argv[0]);
-    if (needle == NULL) return NULL;
-    char* haystack = Evaluate(state, argv[1]);
-    if (haystack == NULL) {
-        free(needle);
-        return NULL;
+                   const std::vector<std::unique_ptr<Expr>>& argv) {
+    std::string needle;
+    if (!Evaluate(state, argv[0], &needle)) {
+        return nullptr;
     }
 
-    char* result = strdup(strstr(haystack, needle) ? "t" : "");
-    free(needle);
-    free(haystack);
+    std::string haystack;
+    if (!Evaluate(state, argv[1], &haystack)) {
+        return nullptr;
+    }
+
+    std::string result = (haystack.find(needle) != std::string::npos) ? "t" : "";
     return StringValue(result);
 }
 
-Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
-    char* left = Evaluate(state, argv[0]);
-    if (left == NULL) return NULL;
-    char* right = Evaluate(state, argv[1]);
-    if (right == NULL) {
-        free(left);
-        return NULL;
+Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    std::string left;
+    if (!Evaluate(state, argv[0], &left)) {
+        return nullptr;
+    }
+    std::string right;
+    if (!Evaluate(state, argv[1], &right)) {
+        return nullptr;
     }
 
-    char* result = strdup(strcmp(left, right) == 0 ? "t" : "");
-    free(left);
-    free(right);
+    const char* result = (left == right) ? "t" : "";
     return StringValue(result);
 }
 
-Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
-    char* left = Evaluate(state, argv[0]);
-    if (left == NULL) return NULL;
-    char* right = Evaluate(state, argv[1]);
-    if (right == NULL) {
-        free(left);
-        return NULL;
+Value* InequalityFn(const char* name, State* state,
+                    const std::vector<std::unique_ptr<Expr>>& argv) {
+    std::string left;
+    if (!Evaluate(state, argv[0], &left)) {
+        return nullptr;
+    }
+    std::string right;
+    if (!Evaluate(state, argv[1], &right)) {
+        return nullptr;
     }
 
-    char* result = strdup(strcmp(left, right) != 0 ? "t" : "");
-    free(left);
-    free(right);
+    const char* result = (left != right) ? "t" : "";
     return StringValue(result);
 }
 
-Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
-    Value* left = EvaluateValue(state, argv[0]);
-    if (left == NULL) return NULL;
-    FreeValue(left);
+Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    std::unique_ptr<Value> left(EvaluateValue(state, argv[0]));
+    if (!left) {
+        return nullptr;
+    }
     return EvaluateValue(state, argv[1]);
 }
 
-Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc != 2) {
-        free(state->errmsg);
-        state->errmsg = strdup("less_than_int expects 2 arguments");
-        return NULL;
+Value* LessThanIntFn(const char* name, State* state,
+                     const std::vector<std::unique_ptr<Expr>>& argv) {
+    if (argv.size() != 2) {
+        state->errmsg = "less_than_int expects 2 arguments";
+        return nullptr;
     }
 
-    char* left;
-    char* right;
-    if (ReadArgs(state, argv, 2, &left, &right) < 0) return NULL;
-
-    bool result = false;
-    char* end;
-
-    long l_int = strtol(left, &end, 10);
-    if (left[0] == '\0' || *end != '\0') {
-        goto done;
+    std::vector<std::string> args;
+    if (!ReadArgs(state, argv, &args)) {
+        return nullptr;
     }
 
-    long r_int;
-    r_int = strtol(right, &end, 10);
-    if (right[0] == '\0' || *end != '\0') {
-        goto done;
+    // Parse up to at least long long or 64-bit integers.
+    int64_t l_int;
+    if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
+        state->errmsg = "failed to parse int in " + args[0];
+        return nullptr;
     }
 
-    result = l_int < r_int;
+    int64_t r_int;
+    if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
+        state->errmsg = "failed to parse int in " + args[1];
+        return nullptr;
+    }
 
-  done:
-    free(left);
-    free(right);
-    return StringValue(strdup(result ? "t" : ""));
+    return StringValue(l_int < r_int ? "t" : "");
 }
 
 Value* GreaterThanIntFn(const char* name, State* state,
-                        int argc, Expr* argv[]) {
-    if (argc != 2) {
-        free(state->errmsg);
-        state->errmsg = strdup("greater_than_int expects 2 arguments");
-        return NULL;
+                        const std::vector<std::unique_ptr<Expr>>& argv) {
+    if (argv.size() != 2) {
+        state->errmsg = "greater_than_int expects 2 arguments";
+        return nullptr;
     }
 
-    Expr* temp[2];
-    temp[0] = argv[1];
-    temp[1] = argv[0];
-
-    return LessThanIntFn(name, state, 2, temp);
-}
-
-Value* Literal(const char* name, State* state, int argc, Expr* argv[]) {
-    return StringValue(strdup(name));
-}
-
-Expr* Build(Function fn, YYLTYPE loc, int count, ...) {
-    va_list v;
-    va_start(v, count);
-    Expr* e = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
-    e->fn = fn;
-    e->name = "(operator)";
-    e->argc = count;
-    e->argv = reinterpret_cast<Expr**>(malloc(count * sizeof(Expr*)));
-    int i;
-    for (i = 0; i < count; ++i) {
-        e->argv[i] = va_arg(v, Expr*);
+    std::vector<std::string> args;
+    if (!ReadArgs(state, argv, &args)) {
+        return nullptr;
     }
-    va_end(v);
-    e->start = loc.start;
-    e->end = loc.end;
-    return e;
+
+    // Parse up to at least long long or 64-bit integers.
+    int64_t l_int;
+    if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
+        state->errmsg = "failed to parse int in " + args[0];
+        return nullptr;
+    }
+
+    int64_t r_int;
+    if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
+        state->errmsg = "failed to parse int in " + args[1];
+        return nullptr;
+    }
+
+    return StringValue(l_int > r_int ? "t" : "");
+}
+
+Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    return StringValue(name);
 }
 
 // -----------------------------------------------------------------
 //   the function table
 // -----------------------------------------------------------------
 
-static int fn_entries = 0;
-static int fn_size = 0;
-NamedFunction* fn_table = NULL;
+static std::unordered_map<std::string, Function> fn_table;
 
-void RegisterFunction(const char* name, Function fn) {
-    if (fn_entries >= fn_size) {
-        fn_size = fn_size*2 + 1;
-        fn_table = reinterpret_cast<NamedFunction*>(realloc(fn_table, fn_size * sizeof(NamedFunction)));
+void RegisterFunction(const std::string& name, Function fn) {
+    fn_table[name] = fn;
+}
+
+Function FindFunction(const std::string& name) {
+    if (fn_table.find(name) == fn_table.end()) {
+        return nullptr;
+    } else {
+        return fn_table[name];
     }
-    fn_table[fn_entries].name = name;
-    fn_table[fn_entries].fn = fn;
-    ++fn_entries;
-}
-
-static int fn_entry_compare(const void* a, const void* b) {
-    const char* na = ((const NamedFunction*)a)->name;
-    const char* nb = ((const NamedFunction*)b)->name;
-    return strcmp(na, nb);
-}
-
-void FinishRegistration() {
-    qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare);
-}
-
-Function FindFunction(const char* name) {
-    NamedFunction key;
-    key.name = name;
-    NamedFunction* nf = reinterpret_cast<NamedFunction*>(bsearch(&key, fn_table, fn_entries,
-            sizeof(NamedFunction), fn_entry_compare));
-    if (nf == NULL) {
-        return NULL;
-    }
-    return nf->fn;
 }
 
 void RegisterBuiltins() {
@@ -404,106 +345,56 @@
 //   convenience methods for functions
 // -----------------------------------------------------------------
 
-// Evaluate the expressions in argv, giving 'count' char* (the ... is
-// zero or more char** to put them in).  If any expression evaluates
-// to NULL, free the rest and return -1.  Return 0 on success.
-int ReadArgs(State* state, Expr* argv[], int count, ...) {
-    char** args = reinterpret_cast<char**>(malloc(count * sizeof(char*)));
-    va_list v;
-    va_start(v, count);
-    int i;
-    for (i = 0; i < count; ++i) {
-        args[i] = Evaluate(state, argv[i]);
-        if (args[i] == NULL) {
-            va_end(v);
-            int j;
-            for (j = 0; j < i; ++j) {
-                free(args[j]);
-            }
-            free(args);
-            return -1;
-        }
-        *(va_arg(v, char**)) = args[i];
-    }
-    va_end(v);
-    free(args);
-    return 0;
+// Evaluate the expressions in argv, and put the results of strings in args. If any expression
+// evaluates to nullptr, return false. Return true on success.
+bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+              std::vector<std::string>* args) {
+    return ReadArgs(state, argv, args, 0, argv.size());
 }
 
-// Evaluate the expressions in argv, giving 'count' Value* (the ... is
-// zero or more Value** to put them in).  If any expression evaluates
-// to NULL, free the rest and return -1.  Return 0 on success.
-int ReadValueArgs(State* state, Expr* argv[], int count, ...) {
-    Value** args = reinterpret_cast<Value**>(malloc(count * sizeof(Value*)));
-    va_list v;
-    va_start(v, count);
-    int i;
-    for (i = 0; i < count; ++i) {
-        args[i] = EvaluateValue(state, argv[i]);
-        if (args[i] == NULL) {
-            va_end(v);
-            int j;
-            for (j = 0; j < i; ++j) {
-                FreeValue(args[j]);
-            }
-            free(args);
-            return -1;
-        }
-        *(va_arg(v, Value**)) = args[i];
+bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+              std::vector<std::string>* args, size_t start, size_t len) {
+    if (args == nullptr) {
+        return false;
     }
-    va_end(v);
-    free(args);
-    return 0;
+    if (start + len > argv.size()) {
+        return false;
+    }
+    for (size_t i = start; i < start + len; ++i) {
+        std::string var;
+        if (!Evaluate(state, argv[i], &var)) {
+            args->clear();
+            return false;
+        }
+        args->push_back(var);
+    }
+    return true;
 }
 
-// Evaluate the expressions in argv, returning an array of char*
-// results.  If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// strings it contains.
-char** ReadVarArgs(State* state, int argc, Expr* argv[]) {
-    char** args = (char**)malloc(argc * sizeof(char*));
-    int i = 0;
-    for (i = 0; i < argc; ++i) {
-        args[i] = Evaluate(state, argv[i]);
-        if (args[i] == NULL) {
-            int j;
-            for (j = 0; j < i; ++j) {
-                free(args[j]);
-            }
-            free(args);
-            return NULL;
-        }
-    }
-    return args;
+// Evaluate the expressions in argv, and put the results of Value* in args. If any expression
+// evaluate to nullptr, return false. Return true on success.
+bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+                   std::vector<std::unique_ptr<Value>>* args) {
+    return ReadValueArgs(state, argv, args, 0, argv.size());
 }
 
-// Evaluate the expressions in argv, returning an array of Value*
-// results.  If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// Values it contains.
-Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]) {
-    Value** args = (Value**)malloc(argc * sizeof(Value*));
-    int i = 0;
-    for (i = 0; i < argc; ++i) {
-        args[i] = EvaluateValue(state, argv[i]);
-        if (args[i] == NULL) {
-            int j;
-            for (j = 0; j < i; ++j) {
-                FreeValue(args[j]);
-            }
-            free(args);
-            return NULL;
-        }
+bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+                   std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len) {
+    if (args == nullptr) {
+        return false;
     }
-    return args;
-}
-
-static void ErrorAbortV(State* state, const char* format, va_list ap) {
-    std::string buffer;
-    android::base::StringAppendV(&buffer, format, ap);
-    free(state->errmsg);
-    state->errmsg = strdup(buffer.c_str());
-    return;
+    if (len == 0 || start + len > argv.size()) {
+        return false;
+    }
+    for (size_t i = start; i < start + len; ++i) {
+        std::unique_ptr<Value> v(EvaluateValue(state, argv[i]));
+        if (!v) {
+            args->clear();
+            return false;
+        }
+        args->push_back(std::move(v));
+    }
+    return true;
 }
 
 // Use printf-style arguments to compose an error message to put into
@@ -511,7 +402,7 @@
 Value* ErrorAbort(State* state, const char* format, ...) {
     va_list ap;
     va_start(ap, format);
-    ErrorAbortV(state, format, ap);
+    android::base::StringAppendV(&state->errmsg, format, ap);
     va_end(ap);
     return nullptr;
 }
@@ -519,8 +410,14 @@
 Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) {
     va_list ap;
     va_start(ap, format);
-    ErrorAbortV(state, format, ap);
+    android::base::StringAppendV(&state->errmsg, format, ap);
     va_end(ap);
     state->cause_code = cause_code;
     return nullptr;
 }
+
+State::State(const std::string& script, void* cookie) :
+    script(script),
+    cookie(cookie) {
+}
+
diff --git a/edify/expr.h b/edify/expr.h
index 8863479..4838d20 100644
--- a/edify/expr.h
+++ b/edify/expr.h
@@ -19,27 +19,26 @@
 
 #include <unistd.h>
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include "error_code.h"
-#include "yydefs.h"
 
-#define MAX_STRING_LEN 1024
+struct State {
+    State(const std::string& script, void* cookie);
 
-typedef struct Expr Expr;
+    // The source of the original script.
+    const std::string& script;
 
-typedef struct {
     // Optional pointer to app-specific data; the core of edify never
     // uses this value.
     void* cookie;
 
-    // The source of the original script.  Must be NULL-terminated,
-    // and in writable memory (Evaluate may make temporary changes to
-    // it but will restore it when done).
-    char* script;
-
     // The error message (if any) returned if the evaluation aborts.
-    // Should be NULL initially, will be either NULL or a malloc'd
-    // pointer after Evaluate() returns.
-    char* errmsg;
+    // Should be empty initially, will be either empty or a string that
+    // Evaluate() returns.
+    std::string errmsg;
 
     // error code indicates the type of failure (e.g. failure to update system image)
     // during the OTA process.
@@ -50,117 +49,95 @@
     CauseCode cause_code = kNoCause;
 
     bool is_retry = false;
-
-} State;
-
-#define VAL_STRING  1  // data will be NULL-terminated; size doesn't count null
-#define VAL_BLOB    2
-
-typedef struct {
-    int type;
-    ssize_t size;
-    char* data;
-} Value;
-
-typedef Value* (*Function)(const char* name, State* state,
-                           int argc, Expr* argv[]);
-
-struct Expr {
-    Function fn;
-    const char* name;
-    int argc;
-    Expr** argv;
-    int start, end;
 };
 
-// Take one of the Expr*s passed to the function as an argument,
-// evaluate it, return the resulting Value.  The caller takes
-// ownership of the returned Value.
-Value* EvaluateValue(State* state, Expr* expr);
+enum ValueType {
+    VAL_INVALID = -1,
+    VAL_STRING = 1,
+    VAL_BLOB = 2,
+};
 
-// Take one of the Expr*s passed to the function as an argument,
-// evaluate it, assert that it is a string, and return the resulting
-// char*.  The caller takes ownership of the returned char*.  This is
-// a convenience function for older functions that want to deal only
-// with strings.
-char* Evaluate(State* state, Expr* expr);
+struct Value {
+    ValueType type;
+    std::string data;
+
+    Value(ValueType type, const std::string& str) :
+        type(type),
+        data(str) {}
+};
+
+struct Expr;
+
+using Function = Value* (*)(const char* name, State* state,
+                            const std::vector<std::unique_ptr<Expr>>& argv);
+
+struct Expr {
+  Function fn;
+  std::string name;
+  std::vector<std::unique_ptr<Expr>> argv;
+  int start, end;
+
+  Expr(Function fn, const std::string& name, int start, int end) :
+    fn(fn),
+    name(name),
+    start(start),
+    end(end) {}
+};
+
+// Evaluate the input expr, return the resulting Value.
+Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr);
+
+// Evaluate the input expr, assert that it is a string, and update the result parameter. This
+// function returns true if the evaluation succeeds. This is a convenience function for older
+// functions that want to deal only with strings.
+bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result);
 
 // Glue to make an Expr out of a literal.
-Value* Literal(const char* name, State* state, int argc, Expr* argv[]);
+Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
 
 // Functions corresponding to various syntactic sugar operators.
 // ("concat" is also available as a builtin function, to concatenate
 // more than two strings.)
-Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* SubstringFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]);
-
-// Convenience function for building expressions with a fixed number
-// of arguments.
-Expr* Build(Function fn, YYLTYPE loc, int count, ...);
+Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* LogicalAndFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* LogicalOrFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* LogicalNotFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* SubstringFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* InequalityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
 
 // Global builtins, registered by RegisterBuiltins().
-Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]);
-
-
-// For setting and getting the global error string (when returning
-// NULL from a function).
-void SetError(const char* message);  // makes a copy
-const char* GetError();              // retains ownership
-void ClearError();
-
-
-typedef struct {
-  const char* name;
-  Function fn;
-} NamedFunction;
+Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
 
 // Register a new function.  The same Function may be registered under
 // multiple names, but a given name should only be used once.
-void RegisterFunction(const char* name, Function fn);
+void RegisterFunction(const std::string& name, Function fn);
 
 // Register all the builtins.
 void RegisterBuiltins();
 
-// Call this after all calls to RegisterFunction() but before parsing
-// any scripts to finish building the function table.
-void FinishRegistration();
-
 // Find the Function for a given name; return NULL if no such function
 // exists.
-Function FindFunction(const char* name);
-
+Function FindFunction(const std::string& name);
 
 // --- convenience functions for use in functions ---
 
-// Evaluate the expressions in argv, giving 'count' char* (the ... is
-// zero or more char** to put them in).  If any expression evaluates
-// to NULL, free the rest and return -1.  Return 0 on success.
-int ReadArgs(State* state, Expr* argv[], int count, ...);
+// Evaluate the expressions in argv, and put the results of strings in args. If any expression
+// evaluates to nullptr, return false. Return true on success.
+bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+              std::vector<std::string>* args);
+bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+              std::vector<std::string>* args, size_t start, size_t len);
 
-// Evaluate the expressions in argv, giving 'count' Value* (the ... is
-// zero or more Value** to put them in).  If any expression evaluates
-// to NULL, free the rest and return -1.  Return 0 on success.
-int ReadValueArgs(State* state, Expr* argv[], int count, ...);
-
-// Evaluate the expressions in argv, returning an array of char*
-// results.  If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// strings it contains.
-char** ReadVarArgs(State* state, int argc, Expr* argv[]);
-
-// Evaluate the expressions in argv, returning an array of Value*
-// results.  If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// Values it contains.
-Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]);
+// Evaluate the expressions in argv, and put the results of Value* in args. If any
+// expression evaluate to nullptr, return false. Return true on success.
+bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+                   std::vector<std::unique_ptr<Value>>* args);
+bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+                   std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len);
 
 // Use printf-style arguments to compose an error message to put into
 // *state.  Returns NULL.
@@ -172,12 +149,11 @@
 Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...)
     __attribute__((format(printf, 3, 4)));
 
-// Wrap a string into a Value, taking ownership of the string.
-Value* StringValue(char* str);
+// Copying the string into a Value.
+Value* StringValue(const char* str);
 
-// Free a Value object.
-void FreeValue(Value* v);
+Value* StringValue(const std::string& str);
 
-int parse_string(const char* str, Expr** root, int* error_count);
+int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count);
 
 #endif  // _EXPRESSION_H
diff --git a/edify/main.cpp b/edify/main.cpp
deleted file mode 100644
index 6007a3d..0000000
--- a/edify/main.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2009 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-
-#include "expr.h"
-#include "parser.h"
-
-extern int yyparse(Expr** root, int* error_count);
-
-int expect(const char* expr_str, const char* expected, int* errors) {
-    Expr* e;
-    char* result;
-
-    printf(".");
-
-    int error_count = parse_string(expr_str, &e, &error_count);
-    if (error_count > 0) {
-        printf("error parsing \"%s\" (%d errors)\n",
-               expr_str, error_count);
-        ++*errors;
-        return 0;
-    }
-
-    State state;
-    state.cookie = NULL;
-    state.script = strdup(expr_str);
-    state.errmsg = NULL;
-
-    result = Evaluate(&state, e);
-    free(state.errmsg);
-    free(state.script);
-    if (result == NULL && expected != NULL) {
-        printf("error evaluating \"%s\"\n", expr_str);
-        ++*errors;
-        return 0;
-    }
-
-    if (result == NULL && expected == NULL) {
-        return 1;
-    }
-
-    if (strcmp(result, expected) != 0) {
-        printf("evaluating \"%s\": expected \"%s\", got \"%s\"\n",
-               expr_str, expected, result);
-        ++*errors;
-        free(result);
-        return 0;
-    }
-
-    free(result);
-    return 1;
-}
-
-int test() {
-    int errors = 0;
-
-    expect("a", "a", &errors);
-    expect("\"a\"", "a", &errors);
-    expect("\"\\x61\"", "a", &errors);
-    expect("# this is a comment\n"
-           "  a\n"
-           "   \n",
-           "a", &errors);
-
-
-    // sequence operator
-    expect("a; b; c", "c", &errors);
-
-    // string concat operator
-    expect("a + b", "ab", &errors);
-    expect("a + \n \"b\"", "ab", &errors);
-    expect("a + b +\nc\n", "abc", &errors);
-
-    // string concat function
-    expect("concat(a, b)", "ab", &errors);
-    expect("concat(a,\n \"b\")", "ab", &errors);
-    expect("concat(a + b,\nc,\"d\")", "abcd", &errors);
-    expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors);
-
-    // logical and
-    expect("a && b", "b", &errors);
-    expect("a && \"\"", "", &errors);
-    expect("\"\" && b", "", &errors);
-    expect("\"\" && \"\"", "", &errors);
-    expect("\"\" && abort()", "", &errors);   // test short-circuiting
-    expect("t && abort()", NULL, &errors);
-
-    // logical or
-    expect("a || b", "a", &errors);
-    expect("a || \"\"", "a", &errors);
-    expect("\"\" || b", "b", &errors);
-    expect("\"\" || \"\"", "", &errors);
-    expect("a || abort()", "a", &errors);     // test short-circuiting
-    expect("\"\" || abort()", NULL, &errors);
-
-    // logical not
-    expect("!a", "", &errors);
-    expect("! \"\"", "t", &errors);
-    expect("!!a", "t", &errors);
-
-    // precedence
-    expect("\"\" == \"\" && b", "b", &errors);
-    expect("a + b == ab", "t", &errors);
-    expect("ab == a + b", "t", &errors);
-    expect("a + (b == ab)", "a", &errors);
-    expect("(ab == a) + b", "b", &errors);
-
-    // substring function
-    expect("is_substring(cad, abracadabra)", "t", &errors);
-    expect("is_substring(abrac, abracadabra)", "t", &errors);
-    expect("is_substring(dabra, abracadabra)", "t", &errors);
-    expect("is_substring(cad, abracxadabra)", "", &errors);
-    expect("is_substring(abrac, axbracadabra)", "", &errors);
-    expect("is_substring(dabra, abracadabrxa)", "", &errors);
-
-    // ifelse function
-    expect("ifelse(t, yes, no)", "yes", &errors);
-    expect("ifelse(!t, yes, no)", "no", &errors);
-    expect("ifelse(t, yes, abort())", "yes", &errors);
-    expect("ifelse(!t, abort(), no)", "no", &errors);
-
-    // if "statements"
-    expect("if t then yes else no endif", "yes", &errors);
-    expect("if \"\" then yes else no endif", "no", &errors);
-    expect("if \"\" then yes endif", "", &errors);
-    expect("if \"\"; t then yes endif", "yes", &errors);
-
-    // numeric comparisons
-    expect("less_than_int(3, 14)", "t", &errors);
-    expect("less_than_int(14, 3)", "", &errors);
-    expect("less_than_int(x, 3)", "", &errors);
-    expect("less_than_int(3, x)", "", &errors);
-    expect("greater_than_int(3, 14)", "", &errors);
-    expect("greater_than_int(14, 3)", "t", &errors);
-    expect("greater_than_int(x, 3)", "", &errors);
-    expect("greater_than_int(3, x)", "", &errors);
-
-    // big string
-    expect(std::string(8192, 's').c_str(), std::string(8192, 's').c_str(), &errors);
-
-    printf("\n");
-
-    return errors;
-}
-
-void ExprDump(int depth, Expr* n, char* script) {
-    printf("%*s", depth*2, "");
-    char temp = script[n->end];
-    script[n->end] = '\0';
-    printf("%s %p (%d-%d) \"%s\"\n",
-           n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end,
-           script+n->start);
-    script[n->end] = temp;
-    int i;
-    for (i = 0; i < n->argc; ++i) {
-        ExprDump(depth+1, n->argv[i], script);
-    }
-}
-
-int main(int argc, char** argv) {
-    RegisterBuiltins();
-    FinishRegistration();
-
-    if (argc == 1) {
-        return test() != 0;
-    }
-
-    FILE* f = fopen(argv[1], "r");
-    if (f == NULL) {
-        printf("%s: %s: No such file or directory\n", argv[0], argv[1]);
-        return 1;
-    }
-    char buffer[8192];
-    int size = fread(buffer, 1, 8191, f);
-    fclose(f);
-    buffer[size] = '\0';
-
-    Expr* root;
-    int error_count = 0;
-    int error = parse_string(buffer, &root, &error_count);
-    printf("parse returned %d; %d errors encountered\n", error, error_count);
-    if (error == 0 || error_count > 0) {
-
-        ExprDump(0, root, buffer);
-
-        State state;
-        state.cookie = NULL;
-        state.script = buffer;
-        state.errmsg = NULL;
-
-        char* result = Evaluate(&state, root);
-        if (result == NULL) {
-            printf("result was NULL, message is: %s\n",
-                   (state.errmsg == NULL ? "(NULL)" : state.errmsg));
-            free(state.errmsg);
-        } else {
-            printf("result is [%s]\n", result);
-        }
-    }
-    return 0;
-}
diff --git a/edify/parser.yy b/edify/parser.yy
index 098a637..97205fe 100644
--- a/edify/parser.yy
+++ b/edify/parser.yy
@@ -19,6 +19,10 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include "expr.h"
 #include "yydefs.h"
 #include "parser.h"
@@ -26,13 +30,26 @@
 extern int gLine;
 extern int gColumn;
 
-void yyerror(Expr** root, int* error_count, const char* s);
-int yyparse(Expr** root, int* error_count);
+void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s);
+int yyparse(std::unique_ptr<Expr>* root, int* error_count);
 
 struct yy_buffer_state;
 void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
 struct yy_buffer_state* yy_scan_string(const char* yystr);
 
+// Convenience function for building expressions with a fixed number
+// of arguments.
+static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {
+    va_list v;
+    va_start(v, count);
+    Expr* e = new Expr(fn, "(operator)", loc.start, loc.end);
+    for (size_t i = 0; i < count; ++i) {
+        e->argv.emplace_back(va_arg(v, Expr*));
+    }
+    va_end(v);
+    return e;
+}
+
 %}
 
 %locations
@@ -40,10 +57,7 @@
 %union {
     char* str;
     Expr* expr;
-    struct {
-        int argc;
-        Expr** argv;
-    } args;
+    std::vector<std::unique_ptr<Expr>>* args;
 }
 
 %token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF
@@ -51,7 +65,10 @@
 %type <expr> expr
 %type <args> arglist
 
-%parse-param {Expr** root}
+%destructor { delete $$; } expr
+%destructor { delete $$; } arglist
+
+%parse-param {std::unique_ptr<Expr>* root}
 %parse-param {int* error_count}
 %error-verbose
 
@@ -66,17 +83,11 @@
 
 %%
 
-input:  expr           { *root = $1; }
+input:  expr           { root->reset($1); }
 ;
 
 expr:  STRING {
-    $$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
-    $$->fn = Literal;
-    $$->name = $1;
-    $$->argc = 0;
-    $$->argv = NULL;
-    $$->start = @$.start;
-    $$->end = @$.end;
+    $$ = new Expr(Literal, $1, @$.start, @$.end);
 }
 |  '(' expr ')'                      { $$ = $2; $$->start=@$.start; $$->end=@$.end; }
 |  expr ';'                          { $$ = $1; $$->start=@1.start; $$->end=@1.end; }
@@ -91,41 +102,32 @@
 |  IF expr THEN expr ENDIF           { $$ = Build(IfElseFn, @$, 2, $2, $4); }
 |  IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); }
 | STRING '(' arglist ')' {
-    $$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
-    $$->fn = FindFunction($1);
-    if ($$->fn == NULL) {
-        char buffer[256];
-        snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
-        yyerror(root, error_count, buffer);
+    Function fn = FindFunction($1);
+    if (fn == nullptr) {
+        std::string msg = "unknown function \"" + std::string($1) + "\"";
+        yyerror(root, error_count, msg.c_str());
         YYERROR;
     }
-    $$->name = $1;
-    $$->argc = $3.argc;
-    $$->argv = $3.argv;
-    $$->start = @$.start;
-    $$->end = @$.end;
+    $$ = new Expr(fn, $1, @$.start, @$.end);
+    $$->argv = std::move(*$3);
 }
 ;
 
 arglist:    /* empty */ {
-    $$.argc = 0;
-    $$.argv = NULL;
+    $$ = new std::vector<std::unique_ptr<Expr>>;
 }
 | expr {
-    $$.argc = 1;
-    $$.argv = reinterpret_cast<Expr**>(malloc(sizeof(Expr*)));
-    $$.argv[0] = $1;
+    $$ = new std::vector<std::unique_ptr<Expr>>;
+    $$->emplace_back($1);
 }
 | arglist ',' expr {
-    $$.argc = $1.argc + 1;
-    $$.argv = reinterpret_cast<Expr**>(realloc($$.argv, $$.argc * sizeof(Expr*)));
-    $$.argv[$$.argc-1] = $3;
+    $$->push_back(std::unique_ptr<Expr>($3));
 }
 ;
 
 %%
 
-void yyerror(Expr** root, int* error_count, const char* s) {
+void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s) {
   if (strlen(s) == 0) {
     s = "syntax error";
   }
@@ -133,7 +135,7 @@
   ++*error_count;
 }
 
-int parse_string(const char* str, Expr** root, int* error_count) {
+int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count) {
     yy_switch_to_buffer(yy_scan_string(str));
     return yyparse(root, error_count);
 }
diff --git a/error_code.h b/error_code.h
index dfea0eb..cde4ee6 100644
--- a/error_code.h
+++ b/error_code.h
@@ -18,52 +18,54 @@
 #define _ERROR_CODE_H_
 
 enum ErrorCode {
-    kNoError = -1,
-    kLowBattery = 20,
-    kZipVerificationFailure,
-    kZipOpenFailure,
-    kBootreasonInBlacklist
+  kNoError = -1,
+  kLowBattery = 20,
+  kZipVerificationFailure,
+  kZipOpenFailure,
+  kBootreasonInBlacklist,
+  kPackageCompatibilityFailure,
 };
 
 enum CauseCode {
-    kNoCause = -1,
-    kArgsParsingFailure = 100,
-    kStashCreationFailure,
-    kFileOpenFailure,
-    kLseekFailure,
-    kFreadFailure,
-    kFwriteFailure,
-    kFsyncFailure,
-    kLibfecFailure,
-    kFileGetPropFailure,
-    kFileRenameFailure,
-    kSymlinkFailure,
-    kSetMetadataFailure,
-    kTune2FsFailure,
-    kRebootFailure,
-    kVendorFailure = 200
+  kNoCause = -1,
+  kArgsParsingFailure = 100,
+  kStashCreationFailure,
+  kFileOpenFailure,
+  kLseekFailure,
+  kFreadFailure,
+  kFwriteFailure,
+  kFsyncFailure,
+  kLibfecFailure,
+  kFileGetPropFailure,
+  kFileRenameFailure,
+  kSymlinkFailure,
+  kSetMetadataFailure,
+  kTune2FsFailure,
+  kRebootFailure,
+  kPackageExtractFileFailure,
+  kVendorFailure = 200
 };
 
 enum UncryptErrorCode {
-    kUncryptNoError = -1,
-    kUncryptErrorPlaceholder = 50,
-    kUncryptTimeoutError = 100,
-    kUncryptFileRemoveError,
-    kUncryptFileOpenError,
-    kUncryptSocketOpenError,
-    kUncryptSocketWriteError,
-    kUncryptSocketListenError,
-    kUncryptSocketAcceptError,
-    kUncryptFstabReadError,
-    kUncryptFileStatError,
-    kUncryptBlockOpenError,
-    kUncryptIoctlError,
-    kUncryptReadError,
-    kUncryptWriteError,
-    kUncryptFileSyncError,
-    kUncryptFileCloseError,
-    kUncryptFileRenameError,
-    kUncryptPackageMissingError,
+  kUncryptNoError = -1,
+  kUncryptErrorPlaceholder = 50,
+  kUncryptTimeoutError = 100,
+  kUncryptFileRemoveError,
+  kUncryptFileOpenError,
+  kUncryptSocketOpenError,
+  kUncryptSocketWriteError,
+  kUncryptSocketListenError,
+  kUncryptSocketAcceptError,
+  kUncryptFstabReadError,
+  kUncryptFileStatError,
+  kUncryptBlockOpenError,
+  kUncryptIoctlError,
+  kUncryptReadError,
+  kUncryptWriteError,
+  kUncryptFileSyncError,
+  kUncryptFileCloseError,
+  kUncryptFileRenameError,
+  kUncryptPackageMissingError,
 };
 
-#endif
+#endif // _ERROR_CODE_H_
diff --git a/etc/Android.mk b/etc/Android.mk
index decd3e2..cda0f37 100644
--- a/etc/Android.mk
+++ b/etc/Android.mk
@@ -51,6 +51,26 @@
     include $(BUILD_PREBUILT)
 endif
 
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+    include $(CLEAR_VARS)
+    LOCAL_MODULE := init.recovery.hlthchrg.rc
+    LOCAL_MODULE_TAGS := eng
+    LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+    LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+    LOCAL_SRC_FILES := init.recovery.hlthchrg26.rc
+    include $(BUILD_PREBUILT)
+else
+    include $(CLEAR_VARS)
+    LOCAL_MODULE := init.recovery.hlthchrg.rc
+    LOCAL_MODULE_TAGS := eng
+    LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+    LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+    LOCAL_SRC_FILES := init.recovery.hlthchrg25.rc
+    include $(BUILD_PREBUILT)
+endif
+
 ifeq ($(TWRP_INCLUDE_LOGCAT), true)
     ifeq ($(TARGET_USES_LOGD), true)
 
diff --git a/etc/META-INF/com/google/android/update-script b/etc/META-INF/com/google/android/update-script
deleted file mode 100644
index b091b19..0000000
--- a/etc/META-INF/com/google/android/update-script
+++ /dev/null
@@ -1,8 +0,0 @@
-assert compatible_with("0.1") == "true"
-assert file_contains("SYSTEM:build.prop", "ro.product.device=dream") == "true" || file_contains("SYSTEM:build.prop", "ro.build.product=dream") == "true"
-assert file_contains("RECOVERY:default.prop", "ro.product.device=dream") == "true" || file_contains("RECOVERY:default.prop", "ro.build.product=dream") == "true"
-assert getprop("ro.product.device") == "dream"
-format BOOT:
-format SYSTEM:
-copy_dir PACKAGE:system SYSTEM:
-write_raw_image PACKAGE:boot.img BOOT:
diff --git a/etc/init.rc b/etc/init.rc
index 3523181..d1c4526 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -16,7 +16,6 @@
     restorecon /postinstall
 
     start ueventd
-    start healthd
 
 service set_permissive /sbin/permissive.sh
     oneshot
@@ -46,7 +45,6 @@
 
 on fs
     mount pstore pstore /sys/fs/pstore
-
     mkdir /dev/usb-ffs 0770 shell shell
     mkdir /dev/usb-ffs/adb 0770 shell shell
     mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000
@@ -104,10 +102,6 @@
     critical
     seclabel u:r:ueventd:s0
 
-service healthd /sbin/healthd -r
-    critical
-    seclabel u:r:healthd:s0
-
 service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
     disabled
     socket adbd stream 660 system system
diff --git a/etc/init.recovery.hlthchrg25.rc b/etc/init.recovery.hlthchrg25.rc
new file mode 100644
index 0000000..62b2489
--- /dev/null
+++ b/etc/init.recovery.hlthchrg25.rc
@@ -0,0 +1,5 @@
+# healthd for pre Android 8.0
+
+service healthd /sbin/healthd -r
+    critical
+    seclabel u:r:healthd:s0
diff --git a/etc/init.recovery.hlthchrg26.rc b/etc/init.recovery.hlthchrg26.rc
new file mode 100644
index 0000000..8a49c86
--- /dev/null
+++ b/etc/init.recovery.hlthchrg26.rc
@@ -0,0 +1,5 @@
+# charger for Android 8.0 and up
+
+service charger /charger -r
+    critical
+    seclabel u:r:charger:s0
diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp
index df96312..b0ecf96 100644
--- a/fuse_sdcard_provider.cpp
+++ b/fuse_sdcard_provider.cpp
@@ -23,6 +23,8 @@
 #include <unistd.h>
 #include <fcntl.h>
 
+#include <android-base/file.h>
+
 #include "fuse_sideload.h"
 
 struct file_data {
@@ -41,14 +43,9 @@
         return -EIO;
     }
 
-    while (fetch_size > 0) {
-        ssize_t r = TEMP_FAILURE_RETRY(read(fd->fd, buffer, fetch_size));
-        if (r == -1) {
-            fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
-            return -EIO;
-        }
-        fetch_size -= r;
-        buffer += r;
+    if (!android::base::ReadFully(fd->fd, buffer, fetch_size)) {
+        fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
+        return -EIO;
     }
 
     return 0;
diff --git a/gui/Android.mk b/gui/Android.mk
index 78e93c6..909850f 100644
--- a/gui/Android.mk
+++ b/gui/Android.mk
@@ -38,7 +38,13 @@
     LOCAL_SRC_FILES += hardwarekeyboard.cpp
 endif
 
-LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++ libminzip libaosprecovery libselinux
+LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++ libaosprecovery libselinux
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+    LOCAL_SHARED_LIBRARIES += libziparchive
+else
+    LOCAL_SHARED_LIBRARIES += libminzip
+    LOCAL_CFLAGS += -DUSE_MINZIP
+endif
 LOCAL_MODULE := libguitwrp
 
 #TWRP_EVENT_LOGGING := true
diff --git a/gui/action.cpp b/gui/action.cpp
index a395158..05ae465 100644
--- a/gui/action.cpp
+++ b/gui/action.cpp
@@ -43,11 +43,11 @@
 #include "../adb_install.h"
 #include "../fuse_sideload.h"
 #include "blanktimer.hpp"
+#include "../twinstall.h"
 
 extern "C" {
 #include "../twcommon.h"
 #include "../variables.h"
-#include "../twinstall.h"
 #include "cutils/properties.h"
 #include "../adb_install.h"
 };
diff --git a/gui/pages.cpp b/gui/pages.cpp
index b901526..1199a28 100644
--- a/gui/pages.cpp
+++ b/gui/pages.cpp
@@ -39,12 +39,17 @@
 #include <string>
 #include <algorithm>
 
+#ifdef USE_MINZIP
+#include "../minzip/SysUtil.h"
+#else
+#include "../otautil/SysUtil.h"
+#endif
+
 extern "C" {
 #include "../twcommon.h"
-#include "../minzip/SysUtil.h"
-#include "../minzip/Zip.h"
 #include "gui.h"
 }
+#include "../zipwrap.hpp"
 #include "../minuitwrp/minui.h"
 
 #include "rapidxml.hpp"
@@ -664,7 +669,7 @@
 // transient data for loading themes
 struct LoadingContext
 {
-	ZipArchive* zip; // zip to load theme from, or NULL for the stock theme
+	ZipWrap* zip; // zip to load theme from, or NULL for the stock theme
 	std::set<std::string> filenames; // to detect cyclic includes
 	std::string basepath; // if zip is NULL, base path to load includes from with trailing slash, otherwise empty
 	std::vector<xml_document<>*> xmldocs; // all loaded xml docs
@@ -806,7 +811,7 @@
 	}
 }
 
-int PageSet::LoadLanguage(char* languageFile, ZipArchive* package)
+int PageSet::LoadLanguage(char* languageFile, ZipWrap* package)
 {
 	xml_document<> lang;
 	xml_node<>* parent;
@@ -1182,7 +1187,7 @@
 	mResources->AddStringResource(resource_source, resource_name, value);
 }
 
-char* PageManager::LoadFileToBuffer(std::string filename, ZipArchive* package) {
+char* PageManager::LoadFileToBuffer(std::string filename, ZipWrap* package) {
 	size_t len;
 	char* buffer = NULL;
 
@@ -1219,19 +1224,18 @@
 		close(fd);
 	} else {
 		LOGINFO("PageManager::LoadFileToBuffer loading filename: '%s' from zip\n", filename.c_str());
-		const ZipEntry* zipentry = mzFindZipEntry(package, filename.c_str());
-		if (zipentry == NULL) {
+		if (!package->EntryExists(filename)) {
 			LOGERR("Unable to locate '%s' in zip file\n", filename.c_str());
 			return NULL;
 		}
 
 		// Allocate the buffer for the file
-		len = mzGetZipEntryUncompLen(zipentry);
+		len = package->GetUncompressedSize(filename);
 		buffer = (char*) malloc(len + 1);
 		if (!buffer)
 			return NULL;
 
-		if (!mzExtractZipEntryToBuffer(package, zipentry, (unsigned char*) buffer)) {
+		if (!package->ExtractToBuffer(filename, (unsigned char*) buffer)) {
 			LOGERR("Unable to extract '%s'\n", filename.c_str());
 			free(buffer);
 			return NULL;
@@ -1295,14 +1299,13 @@
 	closedir(d);
 }
 
-void PageManager::LoadLanguageList(ZipArchive* package) {
+void PageManager::LoadLanguageList(ZipWrap* package) {
 	Language_List.clear();
 	if (TWFunc::Path_Exists(TWRES "customlanguages"))
 		TWFunc::removeDir(TWRES "customlanguages", true);
 	if (package) {
 		TWFunc::Recursive_Mkdir(TWRES "customlanguages");
-		struct utimbuf timestamp = { 1217592000, 1217592000 };  // 8/1/2008 default
-		mzExtractRecursive(package, "languages", TWRES "customlanguages/", &timestamp, NULL, NULL, NULL);
+		package->ExtractRecursive("languages", TWRES "customlanguages/");
 		LoadLanguageListDir(TWRES "customlanguages/");
 	} else {
 		LoadLanguageListDir(TWRES "languages/");
@@ -1330,7 +1333,7 @@
 int PageManager::LoadPackage(std::string name, std::string package, std::string startpage)
 {
 	std::string mainxmlfilename = package;
-	ZipArchive zip;
+	ZipWrap zip;
 	char* languageFile = NULL;
 	char* baseLanguageFile = NULL;
 	PageSet* pageSet = NULL;
@@ -1371,7 +1374,7 @@
 			LOGERR("Failed to map '%s'\n", package.c_str());
 			goto error;
 		}
-		if (mzOpenZipArchive(map.addr, map.length, &zip)) {
+		if (!zip.Open(package.c_str(), &map)) {
 			LOGERR("Unable to open zip archive '%s'\n", package.c_str());
 			sysReleaseMap(&map);
 			goto error;
@@ -1414,7 +1417,7 @@
 	mCurrentSet = pageSet;
 
 	if (ctx.zip) {
-		mzCloseZipArchive(ctx.zip);
+		ctx.zip->Close();
 		sysReleaseMap(&map);
 	}
 	return ret;
@@ -1422,7 +1425,7 @@
 error:
 	// Sometimes we get here without a real error
 	if (ctx.zip) {
-		mzCloseZipArchive(ctx.zip);
+		ctx.zip->Close();
 		sysReleaseMap(&map);
 	}
 	return -1;
diff --git a/gui/pages.hpp b/gui/pages.hpp
index 1e71b10..282b2d5 100644
--- a/gui/pages.hpp
+++ b/gui/pages.hpp
@@ -21,7 +21,7 @@
 #ifndef _PAGES_HEADER_HPP
 #define _PAGES_HEADER_HPP
 
-#include "../minzip/Zip.h"
+#include "../zipwrap.hpp"
 #include <vector>
 #include <map>
 #include <string>
@@ -116,7 +116,7 @@
 
 public:
 	int Load(LoadingContext& ctx, const std::string& filename);
-	int LoadLanguage(char* languageFile, ZipArchive* package);
+	int LoadLanguage(char* languageFile, ZipWrap* package);
 	void MakeEmergencyConsoleIfNeeded();
 
 	Page* FindPage(std::string name);
@@ -155,8 +155,8 @@
 {
 public:
 	// Used by GUI
-	static char* LoadFileToBuffer(std::string filename, ZipArchive* package);
-	static void LoadLanguageList(ZipArchive* package);
+	static char* LoadFileToBuffer(std::string filename, ZipWrap* package);
+	static void LoadLanguageList(ZipWrap* package);
 	static void LoadLanguage(std::string filename);
 	static int LoadPackage(std::string name, std::string package, std::string startpage);
 	static PageSet* SelectPackage(std::string name);
diff --git a/gui/resources.cpp b/gui/resources.cpp
index bd71d50..59b8ed4 100644
--- a/gui/resources.cpp
+++ b/gui/resources.cpp
@@ -30,7 +30,7 @@
 #include <iomanip>
 #include <fcntl.h>
 
-#include "../minzip/Zip.h"
+#include "../zipwrap.hpp"
 extern "C" {
 #include "../twcommon.h"
 #include "gui.h"
@@ -42,38 +42,24 @@
 
 #define TMP_RESOURCE_NAME   "/tmp/extract.bin"
 
-Resource::Resource(xml_node<>* node, ZipArchive* pZip __unused)
+Resource::Resource(xml_node<>* node, ZipWrap* pZip __unused)
 {
 	if (node && node->first_attribute("name"))
 		mName = node->first_attribute("name")->value();
 }
 
-int Resource::ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile)
+int Resource::ExtractResource(ZipWrap* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile)
 {
 	if (!pZip)
 		return -1;
 
 	std::string src = folderName + "/" + fileName + fileExtn;
-
-	const ZipEntry* binary = mzFindZipEntry(pZip, src.c_str());
-	if (binary == NULL) {
+	if (!pZip->ExtractEntry(src, destFile, 0666))
 		return -1;
-	}
-
-	unlink(destFile.c_str());
-	int fd = creat(destFile.c_str(), 0666);
-	if (fd < 0)
-		return -1;
-
-	int ret = 0;
-	if (!mzExtractZipEntryToFile(pZip, binary, fd))
-		ret = -1;
-
-	close(fd);
-	return ret;
+	return 0;
 }
 
-void Resource::LoadImage(ZipArchive* pZip, std::string file, gr_surface* surface)
+void Resource::LoadImage(ZipWrap* pZip, std::string file, gr_surface* surface)
 {
 	int rc = 0;
 	if (ExtractResource(pZip, "images", file, ".png", TMP_RESOURCE_NAME) == 0)
@@ -119,7 +105,7 @@
 	}
 }
 
-FontResource::FontResource(xml_node<>* node, ZipArchive* pZip)
+FontResource::FontResource(xml_node<>* node, ZipWrap* pZip)
  : Resource(node, pZip)
 {
 	origFontSize = 0;
@@ -127,7 +113,7 @@
 	LoadFont(node, pZip);
 }
 
-void FontResource::LoadFont(xml_node<>* node, ZipArchive* pZip)
+void FontResource::LoadFont(xml_node<>* node, ZipWrap* pZip)
 {
 	std::string file;
 	xml_attribute<>* attr;
@@ -192,7 +178,7 @@
 	origFont = NULL;
 }
 
-void FontResource::Override(xml_node<>* node, ZipArchive* pZip) {
+void FontResource::Override(xml_node<>* node, ZipWrap* pZip) {
 	if (!origFont) {
 		origFont = mFont;
 	} else if (mFont) {
@@ -207,7 +193,7 @@
 	DeleteFont();
 }
 
-ImageResource::ImageResource(xml_node<>* node, ZipArchive* pZip)
+ImageResource::ImageResource(xml_node<>* node, ZipWrap* pZip)
  : Resource(node, pZip)
 {
 	std::string file;
@@ -238,7 +224,7 @@
 		res_free_surface(mSurface);
 }
 
-AnimationResource::AnimationResource(xml_node<>* node, ZipArchive* pZip)
+AnimationResource::AnimationResource(xml_node<>* node, ZipWrap* pZip)
  : Resource(node, pZip)
 {
 	std::string file;
@@ -359,7 +345,7 @@
 	mStrings[resource_name] = res;
 }
 
-void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip, std::string resource_source)
+void ResourceManager::LoadResources(xml_node<>* resList, ZipWrap* pZip, std::string resource_source)
 {
 	if (!resList)
 		return;
diff --git a/gui/resources.hpp b/gui/resources.hpp
index 2d30676..de67318 100644
--- a/gui/resources.hpp
+++ b/gui/resources.hpp
@@ -25,8 +25,7 @@
 #include <vector>
 #include <map>
 #include "rapidxml.hpp"
-
-struct ZipArchive;
+#include "../zipwrap.hpp"
 
 extern "C" {
 #include "../minuitwrp/minui.h"
@@ -36,7 +35,7 @@
 class Resource
 {
 public:
-	Resource(xml_node<>* node, ZipArchive* pZip);
+	Resource(xml_node<>* node, ZipWrap* pZip);
 	virtual ~Resource() {}
 
 public:
@@ -46,27 +45,27 @@
 	std::string mName;
 
 protected:
-	static int ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile);
-	static void LoadImage(ZipArchive* pZip, std::string file, gr_surface* surface);
+	static int ExtractResource(ZipWrap* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile);
+	static void LoadImage(ZipWrap* pZip, std::string file, gr_surface* surface);
 	static void CheckAndScaleImage(gr_surface source, gr_surface* destination, int retain_aspect);
 };
 
 class FontResource : public Resource
 {
 public:
-	FontResource(xml_node<>* node, ZipArchive* pZip);
+	FontResource(xml_node<>* node, ZipWrap* pZip);
 	virtual ~FontResource();
 
 public:
 	void* GetResource() { return this ? mFont : NULL; }
 	int GetHeight() { return gr_ttf_getMaxFontHeight(this ? mFont : NULL); }
-	void Override(xml_node<>* node, ZipArchive* pZip);
+	void Override(xml_node<>* node, ZipWrap* pZip);
 
 protected:
 	void* mFont;
 
 private:
-	void LoadFont(xml_node<>* node, ZipArchive* pZip);
+	void LoadFont(xml_node<>* node, ZipWrap* pZip);
 	void DeleteFont();
 
 private:
@@ -77,7 +76,7 @@
 class ImageResource : public Resource
 {
 public:
-	ImageResource(xml_node<>* node, ZipArchive* pZip);
+	ImageResource(xml_node<>* node, ZipWrap* pZip);
 	virtual ~ImageResource();
 
 public:
@@ -92,7 +91,7 @@
 class AnimationResource : public Resource
 {
 public:
-	AnimationResource(xml_node<>* node, ZipArchive* pZip);
+	AnimationResource(xml_node<>* node, ZipWrap* pZip);
 	virtual ~AnimationResource();
 
 public:
@@ -112,7 +111,7 @@
 	ResourceManager();
 	virtual ~ResourceManager();
 	void AddStringResource(std::string resource_source, std::string resource_name, std::string value);
-	void LoadResources(xml_node<>* resList, ZipArchive* pZip, std::string resource_source);
+	void LoadResources(xml_node<>* resList, ZipWrap* pZip, std::string resource_source);
 
 public:
 	FontResource* FindFont(const std::string& name) const;
diff --git a/install.cpp b/install.cpp
index e144d9b..ffeba2e 100644
--- a/install.cpp
+++ b/install.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "install.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -24,48 +26,53 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <chrono>
+#include <condition_variable>
+#include <functional>
 #include <limits>
 #include <map>
+#include <mutex>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
 #include <android-base/parseint.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <cutils/properties.h>
+#include <vintf/VintfObjectRecovery.h>
+#include <ziparchive/zip_archive.h>
 
 #include "common.h"
 #include "error_code.h"
-#include "install.h"
 #include "minui/minui.h"
-#include "minzip/SysUtil.h"
-#include "minzip/Zip.h"
-#include "mtdutils/mounts.h"
-#include "mtdutils/mtdutils.h"
+#include "otautil/SysUtil.h"
+#include "otautil/ThermalUtil.h"
 #include "roots.h"
 #include "ui.h"
 #include "verifier.h"
 
-extern RecoveryUI* ui;
+using namespace std::chrono_literals;
 
-#define ASSUMED_UPDATE_BINARY_NAME  "META-INF/com/google/android/update-binary"
-static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt";
-static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
 #define PUBLIC_KEYS_FILE "/res/keys"
 static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
 static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
 
 // Default allocation of progress bar segments to operations
-static const int VERIFICATION_PROGRESS_TIME = 60;
-static const float VERIFICATION_PROGRESS_FRACTION = 0.25;
-static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
-static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
+static constexpr int VERIFICATION_PROGRESS_TIME = 60;
+static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
+static constexpr float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
+static constexpr float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
+
+static std::condition_variable finish_log_temperature;
 
 // This function parses and returns the build.version.incremental
-static int parse_build_number(std::string str) {
-    size_t pos = str.find("=");
+static int parse_build_number(const std::string& str) {
+    size_t pos = str.find('=');
     if (pos != std::string::npos) {
         std::string num_string = android::base::Trim(str.substr(pos+1));
         int build_number;
@@ -74,27 +81,33 @@
         }
     }
 
-    LOGE("Failed to parse build number in %s.\n", str.c_str());
+    LOG(ERROR) << "Failed to parse build number in " << str;
     return -1;
 }
 
-bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data) {
-    const ZipEntry* meta_entry = mzFindZipEntry(zip, METADATA_PATH);
-    if (meta_entry == nullptr) {
-        LOGE("Failed to find %s in update package.\n", METADATA_PATH);
+bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data) {
+    ZipString metadata_path(METADATA_PATH);
+    ZipEntry meta_entry;
+    if (meta_data == nullptr) {
+        LOG(ERROR) << "string* meta_data can't be nullptr";
+        return false;
+    }
+    if (FindEntry(zip, metadata_path, &meta_entry) != 0) {
+        LOG(ERROR) << "Failed to find " << METADATA_PATH << " in update package";
         return false;
     }
 
-    meta_data->resize(meta_entry->uncompLen, '\0');
-    if (!mzReadZipEntry(zip, meta_entry, &(*meta_data)[0], meta_entry->uncompLen)) {
-        LOGE("Failed to read metadata in update package.\n");
+    meta_data->resize(meta_entry.uncompressed_length, '\0');
+    if (ExtractToMemory(zip, &meta_entry, reinterpret_cast<uint8_t*>(&(*meta_data)[0]),
+                        meta_entry.uncompressed_length) != 0) {
+        LOG(ERROR) << "Failed to read metadata in update package";
         return false;
     }
     return true;
 }
 
 // Read the build.version.incremental of src/tgt from the metadata and log it to last_install.
-static void read_source_target_build(ZipArchive* zip, std::vector<std::string>& log_buffer) {
+static void read_source_target_build(ZipArchiveHandle zip, std::vector<std::string>& log_buffer) {
     std::string meta_data;
     if (!read_metadata_from_package(zip, &meta_data)) {
         return;
@@ -121,348 +134,438 @@
     }
 }
 
-// Extract the update binary from the open zip archive |zip| located at |path|
-// and store into |cmd| the command line that should be called. The |status_fd|
-// is the file descriptor the child process should use to report back the
-// progress of the update.
-static int
-update_binary_command(const char* path, ZipArchive* zip, int retry_count,
-                      int status_fd, std::vector<std::string>* cmd);
+// Extract the update binary from the open zip archive |zip| located at |path| and store into |cmd|
+// the command line that should be called. The |status_fd| is the file descriptor the child process
+// should use to report back the progress of the update.
+int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
+                          int status_fd, std::vector<std::string>* cmd);
 
 #ifdef AB_OTA_UPDATER
 
 // Parses the metadata of the OTA package in |zip| and checks whether we are
 // allowed to accept this A/B package. Downgrading is not allowed unless
 // explicitly enabled in the package and only for incremental packages.
-static int check_newer_ab_build(ZipArchive* zip)
-{
-    std::string metadata_str;
-    if (!read_metadata_from_package(zip, &metadata_str)) {
-        return INSTALL_CORRUPT;
+static int check_newer_ab_build(ZipArchiveHandle zip) {
+  std::string metadata_str;
+  if (!read_metadata_from_package(zip, &metadata_str)) {
+    return INSTALL_CORRUPT;
+  }
+  std::map<std::string, std::string> metadata;
+  for (const std::string& line : android::base::Split(metadata_str, "\n")) {
+    size_t eq = line.find('=');
+    if (eq != std::string::npos) {
+      metadata[line.substr(0, eq)] = line.substr(eq + 1);
     }
-    std::map<std::string, std::string> metadata;
-    for (const std::string& line : android::base::Split(metadata_str, "\n")) {
-        size_t eq = line.find('=');
-        if (eq != std::string::npos) {
-            metadata[line.substr(0, eq)] = line.substr(eq + 1);
-        }
-    }
-    char value[PROPERTY_VALUE_MAX];
+  }
 
-    property_get("ro.product.device", value, "");
-    const std::string& pkg_device = metadata["pre-device"];
-    if (pkg_device != value || pkg_device.empty()) {
-        LOGE("Package is for product %s but expected %s\n",
-             pkg_device.c_str(), value);
-        return INSTALL_ERROR;
-    }
+  std::string value = android::base::GetProperty("ro.product.device", "");
+  const std::string& pkg_device = metadata["pre-device"];
+  if (pkg_device != value || pkg_device.empty()) {
+    LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << value;
+    return INSTALL_ERROR;
+  }
 
-    // We allow the package to not have any serialno, but if it has a non-empty
-    // value it should match.
-    property_get("ro.serialno", value, "");
-    const std::string& pkg_serial_no = metadata["serialno"];
-    if (!pkg_serial_no.empty() && pkg_serial_no != value) {
-        LOGE("Package is for serial %s\n", pkg_serial_no.c_str());
-        return INSTALL_ERROR;
-    }
+  // We allow the package to not have any serialno, but if it has a non-empty
+  // value it should match.
+  value = android::base::GetProperty("ro.serialno", "");
+  const std::string& pkg_serial_no = metadata["serialno"];
+  if (!pkg_serial_no.empty() && pkg_serial_no != value) {
+    LOG(ERROR) << "Package is for serial " << pkg_serial_no;
+    return INSTALL_ERROR;
+  }
 
-    if (metadata["ota-type"] != "AB") {
-        LOGE("Package is not A/B\n");
-        return INSTALL_ERROR;
-    }
+  if (metadata["ota-type"] != "AB") {
+    LOG(ERROR) << "Package is not A/B";
+    return INSTALL_ERROR;
+  }
 
-    // Incremental updates should match the current build.
-    property_get("ro.build.version.incremental", value, "");
-    const std::string& pkg_pre_build = metadata["pre-build-incremental"];
-    if (!pkg_pre_build.empty() && pkg_pre_build != value) {
-        LOGE("Package is for source build %s but expected %s\n",
-             pkg_pre_build.c_str(), value);
-        return INSTALL_ERROR;
-    }
-    property_get("ro.build.fingerprint", value, "");
-    const std::string& pkg_pre_build_fingerprint = metadata["pre-build"];
-    if (!pkg_pre_build_fingerprint.empty() &&
-        pkg_pre_build_fingerprint != value) {
-        LOGE("Package is for source build %s but expected %s\n",
-             pkg_pre_build_fingerprint.c_str(), value);
-        return INSTALL_ERROR;
-    }
+  // Incremental updates should match the current build.
+  value = android::base::GetProperty("ro.build.version.incremental", "");
+  const std::string& pkg_pre_build = metadata["pre-build-incremental"];
+  if (!pkg_pre_build.empty() && pkg_pre_build != value) {
+    LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected " << value;
+    return INSTALL_ERROR;
+  }
 
-    // Check for downgrade version.
-    int64_t build_timestampt = property_get_int64(
-            "ro.build.date.utc", std::numeric_limits<int64_t>::max());
-    int64_t pkg_post_timespampt = 0;
-    // We allow to full update to the same version we are running, in case there
-    // is a problem with the current copy of that version.
-    if (metadata["post-timestamp"].empty() ||
-        !android::base::ParseInt(metadata["post-timestamp"].c_str(),
-                                 &pkg_post_timespampt) ||
-        pkg_post_timespampt < build_timestampt) {
-        if (metadata["ota-downgrade"] != "yes") {
-            LOGE("Update package is older than the current build, expected a "
-                 "build newer than timestamp %" PRIu64 " but package has "
-                 "timestamp %" PRIu64 " and downgrade not allowed.\n",
-                 build_timestampt, pkg_post_timespampt);
-            return INSTALL_ERROR;
-        }
-        if (pkg_pre_build_fingerprint.empty()) {
-            LOGE("Downgrade package must have a pre-build version set, not "
-                 "allowed.\n");
-            return INSTALL_ERROR;
-        }
-    }
+  value = android::base::GetProperty("ro.build.fingerprint", "");
+  const std::string& pkg_pre_build_fingerprint = metadata["pre-build"];
+  if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != value) {
+    LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected "
+               << value;
+    return INSTALL_ERROR;
+  }
 
-    return 0;
+  // Check for downgrade version.
+  int64_t build_timestamp =
+      android::base::GetIntProperty("ro.build.date.utc", std::numeric_limits<int64_t>::max());
+  int64_t pkg_post_timestamp = 0;
+  // We allow to full update to the same version we are running, in case there
+  // is a problem with the current copy of that version.
+  if (metadata["post-timestamp"].empty() ||
+      !android::base::ParseInt(metadata["post-timestamp"].c_str(), &pkg_post_timestamp) ||
+      pkg_post_timestamp < build_timestamp) {
+    if (metadata["ota-downgrade"] != "yes") {
+      LOG(ERROR) << "Update package is older than the current build, expected a build "
+                    "newer than timestamp "
+                 << build_timestamp << " but package has timestamp " << pkg_post_timestamp
+                 << " and downgrade not allowed.";
+      return INSTALL_ERROR;
+    }
+    if (pkg_pre_build_fingerprint.empty()) {
+      LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
+      return INSTALL_ERROR;
+    }
+  }
+
+  return 0;
 }
 
-static int
-update_binary_command(const char* path, ZipArchive* zip, int retry_count,
-                      int status_fd, std::vector<std::string>* cmd)
-{
-    int ret = check_newer_ab_build(zip);
-    if (ret) {
-        return ret;
-    }
+int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
+                          int status_fd, std::vector<std::string>* cmd) {
+  CHECK(cmd != nullptr);
+  int ret = check_newer_ab_build(zip);
+  if (ret != 0) {
+    return ret;
+  }
 
-    // For A/B updates we extract the payload properties to a buffer and obtain
-    // the RAW payload offset in the zip file.
-    const ZipEntry* properties_entry =
-            mzFindZipEntry(zip, AB_OTA_PAYLOAD_PROPERTIES);
-    if (!properties_entry) {
-        LOGE("Can't find %s\n", AB_OTA_PAYLOAD_PROPERTIES);
-        return INSTALL_CORRUPT;
-    }
-    std::vector<unsigned char> payload_properties(
-            mzGetZipEntryUncompLen(properties_entry));
-    if (!mzExtractZipEntryToBuffer(zip, properties_entry,
-                                   payload_properties.data())) {
-        LOGE("Can't extract %s\n", AB_OTA_PAYLOAD_PROPERTIES);
-        return INSTALL_CORRUPT;
-    }
+  // For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset
+  // in the zip file.
+  static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt";
+  ZipString property_name(AB_OTA_PAYLOAD_PROPERTIES);
+  ZipEntry properties_entry;
+  if (FindEntry(zip, property_name, &properties_entry) != 0) {
+    LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD_PROPERTIES;
+    return INSTALL_CORRUPT;
+  }
+  uint32_t properties_entry_length = properties_entry.uncompressed_length;
+  std::vector<uint8_t> payload_properties(properties_entry_length);
+  int32_t err =
+      ExtractToMemory(zip, &properties_entry, payload_properties.data(), properties_entry_length);
+  if (err != 0) {
+    LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES << ": " << ErrorCodeString(err);
+    return INSTALL_CORRUPT;
+  }
 
-    const ZipEntry* payload_entry = mzFindZipEntry(zip, AB_OTA_PAYLOAD);
-    if (!payload_entry) {
-        LOGE("Can't find %s\n", AB_OTA_PAYLOAD);
-        return INSTALL_CORRUPT;
-    }
-    long payload_offset = mzGetZipEntryOffset(payload_entry);
-    *cmd = {
-        "/sbin/update_engine_sideload",
-        android::base::StringPrintf("--payload=file://%s", path),
-        android::base::StringPrintf("--offset=%ld", payload_offset),
-        "--headers=" + std::string(payload_properties.begin(),
-                                   payload_properties.end()),
-        android::base::StringPrintf("--status_fd=%d", status_fd),
-    };
-    return 0;
+  static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
+  ZipString payload_name(AB_OTA_PAYLOAD);
+  ZipEntry payload_entry;
+  if (FindEntry(zip, payload_name, &payload_entry) != 0) {
+    LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD;
+    return INSTALL_CORRUPT;
+  }
+  long payload_offset = payload_entry.offset;
+  *cmd = {
+    "/sbin/update_engine_sideload",
+    "--payload=file://" + path,
+    android::base::StringPrintf("--offset=%ld", payload_offset),
+    "--headers=" + std::string(payload_properties.begin(), payload_properties.end()),
+    android::base::StringPrintf("--status_fd=%d", status_fd),
+  };
+  return 0;
 }
 
 #else  // !AB_OTA_UPDATER
 
-static int
-update_binary_command(const char* path, ZipArchive* zip, int retry_count,
-                      int status_fd, std::vector<std::string>* cmd)
-{
-    // On traditional updates we extract the update binary from the package.
-    const ZipEntry* binary_entry =
-            mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
-    if (binary_entry == NULL) {
-        return INSTALL_CORRUPT;
-    }
+int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
+                          int status_fd, std::vector<std::string>* cmd) {
+  CHECK(cmd != nullptr);
 
-    const char* binary = "/tmp/update_binary";
-    unlink(binary);
-    int fd = creat(binary, 0755);
-    if (fd < 0) {
-        LOGE("Can't make %s\n", binary);
-        return INSTALL_ERROR;
-    }
-    bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
-    close(fd);
+  // On traditional updates we extract the update binary from the package.
+  static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
+  ZipString binary_name(UPDATE_BINARY_NAME);
+  ZipEntry binary_entry;
+  if (FindEntry(zip, binary_name, &binary_entry) != 0) {
+    LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME;
+    return INSTALL_CORRUPT;
+  }
 
-    if (!ok) {
-        LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
-        return INSTALL_ERROR;
-    }
+  const char* binary = "/tmp/update_binary";
+  unlink(binary);
+  int fd = creat(binary, 0755);
+  if (fd == -1) {
+    PLOG(ERROR) << "Failed to create " << binary;
+    return INSTALL_ERROR;
+  }
 
-    *cmd = {
-        binary,
-        EXPAND(RECOVERY_API_VERSION),   // defined in Android.mk
-        std::to_string(status_fd),
-        path,
-    };
-    if (retry_count > 0)
-        cmd->push_back("retry");
-    return 0;
+  int32_t error = ExtractEntryToFile(zip, &binary_entry, fd);
+  close(fd);
+  if (error != 0) {
+    LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error);
+    return INSTALL_ERROR;
+  }
+
+  *cmd = {
+    binary,
+    EXPAND(RECOVERY_API_VERSION),  // defined in Android.mk
+    std::to_string(status_fd),
+    path,
+  };
+  if (retry_count > 0) {
+    cmd->push_back("retry");
+  }
+  return 0;
 }
 #endif  // !AB_OTA_UPDATER
 
+static void log_max_temperature(int* max_temperature) {
+  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) {
+    *max_temperature = std::max(*max_temperature, GetMaxValueFromThermalZone());
+  }
+}
+
 // If the package contains an update binary, extract it and run it.
-static int
-try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache,
-                  std::vector<std::string>& log_buffer, int retry_count)
-{
-    read_source_target_build(zip, log_buffer);
+static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_cache,
+                             std::vector<std::string>& log_buffer, int retry_count,
+                             int* max_temperature) {
+  read_source_target_build(zip, log_buffer);
 
-    int pipefd[2];
-    pipe(pipefd);
+  int pipefd[2];
+  pipe(pipefd);
 
-    std::vector<std::string> args;
-    int ret = update_binary_command(path, zip, retry_count, pipefd[1], &args);
-    mzCloseZipArchive(zip);
-    if (ret) {
-        close(pipefd[0]);
-        close(pipefd[1]);
-        return ret;
-    }
-
-    // When executing the update binary contained in the package, the
-    // arguments passed are:
-    //
-    //   - the version number for this interface
-    //
-    //   - an fd to which the program can write in order to update the
-    //     progress bar.  The program can write single-line commands:
-    //
-    //        progress <frac> <secs>
-    //            fill up the next <frac> part of of the progress bar
-    //            over <secs> seconds.  If <secs> is zero, use
-    //            set_progress commands to manually control the
-    //            progress of this segment of the bar.
-    //
-    //        set_progress <frac>
-    //            <frac> should be between 0.0 and 1.0; sets the
-    //            progress bar within the segment defined by the most
-    //            recent progress command.
-    //
-    //        firmware <"hboot"|"radio"> <filename>
-    //            arrange to install the contents of <filename> in the
-    //            given partition on reboot.
-    //
-    //            (API v2: <filename> may start with "PACKAGE:" to
-    //            indicate taking a file from the OTA package.)
-    //
-    //            (API v3: this command no longer exists.)
-    //
-    //        ui_print <string>
-    //            display <string> on the screen.
-    //
-    //        wipe_cache
-    //            a wipe of cache will be performed following a successful
-    //            installation.
-    //
-    //        clear_display
-    //            turn off the text display.
-    //
-    //        enable_reboot
-    //            packages can explicitly request that they want the user
-    //            to be able to reboot during installation (useful for
-    //            debugging packages that don't exit).
-    //
-    //   - the name of the package zip file.
-    //
-    //   - an optional argument "retry" if this update is a retry of a failed
-    //   update attempt.
-    //
-
-    // Convert the vector to a NULL-terminated char* array suitable for execv.
-    const char* chr_args[args.size() + 1];
-    chr_args[args.size()] = NULL;
-    for (size_t i = 0; i < args.size(); i++) {
-        chr_args[i] = args[i].c_str();
-    }
-
-    pid_t pid = fork();
-
-    if (pid == -1) {
-        close(pipefd[0]);
-        close(pipefd[1]);
-        LOGE("Failed to fork update binary: %s\n", strerror(errno));
-        return INSTALL_ERROR;
-    }
-
-    if (pid == 0) {
-        umask(022);
-        close(pipefd[0]);
-        execv(chr_args[0], const_cast<char**>(chr_args));
-        fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
-        _exit(-1);
-    }
+  std::vector<std::string> args;
+  int ret = update_binary_command(path, zip, retry_count, pipefd[1], &args);
+  if (ret) {
+    close(pipefd[0]);
     close(pipefd[1]);
+    return ret;
+  }
 
-    *wipe_cache = false;
-    bool retry_update = false;
+  // When executing the update binary contained in the package, the
+  // arguments passed are:
+  //
+  //   - the version number for this interface
+  //
+  //   - an FD to which the program can write in order to update the
+  //     progress bar.  The program can write single-line commands:
+  //
+  //        progress <frac> <secs>
+  //            fill up the next <frac> part of of the progress bar
+  //            over <secs> seconds.  If <secs> is zero, use
+  //            set_progress commands to manually control the
+  //            progress of this segment of the bar.
+  //
+  //        set_progress <frac>
+  //            <frac> should be between 0.0 and 1.0; sets the
+  //            progress bar within the segment defined by the most
+  //            recent progress command.
+  //
+  //        ui_print <string>
+  //            display <string> on the screen.
+  //
+  //        wipe_cache
+  //            a wipe of cache will be performed following a successful
+  //            installation.
+  //
+  //        clear_display
+  //            turn off the text display.
+  //
+  //        enable_reboot
+  //            packages can explicitly request that they want the user
+  //            to be able to reboot during installation (useful for
+  //            debugging packages that don't exit).
+  //
+  //        retry_update
+  //            updater encounters some issue during the update. It requests
+  //            a reboot to retry the same package automatically.
+  //
+  //        log <string>
+  //            updater requests logging the string (e.g. cause of the
+  //            failure).
+  //
+  //   - the name of the package zip file.
+  //
+  //   - an optional argument "retry" if this update is a retry of a failed
+  //   update attempt.
+  //
 
-    char buffer[1024];
-    FILE* from_child = fdopen(pipefd[0], "r");
-    while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
-        char* command = strtok(buffer, " \n");
-        if (command == NULL) {
-            continue;
-        } else if (strcmp(command, "progress") == 0) {
-            char* fraction_s = strtok(NULL, " \n");
-            char* seconds_s = strtok(NULL, " \n");
+  // Convert the vector to a NULL-terminated char* array suitable for execv.
+  const char* chr_args[args.size() + 1];
+  chr_args[args.size()] = nullptr;
+  for (size_t i = 0; i < args.size(); i++) {
+    chr_args[i] = args[i].c_str();
+  }
 
-            float fraction = strtof(fraction_s, NULL);
-            int seconds = strtol(seconds_s, NULL, 10);
+  pid_t pid = fork();
 
-            ui->ShowProgress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds);
-        } else if (strcmp(command, "set_progress") == 0) {
-            char* fraction_s = strtok(NULL, " \n");
-            float fraction = strtof(fraction_s, NULL);
-            ui->SetProgress(fraction);
-        } else if (strcmp(command, "ui_print") == 0) {
-            char* str = strtok(NULL, "\n");
-            if (str) {
-                ui->PrintOnScreenOnly("%s", str);
-            } else {
-                ui->PrintOnScreenOnly("\n");
-            }
-            fflush(stdout);
-        } else if (strcmp(command, "wipe_cache") == 0) {
-            *wipe_cache = true;
-        } else if (strcmp(command, "clear_display") == 0) {
-            ui->SetBackground(RecoveryUI::NONE);
-        } else if (strcmp(command, "enable_reboot") == 0) {
-            // packages can explicitly request that they want the user
-            // to be able to reboot during installation (useful for
-            // debugging packages that don't exit).
-            ui->SetEnableReboot(true);
-        } else if (strcmp(command, "retry_update") == 0) {
-            retry_update = true;
-        } else if (strcmp(command, "log") == 0) {
-            // Save the logging request from updater and write to
-            // last_install later.
-            log_buffer.push_back(std::string(strtok(NULL, "\n")));
-        } else {
-            LOGE("unknown command [%s]\n", command);
-        }
+  if (pid == -1) {
+    close(pipefd[0]);
+    close(pipefd[1]);
+    PLOG(ERROR) << "Failed to fork update binary";
+    return INSTALL_ERROR;
+  }
+
+  if (pid == 0) {
+    umask(022);
+    close(pipefd[0]);
+    execv(chr_args[0], const_cast<char**>(chr_args));
+    // Bug: 34769056
+    // We shouldn't use LOG/PLOG in the forked process, since they may cause
+    // the child process to hang. This deadlock results from an improperly
+    // copied mutex in the ui functions.
+    fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
+    _exit(EXIT_FAILURE);
+  }
+  close(pipefd[1]);
+
+  std::thread temperature_logger(log_max_temperature, max_temperature);
+
+  *wipe_cache = false;
+  bool retry_update = false;
+
+  char buffer[1024];
+  FILE* from_child = fdopen(pipefd[0], "r");
+  while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
+    std::string line(buffer);
+    size_t space = line.find_first_of(" \n");
+    std::string command(line.substr(0, space));
+    if (command.empty()) continue;
+
+    // Get rid of the leading and trailing space and/or newline.
+    std::string args = space == std::string::npos ? "" : android::base::Trim(line.substr(space));
+
+    if (command == "progress") {
+      std::vector<std::string> tokens = android::base::Split(args, " ");
+      double fraction;
+      int seconds;
+      if (tokens.size() == 2 && android::base::ParseDouble(tokens[0].c_str(), &fraction) &&
+          android::base::ParseInt(tokens[1], &seconds)) {
+        ui->ShowProgress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), seconds);
+      } else {
+        LOG(ERROR) << "invalid \"progress\" parameters: " << line;
+      }
+    } else if (command == "set_progress") {
+      std::vector<std::string> tokens = android::base::Split(args, " ");
+      double fraction;
+      if (tokens.size() == 1 && android::base::ParseDouble(tokens[0].c_str(), &fraction)) {
+        ui->SetProgress(fraction);
+      } else {
+        LOG(ERROR) << "invalid \"set_progress\" parameters: " << line;
+      }
+    } else if (command == "ui_print") {
+      ui->PrintOnScreenOnly("%s\n", args.c_str());
+      fflush(stdout);
+    } else if (command == "wipe_cache") {
+      *wipe_cache = true;
+    } else if (command == "clear_display") {
+      ui->SetBackground(RecoveryUI::NONE);
+    } else if (command == "enable_reboot") {
+      // packages can explicitly request that they want the user
+      // to be able to reboot during installation (useful for
+      // debugging packages that don't exit).
+      ui->SetEnableReboot(true);
+    } else if (command == "retry_update") {
+      retry_update = true;
+    } else if (command == "log") {
+      if (!args.empty()) {
+        // Save the logging request from updater and write to last_install later.
+        log_buffer.push_back(args);
+      } else {
+        LOG(ERROR) << "invalid \"log\" parameters: " << line;
+      }
+    } else {
+      LOG(ERROR) << "unknown command [" << command << "]";
     }
-    fclose(from_child);
+  }
+  fclose(from_child);
 
-    int status;
-    waitpid(pid, &status, 0);
-    if (retry_update) {
-        return INSTALL_RETRY;
-    }
-    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-        LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
-        return INSTALL_ERROR;
-    }
+  int status;
+  waitpid(pid, &status, 0);
 
-    return INSTALL_SUCCESS;
+  finish_log_temperature.notify_one();
+  temperature_logger.join();
+
+  if (retry_update) {
+    return INSTALL_RETRY;
+  }
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+    LOG(ERROR) << "Error in " << path << " (Status " << WEXITSTATUS(status) << ")";
+    return INSTALL_ERROR;
+  }
+
+  return INSTALL_SUCCESS;
+}
+
+// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
+// entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA
+// package.
+bool verify_package_compatibility(ZipArchiveHandle package_zip) {
+  LOG(INFO) << "Verifying package compatibility...";
+
+  static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip";
+  ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY);
+  ZipEntry compatibility_entry;
+  if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) {
+    LOG(INFO) << "Package doesn't contain " << COMPATIBILITY_ZIP_ENTRY << " entry";
+    return true;
+  }
+
+  std::string zip_content(compatibility_entry.uncompressed_length, '\0');
+  int32_t ret;
+  if ((ret = ExtractToMemory(package_zip, &compatibility_entry,
+                             reinterpret_cast<uint8_t*>(&zip_content[0]),
+                             compatibility_entry.uncompressed_length)) != 0) {
+    LOG(ERROR) << "Failed to read " << COMPATIBILITY_ZIP_ENTRY << ": " << ErrorCodeString(ret);
+    return false;
+  }
+
+  ZipArchiveHandle zip_handle;
+  ret = OpenArchiveFromMemory(static_cast<void*>(const_cast<char*>(zip_content.data())),
+                              zip_content.size(), COMPATIBILITY_ZIP_ENTRY, &zip_handle);
+  if (ret != 0) {
+    LOG(ERROR) << "Failed to OpenArchiveFromMemory: " << ErrorCodeString(ret);
+    return false;
+  }
+
+  // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents.
+  void* cookie;
+  ret = StartIteration(zip_handle, &cookie, nullptr, nullptr);
+  if (ret != 0) {
+    LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret);
+    CloseArchive(zip_handle);
+    return false;
+  }
+  std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
+
+  std::vector<std::string> compatibility_info;
+  ZipEntry info_entry;
+  ZipString info_name;
+  while (Next(cookie, &info_entry, &info_name) == 0) {
+    std::string content(info_entry.uncompressed_length, '\0');
+    int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast<uint8_t*>(&content[0]),
+                                  info_entry.uncompressed_length);
+    if (ret != 0) {
+      LOG(ERROR) << "Failed to read " << info_name.name << ": " << ErrorCodeString(ret);
+      CloseArchive(zip_handle);
+      return false;
+    }
+    compatibility_info.emplace_back(std::move(content));
+  }
+  CloseArchive(zip_handle);
+
+  // VintfObjectRecovery::CheckCompatibility returns zero on success.
+  std::string err;
+  int result = android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err);
+  if (result == 0) {
+    return true;
+  }
+
+  LOG(ERROR) << "Failed to verify package compatibility (result " << result << "): " << err;
+  return false;
 }
 
 static int
 really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
-                       std::vector<std::string>& log_buffer, int retry_count)
+                       std::vector<std::string>& log_buffer, int retry_count, int* max_temperature)
 {
     ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
     ui->Print("Finding update package...\n");
     // Give verification half the progress bar...
     ui->SetProgressType(RecoveryUI::DETERMINATE);
     ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
-    LOGI("Update location: %s\n", path);
+    LOG(INFO) << "Update location: " << path;
 
     // Map the update package into memory.
     ui->Print("Opening update package...\n");
@@ -477,7 +580,7 @@
 
     MemMapping map;
     if (sysMapFile(path, &map) != 0) {
-        LOGE("failed to map file\n");
+        LOG(ERROR) << "failed to map file";
         return INSTALL_CORRUPT;
     }
 
@@ -489,28 +592,37 @@
     }
 
     // Try to open the package.
-    ZipArchive zip;
-    int err = mzOpenZipArchive(map.addr, map.length, &zip);
+    ZipArchiveHandle zip;
+    int err = OpenArchiveFromMemory(map.addr, map.length, path, &zip);
     if (err != 0) {
-        LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
+        LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err);
         log_buffer.push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
 
         sysReleaseMap(&map);
+        CloseArchive(zip);
         return INSTALL_CORRUPT;
     }
 
+    // Additionally verify the compatibility of the package.
+    if (!verify_package_compatibility(zip)) {
+      log_buffer.push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
+      sysReleaseMap(&map);
+      CloseArchive(zip);
+      return INSTALL_CORRUPT;
+    }
+
     // Verify and install the contents of the package.
     ui->Print("Installing update...\n");
     if (retry_count > 0) {
         ui->Print("Retry attempt: %d\n", retry_count);
     }
     ui->SetEnableReboot(false);
-    int result = try_update_binary(path, &zip, wipe_cache, log_buffer, retry_count);
+    int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);
     ui->SetEnableReboot(true);
     ui->Print("\n");
 
     sysReleaseMap(&map);
-
+    CloseArchive(zip);
     return result;
 }
 
@@ -521,30 +633,38 @@
     modified_flash = true;
     auto start = std::chrono::system_clock::now();
 
+    int start_temperature = GetMaxValueFromThermalZone();
+    int max_temperature = start_temperature;
+
     int result;
     std::vector<std::string> log_buffer;
     if (setup_install_mounts() != 0) {
-        LOGE("failed to set up expected mounts for install; aborting\n");
+        LOG(ERROR) << "failed to set up expected mounts for install; aborting";
         result = INSTALL_ERROR;
     } else {
-        result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count);
+        result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count,
+                                        &max_temperature);
     }
 
     // Measure the time spent to apply OTA update in seconds.
     std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
     int time_total = static_cast<int>(duration.count());
 
-    if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
-        LOGW("Can't mount %s\n", UNCRYPT_STATUS);
-    } else {
+    bool has_cache = volume_for_path("/cache") != nullptr;
+    // Skip logging the uncrypt_status on devices without /cache.
+    if (has_cache) {
+      if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
+        LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS;
+      } else {
         std::string uncrypt_status;
         if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
-            LOGW("failed to read uncrypt status: %s\n", strerror(errno));
+          PLOG(WARNING) << "failed to read uncrypt status";
         } else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
-            LOGW("corrupted uncrypt_status: %s: %s\n", uncrypt_status.c_str(), strerror(errno));
+          LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
         } else {
-            log_buffer.push_back(android::base::Trim(uncrypt_status));
+          log_buffer.push_back(android::base::Trim(uncrypt_status));
         }
+      }
     }
 
     // The first two lines need to be the package name and install result.
@@ -554,36 +674,50 @@
         "time_total: " + std::to_string(time_total),
         "retry: " + std::to_string(retry_count),
     };
+
+    int end_temperature = GetMaxValueFromThermalZone();
+    max_temperature = std::max(end_temperature, max_temperature);
+    if (start_temperature > 0) {
+      log_buffer.push_back("temperature_start: " + std::to_string(start_temperature));
+    }
+    if (end_temperature > 0) {
+      log_buffer.push_back("temperature_end: " + std::to_string(end_temperature));
+    }
+    if (max_temperature > 0) {
+      log_buffer.push_back("temperature_max: " + std::to_string(max_temperature));
+    }
+
     std::string log_content = android::base::Join(log_header, "\n") + "\n" +
-            android::base::Join(log_buffer, "\n");
+            android::base::Join(log_buffer, "\n") + "\n";
     if (!android::base::WriteStringToFile(log_content, install_file)) {
-        LOGE("failed to write %s: %s\n", install_file, strerror(errno));
+        PLOG(ERROR) << "failed to write " << install_file;
     }
 
     // Write a copy into last_log.
-    LOGI("%s\n", log_content.c_str());
+    LOG(INFO) << log_content;
 
     return result;
 }
 
 bool verify_package(const unsigned char* package_data, size_t package_size) {
-    std::vector<Certificate> loadedKeys;
-    if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) {
-        LOGE("Failed to load keys\n");
-        return false;
-    }
-    LOGI("%zu key(s) loaded from %s\n", loadedKeys.size(), PUBLIC_KEYS_FILE);
+  std::vector<Certificate> loadedKeys;
+  if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) {
+    LOG(ERROR) << "Failed to load keys";
+    return false;
+  }
+  LOG(INFO) << loadedKeys.size() << " key(s) loaded from " << PUBLIC_KEYS_FILE;
 
-    // Verify package.
-    ui->Print("Verifying update package...\n");
-    auto t0 = std::chrono::system_clock::now();
-    int err = verify_file(const_cast<unsigned char*>(package_data), package_size, loadedKeys);
-    std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
-    ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
-    if (err != VERIFY_SUCCESS) {
-        LOGE("Signature verification failed\n");
-        LOGE("error: %d\n", kZipVerificationFailure);
-        return false;
-    }
-    return true;
+  // Verify package.
+  ui->Print("Verifying update package...\n");
+  auto t0 = std::chrono::system_clock::now();
+  int err = verify_file(package_data, package_size, loadedKeys,
+                        std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
+  std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
+  ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
+  if (err != VERIFY_SUCCESS) {
+    LOG(ERROR) << "Signature verification failed";
+    LOG(ERROR) << "error: " << kZipVerificationFailure;
+    return false;
+  }
+  return true;
 }
diff --git a/install.h b/install.h
index 14de225..fd68c3a 100644
--- a/install.h
+++ b/install.h
@@ -18,12 +18,8 @@
 #define RECOVERY_INSTALL_H_
 
 #include <string>
+#include <ziparchive/zip_archive.h>
 
-#include "common.h"
-#include "minzip/Zip.h"
-
-enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED,
-        INSTALL_RETRY };
 // Install the package specified by root_path.  If INSTALL_SUCCESS is
 // returned and *wipe_cache is true on exit, caller should wipe the
 // cache partition.
@@ -36,6 +32,10 @@
 
 // Read meta data file of the package, write its content in the string pointed by meta_data.
 // Return true if succeed, otherwise return false.
-bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data);
+bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data);
+
+// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
+// entry doesn't exist.
+bool verify_package_compatibility(ZipArchiveHandle package_zip);
 
 #endif  // RECOVERY_INSTALL_H_
diff --git a/installcommand.cpp b/installcommand.cpp
index ba64143..c6b1926 100644
--- a/installcommand.cpp
+++ b/installcommand.cpp
@@ -28,8 +28,11 @@
 
 #include "common.h"
 #include "installcommand.h"
-#include "minzip/SysUtil.h"
-#include "minzip/Zip.h"
+#include "zipwrap.hpp"
+#ifndef USE_MINZIP
+#include <ziparchive/zip_archive.h>
+#include <vintf/VintfObjectRecovery.h>
+#endif
 #ifdef USE_OLD_VERIFIER
 #include "verifier24/verifier.h"
 #else
@@ -57,15 +60,13 @@
     return -1;
 }
 
-bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data) {
-    const ZipEntry* meta_entry = mzFindZipEntry(zip, METADATA_PATH);
-    if (meta_entry == nullptr) {
-        printf("Failed to find %s in update package.\n", METADATA_PATH);
-        return false;
-    }
+bool read_metadata_from_package(ZipWrap* zip, std::string* meta_data) {
+    long size = zip->GetUncompressedSize(METADATA_PATH);
+    if (size <= 0)
+		return false;
 
-    meta_data->resize(meta_entry->uncompLen, '\0');
-    if (!mzReadZipEntry(zip, meta_entry, &(*meta_data)[0], meta_entry->uncompLen)) {
+    meta_data->resize(size, '\0');
+    if (!zip->ExtractToBuffer(METADATA_PATH, reinterpret_cast<uint8_t*>(&(*meta_data)[0]))) {
         printf("Failed to read metadata in update package.\n");
         return false;
     }
@@ -73,7 +74,7 @@
 }
 
 // Read the build.version.incremental of src/tgt from the metadata and log it to last_install.
-static void read_source_target_build(ZipArchive* zip, std::vector<std::string>& log_buffer) {
+static void read_source_target_build(ZipWrap* zip, std::vector<std::string>& log_buffer) {
     std::string meta_data;
     if (!read_metadata_from_package(zip, &meta_data)) {
         return;
@@ -103,7 +104,7 @@
 // Parses the metadata of the OTA package in |zip| and checks whether we are
 // allowed to accept this A/B package. Downgrading is not allowed unless
 // explicitly enabled in the package and only for incremental packages.
-static int check_newer_ab_build(ZipArchive* zip)
+static int check_newer_ab_build(ZipWrap* zip)
 {
     std::string metadata_str;
     if (!read_metadata_from_package(zip, &metadata_str)) {
@@ -185,7 +186,7 @@
 }
 
 int
-abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
+abupdate_binary_command(const char* path, ZipWrap* zip, int retry_count,
                       int status_fd, std::vector<std::string>* cmd)
 {
     int ret = check_newer_ab_build(zip);
@@ -195,26 +196,22 @@
 
     // For A/B updates we extract the payload properties to a buffer and obtain
     // the RAW payload offset in the zip file.
-    const ZipEntry* properties_entry =
-            mzFindZipEntry(zip, AB_OTA_PAYLOAD_PROPERTIES);
-    if (!properties_entry) {
+    if (!zip->EntryExists(AB_OTA_PAYLOAD_PROPERTIES)) {
         printf("Can't find %s\n", AB_OTA_PAYLOAD_PROPERTIES);
         return INSTALL_CORRUPT;
     }
     std::vector<unsigned char> payload_properties(
-            mzGetZipEntryUncompLen(properties_entry));
-    if (!mzExtractZipEntryToBuffer(zip, properties_entry,
-                                   payload_properties.data())) {
+            zip->GetUncompressedSize(AB_OTA_PAYLOAD_PROPERTIES));
+    if (!zip->ExtractToBuffer(AB_OTA_PAYLOAD_PROPERTIES, payload_properties.data())) {
         printf("Can't extract %s\n", AB_OTA_PAYLOAD_PROPERTIES);
         return INSTALL_CORRUPT;
     }
 
-    const ZipEntry* payload_entry = mzFindZipEntry(zip, AB_OTA_PAYLOAD);
-    if (!payload_entry) {
+    if (!zip->EntryExists(AB_OTA_PAYLOAD)) {
         printf("Can't find %s\n", AB_OTA_PAYLOAD);
         return INSTALL_CORRUPT;
     }
-    long payload_offset = mzGetZipEntryOffset(payload_entry);
+    long payload_offset = zip->GetEntryOffset(AB_OTA_PAYLOAD);
     *cmd = {
         "/sbin/update_engine_sideload",
         android::base::StringPrintf("--payload=file://%s", path),
@@ -229,8 +226,8 @@
 #else
 
 int
-abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
-                      int status_fd, std::vector<std::string>* cmd)
+abupdate_binary_command(__unused const char* path, __unused ZipWrap* zip, __unused int retry_count,
+                      __unused int status_fd, __unused std::vector<std::string>* cmd)
 {
     printf("No support for AB OTA zips included\n");
     return INSTALL_CORRUPT;
@@ -239,7 +236,7 @@
 #endif
 
 int
-update_binary_command(const char* path, ZipArchive* zip, int retry_count,
+update_binary_command(const char* path, int retry_count,
                       int status_fd, std::vector<std::string>* cmd)
 {
     char charfd[16];
@@ -258,3 +255,80 @@
         cmd->push_back("retry");
     return 0;
 }
+
+#ifdef USE_MINZIP
+bool verify_package_compatibility(ZipWrap *package_zip) {
+  if (package_zip->EntryExists("compatibility.zip"))
+    printf("Cannot verify treble package compatibility, must build TWRP in Oreo tree or higher.\n");
+  return true;
+}
+#else
+// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
+// entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA
+// package.
+bool verify_package_compatibility(ZipWrap *zw) {
+  ZipArchiveHandle package_zip = zw->GetZipArchiveHandle();
+  printf("Verifying package compatibility...\n");
+
+  static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip";
+  ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY);
+  ZipEntry compatibility_entry;
+  if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) {
+    printf("Package doesn't contain %s entry\n", COMPATIBILITY_ZIP_ENTRY);
+    return true;
+  }
+
+  std::string zip_content(compatibility_entry.uncompressed_length, '\0');
+  int32_t ret;
+  if ((ret = ExtractToMemory(package_zip, &compatibility_entry,
+                             reinterpret_cast<uint8_t*>(&zip_content[0]),
+                             compatibility_entry.uncompressed_length)) != 0) {
+    printf("Failed to read %s: %s\n", COMPATIBILITY_ZIP_ENTRY, ErrorCodeString(ret));
+    return false;
+  }
+
+  ZipArchiveHandle zip_handle;
+  ret = OpenArchiveFromMemory(static_cast<void*>(const_cast<char*>(zip_content.data())),
+                              zip_content.size(), COMPATIBILITY_ZIP_ENTRY, &zip_handle);
+  if (ret != 0) {
+    printf("Failed to OpenArchiveFromMemory: %s\n", ErrorCodeString(ret));
+    return false;
+  }
+
+  // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents.
+  void* cookie;
+  ret = StartIteration(zip_handle, &cookie, nullptr, nullptr);
+  if (ret != 0) {
+    printf("Failed to start iterating zip entries: %s\n", ErrorCodeString(ret));
+    CloseArchive(zip_handle);
+    return false;
+  }
+  std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
+
+  std::vector<std::string> compatibility_info;
+  ZipEntry info_entry;
+  ZipString info_name;
+  while (Next(cookie, &info_entry, &info_name) == 0) {
+    std::string content(info_entry.uncompressed_length, '\0');
+    int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast<uint8_t*>(&content[0]),
+                                  info_entry.uncompressed_length);
+    if (ret != 0) {
+      printf("Failed to read %s: %s\n", info_name.name, ErrorCodeString(ret));
+      CloseArchive(zip_handle);
+      return false;
+    }
+    compatibility_info.emplace_back(std::move(content));
+  }
+  CloseArchive(zip_handle);
+
+  // VintfObjectRecovery::CheckCompatibility returns zero on success.
+  std::string err;
+  int result = android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err);
+  if (result == 0) {
+    return true;
+  }
+
+  printf("Failed to verify package compatibility (result %i): %s\n", result, err.c_str());
+  return false;
+}
+#endif
diff --git a/installcommand.h b/installcommand.h
index 505640e..16c034f 100644
--- a/installcommand.h
+++ b/installcommand.h
@@ -21,15 +21,17 @@
 
 #include <string>
 
-#include "minzip/Zip.h"
+#include "zipwrap.hpp"
 
-bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data);
+bool read_metadata_from_package(ZipWrap* zip, std::string* meta_data);
 
 int
-abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
+abupdate_binary_command(const char* path, ZipWrap* zip, int retry_count,
                       int status_fd, std::vector<std::string>* cmd);
 int
-update_binary_command(const char* path, ZipArchive* zip, int retry_count,
+update_binary_command(const char* path, int retry_count,
                       int status_fd, std::vector<std::string>* cmd);
 
+bool verify_package_compatibility(ZipWrap *package_zip);
+
 #endif  // RECOVERY_INSTALL_COMMAND_H_
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
index 24d1635..fb90f61 100644
--- a/minadbd/Android.mk
+++ b/minadbd/Android.mk
@@ -12,10 +12,10 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-    adb_main.cpp \
     fuse_adb_provider.cpp \
-    services.cpp \
-    ../fuse_sideload.cpp
+    ../fuse_sideload.cpp \
+    minadbd.cpp \
+    minadbd_services.cpp \
 
 LOCAL_CLANG := true
 LOCAL_MODULE := libminadbd
diff --git a/minadbd/README.txt b/minadbd/README.md
similarity index 79%
rename from minadbd/README.txt
rename to minadbd/README.md
index e69dc87..5a0a067 100644
--- a/minadbd/README.txt
+++ b/minadbd/README.md
@@ -3,6 +3,6 @@
 
   - all services removed
   - all host mode support removed
-  - sideload_service() added; this is the only service supported.  It
+  - `sideload_service()` added; this is the only service supported. It
     receives a single blob of data, writes it to a fixed filename, and
     makes the process exit.
diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp
index d71807d..0f4c256 100644
--- a/minadbd/fuse_adb_provider.cpp
+++ b/minadbd/fuse_adb_provider.cpp
@@ -19,8 +19,6 @@
 #include <string.h>
 #include <errno.h>
 
-#include "sysdeps.h"
-
 #include "adb.h"
 #include "adb_io.h"
 #include "fuse_adb_provider.h"
diff --git a/minadbd/adb_main.cpp b/minadbd/minadbd.cpp
similarity index 91%
rename from minadbd/adb_main.cpp
rename to minadbd/minadbd.cpp
index 1db0d5f..d9da197 100644
--- a/minadbd/adb_main.cpp
+++ b/minadbd/minadbd.cpp
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
+#include "minadbd.h"
+
 #include <errno.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "sysdeps.h"
-
 #include "adb.h"
 #include "adb_auth.h"
 #include "transport.h"
 
-int adb_server_main(int is_daemon, int server_port, int /* reply_fd */) {
+int minadbd_main() {
     adb_device_banner = "sideload";
 
     signal(SIGPIPE, SIG_IGN);
diff --git a/updater/install.h b/minadbd/minadbd.h
similarity index 65%
copy from updater/install.h
copy to minadbd/minadbd.h
index 70e3434..3570a5d 100644
--- a/updater/install.h
+++ b/minadbd/minadbd.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef _UPDATER_INSTALL_H_
-#define _UPDATER_INSTALL_H_
+#ifndef MINADBD_H__
+#define MINADBD_H__
 
-void RegisterInstallFunctions();
-
-// uiPrintf function prints msg to screen as well as logs
-void uiPrintf(State* state, const char* format, ...);
-
-static int make_parents(char* name);
+int minadbd_main();
 
 #endif
diff --git a/minadbd/services.cpp b/minadbd/minadbd_services.cpp
similarity index 96%
rename from minadbd/services.cpp
rename to minadbd/minadbd_services.cpp
index 753a14e..e558f97 100644
--- a/minadbd/services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -21,11 +21,10 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "sysdeps.h"
-
 #include "adb.h"
 #include "fdevent.h"
 #include "fuse_adb_provider.h"
+#include "sysdeps.h"
 
 typedef struct stinfo stinfo;
 
@@ -62,12 +61,12 @@
 
 static int create_service_thread(void (*func)(int, void *), void *cookie) {
     int s[2];
-    if(adb_socketpair(s)) {
+    if (adb_socketpair(s)) {
         printf("cannot create service socket pair\n");
         return -1;
     }
 
-    stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
+    stinfo* sti = static_cast<stinfo*>(malloc(sizeof(stinfo)));
     if(sti == 0) fatal("cannot allocate stinfo");
     sti->func = func;
     sti->cookie = cookie;
diff --git a/minui/Android.mk b/minui/Android.mk
index 249feeb..e359887 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -1,3 +1,17 @@
+# Copyright (C) 2007 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.
+
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -6,6 +20,7 @@
     graphics.cpp \
     graphics_drm.cpp \
     graphics_fbdev.cpp \
+    graphics_overlay.cpp \
     resources.cpp
 
 LOCAL_C_INCLUDES := external/libcxx/include external/libpng
@@ -35,8 +50,21 @@
   LOCAL_CFLAGS += -DNEW_ION_HEAP
 endif
 
-LOCAL_WHOLE_STATIC_LIBRARIES += libdrm
 LOCAL_STATIC_LIBRARIES += libpng
+LOCAL_WHOLE_STATIC_LIBRARIES += \
+    libdrm
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+    LOCAL_CFLAGS += -DHAS_LIBSYNC
+    LOCAL_WHOLE_STATIC_LIBRARIES += libsync_recovery
+endif
+
+LOCAL_STATIC_LIBRARIES := \
+    libpng \
+    libbase
+
+LOCAL_CFLAGS := -Werror -std=c++14
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 
 LOCAL_MODULE := libminui
 
@@ -90,7 +118,14 @@
 LOCAL_CLANG := true
 LOCAL_MODULE := libminui
 LOCAL_WHOLE_STATIC_LIBRARIES += libminui
-LOCAL_SHARED_LIBRARIES := libpng
+LOCAL_SHARED_LIBRARIES := \
+    libpng \
+    libbase
+
+LOCAL_CFLAGS := -Werror
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -98,7 +133,15 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
 LOCAL_SRC_FILES := main.cpp
-LOCAL_STATIC_LIBRARIES := libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
+LOCAL_SHARED_LIBRARIES := libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
 LOCAL_C_INCLUDES := external/libcxx/include external/libpng
-LOCAL_FORCE_STATIC_EXECUTABLE := true
+ifneq ($(TARGET_ARCH), arm64)
+    ifneq ($(TARGET_ARCH), x86_64)
+        LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker
+    else
+        LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
+    endif
+else
+    LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
+endif
 include $(BUILD_EXECUTABLE)
diff --git a/minui/events.cpp b/minui/events.cpp
index 3b2262a..470a17a 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -15,17 +15,19 @@
  */
 
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
+#include <linux/input.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/epoll.h>
+#include <sys/ioctl.h>
 #include <unistd.h>
+#include <errno.h>
 
-#include <linux/input.h>
+#include <functional>
 
-#include "minui.h"
+#include "minui/minui.h"
 
 #define MAX_DEVICES 16
 #define MAX_MISC_FDS 16
@@ -34,9 +36,11 @@
 #define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
 
 struct fd_info {
-    int fd;
-    ev_callback cb;
-    void* data;
+  int fd;
+  ev_callback cb;
+#ifdef TW_USE_MINUI_WITH_DATA
+  void* data;
+#endif
 };
 
 static int g_epoll_fd;
@@ -49,92 +53,105 @@
 static unsigned ev_dev_count = 0;
 static unsigned ev_misc_count = 0;
 
-static bool test_bit(size_t bit, unsigned long* array) {
+static bool test_bit(size_t bit, unsigned long* array) { // NOLINT
     return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
 }
 
+#ifdef TW_USE_MINUI_WITH_DATA
 int ev_init(ev_callback input_cb, void* data) {
-    bool epollctlfail = false;
+#else
+int ev_init(ev_callback input_cb) {
+#endif
+  bool epollctlfail = false;
 
-    g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
-    if (g_epoll_fd == -1) {
-        return -1;
+  g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
+  if (g_epoll_fd == -1) {
+    return -1;
+  }
+
+  DIR* dir = opendir("/dev/input");
+  if (dir != NULL) {
+    dirent* de;
+    while ((de = readdir(dir))) {
+      // Use unsigned long to match ioctl's parameter type.
+      unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];  // NOLINT
+
+      //            fprintf(stderr,"/dev/input/%s\n", de->d_name);
+      if (strncmp(de->d_name, "event", 5)) continue;
+      int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
+      if (fd == -1) continue;
+
+      // Read the evbits of the input device.
+      if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+        close(fd);
+        continue;
+      }
+
+      // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
+      if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
+        close(fd);
+        continue;
+      }
+
+      epoll_event ev;
+      ev.events = EPOLLIN | EPOLLWAKEUP;
+      ev.data.ptr = &ev_fdinfo[ev_count];
+      if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+        close(fd);
+        epollctlfail = true;
+        continue;
+      }
+
+      ev_fdinfo[ev_count].fd = fd;
+      ev_fdinfo[ev_count].cb = std::move(input_cb);
+#ifdef TW_USE_MINUI_WITH_DATA
+      ev_fdinfo[ev_count].data = data;
+#endif
+      ev_count++;
+      ev_dev_count++;
+      if (ev_dev_count == MAX_DEVICES) break;
     }
 
-    DIR* dir = opendir("/dev/input");
-    if (dir != NULL) {
-        dirent* de;
-        while ((de = readdir(dir))) {
-            unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
+    closedir(dir);
+  }
 
-//            fprintf(stderr,"/dev/input/%s\n", de->d_name);
-            if (strncmp(de->d_name, "event", 5)) continue;
-            int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
-            if (fd == -1) continue;
+  if (epollctlfail && !ev_count) {
+    close(g_epoll_fd);
+    g_epoll_fd = -1;
+    return -1;
+  }
 
-            // Read the evbits of the input device.
-            if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
-                close(fd);
-                continue;
-            }
-
-            // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
-            if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
-                close(fd);
-                continue;
-            }
-
-            epoll_event ev;
-            ev.events = EPOLLIN | EPOLLWAKEUP;
-            ev.data.ptr = &ev_fdinfo[ev_count];
-            if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
-                close(fd);
-                epollctlfail = true;
-                continue;
-            }
-
-            ev_fdinfo[ev_count].fd = fd;
-            ev_fdinfo[ev_count].cb = input_cb;
-            ev_fdinfo[ev_count].data = data;
-            ev_count++;
-            ev_dev_count++;
-            if (ev_dev_count == MAX_DEVICES) break;
-        }
-
-        closedir(dir);
-    }
-
-    if (epollctlfail && !ev_count) {
-        close(g_epoll_fd);
-        g_epoll_fd = -1;
-        return -1;
-    }
-
-    return 0;
+  return 0;
 }
 
 int ev_get_epollfd(void) {
     return g_epoll_fd;
 }
 
+#ifdef TW_USE_MINUI_WITH_DATA
 int ev_add_fd(int fd, ev_callback cb, void* data) {
-    if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
-        return -1;
-    }
+#else
+int ev_add_fd(int fd, ev_callback cb) {
+#endif
+  if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
+    return -1;
+  }
 
-    epoll_event ev;
-    ev.events = EPOLLIN | EPOLLWAKEUP;
-    ev.data.ptr = (void *)&ev_fdinfo[ev_count];
-    int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
-    if (!ret) {
-        ev_fdinfo[ev_count].fd = fd;
-        ev_fdinfo[ev_count].cb = cb;
-        ev_fdinfo[ev_count].data = data;
-        ev_count++;
-        ev_misc_count++;
-    }
+  epoll_event ev;
+  ev.events = EPOLLIN | EPOLLWAKEUP;
+  ev.data.ptr = static_cast<void*>(&ev_fdinfo[ev_count]);
+  int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+  if (!ret) {
+    ev_fdinfo[ev_count].fd = fd;
+    ev_fdinfo[ev_count].cb = std::move(cb);
+#ifdef TW_USE_MINUI_WITH_DATA
+    ev_fdinfo[ev_count].data = data;
+#endif
+    ev_count++;
+    ev_misc_count++;
+  }
 
-    return ret;
+  return ret;
 }
 
 void ev_exit(void) {
@@ -155,13 +172,17 @@
 }
 
 void ev_dispatch(void) {
-    for (int n = 0; n < npolledevents; n++) {
-        fd_info* fdi = reinterpret_cast<fd_info*>(polledevents[n].data.ptr);
-        ev_callback cb = fdi->cb;
-        if (cb) {
-            cb(fdi->fd, polledevents[n].events, fdi->data);
-        }
+  for (int n = 0; n < npolledevents; n++) {
+    fd_info* fdi = static_cast<fd_info*>(polledevents[n].data.ptr);
+    const ev_callback& cb = fdi->cb;
+    if (cb) {
+#ifdef TW_USE_MINUI_WITH_DATA
+      cb(fdi->fd, polledevents[n].events, fdi->data);
+#else
+      cb(fdi->fd, polledevents[n].events);
+#endif
     }
+  }
 }
 
 int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
@@ -174,37 +195,47 @@
     return -1;
 }
 
+#ifdef TW_USE_MINUI_WITH_DATA
 int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data) {
-    unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
-    unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
+#else
+int ev_sync_key_state(const ev_set_key_callback& set_key_cb) {
+#endif
+  // Use unsigned long to match ioctl's parameter type.
+  unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];    // NOLINT
+  unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];  // NOLINT
 
-    for (size_t i = 0; i < ev_dev_count; ++i) {
-        memset(ev_bits, 0, sizeof(ev_bits));
-        memset(key_bits, 0, sizeof(key_bits));
+  for (size_t i = 0; i < ev_dev_count; ++i) {
+    memset(ev_bits, 0, sizeof(ev_bits));
+    memset(key_bits, 0, sizeof(key_bits));
 
-        if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
-            continue;
-        }
-        if (!test_bit(EV_KEY, ev_bits)) {
-            continue;
-        }
-        if (ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits) == -1) {
-            continue;
-        }
-
-        for (int code = 0; code <= KEY_MAX; code++) {
-            if (test_bit(code, key_bits)) {
-                set_key_cb(code, 1, data);
-            }
-        }
+    if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+      continue;
+    }
+    if (!test_bit(EV_KEY, ev_bits)) {
+      continue;
+    }
+    if (ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits) == -1) {
+      continue;
     }
 
-    return 0;
+    for (int code = 0; code <= KEY_MAX; code++) {
+      if (test_bit(code, key_bits)) {
+#ifdef TW_USE_MINUI_WITH_DATA
+        set_key_cb(code, 1, data);
+#else
+        set_key_cb(code, 1);
+#endif
+      }
+    }
+  }
+
+  return 0;
 }
 
-void ev_iterate_available_keys(std::function<void(int)> f) {
-    unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
-    unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
+void ev_iterate_available_keys(const std::function<void(int)>& f) {
+    // Use unsigned long to match ioctl's parameter type.
+    unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
+    unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
 
     for (size_t i = 0; i < ev_dev_count; ++i) {
         memset(ev_bits, 0, sizeof(ev_bits));
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index 9525176..bb96af1 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -14,22 +14,13 @@
  * limitations under the License.
  */
 
-#include <stdbool.h>
+#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 <memory>
 
 #ifdef BOARD_USE_CUSTOM_RECOVERY_FONT
 #include BOARD_USE_CUSTOM_RECOVERY_FONT
@@ -37,11 +28,14 @@
 #include "font_10x18.h"
 #endif
 
-#include "minui.h"
-#include "graphics.h"
+#include "graphics_adf.h"
+#include "graphics_drm.h"
+#include "graphics_fbdev.h"
+#include "graphics_overlay.h"
+#include "minui/minui.h"
 
 static GRFont* gr_font = NULL;
-static minui_backend* gr_backend = NULL;
+static MinuiBackend* gr_backend = nullptr;
 
 static int overscan_percent = OVERSCAN_PERCENT;
 static int overscan_offset_x = 0;
@@ -470,7 +464,7 @@
 }
 #else // TW_NO_MINUI_CUSTOM_FONTS
 int gr_init_font(const char* name, GRFont** dest) {
-    GRFont* font = reinterpret_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
+    GRFont* font = static_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
     if (font == nullptr) {
         return -1;
     }
@@ -503,15 +497,15 @@
 
 
     // fall back to the compiled-in font.
-    gr_font = reinterpret_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
-    gr_font->texture = reinterpret_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
+    gr_font = static_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
+    gr_font->texture = static_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
     gr_font->texture->width = font.width;
     gr_font->texture->height = font.height;
     gr_font->texture->row_bytes = font.width;
     gr_font->texture->pixel_bytes = 1;
 
-    unsigned char* bits = reinterpret_cast<unsigned char*>(malloc(font.width * font.height));
-    gr_font->texture->data = reinterpret_cast<unsigned char*>(bits);
+    unsigned char* bits = static_cast<unsigned char*>(malloc(font.width * font.height));
+    gr_font->texture->data = bits;
 
     unsigned char data;
     unsigned char* in = font.rundata;
@@ -525,131 +519,75 @@
 }
 #endif // TW_NO_MINUI_CUSTOM_FONTS
 
-#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);
+  gr_draw = gr_backend->Flip();
 }
 
 int gr_init(void)
 {
-    gr_init_font();
-    gr_draw = NULL;
+  gr_init_font();
+
+  auto backend = std::unique_ptr<MinuiBackend>{ std::make_unique<MinuiBackendOverlay>() };
+  gr_draw = backend->Init();
 
 #ifdef MSM_BSP
-    gr_backend = open_overlay();
-    if (gr_backend) {
-        gr_draw = gr_backend->init(gr_backend);
-        if (!gr_draw) {
-            gr_backend->exit(gr_backend);
-        } else
-            printf("Using overlay graphics.\n");
+    if (gr_draw) {
+        printf("Using overlay graphics.\n");
     }
 #endif
 
 #ifndef MSM_BSP
-    if (!gr_backend || !gr_draw) {
-        gr_backend = open_adf();
-        if (gr_backend) {
-            gr_draw = gr_backend->init(gr_backend);
-            if (!gr_draw) {
-                gr_backend->exit(gr_backend);
-            } else
-                printf("Using adf graphics.\n");
-        }
+    if (!gr_draw) {
+		backend = std::make_unique<MinuiBackendAdf>();
+        gr_draw = backend->Init();
+        if (gr_draw)
+             printf("Using adf graphics.\n");
     }
 #else
 	printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n");
 #endif
 
-    if (!gr_backend || !gr_draw) {
-        gr_backend = open_drm();
-        gr_draw = gr_backend->init(gr_backend);
+    if (!gr_draw) {
+        backend = std::make_unique<MinuiBackendDrm>();
+        gr_draw = backend->Init();
         if (gr_draw)
             printf("Using drm graphics.\n");
     }
 
-    if (!gr_backend || !gr_draw) {
-        gr_backend = open_fbdev();
-        gr_draw = gr_backend->init(gr_backend);
-        if (gr_draw == NULL) {
-            return -1;
-        } else
+    if (!gr_draw) {
+        backend = std::make_unique<MinuiBackendFbdev>();
+        gr_draw = backend->Init();
+        if (gr_draw)
             printf("Using fbdev graphics.\n");
     }
 
-    overscan_offset_x = gr_draw->width * overscan_percent / 100;
-    overscan_offset_y = gr_draw->height * overscan_percent / 100;
+  if (!gr_draw) {
+    return -1;
+  }
 
-    gr_flip();
-    gr_flip();
+  gr_backend = backend.release();
 
-    return 0;
+  overscan_offset_x = gr_draw->width * overscan_percent / 100;
+  overscan_offset_y = gr_draw->height * overscan_percent / 100;
+
+  gr_flip();
+  gr_flip();
+
+  return 0;
 }
 
-void gr_exit(void)
-{
-    gr_backend->exit(gr_backend);
+void gr_exit() {
+  delete gr_backend;
 }
 
-int gr_fb_width(void)
-{
-    return gr_draw->width - 2*overscan_offset_x;
+int gr_fb_width() {
+  return gr_draw->width - 2 * overscan_offset_x;
 }
 
-int gr_fb_height(void)
-{
-    return gr_draw->height - 2*overscan_offset_y;
+int gr_fb_height() {
+  return gr_draw->height - 2 * overscan_offset_y;
 }
 
-void gr_fb_blank(bool blank)
-{
-    gr_backend->blank(gr_backend, blank);
+void gr_fb_blank(bool blank) {
+  gr_backend->Blank(blank);
 }
diff --git a/minui/graphics.h b/minui/graphics.h
index a4115fd..3c45a40 100644
--- a/minui/graphics.h
+++ b/minui/graphics.h
@@ -17,28 +17,22 @@
 #ifndef _GRAPHICS_H_
 #define _GRAPHICS_H_
 
-#include "minui.h"
+#include "minui/minui.h"
 
-// TODO: lose the function pointers.
-struct minui_backend {
-    // Initializes the backend and returns a GRSurface* to draw into.
-    GRSurface* (*init)(minui_backend*);
+class MinuiBackend {
+ public:
+  // Initializes the backend and returns a GRSurface* to draw into.
+  virtual GRSurface* Init() = 0;
 
-    // Causes the current drawing surface (returned by the most recent
-    // call to flip() or init()) to be displayed, and returns a new
-    // drawing surface.
-    GRSurface* (*flip)(minui_backend*);
+  // Causes the current drawing surface (returned by the most recent call to Flip() or Init()) to
+  // be displayed, and returns a new drawing surface.
+  virtual GRSurface* Flip() = 0;
 
-    // Blank (or unblank) the screen.
-    void (*blank)(minui_backend*, bool);
+  // Blank (or unblank) the screen.
+  virtual void Blank(bool) = 0;
 
-    // Device cleanup when drawing is done.
-    void (*exit)(minui_backend*);
+  // Device cleanup when drawing is done.
+  virtual ~MinuiBackend() {};
 };
 
-minui_backend* open_fbdev();
-minui_backend* open_adf();
-minui_backend* open_drm();
-minui_backend* open_overlay();
-
-#endif
+#endif  // _GRAPHICS_H_
diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp
index 10e1c4b..79f4db8 100644
--- a/minui/graphics_adf.cpp
+++ b/minui/graphics_adf.cpp
@@ -14,238 +14,190 @@
  * limitations under the License.
  */
 
+#include "graphics_adf.h"
+
 #include <errno.h>
 #include <fcntl.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
+#include <sys/mman.h>
 #include <unistd.h>
 
-#include <sys/cdefs.h>
-#include <sys/mman.h>
-
 #include <adf/adf.h>
-
-#include "graphics.h"
-
-struct adf_surface_pdata {
-    GRSurface base;
-    int fd;
-    __u32 offset;
-    __u32 pitch;
-};
-
-struct adf_pdata {
-    minui_backend base;
-    int intf_fd;
-    adf_id_t eng_id;
-    __u32 format;
-
-    unsigned int current_surface;
-    unsigned int n_surfaces;
-    adf_surface_pdata surfaces[2];
-};
-
-static GRSurface* adf_flip(minui_backend *backend);
-static void adf_blank(minui_backend *backend, bool blank);
-
-static int adf_surface_init(adf_pdata *pdata, drm_mode_modeinfo *mode, adf_surface_pdata *surf) {
-    memset(surf, 0, sizeof(*surf));
-
-    surf->fd = adf_interface_simple_buffer_alloc(pdata->intf_fd, mode->hdisplay,
-            mode->vdisplay, pdata->format, &surf->offset, &surf->pitch);
-    if (surf->fd < 0)
-        return surf->fd;
-
-    surf->base.width = mode->hdisplay;
-    surf->base.height = mode->vdisplay;
-    surf->base.row_bytes = surf->pitch;
-    surf->base.pixel_bytes = (pdata->format == DRM_FORMAT_RGB565) ? 2 : 4;
-
-    surf->base.data = reinterpret_cast<uint8_t*>(mmap(NULL,
-                                                      surf->pitch * surf->base.height, PROT_WRITE,
-                                                      MAP_SHARED, surf->fd, surf->offset));
-    if (surf->base.data == MAP_FAILED) {
-        close(surf->fd);
-        return -errno;
-    }
-
-    return 0;
-}
-
-static int adf_interface_init(adf_pdata *pdata)
-{
-    adf_interface_data intf_data;
-    int ret = 0;
-    int err;
-
-    err = adf_get_interface_data(pdata->intf_fd, &intf_data);
-    if (err < 0)
-        return err;
-
-    err = adf_surface_init(pdata, &intf_data.current_mode, &pdata->surfaces[0]);
-    if (err < 0) {
-        fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err));
-        ret = err;
-        goto done;
-    }
-
-    err = adf_surface_init(pdata, &intf_data.current_mode,
-            &pdata->surfaces[1]);
-    if (err < 0) {
-        fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err));
-        memset(&pdata->surfaces[1], 0, sizeof(pdata->surfaces[1]));
-        pdata->n_surfaces = 1;
-    } else {
-        pdata->n_surfaces = 2;
-    }
-
-done:
-    adf_free_interface_data(&intf_data);
-    return ret;
-}
-
-static int adf_device_init(adf_pdata *pdata, adf_device *dev)
-{
-    adf_id_t intf_id;
-    int intf_fd;
-    int err;
-
-    err = adf_find_simple_post_configuration(dev, &pdata->format, 1, &intf_id,
-            &pdata->eng_id);
-    if (err < 0)
-        return err;
-
-    err = adf_device_attach(dev, pdata->eng_id, intf_id);
-    if (err < 0 && err != -EALREADY)
-        return err;
-
-    pdata->intf_fd = adf_interface_open(dev, intf_id, O_RDWR);
-    if (pdata->intf_fd < 0)
-        return pdata->intf_fd;
-
-    err = adf_interface_init(pdata);
-    if (err < 0) {
-        close(pdata->intf_fd);
-        pdata->intf_fd = -1;
-    }
-
-    return err;
-}
-
-static GRSurface* adf_init(minui_backend *backend)
-{
-    adf_pdata *pdata = (adf_pdata *)backend;
-    adf_id_t *dev_ids = NULL;
-    ssize_t n_dev_ids, i;
-    GRSurface* ret;
-
-#if defined(RECOVERY_ABGR)
-    pdata->format = DRM_FORMAT_ABGR8888;
-#elif defined(RECOVERY_BGRA)
-    pdata->format = DRM_FORMAT_BGRA8888;
-#elif defined(RECOVERY_RGBA)
-    pdata->format = DRM_FORMAT_RGBA8888;
-#elif defined(RECOVERY_RGBX)
-    pdata->format = DRM_FORMAT_RGBX8888;
-#else
-    pdata->format = DRM_FORMAT_RGB565;
+#ifdef HAS_LIBSYNC
+#include <sync/sync.h>
 #endif
 
-    n_dev_ids = adf_devices(&dev_ids);
-    if (n_dev_ids == 0) {
-        return NULL;
-    } else if (n_dev_ids < 0) {
-        fprintf(stderr, "enumerating adf devices failed: %s\n",
-                strerror(-n_dev_ids));
-        return NULL;
-    }
+#include "minui/minui.h"
 
-    pdata->intf_fd = -1;
+MinuiBackendAdf::MinuiBackendAdf() : intf_fd(-1), dev(), n_surfaces(0), surfaces() {}
 
-    for (i = 0; i < n_dev_ids && pdata->intf_fd < 0; i++) {
-        adf_device dev;
+int MinuiBackendAdf::SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* surf) {
+  *surf = {};
+  surf->fence_fd = -1;
+  surf->fd = adf_interface_simple_buffer_alloc(intf_fd, mode->hdisplay, mode->vdisplay, format,
+                                               &surf->offset, &surf->pitch);
+  if (surf->fd < 0) {
+    return surf->fd;
+  }
 
-        int err = adf_device_open(dev_ids[i], O_RDWR, &dev);
-        if (err < 0) {
-            fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i],
-                    strerror(-err));
-            continue;
-        }
+  surf->width = mode->hdisplay;
+  surf->height = mode->vdisplay;
+  surf->row_bytes = surf->pitch;
+  surf->pixel_bytes = (format == DRM_FORMAT_RGB565) ? 2 : 4;
 
-        err = adf_device_init(pdata, &dev);
-        if (err < 0)
-            fprintf(stderr, "initializing adf device %u failed: %s\n",
-                    dev_ids[i], strerror(-err));
-
-        adf_device_close(&dev);
-    }
-
-    free(dev_ids);
-
-    if (pdata->intf_fd < 0)
-        return NULL;
-
-    ret = adf_flip(backend);
-
-    adf_blank(backend, true);
-    adf_blank(backend, false);
-
-    return ret;
-}
-
-static GRSurface* adf_flip(minui_backend *backend)
-{
-    adf_pdata *pdata = (adf_pdata *)backend;
-    adf_surface_pdata *surf = &pdata->surfaces[pdata->current_surface];
-
-    int fence_fd = adf_interface_simple_post(pdata->intf_fd, pdata->eng_id,
-            surf->base.width, surf->base.height, pdata->format, surf->fd,
-            surf->offset, surf->pitch, -1);
-    if (fence_fd >= 0)
-        close(fence_fd);
-
-    pdata->current_surface = (pdata->current_surface + 1) % pdata->n_surfaces;
-    return &pdata->surfaces[pdata->current_surface].base;
-}
-
-static void adf_blank(minui_backend *backend, bool blank)
-{
-    adf_pdata *pdata = (adf_pdata *)backend;
-    adf_interface_blank(pdata->intf_fd,
-            blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON);
-}
-
-static void adf_surface_destroy(adf_surface_pdata *surf)
-{
-    munmap(surf->base.data, surf->pitch * surf->base.height);
+  surf->data = static_cast<uint8_t*>(
+      mmap(nullptr, surf->pitch * surf->height, PROT_WRITE, MAP_SHARED, surf->fd, surf->offset));
+  if (surf->data == MAP_FAILED) {
+    int saved_errno = errno;
     close(surf->fd);
+    return -saved_errno;
+  }
+
+  return 0;
 }
 
-static void adf_exit(minui_backend *backend)
-{
-    adf_pdata *pdata = (adf_pdata *)backend;
-    unsigned int i;
+int MinuiBackendAdf::InterfaceInit() {
+  adf_interface_data intf_data;
+  int err = adf_get_interface_data(intf_fd, &intf_data);
+  if (err < 0) return err;
 
-    for (i = 0; i < pdata->n_surfaces; i++)
-        adf_surface_destroy(&pdata->surfaces[i]);
-    if (pdata->intf_fd >= 0)
-        close(pdata->intf_fd);
-    free(pdata);
+  int ret = 0;
+  err = SurfaceInit(&intf_data.current_mode, &surfaces[0]);
+  if (err < 0) {
+    fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err));
+    ret = err;
+    goto done;
+  }
+
+  err = SurfaceInit(&intf_data.current_mode, &surfaces[1]);
+  if (err < 0) {
+    fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err));
+    surfaces[1] = {};
+    n_surfaces = 1;
+  } else {
+    n_surfaces = 2;
+  }
+
+done:
+  adf_free_interface_data(&intf_data);
+  return ret;
 }
 
-minui_backend *open_adf()
-{
-    adf_pdata* pdata = reinterpret_cast<adf_pdata*>(calloc(1, sizeof(*pdata)));
-    if (!pdata) {
-        perror("allocating adf backend failed");
-        return NULL;
+int MinuiBackendAdf::DeviceInit(adf_device* dev) {
+  adf_id_t intf_id;
+  int err = adf_find_simple_post_configuration(dev, &format, 1, &intf_id, &eng_id);
+  if (err < 0) return err;
+
+  err = adf_device_attach(dev, eng_id, intf_id);
+  if (err < 0 && err != -EALREADY) return err;
+
+  intf_fd = adf_interface_open(dev, intf_id, O_RDWR);
+  if (intf_fd < 0) return intf_fd;
+
+  err = InterfaceInit();
+  if (err < 0) {
+    close(intf_fd);
+    intf_fd = -1;
+  }
+
+  return err;
+}
+
+GRSurface* MinuiBackendAdf::Init() {
+#if defined(RECOVERY_ABGR)
+  format = DRM_FORMAT_ABGR8888;
+#elif defined(RECOVERY_BGRA)
+  format = DRM_FORMAT_BGRA8888;
+#elif defined(RECOVERY_RGBA)
+  format = DRM_FORMAT_RGBA8888;
+#elif defined(RECOVERY_RGBX)
+  format = DRM_FORMAT_RGBX8888;
+#else
+  format = DRM_FORMAT_RGB565;
+#endif
+
+  adf_id_t* dev_ids = nullptr;
+  ssize_t n_dev_ids = adf_devices(&dev_ids);
+  if (n_dev_ids == 0) {
+    return nullptr;
+  } else if (n_dev_ids < 0) {
+    fprintf(stderr, "enumerating adf devices failed: %s\n", strerror(-n_dev_ids));
+    return nullptr;
+  }
+
+  intf_fd = -1;
+
+  for (ssize_t i = 0; i < n_dev_ids && intf_fd < 0; i++) {
+    int err = adf_device_open(dev_ids[i], O_RDWR, &dev);
+    if (err < 0) {
+      fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i], strerror(-err));
+      continue;
     }
 
-    pdata->base.init = adf_init;
-    pdata->base.flip = adf_flip;
-    pdata->base.blank = adf_blank;
-    pdata->base.exit = adf_exit;
-    return &pdata->base;
+    err = DeviceInit(&dev);
+    if (err < 0) {
+      fprintf(stderr, "initializing adf device %u failed: %s\n", dev_ids[i], strerror(-err));
+      adf_device_close(&dev);
+    }
+  }
+
+  free(dev_ids);
+
+  if (intf_fd < 0) return nullptr;
+
+  GRSurface* ret = Flip();
+
+  Blank(true);
+  Blank(false);
+
+  return ret;
+}
+
+void MinuiBackendAdf::Sync(__unused GRSurfaceAdf* surf) {
+#ifdef HAS_LIBSYNC
+  static constexpr unsigned int warningTimeout = 3000;
+
+  if (surf == nullptr) return;
+
+  if (surf->fence_fd >= 0) {
+    int err = sync_wait(surf->fence_fd, warningTimeout);
+    if (err < 0) {
+      perror("adf sync fence wait error\n");
+    }
+
+    close(surf->fence_fd);
+    surf->fence_fd = -1;
+  }
+#endif
+}
+
+GRSurface* MinuiBackendAdf::Flip() {
+  GRSurfaceAdf* surf = &surfaces[current_surface];
+
+  int fence_fd = adf_interface_simple_post(intf_fd, eng_id, surf->width, surf->height, format,
+                                           surf->fd, surf->offset, surf->pitch, -1);
+  if (fence_fd >= 0) surf->fence_fd = fence_fd;
+
+  current_surface = (current_surface + 1) % n_surfaces;
+  Sync(&surfaces[current_surface]);
+  return &surfaces[current_surface];
+}
+
+void MinuiBackendAdf::Blank(bool blank) {
+  adf_interface_blank(intf_fd, blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON);
+}
+
+void MinuiBackendAdf::SurfaceDestroy(GRSurfaceAdf* surf) {
+  munmap(surf->data, surf->pitch * surf->height);
+  close(surf->fence_fd);
+  close(surf->fd);
+}
+
+MinuiBackendAdf::~MinuiBackendAdf() {
+  adf_device_close(&dev);
+  for (unsigned int i = 0; i < n_surfaces; i++) {
+    SurfaceDestroy(&surfaces[i]);
+  }
+  if (intf_fd >= 0) close(intf_fd);
 }
diff --git a/minui/graphics_adf.h b/minui/graphics_adf.h
new file mode 100644
index 0000000..2f019ed
--- /dev/null
+++ b/minui/graphics_adf.h
@@ -0,0 +1,58 @@
+/*
+ * 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 _GRAPHICS_ADF_H_
+#define _GRAPHICS_ADF_H_
+
+#include <adf/adf.h>
+
+#include "graphics.h"
+
+class GRSurfaceAdf : public GRSurface {
+ private:
+  int fence_fd;
+  int fd;
+  __u32 offset;
+  __u32 pitch;
+
+  friend class MinuiBackendAdf;
+};
+
+class MinuiBackendAdf : public MinuiBackend {
+ public:
+  GRSurface* Init() override;
+  GRSurface* Flip() override;
+  void Blank(bool) override;
+  ~MinuiBackendAdf() override;
+  MinuiBackendAdf();
+
+ private:
+  int SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* surf);
+  int InterfaceInit();
+  int DeviceInit(adf_device* dev);
+  void SurfaceDestroy(GRSurfaceAdf* surf);
+  void Sync(GRSurfaceAdf* surf);
+
+  int intf_fd;
+  adf_id_t eng_id;
+  __u32 format;
+  adf_device dev;
+  unsigned int current_surface;
+  unsigned int n_surfaces;
+  GRSurfaceAdf surfaces[2];
+};
+
+#endif  // _GRAPHICS_ADF_H_
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
index ddda187..ef377b7 100644
--- a/minui/graphics_drm.cpp
+++ b/minui/graphics_drm.cpp
@@ -14,466 +14,384 @@
  * limitations under the License.
  */
 
-#include <drm_fourcc.h>
+#include "graphics_drm.h"
+
 #include <fcntl.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
+
+#include <drm_fourcc.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
-#include "minui.h"
-#include "graphics.h"
+#include "minui/minui.h"
 
 #define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
 
-struct drm_surface {
-    GRSurface base;
-    uint32_t fb_id;
-    uint32_t handle;
-};
+MinuiBackendDrm::MinuiBackendDrm()
+    : GRSurfaceDrms(), main_monitor_crtc(nullptr), main_monitor_connector(nullptr), drm_fd(-1) {}
 
-static drm_surface *drm_surfaces[2];
-static int current_buffer;
-
-static drmModeCrtc *main_monitor_crtc;
-static drmModeConnector *main_monitor_connector;
-
-static int drm_fd = -1;
-
-static void drm_disable_crtc(int drm_fd, drmModeCrtc *crtc) {
-    if (crtc) {
-        drmModeSetCrtc(drm_fd, crtc->crtc_id,
-                       0, // fb_id
-                       0, 0,  // x,y
-                       NULL,  // connectors
-                       0,     // connector_count
-                       NULL); // mode
-    }
+void MinuiBackendDrm::DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc) {
+  if (crtc) {
+    drmModeSetCrtc(drm_fd, crtc->crtc_id,
+                   0,         // fb_id
+                   0, 0,      // x,y
+                   nullptr,   // connectors
+                   0,         // connector_count
+                   nullptr);  // mode
+  }
 }
 
-static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc,
-                            struct drm_surface *surface) {
-    int32_t ret;
+void MinuiBackendDrm::DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, GRSurfaceDrm* surface) {
+  int32_t ret = drmModeSetCrtc(drm_fd, crtc->crtc_id, surface->fb_id, 0, 0,  // x,y
+                               &main_monitor_connector->connector_id,
+                               1,  // connector_count
+                               &main_monitor_crtc->mode);
 
-    ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
-                         surface->fb_id,
-                         0, 0,  // x,y
-                         &main_monitor_connector->connector_id,
-                         1,  // connector_count
-                         &main_monitor_crtc->mode);
-
-    if (ret)
-        printf("drmModeSetCrtc failed ret=%d\n", ret);
+  if (ret) {
+    printf("drmModeSetCrtc failed ret=%d\n", ret);
+  }
 }
 
-static void drm_blank(minui_backend* backend __unused, bool blank) {
-    if (blank)
-        drm_disable_crtc(drm_fd, main_monitor_crtc);
-    else
-        drm_enable_crtc(drm_fd, main_monitor_crtc,
-                        drm_surfaces[current_buffer]);
+void MinuiBackendDrm::Blank(bool blank) {
+  if (blank) {
+    DrmDisableCrtc(drm_fd, main_monitor_crtc);
+  } else {
+    DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]);
+  }
 }
 
-static void drm_destroy_surface(struct drm_surface *surface) {
-    struct drm_gem_close gem_close;
-    int ret;
+void MinuiBackendDrm::DrmDestroySurface(GRSurfaceDrm* surface) {
+  if (!surface) return;
 
-    if(!surface)
-        return;
+  if (surface->data) {
+    munmap(surface->data, surface->row_bytes * surface->height);
+  }
 
-    if (surface->base.data)
-        munmap(surface->base.data,
-               surface->base.row_bytes * surface->base.height);
-
-    if (surface->fb_id) {
-        ret = drmModeRmFB(drm_fd, surface->fb_id);
-        if (ret)
-            printf("drmModeRmFB failed ret=%d\n", ret);
+  if (surface->fb_id) {
+    int ret = drmModeRmFB(drm_fd, surface->fb_id);
+    if (ret) {
+      printf("drmModeRmFB failed ret=%d\n", ret);
     }
+  }
 
-    if (surface->handle) {
-        memset(&gem_close, 0, sizeof(gem_close));
-        gem_close.handle = surface->handle;
+  if (surface->handle) {
+    drm_gem_close gem_close = {};
+    gem_close.handle = surface->handle;
 
-        ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
-        if (ret)
-            printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
+    int ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
+    if (ret) {
+      printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
     }
+  }
 
-    free(surface);
+  delete surface;
 }
 
 static int drm_format_to_bpp(uint32_t format) {
-    switch(format) {
-        case DRM_FORMAT_ABGR8888:
-        case DRM_FORMAT_BGRA8888:
-        case DRM_FORMAT_RGBX8888:
-        case DRM_FORMAT_BGRX8888:
-        case DRM_FORMAT_XBGR8888:
-        case DRM_FORMAT_ARGB8888:
-        case DRM_FORMAT_XRGB8888:
-            return 32;
-        case DRM_FORMAT_RGB565:
-            return 16;
-        default:
-            printf("Unknown format %d\n", format);
-            return 32;
-    }
+  switch (format) {
+    case DRM_FORMAT_ABGR8888:
+    case DRM_FORMAT_BGRA8888:
+    case DRM_FORMAT_RGBX8888:
+    case DRM_FORMAT_BGRX8888:
+    case DRM_FORMAT_XBGR8888:
+    case DRM_FORMAT_XRGB8888:
+      return 32;
+    case DRM_FORMAT_RGB565:
+      return 16;
+    default:
+      printf("Unknown format %d\n", format);
+      return 32;
+  }
 }
 
-static drm_surface *drm_create_surface(int width, int height) {
-    struct drm_surface *surface;
-    struct drm_mode_create_dumb create_dumb;
-    uint32_t format;
-    int ret;
+GRSurfaceDrm* MinuiBackendDrm::DrmCreateSurface(int width, int height) {
+  GRSurfaceDrm* surface = new GRSurfaceDrm;
+  *surface = {};
 
-    surface = (struct drm_surface*)calloc(1, sizeof(*surface));
-    if (!surface) {
-        printf("Can't allocate memory\n");
-        return NULL;
-    }
-
+  uint32_t format;
 #if defined(RECOVERY_ABGR)
-    format = DRM_FORMAT_RGBA8888;
+  format = DRM_FORMAT_RGBA8888;
 #elif defined(RECOVERY_BGRA)
-    format = DRM_FORMAT_ARGB8888;
+  format = DRM_FORMAT_ARGB8888;
 #elif defined(RECOVERY_RGBA)
-    format = DRM_FORMAT_ABGR8888;
+  format = DRM_FORMAT_ARGB8888;
 #elif defined(RECOVERY_RGBX)
-    format = DRM_FORMAT_XBGR8888;
+  format = DRM_FORMAT_XBGR8888;
 #else
-    format = DRM_FORMAT_RGB565;
+  format = DRM_FORMAT_RGB565;
 #endif
 
-    memset(&create_dumb, 0, sizeof(create_dumb));
-    create_dumb.height = height;
-    create_dumb.width = width;
-    create_dumb.bpp = drm_format_to_bpp(format);
-    create_dumb.flags = 0;
+  drm_mode_create_dumb create_dumb = {};
+  create_dumb.height = height;
+  create_dumb.width = width;
+  create_dumb.bpp = drm_format_to_bpp(format);
+  create_dumb.flags = 0;
 
-    ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
-    if (ret) {
-        printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret);
-        drm_destroy_surface(surface);
-        return NULL;
-    }
-    surface->handle = create_dumb.handle;
+  int ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
+  if (ret) {
+    printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n", ret);
+    DrmDestroySurface(surface);
+    return nullptr;
+  }
+  surface->handle = create_dumb.handle;
 
-    uint32_t handles[4], pitches[4], offsets[4];
+  uint32_t handles[4], pitches[4], offsets[4];
 
-    handles[0] = surface->handle;
-    pitches[0] = create_dumb.pitch;
-    offsets[0] = 0;
+  handles[0] = surface->handle;
+  pitches[0] = create_dumb.pitch;
+  offsets[0] = 0;
 
-    ret = drmModeAddFB2(drm_fd, width, height,
-            format, handles, pitches, offsets,
-            &(surface->fb_id), 0);
-    if (ret) {
-        printf("drmModeAddFB2 failed ret=%d\n", ret);
-        drm_destroy_surface(surface);
-        return NULL;
-    }
+  ret =
+      drmModeAddFB2(drm_fd, width, height, format, handles, pitches, offsets, &(surface->fb_id), 0);
+  if (ret) {
+    printf("drmModeAddFB2 failed ret=%d\n", ret);
+    DrmDestroySurface(surface);
+    return nullptr;
+  }
 
-    struct drm_mode_map_dumb map_dumb;
-    memset(&map_dumb, 0, sizeof(map_dumb));
-    map_dumb.handle = create_dumb.handle;
-    ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
-    if (ret) {
-        printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n",ret);
-        drm_destroy_surface(surface);
-        return NULL;;
-    }
+  drm_mode_map_dumb map_dumb = {};
+  map_dumb.handle = create_dumb.handle;
+  ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
+  if (ret) {
+    printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n", ret);
+    DrmDestroySurface(surface);
+    return nullptr;
+  }
 
-    surface->base.height = height;
-    surface->base.width = width;
-    surface->base.row_bytes = create_dumb.pitch;
-    surface->base.pixel_bytes = create_dumb.bpp / 8;
-    surface->base.data = (unsigned char*)
-                         mmap(NULL,
-                              surface->base.height * surface->base.row_bytes,
-                              PROT_READ | PROT_WRITE, MAP_SHARED,
-                              drm_fd, map_dumb.offset);
-    if (surface->base.data == MAP_FAILED) {
-        perror("mmap() failed");
-        drm_destroy_surface(surface);
-        return NULL;
-    }
+  surface->height = height;
+  surface->width = width;
+  surface->row_bytes = create_dumb.pitch;
+  surface->pixel_bytes = create_dumb.bpp / 8;
+  surface->data = static_cast<unsigned char*>(mmap(nullptr, surface->height * surface->row_bytes,
+                                                   PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd,
+                                                   map_dumb.offset));
+  if (surface->data == MAP_FAILED) {
+    perror("mmap() failed");
+    DrmDestroySurface(surface);
+    return nullptr;
+  }
 
-    return surface;
+  return surface;
 }
 
-static drmModeCrtc *find_crtc_for_connector(int fd,
-                            drmModeRes *resources,
-                            drmModeConnector *connector) {
-    int i, j;
-    drmModeEncoder *encoder;
-    int32_t crtc;
+static drmModeCrtc* find_crtc_for_connector(int fd, drmModeRes* resources,
+                                            drmModeConnector* connector) {
+  // Find the encoder. If we already have one, just use it.
+  drmModeEncoder* encoder;
+  if (connector->encoder_id) {
+    encoder = drmModeGetEncoder(fd, connector->encoder_id);
+  } else {
+    encoder = nullptr;
+  }
 
-    /*
-     * Find the encoder. If we already have one, just use it.
-     */
-    if (connector->encoder_id)
-        encoder = drmModeGetEncoder(fd, connector->encoder_id);
-    else
-        encoder = NULL;
+  int32_t crtc;
+  if (encoder && encoder->crtc_id) {
+    crtc = encoder->crtc_id;
+    drmModeFreeEncoder(encoder);
+    return drmModeGetCrtc(fd, crtc);
+  }
 
-    if (encoder && encoder->crtc_id) {
-        crtc = encoder->crtc_id;
+  // Didn't find anything, try to find a crtc and encoder combo.
+  crtc = -1;
+  for (int i = 0; i < connector->count_encoders; i++) {
+    encoder = drmModeGetEncoder(fd, connector->encoders[i]);
+
+    if (encoder) {
+      for (int j = 0; j < resources->count_crtcs; j++) {
+        if (!(encoder->possible_crtcs & (1 << j))) continue;
+        crtc = resources->crtcs[j];
+        break;
+      }
+      if (crtc >= 0) {
         drmModeFreeEncoder(encoder);
         return drmModeGetCrtc(fd, crtc);
+      }
     }
+  }
 
-    /*
-     * Didn't find anything, try to find a crtc and encoder combo.
-     */
-    crtc = -1;
-    for (i = 0; i < connector->count_encoders; i++) {
-        encoder = drmModeGetEncoder(fd, connector->encoders[i]);
-
-        if (encoder) {
-            for (j = 0; j < resources->count_crtcs; j++) {
-                if (!(encoder->possible_crtcs & (1 << j)))
-                    continue;
-                crtc = resources->crtcs[j];
-                break;
-            }
-            if (crtc >= 0) {
-                drmModeFreeEncoder(encoder);
-                return drmModeGetCrtc(fd, crtc);
-            }
-        }
-    }
-
-    return NULL;
+  return nullptr;
 }
 
-static drmModeConnector *find_used_connector_by_type(int fd,
-                                 drmModeRes *resources,
-                                 unsigned type) {
-    int i;
-    for (i = 0; i < resources->count_connectors; i++) {
-        drmModeConnector *connector;
-
-        connector = drmModeGetConnector(fd, resources->connectors[i]);
-        if (connector) {
-            if ((connector->connector_type == type) &&
-                    (connector->connection == DRM_MODE_CONNECTED) &&
-                    (connector->count_modes > 0))
-                return connector;
-
-            drmModeFreeConnector(connector);
-        }
+static drmModeConnector* find_used_connector_by_type(int fd, drmModeRes* resources, unsigned type) {
+  for (int i = 0; i < resources->count_connectors; i++) {
+    drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]);
+    if (connector) {
+      if ((connector->connector_type == type) && (connector->connection == DRM_MODE_CONNECTED) &&
+          (connector->count_modes > 0)) {
+        return connector;
+      }
+      drmModeFreeConnector(connector);
     }
-    return NULL;
+  }
+  return nullptr;
 }
 
-static drmModeConnector *find_first_connected_connector(int fd,
-                             drmModeRes *resources) {
-    int i;
-    for (i = 0; i < resources->count_connectors; i++) {
-        drmModeConnector *connector;
+static drmModeConnector* find_first_connected_connector(int fd, drmModeRes* resources) {
+  for (int i = 0; i < resources->count_connectors; i++) {
+    drmModeConnector* connector;
 
-        connector = drmModeGetConnector(fd, resources->connectors[i]);
-        if (connector) {
-            if ((connector->count_modes > 0) &&
-                    (connector->connection == DRM_MODE_CONNECTED))
-                return connector;
+    connector = drmModeGetConnector(fd, resources->connectors[i]);
+    if (connector) {
+      if ((connector->count_modes > 0) && (connector->connection == DRM_MODE_CONNECTED))
+        return connector;
 
-            drmModeFreeConnector(connector);
-        }
+      drmModeFreeConnector(connector);
     }
-    return NULL;
+  }
+  return nullptr;
 }
 
-static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources,
-        uint32_t *mode_index) {
-    unsigned i = 0;
-    int modes;
-    /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
-    unsigned kConnectorPriority[] = {
-        DRM_MODE_CONNECTOR_LVDS,
-        DRM_MODE_CONNECTOR_eDP,
-        DRM_MODE_CONNECTOR_DSI,
-    };
+drmModeConnector* MinuiBackendDrm::FindMainMonitor(int fd, drmModeRes* resources,
+                                                   uint32_t* mode_index) {
+  /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
+  static constexpr unsigned kConnectorPriority[] = {
+    DRM_MODE_CONNECTOR_LVDS,
+    DRM_MODE_CONNECTOR_eDP,
+    DRM_MODE_CONNECTOR_DSI,
+  };
 
-    drmModeConnector *main_monitor_connector = NULL;
-    do {
-        main_monitor_connector = find_used_connector_by_type(fd,
-                                         resources,
-                                         kConnectorPriority[i]);
-        i++;
-    } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
+  drmModeConnector* main_monitor_connector = nullptr;
+  unsigned i = 0;
+  do {
+    main_monitor_connector = find_used_connector_by_type(fd, resources, kConnectorPriority[i]);
+    i++;
+  } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
 
-    /* If we didn't find a connector, grab the first one that is connected. */
-    if (!main_monitor_connector)
-        main_monitor_connector =
-                find_first_connected_connector(fd, resources);
+  /* If we didn't find a connector, grab the first one that is connected. */
+  if (!main_monitor_connector) {
+    main_monitor_connector = find_first_connected_connector(fd, resources);
+  }
 
-    /* If we still didn't find a connector, give up and return. */
-    if (!main_monitor_connector)
-        return NULL;
+  /* If we still didn't find a connector, give up and return. */
+  if (!main_monitor_connector) return nullptr;
 
-    *mode_index = 0;
-    for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
-        if (main_monitor_connector->modes[modes].type &
-                DRM_MODE_TYPE_PREFERRED) {
-            *mode_index = modes;
-            break;
-        }
+  *mode_index = 0;
+  for (int modes = 0; modes < main_monitor_connector->count_modes; modes++) {
+    if (main_monitor_connector->modes[modes].type & DRM_MODE_TYPE_PREFERRED) {
+      *mode_index = modes;
+      break;
     }
+  }
 
-    return main_monitor_connector;
+  return main_monitor_connector;
 }
 
-static void disable_non_main_crtcs(int fd,
-                    drmModeRes *resources,
-                    drmModeCrtc* main_crtc) {
-    int i;
-    drmModeCrtc* crtc;
-
-    for (i = 0; i < resources->count_connectors; i++) {
-        drmModeConnector *connector;
-
-        connector = drmModeGetConnector(fd, resources->connectors[i]);
-        crtc = find_crtc_for_connector(fd, resources, connector);
-        if (crtc->crtc_id != main_crtc->crtc_id)
-            drm_disable_crtc(fd, crtc);
-        drmModeFreeCrtc(crtc);
+void MinuiBackendDrm::DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc) {
+  for (int i = 0; i < resources->count_connectors; i++) {
+    drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]);
+    drmModeCrtc* crtc = find_crtc_for_connector(fd, resources, connector);
+    if (crtc->crtc_id != main_crtc->crtc_id) {
+      DrmDisableCrtc(fd, crtc);
     }
+    drmModeFreeCrtc(crtc);
+  }
 }
 
-static GRSurface* drm_init(minui_backend* backend __unused) {
-    drmModeRes *res = NULL;
-    uint32_t selected_mode;
-    char *dev_name;
-    int width, height;
-    int ret, i;
+GRSurface* MinuiBackendDrm::Init() {
+  drmModeRes* res = nullptr;
 
-    /* Consider DRM devices in order. */
-    for (i = 0; i < DRM_MAX_MINOR; i++) {
-        uint64_t cap = 0;
+  /* Consider DRM devices in order. */
+  for (int i = 0; i < DRM_MAX_MINOR; i++) {
+    char* dev_name;
+    int ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
+    if (ret < 0) continue;
 
-        ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
-        if (ret < 0)
-            continue;
+    drm_fd = open(dev_name, O_RDWR, 0);
+    free(dev_name);
+    if (drm_fd < 0) continue;
 
-        drm_fd = open(dev_name, O_RDWR, 0);
-        free(dev_name);
-        if (drm_fd < 0)
-            continue;
-
-        /* We need dumb buffers. */
-        ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
-        if (ret || cap == 0) {
-            close(drm_fd);
-            continue;
-        }
-
-        res = drmModeGetResources(drm_fd);
-        if (!res) {
-            close(drm_fd);
-            continue;
-        }
-
-        /* Use this device if it has at least one connected monitor. */
-        if (res->count_crtcs > 0 && res->count_connectors > 0)
-            if (find_first_connected_connector(drm_fd, res))
-                break;
-
-        drmModeFreeResources(res);
-        close(drm_fd);
-        res = NULL;
+    uint64_t cap = 0;
+    /* We need dumb buffers. */
+    ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
+    if (ret || cap == 0) {
+      close(drm_fd);
+      continue;
     }
 
-    if (drm_fd < 0 || res == NULL) {
-        perror("cannot find/open a drm device");
-        return NULL;
+    res = drmModeGetResources(drm_fd);
+    if (!res) {
+      close(drm_fd);
+      continue;
     }
 
-    main_monitor_connector = find_main_monitor(drm_fd,
-            res, &selected_mode);
-
-    if (!main_monitor_connector) {
-        printf("main_monitor_connector not found\n");
-        drmModeFreeResources(res);
-        close(drm_fd);
-        return NULL;
+    /* Use this device if it has at least one connected monitor. */
+    if (res->count_crtcs > 0 && res->count_connectors > 0) {
+      if (find_first_connected_connector(drm_fd, res)) break;
     }
 
-    main_monitor_crtc = find_crtc_for_connector(drm_fd, res,
-                                                main_monitor_connector);
-
-    if (!main_monitor_crtc) {
-        printf("main_monitor_crtc not found\n");
-        drmModeFreeResources(res);
-        close(drm_fd);
-        return NULL;
-    }
-
-    disable_non_main_crtcs(drm_fd,
-                           res, main_monitor_crtc);
-
-    main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
-
-    width = main_monitor_crtc->mode.hdisplay;
-    height = main_monitor_crtc->mode.vdisplay;
-
     drmModeFreeResources(res);
-
-    drm_surfaces[0] = drm_create_surface(width, height);
-    drm_surfaces[1] = drm_create_surface(width, height);
-    if (!drm_surfaces[0] || !drm_surfaces[1]) {
-        drm_destroy_surface(drm_surfaces[0]);
-        drm_destroy_surface(drm_surfaces[1]);
-        drmModeFreeResources(res);
-        close(drm_fd);
-        return NULL;
-    }
-
-    current_buffer = 0;
-
-    drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]);
-
-    return &(drm_surfaces[0]->base);
-}
-
-static GRSurface* drm_flip(minui_backend* backend __unused) {
-    int ret;
-
-    ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
-                          drm_surfaces[current_buffer]->fb_id, 0, NULL);
-    if (ret < 0) {
-        printf("drmModePageFlip failed ret=%d\n", ret);
-        return NULL;
-    }
-    current_buffer = 1 - current_buffer;
-    return &(drm_surfaces[current_buffer]->base);
-}
-
-static void drm_exit(minui_backend* backend __unused) {
-    drm_disable_crtc(drm_fd, main_monitor_crtc);
-    drm_destroy_surface(drm_surfaces[0]);
-    drm_destroy_surface(drm_surfaces[1]);
-    drmModeFreeCrtc(main_monitor_crtc);
-    drmModeFreeConnector(main_monitor_connector);
     close(drm_fd);
-    drm_fd = -1;
+    res = nullptr;
+  }
+
+  if (drm_fd < 0 || res == nullptr) {
+    perror("cannot find/open a drm device");
+    return nullptr;
+  }
+
+  uint32_t selected_mode;
+  main_monitor_connector = FindMainMonitor(drm_fd, res, &selected_mode);
+
+  if (!main_monitor_connector) {
+    printf("main_monitor_connector not found\n");
+    drmModeFreeResources(res);
+    close(drm_fd);
+    return nullptr;
+  }
+
+  main_monitor_crtc = find_crtc_for_connector(drm_fd, res, main_monitor_connector);
+
+  if (!main_monitor_crtc) {
+    printf("main_monitor_crtc not found\n");
+    drmModeFreeResources(res);
+    close(drm_fd);
+    return nullptr;
+  }
+
+  DisableNonMainCrtcs(drm_fd, res, main_monitor_crtc);
+
+  main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
+
+  int width = main_monitor_crtc->mode.hdisplay;
+  int height = main_monitor_crtc->mode.vdisplay;
+
+  drmModeFreeResources(res);
+
+  GRSurfaceDrms[0] = DrmCreateSurface(width, height);
+  GRSurfaceDrms[1] = DrmCreateSurface(width, height);
+  if (!GRSurfaceDrms[0] || !GRSurfaceDrms[1]) {
+    // GRSurfaceDrms and drm_fd should be freed in d'tor.
+    return nullptr;
+  }
+
+  current_buffer = 0;
+
+  DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[1]);
+
+  return GRSurfaceDrms[0];
 }
 
-static minui_backend drm_backend = {
-    .init = drm_init,
-    .flip = drm_flip,
-    .blank = drm_blank,
-    .exit = drm_exit,
-};
+GRSurface* MinuiBackendDrm::Flip() {
+  int ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
+                            GRSurfaceDrms[current_buffer]->fb_id, 0, nullptr);
+  if (ret < 0) {
+    printf("drmModePageFlip failed ret=%d\n", ret);
+    return nullptr;
+  }
+  current_buffer = 1 - current_buffer;
+  return GRSurfaceDrms[current_buffer];
+}
 
-minui_backend* open_drm() {
-    return &drm_backend;
+MinuiBackendDrm::~MinuiBackendDrm() {
+  DrmDisableCrtc(drm_fd, main_monitor_crtc);
+  DrmDestroySurface(GRSurfaceDrms[0]);
+  DrmDestroySurface(GRSurfaceDrms[1]);
+  drmModeFreeCrtc(main_monitor_crtc);
+  drmModeFreeConnector(main_monitor_connector);
+  close(drm_fd);
+  drm_fd = -1;
 }
diff --git a/minui/graphics_drm.h b/minui/graphics_drm.h
new file mode 100644
index 0000000..de96212
--- /dev/null
+++ b/minui/graphics_drm.h
@@ -0,0 +1,58 @@
+/*
+ * 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 _GRAPHICS_DRM_H_
+#define _GRAPHICS_DRM_H_
+
+#include <stdint.h>
+
+#include <xf86drmMode.h>
+
+#include "graphics.h"
+#include "minui/minui.h"
+
+class GRSurfaceDrm : public GRSurface {
+ private:
+  uint32_t fb_id;
+  uint32_t handle;
+
+  friend class MinuiBackendDrm;
+};
+
+class MinuiBackendDrm : public MinuiBackend {
+ public:
+  GRSurface* Init() override;
+  GRSurface* Flip() override;
+  void Blank(bool) override;
+  ~MinuiBackendDrm() override;
+  MinuiBackendDrm();
+
+ private:
+  void DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc);
+  void DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, GRSurfaceDrm* surface);
+  GRSurfaceDrm* DrmCreateSurface(int width, int height);
+  void DrmDestroySurface(GRSurfaceDrm* surface);
+  void DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc);
+  drmModeConnector* FindMainMonitor(int fd, drmModeRes* resources, uint32_t* mode_index);
+
+  GRSurfaceDrm* GRSurfaceDrms[2];
+  int current_buffer;
+  drmModeCrtc* main_monitor_crtc;
+  drmModeConnector* main_monitor_connector;
+  int drm_fd;
+};
+
+#endif  // _GRAPHICS_DRM_H_
diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp
index 5f64e1e..bb91a57 100644
--- a/minui/graphics_fbdev.cpp
+++ b/minui/graphics_fbdev.cpp
@@ -14,51 +14,23 @@
  * limitations under the License.
  */
 
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include "graphics_fbdev.h"
 
 #include <fcntl.h>
+#include <linux/fb.h>
 #include <stdio.h>
-
-#include <sys/cdefs.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/types.h>
+#include <unistd.h>
 
-#include <linux/fb.h>
-#include <linux/kd.h>
+#include "minui/minui.h"
 
-#include "minui.h"
-#include "graphics.h"
+MinuiBackendFbdev::MinuiBackendFbdev() : gr_draw(nullptr), fb_fd(-1) {}
 
-static GRSurface* fbdev_init(minui_backend*);
-static GRSurface* fbdev_flip(minui_backend*);
-static void fbdev_blank(minui_backend*, bool);
-static void fbdev_exit(minui_backend*);
-
-static GRSurface gr_framebuffer[2];
-static bool double_buffered;
-static GRSurface* gr_draw = NULL;
-static int displayed_buffer;
-
-static fb_var_screeninfo vi;
-static int fb_fd = -1;
-
-static minui_backend my_backend = {
-    .init = fbdev_init,
-    .flip = fbdev_flip,
-    .blank = fbdev_blank,
-    .exit = fbdev_exit,
-};
-
-minui_backend* open_fbdev() {
-    return &my_backend;
-}
-
-static void fbdev_blank(minui_backend* backend __unused, bool blank)
-{
+void MinuiBackendFbdev::Blank(bool blank) {
 #if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS)
     int fd;
     char brightness[4];
@@ -80,136 +52,133 @@
 #endif
 }
 
-static void set_displayed_framebuffer(unsigned n)
-{
-    if (n > 1 || !double_buffered) return;
+void MinuiBackendFbdev::SetDisplayedFramebuffer(unsigned n) {
+  if (n > 1 || !double_buffered) return;
 
-    vi.yres_virtual = gr_framebuffer[0].height * 2;
-    vi.yoffset = n * gr_framebuffer[0].height;
-    vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
-    if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
-        perror("active fb swap failed");
-    }
-    displayed_buffer = n;
+  vi.yres_virtual = gr_framebuffer[0].height * 2;
+  vi.yoffset = n * gr_framebuffer[0].height;
+  vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
+  if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
+    perror("active fb swap failed");
+  }
+  displayed_buffer = n;
 }
 
-static GRSurface* fbdev_init(minui_backend* backend) {
-    int fd = open("/dev/graphics/fb0", O_RDWR);
-    if (fd == -1) {
-        perror("cannot open fb0");
-        return NULL;
+GRSurface* MinuiBackendFbdev::Init() {
+  int fd = open("/dev/graphics/fb0", O_RDWR);
+  if (fd == -1) {
+    perror("cannot open fb0");
+    return nullptr;
+  }
+
+  fb_fix_screeninfo fi;
+  if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
+    perror("failed to get fb0 info");
+    close(fd);
+    return nullptr;
+  }
+
+  if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
+    perror("failed to get fb0 info");
+    close(fd);
+    return nullptr;
+  }
+
+  // We print this out for informational purposes only, but
+  // throughout we assume that the framebuffer device uses an RGBX
+  // pixel format.  This is the case for every development device I
+  // have access to.  For some of those devices (eg, hammerhead aka
+  // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a
+  // different format (XBGR) but actually produces the correct
+  // results on the display when you write RGBX.
+  //
+  // If you have a device that actually *needs* another pixel format
+  // (ie, BGRX, or 565), patches welcome...
+
+  printf(
+      "fb0 reports (possibly inaccurate):\n"
+      "  vi.bits_per_pixel = %d\n"
+      "  vi.red.offset   = %3d   .length = %3d\n"
+      "  vi.green.offset = %3d   .length = %3d\n"
+      "  vi.blue.offset  = %3d   .length = %3d\n",
+      vi.bits_per_pixel, vi.red.offset, vi.red.length, vi.green.offset, vi.green.length,
+      vi.blue.offset, vi.blue.length);
+
+  void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (bits == MAP_FAILED) {
+    perror("failed to mmap framebuffer");
+    close(fd);
+    return nullptr;
+  }
+
+  memset(bits, 0, fi.smem_len);
+
+  gr_framebuffer[0].width = vi.xres;
+  gr_framebuffer[0].height = vi.yres;
+  gr_framebuffer[0].row_bytes = fi.line_length;
+  gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
+  gr_framebuffer[0].data = static_cast<uint8_t*>(bits);
+  memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
+
+  /* check if we can use double buffering */
+  if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
+    double_buffered = true;
+
+    memcpy(gr_framebuffer + 1, gr_framebuffer, sizeof(GRSurface));
+    gr_framebuffer[1].data =
+        gr_framebuffer[0].data + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;
+
+    gr_draw = gr_framebuffer + 1;
+
+  } else {
+    double_buffered = false;
+
+    // Without double-buffering, we allocate RAM for a buffer to
+    // draw in, and then "flipping" the buffer consists of a
+    // memcpy from the buffer we allocated to the framebuffer.
+
+    gr_draw = static_cast<GRSurface*>(malloc(sizeof(GRSurface)));
+    memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface));
+    gr_draw->data = static_cast<unsigned char*>(malloc(gr_draw->height * gr_draw->row_bytes));
+    if (!gr_draw->data) {
+      perror("failed to allocate in-memory surface");
+      return nullptr;
     }
+  }
 
-    fb_fix_screeninfo fi;
-    if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
-        perror("failed to get fb0 info");
-        close(fd);
-        return NULL;
-    }
+  memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
+  fb_fd = fd;
+  SetDisplayedFramebuffer(0);
 
-    if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
-        perror("failed to get fb0 info");
-        close(fd);
-        return NULL;
-    }
+  printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
 
-    // We print this out for informational purposes only, but
-    // throughout we assume that the framebuffer device uses an RGBX
-    // pixel format.  This is the case for every development device I
-    // have access to.  For some of those devices (eg, hammerhead aka
-    // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a
-    // different format (XBGR) but actually produces the correct
-    // results on the display when you write RGBX.
-    //
-    // If you have a device that actually *needs* another pixel format
-    // (ie, BGRX, or 565), patches welcome...
+  Blank(true);
+  Blank(false);
 
-    printf("fb0 reports (possibly inaccurate):\n"
-           "  vi.bits_per_pixel = %d\n"
-           "  vi.red.offset   = %3d   .length = %3d\n"
-           "  vi.green.offset = %3d   .length = %3d\n"
-           "  vi.blue.offset  = %3d   .length = %3d\n",
-           vi.bits_per_pixel,
-           vi.red.offset, vi.red.length,
-           vi.green.offset, vi.green.length,
-           vi.blue.offset, vi.blue.length);
-
-    void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-    if (bits == MAP_FAILED) {
-        perror("failed to mmap framebuffer");
-        close(fd);
-        return NULL;
-    }
-
-    memset(bits, 0, fi.smem_len);
-
-    gr_framebuffer[0].width = vi.xres;
-    gr_framebuffer[0].height = vi.yres;
-    gr_framebuffer[0].row_bytes = fi.line_length;
-    gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
-    gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits);
-    memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
-
-    /* check if we can use double buffering */
-    if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
-        double_buffered = true;
-
-        memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface));
-        gr_framebuffer[1].data = gr_framebuffer[0].data +
-            gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;
-
-        gr_draw = gr_framebuffer+1;
-
-    } else {
-        double_buffered = false;
-
-        // Without double-buffering, we allocate RAM for a buffer to
-        // draw in, and then "flipping" the buffer consists of a
-        // memcpy from the buffer we allocated to the framebuffer.
-
-        gr_draw = (GRSurface*) malloc(sizeof(GRSurface));
-        memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface));
-        gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes);
-        if (!gr_draw->data) {
-            perror("failed to allocate in-memory surface");
-            return NULL;
-        }
-    }
-
-    memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
-    fb_fd = fd;
-    set_displayed_framebuffer(0);
-
-    printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
-
-    fbdev_blank(backend, true);
-    fbdev_blank(backend, false);
-
-    return gr_draw;
+  return gr_draw;
 }
 
-static GRSurface* fbdev_flip(minui_backend* backend __unused) {
-    if (double_buffered) {
-        // Change gr_draw to point to the buffer currently displayed,
-        // then flip the driver so we're displaying the other buffer
-        // instead.
-        gr_draw = gr_framebuffer + displayed_buffer;
-        set_displayed_framebuffer(1-displayed_buffer);
-    } else {
-        // Copy from the in-memory surface to the framebuffer.
-        memcpy(gr_framebuffer[0].data, gr_draw->data,
-               gr_draw->height * gr_draw->row_bytes);
-    }
-    return gr_draw;
+GRSurface* MinuiBackendFbdev::Flip() {
+  if (double_buffered) {
+    // Change gr_draw to point to the buffer currently displayed,
+    // then flip the driver so we're displaying the other buffer
+    // instead.
+    gr_draw = gr_framebuffer + displayed_buffer;
+    SetDisplayedFramebuffer(1 - displayed_buffer);
+  } else {
+    // Copy from the in-memory surface to the framebuffer.
+    memcpy(gr_framebuffer[0].data, gr_draw->data, gr_draw->height * gr_draw->row_bytes);
+  }
+  return gr_draw;
 }
 
-static void fbdev_exit(minui_backend* backend __unused) {
-    close(fb_fd);
-    fb_fd = -1;
+MinuiBackendFbdev::~MinuiBackendFbdev() {
+  close(fb_fd);
+  fb_fd = -1;
 
-    if (!double_buffered && gr_draw) {
-        free(gr_draw->data);
-        free(gr_draw);
-    }
-    gr_draw = NULL;
+  if (!double_buffered && gr_draw) {
+    free(gr_draw->data);
+    free(gr_draw);
+  }
+  gr_draw = nullptr;
 }
diff --git a/minui/graphics_fbdev.h b/minui/graphics_fbdev.h
new file mode 100644
index 0000000..107e195
--- /dev/null
+++ b/minui/graphics_fbdev.h
@@ -0,0 +1,44 @@
+/*
+ * 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 _GRAPHICS_FBDEV_H_
+#define _GRAPHICS_FBDEV_H_
+
+#include <linux/fb.h>
+
+#include "graphics.h"
+#include "minui/minui.h"
+
+class MinuiBackendFbdev : public MinuiBackend {
+ public:
+  GRSurface* Init() override;
+  GRSurface* Flip() override;
+  void Blank(bool) override;
+  ~MinuiBackendFbdev() override;
+  MinuiBackendFbdev();
+
+ private:
+  void SetDisplayedFramebuffer(unsigned n);
+
+  GRSurface gr_framebuffer[2];
+  bool double_buffered;
+  GRSurface* gr_draw;
+  int displayed_buffer;
+  fb_var_screeninfo vi;
+  int fb_fd;
+};
+
+#endif  // _GRAPHICS_FBDEV_H_
diff --git a/minui/graphics_overlay.cpp b/minui/graphics_overlay.cpp
index 978a3bb..39ec5e4 100644
--- a/minui/graphics_overlay.cpp
+++ b/minui/graphics_overlay.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "graphics_overlay.h"
+
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,50 +36,28 @@
 #ifdef MSM_BSP
 #include <linux/msm_mdp.h>
 #include <linux/msm_ion.h>
+#else
+#define MSMFB_NEW_REQUEST 0
 #endif
 
-#include "minui.h"
-#include "graphics.h"
+#include "minui/minui.h"
 
 #define MDP_V4_0 400
 #define MAX_DISPLAY_DIM  2048
-
-static GRSurface* overlay_init(minui_backend*);
-static GRSurface* overlay_flip(minui_backend*);
-static void overlay_blank(minui_backend*, bool);
-static void overlay_exit(minui_backend*);
-
-static GRSurface gr_framebuffer[2];
-static bool double_buffered;
-static GRSurface* gr_draw = NULL;
-static int displayed_buffer;
-
-static fb_var_screeninfo vi;
-static int fb_fd = -1;
-static bool isMDP5 = false;
-static int leftSplit = 0;
-static int rightSplit = 0;
 #define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1))
 
-static size_t frame_size = 0;
+MinuiBackendOverlay::MinuiBackendOverlay() :
+  gr_draw(nullptr),
+  fb_fd(-1),
+  isMDP5(false),
+  leftSplit(0),
+  rightSplit(0),
+  frame_size(0),
+  overlayL_id(MSMFB_NEW_REQUEST),
+  overlayR_id(MSMFB_NEW_REQUEST) {}
 
 #ifdef MSM_BSP
-typedef struct {
-    unsigned char *mem_buf;
-    int size;
-    int ion_fd;
-    int mem_fd;
-    struct ion_handle_data handle_data;
-} memInfo;
-
-//Left and right overlay id
-static int overlayL_id = MSMFB_NEW_REQUEST;
-static int overlayR_id = MSMFB_NEW_REQUEST;
-
-static memInfo mem_info;
-#
-
-static int map_mdp_pixel_format()
+int MinuiBackendOverlay::map_mdp_pixel_format()
 {
     int format = MDP_RGB_565;
 #if defined(RECOVERY_BGRA)
@@ -91,66 +71,49 @@
 }
 #endif // MSM_BSP
 
-static minui_backend my_backend = {
-    .init = overlay_init,
-    .flip = overlay_flip,
-    .blank = overlay_blank,
-    .exit = overlay_exit,
-};
-
-bool target_has_overlay(char *version)
+bool MinuiBackendOverlay::target_has_overlay()
 {
     int ret;
     int mdp_version;
     bool overlay_supported = false;
-
-    if (strlen(version) >= 8) {
-        if(!strncmp(version, "msmfb", strlen("msmfb"))) {
-            char str_ver[4];
-            memcpy(str_ver, version + strlen("msmfb"), 3);
-            str_ver[3] = '\0';
-            mdp_version = atoi(str_ver);
-            if (mdp_version >= MDP_V4_0) {
-                overlay_supported = true;
-            }
-        } else if (!strncmp(version, "mdssfb", strlen("mdssfb"))) {
-            overlay_supported = true;
-            isMDP5 = true;
-        }
-    }
-
-    return overlay_supported;
-}
-
-minui_backend* open_overlay() {
     fb_fix_screeninfo fi;
     int fd;
 
     fd = open("/dev/graphics/fb0", O_RDWR);
     if (fd < 0) {
         perror("open_overlay cannot open fb0");
-        return NULL;
+        return false;
     }
 
     if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
         perror("failed to get fb0 info");
         close(fd);
-        return NULL;
-    }
-
-    if (target_has_overlay(fi.id)) {
-#ifdef MSM_BSP
-        close(fd);
-        return &my_backend;
-#else
-        printf("Overlay graphics may work (%s), but not enabled. Use TW_TARGET_USES_QCOM_BSP := true to enable.\n", fi.id);
-#endif
+        return false;
     }
     close(fd);
-    return NULL;
+
+    if (strlen(fi.id) >= 8) {
+        if(!strncmp(fi.id, "msmfb", strlen("msmfb"))) {
+            char str_ver[4];
+            memcpy(str_ver, fi.id + strlen("msmfb"), 3);
+            str_ver[3] = '\0';
+            mdp_version = atoi(str_ver);
+            if (mdp_version >= MDP_V4_0) {
+                overlay_supported = true;
+            }
+        } else if (!strncmp(fi.id, "mdssfb", strlen("mdssfb"))) {
+            overlay_supported = true;
+            isMDP5 = true;
+        }
+    }
+#ifndef MSM_BSP
+    if (overlay_supported)
+        printf("Overlay graphics may work (%s), but not enabled. Use TW_TARGET_USES_QCOM_BSP := true to enable.\n", fi.id);
+#endif
+    return overlay_supported;
 }
 
-static void overlay_blank(minui_backend* backend __unused, bool blank)
+void MinuiBackendOverlay::Blank(bool blank)
 {
 #if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS)
     int fd;
@@ -173,7 +136,7 @@
 #endif
 }
 
-static void set_displayed_framebuffer(unsigned n)
+void MinuiBackendOverlay::SetDisplayedFramebuffer(unsigned n)
 {
     if (n > 1 || !double_buffered) return;
 
@@ -187,7 +150,7 @@
 }
 
 #ifdef MSM_BSP
-void setDisplaySplit(void) {
+void MinuiBackendOverlay::setDisplaySplit(void) {
     char split[64] = {0};
     if (!isMDP5)
         return;
@@ -209,7 +172,7 @@
         fclose(fp);
 }
 
-int getLeftSplit(void) {
+int MinuiBackendOverlay::getLeftSplit(void) {
    //Default even split for all displays with high res
    int lSplit = vi.xres / 2;
 
@@ -220,11 +183,11 @@
    return lSplit;
 }
 
-int getRightSplit(void) {
+int MinuiBackendOverlay::getRightSplit(void) {
    return rightSplit;
 }
 
-int free_ion_mem(void) {
+int MinuiBackendOverlay::free_ion_mem(void) {
     int ret = 0;
 
     if (mem_info.mem_buf)
@@ -247,7 +210,7 @@
     return 0;
 }
 
-int alloc_ion_mem(unsigned int size)
+int MinuiBackendOverlay::alloc_ion_mem(unsigned int size)
 {
     int result;
     struct ion_fd_data fd_data;
@@ -298,7 +261,7 @@
     return 0;
 }
 
-bool isDisplaySplit(void) {
+bool MinuiBackendOverlay::isDisplaySplit(void) {
     if (vi.xres > MAX_DISPLAY_DIM)
         return true;
     //check if right split is set by driver
@@ -308,7 +271,7 @@
     return false;
 }
 
-int allocate_overlay(int fd, GRSurface gr_fb[])
+int MinuiBackendOverlay::allocate_overlay(int fd, GRSurface gr_fb[])
 {
     int ret = 0;
 
@@ -407,7 +370,7 @@
     return 0;
 }
 
-int overlay_display_frame(int fd, void* data, size_t size)
+int MinuiBackendOverlay::overlay_display_frame(int fd, void* data, size_t size)
 {
     int ret = 0;
     struct msmfb_overlay_data ovdataL, ovdataR;
@@ -480,7 +443,7 @@
     return ret;
 }
 
-static GRSurface* overlay_flip(minui_backend* backend __unused) {
+GRSurface* MinuiBackendOverlay::Flip() {
     if (double_buffered) {
 #if defined(RECOVERY_BGRA)
         // In case of BGRA, do some byte swapping
@@ -507,7 +470,7 @@
     return gr_draw;
 }
 
-int free_overlay(int fd)
+int MinuiBackendOverlay::free_overlay(int fd)
 {
     int ret = 0;
     struct mdp_display_commit ext_commit;
@@ -557,7 +520,10 @@
     return 0;
 }
 
-static GRSurface* overlay_init(minui_backend* backend) {
+GRSurface* MinuiBackendOverlay::Init() {
+	if (!target_has_overlay())
+	    return NULL;
+
     int fd = open("/dev/graphics/fb0", O_RDWR);
     if (fd == -1) {
         perror("cannot open fb0");
@@ -662,7 +628,7 @@
     return gr_draw;
 }
 
-static void overlay_exit(minui_backend* backend __unused) {
+MinuiBackendOverlay::~MinuiBackendOverlay() {
     free_overlay(fb_fd);
     free_ion_mem();
 
@@ -680,15 +646,17 @@
     }
 }
 #else // MSM_BSP
-static GRSurface* overlay_flip(minui_backend* backend __unused) {
+
+GRSurface* MinuiBackendOverlay::Flip() {
     return NULL;
 }
 
-static GRSurface* overlay_init(minui_backend* backend __unused) {
+GRSurface* MinuiBackendOverlay::Init() {
+	target_has_overlay(); // Don't care about return value, just for logging
     return NULL;
 }
 
-static void overlay_exit(minui_backend* backend __unused) {
+MinuiBackendOverlay::~MinuiBackendOverlay() {
     return;
 }
 #endif // MSM_BSP
diff --git a/minui/graphics_overlay.h b/minui/graphics_overlay.h
new file mode 100644
index 0000000..56f7401
--- /dev/null
+++ b/minui/graphics_overlay.h
@@ -0,0 +1,74 @@
+/*
+ * 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 _GRAPHICS_OVERLAY_H_
+#define _GRAPHICS_OVERLAY_H_
+
+#include <linux/fb.h>
+
+#include "graphics.h"
+#include "minui/minui.h"
+
+#ifdef MSM_BSP
+typedef struct {
+    unsigned char *mem_buf;
+    int size;
+    int ion_fd;
+    int mem_fd;
+    struct ion_handle_data handle_data;
+} memInfo;
+#endif
+
+class MinuiBackendOverlay : public MinuiBackend {
+ public:
+  GRSurface* Init() override;
+  GRSurface* Flip() override;
+  void Blank(bool) override;
+  ~MinuiBackendOverlay() override;
+  MinuiBackendOverlay();
+
+ private:
+  void SetDisplayedFramebuffer(unsigned n);
+  bool target_has_overlay();
+
+#ifdef MSM_BSP
+  int map_mdp_pixel_format();
+  void setDisplaySplit(void);
+  int getLeftSplit(void);
+  int getRightSplit(void);
+  int free_ion_mem(void);
+  int alloc_ion_mem(unsigned int size);
+  bool isDisplaySplit(void);
+  int allocate_overlay(int fd, GRSurface gr_fb[]);
+  int overlay_display_frame(int fd, void* data, size_t size);
+  int free_overlay(int fd);
+#endif
+
+  GRSurface gr_framebuffer[2];
+  bool double_buffered;
+  GRSurface* gr_draw;
+  int displayed_buffer;
+  fb_var_screeninfo vi;
+  int fb_fd;
+  bool isMDP5;
+  int leftSplit;
+  int rightSplit;
+  size_t frame_size;
+  int overlayL_id;
+  int overlayR_id;
+};
+
+#endif  // _GRAPHICS_OVERLAY_H_
diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h
new file mode 100644
index 0000000..766b943
--- /dev/null
+++ b/minui/include/minui/minui.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2007 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 _MINUI_H_
+#define _MINUI_H_
+
+#ifndef TW_USE_MINUI_21
+
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+
+//
+// Graphics.
+//
+
+struct GRSurface {
+    int width;
+    int height;
+    int row_bytes;
+    int pixel_bytes;
+    unsigned char* data;
+};
+
+struct GRFont {
+    GRSurface* texture;
+    int char_width;
+    int char_height;
+};
+
+int gr_init();
+void gr_exit();
+
+int gr_fb_width();
+int gr_fb_height();
+
+void gr_flip();
+void gr_fb_blank(bool blank);
+
+void gr_clear();  // clear entire surface to current color
+void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
+void gr_fill(int x1, int y1, int x2, int y2);
+
+void gr_texticon(int x, int y, GRSurface* icon);
+#ifdef TW_NO_MINUI_CUSTOM_FONTS
+void gr_text(int x, int y, const char *s, bool bold);
+int gr_measure(const char *s);
+void gr_font_size(int *x, int *y);
+void gr_set_font(__attribute__ ((unused))const char* name);
+#else
+
+const GRFont* gr_sys_font();
+int gr_init_font(const char* name, GRFont** dest);
+void gr_text(const GRFont* font, int x, int y, const char *s, bool bold);
+int gr_measure(const GRFont* font, const char *s);
+void gr_font_size(const GRFont* font, int *x, int *y);
+#endif
+
+void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy);
+unsigned int gr_get_width(GRSurface* surface);
+unsigned int gr_get_height(GRSurface* surface);
+
+//
+// Input events.
+//
+
+struct input_event;
+
+#ifdef TW_USE_MINUI_WITH_DATA
+typedef int (*ev_callback)(int fd, uint32_t epevents, void* data);
+typedef int (*ev_set_key_callback)(int code, int value, void* data);
+
+int ev_init(ev_callback input_cb, void* data);
+int ev_add_fd(int fd, ev_callback cb, void* data);
+int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);
+#else
+using ev_callback = std::function<int(int fd, uint32_t epevents)>;
+using ev_set_key_callback = std::function<int(int code, int value)>;
+
+int ev_init(ev_callback input_cb);
+int ev_add_fd(int fd, ev_callback cb);
+int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
+#endif
+void ev_exit();
+void ev_iterate_available_keys(const std::function<void(int)>& f);
+
+// 'timeout' has the same semantics as poll(2).
+//    0 : don't block
+//  < 0 : block forever
+//  > 0 : block for 'timeout' milliseconds
+int ev_wait(int timeout);
+
+int ev_get_input(int fd, uint32_t epevents, input_event* ev);
+void ev_dispatch();
+int ev_get_epollfd();
+
+//
+// Resources
+//
+
+bool matches_locale(const std::string& prefix, const std::string& locale);
+
+// res_create_*_surface() functions return 0 if no error, else
+// negative.
+//
+// A "display" surface is one that is intended to be drawn to the
+// screen with gr_blit().  An "alpha" surface is a grayscale image
+// interpreted as an alpha mask used to render text in the current
+// color (with gr_text() or gr_texticon()).
+//
+// All these functions load PNG images from "/res/images/${name}.png".
+
+// Load a single display surface from a PNG image.
+int res_create_display_surface(const char* name, GRSurface** pSurface);
+
+// Load an array of display surfaces from a single PNG image.  The PNG
+// should have a 'Frames' text chunk whose value is the number of
+// frames this image represents.  The pixel data itself is interlaced
+// by row.
+int res_create_multi_display_surface(const char* name, int* frames,
+                                     int* fps, GRSurface*** pSurface);
+int res_create_multi_display_surface(const char* name, int* frames,
+                                     GRSurface*** pSurface);
+
+// Load a single alpha surface from a grayscale PNG image.
+int res_create_alpha_surface(const char* name, GRSurface** pSurface);
+
+// Load part of a grayscale PNG image that is the first match for the
+// given locale.  The image is expected to be a composite of multiple
+// translations of the same text, with special added rows that encode
+// the subimages' size and intended locale in the pixel data.  See
+// bootable/recovery/tools/recovery_l10n for an app that will generate
+// these specialized images from Android resources.
+int res_create_localized_alpha_surface(const char* name, const char* locale,
+                                       GRSurface** pSurface);
+
+// Free a surface allocated by any of the res_create_*_surface()
+// functions.
+void res_free_surface(GRSurface* surface);
+
+#else //ifndef TW_USE_MINUI_21
+
+// This the old minui21/minui.h for compatibility with building TWRP
+// in pre 6.0 trees.
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* gr_surface;
+typedef unsigned short gr_pixel;
+
+int gr_init(void);
+void gr_exit(void);
+
+int gr_fb_width(void);
+int gr_fb_height(void);
+gr_pixel *gr_fb_data(void);
+void gr_flip(void);
+void gr_fb_blank(bool blank);
+
+void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
+void gr_fill(int x1, int y1, int x2, int y2);
+
+// system/core/charger uses different gr_print signatures in diferent
+// Android versions, either with or without int bold.
+int gr_text(int x, int y, const char *s, ...);
+int gr_text_impl(int x, int y, const char *s, int bold);
+
+ void gr_texticon(int x, int y, gr_surface icon);
+int gr_measure(const char *s);
+void gr_font_size(int *x, int *y);
+void gr_get_memory_surface(gr_surface);
+
+void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);
+unsigned int gr_get_width(gr_surface surface);
+unsigned int gr_get_height(gr_surface surface);
+
+// input event structure, include <linux/input.h> for the definition.
+// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
+struct input_event;
+
+typedef int (*ev_callback)(int fd, uint32_t epevents, void *data);
+typedef int (*ev_set_key_callback)(int code, int value, void *data);
+
+int ev_init(ev_callback input_cb, void *data);
+void ev_exit(void);
+int ev_add_fd(int fd, ev_callback cb, void *data);
+int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data);
+
+/* timeout has the same semantics as for poll
+ *    0 : don't block
+ *  < 0 : block forever
+ *  > 0 : block for 'timeout' milliseconds
+ */
+int ev_wait(int timeout);
+
+int ev_get_input(int fd, uint32_t epevents, struct input_event *ev);
+void ev_dispatch(void);
+int ev_get_epollfd(void);
+
+// Resources
+
+// Returns 0 if no error, else negative.
+int res_create_surface(const char* name, gr_surface* pSurface);
+
+// Load an array of display surfaces from a single PNG image.  The PNG
+// should have a 'Frames' text chunk whose value is the number of
+// frames this image represents.  The pixel data itself is interlaced
+// by row.
+int res_create_multi_display_surface(const char* name,
+                                     int* frames, gr_surface** pSurface);
+
+int res_create_localized_surface(const char* name, gr_surface* pSurface);
+void res_free_surface(gr_surface surface);
+static inline int res_create_display_surface(const char* name, gr_surface* pSurface) {
+    return res_create_surface(name, pSurface);
+}
+
+// These are new graphics functions from 5.0 that were not available in
+// 4.4 that are required by charger and healthd
+void gr_clear();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ifndef TW_USE_MINUI_21
+#endif // ifndef _MINUI_H_
diff --git a/minui/main.cpp b/minui/main.cpp
index 74b830e..13fee87 100644
--- a/minui/main.cpp
+++ b/minui/main.cpp
@@ -15,7 +15,7 @@
 
 #include <time.h>
 
-#include "minui.h"
+#include <minui/minui.h>
 #include "graphics.h"
 
 int main() {
diff --git a/minui/minui.h b/minui/minui.h
index bf79601..766b943 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 
 #include <functional>
+#include <string>
 
 //
 // Graphics.
@@ -79,15 +80,23 @@
 
 struct input_event;
 
-// TODO: move these over to std::function.
+#ifdef TW_USE_MINUI_WITH_DATA
 typedef int (*ev_callback)(int fd, uint32_t epevents, void* data);
 typedef int (*ev_set_key_callback)(int code, int value, void* data);
 
 int ev_init(ev_callback input_cb, void* data);
-void ev_exit();
 int ev_add_fd(int fd, ev_callback cb, void* data);
-void ev_iterate_available_keys(std::function<void(int)> f);
 int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);
+#else
+using ev_callback = std::function<int(int fd, uint32_t epevents)>;
+using ev_set_key_callback = std::function<int(int code, int value)>;
+
+int ev_init(ev_callback input_cb);
+int ev_add_fd(int fd, ev_callback cb);
+int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
+#endif
+void ev_exit();
+void ev_iterate_available_keys(const std::function<void(int)>& f);
 
 // 'timeout' has the same semantics as poll(2).
 //    0 : don't block
@@ -103,7 +112,7 @@
 // Resources
 //
 
-bool matches_locale(const char* prefix, const char* locale);
+bool matches_locale(const std::string& prefix, const std::string& locale);
 
 // res_create_*_surface() functions return 0 if no error, else
 // negative.
@@ -134,8 +143,8 @@
 // given locale.  The image is expected to be a composite of multiple
 // translations of the same text, with special added rows that encode
 // the subimages' size and intended locale in the pixel data.  See
-// development/tools/recovery_l10n for an app that will generate these
-// specialized images from Android resources.
+// bootable/recovery/tools/recovery_l10n for an app that will generate
+// these specialized images from Android resources.
 int res_create_localized_alpha_surface(const char* name, const char* locale,
                                        GRSurface** pSurface);
 
diff --git a/minui/resources.cpp b/minui/resources.cpp
index e25512f..026e1dc 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -14,29 +14,31 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
+#include <linux/fb.h>
+#include <linux/kd.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 <unistd.h>
 
-#include <linux/fb.h>
-#include <linux/kd.h>
+#include <regex>
+#include <string>
+#include <vector>
 
+//#include <android-base/strings.h> // does not exist in 6.0
 #include <png.h>
 
-#include "minui.h"
+#include "minui/minui.h"
 
 #define SURFACE_DATA_ALIGNMENT 8
 
 static GRSurface* malloc_surface(size_t data_size) {
     size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT;
-    unsigned char* temp = reinterpret_cast<unsigned char*>(malloc(size));
+    unsigned char* temp = static_cast<unsigned char*>(malloc(size));
     if (temp == NULL) return NULL;
     GRSurface* surface = reinterpret_cast<GRSurface*>(temp);
     surface->data = temp + sizeof(GRSurface) +
@@ -220,7 +222,7 @@
     png_set_bgr(png_ptr);
 #endif
 
-    p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
+    p_row = static_cast<unsigned char*>(malloc(width * 4));
     for (y = 0; y < height; ++y) {
         png_read_row(png_ptr, p_row, NULL);
         transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width);
@@ -280,7 +282,7 @@
         goto exit;
     }
 
-    surface = reinterpret_cast<GRSurface**>(malloc(*frames * sizeof(GRSurface*)));
+    surface = static_cast<GRSurface**>(calloc(*frames, sizeof(GRSurface*)));
     if (surface == NULL) {
         result = -8;
         goto exit;
@@ -297,7 +299,7 @@
     png_set_bgr(png_ptr);
 #endif
 
-    p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
+    p_row = static_cast<unsigned char*>(malloc(width * 4));
     for (y = 0; y < height; ++y) {
         png_read_row(png_ptr, p_row, NULL);
         int frame = y % *frames;
@@ -307,7 +309,7 @@
     }
     free(p_row);
 
-    *pSurface = reinterpret_cast<GRSurface**>(surface);
+    *pSurface = surface;
 
 exit:
     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
@@ -315,7 +317,7 @@
     if (result < 0) {
         if (surface) {
             for (int i = 0; i < *frames; ++i) {
-                if (surface[i]) free(surface[i]);
+                free(surface[i]);
             }
             free(surface);
         }
@@ -378,14 +380,27 @@
 
 // This function tests if a locale string stored in PNG (prefix) matches
 // the locale string provided by the system (locale).
-bool matches_locale(const char* prefix, const char* locale) {
-    if (locale == NULL) return false;
+bool matches_locale(const std::string& prefix, const std::string& locale) {
+  // According to the BCP 47 format, A locale string may consists of:
+  // language-{extlang}-{script}-{region}-{variant}
+  // The locale headers in PNG mostly consist of language-{region} except for sr-Latn, and some
+  // android's system locale can have the format language-{script}-{region}.
 
-    // Return true if the whole string of prefix matches the top part of
-    // locale. For instance, prefix == "en" matches locale == "en_US";
-    // and prefix == "zh_CN" matches locale == "zh_CN_#Hans".
+  // Return true if the whole string of prefix matches the top part of locale. Otherwise try to
+  // match the locale string without the {script} section.
+  // For instance, prefix == "en" matches locale == "en-US", prefix == "sr-Latn" matches locale
+  // == "sr-Latn-BA", and prefix == "zh-CN" matches locale == "zh-Hans-CN".
+  //if (android::base::StartsWith(locale, prefix.c_str())) { // does not exist in 6.0
+  if (strncmp(prefix.c_str(), locale.c_str(), prefix.length()) == 0) {
+    return true;
+  }
 
-    return (strncmp(prefix, locale, strlen(prefix)) == 0);
+  size_t separator = prefix.find('-');
+  if (separator == std::string::npos) {
+    return false;
+  }
+  std::regex loc_regex(prefix.substr(0, separator) + "-[A-Za-z]*" + prefix.substr(separator));
+  return std::regex_match(locale, loc_regex);
 }
 
 int res_create_localized_alpha_surface(const char* name,
@@ -397,18 +412,13 @@
     png_infop info_ptr = NULL;
     png_uint_32 width, height;
     png_byte channels;
-    unsigned char* row;
     png_uint_32 y;
+    std::vector<unsigned char> row;
 
     *pSurface = NULL;
 
     if (locale == NULL) {
-        surface = malloc_surface(0);
-        surface->width = 0;
-        surface->height = 0;
-        surface->row_bytes = 0;
-        surface->pixel_bytes = 1;
-        goto exit;
+        return result;
     }
 
     result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
@@ -419,13 +429,13 @@
         goto exit;
     }
 
-    row = reinterpret_cast<unsigned char*>(malloc(width));
+    row.resize(width);
     for (y = 0; y < height; ++y) {
-        png_read_row(png_ptr, row, NULL);
+        png_read_row(png_ptr, row.data(), NULL);
         int w = (row[1] << 8) | row[0];
         int h = (row[3] << 8) | row[2];
-        int len = row[4];
-        char* loc = (char*)row+5;
+        __unused int len = row[4];
+        char* loc = reinterpret_cast<char*>(&row[5]);
 
         if (y+1+h >= height || matches_locale(loc, locale)) {
             printf("  %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
@@ -442,16 +452,16 @@
 
             int i;
             for (i = 0; i < h; ++i, ++y) {
-                png_read_row(png_ptr, row, NULL);
-                memcpy(surface->data + i*w, row, w);
+                png_read_row(png_ptr, row.data(), NULL);
+                memcpy(surface->data + i*w, row.data(), w);
             }
 
-            *pSurface = reinterpret_cast<GRSurface*>(surface);
+            *pSurface = surface;
             break;
         } else {
             int i;
             for (i = 0; i < h; ++i, ++y) {
-                png_read_row(png_ptr, row, NULL);
+                png_read_row(png_ptr, row.data(), NULL);
             }
         }
     }
diff --git a/minuitwrp/Android.mk b/minuitwrp/Android.mk
index 054dd45..3f83c97 100644
--- a/minuitwrp/Android.mk
+++ b/minuitwrp/Android.mk
@@ -180,6 +180,9 @@
     LOCAL_SHARED_LIBRARIES += libjpeg
 endif
 LOCAL_STATIC_LIBRARIES += libpixelflinger_twrp
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 25; echo $$?),0)
+LOCAL_SHARED_LIBRARIES += libcutils liblog libutils
+endif
 LOCAL_MODULE_TAGS := eng
 LOCAL_MODULE := libminuitwrp
 
diff --git a/mounts.cpp b/mounts.cpp
new file mode 100644
index 0000000..f23376b
--- /dev/null
+++ b/mounts.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2007 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 "mounts.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+
+#include <string>
+#include <vector>
+
+struct MountedVolume {
+    std::string device;
+    std::string mount_point;
+    std::string filesystem;
+    std::string flags;
+};
+
+std::vector<MountedVolume*> g_mounts_state;
+
+bool scan_mounted_volumes() {
+    for (size_t i = 0; i < g_mounts_state.size(); ++i) {
+        delete g_mounts_state[i];
+    }
+    g_mounts_state.clear();
+
+    // Open and read mount table entries.
+    FILE* fp = setmntent("/proc/mounts", "re");
+    if (fp == NULL) {
+        return false;
+    }
+    mntent* e;
+    while ((e = getmntent(fp)) != NULL) {
+        MountedVolume* v = new MountedVolume;
+        v->device = e->mnt_fsname;
+        v->mount_point = e->mnt_dir;
+        v->filesystem = e->mnt_type;
+        v->flags = e->mnt_opts;
+        g_mounts_state.push_back(v);
+    }
+    endmntent(fp);
+    return true;
+}
+
+MountedVolume* find_mounted_volume_by_device(const char* device) {
+    for (size_t i = 0; i < g_mounts_state.size(); ++i) {
+        if (g_mounts_state[i]->device == device) return g_mounts_state[i];
+    }
+    return nullptr;
+}
+
+MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point) {
+    for (size_t i = 0; i < g_mounts_state.size(); ++i) {
+        if (g_mounts_state[i]->mount_point == mount_point) return g_mounts_state[i];
+    }
+    return nullptr;
+}
+
+int unmount_mounted_volume(MountedVolume* volume) {
+    // Intentionally pass the empty string to umount if the caller tries
+    // to unmount a volume they already unmounted using this
+    // function.
+    std::string mount_point = volume->mount_point;
+    volume->mount_point.clear();
+    return umount(mount_point.c_str());
+}
+
+int remount_read_only(MountedVolume* volume) {
+    return mount(volume->device.c_str(), volume->mount_point.c_str(), volume->filesystem.c_str(),
+                 MS_NOATIME | MS_NODEV | MS_NODIRATIME | MS_RDONLY | MS_REMOUNT, 0);
+}
diff --git a/mounts.h b/mounts.h
index ed7fb5f..1b76703 100644
--- a/mounts.h
+++ b/mounts.h
@@ -14,25 +14,19 @@
  * limitations under the License.
  */
 
-#ifndef MTDUTILS_MOUNTS_H_
-#define MTDUTILS_MOUNTS_H_
+#ifndef MOUNTS_H_
+#define MOUNTS_H_
 
-typedef struct {
- const char *device;
- const char *mount_point;
- const char *filesystem;
- const char *flags;
-} MountedVolume;
+struct MountedVolume;
 
-int scan_mounted_volumes(void);
+bool scan_mounted_volumes();
 
-const MountedVolume *find_mounted_volume_by_device(const char *device);
+MountedVolume* find_mounted_volume_by_device(const char* device);
 
-const MountedVolume *
-find_mounted_volume_by_mount_point(const char *mount_point);
+MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point);
 
-int unmount_mounted_volume(const MountedVolume *volume);
+int unmount_mounted_volume(MountedVolume* volume);
 
-int remount_read_only(const MountedVolume* volume);
+int remount_read_only(MountedVolume* volume);
 
-#endif  // MTDUTILS_MOUNTS_H_
+#endif
diff --git a/mounts.h~HEAD b/mounts.h~HEAD
new file mode 100644
index 0000000..ed7fb5f
--- /dev/null
+++ b/mounts.h~HEAD
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 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 MTDUTILS_MOUNTS_H_
+#define MTDUTILS_MOUNTS_H_
+
+typedef struct {
+ const char *device;
+ const char *mount_point;
+ const char *filesystem;
+ const char *flags;
+} MountedVolume;
+
+int scan_mounted_volumes(void);
+
+const MountedVolume *find_mounted_volume_by_device(const char *device);
+
+const MountedVolume *
+find_mounted_volume_by_mount_point(const char *mount_point);
+
+int unmount_mounted_volume(const MountedVolume *volume);
+
+int remount_read_only(const MountedVolume* volume);
+
+#endif  // MTDUTILS_MOUNTS_H_
diff --git a/mtdutils/flash_image.c b/mtdutils/flash_image.c
deleted file mode 100644
index 36ffa13..0000000
--- a/mtdutils/flash_image.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2008 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 <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "cutils/log.h"
-#include "mtdutils.h"
-
-#ifdef LOG_TAG
-#undef LOG_TAG
-#endif
-#define LOG_TAG "flash_image"
-
-#define HEADER_SIZE 2048  // size of header to compare for equality
-
-void die(const char *msg, ...) {
-    int err = errno;
-    va_list args;
-    va_start(args, msg);
-    char buf[1024];
-    vsnprintf(buf, sizeof(buf), msg, args);
-    va_end(args);
-
-    if (err != 0) {
-        strlcat(buf, ": ", sizeof(buf));
-        strlcat(buf, strerror(err), sizeof(buf));
-    }
-
-    fprintf(stderr, "%s\n", buf);
-    ALOGE("%s\n", buf);
-    exit(1);
-}
-
-/* Read an image file and write it to a flash partition. */
-
-int main(int argc, char **argv) {
-    const MtdPartition *ptn;
-    MtdWriteContext *write;
-    void *data;
-    unsigned sz;
-
-    if (argc != 3) {
-        fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
-        return 2;
-    }
-
-    if (mtd_scan_partitions() <= 0) die("error scanning partitions");
-    const MtdPartition *partition = mtd_find_partition_by_name(argv[1]);
-    if (partition == NULL) die("can't find %s partition", argv[1]);
-
-    // If the first part of the file matches the partition, skip writing
-
-    int fd = open(argv[2], O_RDONLY);
-    if (fd < 0) die("error opening %s", argv[2]);
-
-    char header[HEADER_SIZE];
-    int headerlen = TEMP_FAILURE_RETRY(read(fd, header, sizeof(header)));
-    if (headerlen <= 0) die("error reading %s header", argv[2]);
-
-    MtdReadContext *in = mtd_read_partition(partition);
-    if (in == NULL) {
-        ALOGW("error opening %s: %s\n", argv[1], strerror(errno));
-        // just assume it needs re-writing
-    } else {
-        char check[HEADER_SIZE];
-        int checklen = mtd_read_data(in, check, sizeof(check));
-        if (checklen <= 0) {
-            ALOGW("error reading %s: %s\n", argv[1], strerror(errno));
-            // just assume it needs re-writing
-        } else if (checklen == headerlen && !memcmp(header, check, headerlen)) {
-            ALOGI("header is the same, not flashing %s\n", argv[1]);
-            return 0;
-        }
-        mtd_read_close(in);
-    }
-
-    // Skip the header (we'll come back to it), write everything else
-    ALOGI("flashing %s from %s\n", argv[1], argv[2]);
-
-    MtdWriteContext *out = mtd_write_partition(partition);
-    if (out == NULL) die("error writing %s", argv[1]);
-
-    char buf[HEADER_SIZE];
-    memset(buf, 0, headerlen);
-    int wrote = mtd_write_data(out, buf, headerlen);
-    if (wrote != headerlen) die("error writing %s", argv[1]);
-
-    int len;
-    while ((len = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)))) > 0) {
-        wrote = mtd_write_data(out, buf, len);
-        if (wrote != len) die("error writing %s", argv[1]);
-    }
-    if (len < 0) die("error reading %s", argv[2]);
-
-    if (mtd_write_close(out)) die("error closing %s", argv[1]);
-
-    // Now come back and write the header last
-
-    out = mtd_write_partition(partition);
-    if (out == NULL) die("error re-opening %s", argv[1]);
-
-    wrote = mtd_write_data(out, header, headerlen);
-    if (wrote != headerlen) die("error re-writing %s", argv[1]);
-
-    // Need to write a complete block, so write the rest of the first block
-    size_t block_size;
-    if (mtd_partition_info(partition, NULL, &block_size, NULL))
-        die("error getting %s block size", argv[1]);
-
-    if (TEMP_FAILURE_RETRY(lseek(fd, headerlen, SEEK_SET)) != headerlen)
-        die("error rewinding %s", argv[2]);
-
-    int left = block_size - headerlen;
-    while (left < 0) left += block_size;
-    while (left > 0) {
-        len = TEMP_FAILURE_RETRY(read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left));
-        if (len <= 0) die("error reading %s", argv[2]);
-        if (mtd_write_data(out, buf, len) != len)
-            die("error writing %s", argv[1]);
-        left -= len;
-    }
-
-    if (mtd_write_close(out)) die("error closing %s", argv[1]);
-    return 0;
-}
diff --git a/mtdutils/mounts.h b/mtdutils/mounts.h
index d721355..ed7fb5f 100644
--- a/mtdutils/mounts.h
+++ b/mtdutils/mounts.h
@@ -17,11 +17,12 @@
 #ifndef MTDUTILS_MOUNTS_H_
 #define MTDUTILS_MOUNTS_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct MountedVolume MountedVolume;
+typedef struct {
+ const char *device;
+ const char *mount_point;
+ const char *filesystem;
+ const char *flags;
+} MountedVolume;
 
 int scan_mounted_volumes(void);
 
@@ -34,8 +35,4 @@
 
 int remount_read_only(const MountedVolume* volume);
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif  // MTDUTILS_MOUNTS_H_
diff --git a/openrecoveryscript.cpp b/openrecoveryscript.cpp
index 1cbf800..0235438 100644
--- a/openrecoveryscript.cpp
+++ b/openrecoveryscript.cpp
@@ -52,11 +52,10 @@
 #include "gui/pages.hpp"
 #include "orscmd/orscmd.h"
 #include "adbbu/libtwadbbu.hpp"
+#include "twinstall.h"
 extern "C" {
-	#include "twinstall.h"
 	#include "gui/gui.h"
 	#include "cutils/properties.h"
-	int TWinstall_zip(const char* path, int* wipe_cache);
 }
 
 OpenRecoveryScript::VoidFunction OpenRecoveryScript::call_after_cli_command;
diff --git a/otafault/Android.mk b/otafault/Android.mk
index e8241f4..1898e17 100644
--- a/otafault/Android.mk
+++ b/otafault/Android.mk
@@ -23,11 +23,13 @@
 include $(CLEAR_VARS)
 
 otafault_static_libs := \
-    libbase \
-    libminzip \
+    libziparchive \
     libz \
-    libselinux
+    libselinux \
+    libbase \
+    liblog
 
+LOCAL_CFLAGS := -Werror
 LOCAL_SRC_FILES := config.cpp ota_io.cpp
 LOCAL_MODULE_TAGS := eng
 LOCAL_MODULE := libotafault
@@ -38,12 +40,15 @@
 
 include $(BUILD_STATIC_LIBRARY)
 
+# otafault_test (static executable)
+# ===============================
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := config.cpp ota_io.cpp test.cpp
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE := otafault_test
 LOCAL_STATIC_LIBRARIES := $(otafault_static_libs)
+LOCAL_CFLAGS := -Werror
 LOCAL_C_INCLUDES := $(RECOVERY_PATH)
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
diff --git a/otafault/config.cpp b/otafault/config.cpp
index b456739..8590833 100644
--- a/otafault/config.cpp
+++ b/otafault/config.cpp
@@ -21,38 +21,42 @@
 #include <unistd.h>
 
 #include <android-base/stringprintf.h>
+#include <ziparchive/zip_archive.h>
 
-#include "minzip/Zip.h"
 #include "config.h"
 #include "ota_io.h"
 
 #define OTAIO_MAX_FNAME_SIZE 128
 
-static ZipArchive* archive;
+static ZipArchiveHandle archive;
+static bool is_retry = false;
 static std::map<std::string, bool> should_inject_cache;
 
 static std::string get_type_path(const char* io_type) {
     return android::base::StringPrintf("%s/%s", OTAIO_BASE_DIR, io_type);
 }
 
-void ota_io_init(ZipArchive* za) {
+void ota_io_init(ZipArchiveHandle za, bool retry) {
     archive = za;
+    is_retry = retry;
     ota_set_fault_files();
 }
 
 bool should_fault_inject(const char* io_type) {
     // archive will be NULL if we used an entry point other
     // than updater/updater.cpp:main
-    if (archive == NULL) {
+    if (archive == nullptr || is_retry) {
         return false;
     }
     const std::string type_path = get_type_path(io_type);
     if (should_inject_cache.find(type_path) != should_inject_cache.end()) {
         return should_inject_cache[type_path];
     }
-    const ZipEntry* entry = mzFindZipEntry(archive, type_path.c_str());
-    should_inject_cache[type_path] = entry != nullptr;
-    return entry != NULL;
+    ZipString zip_type_path(type_path.c_str());
+    ZipEntry entry;
+    int status = FindEntry(archive, zip_type_path, &entry);
+    should_inject_cache[type_path] = (status == 0);
+    return (status == 0);
 }
 
 bool should_hit_cache() {
@@ -63,7 +67,9 @@
     std::string type_path = get_type_path(io_type);
     std::string fname;
     fname.resize(OTAIO_MAX_FNAME_SIZE);
-    const ZipEntry* entry = mzFindZipEntry(archive, type_path.c_str());
-    mzReadZipEntry(archive, entry, &fname[0], OTAIO_MAX_FNAME_SIZE);
+    ZipString zip_type_path(type_path.c_str());
+    ZipEntry entry;
+    int status = FindEntry(archive, zip_type_path, &entry);
+    ExtractToMemory(archive, &entry, reinterpret_cast<uint8_t*>(&fname[0]), OTAIO_MAX_FNAME_SIZE);
     return fname;
 }
diff --git a/otafault/config.h b/otafault/config.h
index 4430be3..4adbdd1 100644
--- a/otafault/config.h
+++ b/otafault/config.h
@@ -41,7 +41,7 @@
 
 #include <stdbool.h>
 
-#include "minzip/Zip.h"
+#include <ziparchive/zip_archive.h>
 
 #define OTAIO_BASE_DIR ".libotafault"
 #define OTAIO_READ "READ"
@@ -52,7 +52,7 @@
 /*
  * Initialize libotafault by providing a reference to the OTA package.
  */
-void ota_io_init(ZipArchive* za);
+void ota_io_init(ZipArchiveHandle zip, bool retry);
 
 /*
  * Return true if a config file is present for the given IO type.
diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp
index 0445853..3a89bb5 100644
--- a/otafault/ota_io.cpp
+++ b/otafault/ota_io.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <map>
+#include "ota_io.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -22,15 +22,17 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <map>
+#include <memory>
+
 #include "config.h"
-#include "ota_io.h"
 
 static std::map<intptr_t, const char*> filename_cache;
 static std::string read_fault_file_name = "";
 static std::string write_fault_file_name = "";
 static std::string fsync_fault_file_name = "";
 
-static bool get_hit_file(const char* cached_path, std::string ffn) {
+static bool get_hit_file(const char* cached_path, const std::string& ffn) {
     return should_hit_cache()
         ? !strncmp(cached_path, OTAIO_CACHE_FNAME, strlen(cached_path))
         : !strncmp(cached_path, ffn.c_str(), strlen(cached_path));
@@ -68,17 +70,33 @@
     return fh;
 }
 
-int ota_close(int fd) {
+static int __ota_close(int fd) {
     // descriptors can be reused, so make sure not to leave them in the cache
     filename_cache.erase(fd);
     return close(fd);
 }
 
-int ota_fclose(FILE* fh) {
-    filename_cache.erase((intptr_t)fh);
+void OtaCloser::Close(int fd) {
+    __ota_close(fd);
+}
+
+int ota_close(unique_fd& fd) {
+    return __ota_close(fd.release());
+}
+
+static int __ota_fclose(FILE* fh) {
+    filename_cache.erase(reinterpret_cast<intptr_t>(fh));
     return fclose(fh);
 }
 
+void OtaFcloser::operator()(FILE* f) const {
+    __ota_fclose(f);
+};
+
+int ota_fclose(unique_file& fh) {
+  return __ota_fclose(fh.release());
+}
+
 size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) {
     if (should_fault_inject(OTAIO_READ)) {
         auto cached = filename_cache.find((intptr_t)stream);
@@ -92,6 +110,7 @@
         }
     }
     size_t status = fread(ptr, size, nitems, stream);
+    // If I/O error occurs, set the retry-update flag.
     if (status != nitems && errno == EIO) {
         have_eio_error = true;
     }
diff --git a/otafault/ota_io.h b/otafault/ota_io.h
index 84187a7..9428f1b 100644
--- a/otafault/ota_io.h
+++ b/otafault/ota_io.h
@@ -26,6 +26,10 @@
 #include <stdio.h>
 #include <sys/stat.h>
 
+#include <memory>
+
+#include <android-base/unique_fd.h>
+
 #define OTAIO_CACHE_FNAME "/cache/saved.file"
 
 void ota_set_fault_files();
@@ -36,10 +40,6 @@
 
 FILE* ota_fopen(const char* filename, const char* mode);
 
-int ota_close(int fd);
-
-int ota_fclose(FILE* fh);
-
 size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream);
 
 ssize_t ota_read(int fd, void* buf, size_t nbyte);
@@ -50,4 +50,20 @@
 
 int ota_fsync(int fd);
 
+struct OtaCloser {
+  static void Close(int);
+};
+
+using unique_fd = android::base::unique_fd_impl<OtaCloser>;
+
+int ota_close(unique_fd& fd);
+
+struct OtaFcloser {
+  void operator()(FILE*) const;
+};
+
+using unique_file = std::unique_ptr<FILE, OtaFcloser>;
+
+int ota_fclose(unique_file& fh);
+
 #endif
diff --git a/otautil/Android.mk b/otautil/Android.mk
new file mode 100644
index 0000000..f7ca9a9
--- /dev/null
+++ b/otautil/Android.mk
@@ -0,0 +1,33 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    SysUtil.cpp \
+    DirUtil.cpp \
+    ZipUtil.cpp \
+    ThermalUtil.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+    libselinux \
+    libbase
+
+LOCAL_MODULE := libotautil
+LOCAL_CFLAGS := \
+    -Werror \
+    -Wall
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/otautil/DirUtil.cpp b/otautil/DirUtil.cpp
new file mode 100644
index 0000000..e08e360
--- /dev/null
+++ b/otautil/DirUtil.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2007 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 "DirUtil.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include <string>
+
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+
+typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
+
+static DirStatus
+getPathDirStatus(const char *path)
+{
+    struct stat st;
+    int err;
+
+    err = stat(path, &st);
+    if (err == 0) {
+        /* Something's there; make sure it's a directory.
+         */
+        if (S_ISDIR(st.st_mode)) {
+            return DDIR;
+        }
+        errno = ENOTDIR;
+        return DILLEGAL;
+    } else if (errno != ENOENT) {
+        /* Something went wrong, or something in the path
+         * is bad.  Can't do anything in this situation.
+         */
+        return DILLEGAL;
+    }
+    return DMISSING;
+}
+
+int
+dirCreateHierarchy(const char *path, int mode,
+        const struct utimbuf *timestamp, bool stripFileName,
+        struct selabel_handle *sehnd)
+{
+    DirStatus ds;
+
+    /* Check for an empty string before we bother
+     * making any syscalls.
+     */
+    if (path[0] == '\0') {
+        errno = ENOENT;
+        return -1;
+    }
+    // Allocate a path that we can modify; stick a slash on
+    // the end to make things easier.
+    std::string cpath = path;
+    if (stripFileName) {
+        // Strip everything after the last slash.
+        size_t pos = cpath.rfind('/');
+        if (pos == std::string::npos) {
+            errno = ENOENT;
+            return -1;
+        }
+        cpath.resize(pos + 1);
+    } else {
+        // Make sure that the path ends in a slash.
+        cpath.push_back('/');
+    }
+
+    /* See if it already exists.
+     */
+    ds = getPathDirStatus(cpath.c_str());
+    if (ds == DDIR) {
+        return 0;
+    } else if (ds == DILLEGAL) {
+        return -1;
+    }
+
+    /* Walk up the path from the root and make each level.
+     * If a directory already exists, no big deal.
+     */
+    const char *path_start = &cpath[0];
+    char *p = &cpath[0];
+    while (*p != '\0') {
+        /* Skip any slashes, watching out for the end of the string.
+         */
+        while (*p != '\0' && *p == '/') {
+            p++;
+        }
+        if (*p == '\0') {
+            break;
+        }
+
+        /* Find the end of the next path component.
+         * We know that we'll see a slash before the NUL,
+         * because we added it, above.
+         */
+        while (*p != '/') {
+            p++;
+        }
+        *p = '\0';
+
+        /* Check this part of the path and make a new directory
+         * if necessary.
+         */
+        ds = getPathDirStatus(path_start);
+        if (ds == DILLEGAL) {
+            /* Could happen if some other process/thread is
+             * messing with the filesystem.
+             */
+            return -1;
+        } else if (ds == DMISSING) {
+            int err;
+
+            char *secontext = NULL;
+
+            if (sehnd) {
+                selabel_lookup(sehnd, &secontext, path_start, mode);
+                setfscreatecon(secontext);
+            }
+
+            err = mkdir(path_start, mode);
+
+            if (secontext) {
+                freecon(secontext);
+                setfscreatecon(NULL);
+            }
+
+            if (err != 0) {
+                return -1;
+            }
+            if (timestamp != NULL && utime(path_start, timestamp)) {
+                return -1;
+            }
+        }
+        // else, this directory already exists.
+
+        // Repair the path and continue.
+        *p = '/';
+    }
+    return 0;
+}
+
+int
+dirUnlinkHierarchy(const char *path)
+{
+    struct stat st;
+    DIR *dir;
+    struct dirent *de;
+    int fail = 0;
+
+    /* is it a file or directory? */
+    if (lstat(path, &st) < 0) {
+        return -1;
+    }
+
+    /* a file, so unlink it */
+    if (!S_ISDIR(st.st_mode)) {
+        return unlink(path);
+    }
+
+    /* a directory, so open handle */
+    dir = opendir(path);
+    if (dir == NULL) {
+        return -1;
+    }
+
+    /* recurse over components */
+    errno = 0;
+    while ((de = readdir(dir)) != NULL) {
+        //TODO: don't blow the stack
+        char dn[PATH_MAX];
+        if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
+            continue;
+        }
+        snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
+        if (dirUnlinkHierarchy(dn) < 0) {
+            fail = 1;
+            break;
+        }
+        errno = 0;
+    }
+    /* in case readdir or unlink_recursive failed */
+    if (fail || errno < 0) {
+        int save = errno;
+        closedir(dir);
+        errno = save;
+        return -1;
+    }
+
+    /* close directory handle */
+    if (closedir(dir) < 0) {
+        return -1;
+    }
+
+    /* delete target directory */
+    return rmdir(path);
+}
diff --git a/otautil/DirUtil.h b/otautil/DirUtil.h
new file mode 100644
index 0000000..85b83c3
--- /dev/null
+++ b/otautil/DirUtil.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2007 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 MINZIP_DIRUTIL_H_
+#define MINZIP_DIRUTIL_H_
+
+#include <stdbool.h>
+#include <utime.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct selabel_handle;
+
+/* Like "mkdir -p", try to guarantee that all directories
+ * specified in path are present, creating as many directories
+ * as necessary.  The specified mode is passed to all mkdir
+ * calls;  no modifications are made to umask.
+ *
+ * If stripFileName is set, everything after the final '/'
+ * is stripped before creating the directory hierarchy.
+ *
+ * If timestamp is non-NULL, new directories will be timestamped accordingly.
+ *
+ * Returns 0 on success; returns -1 (and sets errno) on failure
+ * (usually if some element of path is not a directory).
+ */
+int dirCreateHierarchy(const char *path, int mode,
+        const struct utimbuf *timestamp, bool stripFileName,
+        struct selabel_handle* sehnd);
+
+/* rm -rf <path>
+ */
+int dirUnlinkHierarchy(const char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // MINZIP_DIRUTIL_H_
diff --git a/otautil/SysUtil.cpp b/otautil/SysUtil.cpp
new file mode 100644
index 0000000..a2133b9
--- /dev/null
+++ b/otautil/SysUtil.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2006 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 "SysUtil.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+static bool sysMapFD(int fd, MemMapping* pMap) {
+  CHECK(pMap != nullptr);
+
+  struct stat sb;
+  if (fstat(fd, &sb) == -1) {
+    PLOG(ERROR) << "fstat(" << fd << ") failed";
+    return false;
+  }
+
+  void* memPtr = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (memPtr == MAP_FAILED) {
+    PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed";
+    return false;
+  }
+
+  pMap->addr = static_cast<unsigned char*>(memPtr);
+  pMap->length = sb.st_size;
+  pMap->ranges.push_back({ memPtr, static_cast<size_t>(sb.st_size) });
+
+  return true;
+}
+
+// A "block map" which looks like this (from uncrypt/uncrypt.cpp):
+//
+//     /dev/block/platform/msm_sdcc.1/by-name/userdata     # block device
+//     49652 4096                        # file size in bytes, block size
+//     3                                 # count of block ranges
+//     1000 1008                         # block range 0
+//     2100 2102                         # ... block range 1
+//     30 33                             # ... block range 2
+//
+// Each block range represents a half-open interval; the line "30 33"
+// reprents the blocks [30, 31, 32].
+static int sysMapBlockFile(const char* filename, MemMapping* pMap) {
+  CHECK(pMap != nullptr);
+
+  std::string content;
+  if (!android::base::ReadFileToString(filename, &content)) {
+    PLOG(ERROR) << "Failed to read " << filename;
+    return -1;
+  }
+
+  std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
+  if (lines.size() < 4) {
+    LOG(ERROR) << "Block map file is too short: " << lines.size();
+    return -1;
+  }
+
+  size_t size;
+  unsigned int blksize;
+  if (sscanf(lines[1].c_str(), "%zu %u", &size, &blksize) != 2) {
+    LOG(ERROR) << "Failed to parse file size and block size: " << lines[1];
+    return -1;
+  }
+
+  size_t range_count;
+  if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) {
+    LOG(ERROR) << "Failed to parse block map header: " << lines[2];
+    return -1;
+  }
+
+  size_t blocks;
+  if (blksize != 0) {
+    blocks = ((size - 1) / blksize) + 1;
+  }
+  if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 ||
+      lines.size() != 3 + range_count) {
+    LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize
+               << ", range_count " << range_count << ", lines " << lines.size();
+    return -1;
+  }
+
+  // Reserve enough contiguous address space for the whole file.
+  void* reserve = mmap64(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+  if (reserve == MAP_FAILED) {
+    PLOG(ERROR) << "failed to reserve address space";
+    return -1;
+  }
+
+  const std::string& block_dev = lines[0];
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY)));
+  if (fd == -1) {
+    PLOG(ERROR) << "failed to open block device " << block_dev;
+    munmap(reserve, blocks * blksize);
+    return -1;
+  }
+
+  pMap->ranges.resize(range_count);
+
+  unsigned char* next = static_cast<unsigned char*>(reserve);
+  size_t remaining_size = blocks * blksize;
+  bool success = true;
+  for (size_t i = 0; i < range_count; ++i) {
+    const std::string& line = lines[i + 3];
+
+    size_t start, end;
+    if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) {
+      LOG(ERROR) << "failed to parse range " << i << " in block map: " << line;
+      success = false;
+      break;
+    }
+    size_t length = (end - start) * blksize;
+    if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) {
+      LOG(ERROR) << "unexpected range in block map: " << start << " " << end;
+      success = false;
+      break;
+    }
+
+    void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd,
+                        static_cast<off64_t>(start) * blksize);
+    if (addr == MAP_FAILED) {
+      PLOG(ERROR) << "failed to map block " << i;
+      success = false;
+      break;
+    }
+    pMap->ranges[i].addr = addr;
+    pMap->ranges[i].length = length;
+
+    next += length;
+    remaining_size -= length;
+  }
+  if (success && remaining_size != 0) {
+    LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size;
+    success = false;
+  }
+  if (!success) {
+    munmap(reserve, blocks * blksize);
+    return -1;
+  }
+
+  pMap->addr = static_cast<unsigned char*>(reserve);
+  pMap->length = size;
+
+  LOG(INFO) << "mmapped " << range_count << " ranges";
+
+  return 0;
+}
+
+int sysMapFile(const char* fn, MemMapping* pMap) {
+  if (fn == nullptr || pMap == nullptr) {
+    LOG(ERROR) << "Invalid argument(s)";
+    return -1;
+  }
+
+  *pMap = {};
+
+  if (fn[0] == '@') {
+    if (sysMapBlockFile(fn + 1, pMap) != 0) {
+      LOG(ERROR) << "Map of '" << fn << "' failed";
+      return -1;
+    }
+  } else {
+    // This is a regular file.
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn, O_RDONLY)));
+    if (fd == -1) {
+      PLOG(ERROR) << "Unable to open '" << fn << "'";
+      return -1;
+    }
+
+    if (!sysMapFD(fd, pMap)) {
+      LOG(ERROR) << "Map of '" << fn << "' failed";
+      return -1;
+    }
+  }
+  return 0;
+}
+
+/*
+ * Release a memory mapping.
+ */
+void sysReleaseMap(MemMapping* pMap) {
+  std::for_each(pMap->ranges.cbegin(), pMap->ranges.cend(), [](const MappedRange& range) {
+    if (munmap(range.addr, range.length) == -1) {
+      PLOG(ERROR) << "munmap(" << range.addr << ", " << range.length << ") failed";
+    }
+  });
+  pMap->ranges.clear();
+}
diff --git a/otautil/SysUtil.h b/otautil/SysUtil.h
new file mode 100644
index 0000000..6a79bf3
--- /dev/null
+++ b/otautil/SysUtil.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2006 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_SYSUTIL
+#define _OTAUTIL_SYSUTIL
+
+#include <sys/types.h>
+
+#include <vector>
+
+struct MappedRange {
+  void* addr;
+  size_t length;
+};
+
+/*
+ * Use this to keep track of mapped segments.
+ */
+struct MemMapping {
+  unsigned char* addr; /* start of data */
+  size_t length;       /* length of data */
+
+  std::vector<MappedRange> ranges;
+};
+
+/*
+ * Map a file into a private, read-only memory segment.  If 'fn'
+ * begins with an '@' character, it is a map of blocks to be mapped,
+ * otherwise it is treated as an ordinary file.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFile(const char* fn, MemMapping* pMap);
+
+/*
+ * Release the pages associated with a shared memory segment.
+ *
+ * This does not free "pMap"; it just releases the memory.
+ */
+void sysReleaseMap(MemMapping* pMap);
+
+#endif  // _OTAUTIL_SYSUTIL
diff --git a/otautil/ThermalUtil.cpp b/otautil/ThermalUtil.cpp
new file mode 100644
index 0000000..13d3643
--- /dev/null
+++ b/otautil/ThermalUtil.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "ThermalUtil.h"
+
+#include <dirent.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+static constexpr auto THERMAL_PREFIX = "/sys/class/thermal/";
+
+static int thermal_filter(const dirent* de) {
+  if (android::base::StartsWith(de->d_name, "thermal_zone")) {
+    return 1;
+  }
+  return 0;
+}
+