Android10.0 OTA A/B升级流程分析
A/B更新介绍
https://source.android.google.cn/devices/tech/ota/ab#post-installation
1.编译打包流程
build.sh 找到qssi*-target_files-*.zip ${TARGET_PRODUCT}*-target_files-*.zip /merge_target_files.py生成target包。
function generate_ota_zip () {
log "Processing dist/ota commands:"
SYSTEM_TARGET_FILES="$(find $DIST_DIR -name "qssi*-target_files-*.zip" -print)"
log "SYSTEM_TARGET_FILES=$SYSTEM_TARGET_FILES"
check_if_file_exists "$SYSTEM_TARGET_FILES"
OTHER_TARGET_FILES="$(find $DIST_DIR -name "${TARGET_PRODUCT}*-target_files-*.zip" -print)"
log "OTHER_TARGET_FILES=$OTHER_TARGET_FILES"
check_if_file_exists "$OTHER_TARGET_FILES"
log "MERGED_TARGET_FILES=$MERGED_TARGET_FILES"
check_if_file_exists "$DIST_DIR/merge_config_system_misc_info_keys"
check_if_file_exists "$DIST_DIR/merge_config_system_item_list"
check_if_file_exists "$DIST_DIR/merge_config_other_item_list"
MERGE_TARGET_FILES_COMMAND="./build/tools/releasetools/merge_target_files.py \
--system-target-files $SYSTEM_TARGET_FILES \
--other-target-files $OTHER_TARGET_FILES \
--output-target-files $MERGED_TARGET_FILES \
--system-misc-info-keys $DIST_DIR/merge_config_system_misc_info_keys \
--system-item-list $DIST_DIR/merge_config_system_item_list \
--other-item-list $DIST_DIR/merge_config_other_item_list \
--output-ota $MERGED_OTA_ZIP"
if [ "$ENABLE_AB" = false ]; then
MERGE_TARGET_FILES_COMMAND="$MERGE_TARGET_FILES_COMMAND --rebuild_recovery"
fi
command "$MERGE_TARGET_FILES_COMMAND"
}
# -----------------------------------------------------------------
# OTA update package
# $(1): output file
# $(2): additional args
define build-ota-package-target
PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH MKBOOTIMG=$(MKBOOTIMG) \
build/make/tools/releasetools/ota_from_target_files -v \
--block \
--extracted_input_target_files $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) \
-p $(HOST_OUT) \
$(if $(OEM_OTA_CONFIG), -o $(OEM_OTA_CONFIG)) \
$(2) \
$(BUILT_TARGET_FILES_PACKAGE) $(1)
endef
name := $(TARGET_PRODUCT)
ifeq ($(TARGET_BUILD_TYPE),debug)
name := $(name)_debug
endif
name := $(name)-ota-$(FILE_NAME_TAG)
INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
INTERNAL_OTA_METADATA := $(PRODUCT_OUT)/ota_metadata
$(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
ifeq ($(AB_OTA_UPDATER),true)
$(INTERNAL_OTA_PACKAGE_TARGET): $(BRILLO_UPDATE_PAYLOAD)
else
$(INTERNAL_OTA_PACKAGE_TARGET): $(BROTLI)
endif
$(INTERNAL_OTA_PACKAGE_TARGET): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_OTA_METADATA)
$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) \
build/make/tools/releasetools/ota_from_target_files
@echo "Package OTA: $@"
$(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --output_metadata_path $(INTERNAL_OTA_METADATA))
.PHONY: otapackage
otapackage: $(INTERNAL_OTA_PACKAGE_TARGET)
ifeq ($(BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE),true)
name := $(TARGET_PRODUCT)
ifeq ($(TARGET_BUILD_TYPE),debug)
name := $(name)_debug
endif
name := $(name)-ota-retrofit-$(FILE_NAME_TAG)
INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
ifeq ($(AB_OTA_UPDATER),true)
$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): $(BRILLO_UPDATE_PAYLOAD)
else
$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): $(BROTLI)
endif
$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) \
build/make/tools/releasetools/ota_from_target_files
@echo "Package OTA (retrofit dynamic partitions): $@"
$(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --retrofit_dynamic_partitions)
.PHONY: otardppackage
otapackage otardppackage: $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET)
endif # BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE
endif # build_ota_package
ifeq ($(AB_OTA_UPDATER),true)
@# When using the A/B updater, include the updater config files in the zip.
$(hide) cp $(TOPDIR)system/update_engine/update_engine.conf $(zip_root)/META/update_engine_config.txt
$(hide) for part in $(AB_OTA_PARTITIONS); do \
echo "$${part}" >> $(zip_root)/META/ab_partitions.txt; \
done
$(hide) for conf in $(AB_OTA_POSTINSTALL_CONFIG); do \
echo "$${conf}" >> $(zip_root)/META/postinstall_config.txt; \
done
@# Include the build type in META/misc_info.txt so the server can easily differentiate production builds.
$(hide) echo "build_type=$(TARGET_BUILD_VARIANT)" >> $(zip_root)/META/misc_info.txt
$(hide) echo "ab_update=true" >> $(zip_root)/META/misc_info.txt
ifdef OSRELEASED_DIRECTORY
$(hide) cp $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_id $(zip_root)/META/product_id.txt
$(hide) cp $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_version $(zip_root)/META/product_version.txt
$(hide) cp $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/system_version $(zip_root)/META/system_version.txt
endif
endif
boardConfig.mk
AB_OTA_PARTITIONS ?= boot vendor dtbo vbmeta recovery $(OTA_PARTITIONS)
OTA_PARTITIONS所需升级的cp侧镜像,注意名称的修改。
2.制作差分包命令
./build/make/tools/releasetools/ota_from_target_files -i PREVIOUS-tardis-target_files.zip dist_output/tardis-target_files.zip incremental_ota_update.zip
adb root
adb push update.zip /data/update.zip
adb shell mkdir /cache/recovery (If this dir does not exist on the device)
create a file called "command" as follows:
echo "--update_package=/data/update.zip" > command
and push this file (adb push) to /cache/recovery/
adb shell sync
adb reboot recovery
3.调用installPackage进行升级
(1)重启前
根据注释了解:此函数功能为加密差分包并重启进入recovery。
frameworks/base/core/java/android/os/RecoverySystem.java
/**
* If the package hasn't been processed (i.e. uncrypt'd), set up
* UNCRYPT_PACKAGE_FILE and delete BLOCK_MAP_FILE to trigger uncrypt during the
* reboot.
*
* @param context the Context to use
* @param packageFile the update package to install. Must be on a
* partition mountable by recovery.
* @param processed if the package has been processed (uncrypt'd).
*
* @throws IOException if writing the recovery command file fails, or if
* the reboot itself fails.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.RECOVERY)
public static void installPackage(Context context, File packageFile, boolean processed)
throws IOException {
synchronized (sRequestLock) {
LOG_FILE.delete();
// Must delete the file in case it was created by system server.
UNCRYPT_PACKAGE_FILE.delete();
String filename = packageFile.getCanonicalPath();
Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
// If the package name ends with "_s.zip", it's a security update.
boolean securityUpdate = filename.endsWith("_s.zip");
// If the package is on the /data partition, the package needs to
// be processed (i.e. uncrypt'd). The caller specifies if that has
// been done in 'processed' parameter.
if (filename.startsWith("/data/")) {
if (processed) {
if (!BLOCK_MAP_FILE.exists()) {
Log.e(TAG, "Package claimed to have been processed but failed to find "
+ "the block map file.");
throw new IOException("Failed to find block map file");
}
} else {
FileWriter uncryptFile = new FileWriter(UNCRYPT_PACKAGE_FILE);
try {
uncryptFile.write(filename + "\n");
} finally {
uncryptFile.close();
}
// UNCRYPT_PACKAGE_FILE needs to be readable and writable
// by system server.
if (!UNCRYPT_PACKAGE_FILE.setReadable(true, false)
|| !UNCRYPT_PACKAGE_FILE.setWritable(true, false)) {
Log.e(TAG, "Error setting permission for " + UNCRYPT_PACKAGE_FILE);
}
BLOCK_MAP_FILE.delete();
}
// If the package is on the /data partition, use the block map
// file as the package name instead.
filename = "@/cache/recovery/block.map";
}
final String filenameArg = "--update_package=" + filename + "\n";
final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() + "\n";
final String securityArg = "--security\n";
String command = filenameArg + localeArg;
if (securityUpdate) {
command += securityArg;
}
RecoverySystem rs = (RecoverySystem) context.getSystemService(
Context.RECOVERY_SERVICE);
if (!rs.setupBcb(command)) {
throw new IOException("Setup BCB failed");
}
// Having set up the BCB (bootloader control block), go ahead and reboot
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
String reason = PowerManager.REBOOT_RECOVERY_UPDATE;
// On TV, reboot quiescently if the screen is off
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (wm.getDefaultDisplay().getState() != Display.STATE_ON) {
reason += ",quiescent";
}
}
pm.reboot(reason);
throw new IOException("Reboot failed (no permissions?)");
}
}
检查data目录存在指定文件,生成filename = "@/cache/recovery/block.map";然后setupBcb,设置成功后reboot(PowerManager.REBOOT_RECOVERY_UPDATE)。
(2)重启后
此处为UEFI结构的bootloader
bootable/bootloader/edk2/QcomModulePkg/Application/LinuxLoader/LinuxLoader.inf LinuxLoader.c
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = LinuxLoader
FILE_GUID = f536d559-459f-48fa-8bbc-43b554ecae8d
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 0.1
ENTRY_POINT = LinuxLoaderEntry
/**
Linux Loader Application EntryPoint
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The entry point is executed successfully.
@retval other Some error occurs when executing this entry point.
**/
EFI_STATUS EFIAPI __attribute__ ( (no_sanitize ("safe-stack")))
LinuxLoaderEntry (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
EFI_STATUS Status;
UINT32 BootReason = NORMAL_MODE;
UINT32 KeyPressed = SCAN_NULL;
/* MultiSlot Boot */
BOOLEAN MultiSlotBoot;
DEBUG ((EFI_D_INFO, "Loader Build Info: %a %a\n", __DATE__, __TIME__));
DEBUG ((EFI_D_VERBOSE, "LinuxLoader Load Address to debug ABL: 0x%llx\n",
(UINTN)LinuxLoaderEntry & (~ (0xFFF))));
DEBUG ((EFI_D_VERBOSE, "LinuxLoaderEntry Address: 0x%llx\n",
(UINTN)LinuxLoaderEntry));
Status = AllocateUnSafeStackPtr ();
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Unable to Allocate memory for Unsafe Stack: %r\n",
Status));
goto stack_guard_update_default;
}
StackGuardChkSetup ();
BootStatsSetTimeStamp (BS_BL_START);
// Initialize verified boot & Read Device Info
Status = DeviceInfoInit ();
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Initialize the device info failed: %r\n", Status));
goto stack_guard_update_default;
}
Status = EnumeratePartitions ();
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "LinuxLoader: Could not enumerate partitions: %r\n",
Status));
goto stack_guard_update_default;
}
UpdatePartitionEntries ();
/*Check for multislot boot support*/
MultiSlotBoot = PartitionHasMultiSlot ((CONST CHAR16 *)L"boot");
if (MultiSlotBoot) {
DEBUG ((EFI_D_VERBOSE, "Multi Slot boot is supported\n"));
FindPtnActiveSlot ();
}
Status = GetKeyPress (&KeyPressed);
if (Status == EFI_SUCCESS) {
if (KeyPressed == SCAN_DOWN)
BootIntoFastboot = TRUE;
if (KeyPressed == SCAN_UP)
BootIntoRecovery = TRUE;
if (KeyPressed == SCAN_ESC)
RebootDevice (EMERGENCY_DLOAD);
} else if (Status == EFI_DEVICE_ERROR) {
DEBUG ((EFI_D_ERROR, "Error reading key status: %r\n", Status));
goto stack_guard_update_default;
}
// check for reboot mode
Status = GetRebootReason (&BootReason);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Failed to get Reboot reason: %r\n", Status));
goto stack_guard_update_default;
}
switch (BootReason) {
case FASTBOOT_MODE:
BootIntoFastboot = TRUE;
break;
case RECOVERY_MODE:
BootIntoRecovery = TRUE;
break;
case ALARM_BOOT:
BootReasonAlarm = TRUE;
break;
case DM_VERITY_ENFORCING:
// write to device info
Status = EnableEnforcingMode (TRUE);
if (Status != EFI_SUCCESS)
goto stack_guard_update_default;
break;
case DM_VERITY_LOGGING:
/* Disable MDTP if it's Enabled through Local Deactivation */
Status = MdtpDisable ();
if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) {
DEBUG ((EFI_D_ERROR, "MdtpDisable Returned error: %r\n", Status));
goto stack_guard_update_default;
}
// write to device info
Status = EnableEnforcingMode (FALSE);
if (Status != EFI_SUCCESS)
goto stack_guard_update_default;
break;
case DM_VERITY_KEYSCLEAR:
Status = ResetDeviceState ();
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB Reset Device State error: %r\n", Status));
goto stack_guard_update_default;
}
break;
default:
if (BootReason != NORMAL_MODE) {
DEBUG ((EFI_D_ERROR,
"Boot reason: 0x%x not handled, defaulting to Normal Boot\n",
BootReason));
}
break;
}
Status = RecoveryInit (&BootIntoRecovery);
if (Status != EFI_SUCCESS)
DEBUG ((EFI_D_VERBOSE, "RecoveryInit failed ignore: %r\n", Status));
/* Populate board data required for fastboot, dtb selection and cmd line */
Status = BoardInit ();
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Error finding board information: %r\n", Status));
return Status;
}
DEBUG ((EFI_D_INFO, "KeyPress:%u, BootReason:%u\n", KeyPressed, BootReason));
DEBUG ((EFI_D_INFO, "Fastboot=%d, Recovery:%d\n",
BootIntoFastboot, BootIntoRecovery));
if (!GetVmData ()) {
DEBUG ((EFI_D_ERROR, "VM Hyp calls not present\n"));
}
if (!BootIntoFastboot) {
BootInfo Info = {0};
Info.MultiSlotBoot = MultiSlotBoot;
Info.BootIntoRecovery = BootIntoRecovery;
Info.BootReasonAlarm = BootReasonAlarm;
Status = LoadImageAndAuth (&Info);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "LoadImageAndAuth failed: %r\n", Status));
goto fastboot;
}
BootLinux (&Info);
}
fastboot:
DEBUG ((EFI_D_INFO, "Launching fastboot\n"));
Status = FastbootInitialize ();
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to Launch Fastboot App: %d\n", Status));
goto stack_guard_update_default;
}
stack_guard_update_default:
/*Update stack check guard with defualt value then return*/
__stack_chk_guard = DEFAULT_STACK_CHK_GUARD;
return Status;
}
读取重启原因,Status = RecoveryInit (&BootIntoRecovery);然后BootLinux (&Info);
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.
android::base::InitLogging(argv, &UiLogger);
// Take last pmsg contents and rewrite it to the current pmsg session.
static constexpr const char filter[] = "recovery/";
// Do we need to rotate?
bool do_rotate = false;
__android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logbasename, &do_rotate);
// Take action to refresh pmsg contents
__android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &do_rotate);
time_t start = time(nullptr);
// redirect_stdio should be called only in non-sideload mode. Otherwise we may have two logger
// instances with different timestamps.
redirect_stdio(Paths::Get().temporary_log_file().c_str());
load_volume_table();
has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;
std::vector<std::string> args = get_args(argc, 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;
int option_index;
while ((arg = getopt_long(args_to_parse.size() - 1, args_to_parse.data(), "", OPTIONS,
&option_index)) != -1) {
switch (arg) {
case 't':
show_text = true;
break;
case 0: {
std::string option = OPTIONS[option_index].name;
if (option == "locale") {
locale = optarg;
} else if (option == "fastboot" &&
android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
fastboot = true;
}
break;
}
}
}
optind = 1;
if (locale.empty()) {
if (has_cache) {
locale = load_locale_from_cache();
}
if (locale.empty()) {
static constexpr const char* DEFAULT_LOCALE = "en-US";
locale = DEFAULT_LOCALE;
}
}
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
// in on dlclose).
void* librecovery_ui_ext = dlopen(kDefaultLibRecoveryUIExt, RTLD_NOW);
using MakeDeviceType = decltype(&make_device);
MakeDeviceType make_device_func = nullptr;
if (librecovery_ui_ext == nullptr) {
printf("Failed to dlopen %s: %s\n", kDefaultLibRecoveryUIExt, dlerror());
} else {
reinterpret_cast<void*&>(make_device_func) = dlsym(librecovery_ui_ext, "make_device");
if (make_device_func == nullptr) {
printf("Failed to dlsym make_device: %s\n", dlerror());
}
}
Device* device;
if (make_device_func == nullptr) {
printf("Falling back to the default make_device() instead\n");
device = make_device();
} else {
printf("Loading make_device from %s\n", kDefaultLibRecoveryUIExt);
device = (*make_device_func)();
}
if (android::base::GetBoolProperty("ro.boot.quiescent", false)) {
printf("Quiescent recovery mode.\n");
device->ResetUI(new StubRecoveryUI());
} else {
if (!device->GetUI()->Init(locale)) {
printf("Failed to initialize UI; using stub UI instead.\n");
device->ResetUI(new StubRecoveryUI());
}
}
ui = device->GetUI();
if (!has_cache) {
device->RemoveMenuItemForAction(Device::WIPE_CACHE);
}
if (!android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
device->RemoveMenuItemForAction(Device::ENTER_FASTBOOT);
}
if (!is_ro_debuggable()) {
device->RemoveMenuItemForAction(Device::ENTER_RESCUE);
}
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");
}
SetLoggingSehandle(sehandle);
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;
}
}
ui->SetEnableFastbootdLogo(fastboot);
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");
// TODO: Move all the reboots to reboot(), which should conditionally set quiescent flag.
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::REBOOT_FASTBOOT:
ui->Print("Rebooting to recovery/fastboot...\n");
android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
break;
case Device::REBOOT_RECOVERY:
ui->Print("Rebooting to recovery...\n");
reboot("reboot,recovery");
break;
case Device::REBOOT_RESCUE: {
// Not using `reboot("reboot,rescue")`, as it requires matching support in kernel and/or
// bootloader.
bootloader_message boot = {};
strlcpy(boot.command, "boot-rescue", sizeof(boot.command));
std::string err;
if (!write_bootloader_message(boot, &err)) {
LOG(ERROR) << "Failed to write bootloader message: " << err;
// Stay under recovery on failure.
continue;
}
ui->Print("Rebooting to recovery/rescue...\n");
reboot("reboot,recovery");
break;
}
case Device::ENTER_FASTBOOT:
if (logical_partitions_mapped()) {
ui->Print("Partitions may be mounted - rebooting to enter fastboot.");
android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
} else {
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;
}
主要函数:
std::vector<std::string> args = get_args(argc, argv);//获取/cache/recovery/command
auto ret = fastboot ? StartFastboot(device, args) : start_recovery(device, args);//启动recovery
reboot("reboot,");
1)get_args
// command line args come from, in decreasing precedence:
// - the actual command line
// - the bootloader control block (one per line, after "recovery")
// - the contents of COMMAND_FILE (one per line)
static std::vector<std::string> get_args(const int argc, char** const argv) {
CHECK_GT(argc, 0);
bootloader_message boot = {};
std::string err;
if (!read_bootloader_message(&boot, &err)) {
LOG(ERROR) << err;
// If fails, leave a zeroed bootloader_message.
boot = {};
}
stage = std::string(boot.stage);
std::string boot_command;
if (boot.command[0] != 0) {
if (memchr(boot.command, '\0', sizeof(boot.command))) {
boot_command = std::string(boot.command);
} else {
boot_command = std::string(boot.command, sizeof(boot.command));
}
LOG(INFO) << "Boot command: " << boot_command;
}
if (boot.status[0] != 0) {
std::string boot_status = std::string(boot.status, sizeof(boot.status));
LOG(INFO) << "Boot status: " << boot_status;
}
std::vector<std::string> args(argv, argv + argc);
// --- if arguments weren't supplied, look in the bootloader control block
if (args.size() == 1) {
boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
std::string boot_recovery(boot.recovery);
std::vector<std::string> tokens = android::base::Split(boot_recovery, "\n");
if (!tokens.empty() && tokens[0] == "recovery") {
for (auto it = tokens.begin() + 1; it != tokens.end(); it++) {
// Skip empty and '\0'-filled tokens.
if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
}
LOG(INFO) << "Got " << args.size() << " arguments from boot message";
} else if (boot.recovery[0] != 0) {
LOG(ERROR) << "Bad boot message: \"" << boot_recovery << "\"";
}
}
// --- if that doesn't work, try the command file (if we have /cache).
if (args.size() == 1 && has_cache) {
std::string content;
if (ensure_path_mounted(COMMAND_FILE) == 0 &&
android::base::ReadFileToString(COMMAND_FILE, &content)) {
std::vector<std::string> tokens = android::base::Split(content, "\n");
// All the arguments in COMMAND_FILE are needed (unlike the BCB message,
// COMMAND_FILE doesn't use filename as the first argument).
for (auto it = tokens.begin(); it != tokens.end(); it++) {
// Skip empty and '\0'-filled tokens.
if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
}
LOG(INFO) << "Got " << args.size() << " arguments from " << COMMAND_FILE;
}
}
// Write the arguments (excluding the filename in args[0]) back into the
// bootloader control block. So the device will always boot into recovery to
// finish the pending work, until finish_recovery() is called.
std::vector<std::string> options(args.cbegin() + 1, args.cend());
if (!update_bootloader_message(options, &err)) {
LOG(ERROR) << "Failed to set BCB message: " << err;
}
// Finally, if no arguments were specified, check whether we should boot
// into fastboot or rescue mode.
if (args.size() == 1 && boot_command == "boot-fastboot") {
args.emplace_back("--fastboot");
} else if (args.size() == 1 && boot_command == "boot-rescue") {
args.emplace_back("--rescue");
}
return args;
}
(1)if (!read_bootloader_message(&boot, &err)) { //读取BCB内容
(2)读取/cache/recovery/command内容
if (ensure_path_mounted(COMMAND_FILE) == 0 &&
android::base::ReadFileToString(COMMAND_FILE, &content)) {
std::vector<std::string> tokens = android::base::Split(content, "\n");
// All the arguments in COMMAND_FILE are needed (unlike the BCB message,
// COMMAND_FILE doesn't use filename as the first argument).
for (auto it = tokens.begin(); it != tokens.end(); it++) {
// Skip empty and '\0'-filled tokens.
if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
}
LOG(INFO) << "Got " << args.size() << " arguments from " << COMMAND_FILE;
}
(3)update_bootloader_message()
2)start_recovery
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 },
{ "prompt_and_wipe_data", no_argument, nullptr, 0 },
{ "reason", required_argument, nullptr, 0 },
{ "rescue", no_argument, nullptr, 0 },
{ "retry_count", required_argument, nullptr, 0 },
{ "security", no_argument, nullptr, 0 },
{ "show_text", no_argument, nullptr, 't' },
{ "shutdown_after", no_argument, nullptr, 0 },
{ "sideload", no_argument, nullptr, 0 },
{ "sideload_auto_reboot", no_argument, nullptr, 0 },
{ "update_package", required_argument, nullptr, 0 },
{ "wipe_ab", no_argument, nullptr, 0 },
{ "wipe_cache", no_argument, nullptr, 0 },
{ "wipe_data", no_argument, nullptr, 0 },
{ "wipe_package_size", required_argument, nullptr, 0 },
{ nullptr, 0, nullptr, 0 },
};
const char* update_package = nullptr;
bool should_wipe_data = false;
bool should_prompt_and_wipe_data = false;
bool should_wipe_cache = false;
bool should_wipe_ab = false;
size_t wipe_package_size = 0;
bool sideload = false;
bool sideload_auto_reboot = false;
bool rescue = false;
bool just_exit = false;
bool shutdown_after = false;
bool fsck_unshare_blocks = false;
int retry_count = 0;
bool security_update = false;
std::string locale;
auto args_to_parse = StringVectorToNullTerminatedArray(args);
int status = INSTALL_SUCCESS;
// next_action indicates the next target to reboot into upon finishing the install. It could be
// overridden to a different reboot target per user request.
Device::BuiltinAction next_action = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
if (has_cache && ensure_path_mounted(CACHE_ROOT) == 0) {
//Create /cache/recovery specifically if it is not created
//As in cases where device is booted into recovery directly after
//flashing recovery folder is not created in init
mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle);
}
int arg;
int option_index;
// Parse everything before the last element (which must be a nullptr). getopt_long(3) expects a
// null-terminated char* array, but without counting null as an arg (i.e. argv[argc] should be
// nullptr).
while ((arg = getopt_long(args_to_parse.size() - 1, args_to_parse.data(), "", OPTIONS,
&option_index)) != -1) {
switch (arg) {
case 't':
// Handled in recovery_main.cpp
break;
case 'x':
just_exit = true;
break;
case 0: {
std::string option = OPTIONS[option_index].name;
if (option == "fsck_unshare_blocks") {
fsck_unshare_blocks = true;
} else if (option == "locale" || option == "fastboot") {
// Handled in recovery_main.cpp
} else if (option == "prompt_and_wipe_data") {
should_prompt_and_wipe_data = true;
} else if (option == "reason") {
reason = optarg;
} else if (option == "rescue") {
rescue = true;
} else if (option == "retry_count") {
android::base::ParseInt(optarg, &retry_count, 0);
} else if (option == "security") {
security_update = true;
} else if (option == "sideload") {
sideload = true;
} else if (option == "sideload_auto_reboot") {
sideload = true;
sideload_auto_reboot = true;
} else if (option == "shutdown_after") {
shutdown_after = true;
} else if (option == "update_package") {
update_package = optarg;
} else if (option == "wipe_ab") {
should_wipe_ab = true;
} else if (option == "wipe_cache") {
should_wipe_cache = true;
} else if (option == "wipe_data") {
should_wipe_data = true;
} else if (option == "wipe_package_size") {
android::base::ParseUint(optarg, &wipe_package_size);
}
break;
}
case '?':
LOG(ERROR) << "Invalid command argument";
continue;
}
}
optind = 1;
printf("stage is [%s]\n", stage.c_str());
printf("reason is [%s]\n", reason);
// Set background string to "installing security update" for security update,
// otherwise set it to "installing system update".
ui->SetSystemUpdateText(security_update);
int st_cur, st_max;
if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) {
ui->SetStage(st_cur, st_max);
}
std::vector<std::string> title_lines =
android::base::Split(android::base::GetProperty("ro.bootimage.build.fingerprint", ""), ":");
title_lines.insert(std::begin(title_lines), "Android Recovery");
ui->SetTitle(title_lines);
ui->ResetKeyInterruptStatus();
device->StartRecovery();
printf("Command:");
for (const auto& arg : args) {
printf(" \"%s\"", arg.c_str());
}
printf("\n\n");
if (update_package) {
if (!strncmp("/sdcard", update_package, 7)) {
//If this is a UFS device lets mount the sdcard ourselves.Depending
//on if the device is UFS or EMMC based the path to the sdcard
//device changes so we cannot rely on the block dev path from
//recovery.fstab file
if (is_ufs_dev()) {
if(do_sdcard_mount_for_ufs() != 0) {
status = INSTALL_ERROR;
goto error;
}
} else {
ui->Print("Update via sdcard on EMMC dev. Using path from fstab\n");
}
}
}
property_list(print_property, nullptr);
printf("\n");
ui->Print("Supported API: %d\n", kRecoveryApiVersion);
if (update_package != nullptr) {
// It's not entirely true that we will modify the flash. But we want
// to log the update attempt since update_package is non-NULL.
save_current_log = true;
int required_battery_level;
if (retry_count == 0 && !is_battery_ok(&required_battery_level)) {
ui->Print("battery capacity is not enough for installing package: %d%% needed\n",
required_battery_level);
// Log the error code to last_install when installation skips due to
// low battery.
log_failure_code(kLowBattery, update_package);
status = INSTALL_SKIPPED;
} else if (retry_count == 0 && bootreason_in_blacklist()) {
// Skip update-on-reboot when bootreason is kernel_panic or similar
ui->Print("bootreason is in the blacklist; skip OTA installation\n");
log_failure_code(kBootreasonInBlacklist, update_package);
status = INSTALL_SKIPPED;
} else {
// It's a fresh update. Initialize the retry_count in the BCB to 1; therefore we can later
// identify the interrupted update due to unexpected reboots.
if (retry_count == 0) {
set_retry_bootloader_message(retry_count + 1, args);
}
status = install_package(update_package, should_wipe_cache, true, retry_count, ui);
if (status != INSTALL_SUCCESS) {
ui->Print("Installation aborted.\n");
// When I/O error or bspatch/imgpatch error happens, reboot and retry installation
// RETRY_LIMIT times before we abandon this OTA update.
static constexpr int RETRY_LIMIT = 4;
if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) {
copy_logs(save_current_log, has_cache, sehandle);
retry_count += 1;
set_retry_bootloader_message(retry_count, args);
// Print retry count on screen.
ui->Print("Retry attempt %d\n", retry_count);
// Reboot and retry the update
if (!reboot("reboot,recovery")) {
ui->Print("Reboot failed\n");
} else {
while (true) {
pause();
}
}
}
// If this is an eng or userdebug build, then automatically
// turn the text display on if the script fails so the error
// message is visible.
if (is_ro_debuggable()) {
ui->ShowText(true);
}
}
}
} else if (should_wipe_data) {
save_current_log = true;
bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
if (!WipeData(device, convert_fbe)) {
status = INSTALL_ERROR;
}
} else if (should_prompt_and_wipe_data) {
// Trigger the logging to capture the cause, even if user chooses to not wipe data.
save_current_log = true;
ui->ShowText(true);
ui->SetBackground(RecoveryUI::ERROR);
status = prompt_and_wipe_data(device);
if (status != INSTALL_KEY_INTERRUPTED) {
ui->ShowText(false);
}
} else if (should_wipe_cache) {
save_current_log = true;
if (!WipeCache(ui, nullptr)) {
status = INSTALL_ERROR;
}
} else if (should_wipe_ab) {
if (!wipe_ab_device(wipe_package_size)) {
status = INSTALL_ERROR;
}
} else if (sideload) {
// 'adb reboot sideload' acts the same as user presses key combinations to enter the sideload
// mode. When 'sideload-auto-reboot' is used, text display will NOT be turned on by default. And
// it will reboot after sideload finishes even if there are errors. This is to enable automated
// testing.
save_current_log = true;
if (!sideload_auto_reboot) {
ui->ShowText(true);
}
status = ApplyFromAdb(device, false /* rescue_mode */, &next_action);
ui->Print("\nInstall from ADB complete (status: %d).\n", status);
if (sideload_auto_reboot) {
status = INSTALL_REBOOT;
ui->Print("Rebooting automatically.\n");
}
} else if (rescue) {
save_current_log = true;
status = ApplyFromAdb(device, true /* rescue_mode */, &next_action);
ui->Print("\nInstall from ADB complete (status: %d).\n", status);
} else if (fsck_unshare_blocks) {
if (!do_fsck_unshare_blocks()) {
status = INSTALL_ERROR;
}
} else if (!just_exit) {
// If this is an eng or userdebug build, automatically turn on the text display if no command
// is specified. Note that this should be called before setting the background to avoid
// flickering the background image.
if (is_ro_debuggable()) {
ui->ShowText(true);
}
status = INSTALL_NONE; // No command specified
ui->SetBackground(RecoveryUI::NO_COMMAND);
}
error:
if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
ui->SetBackground(RecoveryUI::ERROR);
if (!ui->IsTextVisible()) {
sleep(5);
}
}
// Determine the next action.
// - If the state is INSTALL_REBOOT, device will reboot into the target as specified in
// `next_action`.
// - If the recovery menu is visible, prompt and wait for commands.
// - If the state is INSTALL_NONE, wait for commands (e.g. in user build, one manually boots
// into recovery to sideload a package or to wipe the device).
// - In all other cases, reboot the device. Therefore, normal users will observe the device
// rebooting a) immediately upon successful finish (INSTALL_SUCCESS); or b) an "error" screen
// for 5s followed by an automatic reboot.
if (status != INSTALL_REBOOT) {
if (status == INSTALL_NONE || ui->IsTextVisible()) {
Device::BuiltinAction temp = prompt_and_wait(device, status);
if (temp != Device::NO_ACTION) {
next_action = temp;
}
}
}
// Save logs and clean up before rebooting or shutting down.
finish_recovery();
return next_action;
}
(1)status = install_package(update_package, should_wipe_cache, true, retry_count, ui);//安装
(2)finish_recovery();
(3)install_package
int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount,
int retry_count, RecoveryUI* ui) {
CHECK(!path.empty());
auto start = std::chrono::system_clock::now();
int start_temperature = GetMaxValueFromThermalZone();
int max_temperature = start_temperature;
int result = 0;
std::vector<std::string> log_buffer;
if (needs_mount == true)
result = setup_install_mounts();
if (result != 0 ) {
LOG(ERROR) << "failed to set up expected mounts for install; aborting";
result = INSTALL_ERROR;
} else {
bool updater_wipe_cache = false;
result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,
retry_count, &max_temperature, ui);
should_wipe_cache = should_wipe_cache || updater_wipe_cache;
}
// Measure the time spent to apply OTA update in seconds.
std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
int time_total = static_cast<int>(duration.count());
bool has_cache = volume_for_mount_point("/cache") != nullptr;
// Skip logging the uncrypt_status on devices without /cache.
if (has_cache) {
static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS;
} else {
std::string uncrypt_status;
if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
PLOG(WARNING) << "failed to read uncrypt status";
} else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
} else {
log_buffer.push_back(android::base::Trim(uncrypt_status));
}
}
}
// The first two lines need to be the package name and install result.
std::vector<std::string> log_header = {
path,
result == INSTALL_SUCCESS ? "1" : "0",
"time_total: " + std::to_string(time_total),
"retry: " + std::to_string(retry_count),
};
int end_temperature = GetMaxValueFromThermalZone();
max_temperature = std::max(end_temperature, max_temperature);
if (start_temperature > 0) {
log_buffer.push_back("temperature_start: " + std::to_string(start_temperature));
}
if (end_temperature > 0) {
log_buffer.push_back("temperature_end: " + std::to_string(end_temperature));
}
if (max_temperature > 0) {
log_buffer.push_back("temperature_max: " + std::to_string(max_temperature));
}
std::string log_content =
android::base::Join(log_header, "\n") + "\n" + android::base::Join(log_buffer, "\n") + "\n";
const std::string& install_file = Paths::Get().temporary_install_file();
if (!android::base::WriteStringToFile(log_content, install_file)) {
PLOG(ERROR) << "failed to write " << install_file;
}
// Write a copy into last_log.
LOG(INFO) << log_content;
if (result == INSTALL_SUCCESS && should_wipe_cache) {
if (!WipeCache(ui, nullptr)) {
result = INSTALL_ERROR;
}
}
return result;
}
(1)result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,retry_count, &max_temperature, ui); //really_install_package
(2)ensure_path_mounted(UNCRYPT_STATUS)
(4)really_install_package
static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,
std::vector<std::string>* log_buffer, int retry_count,
int* max_temperature, RecoveryUI* ui) {
ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
ui->Print("Finding update package...\n");
// Give verification half the progress bar...
ui->SetProgressType(RecoveryUI::DETERMINATE);
ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
LOG(INFO) << "Update location: " << path;
// Map the update package into memory.
ui->Print("Opening update package...\n");
if (needs_mount) {
if (path[0] == '@') {
ensure_path_mounted(path.substr(1));
} else {
ensure_path_mounted(path);
}
}
auto package = Package::CreateMemoryPackage(
path, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
if (!package) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
return INSTALL_CORRUPT;
}
// Verify package.
if (!verify_package(package.get(), ui)) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
return INSTALL_CORRUPT;
}
// Try to open the package.
ZipArchiveHandle zip = package->GetZipArchiveHandle();
if (!zip) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
return INSTALL_CORRUPT;
}
// Additionally verify the compatibility of the package if it's a fresh install.
if (retry_count == 0 && !verify_package_compatibility(zip)) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
return INSTALL_CORRUPT;
}
// Verify and install the contents of the package.
ui->Print("Installing update...\n");
if (retry_count > 0) {
ui->Print("Retry attempt: %d\n", retry_count);
}
ui->SetEnableReboot(false);
int result =
try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui);
ui->SetEnableReboot(true);
ui->Print("\n");
return result;
}
(1)CreateMemoryPackage
(2)verify_package
(3)verify_package_compatibility
(4)try_update_binary
(5)SetEnableReboot(true)
5)verify_package
bool verify_package(Package* package, RecoveryUI* ui) {
static constexpr const char* CERTIFICATE_ZIP_FILE = "/system/etc/security/otacerts.zip";
std::vector<Certificate> loaded_keys = LoadKeysFromZipfile(CERTIFICATE_ZIP_FILE);
if (loaded_keys.empty()) {
LOG(ERROR) << "Failed to load keys";
return false;
}
LOG(INFO) << loaded_keys.size() << " key(s) loaded from " << CERTIFICATE_ZIP_FILE;
// Verify package.
ui->Print("Verifying update package...\n");
auto t0 = std::chrono::system_clock::now();
int err = verify_file(package, loaded_keys);
std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
if (err != VERIFY_SUCCESS) {
LOG(ERROR) << "Signature verification failed";
LOG(ERROR) << "error: " << kZipVerificationFailure;
return false;
}
return true;
}
6)verify_package_compatibility
// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
// entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA
// package.
bool verify_package_compatibility(ZipArchiveHandle package_zip) {
LOG(INFO) << "Verifying package compatibility...";
static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip";
ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY);
ZipEntry compatibility_entry;
if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) {
LOG(INFO) << "Package doesn't contain " << COMPATIBILITY_ZIP_ENTRY << " entry";
return true;
}
std::string zip_content(compatibility_entry.uncompressed_length, '\0');
int32_t ret;
if ((ret = ExtractToMemory(package_zip, &compatibility_entry,
reinterpret_cast<uint8_t*>(&zip_content[0]),
compatibility_entry.uncompressed_length)) != 0) {
LOG(ERROR) << "Failed to read " << COMPATIBILITY_ZIP_ENTRY << ": " << ErrorCodeString(ret);
return false;
}
ZipArchiveHandle zip_handle;
ret = OpenArchiveFromMemory(static_cast<void*>(const_cast<char*>(zip_content.data())),
zip_content.size(), COMPATIBILITY_ZIP_ENTRY, &zip_handle);
if (ret != 0) {
LOG(ERROR) << "Failed to OpenArchiveFromMemory: " << ErrorCodeString(ret);
return false;
}
// Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents.
void* cookie;
ret = StartIteration(zip_handle, &cookie, nullptr, nullptr);
if (ret != 0) {
LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret);
CloseArchive(zip_handle);
return false;
}
std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
std::vector<std::string> compatibility_info;
ZipEntry info_entry;
ZipString info_name;
while (Next(cookie, &info_entry, &info_name) == 0) {
std::string content(info_entry.uncompressed_length, '\0');
int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast<uint8_t*>(&content[0]),
info_entry.uncompressed_length);
if (ret != 0) {
LOG(ERROR) << "Failed to read " << info_name.name << ": " << ErrorCodeString(ret);
CloseArchive(zip_handle);
return false;
}
compatibility_info.emplace_back(std::move(content));
}
CloseArchive(zip_handle);
// VintfObjectRecovery::CheckCompatibility returns zero on success.
std::string err;
int result = android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err);
if (result == 0) {
return true;
}
LOG(ERROR) << "Failed to verify package compatibility (result " << result << "): " << err;
return false;
}
7)try_update_binary
// If the package contains an update binary, extract it and run it.
static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
std::vector<std::string>* log_buffer, int retry_count,
int* max_temperature, RecoveryUI* ui) {
std::map<std::string, std::string> metadata;
if (!ReadMetadataFromPackage(zip, &metadata)) {
LOG(ERROR) << "Failed to parse metadata in the zip file";
return INSTALL_CORRUPT;
}
bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
// Verifies against the metadata in the package first.
if (int check_status = is_ab ? CheckPackageMetadata(metadata, OtaType::AB) : 0;
check_status != 0) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
return check_status;
}
ReadSourceTargetBuild(metadata, log_buffer);
// The updater in child process writes to the pipe to communicate with recovery.
android::base::unique_fd pipe_read, pipe_write;
// Explicitly disable O_CLOEXEC using 0 as the flags (last) parameter to Pipe
// so that the child updater process will recieve a non-closed fd.
if (!android::base::Pipe(&pipe_read, &pipe_write, 0)) {
PLOG(ERROR) << "Failed to create pipe for updater-recovery communication";
return INSTALL_CORRUPT;
}
// The updater-recovery communication protocol.
//
// progress <frac> <secs>
// fill up the next <frac> part of of the progress bar over <secs> seconds. If <secs> is
// zero, use `set_progress` commands to manually control the progress of this segment of the
// bar.
//
// set_progress <frac>
// <frac> should be between 0.0 and 1.0; sets the progress bar within the segment defined by
// the most recent progress command.
//
// ui_print <string>
// display <string> on the screen.
//
// wipe_cache
// a wipe of cache will be performed following a successful installation.
//
// clear_display
// turn off the text display.
//
// enable_reboot
// packages can explicitly request that they want the user to be able to reboot during
// installation (useful for debugging packages that don't exit).
//
// retry_update
// updater encounters some issue during the update. It requests a reboot to retry the same
// package automatically.
//
// log <string>
// updater requests logging the string (e.g. cause of the failure).
//
std::vector<std::string> args;
if (int update_status =
is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args)
: SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args);
update_status != 0) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
return update_status;
}
pid_t pid = fork();
if (pid == -1) {
PLOG(ERROR) << "Failed to fork update binary";
log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure));
return INSTALL_ERROR;
}
if (pid == 0) {
umask(022);
pipe_read.reset();
// Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
auto chr_args = StringVectorToNullTerminatedArray(args);
execv(chr_args[0], chr_args.data());
// We shouldn't use LOG/PLOG in the forked process, since they may cause the child process to
// hang. This deadlock results from an improperly copied mutex in the ui functions.
// (Bug: 34769056)
fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
_exit(EXIT_FAILURE);
}
pipe_write.reset();
std::atomic<bool> logger_finished(false);
std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));
*wipe_cache = false;
bool retry_update = false;
char buffer[1024];
FILE* from_child = android::base::Fdopen(std::move(pipe_read), "r");
while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
std::string line(buffer);
size_t space = line.find_first_of(" \n");
std::string command(line.substr(0, space));
if (command.empty()) continue;
// Get rid of the leading and trailing space and/or newline.
std::string args = space == std::string::npos ? "" : android::base::Trim(line.substr(space));
if (command == "progress") {
std::vector<std::string> tokens = android::base::Split(args, " ");
double fraction;
int seconds;
if (tokens.size() == 2 && android::base::ParseDouble(tokens[0].c_str(), &fraction) &&
android::base::ParseInt(tokens[1], &seconds)) {
ui->ShowProgress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), seconds);
} else {
LOG(ERROR) << "invalid \"progress\" parameters: " << line;
}
} else if (command == "set_progress") {
std::vector<std::string> tokens = android::base::Split(args, " ");
double fraction;
if (tokens.size() == 1 && android::base::ParseDouble(tokens[0].c_str(), &fraction)) {
ui->SetProgress(fraction);
} else {
LOG(ERROR) << "invalid \"set_progress\" parameters: " << line;
}
} else if (command == "ui_print") {
ui->PrintOnScreenOnly("%s\n", args.c_str());
fflush(stdout);
} else if (command == "wipe_cache") {
*wipe_cache = true;
} else if (command == "clear_display") {
ui->SetBackground(RecoveryUI::NONE);
} else if (command == "enable_reboot") {
// packages can explicitly request that they want the user
// to be able to reboot during installation (useful for
// debugging packages that don't exit).
ui->SetEnableReboot(true);
} else if (command == "retry_update") {
retry_update = true;
} else if (command == "log") {
if (!args.empty()) {
// Save the logging request from updater and write to last_install later.
log_buffer->push_back(args);
} else {
LOG(ERROR) << "invalid \"log\" parameters: " << line;
}
} else {
LOG(ERROR) << "unknown command [" << command << "]";
}
}
fclose(from_child);
int status;
waitpid(pid, &status, 0);
logger_finished.store(true);
finish_log_temperature.notify_one();
temperature_logger.join();
if (retry_update) {
return INSTALL_RETRY;
}
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != EXIT_SUCCESS) {
LOG(ERROR) << "Error in " << package << " (status " << WEXITSTATUS(status) << ")";
return INSTALL_ERROR;
}
} else if (WIFSIGNALED(status)) {
LOG(ERROR) << "Error in " << package << " (killed by signal " << WTERMSIG(status) << ")";
return INSTALL_ERROR;
} else {
LOG(FATAL) << "Invalid status code " << status;
}
return INSTALL_SUCCESS;
}
(1)CheckPackageMetadata
(2)SetUpAbUpdateCommands
8)finish_recovery
// Clear the recovery command and prepare to boot a (hopefully working) system,
// copy our log file to cache as well (for the system to read). This function is
// idempotent: call it as many times as you like.
static void finish_recovery() {
std::string locale = ui->GetLocale();
// Save the locale to cache, so if recovery is next started up without a '--locale' argument
// (e.g., directly from the bootloader) it will use the last-known locale.
if (!locale.empty() && has_cache) {
LOG(INFO) << "Saving locale \"" << locale << "\"";
if (ensure_path_mounted(LOCALE_FILE) != 0) {
LOG(ERROR) << "Failed to mount " << LOCALE_FILE;
} else if (!android::base::WriteStringToFile(locale, LOCALE_FILE)) {
PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE;
}
}
copy_logs(save_current_log, has_cache, sehandle);
// Reset to normal system boot so recovery won't cycle indefinitely.
std::string err;
if (!clear_bootloader_message(&err)) {
LOG(ERROR) << "Failed to clear BCB message: " << err;
}
// Remove the command file, so recovery won't repeat indefinitely.
if (has_cache) {
if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
}
ensure_path_unmounted(CACHE_ROOT);
}
sync(); // For good measure.
}
ota升级重启后,主要的操作如下:
1.加载bootloader,读取bootloader中的command命令
2.读取到升级的命令后,boot recovery.img,recovery.cpp中的main函数执行。
3.执行install_package操作,这里会解析ota包中的内容(block.map的形式),执行相应的升级包中脚本操作,同时会同步进行一些ui的显示操作。
4.install_package操作完成后,最后finish_recovery,完成升级的操作。