summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2018-08-15 05:11:19 +0200
committerandroid-build-team Robot <android-build-team-robot@google.com>2018-08-15 05:11:19 +0200
commitbc563e6b2b41ede7efb89e5b566f3a83d50e71fd (patch)
treede86583ed092366d56b5f4f7ba4a6db5b03bb28d
parentSnap for 4951335 from 17f5b296d1c0d4bdd6a67fbe7dfd088187c8686e to qt-release (diff)
parentMerge "Add fastboot mode to recovery" am: 19a5316412 am: b0851cf8c4 (diff)
downloadandroid_bootable_recovery-bc563e6b2b41ede7efb89e5b566f3a83d50e71fd.tar
android_bootable_recovery-bc563e6b2b41ede7efb89e5b566f3a83d50e71fd.tar.gz
android_bootable_recovery-bc563e6b2b41ede7efb89e5b566f3a83d50e71fd.tar.bz2
android_bootable_recovery-bc563e6b2b41ede7efb89e5b566f3a83d50e71fd.tar.lz
android_bootable_recovery-bc563e6b2b41ede7efb89e5b566f3a83d50e71fd.tar.xz
android_bootable_recovery-bc563e6b2b41ede7efb89e5b566f3a83d50e71fd.tar.zst
android_bootable_recovery-bc563e6b2b41ede7efb89e5b566f3a83d50e71fd.zip
-rw-r--r--Android.bp44
-rw-r--r--Android.mk2
-rw-r--r--device.cpp1
-rw-r--r--device.h2
-rw-r--r--etc/init.rc103
-rw-r--r--fastboot/fastboot.cpp82
-rw-r--r--fastboot/fastboot.h24
-rw-r--r--recovery.cpp70
-rw-r--r--recovery_main.cpp124
-rw-r--r--tests/Android.mk23
-rw-r--r--tests/unit/commands_test.cpp20
-rw-r--r--updater/Android.bp71
-rw-r--r--updater/Android.mk35
-rw-r--r--updater/commands.cpp42
-rw-r--r--updater/include/private/commands.h52
15 files changed, 551 insertions, 144 deletions
diff --git a/Android.bp b/Android.bp
index 630c7965e..e97f71d70 100644
--- a/Android.bp
+++ b/Android.bp
@@ -96,6 +96,29 @@ cc_library_static {
],
}
+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",
@@ -104,19 +127,20 @@ cc_defaults {
],
shared_libs: [
+ "android.hardware.health@2.0",
"libbase",
"libbootloader_message",
"libcrypto",
- "libcrypto_utils",
"libcutils",
"libext4_utils",
"libfs_mgr",
"libfusesideload",
"libhidl-gen-utils",
+ "libhidlbase",
+ "libhidltransport",
"liblog",
"libpng",
"libselinux",
- "libsparse",
"libtinyxml2",
"libutils",
"libz",
@@ -124,23 +148,15 @@ cc_defaults {
],
static_libs: [
+ "librecovery_fastboot",
"libminui",
"libverifier",
"libotautil",
+
+ // external dependencies
+ "libhealthhalutils",
"libvintf_recovery",
"libvintf",
-
- // TODO(b/80132328): Remove the dependency on static health HAL.
- "libhealthd.default",
- "android.hardware.health@2.0-impl",
- "android.hardware.health@2.0",
- "android.hardware.health@1.0",
- "android.hardware.health@1.0-convert",
- "libhealthstoragedefault",
- "libhidltransport",
- "libhidlbase",
- "libhwbinder_noltopgo",
- "libbatterymonitor",
],
}
diff --git a/Android.mk b/Android.mk
index 9888f8616..7d13f4cc4 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
diff --git a/device.cpp b/device.cpp
index 3c6334e5c..eec1932c2 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 a6ad62788..6a8daf83e 100644
--- a/device.h
+++ b/device.h
@@ -48,6 +48,8 @@ class Device {
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 3821eb6a8..9add2494a 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -6,6 +6,8 @@ on early-init
start ueventd
+ setprop sys.usb.configfs 0
+
on init
export ANDROID_ROOT /system
export ANDROID_DATA /data
@@ -31,20 +33,6 @@ on init
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
@@ -86,6 +74,7 @@ service charger /charger -r
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 @@ service adbd /system/bin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
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 000000000..8458c99dd
--- /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 000000000..53a2adcae
--- /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/recovery.cpp b/recovery.cpp
index cc30035df..01bd83b5e 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 <health2/Health.h>
+#include <healthhalutils/HealthHalUtils.h>
#include <ziparchive/zip_archive.h>
#include "adb_install.h"
@@ -779,6 +779,8 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
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:
@@ -877,42 +879,29 @@ void ui_print(const char* format, ...) {
static bool is_battery_ok(int* required_battery_level) {
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;
- using android::hardware::health::V2_0::implementation::Health;
-
- 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
- };
- auto health =
- android::hardware::health::V2_0::implementation::Health::initInstance(&healthd_config);
+ android::sp<IHealth> health = get_health_service();
static constexpr int BATTERY_READ_TIMEOUT_IN_SEC = 10;
int wait_second = 0;
while (true) {
auto charge_status = BatteryStatus::UNKNOWN;
- health
- ->getChargeStatus([&charge_status](auto res, auto out_status) {
- if (res == Result::SUCCESS) {
- charge_status = out_status;
- }
- })
- .isOk(); // should not have transport error
+
+ 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 != BatteryStatus::DISCHARGING &&
@@ -920,15 +909,17 @@ static bool is_battery_ok(int* required_battery_level) {
Result res = Result::UNKNOWN;
int32_t capacity = INT32_MIN;
- health
- ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) {
- res = out_res;
- capacity = out_capacity;
- })
- .isOk(); // should not have transport error
-
- ui_print("charge_status %d, charged %d, status %s, capacity %" PRId32 "\n", charge_status,
- charged, toString(res).c_str(), capacity);
+ 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
@@ -1006,6 +997,7 @@ static void log_failure_code(ErrorCode code, const std::string& update_package)
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 },
@@ -1060,7 +1052,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
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 9a9890de0..99f965098 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 @@ static std::string load_locale_from_cache() {
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 void redirect_stdio(const char* filename) {
}
}
+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 @@ int main(int argc, char** argv) {
// 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 @@ int main(int argc, char** argv) {
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 @@ int main(int argc, char** argv) {
std::string option = OPTIONS[option_index].name;
if (option == "locale") {
locale = optarg;
+ } else if (option == "fastboot") {
+ fastboot = true;
}
break;
}
@@ -328,8 +378,6 @@ int main(int argc, char** argv) {
}
}
- 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 @@ int main(int argc, char** argv) {
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();
+
+ while (true) {
+ 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;
+ }
+ }
- switch (after) {
- case Device::SHUTDOWN:
- ui->Print("Shutting down...\n");
- android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,");
- break;
+ auto ret = fastboot ? StartFastboot(device, args) : start_recovery(device, args);
- case Device::REBOOT_BOOTLOADER:
- ui->Print("Rebooting to bootloader...\n");
- android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
- break;
+ 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;
- default:
- ui->Print("Rebooting...\n");
- reboot("reboot,");
- break;
- }
- while (true) {
- pause();
+ 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/tests/Android.mk b/tests/Android.mk
index b6f5b451f..b59da8045 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -127,13 +127,11 @@ tune2fs_static_libraries := \
libupdater_static_libraries := \
libupdater \
libapplypatch \
+ libbootloader_message \
libbspatch \
libedify \
- libziparchive \
- libotautil \
- libbootloader_message \
- libutils \
libotafault \
+ libotautil \
libext4_utils \
libfec \
libfec_rs \
@@ -144,28 +142,18 @@ libupdater_static_libraries := \
libselinux \
libsparse \
libsquashfs_utils \
+ libbrotli \
libbz \
+ libziparchive \
libz \
libbase \
libcrypto \
libcrypto_utils \
libcutils \
+ libutils \
libtune2fs \
- libbrotli \
$(tune2fs_static_libraries)
-health_hal_static_libraries := \
- android.hardware.health@2.0-impl \
- android.hardware.health@2.0 \
- android.hardware.health@1.0 \
- android.hardware.health@1.0-convert \
- libhealthstoragedefault \
- libhidltransport \
- libhidlbase \
- libhwbinder_noltopgo \
- libvndksupport \
- libbatterymonitor
-
librecovery_static_libraries := \
librecovery \
libbootloader_message \
@@ -175,7 +163,6 @@ librecovery_static_libraries := \
libminui \
libverifier \
libotautil \
- $(health_hal_static_libraries) \
libcrypto_utils \
libcrypto \
libext4_utils \
diff --git a/tests/unit/commands_test.cpp b/tests/unit/commands_test.cpp
index 9679a9e73..19841d676 100644
--- a/tests/unit/commands_test.cpp
+++ b/tests/unit/commands_test.cpp
@@ -333,6 +333,25 @@ TEST(CommandsTest, Parse_ZERO) {
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 @@ TEST(CommandsTest, Parse_InvalidNumberOfArgs) {
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/updater/Android.bp b/updater/Android.bp
new file mode 100644
index 000000000..9a36ebbb7
--- /dev/null
+++ b/updater/Android.bp
@@ -0,0 +1,71 @@
+// 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_static {
+ name: "libupdater",
+
+ defaults: [
+ "recovery_defaults",
+ ],
+
+ srcs: [
+ "blockimg.cpp",
+ "commands.cpp",
+ "install.cpp",
+ ],
+
+ include_dirs: [
+ "external/e2fsprogs/misc",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ 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",
+ ],
+}
diff --git a/updater/Android.mk b/updater/Android.mk
index 78d0bd451..5478a7df6 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -25,12 +25,10 @@ tune2fs_static_libraries := \
updater_common_static_libraries := \
libapplypatch \
libbootloader_message \
+ libbspatch \
libedify \
libotafault \
libotautil \
- libbspatch \
- libziparchive \
- libutils \
libext4_utils \
libfec \
libfec_rs \
@@ -41,43 +39,18 @@ updater_common_static_libraries := \
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 15a787c51..4a90ea873 100644
--- a/updater/commands.cpp
+++ b/updater/commands.cpp
@@ -31,6 +31,14 @@ using namespace std::string_literals;
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 @@ Command Command::Parse(const std::string& line, size_t index, std::string* err)
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 @@ Command Command::Parse(const std::string& line, size_t index, std::string* err)
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 7f9dc79f4..85b52883b 100644
--- a/updater/include/private/commands.h
+++ b/updater/include/private/commands.h
@@ -166,6 +166,50 @@ class PatchInfo {
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 @@ class Command {
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 @@ class Command {
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 @@ class Command {
// 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);