一、ota流程之framwork installpackage流程
关于install_package的流程,大部分应该已经比较了解,按方法流程执行,这里我们流程上还是做简单说明,主要要要说明其中的一些细节
1、setup-bcb
2、block.map的生成
按方法流程分析如下
framwork/base/core/java/android/os/RecoverySystem.java
@RequiresPermission(android.Manifest.permission.RECOVERY)
public static void installPackage(Context context, File packageFile)
throws IOException {
installPackage(context, packageFile, false);
}
//传入context,文件路径,processed = false
public static void installPackage(Context context, File packageFile, boolean processed)
throws IOException {
//防止并发执行请求。
synchronized (sRequestLock) {
//删除cache/recovery/log
LOG_FILE.delete();
// Must delete the file in case it was created by system server.
//刪除cache/recovery/uncrypt文件,保证为当前系统服务创建
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.如果文件名以data开头
if (filename.startsWith("/data/")) {
//这里传进来的就是false,如果是直接调取的installpackage
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.如果unvrypt_file不可读也不可写,打印出错误
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文件
BLOCK_MAP_FILE.delete();
}
// If the package is on the /data partition, use the block map
// file as the package name instead.
//设定文件名为@/cache/recovery/block.map
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";
//最初的command,包含文件名和语言
String command = filenameArg + localeArg;
if (securityUpdate) {
command += securityArg;
}
RecoverySystem rs = (RecoverySystem) context.getSystemService(
Context.RECOVERY_SERVICE);
//判断是否可以正常写入bcb,传入command,
//这里存在一个优化点,我们碰到很多解密失败的问题,那为什么不先解密完成后返回状态,再写入bcb呢,
//这样直接判定失败,就不用在recovery出现failed to map file的问题
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);
//public static final String REBOOT_RECOVERY_UPDATE = "recovery-update";
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?)");
}
}
setupBcb(command)
framwork/base/core/java/android/os/RecoverySystem.java
/**
* Talks to RecoverySystemService via Binder to clear up the BCB.
*/
private boolean clearBcb() {
try {
return mService.clearBcb();
} catch (RemoteException unused) {
}
return false;
}
framwork/base/services/core/java/com/android/server/RecoverySystemService.java
@Override // Binder call
public boolean clearBcb() {
if (DEBUG) Slog.d(TAG, "clearBcb");
synchronized (sRequestLock) {
return setupOrClearBcb(false, null);
}
}
framwork/base/services/core/java/com/android/server/RecoverySystemService.java
//setupbcb传入 isSetup = true , command = 路径 + 语言
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) {
//属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,
//与init守护进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。
//这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果。
SystemProperties.set("ctl.start", "setup-bcb");
} else {
SystemProperties.set("ctl.start", "clear-bcb");
}
// Connect to the uncrypt service socket.
LocalSocket socket = connectService();
if (socket == null) {
Slog.e(TAG, "Failed to connect to uncrypt socket");
return false;
}
DataInputStream dis = null;
DataOutputStream dos = null;
try {
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
// Send the BCB commands if it's to setup BCB.
// 将commands参数传入uncrypt.cpp,执行write_bootloader_message写入command信息
if (isSetup) {
byte[] cmdUtf8 = command.getBytes("UTF-8");
dos.writeInt(cmdUtf8.length);
dos.write(cmdUtf8, 0, cmdUtf8.length);
dos.flush();
}
// Read the status from the socket.
int status = dis.readInt();
// Ack receipt of the status code. uncrypt waits for the ack so
// the socket won't be destroyed before we receive the code.
dos.writeInt(0);
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 {
IoUtils.closeQuietly(dis);
IoUtils.closeQuietly(dos);
IoUtils.closeQuietly(socket);
}
return true;
}
}
通过socket连接到uncryt服务,传入commang数据,并执行写入
framwork/base/services/core/java/com/android/server/RecoverySystemService.java
//通过socket连接到服务
private LocalSocket connectService() {
LocalSocket socket = new LocalSocket();
boolean done = false;
// The uncrypt socket will be created by init upon receiving the
// service request. It may not be ready by this point. So we will
// keep retrying until success or reaching timeout.
for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
try {
socket.connect(new LocalSocketAddress(UNCRYPT_SOCKET,
LocalSocketAddress.Namespace.RESERVED));
done = true;
break;
} catch (IOException ignored) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Slog.w(TAG, "Interrupted:", e);
}
}
}
if (!done) {
Slog.e(TAG, "Timed out connecting to uncrypt socket");
return null;
}
return socket;
}
SystemProperties.set("ctl.start", "setup-bcb");后会启动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

本文深入解析了OTA升级过程中的关键步骤,重点介绍了framework installpackage流程,涵盖了从安装包验证、解密、block map生成到系统重启的全过程。文章详细解释了如何通过binder机制与uncrypt服务交互,实现升级包的高效解密和block map文件的生成。

2万+

被折叠的 条评论
为什么被折叠?



