Android之Recovery业务(二)-恢复出厂设置
2022-01-24 本文已影响0人
锄禾豆
简介
简单介绍ota升级业务和恢复出厂设置业务在流程上的差异性
框架
Settings
--> Android(MasterClearReceiver)
--RecoverySystem--> RecoverySystemService
--> uncrypt
代码列表
packages/apps/Settings
frameworks/base/core/res/AndroidManifest.xml
frameworks/base/core/java/android/os/RecoverySystem.java
frameworks/base/services/core/java/com/android/server/
RecoverySystemService.java
MasterClearReceiver.java
bootable/recovery
注:
7.1
分析
1.app调用系统接口:RecoverySystem.rebootWipeUserData
权限:
android.permission.REBOOT
Settings发送广播
private void doMasterClear() {
Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);
getActivity().sendBroadcast(intent);
// Intent handling is asynchronous -- assume it will happen soon.
}
Android AndroidManifest.xml定义接受
Thread thr = new Thread("Reboot") {
@Override
public void run() {
try {
RecoverySystem.rebootWipeUserData(context, shutdown, reason, forceWipe);
Log.wtf(TAG, "Still running after master clear?!");
} catch (IOException e) {
Slog.e(TAG, "Can't perform master clear/factory reset", e);
} catch (SecurityException e) {
Slog.e(TAG, "Can't perform master clear/factory reset", e);
}
}
};
thr.start();
RecoverySystem分析业务
rebootWipeUserData --> bootCommand
public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
boolean force) throws IOException {
···
String shutdownArg = null;
if (shutdown) {
shutdownArg = "--shutdown_after";
}
String reasonArg = null;
if (!TextUtils.isEmpty(reason)) {
reasonArg = "--reason=" + sanitizeArg(reason);
}
final String localeArg = "--locale=" + Locale.getDefault().toString();
bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
}
private static void bootCommand(Context context, String... args) throws IOException {
LOG_FILE.delete();
StringBuilder command = new StringBuilder();
for (String arg : args) {
if (!TextUtils.isEmpty(arg)) {
command.append(arg);
command.append("\n");
}
}
// Write the command into BCB (bootloader control block) and boot from
// there. Will not return unless failed.
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
rs.rebootRecoveryWithCommand(command.toString());//写入RecoverySystemService
throw new IOException("Reboot failed (no permissions?)");
}
2.RecoverySystemService接收数据进行处理
RecoverySystemService.BinderService
public void rebootRecoveryWithCommand(String command) {
synchronized (sRequestLock) {
if (!setupOrClearBcb(true, command)) {
return;
}
// Having set up the BCB, go ahead and reboot.
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
pm.reboot(PowerManager.REBOOT_RECOVERY);
}
}
注意:
1.ota升级也是调入setupOrClearBcb,差异仅仅在于command
2.ota升级实在RecoverySystem中操作reboot,参数为REBOOT_RECOVERY_UPDATE
这是是在RecoverySystemService中操作reboot,参数为REBOOT_RECOVERY
这样的目的就是谁控制权限问题
3.uncrypt通过socket与RecoverySystemService建立联系,将数据写入misc分区
省略--此过程可参考:Android之Recovery业务(一)-升级过程
4.PowerManager.reboot差异
Powermanager.reboot(PowerManager.REBOOT_RECOVERY)
-->> PowerManagerService.reboot
-->> ShutdownThread.reboot --> shutdownInner --> beginShutdownSequence --> run
public void run() {
···
if (mRebootHasProgressBar) {//此时mRebootHasProgressBar=false
sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
// If it's to reboot to install an update and uncrypt hasn't been
// done yet, trigger it now.
uncrypt();
}
···
rebootOrShutdown(mContext, mReboot, mReason);
}
5.recovery部分日志及文件分析
1)快速进入recovery的方式
adb reboot recovery
2)日志分析
日记重定向 //可控制日志是否保存文件还是串口输出
bootable/recovery/Android.mk
REDIRECT_LOG_TO := SDCARD
日志所在位置
/cache/recovery
涉及文件:
last_install //升级成功与否的标记
---------------------------------------------
例如
@/cache/recovery/block.map //update.zip的存储位置
1 //这个代表成功
time_total: 51
retry: 0
bytes_written_system: 790208512
bytes_stashed_system: 0
uncrypt_time: 0
---------------------------------------------------
last_log** //日志
-----------------------
last_log //最新的
last_log* //代表过去的
特别注意:
recovery系统中有对应的build.prop等信息 如果更新过 则last_log可查
升级过程可以看到需要升级的目标版本信息,例如:
[ 5.866441] Target: Android/rk3288/rk3288:7.1.2/NHG47K/iotuse08190934:userdebug/test-keys
-------------------------
last_kmsg** //kernel日志
last_locale //语言区域
uncrypt_file //system中,update.zip所在的路径
-------------------------------------
/data/Firmware/update.zip //system中,update.zip所在的路径
------------------------------------
其他
PowerManagerService
SystemProperties.set("sys.powerctl", "reboot,recovery");
system/core/rootdir/init.rc
on property:sys.powerctl=*
powerctl ${sys.powerctl}
system/core/init/builtins.cpp
{"powerctl", {1, 1, do_powerctl}},
android_reboot_with_callback
system/core/libcutils/android_reboot.c
int android_reboot_with_callback(
int cmd, int flags __unused, const char *arg,
void (*cb_on_remount)(const struct mntent*))
{
int ret;
remount_ro(cb_on_remount);
switch (cmd) {
case ANDROID_RB_RESTART:
ret = reboot(RB_AUTOBOOT);
break;
case ANDROID_RB_POWEROFF:
ret = reboot(RB_POWER_OFF);
break;
case ANDROID_RB_RESTART2:
ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, arg);
break;
default:
ret = -1;
}
return ret;
}
//syscall 这是native和kernel打交道的接口
kernel/kernel/reboot.c
case LINUX_REBOOT_CMD_RESTART2:
ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
if (ret < 0) {
ret = -EFAULT;
break;
}
buffer[sizeof(buffer) - 1] = '\0';
kernel_restart(buffer);
break;
void kernel_restart(char *cmd)
{
kernel_restart_prepare(cmd);
migrate_to_reboot_cpu();
syscore_shutdown();
if (!cmd)
pr_emerg("Restarting system\n");
else
pr_emerg("Restarting system with command '%s'\n", cmd);
kmsg_dump(KMSG_DUMP_RESTART);
machine_restart(cmd);
}