Merge "Further compress the recovery animation file"
diff --git a/Android.bp b/Android.bp
index 22c90bd..53b74dc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -16,14 +16,48 @@
     name: "recovery_defaults",
 
     cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+
+        // Must be the same as RECOVERY_API_VERSION.
+        "-DRECOVERY_API_VERSION=3",
+
         "-Wall",
         "-Werror",
     ],
 }
 
+cc_library {
+    name: "librecovery_ui",
+    recovery_available: true,
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    srcs: [
+        "device.cpp",
+        "screen_ui.cpp",
+        "ui.cpp",
+        "vr_ui.cpp",
+        "wear_ui.cpp"
+    ],
+
+    static_libs: [
+        "libminui",
+        "libotautil",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libpng",
+        "libz",
+    ],
+}
+
 // Generic device that uses ScreenRecoveryUI.
 cc_library_static {
     name: "librecovery_ui_default",
+    recovery_available: true,
 
     defaults: [
         "recovery_defaults",
@@ -37,6 +71,7 @@
 // The default wear device that uses WearRecoveryUI.
 cc_library_static {
     name: "librecovery_ui_wear",
+    recovery_available: true,
 
     defaults: [
         "recovery_defaults",
@@ -50,6 +85,7 @@
 // The default VR device that uses VrRecoveryUI.
 cc_library_static {
     name: "librecovery_ui_vr",
+    recovery_available: true,
 
     defaults: [
         "recovery_defaults",
@@ -61,7 +97,94 @@
 }
 
 cc_library_static {
+    name: "librecovery_fastboot",
+    recovery_available: true,
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    srcs: [
+        "fastboot/fastboot.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbootloader_message",
+        "libcutils",
+        "liblog",
+    ],
+
+    static_libs: [
+        "librecovery_ui_default",
+    ],
+}
+
+cc_defaults {
+    name: "librecovery_defaults",
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    shared_libs: [
+        "android.hardware.health@2.0",
+        "libbase",
+        "libbootloader_message",
+        "libcrypto",
+        "libcutils",
+        "libext4_utils",
+        "libfs_mgr",
+        "libfusesideload",
+        "libhidl-gen-utils",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libpng",
+        "libselinux",
+        "libtinyxml2",
+        "libutils",
+        "libz",
+        "libziparchive",
+    ],
+
+    static_libs: [
+        "librecovery_fastboot",
+        "libminui",
+        "libverifier",
+        "libotautil",
+
+        // external dependencies
+        "libhealthhalutils",
+        "libvintf_recovery",
+        "libvintf",
+    ],
+}
+
+cc_library_static {
+    name: "librecovery",
+    recovery_available: true,
+
+    defaults: [
+        "librecovery_defaults",
+    ],
+
+    srcs: [
+        "adb_install.cpp",
+        "fsck_unshare_blocks.cpp",
+        "fuse_sdcard_provider.cpp",
+        "install.cpp",
+        "recovery.cpp",
+        "roots.cpp",
+    ],
+
+    include_dirs: [
+        "system/vold",
+    ],
+}
+
+cc_library_static {
     name: "libverifier",
+    recovery_available: true,
 
     defaults: [
         "recovery_defaults",
@@ -72,14 +195,49 @@
         "verifier.cpp",
     ],
 
-    static_libs: [
+    shared_libs: [
         "libbase",
         "libcrypto",
         "libcrypto_utils",
+    ],
+
+    static_libs: [
         "libotautil",
     ],
 }
 
+cc_binary {
+    name: "recovery",
+    recovery: true,
+
+    defaults: [
+        "librecovery_defaults",
+    ],
+
+    srcs: [
+        "logging.cpp",
+        "recovery_main.cpp",
+    ],
+
+    shared_libs: [
+        "libminadbd_services",
+        "librecovery_ui",
+    ],
+
+    static_libs: [
+        "librecovery",
+        "librecovery_ui_default",
+    ],
+
+    required: [
+        "e2fsdroid.recovery",
+        "librecovery_ui_ext",
+        "mke2fs.conf",
+        "mke2fs.recovery",
+        "recovery_deps",
+    ],
+}
+
 // The dynamic executable that runs after /data mounts.
 cc_binary {
     name: "recovery-persist",
@@ -133,3 +291,11 @@
         "recovery-refresh.rc",
     ],
 }
+
+filegroup {
+    name: "res-testdata",
+
+    srcs: [
+        "res-*/images/*_text.png",
+    ],
+}
diff --git a/Android.mk b/Android.mk
index 9542080..80d107d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,7 +14,7 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# Needed by build/make/core/Makefile.
+# Needed by build/make/core/Makefile. Must be consistent with the value in Android.bp.
 RECOVERY_API_VERSION := 3
 RECOVERY_FSTAB_VERSION := 2
 
@@ -23,11 +23,6 @@
 # librecovery_ui_default, which uses ScreenRecoveryUI.
 TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default
 
-recovery_common_cflags := \
-    -Wall \
-    -Werror \
-    -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
-
 # librecovery_ui_ext (shared library)
 # ===================================
 include $(CLEAR_VARS)
@@ -49,151 +44,16 @@
 LOCAL_SHARED_LIBRARIES := \
     libbase \
     liblog \
-    librecovery_ui
+    librecovery_ui.recovery
 
 include $(BUILD_SHARED_LIBRARY)
 
-# librecovery_ui (shared library)
-# ===============================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
-    device.cpp \
-    screen_ui.cpp \
-    ui.cpp \
-    vr_ui.cpp \
-    wear_ui.cpp
-
-LOCAL_MODULE := librecovery_ui
-
-LOCAL_CFLAGS := $(recovery_common_cflags)
-
-LOCAL_MULTILIB := first
-
-ifeq ($(TARGET_IS_64_BIT),true)
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib64
-else
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib
-endif
-
-LOCAL_STATIC_LIBRARIES := \
-    libminui \
-    libotautil \
-
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    libpng \
-    libz \
-
-include $(BUILD_SHARED_LIBRARY)
-
-# librecovery_ui (static library)
-# ===============================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
-    device.cpp \
-    screen_ui.cpp \
-    ui.cpp \
-    vr_ui.cpp \
-    wear_ui.cpp
-
-LOCAL_MODULE := librecovery_ui
-
-LOCAL_CFLAGS := $(recovery_common_cflags)
-
-LOCAL_STATIC_LIBRARIES := \
-    libminui \
-    libotautil \
-
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    libpng \
-    libz \
-
-include $(BUILD_STATIC_LIBRARY)
-
-librecovery_static_libraries := \
-    libbootloader_message \
-    libfusesideload \
-    libminadbd \
-    libminui \
-    libverifier \
-    libotautil \
-    libasyncio \
-    libbatterymonitor \
-    libcrypto_utils \
-    libcrypto \
-    libext4_utils \
-    libfs_mgr \
-    libpng \
-    libsparse \
-    libvintf_recovery \
-    libvintf \
-    libhidl-gen-utils \
-    libtinyxml2 \
-    libziparchive \
-    libbase \
-    libutils \
-    libcutils \
-    liblog \
-    libselinux \
-    libz \
-
-# librecovery (static library)
-# ===============================
+# recovery_deps: A phony target that's depended on by `recovery`, which
+# builds additional modules conditionally based on Makefile variables.
+# ======================================================================
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := \
-    adb_install.cpp \
-    fsck_unshare_blocks.cpp \
-    fuse_sdcard_provider.cpp \
-    install.cpp \
-    recovery.cpp \
-    roots.cpp \
-
-LOCAL_C_INCLUDES := \
-    system/vold \
-
-LOCAL_CFLAGS := $(recovery_common_cflags)
-
-LOCAL_MODULE := librecovery
-
-LOCAL_STATIC_LIBRARIES := \
-    $(librecovery_static_libraries)
-
-include $(BUILD_STATIC_LIBRARY)
-
-# recovery (static executable)
-# ===============================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    logging.cpp \
-    recovery_main.cpp \
-
-LOCAL_MODULE := recovery
-
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
-
-# Cannot link with LLD: undefined symbol: UsbNoPermissionsLongHelpText
-# http://b/77543887, lld does not handle -Wl,--gc-sections as well as ld.
-LOCAL_USE_CLANG_LLD := false
-
-LOCAL_CFLAGS := $(recovery_common_cflags)
-
-LOCAL_STATIC_LIBRARIES := \
-    librecovery \
-    librecovery_ui_default \
-    $(librecovery_static_libraries)
-
-LOCAL_SHARED_LIBRARIES := \
-    librecovery_ui \
-
-LOCAL_HAL_STATIC_LIBRARIES := libhealthd
-
-LOCAL_REQUIRED_MODULES := \
-    e2fsdroid.recovery \
-    mke2fs.recovery \
-    mke2fs.conf
+LOCAL_MODULE := recovery_deps
 
 ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
 ifeq ($(HOST_OS),linux)
@@ -206,7 +66,8 @@
 # e2fsck is needed for adb remount -R.
 ifeq ($(BOARD_EXT4_SHARE_DUP_BLOCKS),true)
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-LOCAL_REQUIRED_MODULES += e2fsck_static
+LOCAL_REQUIRED_MODULES += \
+    e2fsck_static
 endif
 endif
 
@@ -216,21 +77,7 @@
     recovery-refresh
 endif
 
-LOCAL_REQUIRED_MODULES += \
-    librecovery_ui_ext
-
-# TODO(b/110380063): Explicitly install the following shared libraries to recovery, until `recovery`
-# module is built with Soong (with `recovery: true` flag).
-LOCAL_REQUIRED_MODULES += \
-    libbase.recovery \
-    liblog.recovery \
-    libpng.recovery \
-    libz.recovery \
-
-include $(BUILD_EXECUTABLE)
+include $(BUILD_PHONY_PACKAGE)
 
 include \
-    $(LOCAL_PATH)/boot_control/Android.mk \
-    $(LOCAL_PATH)/tests/Android.mk \
     $(LOCAL_PATH)/updater/Android.mk \
-    $(LOCAL_PATH)/updater_sample/Android.mk \
diff --git a/boot_control/Android.bp b/boot_control/Android.bp
new file mode 100644
index 0000000..7720ead
--- /dev/null
+++ b/boot_control/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2018 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_library_shared {
+    name: "bootctrl.default",
+    recovery_available: true,
+    relative_install_path: "hw",
+
+    srcs: ["boot_control.cpp"],
+
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbootloader_message",
+        "libfs_mgr",
+        "liblog",
+    ],
+}
diff --git a/boot_control/Android.mk b/boot_control/Android.mk
deleted file mode 100644
index 9814d71..0000000
--- a/boot_control/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := bootctrl.bcb
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := boot_control.cpp
-LOCAL_CFLAGS := \
-  -D_FILE_OFFSET_BITS=64 \
-  -Werror \
-  -Wall \
-  -Wextra
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_STATIC_LIBRARIES := libbootloader_message libfs_mgr libbase
-LOCAL_POST_INSTALL_CMD := \
-  $(hide) mkdir -p $(TARGET_OUT_SHARED_LIBRARIES)/hw && \
-  ln -sf bootctrl.bcb.so $(TARGET_OUT_SHARED_LIBRARIES)/hw/bootctrl.default.so
-include $(BUILD_SHARED_LIBRARY)
diff --git a/device.cpp b/device.cpp
index 3c6334e..eec1932 100644
--- a/device.cpp
+++ b/device.cpp
@@ -28,6 +28,7 @@
 static std::vector<std::pair<std::string, Device::BuiltinAction>> g_menu_actions{
   { "Reboot system now", Device::REBOOT },
   { "Reboot to bootloader", Device::REBOOT_BOOTLOADER },
+  { "Enter fastboot", Device::ENTER_FASTBOOT },
   { "Apply update from ADB", Device::APPLY_ADB_SIDELOAD },
   { "Apply update from SD card", Device::APPLY_SDCARD },
   { "Wipe data/factory reset", Device::WIPE_DATA },
diff --git a/device.h b/device.h
index a6ad627..6a8daf8 100644
--- a/device.h
+++ b/device.h
@@ -48,6 +48,8 @@
     RUN_GRAPHICS_TEST = 11,
     RUN_LOCALE_TEST = 12,
     KEY_INTERRUPTED = 13,
+    ENTER_FASTBOOT = 14,
+    ENTER_RECOVERY = 15,
   };
 
   explicit Device(RecoveryUI* ui);
diff --git a/etc/init.rc b/etc/init.rc
index 8e18438..9add249 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -6,6 +6,8 @@
 
     start ueventd
 
+    setprop sys.usb.configfs 0
+
 on init
     export ANDROID_ROOT /system
     export ANDROID_DATA /data
@@ -31,20 +33,6 @@
     write /proc/sys/kernel/panic_on_oops 1
     write /proc/sys/vm/max_map_count 1000000
 
-on fs
-    write /sys/class/android_usb/android0/f_ffs/aliases adb
-    mkdir /dev/usb-ffs 0770 shell shell
-    mkdir /dev/usb-ffs/adb 0770 shell shell
-    mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000
-
-    write /sys/class/android_usb/android0/enable 0
-    write /sys/class/android_usb/android0/idVendor 18D1
-    write /sys/class/android_usb/android0/idProduct D001
-    write /sys/class/android_usb/android0/functions adb
-    write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer}
-    write /sys/class/android_usb/android0/iProduct ${ro.product.model}
-    write /sys/class/android_usb/android0/iSerial ${ro.serialno}
-
 on boot
     ifup lo
     hostname localhost
@@ -77,7 +65,7 @@
     trigger early-boot
     trigger boot
 
-service ueventd /sbin/ueventd
+service ueventd /system/bin/ueventd
     critical
     seclabel u:r:ueventd:s0
 
@@ -86,6 +74,7 @@
     seclabel u:r:charger:s0
 
 service recovery /system/bin/recovery
+    socket recovery stream 422 system system
     seclabel u:r:recovery:s0
 
 service adbd /system/bin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
@@ -93,13 +82,89 @@
     socket adbd stream 660 system system
     seclabel u:r:adbd:s0
 
-# Always start adbd on userdebug and eng builds
-on property:ro.debuggable=1
-    write /sys/class/android_usb/android0/enable 1
-    start adbd
+service fastbootd /system/bin/fastbootd
+    disabled
+    group system
+    seclabel u:r:fastbootd:s0
 
 # Restart adbd so it can run as root
 on property:service.adb.root=1
-    write /sys/class/android_usb/android0/enable 0
     restart adbd
+
+# Always start adbd on userdebug and eng builds
+on fs && property:ro.debuggable=1
+    setprop sys.usb.config adb
+
+on fs && property:sys.usb.configfs=1
+    mount configfs none /config
+    mkdir /config/usb_gadget/g1 0770 shell shell
+    write /config/usb_gadget/g1/idVendor 0x18D1
+    mkdir /config/usb_gadget/g1/strings/0x409 0770
+    write /config/usb_gadget/g1/strings/0x409/serialnumber ${ro.serialno}
+    write /config/usb_gadget/g1/strings/0x409/manufacturer ${ro.product.manufacturer}
+    write /config/usb_gadget/g1/strings/0x409/product ${ro.product.model}
+    mkdir /config/usb_gadget/g1/functions/ffs.adb
+    mkdir /config/usb_gadget/g1/functions/ffs.fastboot
+    mkdir /config/usb_gadget/g1/configs/b.1 0777 shell shell
+    mkdir /config/usb_gadget/g1/configs/b.1/strings/0x409 0770 shell shell
+
+on fs && property:sys.usb.configfs=0
+    write /sys/class/android_usb/android0/f_ffs/aliases adb,fastboot
+    write /sys/class/android_usb/android0/idVendor 18D1
+    write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer}
+    write /sys/class/android_usb/android0/iProduct ${ro.product.model}
+    write /sys/class/android_usb/android0/iSerial ${ro.serialno}
+
+on fs
+    mkdir /dev/usb-ffs 0775 shell shell
+    mkdir /dev/usb-ffs/adb 0770 shell shell
+    mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000
+    mkdir /dev/usb-ffs/fastboot 0770 system system
+    mount functionfs fastboot /dev/usb-ffs/fastboot rmode=0770,fmode=0660,uid=1000,gid=1000
+
+on property:sys.usb.config=adb
+    start adbd
+
+on property:sys.usb.config=fastboot
+    start fastbootd
+
+on property:sys.usb.config=none
+    stop adbd
+    stop fastbootd
+
+on property:sys.usb.config=none && property:sys.usb.configfs=0
+    write /sys/class/android_usb/android0/enable 0
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=adb && property:sys.usb.configfs=0
+    write /sys/class/android_usb/android0/idProduct D001
+    write /sys/class/android_usb/android0/functions adb
     write /sys/class/android_usb/android0/enable 1
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=fastboot && property:sys.usb.configfs=0
+    write /sys/class/android_usb/android0/idProduct 4EE0
+    write /sys/class/android_usb/android0/functions fastboot
+    write /sys/class/android_usb/android0/enable 1
+    setprop sys.usb.state ${sys.usb.config}
+
+# Configfs triggers
+on property:sys.usb.config=none && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/UDC "none"
+    setprop sys.usb.ffs.ready 0
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=adb && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/idProduct 0xD001
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb"
+    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=fastboot && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/idProduct 0x4EE0
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "fastboot"
+    symlink /config/usb_gadget/g1/functions/ffs.fastboot /config/usb_gadget/g1/configs/b.1/f1
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
new file mode 100644
index 0000000..8458c99
--- /dev/null
+++ b/fastboot/fastboot.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 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 "fastboot.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <bootloader_message/bootloader_message.h>
+
+#include "device.h"
+#include "ui.h"
+
+static const std::vector<std::pair<std::string, Device::BuiltinAction>> kFastbootMenuActions{
+  { "Reboot system now", Device::REBOOT },
+  { "Enter recovery", Device::ENTER_RECOVERY },
+  { "Reboot to bootloader", Device::REBOOT_BOOTLOADER },
+  { "Power off", Device::SHUTDOWN },
+};
+
+Device::BuiltinAction StartFastboot(Device* device, const std::vector<std::string>& /* args */) {
+  RecoveryUI* ui = device->GetUI();
+
+  std::vector<std::string> title_lines = { "Android Fastboot" };
+  title_lines.push_back("Product name - " + android::base::GetProperty("ro.product.device", ""));
+  title_lines.push_back("Bootloader version - " + android::base::GetProperty("ro.bootloader", ""));
+  title_lines.push_back("Baseband version - " +
+                        android::base::GetProperty("ro.build.expect.baseband", ""));
+  title_lines.push_back("Serial number - " + android::base::GetProperty("ro.serialno", ""));
+  title_lines.push_back(std::string("Secure boot - ") +
+                        ((android::base::GetProperty("ro.secure", "") == "1") ? "yes" : "no"));
+  title_lines.push_back("HW version - " + android::base::GetProperty("ro.revision", ""));
+
+  ui->ResetKeyInterruptStatus();
+  ui->SetTitle(title_lines);
+  ui->ShowText(true);
+
+  // Reset to normal system boot so recovery won't cycle indefinitely.
+  // TODO(b/112277594) Clear only if 'recovery' field of BCB is empty. If not,
+  // set the 'command' field of BCB to 'boot-recovery' so the next boot is into recovery
+  // to finish any interrupted tasks.
+  std::string err;
+  if (!clear_bootloader_message(&err)) {
+    LOG(ERROR) << "Failed to clear BCB message: " << err;
+  }
+
+  std::vector<std::string> fastboot_menu_items;
+  std::transform(kFastbootMenuActions.cbegin(), kFastbootMenuActions.cend(),
+                 std::back_inserter(fastboot_menu_items),
+                 [](const auto& entry) { return entry.first; });
+
+  auto chosen_item = ui->ShowMenu(
+      {}, fastboot_menu_items, 0, false,
+      std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
+
+  if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
+    return Device::KEY_INTERRUPTED;
+  }
+  if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::TIMED_OUT)) {
+    return Device::BuiltinAction::NO_ACTION;
+  }
+  return kFastbootMenuActions[chosen_item].second;
+}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
new file mode 100644
index 0000000..53a2adc
--- /dev/null
+++ b/fastboot/fastboot.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 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>
+#include <vector>
+
+#include "device.h"
+
+Device::BuiltinAction StartFastboot(Device* device, const std::vector<std::string>& args);
diff --git a/fuse_sideload/Android.bp b/fuse_sideload/Android.bp
index 76bc16d..90c4c22 100644
--- a/fuse_sideload/Android.bp
+++ b/fuse_sideload/Android.bp
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_static {
+cc_library {
     name: "libfusesideload",
+    recovery_available: true,
 
     cflags: [
         "-D_XOPEN_SOURCE",
@@ -30,7 +31,7 @@
         "include",
     ],
 
-    static_libs: [
+    shared_libs: [
         "libbase",
         "libcrypto",
     ],
diff --git a/install.cpp b/install.cpp
index 800847f..e379ef3 100644
--- a/install.cpp
+++ b/install.cpp
@@ -248,7 +248,7 @@
   }
   long payload_offset = payload_entry.offset;
   *cmd = {
-    "/sbin/update_engine_sideload",
+    "/system/bin/update_engine_sideload",
     "--payload=file://" + package,
     android::base::StringPrintf("--offset=%ld", payload_offset),
     "--headers=" + std::string(payload_properties.begin(), payload_properties.end()),
diff --git a/minadbd/Android.bp b/minadbd/Android.bp
index 432b2f0..00244ee 100644
--- a/minadbd/Android.bp
+++ b/minadbd/Android.bp
@@ -26,8 +26,11 @@
     ],
 }
 
-cc_library_static {
-    name: "libminadbd",
+// `libminadbd_services` is analogous to the `libadbd_services` for regular `adbd`, but providing
+// the sideload service only.
+cc_library {
+    name: "libminadbd_services",
+    recovery_available: true,
 
     defaults: [
         "minadbd_defaults",
@@ -39,14 +42,11 @@
         "minadbd_services.cpp",
     ],
 
-    static_libs: [
-        "libfusesideload",
+    shared_libs: [
+        "libadbd",
         "libbase",
         "libcrypto",
-    ],
-
-    whole_static_libs: [
-        "libadbd",
+        "libfusesideload",
     ],
 }
 
@@ -62,8 +62,9 @@
     ],
 
     static_libs: [
+        "libminadbd_services",
+        "libadbd",
         "libBionicGtestMain",
-        "libminadbd",
     ],
 
     shared_libs: [
diff --git a/minui/Android.bp b/minui/Android.bp
index 19d28be..fff3a8e 100644
--- a/minui/Android.bp
+++ b/minui/Android.bp
@@ -14,6 +14,7 @@
 
 cc_library {
     name: "libminui",
+    recovery_available: true,
 
     defaults: [
         "recovery_defaults",
@@ -35,7 +36,7 @@
     whole_static_libs: [
         "libadf",
         "libdrm",
-        "libsync_recovery",
+        "libsync",
     ],
 
     shared_libs: [
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
index 9336a1e..630b801 100644
--- a/minui/graphics_drm.cpp
+++ b/minui/graphics_drm.cpp
@@ -117,12 +117,16 @@
 
   uint32_t format;
   PixelFormat pixel_format = gr_pixel_format();
+  // PixelFormat comes in byte order, whereas DRM_FORMAT_* uses little-endian
+  // (external/libdrm/include/drm/drm_fourcc.h). Note that although drm_fourcc.h also defines a
+  // macro of DRM_FORMAT_BIG_ENDIAN, it doesn't seem to be actually supported (see the discussion
+  // in https://lists.freedesktop.org/archives/amd-gfx/2017-May/008560.html).
   if (pixel_format == PixelFormat::ABGR) {
-    format = DRM_FORMAT_ABGR8888;
+    format = DRM_FORMAT_RGBA8888;
   } else if (pixel_format == PixelFormat::BGRA) {
-    format = DRM_FORMAT_BGRA8888;
+    format = DRM_FORMAT_ARGB8888;
   } else if (pixel_format == PixelFormat::RGBX) {
-    format = DRM_FORMAT_RGBX8888;
+    format = DRM_FORMAT_XBGR8888;
   } else {
     format = DRM_FORMAT_RGB565;
   }
diff --git a/otautil/Android.bp b/otautil/Android.bp
index b058f7b..56c7c9e 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -16,6 +16,11 @@
     name: "libotautil",
 
     host_supported: true,
+    recovery_available: true,
+
+    defaults: [
+        "recovery_defaults",
+    ],
 
     // Minimal set of files to support host build.
     srcs: [
@@ -23,16 +28,10 @@
         "rangeset.cpp",
     ],
 
-    static_libs: [
+    shared_libs: [
         "libbase",
     ],
 
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-        "-Werror",
-        "-Wall",
-    ],
-
     export_include_dirs: [
         "include",
     ],
@@ -46,9 +45,9 @@
                 "thermalutil.cpp",
             ],
 
-            static_libs: [
-                "libselinux",
+            shared_libs: [
                 "libcutils",
+                "libselinux",
             ],
         },
     },
diff --git a/recovery.cpp b/recovery.cpp
index 3828e29..01bd83b 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -49,7 +49,7 @@
 #include <android-base/unique_fd.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/properties.h> /* for property_list */
-#include <healthd/BatteryMonitor.h>
+#include <healthhalutils/HealthHalUtils.h>
 #include <ziparchive/zip_archive.h>
 
 #include "adb_install.h"
@@ -779,6 +779,8 @@
       case Device::REBOOT:
       case Device::SHUTDOWN:
       case Device::REBOOT_BOOTLOADER:
+      case Device::ENTER_FASTBOOT:
+      case Device::ENTER_RECOVERY:
         return chosen_action;
 
       case Device::WIPE_DATA:
@@ -876,54 +878,63 @@
 }
 
 static bool is_battery_ok(int* required_battery_level) {
-  struct healthd_config healthd_config = {
-    .batteryStatusPath = android::String8(android::String8::kEmptyString),
-    .batteryHealthPath = android::String8(android::String8::kEmptyString),
-    .batteryPresentPath = android::String8(android::String8::kEmptyString),
-    .batteryCapacityPath = android::String8(android::String8::kEmptyString),
-    .batteryVoltagePath = android::String8(android::String8::kEmptyString),
-    .batteryTemperaturePath = android::String8(android::String8::kEmptyString),
-    .batteryTechnologyPath = android::String8(android::String8::kEmptyString),
-    .batteryCurrentNowPath = android::String8(android::String8::kEmptyString),
-    .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString),
-    .batteryChargeCounterPath = android::String8(android::String8::kEmptyString),
-    .batteryFullChargePath = android::String8(android::String8::kEmptyString),
-    .batteryCycleCountPath = android::String8(android::String8::kEmptyString),
-    .energyCounter = nullptr,
-    .boot_min_cap = 0,
-    .screen_on = nullptr
-  };
-  healthd_board_init(&healthd_config);
+  using android::hardware::health::V1_0::BatteryStatus;
+  using android::hardware::health::V2_0::get_health_service;
+  using android::hardware::health::V2_0::IHealth;
+  using android::hardware::health::V2_0::Result;
+  using android::hardware::health::V2_0::toString;
 
-  android::BatteryMonitor monitor;
-  monitor.init(&healthd_config);
+  android::sp<IHealth> health = get_health_service();
 
   static constexpr int BATTERY_READ_TIMEOUT_IN_SEC = 10;
   int wait_second = 0;
   while (true) {
-    int charge_status = monitor.getChargeStatus();
+    auto charge_status = BatteryStatus::UNKNOWN;
+
+    if (health == nullptr) {
+      LOG(WARNING) << "no health implementation is found, assuming defaults";
+    } else {
+      health
+          ->getChargeStatus([&charge_status](auto res, auto out_status) {
+            if (res == Result::SUCCESS) {
+              charge_status = out_status;
+            }
+          })
+          .isOk();  // should not have transport error
+    }
+
     // Treat unknown status as charged.
-    bool charged = (charge_status != android::BATTERY_STATUS_DISCHARGING &&
-                    charge_status != android::BATTERY_STATUS_NOT_CHARGING);
-    android::BatteryProperty capacity;
-    android::status_t status = monitor.getProperty(android::BATTERY_PROP_CAPACITY, &capacity);
-    ui_print("charge_status %d, charged %d, status %d, capacity %" PRId64 "\n", charge_status,
-             charged, status, capacity.valueInt64);
+    bool charged = (charge_status != BatteryStatus::DISCHARGING &&
+                    charge_status != BatteryStatus::NOT_CHARGING);
+
+    Result res = Result::UNKNOWN;
+    int32_t capacity = INT32_MIN;
+    if (health != nullptr) {
+      health
+          ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) {
+            res = out_res;
+            capacity = out_capacity;
+          })
+          .isOk();  // should not have transport error
+    }
+
+    LOG(INFO) << "charge_status " << toString(charge_status) << ", charged " << charged
+              << ", status " << toString(res) << ", capacity " << capacity;
     // At startup, the battery drivers in devices like N5X/N6P take some time to load
     // the battery profile. Before the load finishes, it reports value 50 as a fake
     // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected
     // to finish loading the battery profile earlier than 10 seconds after kernel startup.
-    if (status == 0 && capacity.valueInt64 == 50) {
+    if (res == Result::SUCCESS && capacity == 50) {
       if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) {
         sleep(1);
         wait_second++;
         continue;
       }
     }
-    // If we can't read battery percentage, it may be a device without battery. In this situation,
-    // use 100 as a fake battery percentage.
-    if (status != 0) {
-      capacity.valueInt64 = 100;
+    // If we can't read battery percentage, it may be a device without battery. In this
+    // situation, use 100 as a fake battery percentage.
+    if (res != Result::SUCCESS) {
+      capacity = 100;
     }
 
     // GmsCore enters recovery mode to install package when having enough battery percentage.
@@ -932,7 +943,7 @@
     static constexpr int BATTERY_OK_PERCENTAGE = 20;
     static constexpr int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15;
     *required_battery_level = charged ? BATTERY_WITH_CHARGER_OK_PERCENTAGE : BATTERY_OK_PERCENTAGE;
-    return capacity.valueInt64 >= *required_battery_level;
+    return capacity >= *required_battery_level;
   }
 }
 
@@ -986,6 +997,7 @@
 
 Device::BuiltinAction start_recovery(Device* device, const std::vector<std::string>& args) {
   static constexpr struct option OPTIONS[] = {
+    { "fastboot", no_argument, nullptr, 0 },
     { "fsck_unshare_blocks", no_argument, nullptr, 0 },
     { "just_exit", no_argument, nullptr, 'x' },
     { "locale", required_argument, nullptr, 0 },
@@ -1040,7 +1052,7 @@
         std::string option = OPTIONS[option_index].name;
         if (option == "fsck_unshare_blocks") {
           fsck_unshare_blocks = true;
-        } else if (option == "locale") {
+        } else if (option == "locale" || option == "fastboot") {
           // Handled in recovery_main.cpp
         } else if (option == "prompt_and_wipe_data") {
           should_prompt_and_wipe_data = true;
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 9a9890d..99f9650 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -30,15 +30,19 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <atomic>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
+#include <cutils/sockets.h>
 #include <private/android_logger.h> /* private pmsg functions */
 #include <selinux/android.h>
 #include <selinux/label.h>
@@ -46,6 +50,7 @@
 
 #include "common.h"
 #include "device.h"
+#include "fastboot/fastboot.h"
 #include "logging.h"
 #include "minadbd/minadbd.h"
 #include "otautil/paths.h"
@@ -162,6 +167,44 @@
   return android::base::Trim(content);
 }
 
+static void ListenRecoverySocket(RecoveryUI* ui, std::atomic<Device::BuiltinAction>& action) {
+  android::base::unique_fd sock_fd(android_get_control_socket("recovery"));
+  if (sock_fd < 0) {
+    PLOG(ERROR) << "Failed to open recovery socket";
+    return;
+  }
+  listen(sock_fd, 4);
+
+  while (true) {
+    android::base::unique_fd connection_fd;
+    connection_fd.reset(accept(sock_fd, nullptr, nullptr));
+    if (connection_fd < 0) {
+      PLOG(ERROR) << "Failed to accept socket connection";
+      continue;
+    }
+    char msg;
+    constexpr char kSwitchToFastboot = 'f';
+    constexpr char kSwitchToRecovery = 'r';
+    ssize_t ret = TEMP_FAILURE_RETRY(read(connection_fd, &msg, sizeof(msg)));
+    if (ret != sizeof(msg)) {
+      PLOG(ERROR) << "Couldn't read from socket";
+      continue;
+    }
+    switch (msg) {
+      case kSwitchToRecovery:
+        action = Device::BuiltinAction::ENTER_RECOVERY;
+        break;
+      case kSwitchToFastboot:
+        action = Device::BuiltinAction::ENTER_FASTBOOT;
+        break;
+      default:
+        LOG(ERROR) << "Unrecognized char from socket " << msg;
+        continue;
+    }
+    ui->InterruptKey();
+  }
+}
+
 static void redirect_stdio(const char* filename) {
   int pipefd[2];
   if (pipe(pipefd) == -1) {
@@ -251,6 +294,11 @@
   }
 }
 
+static bool SetUsbConfig(const std::string& state) {
+  android::base::SetProperty("sys.usb.config", state);
+  return android::base::WaitForProperty("sys.usb.state", state);
+}
+
 int main(int argc, char** argv) {
   // We don't have logcat yet under recovery; so we'll print error on screen and log to stdout
   // (which is redirected to recovery.log) as we used to do.
@@ -281,8 +329,6 @@
   // instances with different timestamps.
   redirect_stdio(Paths::Get().temporary_log_file().c_str());
 
-  printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
-
   load_volume_table();
   has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;
 
@@ -290,12 +336,14 @@
   auto args_to_parse = StringVectorToNullTerminatedArray(args);
 
   static constexpr struct option OPTIONS[] = {
+    { "fastboot", no_argument, nullptr, 0 },
     { "locale", required_argument, nullptr, 0 },
     { "show_text", no_argument, nullptr, 't' },
     { nullptr, 0, nullptr, 0 },
   };
 
   bool show_text = false;
+  bool fastboot = false;
   std::string locale;
 
   int arg;
@@ -310,6 +358,8 @@
         std::string option = OPTIONS[option_index].name;
         if (option == "locale") {
           locale = optarg;
+        } else if (option == "fastboot") {
+          fastboot = true;
         }
         break;
       }
@@ -328,8 +378,6 @@
     }
   }
 
-  printf("locale is [%s]\n", locale.c_str());
-
   static constexpr const char* kDefaultLibRecoveryUIExt = "librecovery_ui_ext.so";
   // Intentionally not calling dlclose(3) to avoid potential gotchas (e.g. `make_device` may have
   // handed out pointers to code or static [or thread-local] data and doesn't collect them all back
@@ -374,33 +422,67 @@
   ui->SetBackground(RecoveryUI::NONE);
   if (show_text) ui->ShowText(true);
 
+  LOG(INFO) << "Starting recovery (pid " << getpid() << ") on " << ctime(&start);
+  LOG(INFO) << "locale is [" << locale << "]";
+
   sehandle = selinux_android_file_context_handle();
   selinux_android_set_sehandle(sehandle);
   if (!sehandle) {
     ui->Print("Warning: No file_contexts\n");
   }
 
-  Device::BuiltinAction after = start_recovery(device, args);
+  std::atomic<Device::BuiltinAction> action;
+  std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action));
+  listener_thread.detach();
 
-  switch (after) {
-    case Device::SHUTDOWN:
-      ui->Print("Shutting down...\n");
-      android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,");
-      break;
-
-    case Device::REBOOT_BOOTLOADER:
-      ui->Print("Rebooting to bootloader...\n");
-      android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
-      break;
-
-    default:
-      ui->Print("Rebooting...\n");
-      reboot("reboot,");
-      break;
-  }
   while (true) {
-    pause();
+    std::string usb_config = fastboot ? "fastboot" : is_ro_debuggable() ? "adb" : "none";
+    std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
+    if (usb_config != usb_state) {
+      if (!SetUsbConfig("none")) {
+        LOG(ERROR) << "Failed to clear USB config";
+      }
+      if (!SetUsbConfig(usb_config)) {
+        LOG(ERROR) << "Failed to set USB config to " << usb_config;
+      }
+    }
+
+    auto ret = fastboot ? StartFastboot(device, args) : start_recovery(device, args);
+
+    if (ret == Device::KEY_INTERRUPTED) {
+      ret = action.exchange(ret);
+      if (ret == Device::NO_ACTION) {
+        continue;
+      }
+    }
+    switch (ret) {
+      case Device::SHUTDOWN:
+        ui->Print("Shutting down...\n");
+        android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,");
+        break;
+
+      case Device::REBOOT_BOOTLOADER:
+        ui->Print("Rebooting to bootloader...\n");
+        android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
+        break;
+
+      case Device::ENTER_FASTBOOT:
+        LOG(INFO) << "Entering fastboot";
+        fastboot = true;
+        break;
+
+      case Device::ENTER_RECOVERY:
+        LOG(INFO) << "Entering recovery";
+        fastboot = false;
+        break;
+
+      default:
+        ui->Print("Rebooting...\n");
+        reboot("reboot,");
+        break;
+    }
   }
+
   // Should be unreachable.
   return EXIT_SUCCESS;
 }
diff --git a/roots.cpp b/roots.cpp
index 2b26bf4..06a77c1 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -327,6 +327,7 @@
     "-f",
     "-O", "encrypt",
     "-O", "quota",
+    "-O", "verity",
     "-w", std::to_string(kSectorSize),
     v->blk_device,
   };
diff --git a/tests/Android.bp b/tests/Android.bp
new file mode 100644
index 0000000..d305e25
--- /dev/null
+++ b/tests/Android.bp
@@ -0,0 +1,218 @@
+// Copyright (C) 2018 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: "recovery_test_defaults",
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    include_dirs: [
+        "bootable/recovery",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "libcutils",
+        "liblog",
+        "libpng",
+        "libselinux",
+        "libz",
+        "libziparchive",
+    ],
+
+    target: {
+        android: {
+            shared_libs: [
+                "libutils",
+            ],
+        },
+
+        host: {
+            static_libs: [
+                "libutils",
+            ],
+        }
+    },
+}
+
+// libapplypatch, libapplypatch_modes, libimgdiff, libimgpatch
+libapplypatch_static_libs = [
+    "libapplypatch_modes",
+    "libapplypatch",
+    "libedify",
+    "libimgdiff",
+    "libimgpatch",
+    "libotafault",
+    "libotautil",
+    "libbsdiff",
+    "libbspatch",
+    "libdivsufsort",
+    "libdivsufsort64",
+    "libutils",
+    "libbase",
+    "libbrotli",
+    "libbz",
+    "libcrypto",
+    "libz",
+    "libziparchive",
+]
+
+// librecovery_defaults uses many shared libs that we want to avoid using in tests (e.g. we don't
+// have 32-bit android.hardware.health@2.0.so or libbootloader_message.so on marlin).
+librecovery_static_libs = [
+    "librecovery",
+    "librecovery_fastboot",
+    "libminui",
+    "libverifier",
+    "libotautil",
+
+    "libhealthhalutils",
+    "libvintf_recovery",
+    "libvintf",
+
+    "android.hardware.health@2.0",
+    "android.hardware.health@1.0",
+    "libbootloader_message",
+    "libext4_utils",
+    "libfs_mgr",
+    "libfusesideload",
+    "libhidl-gen-utils",
+    "libhidlbase",
+    "libhidltransport",
+    "libhwbinder",
+    "libvndksupport",
+    "libtinyxml2",
+]
+
+cc_test {
+    name: "recovery_unit_test",
+
+    defaults: [
+        "recovery_test_defaults",
+    ],
+
+    test_suites: ["device-tests"],
+
+    srcs: [
+        "unit/asn1_decoder_test.cpp",
+        "unit/applypatch_test.cpp",
+        "unit/commands_test.cpp",
+        "unit/dirutil_test.cpp",
+        "unit/locale_test.cpp",
+        "unit/rangeset_test.cpp",
+        "unit/screen_ui_test.cpp",
+        "unit/sysutil_test.cpp",
+        "unit/zip_test.cpp",
+    ],
+
+    static_libs: libapplypatch_static_libs + [
+        "librecovery_ui",
+        "libminui",
+        "libverifier",
+        "libotautil",
+        "libupdater",
+        "libgtest_prod",
+        "libBionicGtestMain",
+    ],
+
+    data: ["testdata/*"],
+}
+
+cc_test {
+    name: "recovery_manual_test",
+
+    defaults: [
+        "recovery_test_defaults",
+    ],
+
+    test_suites: ["device-tests"],
+
+    srcs: [
+        "manual/recovery_test.cpp",
+    ],
+
+    static_libs: [
+        "libBionicGtestMain",
+    ],
+}
+
+cc_test {
+    name: "recovery_component_test",
+
+    defaults: [
+        "recovery_test_defaults",
+        "libupdater_defaults",
+    ],
+
+    test_suites: ["device-tests"],
+
+    srcs: [
+        "component/bootloader_message_test.cpp",
+        "component/edify_test.cpp",
+        "component/imgdiff_test.cpp",
+        "component/install_test.cpp",
+        "component/resources_test.cpp",
+        "component/sideload_test.cpp",
+        "component/uncrypt_test.cpp",
+        "component/updater_test.cpp",
+        "component/update_verifier_test.cpp",
+        "component/verifier_test.cpp",
+    ],
+
+    static_libs: libapplypatch_static_libs + librecovery_static_libs + [
+        "libupdater",
+        "libupdate_verifier",
+        "libprotobuf-cpp-lite",
+        "libBionicGtestMain",
+    ],
+
+    data: [
+        "testdata/*",
+        ":res-testdata",
+    ],
+}
+
+cc_test_host {
+    name: "recovery_host_test",
+
+    defaults: [
+        "recovery_test_defaults",
+    ],
+
+    srcs: [
+        "component/imgdiff_test.cpp",
+    ],
+
+    static_libs: [
+        "libimgdiff",
+        "libimgpatch",
+        "libotautil",
+        "libbsdiff",
+        "libbspatch",
+        "libziparchive",
+        "libutils",
+        "libcrypto",
+        "libbrotli",
+        "libbz",
+        "libdivsufsort64",
+        "libdivsufsort",
+        "libz",
+        "libBionicGtestMain",
+    ],
+
+    data: ["testdata/*"],
+}
diff --git a/tests/Android.mk b/tests/Android.mk
deleted file mode 100644
index daf4853..0000000
--- a/tests/Android.mk
+++ /dev/null
@@ -1,230 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# libapplypatch, libapplypatch_modes, libimgdiff, libimgpatch.
-libapplypatch_static_libraries := \
-    libapplypatch_modes \
-    libapplypatch \
-    libedify \
-    libimgdiff \
-    libimgpatch \
-    libotafault \
-    libotautil \
-    libbsdiff \
-    libbspatch \
-    libdivsufsort \
-    libdivsufsort64 \
-    libutils \
-    libbase \
-    libbrotli \
-    libbz \
-    libcrypto \
-    libz \
-    libziparchive \
-
-# Unit tests
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_MODULE := recovery_unit_test
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_STATIC_LIBRARIES := \
-    $(libapplypatch_static_libraries) \
-    libverifier \
-    librecovery_ui \
-    libminui \
-    libotautil \
-    libupdater \
-    libgtest_prod \
-    libpng \
-    libziparchive \
-    libutils \
-    libz \
-    libselinux \
-    libbase \
-    libBionicGtestMain
-
-LOCAL_SRC_FILES := \
-    unit/applypatch_test.cpp \
-    unit/asn1_decoder_test.cpp \
-    unit/commands_test.cpp \
-    unit/dirutil_test.cpp \
-    unit/locale_test.cpp \
-    unit/rangeset_test.cpp \
-    unit/screen_ui_test.cpp \
-    unit/sysutil_test.cpp \
-    unit/zip_test.cpp
-
-LOCAL_C_INCLUDES := bootable/recovery
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_TEST_DATA := \
-    $(call find-test-data-in-subdirs, $(LOCAL_PATH), "*", testdata)
-include $(BUILD_NATIVE_TEST)
-
-# Manual tests
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_MODULE := recovery_manual_test
-LOCAL_STATIC_LIBRARIES := \
-    libbase \
-    libBionicGtestMain
-
-LOCAL_SRC_FILES := manual/recovery_test.cpp
-LOCAL_SHARED_LIBRARIES := \
-    liblog
-
-include $(BUILD_NATIVE_TEST)
-
-# Component tests
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := \
-    -Wall \
-    -Werror \
-    -D_FILE_OFFSET_BITS=64
-
-LOCAL_MODULE := recovery_component_test
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_C_INCLUDES := bootable/recovery
-LOCAL_SRC_FILES := \
-    component/applypatch_modes_test.cpp \
-    component/bootloader_message_test.cpp \
-    component/edify_test.cpp \
-    component/imgdiff_test.cpp \
-    component/install_test.cpp \
-    component/resources_test.cpp \
-    component/sideload_test.cpp \
-    component/uncrypt_test.cpp \
-    component/updater_test.cpp \
-    component/update_verifier_test.cpp \
-    component/verifier_test.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    libhidlbase \
-    libprotobuf-cpp-lite
-
-tune2fs_static_libraries := \
-    libext2_com_err \
-    libext2_blkid \
-    libext2_quota \
-    libext2_uuid \
-    libext2_e2p \
-    libext2fs
-
-libupdater_static_libraries := \
-    libupdater \
-    libapplypatch \
-    libbspatch \
-    libedify \
-    libziparchive \
-    libotautil \
-    libbootloader_message \
-    libutils \
-    libotafault \
-    libext4_utils \
-    libfec \
-    libfec_rs \
-    libverity_tree \
-    libfs_mgr \
-    libgtest_prod \
-    liblog \
-    libselinux \
-    libsparse \
-    libsquashfs_utils \
-    libbz \
-    libz \
-    libbase \
-    libcrypto \
-    libcrypto_utils \
-    libcutils \
-    libtune2fs \
-    libbrotli \
-    $(tune2fs_static_libraries)
-
-librecovery_static_libraries := \
-    librecovery \
-    libbootloader_message \
-    libfusesideload \
-    libminadbd \
-    librecovery_ui_default \
-    librecovery_ui \
-    libminui \
-    libverifier \
-    libotautil \
-    libasyncio \
-    libbatterymonitor \
-    libcrypto_utils \
-    libcrypto \
-    libext4_utils \
-    libfs_mgr \
-    libpng \
-    libsparse \
-    libvintf_recovery \
-    libvintf \
-    libhidl-gen-utils \
-    libtinyxml2 \
-    libziparchive \
-    libbase \
-    libutils \
-    libcutils \
-    liblog \
-    libselinux \
-    libz \
-
-libupdate_verifier_static_libraries := \
-    libupdate_verifier \
-
-LOCAL_STATIC_LIBRARIES := \
-    $(libapplypatch_static_libraries) \
-    $(librecovery_static_libraries) \
-    $(libupdate_verifier_static_libraries) \
-    $(libupdater_static_libraries) \
-    libBionicGtestMain
-
-LOCAL_TEST_DATA := \
-    $(call find-test-data-in-subdirs, $(LOCAL_PATH), "*", testdata) \
-    $(call find-test-data-in-subdirs, bootable/recovery, "*_text.png", res-*)
-include $(BUILD_NATIVE_TEST)
-
-# Host tests
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_MODULE := recovery_host_test
-LOCAL_MODULE_HOST_OS := linux
-LOCAL_C_INCLUDES := bootable/recovery
-LOCAL_SRC_FILES := \
-    component/imgdiff_test.cpp
-LOCAL_STATIC_LIBRARIES := \
-    libimgdiff \
-    libimgpatch \
-    libotautil \
-    libbsdiff \
-    libbspatch \
-    libziparchive \
-    libutils \
-    libbase \
-    libcrypto \
-    libbrotli \
-    libbz \
-    libdivsufsort64 \
-    libdivsufsort \
-    libz \
-    libBionicGtestMain
-LOCAL_SHARED_LIBRARIES := \
-    liblog
-LOCAL_TEST_DATA := \
-    $(call find-test-data-in-subdirs, $(LOCAL_PATH), "*", testdata)
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp
index b9af0b1..08b4290 100644
--- a/tests/component/install_test.cpp
+++ b/tests/component/install_test.cpp
@@ -305,7 +305,7 @@
   if (success) {
     ASSERT_EQ(0, SetUpAbUpdateCommands(package, zip, status_fd, &cmd));
     ASSERT_EQ(5U, cmd.size());
-    ASSERT_EQ("/sbin/update_engine_sideload", cmd[0]);
+    ASSERT_EQ("/system/bin/update_engine_sideload", cmd[0]);
     ASSERT_EQ("--payload=file://" + package, cmd[1]);
     ASSERT_EQ("--offset=" + std::to_string(payload_entry.offset), cmd[2]);
     ASSERT_EQ("--headers=" + properties, cmd[3]);
diff --git a/tests/component/resources_test.cpp b/tests/component/resources_test.cpp
index 618d5a4..54329db 100644
--- a/tests/component/resources_test.cpp
+++ b/tests/component/resources_test.cpp
@@ -32,10 +32,10 @@
 
 static const std::string kLocale = "zu";
 
-static const std::vector<std::string> kResourceImagesDirs{ "res-mdpi/images/", "res-hdpi/images/",
-                                                           "res-xhdpi/images/",
-                                                           "res-xxhdpi/images/",
-                                                           "res-xxxhdpi/images/" };
+static const std::vector<std::string> kResourceImagesDirs{
+  "res-mdpi/images/",   "res-hdpi/images/",    "res-xhdpi/images/",
+  "res-xxhdpi/images/", "res-xxxhdpi/images/",
+};
 
 static int png_filter(const dirent* de) {
   if (de->d_type != DT_REG || !android::base::EndsWith(de->d_name, "_text.png")) {
diff --git a/tests/unit/commands_test.cpp b/tests/unit/commands_test.cpp
index 9679a9e..19841d6 100644
--- a/tests/unit/commands_test.cpp
+++ b/tests/unit/commands_test.cpp
@@ -333,6 +333,25 @@
   ASSERT_EQ(PatchInfo(), command.patch());
 }
 
+TEST(CommandsTest, Parse_COMPUTE_HASH_TREE) {
+  const std::string input{ "compute_hash_tree 2,0,1 2,3,4 sha1 unknown-salt unknown-root-hash" };
+  std::string err;
+  Command command = Command::Parse(input, 9, &err);
+  ASSERT_TRUE(command);
+
+  ASSERT_EQ(Command::Type::COMPUTE_HASH_TREE, command.type());
+  ASSERT_EQ(9, command.index());
+  ASSERT_EQ(input, command.cmdline());
+
+  HashTreeInfo expected_info(RangeSet({ { 0, 1 } }), RangeSet({ { 3, 4 } }), "sha1", "unknown-salt",
+                             "unknown-root-hash");
+  ASSERT_EQ(expected_info, command.hash_tree_info());
+  ASSERT_EQ(TargetInfo(), command.target());
+  ASSERT_EQ(SourceInfo(), command.source());
+  ASSERT_EQ(StashInfo(), command.stash());
+  ASSERT_EQ(PatchInfo(), command.patch());
+}
+
 TEST(CommandsTest, Parse_InvalidNumberOfArgs) {
   Command::abort_allowed_ = true;
 
@@ -341,6 +360,7 @@
   std::vector<std::string> inputs{
     "abort foo",
     "bsdiff",
+    "compute_hash_tree, 2,0,1 2,0,1 unknown-algorithm unknown-salt",
     "erase",
     "erase 4,3,5,10,12 hash1",
     "free",
diff --git a/tools/recovery_l10n/res/values-as/strings.xml b/tools/recovery_l10n/res/values-as/strings.xml
new file mode 100644
index 0000000..2624ceb
--- /dev/null
+++ b/tools/recovery_l10n/res/values-as/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="recovery_installing" msgid="2013591905463558223">"আপডেইট ইনষ্টল কৰি থকা হৈছে"</string>
+    <string name="recovery_erasing" msgid="7334826894904037088">"মচি থকা হৈছে"</string>
+    <string name="recovery_no_command" msgid="4465476568623024327">"কোনো আদেশ নাই"</string>
+    <string name="recovery_error" msgid="5748178989622716736">"ত্ৰুটি!"</string>
+    <string name="recovery_installing_security" msgid="9184031299717114342">"সুৰক্ষা আপডেইট ইনষ্টল কৰি থকা হৈছে"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-en-rCA/strings.xml b/tools/recovery_l10n/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..dc75c23
--- /dev/null
+++ b/tools/recovery_l10n/res/values-en-rCA/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="recovery_installing" msgid="2013591905463558223">"Installing system update"</string>
+    <string name="recovery_erasing" msgid="7334826894904037088">"Erasing"</string>
+    <string name="recovery_no_command" msgid="4465476568623024327">"No command"</string>
+    <string name="recovery_error" msgid="5748178989622716736">"Error!"</string>
+    <string name="recovery_installing_security" msgid="9184031299717114342">"Installing security update"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-en-rXC/strings.xml b/tools/recovery_l10n/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..2d528b3
--- /dev/null
+++ b/tools/recovery_l10n/res/values-en-rXC/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="recovery_installing" msgid="2013591905463558223">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎Installing system update‎‏‎‎‏‎"</string>
+    <string name="recovery_erasing" msgid="7334826894904037088">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎Erasing‎‏‎‎‏‎"</string>
+    <string name="recovery_no_command" msgid="4465476568623024327">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎No command‎‏‎‎‏‎"</string>
+    <string name="recovery_error" msgid="5748178989622716736">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎Error!‎‏‎‎‏‎"</string>
+    <string name="recovery_installing_security" msgid="9184031299717114342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎Installing security update‎‏‎‎‏‎"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-or/strings.xml b/tools/recovery_l10n/res/values-or/strings.xml
new file mode 100644
index 0000000..2b0851c
--- /dev/null
+++ b/tools/recovery_l10n/res/values-or/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="recovery_installing" msgid="2013591905463558223">"ସିଷ୍ଟମ ଅପଡେଟ ଇନଷ୍ଟଲ କରୁଛି"</string>
+    <string name="recovery_erasing" msgid="7334826894904037088">"ଲିଭାଉଛି"</string>
+    <string name="recovery_no_command" msgid="4465476568623024327">"କୌଣସି କମାଣ୍ଡ ନାହିଁ"</string>
+    <string name="recovery_error" msgid="5748178989622716736">"ତ୍ରୁଟି!"</string>
+    <string name="recovery_installing_security" msgid="9184031299717114342">"ସୁରକ୍ଷା ଅପ୍‌ଡେଟ୍‌ ଇନ୍‌ଷ୍ଟଲ୍‌ କରୁଛି"</string>
+</resources>
diff --git a/updater/Android.bp b/updater/Android.bp
new file mode 100644
index 0000000..c77bac8
--- /dev/null
+++ b/updater/Android.bp
@@ -0,0 +1,80 @@
+// Copyright (C) 2018 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: "libupdater_defaults",
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    static_libs: [
+        "libapplypatch",
+        "libbootloader_message",
+        "libbspatch",
+        "libedify",
+        "libotafault",
+        "libotautil",
+        "libext4_utils",
+        "libfec",
+        "libfec_rs",
+        "libverity_tree",
+        "libfs_mgr",
+        "libgtest_prod",
+        "liblog",
+        "libselinux",
+        "libsparse",
+        "libsquashfs_utils",
+        "libbrotli",
+        "libbz",
+        "libziparchive",
+        "libz",
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "libutils",
+        "libtune2fs",
+
+        "libext2_com_err",
+        "libext2_blkid",
+        "libext2_quota",
+        "libext2_uuid",
+        "libext2_e2p",
+        "libext2fs",
+    ],
+}
+
+cc_library_static {
+    name: "libupdater",
+
+    defaults: [
+        "recovery_defaults",
+        "libupdater_defaults",
+    ],
+
+    srcs: [
+        "blockimg.cpp",
+        "commands.cpp",
+        "install.cpp",
+    ],
+
+    include_dirs: [
+        "external/e2fsprogs/misc",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+}
diff --git a/updater/Android.mk b/updater/Android.mk
index 78d0bd4..5478a7d 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -25,12 +25,10 @@
 updater_common_static_libraries := \
     libapplypatch \
     libbootloader_message \
+    libbspatch \
     libedify \
     libotafault \
     libotautil \
-    libbspatch \
-    libziparchive \
-    libutils \
     libext4_utils \
     libfec \
     libfec_rs \
@@ -41,43 +39,18 @@
     libselinux \
     libsparse \
     libsquashfs_utils \
+    libbrotli \
     libbz \
+    libziparchive \
     libz \
     libbase \
     libcrypto \
     libcrypto_utils \
     libcutils \
+    libutils \
     libtune2fs \
-    libbrotli \
     $(tune2fs_static_libraries)
 
-# libupdater (static library)
-# ===============================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libupdater
-
-LOCAL_SRC_FILES := \
-    commands.cpp \
-    install.cpp \
-    blockimg.cpp
-
-LOCAL_C_INCLUDES := \
-    $(LOCAL_PATH)/include \
-    external/e2fsprogs/misc
-
-LOCAL_CFLAGS := \
-    -Wall \
-    -Werror
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
-    $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
-    $(updater_common_static_libraries)
-
-include $(BUILD_STATIC_LIBRARY)
-
 # updater (static executable)
 # ===============================
 include $(CLEAR_VARS)
diff --git a/updater/commands.cpp b/updater/commands.cpp
index 15a787c..4a90ea8 100644
--- a/updater/commands.cpp
+++ b/updater/commands.cpp
@@ -31,6 +31,14 @@
 
 bool Command::abort_allowed_ = false;
 
+Command::Command(Type type, size_t index, std::string cmdline, HashTreeInfo hash_tree_info)
+    : type_(type),
+      index_(index),
+      cmdline_(std::move(cmdline)),
+      hash_tree_info_(std::move(hash_tree_info)) {
+  CHECK(type == Type::COMPUTE_HASH_TREE);
+}
+
 Command::Type Command::ParseType(const std::string& type_str) {
   if (type_str == "abort") {
     if (!abort_allowed_) {
@@ -177,7 +185,6 @@
   SourceInfo source_info;
   StashInfo stash_info;
 
-  // TODO(xunchang) add the parse code of compute_hash_tree
   if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) {
     // zero/new/erase <rangeset>
     if (pos + 1 != tokens.size()) {
@@ -255,6 +262,39 @@
                                          tokens.size() - pos);
       return {};
     }
+  } else if (op == Type::COMPUTE_HASH_TREE) {
+    // <hash_tree_ranges> <source_ranges> <hash_algorithm> <salt_hex> <root_hash>
+    if (pos + 5 != tokens.size()) {
+      *err = android::base::StringPrintf("invalid number of args: %zu (expected 5)",
+                                         tokens.size() - pos);
+      return {};
+    }
+
+    // Expects the hash_tree data to be contiguous.
+    RangeSet hash_tree_ranges = RangeSet::Parse(tokens[pos++]);
+    if (!hash_tree_ranges || hash_tree_ranges.size() != 1) {
+      *err = "invalid hash tree ranges in: " + line;
+      return {};
+    }
+
+    RangeSet source_ranges = RangeSet::Parse(tokens[pos++]);
+    if (!source_ranges) {
+      *err = "invalid source ranges in: " + line;
+      return {};
+    }
+
+    std::string hash_algorithm = tokens[pos++];
+    std::string salt_hex = tokens[pos++];
+    std::string root_hash = tokens[pos++];
+    if (hash_algorithm.empty() || salt_hex.empty() || root_hash.empty()) {
+      *err = "invalid hash tree arguments in " + line;
+      return {};
+    }
+
+    HashTreeInfo hash_tree_info(std::move(hash_tree_ranges), std::move(source_ranges),
+                                std::move(hash_algorithm), std::move(salt_hex),
+                                std::move(root_hash));
+    return Command(op, index, line, std::move(hash_tree_info));
   } else {
     *err = "invalid op";
     return {};
diff --git a/updater/include/private/commands.h b/updater/include/private/commands.h
index 7f9dc79..85b5288 100644
--- a/updater/include/private/commands.h
+++ b/updater/include/private/commands.h
@@ -166,6 +166,50 @@
   size_t length_{ 0 };
 };
 
+// The arguments to build a hash tree from blocks on the block device.
+class HashTreeInfo {
+ public:
+  HashTreeInfo() = default;
+
+  HashTreeInfo(RangeSet hash_tree_ranges, RangeSet source_ranges, std::string hash_algorithm,
+               std::string salt_hex, std::string root_hash)
+      : hash_tree_ranges_(std::move(hash_tree_ranges)),
+        source_ranges_(std::move(source_ranges)),
+        hash_algorithm_(std::move(hash_algorithm)),
+        salt_hex_(std::move(salt_hex)),
+        root_hash_(std::move(root_hash)) {}
+
+  const RangeSet& hash_tree_ranges() const {
+    return hash_tree_ranges_;
+  }
+  const RangeSet& source_ranges() const {
+    return source_ranges_;
+  }
+
+  const std::string& hash_algorithm() const {
+    return hash_algorithm_;
+  }
+  const std::string& salt_hex() const {
+    return salt_hex_;
+  }
+  const std::string& root_hash() const {
+    return root_hash_;
+  }
+
+  bool operator==(const HashTreeInfo& other) const {
+    return hash_tree_ranges_ == other.hash_tree_ranges_ && source_ranges_ == other.source_ranges_ &&
+           hash_algorithm_ == other.hash_algorithm_ && salt_hex_ == other.salt_hex_ &&
+           root_hash_ == other.root_hash_;
+  }
+
+ private:
+  RangeSet hash_tree_ranges_;
+  RangeSet source_ranges_;
+  std::string hash_algorithm_;
+  std::string salt_hex_;
+  std::string root_hash_;
+};
+
 // Command class holds the info for an update command that performs block-based OTA (BBOTA). Each
 // command consists of one or several args, namely TargetInfo, SourceInfo, StashInfo and PatchInfo.
 // The currently used BBOTA version is v4.
@@ -248,6 +292,8 @@
         source_(std::move(source)),
         stash_(std::move(stash)) {}
 
+  Command(Type type, size_t index, std::string cmdline, HashTreeInfo hash_tree_info);
+
   // Parses the given command 'line' into a Command object and returns it. The 'index' is specified
   // by the caller to index the object. On parsing error, it returns an empty Command object that
   // evaluates to false, and the specific error message will be set in 'err'.
@@ -284,6 +330,10 @@
     return stash_;
   }
 
+  const HashTreeInfo& hash_tree_info() const {
+    return hash_tree_info_;
+  }
+
   constexpr explicit operator bool() const {
     return type_ != Type::LAST;
   }
@@ -325,6 +375,8 @@
   // The stash info. Only meaningful for STASH and FREE commands. Note that although SourceInfo may
   // also load data from stash, such info will be owned and managed by SourceInfo (i.e. in source_).
   StashInfo stash_;
+  // The hash_tree info. Only meaningful for COMPUTE_HASH_TREE.
+  HashTreeInfo hash_tree_info_;
 };
 
 std::ostream& operator<<(std::ostream& os, const Command& command);
diff --git a/updater/install.cpp b/updater/install.cpp
index 088d24b..f933345 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -497,12 +497,10 @@
     const char* f2fs_argv[] = { "mkfs.f2fs",
                                 "-d1",
                                 "-f",
-                                "-O",
-                                "encrypt",
-                                "-O",
-                                "quota",
-                                "-w",
-                                "512",
+                                "-O", "encrypt",
+                                "-O", "quota",
+                                "-O", "verity",
+                                "-w", "512",
                                 location.c_str(),
                                 (size < 512) ? nullptr : num_sectors.c_str(),
                                 nullptr };
diff --git a/updater_sample/Android.bp b/updater_sample/Android.bp
new file mode 100644
index 0000000..845e07b
--- /dev/null
+++ b/updater_sample/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2018 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.
+
+android_app {
+    name: "SystemUpdaterSample",
+    sdk_version: "system_current",
+    privileged: true,
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "guava",
+    ],
+
+    optimize: {
+        proguard_flags_files: [
+            "proguard.flags",
+        ],
+    },
+
+    resource_dirs: ["res"],
+}
diff --git a/updater_sample/Android.mk b/updater_sample/Android.mk
deleted file mode 100644
index cff5b0c..0000000
--- a/updater_sample/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := SystemUpdaterSample
-LOCAL_SDK_VERSION := system_current
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES += guava
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/updater_sample/README.md b/updater_sample/README.md
index 306e71e..69e8e24 100644
--- a/updater_sample/README.md
+++ b/updater_sample/README.md
@@ -227,7 +227,7 @@
 
 1. Build `mmma bootable/recovery/updater_sample/`
 2. Install app
-   `adb install $OUT/system/app/SystemUpdaterSample/SystemUpdaterSample.apk`
+   `adb install $OUT/system/priv-app/SystemUpdaterSample/SystemUpdaterSample.apk`
 3. Install tests
    `adb install $OUT/testcases/SystemUpdaterSampleTests/SystemUpdaterSampleTests.apk`
 4. Run tests
diff --git a/updater_sample/tests/Android.bp b/updater_sample/tests/Android.bp
new file mode 100644
index 0000000..c2783ef
--- /dev/null
+++ b/updater_sample/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 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.
+
+android_test {
+    name: "SystemUpdaterSampleTests",
+    sdk_version: "system_current",
+
+    libs: [
+        "android.test.base.stubs",
+        "android.test.runner.stubs",
+        "SystemUpdaterSample",
+    ],
+
+    static_libs: [
+        "android-support-test",
+        "mockito-target-minus-junit4",
+        "guava",
+    ],
+
+    instrumentation_for: "com.example.android.systemupdatersample",
+
+    optimize: {
+        enabled: false,
+    },
+
+    resource_dirs: ["res"],
+
+    srcs: ["src/**/*.java"],
+}
diff --git a/updater_sample/tests/Android.mk b/updater_sample/tests/Android.mk
deleted file mode 100644
index 4157604..0000000
--- a/updater_sample/tests/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := SystemUpdaterSampleTests
-LOCAL_SDK_VERSION := system_current
-LOCAL_MODULE_TAGS := tests
-LOCAL_JAVA_LIBRARIES := \
-    android.test.base.stubs \
-    android.test.runner.stubs
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test \
-    mockito-target-minus-junit4 \
-    guava
-LOCAL_INSTRUMENTATION_FOR := SystemUpdaterSample
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-include $(BUILD_PACKAGE)