关于android的recovery网上有各种版本的定义,这里我总结一下:所谓recovery是android下加入的一种特殊工作模式,有点类似于windows下的gost,系统进入到这种模式下时,可以在这里通过按键选择相应的操作菜单实现相应的功能,比如android系统和数据区的快速格式化(wipe);系统和用户数据的备份和恢复;通过sd卡刷新的rom等等。典型的recovery界面如下:
Recovery的源代码在bootable/recovery这个目录下面,主要来看看recovery.c这个文件中的main函数:
Int main(int argc, char **argv) {
.
.
.
ui_init();
ui_set_background(BACKGROUND_ICON_INSTALLING);
load_volume_table();
.
.
.
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
switch (arg) {
case 'p': previous_runs = atoi(optarg); break;
case 's': send_intent = optarg; break;
case 'u': update_package = optarg; break;
case 'w': wipe_data = wipe_cache = 1; break;
case 'c': wipe_cache = 1; break;
case 'e': encrypted_fs_mode = optarg; toggle_secure_fs = 1; break;
case 't': ui_show_text(1); break;
case '?':
LOGE("Invalid command argument\n");
continue;
}
}
device_recovery_start();
.
.
.
if (update_package)
{
// For backwards compatibility on the cache partition only, if
// we're given an old 'root' path "CACHE:foo", change it to
// "/cache/foo".
if (strncmp(update_package, "CACHE:", 6) == 0)
{
int len = strlen(update_package) + 10;
char* modified_path = malloc(len);
strlcpy(modified_path, "/cache/", len);
strlcat(modified_path, update_package+6, len);
printf("(replacing path \"%s\" with \"%s\")\n",
update_package, modified_path);
update_package = modified_path;
}
//for update from "/mnt/sdcard/update.zip",but at recovery system is "/sdcard" so change it to "/sdcard"
//ui_print("before:[%s]\n",update_package);
if (strncmp(update_package, "/mnt", 4) == 0)
{
//jump the "/mnt"
update_package +=4;
}
ui_print("install package from[%s]\n",update_package);
}
printf("\n");
property_list(print_property, NULL);
printf("\n");
int status = INSTALL_SUCCESS;
.
.
.
// Recovery strategy: if the data partition is damaged, disable encrypted file systems.
// This preventsthe device recycling endlessly in recovery mode.
.
.
.
if (update_package != NULL)
{
status = install_package(update_package);
if (status != INSTALL_SUCCESS)
ui_print("Installation aborted.\n");
else
{
erase_volume("/data");
erase_volume("/cache");
}
} else if (wipe_data) {
if (device_wipe_data()) status = INSTALL_ERROR;
if (erase_volume("/data")) status = INSTALL_ERROR;
if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
} else if (wipe_cache) {
if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");
} else {
status = INSTALL_ERROR; // No command specified
}
if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
//Xandy modify for view the install infomation
//if (status != INSTALL_SUCCESS || ui_text_visible())
if(status != INSTALL_SUCCESS)
{
prompt_and_wait();
}
// Otherwise, get ready to boot the main system...
finish_recovery(send_intent);
ui_print("Rebooting...\n");
sync();
reboot(RB_AUTOBOOT);
return EXIT_SUCCESS;
}
在这里首先完成recovery模式轻量级的UI系统初始化,设置背景图片,然后对输入的参数格式化,最后根据输入的参数进行相应的操作,如:安装新的ROM、格式化(wipe)data及cache分区等等;值得注意的是刷新ROM的时候,要制作相应的update.zip的安装包,这个在最后一章节讲述,这里遇到的一个问题是在recovery模式下sd卡的挂载点为/sdcard而不是android系统下的/mnt/sdcard,所以我在这里通过:
//for update from "/mnt/sdcard/update.zip",but at recovery system is "/sdcard" so change it to "/sdcard"
//ui_print("before:[%s]\n",update_package);
if (strncmp(update_package, "/mnt", 4) == 0)
{
//jump the "/mnt"
update_package +=4;
}
这样的操作跳过了上层传过来的/mnt这四个字符。另外一个值得一提的是,传入这里的这些参数都是从/cache/recovery/command这个文件中提取。具体对command文件的解析过程这里不再讲述,可能通过查看recovery.c这个文件中的get_args函数。
那么command这个文件是在什么情况下创建的呢?下面我们就来看看吧!
恢复出厂设置和固件升级
在android的系统设备中进入“隐私权->恢复出厂设置->重置手机”将为进入到恢复出厂设置的状态,这时将会清除data、cache分区中的所有用户数据,使得系统重启后和刚刷机时一样了。另外为了方便操作我们还可在“隐私权->固件升级->刷新ROM”这里加入了固件升级这一项。
在讲述这些内容之前,我们有必要来看看/cache/recovery/command这个文件相应的一些recovery命令,这些命令都由android系统写入。所有的命令如下:
* --send_intent=anystring ―― write the text out to recovery.intent
* --update_package=root:path —— verify install an OTA package file
* --wipe_data —— erase user data (and cache), then reboot
* --wipe_cache —— wipe cache (but not user data), then reboot
恢复出厂设置
在frameworks/base/services/java/com/android/server/masterClearReceiver.java
这个文件中有如下代码:
public class MasterClearReceiver extends BroadcastReceiver { private static final String TAG = "MasterClear"; @Override public void onReceive(final Context context, final Intent intent) { if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) { if (!"google.com".equals(intent.getStringExtra("from"))) { Slog.w(TAG, "Ignoring master clear request -- not from trusted server."); return; } } Slog.w(TAG, "!!! FACTORY RESET !!!"); // The reboot call is blocking, so we need to do it on another thread. Thread thr = new Thread("Reboot") { @Override public void run() { try { if (intent.hasExtra("enableEFS")) { RecoverySystem.rebootToggleEFS(context, intent.getBooleanExtra("enableEFS", false)); } else { RecoverySystem.rebootWipeUserData(context); } Log.wtf(TAG, "Still running after master clear?!"); } catch (IOException e) { Slog.e(TAG, "Can't perform master clear/factory reset", e); } } }; thr.start(); } }当app中操作了“恢复出厂设置”这一项时,将发出广播,这个广播将在这里被监听,然后进入到恢复出厂设置状态,我们来看看rebootWipeUserData这个方法的代码:
public static void rebootWipeUserData(Context context) throws IOException { final ConditionVariable condition = new ConditionVariable(); Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION"); context.sendOrderedBroadcast(intent, android.Manifest.permission.MASTER_CLEAR, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { condition.open(); } }, null, 0, null, null); // Block until the ordered broadcast has completed. condition.block(); bootCommand(context, "--wipe_data"); }
我们可以看到在这里参入了“--wipe_data”这个参数,并把这条命令写入到command这个文件中去了,在进入recovery模式的时候解析到这条命令时就会清除data和cache中的数据了。
再来看看bootCommand这个方法里的代码:
private static void bootCommand(Context context, String arg) throws IOException { RECOVERY_DIR.mkdirs(); // In case we need it COMMAND_FILE.delete(); // In case it'其中COMMAND_FILE这个成员的定义如下:固件升级的流程和恢复出厂设置差不多,不同之处是入command这个文件中写入的命令不一样,下面是恢复出厂设置时的写命令的代码:
public static void installPackage(Context context, File packageFile) throws IOException { String filename = packageFile.getCanonicalPath(); Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); String arg = "--update_package=" + filename; bootCommand(context, arg); }这里的packageFile是由上层app传入的,内容如下:
File packageFile = new File("/sdcard/update.zip"); RecoverySystem.installPackage(context, packageFile);
这样当系统重启进入到recovery模式时将会自动查找sdcard的根目录下是否有update.zip这个文件,如果有将会进入到update状态,否则会提示无法找到update.zip!
s not writable LOG_FILE.delete(); FileWriter command = new FileWriter(COMMAND_FILE); try { command.write(arg); command.write("\n"); } finally { command.close(); } // Having written the command file, go ahead and reboot PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); pm.reboot("recovery"); throw new IOException("Reboot failed (no permissions?)"); }
/** Used to communicate with recovery. See bootable/recovery/recovery.c. */ private static File RECOVERY_DIR = new File("/cache/recovery"); private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
至此恢复出厂设置的命令就写入了recovery cmd file中去了,通过pm.reboot(“recovery”);重启系统,系统就自动进入到recovery模式自动清除用户数据后再重启系统。
固件升级
固件升级的流程和恢复出厂设置差不多,不同之处是入command这个文件中写入的命令不一样,下面是恢复出厂设置时的写命令的代码:
public static void installPackage(Context context, File packageFile) throws IOException { String filename = packageFile.getCanonicalPath(); Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); String arg = "--update_package=" + filename; bootCommand(context, arg); }
这里的packageFile是由上层app传入的,内容如下:
File packageFile = new File("/sdcard/update.zip");
RecoverySystem.installPackage(context, packageFile);
这样当系统重启进入到recovery模式时将会自动查找sdcard的根目录下是否有update.zip这个文件,如果有将会进入到update状态,否则会提示无法找到update.zip!