随着android版本的更新,系统固件的大小也越来越大,升级包也越来越大,cache分区已经不够存储update.zip了,所以应用把update.zip下载到data分区,默认情况下data分区是可以存储升级包的。

我们有分区加密的功能,当打开加密分区后,data分区是加密的,当升级包存在data分区的时候,recovery下获取不到对应的秘钥,也没有对应的程序去解密,所以recovery无法正常挂载data分区,获取升级包升级。那么google是如何完成分区加密时,从data分区升级的呢?

当应用从远程服务器下载update.zip升级包后,是如何一步步进入recovery升级的呢?

android P(9.0)aosp code:

frameworks/base/core/java/android/os/RecoverySystem.java

主要分为两步进行,第一步处理升级包(processPackage),第二步安装升级包(installPackage):

处理升级包:

public static void processPackage(Context context,
File packageFile,
final ProgressListener listener,
final Handler handler)
throws IOException {
String filename = packageFile.getCanonicalPath();
if (!filename.startsWith("/data/")) {
return;
} RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
IRecoverySystemProgressListener progressListener = null;
if (listener != null) {
final Handler progressHandler;
if (handler != null) {
progressHandler = handler;
} else {
progressHandler = new Handler(context.getMainLooper());
}
progressListener = new IRecoverySystemProgressListener.Stub() {
int lastProgress = 0;
long lastPublishTime = System.currentTimeMillis(); @Override
public void onProgress(final int progress) {
final long now = System.currentTimeMillis();
progressHandler.post(new Runnable() {
@Override
public void run() {
if (progress > lastProgress &&
now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {
lastProgress = progress;
lastPublishTime = now;
listener.onProgress(progress);
}
}
});
}
};
} if (!rs.uncrypt(filename, progressListener)) {
throw new IOException("process package failed");
}
}

主要做了如下工作:

(1) 只处理升级包在/data分区的场景

(2) 处理进度显示

(3)调用rs.uncrypt(filename, progressListener) 处理升级包

    /**
* Talks to RecoverySystemService via Binder to trigger uncrypt.
*/
private boolean uncrypt(String packageFile, IRecoverySystemProgressListener listener) {
try {
return mService.uncrypt(packageFile, listener);
} catch (RemoteException unused) {
}
return false;
}

RecoverySystem 通过Binder 触发 uncrypt服务

/system/etc/init/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

调用了/system/bin/uncrypt程序来处理升级包, uncrypt对应的源码在 bootable/recovery/uncrypt/uncrypt.cpp

具体处理细节我们在章节详解:recovery uncrypt功能解析(bootable/recovery/uncrypt/uncrypt.cpp)

安装升级包:

 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?)");
}
}

主要做了如下工作:

(1) 如果升级包路径为/data开始的根目录,把升级包的名字写到文件/cache/recovery/uncrypt_file里

(2) 写升级命令--update_package=@/cache/recovery/block.map到文件/cache/recovery/command

(3) 调用pm.reboot(PowerManager.REBOOT_RECOVERY_UPDATE)重启。

参考资料:https://blog.csdn.net/miaotao/article/details/45129423

http://feed.askmaclean.com/archives/linux查看稀疏文件的哪些块没有分配空间.html

recovery 根据@/cache/recovery/block.map描述从data分区升级的更多相关文章

  1. recovery 升级'@/cache/recovery/block.map' failed错误问题

    随着android版本升级,升级包越来越大,当升级包无法存储在cache分区的时候,会把升级包下载到data分区,然后从data分区升级,最近从data分区加载升级包升级的时候,遇到了如下错误: [ ...

  2. Recovery启动流程--recovery.cpp分析

    这篇文章主要通过分析高通recovery目录下的recovery.cpp源码,对recovery启动流程有一个宏观的了解. 当开机以后,在lk阶段,如果是recovery,会设置boot_into_r ...

  3. Oracle实例的恢复、介质恢复( crash recovery)( Media recovery)

    实例的恢复( crash recovery) 什么时候发生Oracle实例恢复? shutdown abort; 数据库异常down掉(机器死机,掉电...) 实例恢复的原因是数据有丢掉,使用redo ...

  4. FATAL: using recovery command file "recovery.conf" is not supported

    PostgreSQL12 附录 E. 版本说明 将recovery.conf设置移动到postgresql.conf中. (Masao Fujii, Simon Riggs, Abhijit Meno ...

  5. Python 描述符 data 和 non-data 两种类型

    仅包含__get__的,是non-data descriptor, 如果实例__dict__包含同名变量, 则实例优先; 如果还包含__set__, 则是data descriptor, 优先于实例_ ...

  6. recovery uncrypt功能解析(bootable/recovery/uncrypt/uncrypt.cpp)

    我们通常对一个文件可以直接读写操作,或者普通的分区(没有文件系统)也是一样,直接对/dev/block/boot直接读写,就可以获取里面的数据内容了. 当我们在ota升级的时候,把升级包下载到cach ...

  7. Android 的Recovery机制【转】

    本文转载自:http://blog.csdn.net/fengying765/article/details/38301895 Android 的Recovery机制 目录 1. 系统的启动模式 1 ...

  8. Android系统Recovery模式的工作原理【转】

    本文转载自:http://blog.csdn.net/mu0206mu/article/details/7464987  在使用update.zip包升级时怎样从主系统(main system)重启进 ...

  9. Bootloader - main system - Recovery的三角关系【转】

    本文转载自:http://blog.csdn.net/u012719256/article/details/52304273 一.MTD分区: BOOT:        boot.img,Linux ...

随机推荐

  1. Liferay7 BPM门户开发之16: Liferay中用户\站点\组织架构\角色\用户组以及关联关系

    用户 . 站点 . 组织架构 . 角色 . 用户组分别是:Users. Site. Organization. Role. UserGroups Users 用户是Liferay portal中的关键 ...

  2. 同时使用 Ant Design of React 中 Mention 和 Form

    使用场景,在一个列表中,点击每一行会弹出一个表单,通过修改表单数据并提交来修改这一行的数据,其中某个数据的填写需要通过Mention实现动态提示及自动补全的功能. 具体效果为: 遇到的问题: 1.希望 ...

  3. 基于httpclient的效率优化

    1.背景 我们有个业务,会调用其他部门提供的一个基于http的服务,日调用量在千万级别.使用了httpclient来完成业务.之前因为qps上不去,就看了一下业务代码,并做了一些优化,记录在这里. 先 ...

  4. CSS--最最最基础操作

    CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. h1 {color:red; font-size:14px;} css的四种引入方式 1.行内式  行内式是在标记的style属性中设定 ...

  5. Python -- tabulate 模块,

    pip install tabulate >>> from tabulate import tabulate >>> table = [["Sun&quo ...

  6. Android开源系列:仿网易Tab分类排序控件实现

    前言 产品:网易新闻那个Tab排序好帅. 开发:哦~ 然后这个东东在几天后就出现了..... (PS:差不多一年没回来写博客了~~~~(>_<)~~~~,顺便把名字从 enjoy风铃 修改 ...

  7. 如何将打包好的文件做成一个APP

    本文主要是用来简短的对做成一个APP进行说明,内容可能不是多详细,但会给出具体思路.(仅供参考) 因为各种打包和生成APP的方式多样,今天这里仅仅对用Hbuilder打包进行说明. 1.首先当然需要一 ...

  8. Java并发编程笔记之FutureTask源码分析

    FutureTask可用于异步获取执行结果或取消执行任务的场景.通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过Fu ...

  9. REX:EOS资源租赁平台详解

    关键字:REX,资源交易,资源租赁,系统费用,bancor,成熟期,EOS,eosio.system,voting EOSIO 智能合约在v1.6.0版本增加了一个system合约使用的例子,可提供E ...

  10. linux下修改时间和时区

    一.修改linux的时间在root用户下,使用date指令:date -s1.只修改日期,不修改时间,输入: date -s -- 2.只修改时间,输入: date -s :: 3.同时修改日期时间, ...