当前位置:   article > 正文

Android OTA 升级入门篇_android ota升级流程

android ota升级流程

简介

第一次接手FOTA ,记录下app 端的实现过程,和一些基本概念,不对的地方,还望指正.

Android FOTA 广义是Android 实现设备系统无线升级的全部过程和手段。如果一个设备无线升级的系统是完备的话,则一个FOTA过程主要包含以下工作:

1. 制作FOTA升级包:升级包一种可供设备实现系统更新升级的一个压缩包文件,解压缩后本质是各种img文件、被执行文件和一些配置信息等。Android升级时

会有对应的程序解析这个文件。

2. 测试FOTA升级包:在发布这个版本之后,需要测试设备是否可以进行无线升级,是否会出现问题。

3. 发布升级包:将FOTA升级包发给客户或者上传客户服务器

其实和app 升级是一个道理,下载-安装-重启生效

升级

google 已经为我们集成了升级方式,AB 升级和Recovery 升级,相对来说recovery 会进入系统recovery mode ,影响用户使用,而AB 升级是无感升级,两个slot 重启切换,不影响用户使用,体验更好.

目前android 11 默认是开启 virtural AB,对于上层来说其实没啥区别,都是调用updata_engine就可以了,参考了一部分文章,两者主要是super 分区的差异

bootloader_a

bootloader_b

boot_a

boot_b

vendor_boot_a

vendor_boot_b

dtbo_a

dtbo_b

vbmeta_a

vbmeta_b

super

virtualAB        通过快照方式写入 super 分区,只保留一份                         

system

product

vendor

   AB 传统分区,两个slot

system_a

product_a

vendor_a

system_b

product_b

vendor_b

app 实现

第一次接触的,可以参考packages/apps/Car/SystemUpdater android 系统为我们提供的demo

好了直接上代码,我把安装流程集成到service 里面.actvity 等要是想要进度条,实现 ResultReceiver,传递过来就可以了

  1. /**
  2. 安装软件的服务
  3. */
  4. public class InstallService extends IntentService {
  5. private static final String TAG = "InstallService";
  6. private final UpdateEngine mUpdateEngine = new UpdateEngine();
  7. private final SimUpdateEngineCallback mSimUpdateEngineCallback = new SimUpdateEngineCallback();
  8. public static final String EXTRA_PARAM_RESULT_RECEIVER = "result-receiver";
  9. public InstallService() {
  10. super("InstallService");
  11. }
  12. WeakReference<ResultReceiver> resultCallbackWeak;
  13. @Override
  14. protected void onHandleIntent(Intent intent) {
  15. Log.d(TAG, "On handle intent is called");
  16. spec = new PayloadSpec();
  17. ResultReceiver resultCallback = (ResultReceiver)intent.getParcelableExtra(EXTRA_PARAM_RESULT_RECEIVER);
  18. this.resultCallbackWeak = new WeakReference<ResultReceiver>(resultCallback);
  19. Log.d(TAG, "resultCallback = "+resultCallback);
  20. File otaFile = new File(CommonUtils.INSTALL_PATH);
  21. if(otaFile.exists()){
  22. execute(otaFile);
  23. }
  24. }
  25. PayloadSpec spec;
  26. @Override
  27. public void onCreate() {
  28. super.onCreate();
  29. }
  30. @Override
  31. public void onStart(Intent intent, int startId) {
  32. super.onStart(intent, startId);
  33. }
  34. @Override
  35. public android.os.IBinder onBind(Intent intent) {
  36. return null;
  37. }
  38. android.os.Handler mHandler = new android.os.Handler() {
  39. @Override
  40. public void handleMessage(android.os.Message msg) {
  41. }
  42. };
  43. /**
  44. * Starts InstallService.
  45. *
  46. * @param context application context
  47. * @param resultCallback callback that will be called when the update is ready to be installed
  48. */
  49. public static void startService(Context context,
  50. ResultReceiver resultCallback) {
  51. Log.d(TAG, "Starting InstallService");
  52. Intent intent = new Intent(context, InstallService.class);
  53. intent.putExtra(EXTRA_PARAM_RESULT_RECEIVER, resultCallback);
  54. context.startService(intent);
  55. }
  56. private void execute(File file){
  57. // Preconditions.checkArgument(files.length > 0, "No file specified");
  58. try {
  59. UpdateParser.ParsedUpdate result = UpdateParser.parse(file);
  60. if (result == null) {
  61. spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  62. spec.description = "Failed verification";
  63. Log.e(TAG, String.format("Failed verification"));
  64. updateResult(spec);
  65. return;
  66. }
  67. if (!result.isValid()) {
  68. spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  69. spec.description = "file invalid";
  70. Log.e(TAG, String.format("Failed verification %s", result));
  71. updateResult(spec);
  72. return;
  73. }
  74. if (Log.isLoggable(TAG, Log.INFO)) {
  75. Log.i(TAG, result.toString());
  76. }
  77. mUpdateEngine.bind(mSimUpdateEngineCallback, handler);
  78. mUpdateEngine.applyPayload(
  79. result.mUrl, result.mOffset, result.mSize, result.mProps);
  80. } catch (IOException e) {
  81. Log.e(TAG, String.format("For file %s", file), e);
  82. spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  83. spec.description = "IOException";
  84. updateResult(spec);
  85. }
  86. }
  87. /** Handles events from the UpdateEngine. */
  88. public class SimUpdateEngineCallback extends UpdateEngineCallback {
  89. @Override
  90. public void onStatusUpdate(int status, float percent) {
  91. Log.d(TAG, String.format("onStatusUpdate %d, Percent %.2f", status, percent));
  92. switch (status) {
  93. case UpdateEngine.UpdateStatusConstants.IDLE:
  94. //todo ,Update status code: update engine is in idle state.
  95. break;
  96. case UpdateEngine.UpdateStatusConstants.CHECKING_FOR_UPDATE:
  97. //todo ,Update status code: update engine is checking for update.
  98. break;
  99. case 11:
  100. //todo ,i do not know why
  101. break;
  102. case UpdateEngine.UpdateStatusConstants.UPDATE_AVAILABLE:
  103. //todo ,Update status code: an update is available.
  104. break;
  105. case UpdateEngine.UpdateStatusConstants.DOWNLOADING:
  106. Log.d(TAG, "UpdateEngine.UpdateStatusConstants.DOWNLOADING progress = "+percent);
  107. int progress = (int) (percent * 100);
  108. spec.resultCode = MessageContant.FOTA_UPDATE_PROGRESS;
  109. spec.progress = progress;
  110. updateResult(spec);
  111. break;
  112. case UpdateEngine.UpdateStatusConstants.VERIFYING:
  113. //todo ,Update status code: update engine is verifying an update.
  114. break;
  115. case UpdateEngine.UpdateStatusConstants.FINALIZING :
  116. //todo Update status code: update engine is finalizing an update.
  117. break;
  118. case UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT:
  119. spec.resultCode = MessageContant.FOTA_FINISH_REBOOT;
  120. spec.description = "更新成功";
  121. updateResult(spec);
  122. break;
  123. case UpdateEngine.UpdateStatusConstants.REPORTING_ERROR_EVENT:
  124. //出错
  125. // spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  126. // spec.description = "系统更新失败";
  127. // updateResult(spec);
  128. break;
  129. case UpdateEngine.UpdateStatusConstants.ATTEMPTING_ROLLBACK:
  130. //系统回滚
  131. // spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  132. // spec.description = "系统更新失败";
  133. // updateResult(spec);
  134. break;
  135. case UpdateEngine.UpdateStatusConstants.DISABLED:
  136. //update engine disable
  137. break;
  138. default:
  139. // spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  140. // spec.description = "系统更新失败";
  141. // updateResult(spec);
  142. break;
  143. // noop
  144. }
  145. }
  146. @Override
  147. public void onPayloadApplicationComplete(int errorCode) {
  148. Log.w(TAG, String.format("ErrorCodeConstants onPayloadApplicationComplete %d", errorCode));
  149. mUpdateEngine.unbind();
  150. switch(errorCode){
  151. case UpdateEngine.ErrorCodeConstants.SUCCESS:
  152. //todo 升级成功
  153. break;
  154. case UpdateEngine.ErrorCodeConstants.ERROR:
  155. spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  156. spec.description = "ERROR";
  157. updateResult(spec);
  158. break;
  159. case UpdateEngine.ErrorCodeConstants.NOT_ENOUGH_SPACE:
  160. spec.resultCode = MessageContant.FOTA_NOT_ENOUGH_SPACE;
  161. spec.description = "存储空间不足";
  162. updateResult(spec);
  163. break;
  164. case UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR:
  165. spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  166. spec.description = "时间戳不对";
  167. updateResult(spec);
  168. break;
  169. case UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR:
  170. spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  171. spec.description = "hash值不对";
  172. updateResult(spec);
  173. break;
  174. case UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR:
  175. spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  176. spec.description = "大小不对";
  177. updateResult(spec);
  178. break;
  179. case UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR:
  180. spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  181. spec.description = "签名不对";
  182. updateResult(spec);
  183. break;
  184. case UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR:
  185. spec.resultCode = MessageContant.FOTA_FILE_INVAlID;
  186. spec.description = "类型不匹配";
  187. updateResult(spec);
  188. break;
  189. default:
  190. //升级失败
  191. spec.resultCode = MessageContant.FOTA_UPGRADE_FAIL;
  192. spec.description = "系统更新失败";
  193. updateResult(spec);
  194. break;
  195. }
  196. }
  197. }
  198. private Handler handler=new Handler(){
  199. @Override
  200. public void handleMessage(Message msg) {
  201. // switch (msg.what){
  202. // case MessageContant.FOTA_START_UPDATE:
  203. // Log.e(TAG,"FOTA_START_UPDATE");
  204. // break;
  205. // case MessageContant.FOTA_UPDATE_PROGRESS:
  206. // Log.e(TAG,"FOTA_UPDATE_PROGRESS");
  207. // //通过 msg.obj 获取 ProgressBar 的进度,然后显示进度值
  208. // int process = (int) msg.obj;
  209. // break;
  210. // }
  211. super.handleMessage(msg);
  212. }
  213. };
  214. Bundle resultData = new Bundle();
  215. /**
  216. return to UI
  217. */
  218. private void updateResult(PayloadSpec spec){
  219. //非法安装包,则统一删除
  220. if(spec.description != null){
  221. Log.e(TAG, "spec.description = "+spec.description);
  222. }
  223. if(spec.resultCode == MessageContant.FOTA_FILE_INVAlID){
  224. Log.e(TAG, "parse file error");
  225. UpdateUtil.showUpdateResult(getApplicationContext());
  226. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
  227. SharedPreferences.Editor pEdits = sp.edit();
  228. pEdits.putInt ("INSTALL",0);
  229. pEdits.putInt ("DOWNLOADING",0);
  230. pEdits.commit();
  231. }
  232. if(null != resultCallbackWeak.get()){
  233. Log.i(TAG,"updateResult");
  234. resultData.putSerializable("result",spec);
  235. resultCallbackWeak.get().send(0, resultData);
  236. }
  237. }
  238. }

PayloadSpec.java

  1. public class PayloadSpec implements Serializable {
  2. private static final long serialVersionUID = 41043L;
  3. /**
  4. 返回结果码
  5. */
  6. public int resultCode;
  7. /**
  8. 描述
  9. */
  10. public String description;
  11. /**
  12. 进度条
  13. */
  14. public int progress;
  15. }

只需调用

/**

mResultReceiver 为ResultReceiver 实例

*/

InstallService.startService(mContext,mResultReceiver);

我们主要关注几个状态码

 case UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT:

升级成功,提示您是否重启
public void onPayloadApplicationComplete(int errorCode) 
中的      

case UpdateEngine.ErrorCodeConstants.SUCCESS:
提示升级完成,

升级失败的,主要监听这里面的,这个是最终状态.             

OTA 包

整包升级

  make otapackage

out/target/product/(device)/(device)ota-eng.xss.zip

编译出整包,优点是只保留最新版本就可以了,缺点是下载包太大.

差分包

差分包顾名思义,就是两个版本的差异,通过以下命令.
./build/make/tools/releasetools/ota_from_target_files.py -v -p ./out/host/linux-x86/ -i old-target-files.zip new-target-files.zip  fota.zip

old-target-files.zip

new-target-files.zip 这两个target 包,整编后位置在out/disk 下面

或者out/target/product/(device)/obj/PACKAGING/target_files_intermediates

差分包升级缺点是每次都得备份target 包,优点是差分包比较小,节约流量;

命令升级

为了验证差分包是否ok, 可以先通过命令升级,看看是否ok.

制作完差分包后 adb shell ,输入下面命令

update_engine_client --payload=file:///sdcard/payload.bin --update --headers="
FILE_HASH=osa/esLUKk6WPwoZkIehvz4JB/PVO7j15DEBaHKhr/s=
FILE_SIZE=192242848
METADATA_HASH=pdqbMcHlZpKSPll7XRMUz8wN/demqmgRy3fOIiPpQ50=
METADATA_SIZE=95795"

注意换行,标注红色部分的,需要手动修改对应差分包里面的信息,解压差分包得到下面信息

打开payload_properties.txt 就能看到上面需要的参数信息了,对应填上就可以了,

注意把payload.bin push 到设置升级的位置,我这边选择的是file:///sdcard/payload.bin

错误分析

1,针对http 下载的文件,一定要主要文件权限问题,我这边下载到/data/ota_package/ 后发现文件权限变成,-rw-------  1 system system ,这样升级就会遇到权限问题,其他分组的就没有读写权限.

修改方式:修改下载文件的权限,注意一点要下载之前,就修改,下载后修改就不行了.

  1. public static void changemode(File destFile){
  2. try{
  3. if(!destFile.exists()){
  4. destFile.createNewFile();
  5. }
  6. /* file.createNewFile();*/
  7. destFile.setWritable(Boolean.TRUE);
  8. destFile.setReadable (Boolean.TRUE);
  9. String command = "chmod 666 " + destFile.getAbsolutePath();
  10. Log.i(TAG, "command = " + command);
  11. Runtime runtime = Runtime.getRuntime();
  12. Process proc = runtime.exec(command);
  13. } catch (IOException e) {
  14. Log.i(TAG,"chmod fail!!!!");
  15. e.printStackTrace();
  16. }
  17. }

2,刚开始升级会抛出状态码

onStatusUpdate

2  11  3

其中11 ,我查了值是大小不匹配,我以为是异常情况,认为安装包不对,直接删除了,导致后面更新进行不下去了,莫名其妙

所以onStatusUpdate  中的状态码,我们主要关注reboot 的,即可,

onPayloadApplicationComplete 中返回的,才是真正是否升级成功状态.

参考system/update_engine/common/error_code.h 中状态码

 

注意事项

1,android 默认升级文件是放在/data/ota_package/,建议保留,但是data 分区随着时间的推移,空间可能不够了,所以,也可以创建一个单独的分区放升级文件 eg:/fota/

2,测试的时候一定要把selinux 关掉,adb shell setenforce 0,可以专注升级流程是否ok,最后通过dmesg ,把权限都加上.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/576271
推荐阅读
相关标签
  

闽ICP备14008679号