Android旧式的系统升级是在Recover模式下将U盘里面的update.zip升级包进行安装,这种方式不是特别的安全,如果升级过程中出现掉电,或者其他升级错误,容易造成机器变成砖,只能通过烧录才能修复。现在已使用全新的一套升级系统update_engine,这套系统的加入使原有的Recover变得多余,但是为了向下兼容和恢复出厂设置需要使用所以还保留了Recover模块。update_engine升级思路是A/B升级模式,意思就是机器上有两套可引导系统,如果目前运行的是A系统则通过A引导升级B系统,如果是B系统则引导升级A系统。这样能保证手机不会变成砖,就算升级失败,那也还是原来的系统。升级过程后台运行,不影响用户使用。要想深入学习需要做大量功课,以下一个成功的demo。
模块路径:system/update_engine
测试程序:system/update_engine/update_engine_client_android.cc
测试模块是否打通:
- 制作升级包make otapackage
- 解压升级包out\target\product\product\update.zip
- 提取payload.bin、payload_properties.txt放置到指定下载路径或者U盘
- FILE_HASH、FILE_SIZE、METADATA_HASH这些参数在payload_properties.txt文件中
- --payload=可以是HTTP形式的URL,直接进行OTA
- 特别要注意--headers="最好在第一行,格式解析有换行的要求,不然参数传不下去
测试指令:
update_engine_client --payload=file:///storage/udisk/payload.bin --update --headers="
FILE_HASH=wpG0gLjbUg3uTQ4LTnXjPfH3rvjxCPAEHzCspps28Sg=
FILE_SIZE=589122832
METADATA_HASH=EAlcdErle8BiZLOMZUkWAcwVYdmZc6StLfPMQE0OwE4=
METADATA_SIZE=54970"
解析程序通过\n来进行切割,然后调用service_->applyPayload进行升级。
if (FLAGS_update) {
std::vector<std::string> headers = base::SplitString(
FLAGS_headers, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
std::vector<android::String16> and_headers;
for (const auto& header : headers) {
and_headers.push_back(android::String16{header.data(), header.size()});
}
Status status = service_->applyPayload(
android::String16{FLAGS_payload.data(), FLAGS_payload.size()},
FLAGS_offset,
FLAGS_size,
and_headers);
if (!status.isOk())
return ExitWhenIdle(status);
}
如果使用测试指令能成功升级,则可以直接用java接口进行升级工作。
package com.test.update.manager;
import java.net.MalformedURLException;
import java.net.URI;
import java.text.DecimalFormat;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.os.UpdateEngine;
import android.os.UpdateEngineCallback;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.net.Uri;
import android.util.Log;
public class SystemUpdateManager {
public static final String ROOTDIR = "/storage/udisk/UpdatePack";
public static final String PATH_PAYLOAD_BIN = ROOTDIR + "/OS/payload.bin";
public static final String PATH_PAYLOAD_PROPERTIES = ROOTDIR + "/OS/payload_properties.txt";
UpdateEngine mUpdateEngine;
public SystemUpdateManager(Context context) throws MalformedURLException {
mUpdateEngine = new UpdateEngine();
}
UpdateEngineCallback mUpdateEngineCallback = new UpdateEngineCallback {
@Override
public void onStatusUpdate(int status, float percent) {
if (status == UpdateEngine.UpdateStatusConstants.DOWNLOADING) {// 回调状态,升级进度
DecimalFormat df = new DecimalFormat("#");
String progress = df.format(percent * 100);
Log.d(TAG, "update progress: " + progress);
if (this.mUpdateEngine != null) {
this.mUpdateEngine.onStateOrProgress(UpdateChangeListener.MESSAGE_UPDATE_PROGRESS,
0, progress);
}
}
}
@Override
public void onPayloadApplicationComplete(int errorCode) {
if (errorCode == UpdateEngine.ErrorCodeConstants.SUCCESS) {// 回调状态
Log.d(TAG, "UPDATE SUCCESS!");
if (this.mUpdateEngine != null) {
this.mUpdateEngine.onStateOrProgress(UpdateChangeListener.MESSAGE_WAIT_REBOOT, 0,
null);
}
}
}
};
public void startUpdateSystem() {
Uri uri = Uri.fromFile(new File(PATH_PAYLOAD_BIN));
mUpdateEngine.bind(mUpdateEngineCallback);// 绑定callback
mUpdateEngine.applyPayload(uri.toString(), 0l, 0l, getPayloadProperties());// 进行升级
}
// 读取payload_properties.txt
public static String[] getPayloadProperties() {
try {
File file = new File(PATH_PAYLOAD_PROPERTIES);
InputStreamReader is = new InputStreamReader(new FileInputStream(file));
BufferedReader br = new BufferedReader(is);
List<String> lines = new ArrayList<String>();
String line = null;
while ((line = br.readLine()) != null) {
Log.d(TAG, "getPayloadProperties line: " + line);
lines.add(line);
}
br.close();
is.close();
return lines.toArray(new String[lines.size()]);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
参考博客:https://blog.youkuaiyun.com/guyongqiangx/article/details/77650362