一、升级包制作流程
1.1、编译一个完整刷机包

1.2、制作OTA包

在android目录下执行 make otapackage –j8 编译OTA包。以450平台为例,编译后会在

out\target\product\**\ 目录下生成一个升级整包 msm8953_64-ota-*.zip
out\target\product\**\obj\PACKAGING\target_files_intermediates目录下生成一个基础包

整包:是可以直接用于OTA升级使用,相当于整个替换机器image。优点是操作简单,不容易出问题。缺点是内存很大。

基础包:是用于制作差分包。里面包含所有支持升级的image文件。将新旧两个基础包,使用系统自带脚本进行对比差分操作后,就会按照新旧的差异点生成一个差分包。

差分包:是只有包含差点点信息和差异patch的。所以使用差分包升级,一定要保证机器里面当前的image和制作差分的基础包里面的image完全一致。否则肯定升级失败。

二、Recovery 升级模式
Recovery升级是原生默认的升级方式,后面某些高端平台可能会默认废弃这种方式。升级基于recovery分区进行,对image进行升级操作。这种升级方式,优点是节省ROM,缺点是如果发生意外导致系统image损坏,机器就可能变砖了。

debug模式下调试OTA方法
执行如下 adb 指令进行升级操作

adb root
 adb remount
 adb push update.zip /data/update.zip
 adb shell
 uncrypt /data/update.zip /cache/recovery/block.map
 echo "--update_package=@/cache/recovery/block.map" > /cache/recovery/command
 adb reboot recovery

注:7.0之前系统没有uncrypt,将升级包push到/cache/update.zip,再执行如下指令

adb root 
 adb remount
 adb push update.zip / cache /update.zip
 adb shell
 echo " --update_package=/cache/update.zip " > /cache/recovery/command
 adb reboot recovery


user版本需要可以使用系统API接口
方法一:使用系统自带API相关接口文件叫 android.os.RecoverySystem,调用其 installPackage 等方法即可。注意权限需要是系统APP才可以。

方法二:adb reboot recovery 进入到 recovery 界面,有如下两项

Apply update from ADB
Apply update from SD Card

这两种都可以升级,选择后根据界面提示操作即可。

备注:可能有些平台没有这个界面功能。

三、A/B分区模式升级
AB升级模式应该是后续主要升级模式。原理就是在ROM中分了两个区域,各自有一套image。优点是更安全,即使是一套分区出问题了,仍然有一套可以使用,机器不会变砖。缺点就是多一套分区导致ROM占用更多。

debug模式下调试

adb root
 adb remount
 adb push update.zip data/ota_package/update.zip
 adb shell
 update_engine_client --update --follow --payload=file:///data/ota_package/update.zip --offset=7919 --size=724346 --headers="FILE_HASH=1RilNlJjIRfwwd8t86YucEhJV67msLzETHprS+HcWyg=
 FILE_SIZE=724346
 METADATA_HASH=vQsISDOuPUCkczn6FizKVHV8mkF8YqNYnNi21l6Du9o=
 METADATA_SIZE=440778

"
update_engine_client后面的参数根据OTA包生成。表示OTA包的参数。新建ota.py,将下面代码复制过去,再执行python脚本 python ota.py update.zip 即会生成。

#!/usr/bin/env python
 import sys
 import zipfile
  
 def main():
          if len(sys.argv) != 2:
                    sys.stderr.write('Use: %s <ota_file.zip>\n' % sys.arv[0])
                    return 1
  
          otazip = zipfile.ZipFile(sys.argv[1], 'r')
          payload_info = otazip.getinfo('payload.bin')
          payload_offset = payload_info.header_offset + len(payload_info.FileHeader())
          payload_size = payload_info.file_size
          payload_location = '/data/ota_package/update.zip'
          headers = otazip.read('payload_properties.txt')
          print (
          'update_engine_client --update --follow --payload=file://{payload_location}'
          ' --offset={payload_offset} --size={payload_size}'
          ' --headers="{headers}"').format(**locals())
          return 0
  
 if __name__ == '__main__':
          sys.exit(main())


执行update_engine_client指令后,等待升级完成,窗口有如下相关提示,表示升级成功。

[INFO:update_engine_client_android.cc(90)] onStatusUpdate(UPDATE_STATUS_UPDATED_NEED_REBOOT (6), 0)
 [INFO:update_engine_client_android.cc(98)] onPayloadApplicationComplete(ErrorCode::kSuccess (0))

user版本调试
需要使用系统相关接口,android.os.UpdateEngine android.os.UpdateEngineCallback。

UpdateEngine.applyPayload  升级接口

UpdateEngineCallback是升级监听,如下:

 

class MyUpdateEngineCallback extends UpdateEngineCallback {
         @Override
         public void onStatusUpdate(int status, float percent) {
             Log.i(TAG, "percent:" + percent);
             if (percent > 0)
                 mInfoTextView.setText("升级进度:" + percent);
         }
  
         @Override
         public void onPayloadApplicationComplete(int errorCode) {
             Log.i(TAG, "errorCode:" + errorCode);
             if (errorCode == 0) {
                 handler.sendEmptyMessage(UPDATE_SUCCESS);
             } else {
                 Message msg = new Message();
                 msg.what = UPDATE_ERROR;
                 Bundle bundle = new Bundle();
                 bundle.putInt("errorCode", errorCode);
                 msg.setData(bundle);
                 handler.sendMessage(msg);
             }
             if (mUpdateEngine != null) {
                 mUpdateEngine.unbind();
             }
         }
     }

常见调试问题
a、如何确认AB升级当前分区
通过查看 ro.boot.slot_suffix 属性值的方式,这个属性值显示当前使用分区名

b、如何手动切换分区
通过进入bootloader 模式,指定启动槽值来选择下次启动的分区,按照如下指令即可

adb reboot bootloader
fastboot --set-active=a
fastboot reboot

c、如何判断失败原因
升级结束时,可以看到有个错误码,常见错误码如下,按照错误码去查找可能原因

ErrorCode::kSuccess (0) 升级成功
ErrorCode::kSuccess (1) 升级失败
ErrorCode::kFilesystemCopierError (4) 未知,暂时未使用的错误码
ErrorCode::kPostinstallRunnerError (5) 升级安装结束,设置启动分区失败
ErrorCode::kPayloadMismatchedType (6) 升级包的升级类型不匹配或升级包minor version不兼容
ErrorCode::kInstallDeviceOpenError (7) 无法启动升级。可能是原因:分区错误,设备支持升级的分区和升级包内的不匹配;设备处于disable-verity状态;
ErrorCode::kKernelDeviceOpenError (8) 未知,暂时未使用的错误码
ErrorCode::kDownloadTransferError (9) w,找不到升级包
ErrorCode::kPayloadHashMismatchError (10) FILE_HASH值不匹配
ErrorCode::kPayloadSizeMismatchError (11) 数据size不匹配
ErrorCode::kDownloadPayloadVerificationError (12) 签名验证失败
ErrorCode::kDownloadStateInitializationError (20) 升级包写入时失败  一般都是发生在差分包升级时,检测boot、system、vendor的hash值不匹配。
ErrorCode::kDownloadInvalidMetadataMagicString (21)  未找到正确bin文件,一般是offset不对导致
ErrorCode::kDownloadInvalidMetadataSize (32) METADATA_SIZE值不匹配
ErrorCode::kPayloadTimestampError (51) 升级包的date比机器当前版本早
system/update_engine/update_engine_client_android.cc