twrpinstall: create library for twrpinstall

This library will need to mirror AOSP for any changes to installing
packages. The library has been separated out in order to make importing
updates from AOSP into the TWRP project.

twinstall.cpp has been removed from the recovery binary and added to
this library. It has been refactored for libziparchive.

Sideload has been reworked to use the newer methods from AOSP on
flashing packages through adb sideload.

We are also removing old libraries for adb and verifier.

Lastly before flashing a zip or image, we want to unlock block devices
for writing so that when an OTA is flashed to the inactive slot,
the flash will succeed.

Change-Id: I6d8702fc9031ffaf9f666b4ba375dc7d9362e473
diff --git a/twrpinstall/Android.bp b/twrpinstall/Android.bp
new file mode 100755
index 0000000..66ae163
--- /dev/null
+++ b/twrpinstall/Android.bp
@@ -0,0 +1,92 @@
+// Copyright (C) 2019 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.
+
+cc_defaults {
+    name: "libtwrpinstall_defaults",
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    header_libs: [
+        "libminadbd_headers",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbootloader_message",
+        "libcrypto",
+        "libext4_utils",
+        "libfs_mgr",
+        "libfusesideload",
+        "libhidl-gen-utils",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libselinux",
+        "libtinyxml2",
+        "libutils",
+        "libz",
+        "libziparchive",
+    ],
+
+    static_libs: [
+        "libotautil",
+
+        // external dependencies
+        "libvintf_recovery",
+        "libvintf",
+        "libfstab",
+    ],
+}
+
+cc_library_static {
+    name: "libtwrpinstall",
+    recovery_available: true,
+
+    defaults: [
+        "libtwrpinstall_defaults",
+    ],
+
+    cflags: [
+        "-DAB_OTA_UPDATER=1"
+    ],
+
+    include_dirs: [
+        "bootable/recovery",
+        "bootable/recovery/install/include",
+    ],
+
+    srcs: [
+        "adb_install.cpp",
+        "asn1_decoder.cpp",
+        "install.cpp",
+        "installcommand.cpp",
+        "package.cpp",
+        "tw_atomic.cpp",
+        "twinstall.cpp",
+        "verifier.cpp",
+    ],
+    shared_libs: [
+        "librecovery_ui",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "librecovery_ui",
+    ],
+}
diff --git a/twrpinstall/ZipUtil.cpp b/twrpinstall/ZipUtil.cpp
new file mode 100755
index 0000000..f8134bc
--- /dev/null
+++ b/twrpinstall/ZipUtil.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ZipUtil.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <utime.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+#include <ziparchive/zip_archive.h>
+
+#include "otautil/dirutil.h"
+
+static constexpr mode_t UNZIP_DIRMODE = 0755;
+static constexpr mode_t UNZIP_FILEMODE = 0644;
+
+bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path,
+                             const std::string& dest_path, const struct utimbuf* timestamp,
+                             struct selabel_handle* sehnd) {
+    if (!zip_path.empty() && zip_path[0] == '/') {
+        LOG(ERROR) << "ExtractPackageRecursive(): zip_path must be a relative path " << zip_path;
+        return false;
+    }
+    if (dest_path.empty() || dest_path[0] != '/') {
+        LOG(ERROR) << "ExtractPackageRecursive(): dest_path must be an absolute path " << dest_path;
+        return false;
+    }
+
+    void* cookie;
+    std::string target_dir(dest_path);
+    if (dest_path.back() != '/') {
+        target_dir += '/';
+    }
+    std::string prefix_path(zip_path);
+    if (!zip_path.empty() && zip_path.back() != '/') {
+        prefix_path += '/';
+    }
+    const ZipString zip_prefix(prefix_path.c_str());
+
+    int ret = StartIteration(zip, &cookie, &zip_prefix, nullptr);
+    if (ret != 0) {
+        LOG(ERROR) << "failed to start iterating zip entries.";
+        return false;
+    }
+
+    std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
+    ZipEntry entry;
+    ZipString name;
+    int extractCount = 0;
+    while (Next(cookie, &entry, &name) == 0) {
+        std::string entry_name(name.name, name.name + name.name_length);
+        CHECK_LE(prefix_path.size(), entry_name.size());
+        std::string path = target_dir + entry_name.substr(prefix_path.size());
+        // Skip dir.
+        if (path.back() == '/') {
+            continue;
+        }
+        //TODO(b/31917448) handle the symlink.
+
+        if (dirCreateHierarchy(path.c_str(), UNZIP_DIRMODE, timestamp, true, sehnd) != 0) {
+            LOG(ERROR) << "failed to create dir for " << path;
+            return false;
+        }
+
+        char *secontext = NULL;
+        if (sehnd) {
+            selabel_lookup(sehnd, &secontext, path.c_str(), UNZIP_FILEMODE);
+            setfscreatecon(secontext);
+        }
+        android::base::unique_fd fd(open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, UNZIP_FILEMODE));
+        if (fd == -1) {
+            PLOG(ERROR) << "Can't create target file \"" << path << "\"";
+            return false;
+        }
+        if (secontext) {
+            freecon(secontext);
+            setfscreatecon(NULL);
+        }
+
+        int err = ExtractEntryToFile(zip, &entry, fd);
+        if (err != 0) {
+            LOG(ERROR) << "Error extracting \"" << path << "\" : " << ErrorCodeString(err);
+            return false;
+        }
+
+        if (fsync(fd) != 0) {
+            PLOG(ERROR) << "Error syncing file descriptor when extracting \"" << path << "\"";
+            return false;
+        }
+
+        if (timestamp != nullptr && utime(path.c_str(), timestamp)) {
+            PLOG(ERROR) << "Error touching \"" << path << "\"";
+            return false;
+        }
+
+        LOG(INFO) << "Extracted file \"" << path << "\"";
+        ++extractCount;
+    }
+
+    LOG(INFO) << "Extracted " << extractCount << " file(s)";
+    return true;
+}
diff --git a/twrpinstall/ZipUtil.h b/twrpinstall/ZipUtil.h
new file mode 100644
index 0000000..cda405c
--- /dev/null
+++ b/twrpinstall/ZipUtil.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _OTAUTIL_ZIPUTIL_H
+#define _OTAUTIL_ZIPUTIL_H
+
+#include <utime.h>
+
+#include <string>
+
+#include <selinux/label.h>
+#include <ziparchive/zip_archive.h>
+
+/*
+ * Inflate all files under zip_path to the directory specified by
+ * dest_path, which must exist and be a writable directory. The zip_path
+ * is allowed to be an empty string, in which case the whole package
+ * will be extracted.
+ *
+ * Directory entries are not extracted.
+ *
+ * The immediate children of zip_path will become the immediate
+ * children of dest_path; e.g., if the archive contains the entries
+ *
+ *     a/b/c/one
+ *     a/b/c/two
+ *     a/b/c/d/three
+ *
+ * and ExtractPackageRecursive(a, "a/b/c", "/tmp", ...) is called, the resulting
+ * files will be
+ *
+ *     /tmp/one
+ *     /tmp/two
+ *     /tmp/d/three
+ *
+ * If timestamp is non-NULL, file timestamps will be set accordingly.
+ *
+ * Returns true on success, false on failure.
+ */
+bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path,
+                             const std::string& dest_path, const struct utimbuf* timestamp,
+                             struct selabel_handle* sehnd);
+
+#endif // _OTAUTIL_ZIPUTIL_H
diff --git a/twrpinstall/adb_install.cpp b/twrpinstall/adb_install.cpp
new file mode 100755
index 0000000..5eb8c0b
--- /dev/null
+++ b/twrpinstall/adb_install.cpp
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2012 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 "twinstall/adb_install.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <functional>
+#include <map>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/memory.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "fuse_sideload.h"
+#include "twinstall/install.h"
+#include "twinstall/wipe_data.h"
+#include "minadbd_types.h"
+#include "otautil/sysutil.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
+
+// A CommandFunction returns a pair of (result, should_continue), which indicates the command
+// execution result and whether it should proceed to the next iteration. The execution result will
+// always be sent to the minadbd side.
+using CommandFunction = std::function<std::pair<bool, bool>()>;
+
+pid_t child;
+
+pid_t GetMiniAdbdPid() {
+  return child;
+}
+
+static bool SetUsbConfig(const std::string& state) {
+  android::base::SetProperty("sys.usb.config", state);
+  return android::base::WaitForProperty("sys.usb.state", state);
+}
+
+// Parses the minadbd command in |message|; returns MinadbdCommand::kError upon errors.
+static MinadbdCommand ParseMinadbdCommand(const std::string& message) {
+  if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) {
+    LOG(ERROR) << "Failed to parse command in message " << message;
+    return MinadbdCommand::kError;
+  }
+
+  auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix));
+  auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str());
+  if (cmd_code >= static_cast<uint32_t>(MinadbdCommand::kError)) {
+    LOG(ERROR) << "Unsupported command code: " << cmd_code;
+    return MinadbdCommand::kError;
+  }
+
+  return static_cast<MinadbdCommand>(cmd_code);
+}
+
+static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) {
+  char message[kMinadbdMessageSize];
+  memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
+  android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status);
+
+  if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
+    PLOG(ERROR) << "Failed to write message " << message;
+    return false;
+  }
+  return true;
+}
+
+// Installs the package from FUSE. Returns the installation result and whether it should continue
+// waiting for new commands.
+static auto AdbInstallPackageHandler(int* result) {
+  // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long
+  // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME
+  // will start to exist once the host connects and starts serving a package. Poll for its
+  // appearance. (Note that inotify doesn't work with FUSE.)
+  constexpr int ADB_INSTALL_TIMEOUT = 15;
+  bool should_continue = true;
+  *result = INSTALL_ERROR;
+  for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
+    PLOG(ERROR) << "i: " << i;
+    struct stat st;
+    if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
+      if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
+        sleep(1);
+        continue;
+      } else {
+        should_continue = false;
+        break;
+      }
+    }
+    *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0);
+    break;
+  }
+
+  // Calling stat() on this magic filename signals the FUSE to exit.
+  struct stat st;
+  stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+  return std::make_pair(*result == INSTALL_SUCCESS, should_continue);
+}
+
+static auto AdbRebootHandler(MinadbdCommand command, int* result,
+                             Device::BuiltinAction* reboot_action) {
+  // Use Device::REBOOT_{FASTBOOT,RECOVERY,RESCUE}, instead of the ones with ENTER_. This allows
+  // rebooting back into fastboot/recovery/rescue mode through bootloader, which may use a newly
+  // installed bootloader/recovery image.
+  switch (command) {
+    case MinadbdCommand::kRebootBootloader:
+      *reboot_action = Device::REBOOT_BOOTLOADER;
+      break;
+    case MinadbdCommand::kRebootFastboot:
+      *reboot_action = Device::REBOOT_FASTBOOT;
+      break;
+    case MinadbdCommand::kRebootRecovery:
+      *reboot_action = Device::REBOOT_RECOVERY;
+      break;
+    case MinadbdCommand::kRebootRescue:
+      *reboot_action = Device::REBOOT_RESCUE;
+      break;
+    case MinadbdCommand::kRebootAndroid:
+    default:
+      *reboot_action = Device::REBOOT;
+      break;
+  }
+  *result = INSTALL_REBOOT;
+  return std::make_pair(true, false);
+}
+
+// Parses and executes the command from minadbd. Returns whether the caller should keep waiting for
+// next command.
+static bool HandleMessageFromMinadbd(int socket_fd,
+                                     const std::map<MinadbdCommand, CommandFunction>& command_map) {
+  char buffer[kMinadbdMessageSize];
+  if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) {
+    PLOG(ERROR) << "Failed to read message from minadbd";
+    return false;
+  }
+
+  std::string message(buffer, buffer + kMinadbdMessageSize);
+  auto command_type = ParseMinadbdCommand(message);
+  if (command_type == MinadbdCommand::kError) {
+    return false;
+  }
+  if (command_map.find(command_type) == command_map.end()) {
+    LOG(ERROR) << "Unsupported command: "
+               << android::base::get_unaligned<unsigned int>(
+                      message.substr(strlen(kMinadbdCommandPrefix)).c_str());
+    return false;
+  }
+
+  // We have received a valid command, execute the corresponding function.
+  const auto& command_func = command_map.at(command_type);
+  const auto [result, should_continue] = command_func();
+  LOG(INFO) << "Command " << static_cast<uint32_t>(command_type) << " finished with " << result;
+  if (!WriteStatusToFd(result ? MinadbdCommandStatus::kSuccess : MinadbdCommandStatus::kFailure,
+                       socket_fd)) {
+    return false;
+  }
+  return should_continue;
+}
+
+// TODO(xunchang) add a wrapper function and kill the minadbd service there.
+static void ListenAndExecuteMinadbdCommands(
+    pid_t minadbd_pid, android::base::unique_fd&& socket_fd,
+    const std::map<MinadbdCommand, CommandFunction>& command_map) {
+  android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC));
+  if (epoll_fd == -1) {
+    PLOG(ERROR) << "Failed to create epoll";
+    kill(minadbd_pid, SIGKILL);
+    return;
+  }
+
+  constexpr int EPOLL_MAX_EVENTS = 10;
+  struct epoll_event ev = {};
+  ev.events = EPOLLIN | EPOLLHUP;
+  ev.data.fd = socket_fd.get();
+  struct epoll_event events[EPOLL_MAX_EVENTS];
+  if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) {
+    PLOG(ERROR) << "Failed to add socket fd to epoll";
+    kill(minadbd_pid, SIGKILL);
+    return;
+  }
+
+  // Set the timeout to be 300s when waiting for minadbd commands.
+  constexpr int TIMEOUT_MILLIS = 300 * 1000;
+  while (true) {
+    // Poll for the status change of the socket_fd, and handle the message if the fd is ready to
+    // read.
+    int event_count =
+        TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS));
+    if (event_count == -1) {
+      PLOG(ERROR) << "Failed to wait for epoll events";
+      kill(minadbd_pid, SIGKILL);
+      return;
+    }
+    if (event_count == 0) {
+      LOG(ERROR) << "Timeout waiting for messages from minadbd";
+      kill(minadbd_pid, SIGKILL);
+      return;
+    }
+
+    for (int n = 0; n < event_count; n++) {
+      if (events[n].events & EPOLLHUP) {
+        LOG(INFO) << "Socket has been closed";
+        kill(minadbd_pid, SIGKILL);
+        return;
+      }
+      if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) {
+        kill(minadbd_pid, SIGKILL);
+        return;
+      }
+    }
+  }
+}
+
+// Recovery starts minadbd service as a child process, and spawns another thread to listen for the
+// message from minadbd through a socket pair. Here is an example to execute one command from adb
+// host.
+//  a. recovery                    b. listener thread               c. minadbd service
+//
+//  a1. create socket pair
+//  a2. fork minadbd service
+//                                                                c3. wait for the adb commands
+//                                                                    from host
+//                                                                c4. after receiving host commands:
+//                                                                  1) set up pre-condition (i.e.
+//                                                                     start fuse for adb sideload)
+//                                                                  2) issue command through
+//                                                                     socket.
+//                                                                  3) wait for result
+//  a5. start listener thread
+//                               b6. listen for message from
+//                                   minadbd in a loop.
+//                               b7. After receiving a minadbd
+//                                   command from socket
+//                                 1) execute the command function
+//                                 2) send the result back to
+//                                    minadbd
+//  ......
+//                                                                 c8. exit upon receiving the
+//                                                                     result
+// a9.  wait for listener thread
+//      to exit.
+//
+// a10. wait for minadbd to
+//      exit
+//                               b11. exit the listening loop
+//
+static void CreateMinadbdServiceAndExecuteCommands(
+    const std::map<MinadbdCommand, CommandFunction>& command_map,
+    bool rescue_mode __unused, std::string install_file __unused) {
+  signal(SIGPIPE, SIG_IGN);
+
+  android::base::unique_fd recovery_socket;
+  android::base::unique_fd minadbd_socket;
+  if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) {
+    PLOG(ERROR) << "Failed to create socket";
+    return;
+  }
+
+  child = fork();
+  if (child == -1) {
+    PLOG(ERROR) << "Failed to fork child process";
+    return;
+  }
+  if (child == 0) {
+    recovery_socket.reset();
+    std::vector<std::string> minadbd_commands = {
+      "/system/bin/minadbd",
+      "--socket_fd",
+      std::to_string(minadbd_socket.release()),
+    };
+    // if (rescue_mode) {
+    //   minadbd_commands.push_back("--rescue");
+    // }
+    auto exec_args = StringVectorToNullTerminatedArray(minadbd_commands);
+    execv(exec_args[0], exec_args.data());
+    _exit(EXIT_FAILURE);
+  }
+
+  minadbd_socket.reset();
+
+  // We need to call SetUsbConfig() after forking minadbd service. Because the function waits for
+  // the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon.
+  if (!SetUsbConfig("sideload")) {
+    LOG(ERROR) << "Failed to set usb config to sideload";
+    return;
+  }
+
+  std::thread listener_thread(ListenAndExecuteMinadbdCommands, child,
+                              std::move(recovery_socket), std::ref(command_map));
+  if (listener_thread.joinable()) {
+    listener_thread.join();
+  }
+
+  int status;
+  waitpid(child, &status, 0);
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+    if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) {
+      LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n";
+    } else if (!WIFSIGNALED(status)) {
+      LOG(ERROR) << "\n(adbd status " << WEXITSTATUS(status) << ")";
+    }
+  }
+
+  signal(SIGPIPE, SIG_DFL);
+}
+
+  int twrp_sideload(const char* install_file, Device::BuiltinAction* reboot_action) {
+
+  // Save the usb state to restore after the sideload operation.
+  std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
+  // Clean up state and stop adbd.
+  if (usb_state != "none" && !SetUsbConfig("none")) {
+    LOG(ERROR) << "Failed to clear USB config";
+    return INSTALL_ERROR;
+  }
+
+  int install_result = INSTALL_ERROR;
+  std::map<MinadbdCommand, CommandFunction> command_map{
+  { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, &install_result) },
+  { MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid,
+                                              &install_result, reboot_action) },
+  { MinadbdCommand::kRebootBootloader,
+    std::bind(&AdbRebootHandler, MinadbdCommand::kRebootBootloader, &install_result,
+              reboot_action) },
+  { MinadbdCommand::kRebootFastboot, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootFastboot,
+                                                &install_result, reboot_action) },
+  { MinadbdCommand::kRebootRecovery, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRecovery,
+                                                &install_result, reboot_action) },
+  { MinadbdCommand::kRebootRescue,
+    std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) },
+};
+
+  CreateMinadbdServiceAndExecuteCommands(command_map, false, install_file);
+
+  // Clean up before switching to the older state, for example setting the state
+  // to none sets sys/class/android_usb/android0/enable to 0.
+  if (!SetUsbConfig("none")) {
+    LOG(ERROR) << "Failed to clear USB config";
+  }
+
+  if (usb_state != "none") {
+    if (!SetUsbConfig(usb_state)) {
+      LOG(ERROR) << "Failed to set USB config to " << usb_state;
+    }
+  }
+
+  return install_result;
+}
diff --git a/twrpinstall/asn1_decoder.cpp b/twrpinstall/asn1_decoder.cpp
new file mode 100644
index 0000000..2d81a6e
--- /dev/null
+++ b/twrpinstall/asn1_decoder.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2013 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 "private/asn1_decoder.h"
+
+int asn1_context::peek_byte() const {
+  if (length_ == 0) {
+    return -1;
+  }
+  return *p_;
+}
+
+int asn1_context::get_byte() {
+  if (length_ == 0) {
+    return -1;
+  }
+
+  int byte = *p_;
+  p_++;
+  length_--;
+  return byte;
+}
+
+bool asn1_context::skip_bytes(size_t num_skip) {
+  if (length_ < num_skip) {
+    return false;
+  }
+  p_ += num_skip;
+  length_ -= num_skip;
+  return true;
+}
+
+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;
+  }
+  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;
+    }
+    length <<= 8;
+    length += byte;
+  }
+  *out_len = length;
+  return true;
+}
+
+/**
+ * Returns the constructed type and advances the pointer. E.g. A0 -> 0
+ */
+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(&length) || !skip_bytes(length)) {
+      return false;
+    }
+    byte = peek_byte();
+  }
+  return byte != -1;
+}
+
+int asn1_context::asn1_constructed_type() const {
+  return 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* 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);
+}
+
+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_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_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/twrpinstall/include/common.h b/twrpinstall/include/common.h
new file mode 100755
index 0000000..e82bec3
--- /dev/null
+++ b/twrpinstall/include/common.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+#define LOGE(...) fprintf(stdout, "E:" __VA_ARGS__)
+#define LOGW(...) fprintf(stdout, "W:" __VA_ARGS__)
+#define LOGI(...) fprintf(stdout, "I:" __VA_ARGS__)
+
+#if 0
+#define LOGV(...) fprintf(stdout, "V:" __VA_ARGS__)
+#define LOGD(...) fprintf(stdout, "D:" __VA_ARGS__)
+#else
+#define LOGV(...) do {} while (0)
+#define LOGD(...) do {} while (0)
+#endif
+
+#define STRINGIFY(x) #x
+#define EXPAND(x) STRINGIFY(x)
+
+// Not using the command-line defined macro here because this header could be included by
+// device-specific recovery libraries. We static assert the value consistency in recovery.cpp.
+//static constexpr int kRecoveryApiVersion = 3;
+
+class RecoveryUI;
+struct selabel_handle;
+
+extern struct selabel_handle* sehandle;
+extern RecoveryUI* ui;
+extern bool has_cache;
+
+// The current stage, e.g. "1/2".
+extern std::string stage;
+
+// The reason argument provided in "--reason=".
+extern const char* reason;
+
+// static bool is_ro_debuggable();
diff --git a/twrpinstall/include/installcommand.h b/twrpinstall/include/installcommand.h
new file mode 100644
index 0000000..f265801
--- /dev/null
+++ b/twrpinstall/include/installcommand.h
@@ -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 RECOVERY_INSTALL_COMMAND_H_
+#define RECOVERY_INSTALL_COMMAND_H_
+
+#define TMP_UPDATER_BINARY_PATH "/tmp/updater"
+
+#include <string>
+#include <ziparchive/zip_archive.h>
+
+bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data);
+
+int
+abupdate_binary_command(const char* path, int retry_count,
+                      int status_fd, std::vector<std::string>* cmd);
+int
+update_binary_command(const char* path, int retry_count,
+                      int status_fd, std::vector<std::string>* cmd);
+
+bool verify_package_compatibility(ZipArchiveHandle package_zip);
+
+void read_source_target_build(ZipArchiveHandle zip/*, std::vector<std::string>& log_buffer*/);
+
+#endif  // RECOVERY_INSTALL_COMMAND_H_
diff --git a/twrpinstall/include/legacy_property_service.h b/twrpinstall/include/legacy_property_service.h
new file mode 100644
index 0000000..d20bdef
--- /dev/null
+++ b/twrpinstall/include/legacy_property_service.h
@@ -0,0 +1,25 @@
+/*
+ * 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 _LEGACY_PROPERTY_H
+#define _LEGACY_PROPERTY_H
+
+#include <stdbool.h>
+
+void legacy_get_property_workspace(int *fd, int *sz);
+int legacy_properties_init();
+
+#endif	/* _LEGACY_PROPERTY_H */
diff --git a/twrpinstall/include/private/asn1_decoder.h b/twrpinstall/include/private/asn1_decoder.h
new file mode 100644
index 0000000..e5337d9
--- /dev/null
+++ b/twrpinstall/include/private/asn1_decoder.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 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 ASN1_DECODER_H_
+#define ASN1_DECODER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+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);
+
+ 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/twrpinstall/include/private/setup_commands.h b/twrpinstall/include/private/setup_commands.h
new file mode 100644
index 0000000..7fdc741
--- /dev/null
+++ b/twrpinstall/include/private/setup_commands.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+// Private headers exposed for testing purpose only.
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <ziparchive/zip_archive.h>
+
+// Sets up the commands for a non-A/B update. Extracts the updater binary from the open zip archive
+// |zip| located at |package|. Stores the command line that should be called into |cmd|. The
+// |status_fd| is the file descriptor the child process should use to report back the progress of
+// the update.
+int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
+                             int status_fd, std::vector<std::string>* cmd);
+
+// Sets up the commands for an A/B update. Extracts the needed entries from the open zip archive
+// |zip| located at |package|. Stores the command line that should be called into |cmd|. The
+// |status_fd| is the file descriptor the child process should use to report back the progress of
+// the update. Note that since this applies to the sideloading flow only, it takes one less
+// parameter |retry_count| than the non-A/B version.
+int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
+                          std::vector<std::string>* cmd);
diff --git a/twrpinstall/include/set_metadata.h b/twrpinstall/include/set_metadata.h
new file mode 100644
index 0000000..9a46be9
--- /dev/null
+++ b/twrpinstall/include/set_metadata.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Team Win Recovery 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.
+ */
+
+/*
+ * The purpose of these functions is to try to get and set the proper
+ * file permissions, SELinux contexts, owner, and group so that these
+ * files are accessible when we boot up to normal Android via MTP and to
+ * file manager apps. During early boot we try to read the contexts and
+ * owner / group info from /data/media or from /data/media/0 and store
+ * them in static variables. From there, we'll try to set the same
+ * contexts, owner, and group information on most files we create during
+ * operations like backups, copying the log, and MTP operations.
+ */
+
+#ifndef _RECOVERY_SET_CONTEXTS_H
+#define _RECOVERY_SET_CONTEXTS_H
+
+#include <sys/stat.h>
+#include "selinux/selinux.h"
+
+int tw_get_default_metadata(const char* filename);
+int tw_set_default_metadata(const char* filename);
+
+#endif //_RECOVERY_SET_CONTEXTS_H
diff --git a/twrpinstall/include/tw_atomic.hpp b/twrpinstall/include/tw_atomic.hpp
new file mode 100644
index 0000000..0f29f02
--- /dev/null
+++ b/twrpinstall/include/tw_atomic.hpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Team Win Recovery 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 _TWATOMIC_HPP_HEADER
+#define _TWATOMIC_HPP_HEADER
+
+#include <pthread.h>
+
+class TWAtomicInt
+{
+public:
+	TWAtomicInt(int initial_value = 0);
+	~TWAtomicInt();
+	void set_value(int new_value);
+	int get_value();
+
+private:
+	int value;
+	bool use_mutex;
+	pthread_mutex_t mutex_lock;
+};
+
+#endif //_TWATOMIC_HPP_HEADER
diff --git a/twrpinstall/include/twinstall.h b/twrpinstall/include/twinstall.h
new file mode 100755
index 0000000..f60dd26
--- /dev/null
+++ b/twrpinstall/include/twinstall.h
@@ -0,0 +1,6 @@
+#ifndef RECOVERY_TWINSTALL_H_
+#define RECOVERY_TWINSTALL_H_
+
+int TWinstall_zip(const char* path, int* wipe_cache);
+
+#endif  // RECOVERY_TWINSTALL_H_
diff --git a/twrpinstall/include/twinstall/adb_install.h b/twrpinstall/include/twinstall/adb_install.h
new file mode 100755
index 0000000..42321d4
--- /dev/null
+++ b/twrpinstall/include/twinstall/adb_install.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#pragma once
+
+#include <recovery_ui/device.h>
+
+// Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum
+// InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with
+// the reboot target set in reboot_action.
+int twrp_sideload(const char* install_file, Device::BuiltinAction* reboot_action);
+pid_t GetMiniAdbdPid();
\ No newline at end of file
diff --git a/twrpinstall/include/twinstall/get_args.h b/twrpinstall/include/twinstall/get_args.h
new file mode 100755
index 0000000..edad642
--- /dev/null
+++ b/twrpinstall/include/twinstall/get_args.h
@@ -0,0 +1,17 @@
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "otautil/roots.h"
+
+#include "bootloader_message/include/bootloader_message/bootloader_message.h"
+
+extern std::string stage;
+
+class args {
+    public:
+        static std::vector<std::string> get_args(const int *argc, char*** const argv);
+};
\ No newline at end of file
diff --git a/twrpinstall/include/twinstall/install.h b/twrpinstall/include/twinstall/install.h
new file mode 100755
index 0000000..29647ad
--- /dev/null
+++ b/twrpinstall/include/twinstall/install.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stddef.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <ziparchive/zip_archive.h>
+
+#include "package.h"
+
+enum InstallResult {
+  INSTALL_SUCCESS,
+  INSTALL_ERROR,
+  INSTALL_CORRUPT,
+  INSTALL_NONE,
+  INSTALL_SKIPPED,
+  INSTALL_RETRY,
+  INSTALL_KEY_INTERRUPTED,
+  INSTALL_REBOOT,
+};
+
+enum class OtaType {
+  AB,
+  BLOCK,
+  BRICK,
+};
+
+static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
+static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
+
+// Installs the given update package. This function should also wipe the cache partition after a
+// successful installation if |should_wipe_cache| is true or an updater command asks to wipe the
+// cache.
+int install_package(const std::string& package, bool should_wipe_cache, bool needs_mount,
+                    int retry_count);
+
+// Verifies the package by ota keys. Returns true if the package is verified successfully,
+// otherwise returns false.
+bool verify_package(Package* package);
+
+// Reads meta data file of the package; parses each line in the format "key=value"; and writes the
+// result to |metadata|. Return true if succeed, otherwise return false.
+bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata);
+
+// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe.
+std::vector<std::string> GetWipePartitionList(Package* wipe_package);
+
+// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the
+// entry doesn't exist.
+bool verify_package_compatibility(ZipArchiveHandle package_zip);
+
+// Checks if the the metadata in the OTA package has expected values. Returns 0 on success.
+// Mandatory checks: ota-type, pre-device and serial number(if presents)
+// AB OTA specific checks: pre-build version, fingerprint, timestamp.
+int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type);
+bool HasUpdaterBinary(ZipArchiveHandle zip);
\ No newline at end of file
diff --git a/twrpinstall/include/twinstall/package.h b/twrpinstall/include/twinstall/package.h
new file mode 100755
index 0000000..50a4ffa
--- /dev/null
+++ b/twrpinstall/include/twinstall/package.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <ziparchive/zip_archive.h>
+
+#include "verifier.h"
+
+// This class serves as a wrapper for an OTA update package. It aims to provide the common
+// interface for both packages loaded in memory and packages read from fd.
+class Package : public VerifierInterface {
+ public:
+  static std::unique_ptr<Package> CreateMemoryPackage(
+      const std::string& path);
+  static std::unique_ptr<Package> CreateMemoryPackage(
+      std::vector<uint8_t> content);
+  static std::unique_ptr<Package> CreateFilePackage(const std::string& path,
+                                                    const std::function<void(float)>& set_progress);
+
+  virtual ~Package() = default;
+
+  // Opens the package as a zip file and returns the ZipArchiveHandle.
+  virtual ZipArchiveHandle GetZipArchiveHandle() = 0;
+
+  // Updates the progress in fraction during package verification.
+  void SetProgress(float progress) override;
+
+ protected:
+  // An optional function to update the progress.
+  std::function<void(float)> set_progress_;
+};
diff --git a/twrpinstall/include/twinstall/verifier.h b/twrpinstall/include/twinstall/verifier.h
new file mode 100644
index 0000000..5d66e85
--- /dev/null
+++ b/twrpinstall/include/twinstall/verifier.h
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <openssl/ec_key.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+constexpr size_t MiB = 1024 * 1024;
+
+using HasherUpdateCallback = std::function<void(const uint8_t* addr, uint64_t size)>;
+
+struct RSADeleter {
+  void operator()(RSA* rsa) const {
+    RSA_free(rsa);
+  }
+};
+
+struct ECKEYDeleter {
+  void operator()(EC_KEY* ec_key) const {
+    EC_KEY_free(ec_key);
+  }
+};
+
+struct Certificate {
+  typedef enum {
+    KEY_TYPE_RSA,
+    KEY_TYPE_EC,
+  } KeyType;
+
+  Certificate(int hash_len_, KeyType key_type_, std::unique_ptr<RSA, RSADeleter>&& rsa_,
+              std::unique_ptr<EC_KEY, ECKEYDeleter>&& ec_)
+      : hash_len(hash_len_), key_type(key_type_), rsa(std::move(rsa_)), ec(std::move(ec_)) {}
+
+  // SHA_DIGEST_LENGTH (SHA-1) or SHA256_DIGEST_LENGTH (SHA-256)
+  int hash_len;
+  KeyType key_type;
+  std::unique_ptr<RSA, RSADeleter> rsa;
+  std::unique_ptr<EC_KEY, ECKEYDeleter> ec;
+};
+
+class VerifierInterface {
+ public:
+  virtual ~VerifierInterface() = default;
+
+  // Returns the package size in bytes.
+  virtual uint64_t GetPackageSize() const = 0;
+
+  // Reads |byte_count| data starting from |offset|, and puts the result in |buffer|.
+  virtual bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) = 0;
+
+  // Updates the hash contexts for |length| bytes data starting from |start|.
+  virtual bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
+                                  uint64_t length) = 0;
+
+  // Updates the progress in fraction during package verification.
+  virtual void SetProgress(float progress) = 0;
+};
+
+//  Looks for an RSA signature embedded in the .ZIP file comment given the path to the zip.
+//  Verifies that it matches one of the given public keys. Returns VERIFY_SUCCESS or
+//  VERIFY_FAILURE (if any error is encountered or no key matches the signature).
+int verify_file(VerifierInterface* package, const std::vector<Certificate>& keys,
+                const std::function<void(float)>& set_progress = nullptr);
+
+// Checks that the RSA key has a modulus of 2048 or 4096 bits long, and public exponent is 3 or
+// 65537.
+bool CheckRSAKey(const std::unique_ptr<RSA, RSADeleter>& rsa);
+
+// Checks that the field size of the curve for the EC key is 256 bits.
+bool CheckECKey(const std::unique_ptr<EC_KEY, ECKEYDeleter>& ec_key);
+
+// Parses a PEM-encoded x509 certificate from the given buffer and saves it into |cert|. Returns
+// false if there is a parsing failure or the signature's encryption algorithm is not supported.
+bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert);
+
+// Iterates over the zip entries with the suffix "x509.pem" and returns a list of recognized
+// certificates. Returns an empty list if we fail to parse any of the entries.
+std::vector<Certificate> LoadKeysFromZipfile(const std::string& zip_name);
+
+#define VERIFY_SUCCESS 0
+#define VERIFY_FAILURE 1
diff --git a/twrpinstall/include/twinstall/wipe_data.h b/twrpinstall/include/twinstall/wipe_data.h
new file mode 100755
index 0000000..07b1e40
--- /dev/null
+++ b/twrpinstall/include/twinstall/wipe_data.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <functional>
+
+struct selabel_handle;
+
+// Returns true on success.
+bool WipeCache(const std::function<bool()>& confirm);
+
+// Returns true on success.
+bool WipeData(bool convert_fbe);
diff --git a/twrpinstall/install.cpp b/twrpinstall/install.cpp
new file mode 100755
index 0000000..f3c7769
--- /dev/null
+++ b/twrpinstall/install.cpp
@@ -0,0 +1,753 @@
+/*
+ * 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 "twinstall/install.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <functional>
+#include <limits>
+#include <mutex>
+#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 <android-base/unique_fd.h>
+#include <vintf/VintfObjectRecovery.h>
+
+#include "twinstall/package.h"
+#include "twinstall/verifier.h"
+#include "twinstall/wipe_data.h"
+#include "otautil/error_code.h"
+#include "otautil/paths.h"
+#include "otautil/roots.h"
+#include "otautil/sysutil.h"
+#include "otautil/thermalutil.h"
+#include "private/setup_commands.h"
+
+using namespace std::chrono_literals;
+
+static constexpr int kRecoveryApiVersion = 3;
+// Assert the version defined in code and in Android.mk are consistent.
+static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions.");
+
+// Default allocation of progress bar segments to operations
+// static constexpr int VERIFICATION_PROGRESS_TIME = 60;
+// static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
+
+static std::condition_variable finish_log_temperature;
+
+bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata) {
+  CHECK(metadata != nullptr);
+
+  static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
+  ZipString path(METADATA_PATH);
+  ZipEntry entry;
+  if (FindEntry(zip, path, &entry) != 0) {
+    LOG(ERROR) << "Failed to find " << METADATA_PATH;
+    return false;
+  }
+
+  uint32_t length = entry.uncompressed_length;
+  std::string metadata_string(length, '\0');
+  int32_t err =
+      ExtractToMemory(zip, &entry, reinterpret_cast<uint8_t*>(&metadata_string[0]), length);
+  if (err != 0) {
+    LOG(ERROR) << "Failed to extract " << METADATA_PATH << ": " << ErrorCodeString(err);
+    return false;
+  }
+
+  for (const std::string& line : android::base::Split(metadata_string, "\n")) {
+    size_t eq = line.find('=');
+    if (eq != std::string::npos) {
+      metadata->emplace(android::base::Trim(line.substr(0, eq)),
+                        android::base::Trim(line.substr(eq + 1)));
+    }
+  }
+
+  return true;
+}
+
+// Gets the value for the given key in |metadata|. Returns an emtpy string if the key isn't
+// present.
+static std::string get_value(const std::map<std::string, std::string>& metadata,
+                             const std::string& key) {
+  const auto& it = metadata.find(key);
+  return (it == metadata.end()) ? "" : it->second;
+}
+
+static std::string OtaTypeToString(OtaType type) {
+  switch (type) {
+    case OtaType::AB:
+      return "AB";
+    case OtaType::BLOCK:
+      return "BLOCK";
+    case OtaType::BRICK:
+      return "BRICK";
+  }
+}
+
+// Read the build.version.incremental of src/tgt from the metadata and log it to last_install.
+static void ReadSourceTargetBuild(const std::map<std::string, std::string>& metadata,
+                                  std::vector<std::string>* log_buffer) {
+  // Examples of the pre-build and post-build strings in metadata:
+  //   pre-build-incremental=2943039
+  //   post-build-incremental=2951741
+  auto source_build = get_value(metadata, "pre-build-incremental");
+  if (!source_build.empty()) {
+    log_buffer->push_back("source_build: " + source_build);
+  }
+
+  auto target_build = get_value(metadata, "post-build-incremental");
+  if (!target_build.empty()) {
+    log_buffer->push_back("target_build: " + target_build);
+  }
+}
+
+// Checks the build version, fingerprint and timestamp in the metadata of the A/B package.
+// Downgrading is not allowed unless explicitly enabled in the package and only for
+// incremental packages.
+static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata) {
+  // Incremental updates should match the current build.
+  auto device_pre_build = android::base::GetProperty("ro.build.version.incremental", "");
+  auto pkg_pre_build = get_value(metadata, "pre-build-incremental");
+  if (!pkg_pre_build.empty() && pkg_pre_build != device_pre_build) {
+    LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected "
+               << device_pre_build;
+    return INSTALL_ERROR;
+  }
+
+  auto device_fingerprint = android::base::GetProperty("ro.build.fingerprint", "");
+  auto pkg_pre_build_fingerprint = get_value(metadata, "pre-build");
+  if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != device_fingerprint) {
+    LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected "
+               << device_fingerprint;
+    return INSTALL_ERROR;
+  }
+
+  // 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.
+  auto pkg_post_timestamp_string = get_value(metadata, "post-timestamp");
+  // if (pkg_post_timestamp_string.empty() ||
+  //     !android::base::ParseInt(pkg_post_timestamp_string, &pkg_post_timestamp) ||
+  //     pkg_post_timestamp < build_timestamp) {
+  //   if (get_value(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;
+}
+
+int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type) {
+  auto package_ota_type = get_value(metadata, "ota-type");
+  auto expected_ota_type = OtaTypeToString(ota_type);
+  if (ota_type != OtaType::AB && ota_type != OtaType::BRICK) {
+    LOG(INFO) << "Skip package metadata check for ota type " << expected_ota_type;
+    return 0;
+  }
+
+  if (package_ota_type != expected_ota_type) {
+    LOG(ERROR) << "Unexpected ota package type, expects " << expected_ota_type << ", actual "
+               << package_ota_type;
+    return INSTALL_ERROR;
+  }
+
+  auto device = android::base::GetProperty("ro.product.device", "");
+  auto pkg_device = get_value(metadata, "pre-device");
+  if (pkg_device != device || pkg_device.empty()) {
+    LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << device;
+    return INSTALL_ERROR;
+  }
+
+  // We allow the package to not have any serialno; and we also allow it to carry multiple serial
+  // numbers split by "|"; e.g. serialno=serialno1|serialno2|serialno3 ... We will fail the
+  // verification if the device's serialno doesn't match any of these carried numbers.
+  auto pkg_serial_no = get_value(metadata, "serialno");
+  if (!pkg_serial_no.empty()) {
+    auto device_serial_no = android::base::GetProperty("ro.serialno", "");
+    bool serial_number_match = false;
+    for (const auto& number : android::base::Split(pkg_serial_no, "|")) {
+      if (device_serial_no == android::base::Trim(number)) {
+        serial_number_match = true;
+      }
+    }
+    if (!serial_number_match) {
+      LOG(ERROR) << "Package is for serial " << pkg_serial_no;
+      return INSTALL_ERROR;
+    }
+  }
+
+  if (ota_type == OtaType::AB) {
+    return CheckAbSpecificMetadata(metadata);
+  }
+
+  return 0;
+}
+
+int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
+                          std::vector<std::string>* cmd) {
+  CHECK(cmd != nullptr);
+
+  // 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;
+  }
+
+  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 = {
+    "/system/bin/update_engine_sideload",
+    "--payload=file://" + package,
+    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;
+}
+
+int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
+                             int status_fd, std::vector<std::string>* cmd) {
+  CHECK(cmd != nullptr);
+
+  // In non-A/B updates we extract the update binary from the package.
+  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;
+  }
+
+  LOG(ERROR) << "SetupNonAbUpdateCommands::here1";
+  const std::string binary_path = Paths::Get().temporary_update_binary();
+  unlink(binary_path.c_str());
+  LOG(ERROR) << "SetupNonAbUpdateCommands::here2";
+  android::base::unique_fd fd(
+      open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755));
+  if (fd == -1) {
+    PLOG(ERROR) << "Failed to create " << binary_path;
+    return INSTALL_ERROR;
+  }
+  LOG(ERROR) << "SetupNonAbUpdateCommands::here3";
+
+  int32_t error = ExtractEntryToFile(zip, &binary_entry, fd);
+  if (error != 0) {
+    LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error);
+    return INSTALL_ERROR;
+  }
+  LOG(ERROR) << "SetupNonAbUpdateCommands::here4";
+
+  // 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 name of the package zip file.
+  //   - an optional argument "retry" if this update is a retry of a failed update attempt.
+  *cmd = {
+    binary_path,
+    std::to_string(kRecoveryApiVersion),
+    std::to_string(status_fd),
+    package,
+  };
+  if (retry_count > 0) {
+    cmd->push_back("retry");
+  }
+  return 0;
+}
+
+static void log_max_temperature(int* max_temperature, const std::atomic<bool>& logger_finished) {
+  CHECK(max_temperature != nullptr);
+  std::mutex mtx;
+  std::unique_lock<std::mutex> lck(mtx);
+  while (!logger_finished.load() &&
+         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 std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
+                             std::vector<std::string>* log_buffer, int retry_count,
+                             int* max_temperature) {
+  std::map<std::string, std::string> metadata;
+
+  if (!ReadMetadataFromPackage(zip, &metadata)) {
+    LOG(ERROR) << "Failed to parse metadata in the zip file";
+    // return INSTALL_CORRUPT;
+  }
+
+  bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
+  // Verifies against the metadata in the package first.
+  // if (int check_status = is_ab ? CheckPackageMetadata(metadata, OtaType::AB) : 0;
+  //     check_status != 0) {
+  //   log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
+    // return check_status;
+  // }
+
+  ReadSourceTargetBuild(metadata, log_buffer);
+  LOG(ERROR) << "try_update_binary::here1";
+  // The updater in child process writes to the pipe to communicate with recovery.
+  android::base::unique_fd pipe_read, pipe_write;
+  // Explicitly disable O_CLOEXEC using 0 as the flags (last) parameter to Pipe
+  // so that the child updater process will recieve a non-closed fd.
+  if (!android::base::Pipe(&pipe_read, &pipe_write, 0)) {
+    PLOG(ERROR) << "Failed to create pipe for updater-recovery communication";
+    return INSTALL_CORRUPT;
+  }
+
+  // The updater-recovery communication protocol.
+  //
+  //   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).
+  //
+
+  std::vector<std::string> args;
+
+  is_ab = false;
+  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;
+    is_ab = true;
+  }
+
+  if (int update_status =
+          is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args)
+                : SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args);
+      update_status != 0) {
+    log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
+    // return update_status;
+  }
+
+  pid_t pid = fork();
+  if (pid == -1) {
+    PLOG(ERROR) << "Failed to fork update binary";
+    log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure));
+    return INSTALL_ERROR;
+  }
+
+  if (pid == 0) {
+    umask(022);
+    pipe_read.reset();
+
+    // Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
+    auto chr_args = StringVectorToNullTerminatedArray(args);
+    execv(chr_args[0], chr_args.data());
+    // 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.
+    // (Bug: 34769056)
+    fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
+    _exit(EXIT_FAILURE);
+  }
+  pipe_write.reset();
+
+  std::atomic<bool> logger_finished(false);
+  std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));
+
+  *wipe_cache = false;
+  bool retry_update = false;
+
+  char buffer[1024];
+  FILE* from_child = android::base::Fdopen(std::move(pipe_read), "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);
+
+  int status;
+  waitpid(pid, &status, 0);
+
+  logger_finished.store(true);
+  finish_log_temperature.notify_one();
+  temperature_logger.join();
+
+  if (retry_update) {
+    return INSTALL_RETRY;
+  }
+  if (WIFEXITED(status)) {
+    if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+      LOG(ERROR) << "Error in " << package << " (status " << WEXITSTATUS(status) << ")";
+      return INSTALL_ERROR;
+    }
+  } else if (WIFSIGNALED(status)) {
+    LOG(ERROR) << "Error in " << package << " (killed by signal " << WTERMSIG(status) << ")";
+    return INSTALL_ERROR;
+  } else {
+    LOG(FATAL) << "Invalid status code " << status;
+  }
+
+  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 __unused) {
+//   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 std::string& path, bool* wipe_cache __unused, bool needs_mount __unused,
+                                  std::vector<std::string>* log_buffer __unused, int retry_count __unused,
+                                  int* max_temperature __unused) {
+  // 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);
+  LOG(INFO) << "Update location: " << path;
+
+  // Map the update package into memory.
+  // ui->Print("Opening update package...\n");
+  if (needs_mount) {
+    if (path[0] == '@') {
+      ensure_path_mounted(path.substr(1));
+    } else {
+      ensure_path_mounted(path);
+    }
+  }
+
+  auto package = Package::CreateMemoryPackage(
+      path);
+  if (!package) {
+    log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
+    return INSTALL_CORRUPT;
+  }
+
+  // Verify package.
+  if (!verify_package(package.get())) {
+    log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
+    return INSTALL_CORRUPT;
+  }
+
+  // Try to open the package.
+  ZipArchiveHandle zip = package->GetZipArchiveHandle();
+  if (!zip) {
+    log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
+    return INSTALL_CORRUPT;
+  }
+  LOG(ERROR) << "really_install_package::here1";
+
+  // Additionally verify the compatibility of the package if it's a fresh install.
+  if (retry_count == 0 && !verify_package_compatibility(zip)) {
+    log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
+    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);
+  LOG(ERROR) << "really_install_package::here2";
+  int result =
+      try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);
+  // ui->SetEnableReboot(true);
+  // ui->Print("\n");
+  return result;
+}
+
+int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount,
+                    int retry_count) {
+  CHECK(!path.empty());
+
+  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) {
+  //   LOG(ERROR) << "failed to set up expected mounts for install; aborting";
+  //   result = INSTALL_ERROR;
+  // } else {
+    bool updater_wipe_cache = false;
+    result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,
+                                    retry_count, &max_temperature);
+    should_wipe_cache = should_wipe_cache || updater_wipe_cache;
+  // }
+
+  // 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());
+
+  bool has_cache = volume_for_mount_point("/cache") != nullptr;
+  // Skip logging the uncrypt_status on devices without /cache.
+  if (has_cache) {
+    static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
+    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)) {
+        PLOG(WARNING) << "failed to read uncrypt status";
+      } else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
+        LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
+      } else {
+        log_buffer.push_back(android::base::Trim(uncrypt_status));
+      }
+    }
+  }
+
+  // The first two lines need to be the package name and install result.
+  std::vector<std::string> log_header = {
+    path,
+    result == INSTALL_SUCCESS ? "1" : "0",
+    "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") + "\n";
+  const std::string& install_file = Paths::Get().temporary_install_file();
+  if (!android::base::WriteStringToFile(log_content, install_file)) {
+    PLOG(ERROR) << "failed to write " << install_file;
+  }
+
+  // Write a copy into last_log.
+  LOG(INFO) << log_content;
+
+  if (result == INSTALL_SUCCESS && should_wipe_cache) {
+    if (!WipeCache(nullptr)) {
+      result = INSTALL_ERROR;
+    }
+  }
+
+  return result;
+}
+
+bool verify_package(Package* package) {
+  static constexpr const char* CERTIFICATE_ZIP_FILE = "/system/etc/security/otacerts.zip";
+  std::vector<Certificate> loaded_keys = LoadKeysFromZipfile(CERTIFICATE_ZIP_FILE);
+  if (loaded_keys.empty()) {
+    LOG(ERROR) << "Failed to load keys";
+    return false;
+  }
+  LOG(INFO) << loaded_keys.size() << " key(s) loaded from " << CERTIFICATE_ZIP_FILE;
+
+  // Verify package.
+  // ui->Print("Verifying update package...\n");
+  // auto t0 = std::chrono::system_clock::now();
+  int err = verify_file(package, loaded_keys);
+  // if(err != VERIFY_SUCCESS) {
+
+  // }
+  // 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/twrpinstall/installcommand.cpp b/twrpinstall/installcommand.cpp
new file mode 100755
index 0000000..a03cb01
--- /dev/null
+++ b/twrpinstall/installcommand.cpp
@@ -0,0 +1,341 @@
+/*
+ * 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 <stdlib.h>
+#include <string>
+#include <vector>
+
+#ifdef AB_OTA_UPDATER
+#include <inttypes.h>
+#include <map>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#endif
+#include <cutils/properties.h>
+
+#include "common.h"
+#include "installcommand.h"
+#include <ziparchive/zip_archive.h>
+#include <vintf/VintfObjectRecovery.h>
+#include "twinstall/install.h"
+
+#ifdef AB_OTA_UPDATER
+
+static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt";
+static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
+static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
+
+// This function parses and returns the build.version.incremental
+static int parse_build_number(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;
+        if (android::base::ParseInt(num_string.c_str(), &build_number, 0)) {
+            return build_number;
+        }
+    }
+
+    printf("Failed to parse build number in %s.\n", str.c_str());
+    return -1;
+}
+
+bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data) {
+    ZipString binary_name(METADATA_PATH);
+    ZipEntry binary_entry;
+    if (FindEntry(zip, binary_name, &binary_entry) == 0) {
+        long size = binary_entry.uncompressed_length;
+        if (size <= 0)
+            return false;
+
+        meta_data->resize(size, '\0');
+        int32_t ret = ExtractToMemory(zip, &binary_entry, reinterpret_cast<uint8_t*>(&(*meta_data)[0]),
+                                  size);
+        if (ret != 0) {
+            printf("Failed to read metadata in update package.\n");
+            CloseArchive(zip);
+            return false;
+        }
+        return true;
+      }
+      return false;
+}
+
+// Read the build.version.incremental of src/tgt from the metadata and log it to last_install.
+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;
+    }
+    // Examples of the pre-build and post-build strings in metadata:
+    // pre-build-incremental=2943039
+    // post-build-incremental=2951741
+    std::vector<std::string> lines = android::base::Split(meta_data, "\n");
+    for (const std::string& line : lines) {
+        std::string str = android::base::Trim(line);
+        if (android::base::StartsWith(str, "pre-build-incremental")){
+            int source_build = parse_build_number(str);
+            if (source_build != -1) {
+                printf("source_build: %d\n", source_build);
+                /*log_buffer.push_back(android::base::StringPrintf("source_build: %d",
+                        source_build));*/
+            }
+        } else if (android::base::StartsWith(str, "post-build-incremental")) {
+            int target_build = parse_build_number(str);
+            if (target_build != -1) {
+                printf("target_build: %d\n", target_build);
+                /*log_buffer.push_back(android::base::StringPrintf("target_build: %d",
+                        target_build));*/
+            }
+        }
+    }
+}
+
+// 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(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);
+        }
+    }
+    char value[PROPERTY_VALUE_MAX];
+    char propmodel[PROPERTY_VALUE_MAX];
+    char propname[PROPERTY_VALUE_MAX];
+
+    property_get("ro.product.device", value, "");
+    property_get("ro.product.model", propmodel, "");
+    property_get("ro.product.name", propname, "");
+    const std::string& pkg_device = metadata["pre-device"];
+
+    std::vector<std::string> assertResults = android::base::Split(pkg_device, ",");
+
+    bool deviceExists = false;
+
+    for(const std::string& deviceAssert : assertResults)
+    {
+        std::string assertName = android::base::Trim(deviceAssert);
+        if ((assertName == value || assertName == propmodel || assertName == propname ) && !assertName.empty()) {
+            deviceExists = true;
+            break;
+        }
+    }
+
+    if (!deviceExists) {
+        printf("Package is for product %s but expected %s\n",
+            pkg_device.c_str(), 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) {
+        printf("Package is for serial %s\n", pkg_serial_no.c_str());
+        return INSTALL_ERROR;
+    }
+
+    if (metadata["ota-type"] != "AB") {
+        printf("Package is not A/B\n");
+        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) {
+        printf("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) {
+        printf("Package is for source build %s but expected %s\n",
+             pkg_pre_build_fingerprint.c_str(), value);
+        return INSTALL_ERROR;
+    }
+
+    return 0;
+}
+
+int
+abupdate_binary_command(const char* path, int retry_count __unused,
+                      int status_fd, std::vector<std::string>* cmd)
+{
+    auto package = Package::CreateMemoryPackage(path);
+	if (!package) {
+		return INSTALL_CORRUPT;
+	}
+
+	ZipArchiveHandle Zip = package->GetZipArchiveHandle();
+    read_source_target_build(Zip);
+    int ret = check_newer_ab_build(Zip);
+    if (ret) {
+        return ret;
+    }
+
+    // For A/B updates we extract the payload properties to a buffer and obtain
+    // the RAW payload offset in the zip file.
+    // if (!Zip->EntryExists(AB_OTA_PAYLOAD_PROPERTIES)) {
+	ZipString binary_name(AB_OTA_PAYLOAD_PROPERTIES);
+    ZipEntry binary_entry;
+    if (FindEntry(Zip, binary_name, &binary_entry) != 0) {
+        printf("Can't find %s\n", AB_OTA_PAYLOAD_PROPERTIES);
+        return INSTALL_CORRUPT;
+    }
+    std::vector<unsigned char> payload_properties(
+            binary_entry.uncompressed_length);
+    int32_t extract_ret = ExtractToMemory(Zip, &binary_entry, reinterpret_cast<uint8_t*>(payload_properties.data()),
+                                  binary_entry.uncompressed_length);
+    if (extract_ret != 0) {
+        printf("Can't extract %s\n", AB_OTA_PAYLOAD_PROPERTIES);
+        CloseArchive(Zip);
+        return false;
+    }
+
+    ZipString ab_ota_payload(AB_OTA_PAYLOAD);
+    ZipEntry ab_ota_payload_entry;
+    if (FindEntry(Zip, ab_ota_payload, &ab_ota_payload_entry) != 0) {
+        printf("Can't find %s\n", AB_OTA_PAYLOAD);
+        return INSTALL_CORRUPT;
+    }
+    // long payload_offset = Zip->GetEntryOffset(AB_OTA_PAYLOAD);
+    long payload_offset = ab_ota_payload_entry.offset;
+    *cmd = {
+        "/system/bin/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 INSTALL_SUCCESS;
+}
+
+#else
+
+void read_source_target_build(ZipArchiveHandle zip __unused /*, std::vector<std::string>& log_buffer*/) {return;}
+
+int
+abupdate_binary_command(__unused const char* path, __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;
+}
+
+#endif
+
+int
+update_binary_command(const char* path, int retry_count,
+                      int status_fd, std::vector<std::string>* cmd)
+{
+    char charfd[16];
+    sprintf(charfd, "%i", status_fd);
+    cmd->push_back(TMP_UPDATER_BINARY_PATH);
+    cmd->push_back(EXPAND(RECOVERY_API_VERSION));
+    cmd->push_back(charfd);
+    cmd->push_back(path);
+    /**cmd = {
+        TMP_UPDATER_BINARY_PATH,
+        EXPAND(RECOVERY_API_VERSION),   // defined in Android.mk
+        charfd,
+        path,
+    };*/
+    if (retry_count > 0)
+        cmd->push_back("retry");
+    return 0;
+}
+
+// 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 zw) {
+  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(zw, 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(zw, &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. TODO THIS CAUSES A WEIRD COMPILE ERROR
+  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;
+}
diff --git a/twrpinstall/legacy_property_service.cpp b/twrpinstall/legacy_property_service.cpp
new file mode 100644
index 0000000..c3990f7
--- /dev/null
+++ b/twrpinstall/legacy_property_service.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "../../bionic/libc/private/bionic_futex.h"
+
+#include <cutils/properties.h>
+
+#include "legacy_properties.h"
+
+#include <sys/mman.h>
+// Not available in 5.0
+//#include <sys/atomics.h>
+#include "legacy_property_service.h"
+
+#ifndef INT32_MAX
+#define INT32_MAX	(2147483647)
+#endif
+
+static int property_area_inited = 0;
+
+typedef struct {
+    void *data;
+    size_t size;
+    int fd;
+} workspace;
+
+static int init_workspace(workspace *w, size_t size)
+{
+    void *data;
+    int fd;
+
+        /* dev is a tmpfs that we can use to carve a shared workspace
+         * out of, so let's do that...
+         */
+    fd = open("/dev/__legacy_properties__", O_RDWR | O_CREAT, 0600);
+    if (fd < 0)
+        return -1;
+
+    if (ftruncate(fd, size) < 0)
+        goto out;
+
+    data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    if(data == MAP_FAILED)
+        goto out;
+
+    close(fd);
+
+    fd = open("/dev/__legacy_properties__", O_RDONLY);
+    if (fd < 0)
+        return -1;
+
+    unlink("/dev/__legacy_properties__");
+
+    w->data = data;
+    w->size = size;
+    w->fd = fd;
+    return 0;
+
+out:
+    close(fd);
+    return -1;
+}
+
+/* (8 header words + 247 toc words) = 1020 bytes */
+/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */
+
+#define PA_COUNT_MAX  247
+#define PA_INFO_START 1024
+#define PA_SIZE       32768
+
+static workspace pa_workspace;
+static prop_info *pa_info_array;
+
+prop_area *__legacy_property_area__;
+
+static int init_property_area(void)
+{
+    prop_area *pa;
+
+    if(pa_info_array)
+        return -1;
+
+    if(init_workspace(&pa_workspace, PA_SIZE))
+        return -1;
+
+    fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
+
+    pa_info_array = (prop_info*) (((char*) pa_workspace.data) + PA_INFO_START);
+
+    pa = (prop_area*)(pa_workspace.data);
+    memset(pa, 0, PA_SIZE);
+    pa->magic = PROP_AREA_MAGIC;
+    pa->version = PROP_AREA_VERSION;
+
+    /* plug into the lib property services */
+    __legacy_property_area__ = pa;
+    property_area_inited = 1;
+    return 0;
+}
+
+static void update_prop_info(prop_info *pi, const char *value, unsigned len)
+{
+    pi->serial = pi->serial | 1;
+    memcpy(pi->value, value, len + 1);
+    pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff);
+    __futex_wake(&pi->serial, INT32_MAX);
+}
+
+static const prop_info *__legacy_property_find(const char *name)
+{
+    prop_area *pa = __legacy_property_area__;
+    unsigned count = pa->count;
+    unsigned *toc = pa->toc;
+    unsigned len = strlen(name);
+    prop_info *pi;
+
+    while(count--) {
+        unsigned entry = *toc++;
+        if(TOC_NAME_LEN(entry) != len) continue;
+
+        pi = TOC_TO_INFO(pa, entry);
+        if(memcmp(name, pi->name, len)) continue;
+
+        return pi;
+    }
+
+    return 0;
+}
+
+static int legacy_property_set(const char *name, const char *value)
+{
+    prop_area *pa;
+    prop_info *pi;
+
+    int namelen = strlen(name);
+    int valuelen = strlen(value);
+
+    if(namelen >= PROP_NAME_MAX) return -1;
+    if(valuelen >= PROP_VALUE_MAX) return -1;
+    if(namelen < 1) return -1;
+
+    pi = (prop_info*) __legacy_property_find(name);
+
+
+    if(pi != 0) {
+        /* ro.* properties may NEVER be modified once set */
+        if(!strncmp(name, "ro.", 3)) return -1;
+
+        pa = __legacy_property_area__;
+        update_prop_info(pi, value, valuelen);
+        pa->serial++;
+        __futex_wake(&pa->serial, INT32_MAX);
+    } else {
+        pa = __legacy_property_area__;
+        if(pa->count == PA_COUNT_MAX) return -1;
+
+        pi = pa_info_array + pa->count;
+        pi->serial = (valuelen << 24);
+        memcpy(pi->name, name, namelen + 1);
+        memcpy(pi->value, value, valuelen + 1);
+
+        pa->toc[pa->count] =
+            (namelen << 24) | (((unsigned long) pi) - ((unsigned long) pa));
+
+        pa->count++;
+        pa->serial++;
+        __futex_wake(&pa->serial, INT32_MAX);
+    }
+
+    return 0;
+}
+
+void legacy_get_property_workspace(int *fd, int *sz)
+{
+    *fd = pa_workspace.fd;
+    *sz = pa_workspace.size;
+}
+
+static void copy_property_to_legacy(const char *key, const char *value, void *cookie __unused)
+{
+    legacy_property_set(key, value);
+}
+
+int legacy_properties_init()
+{
+    if(init_property_area() != 0)
+        return -1;
+
+    if(property_list(copy_property_to_legacy, 0) != 0)
+        return -1;
+
+    return 0;
+}
diff --git a/twrpinstall/package.cpp b/twrpinstall/package.cpp
new file mode 100755
index 0000000..ba1d5df
--- /dev/null
+++ b/twrpinstall/package.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2019 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 "twinstall/package.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+#include "otautil/error_code.h"
+#include "otautil/sysutil.h"
+
+// This class wraps the package in memory, i.e. a memory mapped package, or a package loaded
+// to a string/vector.
+class MemoryPackage : public Package {
+ public:
+  // Constructs the class from a file. We will memory maps the file later.
+  MemoryPackage(const std::string& path, std::unique_ptr<MemMapping> map);
+
+  // Constructs the class from the package bytes in |content|.
+  MemoryPackage(std::vector<uint8_t> content);
+
+  ~MemoryPackage() override;
+
+  // Memory maps the package file if necessary. Initializes the start address and size of the
+  // package.
+  uint64_t GetPackageSize() const override {
+    return package_size_;
+  }
+
+  bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
+
+  ZipArchiveHandle GetZipArchiveHandle() override;
+
+  bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
+                          uint64_t length) override;
+
+ private:
+  const uint8_t* addr_;    // Start address of the package in memory.
+  uint64_t package_size_;  // Package size in bytes.
+
+  // The memory mapped package.
+  std::unique_ptr<MemMapping> map_;
+  // A copy of the package content, valid only if we create the class with the exact bytes of
+  // the package.
+  std::vector<uint8_t> package_content_;
+  // The physical path to the package, empty if we create the class with the package content.
+  std::string path_;
+
+  // The ZipArchiveHandle of the package.
+  ZipArchiveHandle zip_handle_;
+};
+
+void Package::SetProgress(float progress) {
+  if (set_progress_) {
+    set_progress_(progress);
+  }
+}
+
+class FilePackage : public Package {
+ public:
+  FilePackage(android::base::unique_fd&& fd, uint64_t file_size, const std::string& path,
+              const std::function<void(float)>& set_progress);
+
+  ~FilePackage() override;
+
+  uint64_t GetPackageSize() const override {
+    return package_size_;
+  }
+
+  bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
+
+  ZipArchiveHandle GetZipArchiveHandle() override;
+
+  bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
+                          uint64_t length) override;
+
+ private:
+  android::base::unique_fd fd_;  // The underlying fd to the open package.
+  uint64_t package_size_;
+  std::string path_;  // The physical path to the package.
+
+  ZipArchiveHandle zip_handle_;
+};
+
+std::unique_ptr<Package> Package::CreateMemoryPackage(
+    const std::string& path) {
+  std::unique_ptr<MemMapping> mmap = std::make_unique<MemMapping>();
+  if (!mmap->MapFile(path)) {
+    LOG(ERROR) << "failed to map file";
+    return nullptr;
+  }
+
+  return std::make_unique<MemoryPackage>(path, std::move(mmap));
+}
+
+std::unique_ptr<Package> Package::CreateFilePackage(
+    const std::string& path, const std::function<void(float)>& set_progress) {
+  android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+  if (fd == -1) {
+    PLOG(ERROR) << "Failed to open " << path;
+    return nullptr;
+  }
+
+  off64_t file_size = lseek64(fd.get(), 0, SEEK_END);
+  if (file_size == -1) {
+    PLOG(ERROR) << "Failed to get the package size";
+    return nullptr;
+  }
+
+  return std::make_unique<FilePackage>(std::move(fd), file_size, path, set_progress);
+}
+
+std::unique_ptr<Package> Package::CreateMemoryPackage(
+    std::vector<uint8_t> content) {
+  return std::make_unique<MemoryPackage>(std::move(content));
+}
+
+MemoryPackage::MemoryPackage(const std::string& path, std::unique_ptr<MemMapping> map)
+    : map_(std::move(map)), path_(path), zip_handle_(nullptr) {
+  addr_ = map_->addr;
+  package_size_ = map_->length;
+}
+
+MemoryPackage::MemoryPackage(std::vector<uint8_t> content)
+    : package_content_(std::move(content)), zip_handle_(nullptr) {
+  CHECK(!package_content_.empty());
+  addr_ = package_content_.data();
+  package_size_ = package_content_.size();
+}
+
+MemoryPackage::~MemoryPackage() {
+  if (zip_handle_) {
+    CloseArchive(zip_handle_);
+  }
+}
+
+bool MemoryPackage::ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) {
+  if (byte_count > package_size_ || offset > package_size_ - byte_count) {
+    LOG(ERROR) << "Out of bound read, offset: " << offset << ", size: " << byte_count
+               << ", total package_size: " << package_size_;
+    return false;
+  }
+  memcpy(buffer, addr_ + offset, byte_count);
+  return true;
+}
+
+bool MemoryPackage::UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers,
+                                       uint64_t start, uint64_t length) {
+  if (length > package_size_ || start > package_size_ - length) {
+    LOG(ERROR) << "Out of bound read, offset: " << start << ", size: " << length
+               << ", total package_size: " << package_size_;
+    return false;
+  }
+
+  for (const auto& hasher : hashers) {
+    hasher(addr_ + start, length);
+  }
+  return true;
+}
+
+ZipArchiveHandle MemoryPackage::GetZipArchiveHandle() {
+  if (zip_handle_) {
+    return zip_handle_;
+  }
+
+  if (auto err = OpenArchiveFromMemory(const_cast<uint8_t*>(addr_), package_size_, path_.c_str(),
+                                       &zip_handle_);
+      err != 0) {
+    LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err);
+    return nullptr;
+  }
+
+  return zip_handle_;
+}
+
+FilePackage::FilePackage(android::base::unique_fd&& fd, uint64_t file_size, const std::string& path,
+                         const std::function<void(float)>& set_progress)
+    : fd_(std::move(fd)), package_size_(file_size), path_(path), zip_handle_(nullptr) {
+  set_progress_ = set_progress;
+}
+
+FilePackage::~FilePackage() {
+  if (zip_handle_) {
+    CloseArchive(zip_handle_);
+  }
+}
+
+bool FilePackage::ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) {
+  if (byte_count > package_size_ || offset > package_size_ - byte_count) {
+    LOG(ERROR) << "Out of bound read, offset: " << offset << ", size: " << byte_count
+               << ", total package_size: " << package_size_;
+    return false;
+  }
+
+  if (!android::base::ReadFullyAtOffset(fd_.get(), buffer, byte_count, offset)) {
+    PLOG(ERROR) << "Failed to read " << byte_count << " bytes data at offset " << offset;
+    return false;
+  }
+
+  return true;
+}
+
+bool FilePackage::UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers,
+                                     uint64_t start, uint64_t length) {
+  if (length > package_size_ || start > package_size_ - length) {
+    LOG(ERROR) << "Out of bound read, offset: " << start << ", size: " << length
+               << ", total package_size: " << package_size_;
+    return false;
+  }
+
+  uint64_t so_far = 0;
+  while (so_far < length) {
+    uint64_t read_size = std::min<uint64_t>(length - so_far, 16 * MiB);
+    std::vector<uint8_t> buffer(read_size);
+    if (!ReadFullyAtOffset(buffer.data(), read_size, start + so_far)) {
+      return false;
+    }
+
+    for (const auto& hasher : hashers) {
+      hasher(buffer.data(), read_size);
+    }
+    so_far += read_size;
+  }
+
+  return true;
+}
+
+ZipArchiveHandle FilePackage::GetZipArchiveHandle() {
+  if (zip_handle_) {
+    return zip_handle_;
+  }
+
+  if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_); err != 0) {
+    LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err);
+    return nullptr;
+  }
+
+  return zip_handle_;
+}
diff --git a/twrpinstall/set_metadata.cpp b/twrpinstall/set_metadata.cpp
new file mode 100644
index 0000000..2e1d769
--- /dev/null
+++ b/twrpinstall/set_metadata.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 The Team Win Recovery 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.
+ */
+
+/*
+ * The purpose of these functions is to try to get and set the proper
+ * file permissions, SELinux contexts, owner, and group so that these
+ * files are accessible when we boot up to normal Android via MTP and to
+ * file manager apps. During early boot we try to read the contexts and
+ * owner / group info from /data/media or from /data/media/0 and store
+ * them in static variables. From there, we'll try to set the same
+ * contexts, owner, and group information on most files we create during
+ * operations like backups, copying the log, and MTP operations.
+ */
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "selinux/selinux.h"
+
+static security_context_t selinux_context;
+struct stat s;
+static int has_stat = 0;
+
+int tw_get_context(const char* filename) {
+	if (lgetfilecon(filename, &selinux_context) >= 0) {
+		printf("tw_get_context got selinux context: %s\n", selinux_context);
+		return 0;
+	} else {
+		printf("tw_get_context failed to get selinux context\n");
+		selinux_context = NULL;
+	}
+	return -1;
+}
+
+int tw_get_stat(const char* filename) {
+	if (lstat(filename, &s) == 0) {
+		has_stat = 1;
+		return 0;
+	}
+	printf("tw_get_stat failed to lstat '%s'\n", filename);
+	return -1;
+}
+
+int tw_get_default_metadata(const char* filename) {
+	if (tw_get_context(filename) == 0 && tw_get_stat(filename) == 0)
+		return 0;
+	return -1;
+}
+
+// Most of this logging is disabled to prevent log spam if we are trying
+// to set contexts and permissions on file systems that do not support
+// these types of things (e.g. vfat / FAT / FAT32).
+int tw_set_default_metadata(const char* filename) {
+	int ret = 0;
+	struct stat st;
+
+	if (selinux_context == NULL) {
+		//printf("selinux_context was null, '%s'\n", filename);
+		ret = -1;
+	} else if (lsetfilecon(filename, selinux_context) < 0) {
+		//printf("Failed to set default contexts on '%s'.\n", filename);
+		ret = -1;
+	}
+
+	if (lstat(filename, &st) == 0 && st.st_mode & S_IFREG && chmod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) < 0) {
+		//printf("Failed to chmod '%s'\n", filename);
+		ret = -1;
+	}
+
+	if (has_stat && chown(filename, s.st_uid, s.st_gid) < 0) {
+		//printf("Failed to lchown '%s'.\n", filename);
+		ret = -1;
+	}
+	//printf("Done trying to set defaults on '%s'\n");
+	return ret;
+}
diff --git a/twrpinstall/tw_atomic.cpp b/twrpinstall/tw_atomic.cpp
new file mode 100644
index 0000000..31cdd85
--- /dev/null
+++ b/twrpinstall/tw_atomic.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Team Win Recovery 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 <pthread.h>
+#include <stdio.h>
+#include "tw_atomic.hpp"
+
+/*
+ * According to this documentation:
+ * https://developer.android.com/training/articles/smp.html
+ * it is recommended to use mutexes instead of atomics. This class
+ * provides us with a wrapper to make "atomic" variables easy to use.
+ */
+
+TWAtomicInt::TWAtomicInt(int initial_value /* = 0 */) {
+	if (pthread_mutex_init(&mutex_lock, NULL) != 0) {
+		// This should hopefully never happen. If it does, the
+		// operations will not be atomic, but we will allow things to
+		// continue anyway after logging the issue and just hope for
+		// the best.
+		printf("TWAtomic error initializing mutex.\n");
+		use_mutex = false;
+	} else {
+		use_mutex = true;
+	}
+	value = initial_value;
+}
+
+TWAtomicInt::~TWAtomicInt() {
+	if (use_mutex)
+		pthread_mutex_destroy(&mutex_lock);
+}
+
+void TWAtomicInt::set_value(int new_value) {
+	if (use_mutex) {
+		pthread_mutex_lock(&mutex_lock);
+		value = new_value;
+		pthread_mutex_unlock(&mutex_lock);
+	} else {
+		value = new_value;
+	}
+}
+
+int TWAtomicInt::get_value(void) {
+	int ret_val;
+
+	if (use_mutex) {
+		pthread_mutex_lock(&mutex_lock);
+		ret_val = value;
+		pthread_mutex_unlock(&mutex_lock);
+	} else {
+		ret_val = value;
+	}
+	return ret_val;
+}
diff --git a/twrpinstall/twinstall.cpp b/twrpinstall/twinstall.cpp
new file mode 100755
index 0000000..65a83a6
--- /dev/null
+++ b/twrpinstall/twinstall.cpp
@@ -0,0 +1,478 @@
+/*
+	Copyright 2012 to 2017 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <cutils/properties.h>
+
+#include <android-base/unique_fd.h>
+
+#include "twcommon.h"
+#include "mtdutils/mounts.h"
+#include "mtdutils/mtdutils.h"
+
+#include "otautil/sysutil.h"
+#include <ziparchive/zip_archive.h>
+#include "twinstall/install.h"
+#include "twinstall/verifier.h"
+#include "variables.h"
+#include "data.hpp"
+#include "partitions.hpp"
+#include "twrpDigestDriver.hpp"
+#include "twrpDigest/twrpDigest.hpp"
+#include "twrpDigest/twrpMD5.hpp"
+#include "twrp-functions.hpp"
+#include "gui/gui.hpp"
+#include "gui/pages.hpp"
+#include "legacy_property_service.h"
+#include "twinstall.h"
+#include "installcommand.h"
+extern "C" {
+	#include "gui/gui.h"
+}
+
+#define AB_OTA "payload_properties.txt"
+
+#ifndef TW_NO_LEGACY_PROPS
+static const char* properties_path = "/dev/__properties__";
+static const char* properties_path_renamed = "/dev/__properties_kk__";
+static bool legacy_props_env_initd = false;
+static bool legacy_props_path_modified = false;
+#endif
+
+enum zip_type {
+	UNKNOWN_ZIP_TYPE = 0,
+	UPDATE_BINARY_ZIP_TYPE,
+	AB_OTA_ZIP_TYPE,
+	TWRP_THEME_ZIP_TYPE
+};
+
+#ifndef TW_NO_LEGACY_PROPS
+// to support pre-KitKat update-binaries that expect properties in the legacy format
+static int switch_to_legacy_properties()
+{
+	if (!legacy_props_env_initd) {
+		if (legacy_properties_init() != 0)
+			return -1;
+
+		char tmp[32];
+		int propfd, propsz;
+		legacy_get_property_workspace(&propfd, &propsz);
+		sprintf(tmp, "%d,%d", dup(propfd), propsz);
+		setenv("ANDROID_PROPERTY_WORKSPACE", tmp, 1);
+		legacy_props_env_initd = true;
+	}
+
+	if (TWFunc::Path_Exists(properties_path)) {
+		// hide real properties so that the updater uses the envvar to find the legacy format properties
+		if (rename(properties_path, properties_path_renamed) != 0) {
+			LOGERR("Renaming %s failed: %s\n", properties_path, strerror(errno));
+			return -1;
+		} else {
+			legacy_props_path_modified = true;
+		}
+	}
+
+	return 0;
+}
+
+static int switch_to_new_properties()
+{
+	if (TWFunc::Path_Exists(properties_path_renamed)) {
+		if (rename(properties_path_renamed, properties_path) != 0) {
+			LOGERR("Renaming %s failed: %s\n", properties_path_renamed, strerror(errno));
+			return -1;
+		} else {
+			legacy_props_path_modified = false;
+		}
+	}
+
+	return 0;
+}
+#endif
+
+static int Install_Theme(const char* path, ZipArchiveHandle Zip) {
+#ifdef TW_OEM_BUILD // We don't do custom themes in OEM builds
+	CloseArchive(Zip);
+	return INSTALL_CORRUPT;
+#else
+	ZipString binary_name("ui.xml");
+	ZipEntry binary_entry;
+	if (FindEntry(Zip, binary_name, &binary_entry) != 0) {
+		CloseArchive(Zip);
+		return INSTALL_CORRUPT;
+	}
+	if (!PartitionManager.Mount_Settings_Storage(true))
+		return INSTALL_ERROR;
+	string theme_path = DataManager::GetSettingsStoragePath();
+	theme_path += "/TWRP/theme";
+	if (!TWFunc::Path_Exists(theme_path)) {
+		if (!TWFunc::Recursive_Mkdir(theme_path)) {
+			return INSTALL_ERROR;
+		}
+	}
+	theme_path += "/ui.zip";
+	if (TWFunc::copy_file(path, theme_path, 0644) != 0) {
+		return INSTALL_ERROR;
+	}
+	LOGINFO("Installing custom theme '%s' to '%s'\n", path, theme_path.c_str());
+	PageManager::RequestReload();
+	return INSTALL_SUCCESS;
+#endif
+}
+
+static int Prepare_Update_Binary(ZipArchiveHandle Zip) {
+	char arches[PATH_MAX];
+	property_get("ro.product.cpu.abilist", arches, "error");
+	if (strcmp(arches, "error") == 0)
+		property_get("ro.product.cpu.abi", arches, "error");
+	vector<string> split = TWFunc::split_string(arches, ',', true);
+	std::vector<string>::iterator arch;
+	std::string base_name = UPDATE_BINARY_NAME;
+	base_name += "-";
+	ZipEntry binary_entry;
+	ZipString update_binary_string(UPDATE_BINARY_NAME);
+	if (FindEntry(Zip, update_binary_string, &binary_entry) != 0) {
+		for (arch = split.begin(); arch != split.end(); arch++) {
+			std::string temp = base_name + *arch;
+			ZipString binary_name(temp.c_str());
+			if (FindEntry(Zip, binary_name, &binary_entry) != 0) {
+				ZipString binary_name(temp.c_str());
+				break;
+			}
+		}
+	}
+	LOGINFO("Extracting updater binary '%s'\n", UPDATE_BINARY_NAME);
+	unlink(TMP_UPDATER_BINARY_PATH);
+	android::base::unique_fd fd(
+		open(TMP_UPDATER_BINARY_PATH, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755));
+	if (fd == -1) {
+		return INSTALL_ERROR;
+	}
+	int32_t err = ExtractEntryToFile(Zip, &binary_entry, fd);
+	if (err != 0) {
+		CloseArchive(Zip);
+		LOGERR("Could not extract '%s'\n", UPDATE_BINARY_NAME);
+		return INSTALL_ERROR;
+	}
+
+	// If exists, extract file_contexts from the zip file
+	ZipString file_contexts("file_contexts");
+	ZipEntry file_contexts_entry;
+	if (FindEntry(Zip, file_contexts, &file_contexts_entry) != 0) {
+		LOGINFO("Zip does not contain SELinux file_contexts file in its root.\n");
+	} else {
+		const string output_filename = "/file_contexts";
+		LOGINFO("Zip contains SELinux file_contexts file in its root. Extracting to %s\n", output_filename.c_str());
+		android::base::unique_fd fd(
+			open(output_filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0644));
+		if (fd == -1) {
+			return INSTALL_ERROR;
+		}
+		if (ExtractEntryToFile(Zip, &file_contexts_entry, fd)) {
+			CloseArchive(Zip);
+			LOGERR("Could not extract '%s'\n", output_filename.c_str());
+			return INSTALL_ERROR;
+		}
+	}
+	return INSTALL_SUCCESS;
+}
+
+#ifndef TW_NO_LEGACY_PROPS
+static bool update_binary_has_legacy_properties(const char *binary) {
+	const char str_to_match[] = "ANDROID_PROPERTY_WORKSPACE";
+	int len_to_match = sizeof(str_to_match) - 1;
+	bool found = false;
+
+	int fd = open(binary, O_RDONLY);
+	if (fd < 0) {
+		LOGINFO("has_legacy_properties: Could not open %s: %s!\n", binary, strerror(errno));
+		return false;
+	}
+
+	struct stat finfo;
+	if (fstat(fd, &finfo) < 0) {
+		LOGINFO("has_legacy_properties: Could not fstat %d: %s!\n", fd, strerror(errno));
+		close(fd);
+		return false;
+	}
+
+	void *data = mmap(NULL, finfo.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (data == MAP_FAILED) {
+		LOGINFO("has_legacy_properties: mmap (size=%zu) failed: %s!\n", (size_t)finfo.st_size, strerror(errno));
+	} else {
+		if (memmem(data, finfo.st_size, str_to_match, len_to_match)) {
+			LOGINFO("has_legacy_properties: Found legacy property match!\n");
+			found = true;
+		}
+		munmap(data, finfo.st_size);
+	}
+	close(fd);
+
+	return found;
+}
+#endif
+
+static int Run_Update_Binary(const char *path, int* wipe_cache, zip_type ztype) {
+	int ret_val, pipe_fd[2], status, zip_verify;
+	char buffer[1024];
+	FILE* child_data;
+
+#ifndef TW_NO_LEGACY_PROPS
+	if (!update_binary_has_legacy_properties(TMP_UPDATER_BINARY_PATH)) {
+		LOGINFO("Legacy property environment not used in updater.\n");
+	} else if (switch_to_legacy_properties() != 0) { /* Set legacy properties */
+		LOGERR("Legacy property environment did not initialize successfully. Properties may not be detected.\n");
+	} else {
+		LOGINFO("Legacy property environment initialized.\n");
+	}
+#endif
+
+	pipe(pipe_fd);
+
+	std::vector<std::string> args;
+    if (ztype == UPDATE_BINARY_ZIP_TYPE) {
+		ret_val = update_binary_command(path, 0, pipe_fd[1], &args);
+    } else if (ztype == AB_OTA_ZIP_TYPE) {
+		ret_val = abupdate_binary_command(path, 0, pipe_fd[1], &args);
+	} else {
+		LOGERR("Unknown zip type %i\n", ztype);
+		ret_val = INSTALL_CORRUPT;
+	}
+    if (ret_val) {
+        close(pipe_fd[0]);
+        close(pipe_fd[1]);
+        return ret_val;
+    }
+
+	// 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 == 0) {
+		close(pipe_fd[0]);
+		execve(chr_args[0], const_cast<char**>(chr_args), environ);
+		printf("E:Can't execute '%s': %s\n", chr_args[0], strerror(errno));
+		_exit(-1);
+	}
+	close(pipe_fd[1]);
+
+	*wipe_cache = 0;
+
+	DataManager::GetValue(TW_SIGNED_ZIP_VERIFY_VAR, zip_verify);
+	child_data = fdopen(pipe_fd[0], "r");
+	while (fgets(buffer, sizeof(buffer), child_data) != NULL) {
+		char* command = strtok(buffer, " \n");
+		if (command == NULL) {
+			continue;
+		} else if (strcmp(command, "progress") == 0) {
+			char* fraction_char = strtok(NULL, " \n");
+			char* seconds_char = strtok(NULL, " \n");
+
+			float fraction_float = strtof(fraction_char, NULL);
+			int seconds_float = strtol(seconds_char, NULL, 10);
+
+			if (zip_verify)
+				DataManager::ShowProgress(fraction_float * (1 - VERIFICATION_PROGRESS_FRACTION), seconds_float);
+			else
+				DataManager::ShowProgress(fraction_float, seconds_float);
+		} else if (strcmp(command, "set_progress") == 0) {
+			char* fraction_char = strtok(NULL, " \n");
+			float fraction_float = strtof(fraction_char, NULL);
+			DataManager::_SetProgress(fraction_float);
+		} else if (strcmp(command, "ui_print") == 0) {
+			char* display_value = strtok(NULL, "\n");
+			if (display_value) {
+				gui_print("%s", display_value);
+			} else {
+				gui_print("\n");
+			}
+		} else if (strcmp(command, "wipe_cache") == 0) {
+			*wipe_cache = 1;
+		} else if (strcmp(command, "clear_display") == 0) {
+			// Do nothing, not supported by TWRP
+		} else if (strcmp(command, "log") == 0) {
+			printf("%s\n", strtok(NULL, "\n"));
+		} else {
+			LOGERR("unknown command [%s]\n", command);
+		}
+	}
+	fclose(child_data);
+
+	int waitrc = TWFunc::Wait_For_Child(pid, &status, "Updater");
+
+#ifndef TW_NO_LEGACY_PROPS
+	/* Unset legacy properties */
+	if (legacy_props_path_modified) {
+		if (switch_to_new_properties() != 0) {
+			LOGERR("Legacy property environment did not disable successfully. Legacy properties may still be in use.\n");
+		} else {
+			LOGINFO("Legacy property environment disabled.\n");
+		}
+	}
+#endif
+
+	if (waitrc != 0)
+		return INSTALL_ERROR;
+
+	return INSTALL_SUCCESS;
+}
+
+int TWinstall_zip(const char* path, int* wipe_cache) {
+	int ret_val, zip_verify = 1, unmount_system = 1;
+
+	gui_msg(Msg("installing_zip=Installing zip file '{1}'")(path));
+	if (strlen(path) < 9 || strncmp(path, "/sideload", 9) != 0) {
+		string digest_str;
+		string Full_Filename = path;
+
+		gui_msg("check_for_digest=Checking for Digest file...");
+
+		if (*path != '@' && !twrpDigestDriver::Check_File_Digest(Full_Filename)) {
+			LOGERR("Aborting zip install: Digest verification failed\n");
+			return INSTALL_CORRUPT;
+		}
+	}
+
+	DataManager::GetValue(TW_UNMOUNT_SYSTEM, unmount_system);
+
+#ifndef TW_OEM_BUILD
+	DataManager::GetValue(TW_SIGNED_ZIP_VERIFY_VAR, zip_verify);
+#endif
+	DataManager::SetProgress(0);
+
+	auto package = Package::CreateMemoryPackage(path);
+	if (!package) {
+		return INSTALL_CORRUPT;
+	}
+
+	if (zip_verify) {
+		gui_msg("verify_zip_sig=Verifying zip signature...");
+		static constexpr const char* CERTIFICATE_ZIP_FILE = "/system/etc/security/otacerts.zip";
+		std::vector<Certificate> loaded_keys = LoadKeysFromZipfile(CERTIFICATE_ZIP_FILE);
+		if (loaded_keys.empty()) {
+			LOGERR("Failed to load keys\n");
+			return -1;
+		}
+		LOGINFO("%zu key(s) loaded from %s\n", loaded_keys.size(), CERTIFICATE_ZIP_FILE);
+
+		ret_val = verify_file(package.get(), loaded_keys, std::bind(&DataManager::SetProgress, std::placeholders::_1));
+		if (ret_val != VERIFY_SUCCESS) {
+			LOGINFO("Zip signature verification failed: %i\n", ret_val);
+			gui_err("verify_zip_fail=Zip signature verification failed!");
+#ifdef USE_MINZIP
+			sysReleaseMap(&map);
+#endif
+			return -1;
+		} else {
+			gui_msg("verify_zip_done=Zip signature verified successfully.");
+		}
+	}
+
+	ZipArchiveHandle Zip = package->GetZipArchiveHandle();
+	if (!Zip) {
+		return INSTALL_CORRUPT;
+	}
+
+	if (unmount_system) {
+		gui_msg("unmount_system=Unmounting System...");
+		if(!PartitionManager.UnMount_By_Path(PartitionManager.Get_Android_Root_Path(), true)) {
+			gui_err("unmount_system_err=Failed unmounting System");
+			return -1;
+		}
+		unlink("/system");
+		mkdir("/system", 0755);
+	}
+
+	time_t start, stop;
+	time(&start);
+
+	ZipString update_binary_name(UPDATE_BINARY_NAME);
+	ZipEntry update_binary_entry;
+	if (FindEntry(Zip, update_binary_name, &update_binary_entry) == 0) {
+		LOGINFO("Update binary zip\n");
+		// Additionally verify the compatibility of the package.
+		if (!verify_package_compatibility(Zip)) {
+			gui_err("zip_compatible_err=Zip Treble compatibility error!");
+			CloseArchive(Zip);
+			ret_val = INSTALL_CORRUPT;
+		} else {
+			ret_val = Prepare_Update_Binary(Zip);
+			if (ret_val == INSTALL_SUCCESS)
+				ret_val = Run_Update_Binary(path, wipe_cache, UPDATE_BINARY_ZIP_TYPE);
+		}
+	} else {
+		ZipString ab_binary_name(AB_OTA);
+		ZipEntry ab_binary_entry;
+		if (FindEntry(Zip, ab_binary_name, &ab_binary_entry) == 0) {
+			LOGINFO("AB zip\n");
+			gui_msg(Msg(msg::kHighlight, "flash_ab_inactive=Flashing A/B zip to inactive slot: {1}")(PartitionManager.Get_Active_Slot_Display()=="A"?"B":"A"));
+			// We need this so backuptool can do its magic
+			bool system_mount_state = PartitionManager.Is_Mounted_By_Path(PartitionManager.Get_Android_Root_Path());
+			bool vendor_mount_state = PartitionManager.Is_Mounted_By_Path("/vendor");
+			PartitionManager.Mount_By_Path(PartitionManager.Get_Android_Root_Path(), true);
+			PartitionManager.Mount_By_Path("/vendor", true);
+			TWFunc::copy_file("/system/bin/sh", "/tmp/sh", 0755);
+			mount("/tmp/sh", "/system/bin/sh", "auto", MS_BIND, NULL);
+			ret_val = Run_Update_Binary(path, wipe_cache, AB_OTA_ZIP_TYPE);
+			umount("/system/bin/sh");
+			unlink("/tmp/sh");
+			if (!vendor_mount_state)
+				PartitionManager.UnMount_By_Path("/vendor", true);
+			if (!system_mount_state)
+				PartitionManager.UnMount_By_Path(PartitionManager.Get_Android_Root_Path(), true);
+			gui_warn("flash_ab_reboot=To flash additional zips, please reboot recovery to switch to the updated slot.");
+		} else {
+			ZipString binary_name("ui.xml");
+			ZipEntry binary_entry;
+			if (FindEntry(Zip, binary_name, &binary_entry) != 0) {
+				LOGINFO("TWRP theme zip\n");
+				ret_val = Install_Theme(path, Zip);
+			} else {
+				CloseArchive(Zip);
+				ret_val = INSTALL_CORRUPT;
+			}
+		}
+	}
+	time(&stop);
+	int total_time = (int) difftime(stop, start);
+	if (ret_val == INSTALL_CORRUPT) {
+		gui_err("invalid_zip_format=Invalid zip file format!");
+	} else {
+		LOGINFO("Install took %i second(s).\n", total_time);
+	}
+	return ret_val;
+}
diff --git a/twrpinstall/verifier.cpp b/twrpinstall/verifier.cpp
new file mode 100755
index 0000000..f577961
--- /dev/null
+++ b/twrpinstall/verifier.cpp
@@ -0,0 +1,549 @@
+/*
+ * 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 "twinstall/verifier.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/ecdsa.h>
+#include <openssl/evp.h>
+#include <openssl/obj_mac.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <ziparchive/zip_archive.h>
+
+#include "otautil/print_sha1.h"
+#include "private/asn1_decoder.h"
+
+/*
+ * Simple version of PKCS#7 SignedData extraction. This extracts the
+ * signature OCTET STRING to be used for signature verification.
+ *
+ * For full details, see http://www.ietf.org/rfc/rfc3852.txt
+ *
+ * The PKCS#7 structure looks like:
+ *
+ *   SEQUENCE (ContentInfo)
+ *     OID (ContentType)
+ *     [0] (content)
+ *       SEQUENCE (SignedData)
+ *         INTEGER (version CMSVersion)
+ *         SET (DigestAlgorithmIdentifiers)
+ *         SEQUENCE (EncapsulatedContentInfo)
+ *         [0] (CertificateSet OPTIONAL)
+ *         [1] (RevocationInfoChoices OPTIONAL)
+ *         SET (SignerInfos)
+ *           SEQUENCE (SignerInfo)
+ *             INTEGER (CMSVersion)
+ *             SEQUENCE (SignerIdentifier)
+ *             SEQUENCE (DigestAlgorithmIdentifier)
+ *             SEQUENCE (SignatureAlgorithmIdentifier)
+ *             OCTET STRING (SignatureValue)
+ */
+static bool read_pkcs7(const uint8_t* pkcs7_der, size_t pkcs7_der_len,
+                       std::vector<uint8_t>* sig_der) {
+  CHECK(sig_der != nullptr);
+  sig_der->clear();
+
+  asn1_context ctx(pkcs7_der, pkcs7_der_len);
+
+  std::unique_ptr<asn1_context> pkcs7_seq(ctx.asn1_sequence_get());
+  if (pkcs7_seq == nullptr || !pkcs7_seq->asn1_sequence_next()) {
+    return false;
+  }
+
+  std::unique_ptr<asn1_context> signed_data_app(pkcs7_seq->asn1_constructed_get());
+  if (signed_data_app == nullptr) {
+    return false;
+  }
+
+  std::unique_ptr<asn1_context> signed_data_seq(signed_data_app->asn1_sequence_get());
+  if (signed_data_seq == nullptr || !signed_data_seq->asn1_sequence_next() ||
+      !signed_data_seq->asn1_sequence_next() || !signed_data_seq->asn1_sequence_next() ||
+      !signed_data_seq->asn1_constructed_skip_all()) {
+    return false;
+  }
+
+  std::unique_ptr<asn1_context> sig_set(signed_data_seq->asn1_set_get());
+  if (sig_set == nullptr) {
+    return false;
+  }
+
+  std::unique_ptr<asn1_context> sig_seq(sig_set->asn1_sequence_get());
+  if (sig_seq == nullptr || !sig_seq->asn1_sequence_next() || !sig_seq->asn1_sequence_next() ||
+      !sig_seq->asn1_sequence_next() || !sig_seq->asn1_sequence_next()) {
+    return false;
+  }
+
+  const uint8_t* sig_der_ptr;
+  size_t sig_der_length;
+  if (!sig_seq->asn1_octet_string_get(&sig_der_ptr, &sig_der_length)) {
+    return false;
+  }
+
+  sig_der->resize(sig_der_length);
+  std::copy(sig_der_ptr, sig_der_ptr + sig_der_length, sig_der->begin());
+  return true;
+}
+
+int verify_file(VerifierInterface* package, const std::vector<Certificate>& keys,
+  const std::function<void(float)>& set_progress) {
+  CHECK(package);
+  package->SetProgress(0.0);
+
+  if (set_progress) {
+    set_progress(0.0);
+  }
+
+  // An archive with a whole-file signature will end in six bytes:
+  //
+  //   (2-byte signature start) $ff $ff (2-byte comment size)
+  //
+  // (As far as the ZIP format is concerned, these are part of the archive comment.) We start by
+  // reading this footer, this tells us how far back from the end we have to start reading to find
+  // the whole comment.
+
+#define FOOTER_SIZE 6
+  uint64_t length = package->GetPackageSize();
+
+  if (length < FOOTER_SIZE) {
+    LOG(ERROR) << "not big enough to contain footer";
+    return VERIFY_FAILURE;
+  }
+
+  uint8_t footer[FOOTER_SIZE];
+  if (!package->ReadFullyAtOffset(footer, FOOTER_SIZE, length - FOOTER_SIZE)) {
+    LOG(ERROR) << "Failed to read footer";
+    return VERIFY_FAILURE;
+  }
+
+  if (footer[2] != 0xff || footer[3] != 0xff) {
+    LOG(ERROR) << "footer is wrong";
+    return VERIFY_FAILURE;
+  }
+
+  size_t comment_size = footer[4] + (footer[5] << 8);
+  size_t signature_start = footer[0] + (footer[1] << 8);
+  LOG(INFO) << "comment is " << comment_size << " bytes; signature is " << signature_start
+            << " bytes from end";
+
+  if (signature_start > comment_size) {
+    LOG(ERROR) << "signature start: " << signature_start
+               << " is larger than comment size: " << comment_size;
+    return VERIFY_FAILURE;
+  }
+
+  if (signature_start <= FOOTER_SIZE) {
+    LOG(ERROR) << "Signature start is in the footer";
+    return VERIFY_FAILURE;
+  }
+
+#define EOCD_HEADER_SIZE 22
+
+  // The end-of-central-directory record is 22 bytes plus any comment length.
+  size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
+
+  if (length < eocd_size) {
+    LOG(ERROR) << "not big enough to contain EOCD";
+    return VERIFY_FAILURE;
+  }
+
+  // Determine how much of the file is covered by the signature. This is everything except the
+  // signature data and length, which includes all of the EOCD except for the comment length field
+  // (2 bytes) and the comment data.
+  uint64_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
+
+  uint8_t eocd[eocd_size];
+  if (!package->ReadFullyAtOffset(eocd, eocd_size, length - eocd_size)) {
+    LOG(ERROR) << "Failed to read EOCD of " << eocd_size << " bytes";
+    return VERIFY_FAILURE;
+  }
+
+  // If this is really is the EOCD record, it will begin with the magic number $50 $4b $05 $06.
+  if (eocd[0] != 0x50 || eocd[1] != 0x4b || eocd[2] != 0x05 || eocd[3] != 0x06) {
+    LOG(ERROR) << "signature length doesn't match EOCD marker";
+    return VERIFY_FAILURE;
+  }
+
+  for (size_t i = 4; i < eocd_size - 3; ++i) {
+    if (eocd[i] == 0x50 && eocd[i + 1] == 0x4b && eocd[i + 2] == 0x05 && eocd[i + 3] == 0x06) {
+      // If the sequence $50 $4b $05 $06 appears anywhere after the real one, libziparchive will
+      // find the later (wrong) one, which could be exploitable. Fail the verification if this
+      // sequence occurs anywhere after the real one.
+      LOG(ERROR) << "EOCD marker occurs after start of EOCD";
+      return VERIFY_FAILURE;
+    }
+  }
+
+  bool need_sha1 = false;
+  bool need_sha256 = false;
+  for (const auto& key : keys) {
+    switch (key.hash_len) {
+      case SHA_DIGEST_LENGTH:
+        need_sha1 = true;
+        break;
+      case SHA256_DIGEST_LENGTH:
+        need_sha256 = true;
+        break;
+    }
+  }
+
+  SHA_CTX sha1_ctx;
+  SHA256_CTX sha256_ctx;
+  SHA1_Init(&sha1_ctx);
+  SHA256_Init(&sha256_ctx);
+
+  std::vector<HasherUpdateCallback> hashers;
+  if (need_sha1) {
+    hashers.emplace_back(
+        std::bind(&SHA1_Update, &sha1_ctx, std::placeholders::_1, std::placeholders::_2));
+  }
+  if (need_sha256) {
+    hashers.emplace_back(
+        std::bind(&SHA256_Update, &sha256_ctx, std::placeholders::_1, std::placeholders::_2));
+  }
+
+  double frac = -1.0;
+  uint64_t so_far = 0;
+  while (so_far < signed_len) {
+    // On a Nexus 5X, experiment showed 16MiB beat 1MiB by 6% faster for a 1196MiB full OTA and
+    // 60% for an 89MiB incremental OTA. http://b/28135231.
+    uint64_t read_size = std::min<uint64_t>(signed_len - so_far, 16 * MiB);
+    package->UpdateHashAtOffset(hashers, so_far, read_size);
+    so_far += read_size;
+
+    double f = so_far / static_cast<double>(signed_len);
+    if (f > frac + 0.02 || read_size == so_far) {
+      package->SetProgress(f);
+      frac = f;
+      if (set_progress) {
+        set_progress(f);
+      }
+    }
+  }
+
+  uint8_t sha1[SHA_DIGEST_LENGTH];
+  SHA1_Final(sha1, &sha1_ctx);
+  uint8_t sha256[SHA256_DIGEST_LENGTH];
+  SHA256_Final(sha256, &sha256_ctx);
+
+  const uint8_t* signature = eocd + eocd_size - signature_start;
+  size_t signature_size = signature_start - FOOTER_SIZE;
+
+  LOG(INFO) << "signature (offset: " << std::hex << (length - signature_start)
+            << ", length: " << signature_size << "): " << print_hex(signature, signature_size);
+
+  std::vector<uint8_t> sig_der;
+  if (!read_pkcs7(signature, signature_size, &sig_der)) {
+    LOG(ERROR) << "Could not find signature DER block";
+    return VERIFY_FAILURE;
+  }
+
+  // Check to make sure at least one of the keys matches the signature. Since any key can match,
+  // we need to try each before determining a verification failure has happened.
+  size_t i = 0;
+  for (const auto& key : keys) {
+    const uint8_t* hash;
+    int hash_nid;
+    switch (key.hash_len) {
+      case SHA_DIGEST_LENGTH:
+        hash = sha1;
+        hash_nid = NID_sha1;
+        break;
+      case SHA256_DIGEST_LENGTH:
+        hash = sha256;
+        hash_nid = NID_sha256;
+        break;
+      default:
+        continue;
+    }
+
+    // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that the signing tool appends
+    // after the signature itself.
+    if (key.key_type == Certificate::KEY_TYPE_RSA) {
+      if (!RSA_verify(hash_nid, hash, key.hash_len, sig_der.data(), sig_der.size(),
+                      key.rsa.get())) {
+        LOG(INFO) << "failed to verify against RSA key " << i;
+        continue;
+      }
+
+      LOG(INFO) << "whole-file signature verified against RSA key " << i;
+      return VERIFY_SUCCESS;
+    } else if (key.key_type == Certificate::KEY_TYPE_EC && key.hash_len == SHA256_DIGEST_LENGTH) {
+      if (!ECDSA_verify(0, hash, key.hash_len, sig_der.data(), sig_der.size(), key.ec.get())) {
+        LOG(INFO) << "failed to verify against EC key " << i;
+        continue;
+      }
+
+      LOG(INFO) << "whole-file signature verified against EC key " << i;
+      return VERIFY_SUCCESS;
+    } else {
+      LOG(INFO) << "Unknown key type " << key.key_type;
+    }
+    i++;
+  }
+
+  if (need_sha1) {
+    LOG(INFO) << "SHA-1 digest: " << print_hex(sha1, SHA_DIGEST_LENGTH);
+  }
+  if (need_sha256) {
+    LOG(INFO) << "SHA-256 digest: " << print_hex(sha256, SHA256_DIGEST_LENGTH);
+  }
+  LOG(ERROR) << "failed to verify whole-file signature";
+  return VERIFY_FAILURE;
+}
+
+std::unique_ptr<RSA, RSADeleter> parse_rsa_key(FILE* file, uint32_t exponent) {
+    // Read key length in words and n0inv. n0inv is a precomputed montgomery
+    // parameter derived from the modulus and can be used to speed up
+    // verification. n0inv is 32 bits wide here, assuming the verification logic
+    // uses 32 bit arithmetic. However, BoringSSL may use a word size of 64 bits
+    // internally, in which case we don't have a valid n0inv. Thus, we just
+    // ignore the montgomery parameters and have BoringSSL recompute them
+    // internally. If/When the speedup from using the montgomery parameters
+    // becomes relevant, we can add more sophisticated code here to obtain a
+    // 64-bit n0inv and initialize the montgomery parameters in the key object.
+    uint32_t key_len_words = 0;
+    uint32_t n0inv = 0;
+    if (fscanf(file, " %i , 0x%x", &key_len_words, &n0inv) != 2) {
+        return nullptr;
+    }
+
+    if (key_len_words > 8192 / 32) {
+        LOG(ERROR) << "key length (" << key_len_words << ") too large";
+        return nullptr;
+    }
+
+    // Read the modulus.
+    std::unique_ptr<uint32_t[]> modulus(new uint32_t[key_len_words]);
+    if (fscanf(file, " , { %u", &modulus[0]) != 1) {
+        return nullptr;
+    }
+    for (uint32_t i = 1; i < key_len_words; ++i) {
+        if (fscanf(file, " , %u", &modulus[i]) != 1) {
+            return nullptr;
+        }
+    }
+
+    // Cconvert from little-endian array of little-endian words to big-endian
+    // byte array suitable as input for BN_bin2bn.
+    std::reverse((uint8_t*)modulus.get(),
+                 (uint8_t*)(modulus.get() + key_len_words));
+
+    // The next sequence of values is the montgomery parameter R^2. Since we
+    // generally don't have a valid |n0inv|, we ignore this (see comment above).
+    uint32_t rr_value;
+    if (fscanf(file, " } , { %u", &rr_value) != 1) {
+        return nullptr;
+    }
+    for (uint32_t i = 1; i < key_len_words; ++i) {
+        if (fscanf(file, " , %u", &rr_value) != 1) {
+            return nullptr;
+        }
+    }
+    if (fscanf(file, " } } ") != 0) {
+        return nullptr;
+    }
+
+    // Initialize the key.
+    std::unique_ptr<RSA, RSADeleter> key(RSA_new());
+    if (!key) {
+      return nullptr;
+    }
+
+    key->n = BN_bin2bn((uint8_t*)modulus.get(),
+                       key_len_words * sizeof(uint32_t), NULL);
+    if (!key->n) {
+      return nullptr;
+    }
+
+    key->e = BN_new();
+    if (!key->e || !BN_set_word(key->e, exponent)) {
+      return nullptr;
+    }
+
+    return key;
+}
+
+
+static std::vector<Certificate> IterateZipEntriesAndSearchForKeys(const ZipArchiveHandle& handle) {
+  void* cookie;
+  ZipString suffix("x509.pem");
+  int32_t iter_status = StartIteration(handle, &cookie, nullptr, &suffix);
+  if (iter_status != 0) {
+    LOG(ERROR) << "Failed to iterate over entries in the certificate zipfile: "
+               << ErrorCodeString(iter_status);
+    return {};
+  }
+
+  std::vector<Certificate> result;
+
+  ZipString name;
+  ZipEntry entry;
+  while ((iter_status = Next(cookie, &entry, &name)) == 0) {
+    std::vector<uint8_t> pem_content(entry.uncompressed_length);
+    if (int32_t extract_status =
+            ExtractToMemory(handle, &entry, pem_content.data(), pem_content.size());
+        extract_status != 0) {
+      LOG(ERROR) << "Failed to extract " << std::string(name.name, name.name + name.name_length);
+      return {};
+    }
+
+    Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
+    // Aborts the parsing if we fail to load one of the key file.
+    if (!LoadCertificateFromBuffer(pem_content, &cert)) {
+      LOG(ERROR) << "Failed to load keys from "
+                 << std::string(name.name, name.name + name.name_length);
+      return {};
+    }
+
+    result.emplace_back(std::move(cert));
+  }
+
+  if (iter_status != -1) {
+    LOG(ERROR) << "Error while iterating over zip entries: " << ErrorCodeString(iter_status);
+    return {};
+  }
+
+  return result;
+}
+
+std::vector<Certificate> LoadKeysFromZipfile(const std::string& zip_name) {
+  ZipArchiveHandle handle;
+  if (int32_t open_status = OpenArchive(zip_name.c_str(), &handle); open_status != 0) {
+    LOG(ERROR) << "Failed to open " << zip_name << ": " << ErrorCodeString(open_status);
+    return {};
+  }
+
+  std::vector<Certificate> result = IterateZipEntriesAndSearchForKeys(handle);
+  CloseArchive(handle);
+  return result;
+}
+
+bool CheckRSAKey(const std::unique_ptr<RSA, RSADeleter>& rsa) {
+  if (!rsa) {
+    return false;
+  }
+
+  const BIGNUM* out_n;
+  const BIGNUM* out_e;
+  RSA_get0_key(rsa.get(), &out_n, &out_e, nullptr /* private exponent */);
+  auto modulus_bits = BN_num_bits(out_n);
+  if (modulus_bits != 2048 && modulus_bits != 4096) {
+    LOG(ERROR) << "Modulus should be 2048 or 4096 bits long, actual: " << modulus_bits;
+    return false;
+  }
+
+  BN_ULONG exponent = BN_get_word(out_e);
+  if (exponent != 3 && exponent != 65537) {
+    LOG(ERROR) << "Public exponent should be 3 or 65537, actual: " << exponent;
+    return false;
+  }
+
+  return true;
+}
+
+bool CheckECKey(const std::unique_ptr<EC_KEY, ECKEYDeleter>& ec_key) {
+  if (!ec_key) {
+    return false;
+  }
+
+  const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key.get());
+  if (!ec_group) {
+    LOG(ERROR) << "Failed to get the ec_group from the ec_key";
+    return false;
+  }
+  auto degree = EC_GROUP_get_degree(ec_group);
+  if (degree != 256) {
+    LOG(ERROR) << "Field size of the ec key should be 256 bits long, actual: " << degree;
+    return false;
+  }
+
+  return true;
+}
+
+bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert) {
+  std::unique_ptr<BIO, decltype(&BIO_free)> content(
+      BIO_new_mem_buf(pem_content.data(), pem_content.size()), BIO_free);
+
+  std::unique_ptr<X509, decltype(&X509_free)> x509(
+      PEM_read_bio_X509(content.get(), nullptr, nullptr, nullptr), X509_free);
+  if (!x509) {
+    LOG(ERROR) << "Failed to read x509 certificate";
+    return false;
+  }
+
+  int nid = X509_get_signature_nid(x509.get());
+  switch (nid) {
+    // SignApk has historically accepted md5WithRSA certificates, but treated them as
+    // sha1WithRSA anyway. Continue to do so for backwards compatibility.
+    case NID_md5WithRSA:
+    case NID_md5WithRSAEncryption:
+    case NID_sha1WithRSA:
+    case NID_sha1WithRSAEncryption:
+      cert->hash_len = SHA_DIGEST_LENGTH;
+      break;
+    case NID_sha256WithRSAEncryption:
+    case NID_ecdsa_with_SHA256:
+      cert->hash_len = SHA256_DIGEST_LENGTH;
+      break;
+    default:
+      LOG(ERROR) << "Unrecognized signature nid " << OBJ_nid2ln(nid);
+      return false;
+  }
+
+  std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> public_key(X509_get_pubkey(x509.get()),
+                                                                 EVP_PKEY_free);
+  if (!public_key) {
+    LOG(ERROR) << "Failed to extract the public key from x509 certificate";
+    return false;
+  }
+
+  int key_type = EVP_PKEY_id(public_key.get());
+  if (key_type == EVP_PKEY_RSA) {
+    cert->key_type = Certificate::KEY_TYPE_RSA;
+    cert->ec.reset();
+    cert->rsa.reset(EVP_PKEY_get1_RSA(public_key.get()));
+    if (!cert->rsa || !CheckRSAKey(cert->rsa)) {
+      LOG(ERROR) << "Failed to validate the rsa key info from public key";
+      return false;
+    }
+  } else if (key_type == EVP_PKEY_EC) {
+    cert->key_type = Certificate::KEY_TYPE_EC;
+    cert->rsa.reset();
+    cert->ec.reset(EVP_PKEY_get1_EC_KEY(public_key.get()));
+    if (!cert->ec || !CheckECKey(cert->ec)) {
+      LOG(ERROR) << "Failed to validate the ec key info from the public key";
+      return false;
+    }
+  } else {
+    LOG(ERROR) << "Unrecognized public key type " << OBJ_nid2ln(key_type);
+    return false;
+  }
+
+  return true;
+}