Android从7.0开始引入新的OTA升级方式,A/B System Updates,这里将其叫做A/B系统,涉及的内容较多,分多篇对A/B系统的各个方面进行分析。本文为第四篇,系统的启动和升级。

本文为洛奇看世界(guyongqiangx)原创,转载请注明出处。
文章链接:https://blog.csdn.net/guyongqiangx/article/details/72604355

Android A/B 系统基础入门系列《Android A/B 系统》已完结,文章列表:

  • Android A/B System OTA分析(一)概览
  • Android A/B System OTA分析(二)系统image的生成
  • Android A/B System OTA分析(三)主系统和bootloader的通信
  • Android A/B System OTA分析(四)系统的启动和升级
  • Android A/B System OTA分析(五)客户端参数
  • Android A/B System OTA分析(六)如何获取 payload 的 offset 和 size

更多关于 Android OTA 升级相关文章,请参考《Android OTA 升级系列专栏文章导读》。

本文基于AOSP 7.1.1_r23 (NMF27D)代码进行分析。

1. 系统的启动

1.1 bootloader检查slot metadata

系统复位后,bootloader会去读取boot_control私有的存储数slot metadata并进行解析,以此确定从哪一个slot启动。
以下是Android官方的一个bootloader加载流程图:

大致启动流程如下:

  1. 系统启动后,bootloader读取分区元数据slot metadata
  2. 检查分区元数据中是否有可启动的分区,如果没有可启动分区,直接进入bootloaderrecovery mode(即bootloader下的刷机模式),一般是进入fastboot命令行;
  3. 如果分区元数据中有可启动的分区,则选择所有可启动分区中优先级最高的slot(例如,直接选择当前设置为active的分区);
  4. 检查所选择分区的retry countretry count表示当前分区可以尝试启动的次数);
  5. 如果当前选择分区的retry count为0,且没有启动成功(启动成功的分区会标记为successful),则将所选择分区标记为无效分区(通常设置为unbootable),然后重复第2步,查找下一个可以启动的分区;
  6. 如果当前选择的分区尝试启动次数retry count不为0,则表示还可以继续尝试从当前分区启动,需要将其retry count进行递减,然后加载相应的slot进行启动;

1.2 linux系统的启动

上一步中,bootloader会根据slot metadata确定读取哪一个slotboot分区进行启动。

每一个slot上有两个rootfs

  • boot分区自带recovery moderamdisk;
  • system分区包含了Android系统的rootfs;

启动中,如何选择加载boot分区的ramdisk还是system分区的rootfs呢?
答案是由kernel的命令行参数skip_initramfs来决定。

下面来看skip_initramfs参数是如何起作用的。

系统同时包含init\noinitramfs.cinit\initramfs.c的代码,并在initramfs.c模块中定义并解析skip_initramfs参数:

# init\initramfs.c
static int __initdata do_skip_initramfs;static int __init skip_initramfs_param(char *str)
{if (*str)return 0;# 设置do_skip_initramfs标志do_skip_initramfs = 1;return 1;
}
# 用于解析命令行的`skip_initramfs`参数
__setup("skip_initramfs", skip_initramfs_param);

如果命令行设置了skip_initramfs,则do_skip_initramfs会被设置为1。

linux调用populate_rootfs默认会并加载boot分区自带的ramdisk,但如果do_skip_initramfs
设置为1,则调用default_rootfs生成一个极小的rootfs

# init\initramfs.c
static int __init populate_rootfs(void)
{char *err;# 如果do_skip_initramfs置1,则调用default_rootfs生成一个极小的rootfsif (do_skip_initramfs)return default_rootfs();# 没有设置do_skip_initramfs的情况下,才会解析并加载`boot`分区所包含的`ramdisk`err = unpack_to_rootfs(__initramfs_start, __initramfs_size);if (err)panic("%s", err); /* Failed to decompress INTERNAL initramfs */...return 0;
}

default_rootfs的内容很简单,用于在内存中生成一个极小的rootfs,仅包含/devroot两个文件夹以及一个设备节点/dev/console

# init\noinitramfs.c
/** Create a simple rootfs that is similar to the default initramfs*/
static int __init default_rootfs(void)
{int err;# 创建/dev文件夹用于存放/dev/console设备节点err = sys_mkdir((const char __user __force *) "/dev", 0755);if (err < 0)goto out;# 创建/dev/console设备节点err = sys_mknod((const char __user __force *) "/dev/console",S_IFCHR | S_IRUSR | S_IWUSR,new_encode_dev(MKDEV(5, 1)));if (err < 0)goto out;# 创建/root目录,作为根用户root的homeerr = sys_mkdir((const char __user __force *) "/root", 0700);if (err < 0)goto out;return 0;out:printk(KERN_WARNING "Failed to create a rootfs\n");return err;
}

因此skip_initramfs参数决定了加载哪一个rootfs,进入哪一个系统。

  • 加载android系统的命令行参数
    skip_initramfs rootwait ro init=/init root="/dev/dm-0 dm=system none ro,0 1 android-verity <public-key-id> <path-to-system-partition>"
    例如Broadcom7252SSFFDR3参考平台的启动Android系统的参数为:

      mem=1016m@0m mem=1024m@2048m bmem=339m@669m bmem=237m@2048m  \brcm_cma=784m@2288m \ramoops.mem_address=0x3F800000 ramoops.mem_size=0x800000 ramoops.console_size=0x400000 \buildvariant=userdebug \veritykeyid=id:7e4333f9bba00adfe0ede979e28ed1920492b40f buildvariant=eng \rootwait init=/init ro \root=/dev/dm-0 dm="system none ro,0 1 android-verity PARTUUID=c49e0acb-1b38-95e5-548a-2b7260e704a4" skip_initramfs
    

    除去rootfs不相关的参数,看起来是这样的:
    rootwait init=/init ro root=/dev/dm-0 dm="system none ro,0 1 android-verity PARTUUID=c49e0acb-1b38-95e5-548a-2b7260e704a4" skip_initramfs

命令行中,文件系统的root设备由参数root="/dev/dm-0 dm=system none ro,0 1 android-verity <public-key-id> <path-to-system-partition>"指定,显然,这里的root参数设置将设备名设置为/dev/dm-0,至于设备/dev/dm-0是一个什么设备,作用为何,属于另一个话题dm-verity,此处不再展开讨论。

  • 加载recovery系统的命令行参数
    rootwait init=/init ro
    例如Broadcom7252SSFFDR3参考平台的启动Recovery的参数为:

      mem=1016m@0m mem=1024m@2048m bmem=339m@669m bmem=237m@2048m \brcm_cma=784m@2288m \ramoops.mem_address=0x3F800000 ramoops.mem_size=0x800000 ramoops.console_size=0x400000 \rootwait init=/init ro \buildvariant=userdebug veritykeyid=id:7e4333f9bba00adfe0ede979e28ed1920492b40f buildvariant=eng
    

    除去rootfs不相关的参数,看起来是这样的:
    rootwait init=/init ro

默认linux是不支持参数skip_initramfs参数的,Android系统中为了跳过boot分区的ramdisk,引入了这个命令行参数,参考以下提交:

https://android-review.googlesource.com/#/c/158491/ [initramfs: Add skip_initramfs command line option]

1.3 Android系统的启动

linux启动后,通过dm-verify机制校验system分区,完成后加载system分区内包含的rootfs,通过/init程序解析/init.rc脚本,完成Android系统的启动。

这部分的启动过程跟传统的系统启动是一样的。

1.4 Recovery系统的启动

linux启动后,根据参数,加载boot分区的ramdisk,通过/init程序解析/init.rc脚本,启动Recovery系统。

这部分的启动过程跟传统的Recovery系统启动是一样的。

2. 系统的升级

A/B系统升级包的制作方式跟传统系统升级包制作方式基本一致,主要分为两步:

  1. 编译系统文件
  2. 制作升级包

升级方式根据升级包的内容分为两种:

  1. 完整升级,升级包包含完整的系统,对内容进行全新升级;
  2. 增量升级/差分升级,升级包仅包含跟当前系统不一样的内容,对系统进行打补丁式升级;

2.1 完整升级

  1. 升级包的制作
  • 第一步,编译系统

     $ source build/envsetup.sh$ lunch bcm7252ssffdr4-userdebug$ mkdir dist_output$ make -j32 dist DIST_DIR=dist_output[...]$ ls -lh dist-output/*target_files*-rw-r--r-- 1 ygu users 566M May 21 14:49 bcm7252ssffdr4-target_files-eng.ygu.zip
    
  • 第二步,制作安装包

     $ ./build/tools/releasetools/ota_from_target_files dist-output/bcm7252ssffdr4-target_files-eng.ygu.zip full-ota.zip$ ls -lh dist-output-rw-r--r-- 1 ygu users 270M May 21 14:51 full-ota.zip
    
  1. 升级包的内容

    解压缩full-ota.zip可以看到其内容:

     $ mkdir full-ota$ unzip full-ota.zip -d full-otaArchive:  full-ota.zipsigned by SignApkextracting: full-ota/payload.bin    inflating: full-ota/META-INF/com/android/metadata  inflating: full-ota/care_map.txt   inflating: full-ota/payload_properties.txt  inflating: full-ota/META-INF/com/android/otacert  inflating: full-ota/META-INF/MANIFEST.MF  inflating: full-ota/META-INF/CERT.SF  inflating: full-ota/META-INF/CERT.RSA  $ ls -lh full-otatotal 270Mdrwxr-sr-x 3 ygu users 4.0K May 21 18:14 META-INF-rw-r--r-- 1 ygu users   36 Jan  1  2009 care_map.txt-rw-r--r-- 1 ygu users 270M Jan  1  2009 payload.bin-rw-r--r-- 1 ygu users  154 Jan  1  2009 payload_properties.txt$ tree -l full-otafull-ota|-- META-INF|   |-- CERT.RSA|   |-- CERT.SF|   |-- MANIFEST.MF|   `-- com|       `-- android|           |-- metadata|           `-- otacert|-- care_map.txt|-- payload.bin`-- payload_properties.txt3 directories, 8 files
    

    其中,payload.bin是系统要更新的数据文件,payload_properties.txt包含了升级内容的一些属性信息,如下:

     $ cat full-ota/payload_properties.txt FILE_HASH=ozGgyQEcnkI5ZaX+Wbjo5I/PCR7PEZka9fGd0nWa+oY=FILE_SIZE=282164983METADATA_HASH=GLIKfE6KRwylWMHsNadG/Q8iy5f7ENWTatvMdBlpoPg=METADATA_SIZE=21023
    

    升级时会使用到payload_properties.txt里面的信息。

  2. 系统包的使用

    A/B系统在debug模式下会包含升级应用update_engine_client,其参数如下:

     bcm7252ssffdr4:/ # which update_engine_client/system/bin/update_engine_clientbcm7252ssffdr4:/ # update_engine_client --help Android Update Engine Client--cancel  (Cancel the ongoing update and exit.)  type: bool  default: false--follow  (Follow status update changes until a final state is reached. Exit status is 0 if the update succeeded, and 1 otherwise.)  type: bool  default: false--headers  (A list of key-value pairs, one element of the list per line. Used when --update is passed.)  type: string  default: ""--help  (Show this help message)  type: bool  default: false--offset  (The offset in the payload where the CrAU update starts. Used when --update is passed.)  type: int64  default: 0--payload  (The URI to the update payload to use.)  type: string  default: "http://127.0.0.1:8080/payload"--reset_status  (Reset an already applied update and exit.)  type: bool  default: false--resume  (Resume a suspended update.)  type: bool  default: false--size  (The size of the CrAU part of the payload. If 0 is passed, it will be autodetected. Used when --update is passed.)  type: int64  default: 0--suspend  (Suspend an ongoing update and exit.)  type: bool  default: false--update  (Start a new update, if no update in progress.)  type: bool  default: false
    

    payload.bin文件放到可以通过http访问的地方,然后在命令行调用update_engine_client进行升级:

     bcm7252ssffdr4:/ # update_engine_client \--payload=http://stbszx-bld-5/public/android/full-ota/payload.bin \--update \--headers="FILE_HASH=ozGgyQEcnkI5ZaX+Wbjo5I/PCR7PEZka9fGd0nWa+oY= FILE_SIZE=282164983METADATA_HASH=GLIKfE6KRwylWMHsNadG/Q8iy5f7ENWTatvMdBlpoPg= METADATA_SIZE=21023 "
    

    其中headers选项需要填写payload_properties.txt文件的内容。

    [2019/08/13] 补充一点,这里update_engine_client--headers=参数最终是按行进行拆分提取的,所以需要将--headers=的每个参数分别写到一行上,然后全部参数用双引号包含,否则可能会出现无法正常解析headers参数导致无法正常执行的情况。

2.2 增量升级/差分升级

差分包升级与完整包升级除了升级包的制作不一样之外,生成的升级包文件内容一样,使用update_engine_client进行升级的操作也完全一样,因此这里仅说明差分包的制作。

差分升级包的制作

  • 第一步,对android进行改动并编译系统

    差分升级时,需要保留原有系统的生成文件,然后修改后生成新的系统文件,这里假定原有系统生成文件位于:dist_output,修改后生成的系统文件位于dist_output-new,编译方式跟完整包的生成方式一样。

    $ source build/envsetup.sh
    $ lunch bcm7252ssffdr4-userdebug
    $ mkdir dist_output
    $ make -j32 dist DIST_DIR=dist_output[...]
    $ ls -lh dist-output-new/*target_files*
    -rw-r--r-- 1 ygu users 566M May 21 15:27 bcm7252ssffdr4-target_files-eng.ygu.zip
    
  • 第二步,制作安装包

    对比原有系统文件和修改后的系统文件生成差分包,这里通过-i指定差分包生成的基线(baseline)。

    $./build/tools/releasetools/ota_from_target_files \
    -i dist-output/bcm7252ssffdr4-target_files-eng.ygu.zip \
    dist-output-new/bcm7252ssffdr4-target_files-eng.ygu.zip \
    incremental-ota.zip
    

2.3 升级日志样本

调用update_engine_client进行升级后可以通过logcat查看其升级日志,如:

bcm7252ssffdr4:/ # update_engine_client \
--payload=http://stbszx-bld-5/public/android/full-ota/payload.bin \
--update \
--headers="\FILE_HASH=ozGgyQEcnkI5ZaX+Wbjo5I/PCR7PEZka9fGd0nWa+oY= FILE_SIZE=282164983 METADATA_HASH=GLIKfE6KRwylWMHsNadG/Q8iy5f7ENWTatvMdBlpoPg= METADATA_SIZE=21023
"
bcm7252ssffdr4:/ #
bcm7252ssffdr4:/ # logcat -s update_engine:v
--------- beginning of main
--------- beginning of system
I update_engine: [INFO:main.cc(113)] Chrome OS Update Engine starting
I update_engine: [INFO:boot_control_android.cc(78)] Loaded boot_control HAL 'boot control hal for bcm platform' version 0.1 authored by 'Broadcom'.
I update_engine: [INFO:daemon_state_android.cc(43)] Booted in dev mode.
I update_engine: [INFO:update_attempter_android.cc(199)] Using this install plan:
I update_engine: [INFO:install_plan.cc(71)] InstallPlan: new_update, payload type: unknown, source_slot: A, target_slot: B, url: http://stbszx-bld-5/public/android/full-ota/payload.bin, payload size: 282164983, payload hash: ozGgyQEcnkI5ZaX+Wbjo5I/PCR7PEZka9fGd0nWa+oY=, metadata size: 21023, metadata signature: , hash_checks_mandatory: true, powerwash_required: false
W update_engine: [WARNING:hardware_android.cc(126)] STUB: Assuming OOBE is complete.
I update_engine: [INFO:cpu_limiter.cc(71)] Setting cgroup cpu shares to  2
E update_engine: [ERROR:utils.cc(199)] 0 == writer.Open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600) failed: No such file or directory
E update_engine: [ERROR:cpu_limiter.cc(74)] Failed to change cgroup cpu shares to 2 using /sys/fs/cgroup/cpu/update-engine/cpu.shares
I update_engine: [INFO:update_attempter_android.cc(379)] Marking booted slot as good.
I update_engine: [INFO:update_attempter_android.cc(394)] Scheduling an action processor start.
I update_engine: [INFO:action_processor.cc(46)] ActionProcessor: starting InstallPlanAction
I update_engine: [INFO:action_processor.cc(116)] ActionProcessor: finished InstallPlanAction with code ErrorCode::kSuccess
I update_engine: [INFO:action_processor.cc(143)] ActionProcessor: starting DownloadAction
I update_engine: [INFO:install_plan.cc(71)] InstallPlan: new_update, payload type: unknown, source_slot: A, target_slot: B, url: http://stbszx-bld-5/public/android/full-ota/payload.bin, payload size: 282164983, payload hash: ozGgyQEcnkI5ZaX+Wbjo5I/PCR7PEZka9fGd0nWa+oY=, metadata size: 21023, metadata signature: , hash_checks_mandatory: true, powerwash_required: false
I update_engine: [INFO:download_action.cc(178)] Marking new slot as unbootable
I update_engine: [INFO:multi_range_http_fetcher.cc(45)] starting first transfer
I update_engine: [INFO:multi_range_http_fetcher.cc(73)] starting transfer of range 0+282164983
I update_engine: [INFO:libcurl_http_fetcher.cc(94)] Starting/Resuming transfer
I update_engine: [INFO:libcurl_http_fetcher.cc(106)] Using proxy: no
I update_engine: [INFO:libcurl_http_fetcher.cc(237)] Setting up curl options for HTTP
I update_engine: [INFO:delta_performer.cc(196)] Completed 0/? operations, 14169/282164983 bytes downloaded (0%), overall progress 0%
I update_engine: [INFO:delta_performer.cc(536)] Manifest size in payload matches expected value from Omaha
I update_engine: [INFO:delta_performer.cc(1396)] Verifying metadata hash signature using public key: /etc/update_engine/update-payload-key.pub.pem
I update_engine: [INFO:payload_verifier.cc(93)] signature blob size = 264
I update_engine: [INFO:payload_verifier.cc(112)] Verified correct signature 1 out of 1 signatures.
I update_engine: [INFO:delta_performer.cc(1439)] Metadata hash signature matches value in Omaha response.
I update_engine: [INFO:delta_performer.cc(1459)] Detected a 'full' payload.
I update_engine: [INFO:delta_performer.cc(374)] PartitionInfo old boot sha256:  size: 0
I update_engine: [INFO:delta_performer.cc(374)] PartitionInfo new boot sha256: dZpLY9KsQYa2B14B0oBzfUKxVFIH7ocbgT70JavheSc= size: 19480576
I update_engine: [INFO:delta_performer.cc(374)] PartitionInfo old system sha256:  size: 0
I update_engine: [INFO:delta_performer.cc(374)] PartitionInfo new system sha256: kFXbYzaM47PifNjuL+Plz1zTMEp1MoajOuXZuCh9yw0= size: 769654784
I update_engine: [INFO:delta_performer.cc(359)] Applying 10 operations to partition "boot"
I update_engine: [INFO:delta_performer.cc(647)] Starting to apply update payload operations
I update_engine: [INFO:delta_performer.cc(359)] Applying 367 operations to partition "system"
I update_engine: [INFO:delta_performer.cc(196)] Completed 23/377 operations (6%), 40302425/282164983 bytes downloaded (14%), overall progress 10%
I update_engine: [INFO:delta_performer.cc(196)] Completed 48/377 operations (12%), 79017817/282164983 bytes downloaded (28%), overall progress 20%
I update_engine: [INFO:delta_performer.cc(196)] Completed 74/377 operations (19%), 118519641/282164983 bytes downloaded (42%), overall progress 30%
I update_engine: [INFO:delta_performer.cc(196)] Completed 98/377 operations (25%), 158021465/282164983 bytes downloaded (56%), overall progress 40%
I update_engine: [INFO:delta_performer.cc(196)] Completed 121/377 operations (32%), 192001881/282164983 bytes downloaded (68%), overall progress 50%
I update_engine: [INFO:delta_performer.cc(196)] Completed 145/377 operations (38%), 231389017/282164983 bytes downloaded (82%), overall progress 60%
I update_engine: [INFO:delta_performer.cc(196)] Completed 171/377 operations (45%), 270890841/282164983 bytes downloaded (96%), overall progress 70%
I update_engine: [INFO:delta_performer.cc(196)] Completed 242/377 operations (64%), 273413977/282164983 bytes downloaded (96%), overall progress 80%
I update_engine: [INFO:delta_performer.cc(196)] Completed 317/377 operations (84%), 273430361/282164983 bytes downloaded (96%), overall progress 90%
I update_engine: [INFO:delta_performer.cc(196)] Completed 377/377 operations (100%), 282164983/282164983 bytes downloaded (100%), overall progress 100%
I update_engine: [INFO:delta_performer.cc(1336)] Extracted signature data of size 264 at 282143432
I update_engine: [INFO:multi_range_http_fetcher.cc(111)] terminating transfer
I update_engine: [INFO:multi_range_http_fetcher.cc(171)] Received transfer terminated.
I update_engine: [INFO:multi_range_http_fetcher.cc(123)] TransferEnded w/ code 206
I update_engine: [INFO:multi_range_http_fetcher.cc(157)] Done w/ all transfers
I update_engine: [INFO:delta_performer.cc(1596)] Verifying payload using public key: /etc/update_engine/update-payload-key.pub.pem
I update_engine: [INFO:payload_verifier.cc(93)] signature blob size = 264
I update_engine: [INFO:payload_verifier.cc(112)] Verified correct signature 1 out of 1 signatures.
I update_engine: [INFO:delta_performer.cc(1633)] Payload hash matches value in payload.
I update_engine: [INFO:action_processor.cc(116)] ActionProcessor: finished DownloadAction with code ErrorCode::kSuccess
I update_engine: [INFO:action_processor.cc(143)] ActionProcessor: starting FilesystemVerifierAction
I update_engine: [INFO:filesystem_verifier_action.cc(157)] Hashing partition 0 (boot) on device /dev/block/by-name/boot_e
I update_engine: [INFO:filesystem_verifier_action.cc(248)] Hash of boot: dZpLY9KsQYa2B14B0oBzfUKxVFIH7ocbgT70JavheSc=
I update_engine: [INFO:filesystem_verifier_action.cc(157)] Hashing partition 1 (system) on device /dev/block/by-name/system_e
I update_engine: [INFO:filesystem_verifier_action.cc(248)] Hash of system: kFXbYzaM47PifNjuL+Plz1zTMEp1MoajOuXZuCh9yw0=
I update_engine: [INFO:action_processor.cc(116)] ActionProcessor: finished FilesystemVerifierAction with code ErrorCode::kSuccess
I update_engine: [INFO:action_processor.cc(143)] ActionProcessor: starting PostinstallRunnerAction
I update_engine: [INFO:postinstall_runner_action.cc(341)] All post-install commands succeeded
I update_engine: [INFO:action_processor.cc(116)] ActionProcessor: finished last action PostinstallRunnerAction with code ErrorCode::kSuccess
I update_engine: [INFO:update_attempter_android.cc(282)] Processing Done.
I update_engine: [INFO:update_attempter_android.cc(291)] Update successfully applied, waiting to reboot.

以上logcat信息已经去掉了时间戳,原始的log信息请参考:update_engine_client log

update_engine更新操作成功后会提示Update successfully applied, waiting to reboot.,要求系统进行重启,重启后会设置相应分区slot的属性为successful,表明系统能够成功启动。

重启系统,检查Android系统的编译版本和时间戳,验证升级是否成功。

3. 其它

到目前为止,我写过 Android OTA 升级相关的话题包括:

  • 基础入门:《Android A/B 系统》系列
  • 核心模块:《Android Update Engine 分析》 系列
  • 动态分区:《Android 动态分区》 系列
  • 虚拟 A/B:《Android 虚拟 A/B 分区》系列
  • 升级工具:《Android OTA 相关工具》系列

更多这些关于 Android OTA 升级相关文章的内容,请参考《Android OTA 升级系列专栏文章导读》。

如果您已经订阅了动态分区和虚拟分区付费专栏,请务必加我微信,备注订阅账号,拉您进“动态分区 & 虚拟分区专栏 VIP 答疑群”。我会在方便的时候,回答大家关于 A/B 系统、动态分区、虚拟分区、各种 OTA 升级和签名的问题。

除此之外,我有一个 Android OTA 升级讨论群,里面现在有 400+ 朋友,主要讨论手机,车机,电视,机顶盒,平板等各种设备的 OTA 升级话题,如果您从事 OTA 升级工作,欢迎加群一起交流,请在加我微信时注明“Android OTA 讨论组”。此群仅限 Android OTA 开发者参与~

公众号“洛奇看世界”后台回复“wx”获取个人微信。

Android A/B System OTA分析(四)系统的启动和升级相关推荐

  1. Android A/B System OTA分析(三)主系统和bootloader的通信

    Android从7.0开始引入新的OTA升级方式,A/B System Updates,这里将其叫做A/B系统,涉及的内容较多,分多篇对A/B系统的各个方面进行分析.本文为第三篇,主系统和bootlo ...

  2. Android A/B System OTA分析(六)如何获取 payload 的 offset 和 size

    本文为洛奇看世界(guyongqiangx)原创,转载请注明出处. 文章链接:https://blog.csdn.net/guyongqiangx/article/details/72604355 A ...

  3. Android A/B System OTA分析(五)客户端参数

    本文为洛奇看世界(guyongqiangx)原创,转载请注明出处. 文章链接:https://blog.csdn.net/guyongqiangx/article/details/122430246 ...

  4. Android A/B System OTA分析(一)概览

    本文为洛奇看世界(guyongqiangx)原创,转载请注明出处. 文章链接:https://blog.csdn.net/guyongqiangx/article/details/71334889 A ...

  5. android动态分区AB升级,Android A/B System OTA分析(四)系统的启动和升级

    Android从7.0开始引入新的OTA升级方式,A/B System Updates,这里将其叫做A/B系统,涉及的内容较多,分多篇对A/B系统的各个方面进行分析.本文为第四篇,系统的启动和升级. ...

  6. linux启动过程剖析,分析Linux系统的启动过程

    导读 一直使用linux系统,却对系统启动过程及系统初始化和各种服务的启动不太清楚.今天终于搞明白整个是怎么一回事了.本来想自己写篇文章,刚好在网上看到一篇不错的介绍,很详细,就直接拿来了. Linu ...

  7. Android 8.1 PowerManagerService分析(四)——亮屏流程分析

    欢迎大家关注我的掘金帐号 我会在那里定期更新最新版本的Android Framework源码分析! 相关文章: [Android Framework] 8.1 PowerManagerService分 ...

  8. Android OTA 相关工具(四) 查看 payload 文件信息

    文章目录 1. payload_info.py 的使用 1. 环境 2. 帮助信息 2. 查看 payload 文件信息 1. 不带选项查看 2. 使用 stats 选项查看 3. 使用 signag ...

  9. Android studio中NDK开发(四)——使用addr2line分析Crash日志 backtrace

    文章目录 一.前言 二.分析 1.先提取backtrace部分 2.提取对应so库的信息 3.提取错误地址 三.使用addr2line对地址进行转换 1.addr2line工具的路径放在 2.Term ...

最新文章

  1. 网络工程师转售前的条件
  2. qq分享 设备未授权报错解决方案_金融行业思科设备典型网络故障案例:76系列典型案例(四)...
  3. html 横屏内容显示不全_为什么我的文本显示不全?
  4. 浅析epoll-为何多路复用I/O要使用epoll
  5. es6 class extends
  6. Capybara 2.14.1 发布,Web 应用验收测试框架
  7. view [bootstrap-4] not found如何解决_Dubbo如何处理业务异常,这个一定要知道哦
  8. loss 加权_【转载】keras 自定义 loss损失函数, sample在loss上的加权 和 metric
  9. 除了后置三摄 新一代iPhone屏幕更大机身更薄...
  10. Python中yield的作用??
  11. opencv python教程简书_OpenCV-Python教程:28.模板匹配
  12. C++ STL算法系列5---equal() , mismatch()
  13. C#【必备技能篇】注册dll+批量注册dll
  14. HashMap 为什么是2倍扩容?
  15. 智能时代悄然到来刷脸支付逐渐成为潮流
  16. 未来最有发展前景的行业分享
  17. 市场调研及发展前景分析报告有什么作用
  18. 每日词根——sol(完整可靠安慰,太阳严肃)
  19. js根据开始日期和相隔天数计算出结束日期
  20. 如何在Linux上查看历史命令

热门文章

  1. FTP:FTP状态码对照
  2. 标题|服务器标题|服务器名称|服务器IP|服务器端口|是否自动展开(0不展开,1自动展开)|微端IP|微端端口(0表示不使用微端)|安全盾防火墙端口(0表示不使用防火墙)|防火墙类型,0=安全盾防火墙
  3. Google Earth Engine(GEE)——利用updatemask进行纯NDVI和NDWI提取(以中国区域为例)
  4. Win10解决Visio无法提供快速搜索结果
  5. sql server数据库Log文件特别大怎么处理
  6. VBA——合并工作表及工作表单独保存的功能
  7. 初一计算机课要考什么,期中考查,信息技术课老师限时40分钟要求每位七年级学生打完一篇...
  8. 《小家:越住越大2》
  9. 可以浏览的国外网站(学习和放松)
  10. 【算法训练营】 - 顺丰科技智慧物流校园技术挑战赛