安卓恢复出厂设置过程详解 您所在的位置:网站首页 安卓降级系统版本怎么恢复出厂设置 安卓恢复出厂设置过程详解

安卓恢复出厂设置过程详解

2024-07-03 07:42| 来源: 网络整理| 查看: 265

本文基于Android R。高通平台。

设置中点击“恢复出厂设置”即可清楚用户数据。查看代码发现其只是发送了一个广播而已。

Intent intent = new Intent(Intent.ACTION_FACTORY_RESET); intent.setPackage("android"); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_REASON, "CryptKeeper.showFactoryReset() corrupt=" + corrupt); sendBroadcast(intent); public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";

/framework/base/core/res/AndroidManifest.xml中静态注册了这个广播的接收者MasterClearReceiver。

查看MasterClearReceiver代码,onReceive接收到广播时,判断如果广播是Intent.ACTION_FACTORY_RESET,且factoryResetPackage不为空,就重新将广播发出去,return退出。从此可以看到MasterClearReceiver支持供应商重载“恢复出厂设置”操作,只需要将com.android.internal.R.string.config_factoryResetPackage值overlay就可以了。否则继续执行默认的恢厂操作。

final String factoryResetPackage = context .getString(com.android.internal.R.string.config_factoryResetPackage); if (Intent.ACTION_FACTORY_RESET.equals(intent.getAction()) && !TextUtils.isEmpty(factoryResetPackage)) { intent.setPackage(factoryResetPackage).setComponent(null); context.sendBroadcastAsUser(intent, UserHandle.SYSTEM); return; }

新启线程执行操作:

RecoverySystem.rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims);

RecoverySystem的rebootWipeUserData方法中调用bootCommand方法,传入参数"–wipe_data"和当前local参数(当前地区语言)、reasonArg、shutdownArg。

String shutdownArg = null; if (shutdown) { shutdownArg = "--shutdown_after"; } String reasonArg = null; if (!TextUtils.isEmpty(reason)) { String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString(); reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp); } final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);

最终会调用到RecoverySystemService的rebootRecoveryWithCommand方法。

@Override // Binder call public void rebootRecoveryWithCommand(String command) { if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]"); synchronized (sRequestLock) { if (!setupOrClearBcb(true, command)) { return; } // Having set up the BCB, go ahead and reboot. PowerManager pm = mInjector.getPowerManager(); pm.reboot(PowerManager.REBOOT_RECOVERY); } }

可以看到rebootRecoveryWithCommand方法只执行了两个操作:

setupOrClearBcb(true, command)pm.reboot(PowerManager.REBOOT_RECOVERY);

setupOrClearBcb将之前传递过来的参数写入BCB中,然后重启进入recovery模式中。

setupBCB private boolean setupOrClearBcb(boolean isSetup, String command) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); final boolean available = checkAndWaitForUncryptService(); if (!available) { Slog.e(TAG, "uncrypt service is unavailable."); return false; } if (isSetup) { mInjector.systemPropertiesSet("ctl.start", "setup-bcb"); } else { mInjector.systemPropertiesSet("ctl.start", "clear-bcb"); } // Connect to the uncrypt service socket. UncryptSocket socket = mInjector.connectService(); if (socket == null) { Slog.e(TAG, "Failed to connect to uncrypt socket"); return false; } try { // Send the BCB commands if it's to setup BCB. if (isSetup) { socket.sendCommand(command); } // Read the status from the socket. int status = socket.getPercentageUncrypted(); // Ack receipt of the status code. uncrypt waits for the ack so // the socket won't be destroyed before we receive the code. socket.sendAck(); if (status == 100) { Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") + " bcb successfully finished."); } else { // Error in /system/bin/uncrypt. Slog.e(TAG, "uncrypt failed with status: " + status); return false; } } catch (IOException e) { Slog.e(TAG, "IOException when communicating with uncrypt:", e); return false; } finally { socket.close(); } return true; } uncrypt

通过设置属性启动 setup-bcb 程序,通过socket与 uncrypt通信,将之前的参数发送给 uncrypt。(关于“ctl.start”属性启动程序的细节可以参考https://blog.csdn.net/u010223349/article/details/8876232)

setup-bcb,clear-bcb,uncrypt都是指同一程序,代码位置位于 /bootable/recovery/uncrypt/。

Android.bp:

cc_binary { name: "uncrypt", srcs: [ "uncrypt.cpp", ], cflags: [ "-Wall", "-Werror", ], shared_libs: [ "libbase", "libbootloader_message", "libcutils", "libfs_mgr", ], static_libs: [ "libotautil", ], init_rc: [ "uncrypt.rc", ], }

uncrypt.rc:

service uncrypt /system/bin/uncrypt class main socket uncrypt stream 600 system system disabled oneshot service setup-bcb /system/bin/uncrypt --setup-bcb class main socket uncrypt stream 600 system system disabled oneshot service clear-bcb /system/bin/uncrypt --clear-bcb class main socket uncrypt stream 600 system system disabled oneshot

uncrypt.cpp对程序启动的参数分别处理,当参数为“setup-bcb”时调用setup_bcb()方法。

static bool setup_bcb(const int socket) { // c5. receive message length int length; if (!android::base::ReadFully(socket, &length, 4)) { PLOG(ERROR) if (android::base::StartsWith(option, "--wipe_package=")) { std::string path = option.substr(strlen("--wipe_package=")); if (!android::base::ReadFileToString(path, &wipe_package)) { PLOG(ERROR) PLOG(ERROR) *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(), strerror(errno)); return false; } if (lseek(fd, static_cast(offset), SEEK_SET) != static_cast(offset)) { *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(), strerror(errno)); return false; } if (!android::base::WriteFully(fd, p, size)) { *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(), strerror(errno)); return false; } if (fsync(fd) == -1) { *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(), strerror(errno)); return false; } return true; }

setup_bcb()方法在简单处理从socket接收的消息后,返回状态值“100”就结束了。

关于write_misc_partition()方法中变量misc_blk_device,misc_blk_device由方法get_misc_blk_device()初始化。查找/misc分区。

static std::string get_misc_blk_device(std::string* err) { Fstab fstab; if (!ReadDefaultFstab(&fstab)) { *err = "failed to read default fstab"; return ""; } auto record = GetEntryForMountPoint(&fstab, "/misc"); if (record == nullptr) { *err = "failed to find /misc partition"; return ""; } return record->blk_device; }

ReadDefaultFstab()方法读取设备分区信息,返回挂载点。

// Loads the fstab file and combines with fstab entries passed in from device tree. bool ReadDefaultFstab(Fstab* fstab) { Fstab dt_fstab; ReadFstabFromDt(&dt_fstab, false); *fstab = std::move(dt_fstab); std::string default_fstab_path; // Use different fstab paths for normal boot and recovery boot, respectively if (access("/system/bin/recovery", F_OK) == 0) { default_fstab_path = "/etc/recovery.fstab"; } else { // normal boot default_fstab_path = GetFstabPath(); } Fstab default_fstab; if (!default_fstab_path.empty()) { ReadFstabFromFile(default_fstab_path, &default_fstab); } else { LINFO ... load_volume_table(); std::string stage; std::vector args = get_args(argc, argv, &stage); auto args_to_parse = StringVectorToNullTerminatedArray(args); static constexpr struct option OPTIONS[] = { { "fastboot", no_argument, nullptr, 0 }, { "locale", required_argument, nullptr, 0 }, { "reason", required_argument, nullptr, 0 }, { "show_text", no_argument, nullptr, 't' }, { nullptr, 0, nullptr, 0 }, }; bool show_text = false; bool fastboot = false; std::string locale; std::string reason; // The code here is only interested in the options that signal the intent to start fastbootd or // recovery. Unrecognized options are likely meant for recovery, which will be processed later in // start_recovery(). Suppress the warnings for such -- even if some flags were indeed invalid, the // code in start_recovery() will capture and report them. opterr = 0; 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 == "reason") { reason = optarg; LOG(INFO) printf("Failed to dlopen %s: %s\n", kDefaultLibRecoveryUIExt, dlerror()); } else { reinterpret_cast(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; ... BootState boot_state(reason, stage); // recovery_main owns the state of boot. device->SetBootState(&boot_state); ... while (true) { ... 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"); Shutdown("userrequested,recovery"); break; case Device::REBOOT_BOOTLOADER: ui->Print("Rebooting to bootloader...\n"); Reboot("bootloader"); break; case Device::REBOOT_FASTBOOT: ui->Print("Rebooting to recovery/fastboot...\n"); Reboot("fastboot"); break; case Device::REBOOT_RECOVERY: ui->Print("Rebooting to recovery...\n"); Reboot("recovery"); break; case Device::ENTER_FASTBOOT: if (android::fs_mgr::LogicalPartitionsMapped()) { ui->Print("Partitions may be mounted - rebooting to enter fastboot."); Reboot("fastboot"); } else { LOG(INFO) { "fastboot", no_argument, nullptr, 0 }, { "install_with_fuse", 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 }, }; ... auto args_to_parse = StringVectorToNullTerminatedArray(args); 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 == "install_with_fuse") { install_with_fuse = true; } else if (option == "locale" || option == "fastboot" || option == "reason") { // Handled in recovery_main.cpp } else if (option == "prompt_and_wipe_data") { should_prompt_and_wipe_data = true; } 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) save_current_log = true; CHECK(device->GetReason().has_value()); bool convert_fbe = device->GetReason().value() == "convert_fbe"; if (!WipeData(device, convert_fbe)) { status = INSTALL_ERROR; } } ... 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()) { auto temp = PromptAndWait(device, status); if (temp != Device::NO_ACTION) { next_action = temp; } } } // Save logs and clean up before rebooting or shutting down. FinishRecovery(ui); return next_action; }

和上面的main()方法一样,args参数被处理赋值给args_to_parse变量,通过getopt_long()对参数进行判断选择对应的操作,option == "wipe_data"时,should_wipe_data = true,之后通过判断变量should_wipe_data调用WipeData(device, convert_fbe)方法清楚用户数据:

constexpr const char* CACHE_ROOT = "/cache"; constexpr const char* DATA_ROOT = "/data"; constexpr const char* METADATA_ROOT = "/metadata"; bool WipeData(Device* device, bool convert_fbe) { RecoveryUI* ui = device->GetUI(); ui->Print("\n-- Wiping data...\n"); if (!FinishPendingSnapshotMerges(device)) { ui->Print("Unable to check update status or complete merge, cannot wipe partitions.\n"); return false; } bool success = device->PreWipeData(); if (success) { success &= EraseVolume(DATA_ROOT, ui, convert_fbe); bool has_cache = volume_for_mount_point("/cache") != nullptr; if (has_cache) { success &= EraseVolume(CACHE_ROOT, ui, false); } if (volume_for_mount_point(METADATA_ROOT) != nullptr) { success &= EraseVolume(METADATA_ROOT, ui, false); } } if (success) { success &= device->PostWipeData(); } ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); return success; }

WipeData()方法对/data,/cache,/metadata分区进行格式化操作。之后start_recovery()返回Device::REBOOT,设备重启。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有