修改MTK平台Android P系统支持系统A/B分区升级

本文详细介绍了在MT6739平台上,如何修改Android 9.0系统以支持A/B分区升级。涉及device目录、kernel、lk、preloader的配置修改,以及img分区的调整,确保在编译后生成适用于A/B升级的img文件。同时,提供了Android A/B System OTA升级的相关学习资源。

Platform: MT6739
OS: Android 9.0

MTK 平台 A/B partition 功能在系统上基本已经做好,我们只需要根据对应的 MTK文档 打开相应的宏开关: MTK_AB_OTA_UPDATER ,即可开启 A/B 升级。
MTK文档截图

一、device目录下的修改

device 目录下的修改主要涉及到具体厂商目录下的 BoardConfig.mkProjectConfig.mk 文件。

./device/厂商名称/具体项目名称/BoardConfig.mk

修改前:

#Config partition size
-include $(MTK_PTGEN_OUT)/partition_size.mk

BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4

BOARD_FLASH_BLOCK_SIZE := 4096

修改后:

#Config partition size
-include $(MTK_PTGEN_OUT)/partition_size.mk

ifneq ($(strip $(MTK_AB_OTA_UPDATER
<think>我们正在讨论AndroidA/B系统升级后的恢复出厂设置问题。根据引用[1],在A/B系统中,misc分区变得可有可无,因为其功能只是传递恢复出厂设置的参数,而recovery的功能也只剩下恢复出厂设置。因此,我们可能需要通过其他方式来实现恢复出厂设置。用户的问题是:在进行AB升级后如何默认恢复出厂设置?即如何设置使得在AB升级完成后,系统自动执行恢复出厂设置操作。分析:在传统的Android升级流程中,恢复出厂设置通常通过recovery模式来执行。但在A/B系统中,recovery分区被取消,取而代之的是将recovery功能集成到boot分区中(即recovery作为boot的一个启动模式)。因此,恢复出厂设置的触发方式可能有所不同。根据引用[3]和[4],我们可以看到在recovery过程中,当完成操作(如安装更新)后,会执行`finish_recovery()`函数,然后根据不同的情况选择关机、重启到bootloader或重启。在恢复出厂设置的情况下,我们通常需要擦除用户数据分区(如/data分区)。在A/B系统中,升级操作是在后台进行的,升级完成后重启到新系统。如果要在升级后自动恢复出厂设置,我们需要在升级流程完成后触发恢复出厂设置操作。可能的实现方式:1.在升级完成后,系统第一次启动时,由系统应用(比如某个服务)检测到需要恢复出厂设置,然后触发恢复出厂设置的流程。2.修改升级流程,在升级脚本(update-binary)中,除了安装更新外,还添加擦除用户数据的命令。但是,第二种方法在升级过程中擦除数据可能会带来风险,因为升级过程中用户数据分区可能尚未被挂载,而且升级脚本的执行环境(recovery环境)在A/B系统中是临时启动的,擦除操作可能会影响当前运行的系统。因此,更常见的做法是在升级后的首次启动时触发恢复出厂设置。具体步骤:1.在升级包中放置一个标志文件,比如在/system分区放置一个文件(例如`/system/etc/trigger_factory_reset`)。2.系统在启动过程中(例如在init.rc阶段或某个系统服务启动时)检查该标志文件是否存在。如果存在,则触发恢复出厂设置操作,并删除该标志文件(避免下次启动再次触发)。如何触发恢复出厂设置?在Android系统中,可以通过发送广播`android.intent.action.MASTER_CLEAR`来触发恢复出厂设置。但是,这个广播需要由系统进程发送,并且需要具有系统权限。另一种方法是直接调用`RecoverySystem`的`rebootWipeUserData`方法(需要系统权限)。我们可以在系统启动时,由一个具有系统权限的服务来执行这个操作。考虑到系统启动过程,我们可以编写一个init服务,在启动到一定阶段时执行一个脚本或程序,该程序检查标志文件,如果存在则触发恢复出厂设置。示例步骤:1.在升级包中(如OTA包)的更新脚本(updater-script)中,在刷写系统分区后,在某个系统目录(如/system/etc)创建标志文件:```package_extract_file("trigger_factory_reset","/system/etc/trigger_factory_reset");```注意:这个标志文件需要预先放在OTA包中。2.在设备的init.rc文件中添加一个服务,该服务在启动时运行一个脚本(或可执行文件)来检查标志文件:servicefactory_reset_trigger/system/bin/factory_reset_trigger.shclassmainuserrootgrouprootoneshot3.编写脚本`factory_reset_trigger.sh`,内容如下:```bash#!/system/bin/shif[-f/system/etc/trigger_factory_reset];thenrm/system/etc/trigger_factory_reset#触发恢复出厂设置setpropfactory.reset.requested1#或者直接调用RecoverySystem的API(需要Java环境,可能不适用)或者使用命令#在Android中,我们可以通过am命令发送广播,但需要等到系统服务启动#这里我们更倾向于使用系统属性来触发,然后在另一个服务(如zygote启动后的服务)中处理fi```但是,直接在这个脚本中发送广播可能行不通,因为此时系统服务可能还没有启动。因此,我们可以设置一个系统属性,然后由另一个在系统服务启动后运行的服务来执行恢复出厂设置。4.修改init.rc,添加一个属性触发器:onproperty:factory.reset.requested=1class_startmain_factory_reset5.然后定义一个服务`main_factory_reset`,该服务负责在系统服务启动后执行恢复出厂设置:servicemain_factory_reset/system/bin/ambroadcast-aandroid.intent.action.MASTER_CLEARclasslate_startuserrootoneshot注意:`am`命令需要等到系统服务完全启动后才可用,因此我们将其放在`late_start`类中。但是,直接使用`am`命令可能不够可靠,因为恢复出厂设置需要系统权限。我们可以编写一个小的可执行程序(C/C++)来调用`RecoverySystem`的底层函数,或者使用`svc`命令(如果有)来调用系统服务。另一种更直接的方式是使用`RecoverySystem`的`rebootWipeUserData`方法,这需要我们在一个系统应用中实现。但是,为了在启动早期触发,我们可以通过系统服务调用。实际上,Android系统本身有一个机制:当`persist.sys.factory_reset`属性被设置时,会触发恢复出厂设置。我们可以参考这个机制。在Android源代码中,`MasterClearReceiver`会监听`android.intent.action.MASTER_CLEAR`广播,然后执行恢复出厂设置。因此,我们发送这个广播即可。但是,发送广播需要`android.permission.MASTER_CLEAR`权限,该权限只有系统应用才能获得。因此,我们的脚本或程序必须具有系统权限。由于我们是在系统启动过程中由init服务触发的,init服务以root身份运行,所以我们可以使用`am`命令,但需要确保在系统服务启动后。因此,我们将服务放在`late_start`类中。综合以上,我们可以这样实现:在`factory_reset_trigger.sh`脚本中:#!/system/bin/shif[-f/system/etc/trigger_factory_reset];thenrm/system/etc/trigger_factory_reset#设置一个属性,然后触发一个服务(该服务在late_start类中)setpropfactory.reset.requested1fi在init.rc中:onproperty:factory.reset.requested=1#启动一个服务来发送广播startmaster_clear_serviceservicemaster_clear_service/system/bin/ambroadcast-aandroid.intent.action.MASTER_CLEAR--user0classlate_startuserrootgrouprootsystemdisabledoneshot注意:`am`命令需要指定`--user0`,因为系统广播通常是发送给用户0的。但是,使用`am`命令可能不是最可靠的方式,因为`am`是一个shell命令,它依赖于系统运行状态。更可靠的方式是使用一个小的C++程序,直接调用`RecoverySystem`的底层函数。在Android系统中,恢复出厂设置的底层实现是通过`RecoverySystem`的`rebootWipeUserData`方法,该方法最终会调用`PowerManagerService`的`reboot`方法,并传递参数`recovery`和`--wipe_data`。我们可以模拟这个过程。实际上,我们可以直接调用`reboot`命令并传递参数到`android_reboot`服务。例如:rebootrecovery"--wipe_data"但是,在A/B系统中,recovery模式已经集成到boot中,所以我们需要使用新的方式。在Android8.0及以上,恢复出厂设置可以通过`RecoverySystem`的`rebootWipeUserData`方法,该方法会触发系统重启到恢复模式并擦除数据。但在A/B系统中,重启到恢复模式实际上是重启到当前slot的恢复模式(即boot分区的恢复模式)。然而,我们也可以不重启,而是直接擦除数据分区。但是,直接擦除数据分区需要在系统运行之前完成,否则会破坏正在运行的系统。所以,通常的做法是重启到一个可以安全擦除数据的环境,即recovery环境。因此,我们仍然需要重启到recovery环境,然后由recovery环境擦除数据。在A/B系统中,重启到recovery环境的命令可以是:rebootrecovery然后,在recovery环境中,执行擦除数据的操作。但是,如何让recovery环境知道要擦除数据?我们可以通过传递参数。在引用[3]中,我们看到recovery通过`android::base::SetProperty(ANDROID_RB_PROPERTY,"reboot,recovery");`来重启到recovery。同时,我们可以在重启之前设置一些参数,比如在`/cache/recovery/command`文件中写入命令。所以,我们可以这样:1.在触发恢复出厂设置时,写入命令到`/cache/recovery/command`:--wipe_data2.然后重启到recovery。具体步骤(在脚本中):#!/system/bin/shif[-f/system/etc/trigger_factory_reset];thenrm/system/etc/trigger_factory_reset#写入命令到/cache/recovery/commandecho"--wipe_data">/cache/recovery/command#重启到recovery/system/bin/rebootrecoveryfi但是,在系统启动过程中执行`reboot`命令会中断启动过程,而且可能会在系统未完全启动时执行,这可能导致不可预知的问题。因此,我们需要确保在系统启动完成后再执行恢复出厂设置,但这样用户会看到系统启动一次,然后突然重启进入恢复模式,体验不好。所以,我们更希望在系统启动前就完成这个操作。但是,在init的早期阶段,我们无法访问`/cache`分区(因为需要挂载),而且`/cache`分区可能是加密的。因此,更推荐的做法是在升级流程中直接擦除数据。在A/B升级的post-install阶段,我们可以执行擦除数据的操作。在A/B升级中,升级过程是在后台进行的,当前系统仍在运行。升级完成后,重启到新系统。如果我们希望升级后恢复出厂设置,那么我们在切换slot后,重启之前,擦除用户数据分区。但是,在recovery环境中(即执行升级的环境),我们可以通过update_engine在安装完成后执行一个钩子脚本,在钩子脚本中擦除数据。在update_engine的流程中,有一个`postinstall`步骤,我们可以在这里执行自定义操作。具体步骤:1.在OTA包中包含一个钩子脚本,比如`postinstall.sh`。2.在update_engine执行完安装后,会执行这个钩子脚本。3.在钩子脚本中,我们可以擦除/data分区。但是,擦除/data分区需要权限,而update_engine是以root权限运行的,所以可以执行。示例钩子脚本:#!/system/bin/sh#擦除/data分区make_ext4fs/dev/block/by-name/userdata或者使用`fastbooteraseuserdata`,但在设备内部,我们可以使用`recovery`中的工具,例如`make_ext4fs`或`wipe_data`程序。实际上,在recovery环境中,有一个`wipe_data`程序,我们可以调用它。但是,在update_engine执行的环境中(在系统运行时,update_engine是系统的一个服务),我们无法使用recovery的工具。因此,我们可以直接格式化分区:/system/bin/make_ext4fs-w-Luserdata/dev/block/by-name/userdata注意:这需要知道userdata分区的设备节点。但是,这样做会立即擦除当前运行系统的用户数据,这可能会造成系统不稳定(因为当前系统正在使用/data分区)。所以,我们不能在系统运行时擦除/data分区。所以,我们必须在重启之前,在recovery环境中擦除/data分区。而update_engine的postinstall步骤是在系统运行环境下执行的,因此不适合做擦除操作。回到之前的方法:在升级后的首次启动时触发恢复出厂设置。这样,系统会启动一次,然后触发重启到recovery并擦除数据。虽然用户体验上会重启两次,但这是安全的。因此,我们采用在首次启动时设置标志,然后重启到recovery执行擦除。总结方案:1.在OTA包中的更新脚本(updater-script)中,在系统分区刷写完成后,将一个标志文件放到/system分区(例如`/system/etc/trigger_factory_reset`)。2.在设备init.rc中配置一个服务,该服务在启动时运行一个脚本(如`factory_reset_trigger.sh`)。3.脚本检查标志文件,如果存在,则:a.删除标志文件。b.写入`--wipe_data`命令到`/cache/recovery/command`。c.执行`rebootrecovery`。注意:需要确保`/cache`分区在脚本执行时已经挂载。因此,这个服务应该在`mount_all`之后执行,比如放在`late_init`类中。修改init.rc:servicefactory_reset_trigger/system/bin/factory_reset_trigger.shclasslate_startuserrootgrouprootoneshot脚本`factory_reset_trigger.sh`:#!/system/bin/shif[-f/system/etc/trigger_factory_reset];thenrm-f/system/etc/trigger_factory_reset#创建/cache/recovery目录(如果不存在)mkdir-p/cache/recoveryecho"--wipe_data">/cache/recovery/command/system/bin/rebootrecoveryfi这样,在升级后首次启动时,系统会启动到用户界面之前,执行该脚本,然后立即重启到recovery模式,recovery模式会读取`/cache/recovery/command`文件,执行擦除数据的操作,然后重启。擦除数据后,系统将恢复出厂设置。注意:这个脚本的执行时机是在`late_start`类服务启动时,此时`/cache`分区应该已经挂载。另外,需要确保`/system/bin/reboot`命令可用,且具有执行权限。最后,在OTA包中,我们如何放置标志文件?我们可以在构建OTA包时,在`/system/etc`目录下放置一个名为`trigger_factory_reset`的空文件。这样,当OTA升级完成后,系统重启到新系统,然后触发脚本,执行恢复出厂设置。引用[4]中显示,在recovery中执行擦除数据后,会重启系统。所以,整个流程是:-升级完成,重启到新系统(slot切换后)-新系统启动,执行init.rc,启动`factory_reset_trigger`服务-脚本检测到标志文件,写入擦除命令到/cache/recovery/command,并重启到recovery-设备重启到recovery模式(当前slot的recovery),读取/cache/recovery/command,执行擦除数据-擦除完成后,recovery重启系统,此时数据已被擦除,系统以新系统的初始状态启动。因此,用户最终会看到一个恢复出厂设置后的新系统。注意:这个流程会导致设备在升级后自动重启两次(一次到新系统,然后立即重启到recovery,再重启回新系统)。为了减少重启次数,我们能否在升级后的首次启动时,在recovery环境中直接擦除数据?但是,升级后的首次启动是正常启动(boot到系统),而不是recovery。所以,我们无法避免两次重启。另一种思路:在bootloader中支持升级后直接恢复出厂设置。但这样需要修改bootloader,通常不可行。因此,上述方案是可行的。但是,需要特别注意:在擦除数据后,设备将丢失所有用户数据,包括应用程序、设置等。因此,这个操作必须在用户明确要求的情况下进行,或者在特定场景(如企业设备管理)中使用。用户的问题是“AB升级后如何设置默认恢复出厂设置”,所以我们的方案就是通过OTA包放置标志文件,然后通过init服务触发。实现:1.修改设备的init.rc,添加上述服务。2.将`factory_reset_trigger.sh`脚本放到设备的`/system/bin`目录(可以通过OTA包刷入)。3.在需要恢复出厂设置的OTA包中,在刷写system分区后,放置标志文件。注意:标志文件的位置和脚本中的位置要一致。另外,在A/B系统中,/system分区是只读的,所以放置标志文件必须在OTA刷写时进行,因为系统运行时无法修改/system分区。最后,由于每个设备的init.rc可能不同,所以需要设备制造商根据具体情况进行调整。以上方案供参考。相关问题:1.在A/B系统升级中,如何确保数据安全?2.除了在/system分区放置标志文件,还有哪些方式可以触发恢复出厂设置?3.在恢复出厂设置过程中,如何避免设备无法启动?</think>在Android A/B系统升级后实现默认恢复出厂设置,需要通过系统级机制触发数据擦除流程。以下是具体实现方案: ### 核心实现原理 1. **A/B系统特性**: A/B升级系统会启动新分区(如`_b`),此时需在**首次启动新系统时**触发恢复出厂设置 2. **关键组件**: - **Init 服务**:在系统启动阶段执行擦除操作 - **Recovery 命令**:通过`/cache/recovery/command`传递擦除指令 - **系统属性**:使用`sys.powerctl`触发重启到恢复模式 ### 具体实现步骤 #### 1. 创建启动触发脚本 在`/system/bin/`下添加脚本 `init_factory_reset.sh`: ```bash #!/system/bin/sh if [ -f /system/etc/trigger_factory_reset ]; then rm /system/etc/trigger_factory_reset mkdir -p /cache/recovery echo "--wipe_data" > /cache/recovery/command setprop sys.powerctl reboot,recovery fi ``` #### 2. 修改Init配置 在`init.rc`中添加服务声明: ```rc service factory_reset_trigger /system/bin/init_factory_reset.sh class main user root group root oneshot disabled on property:sys.boot_completed=1 && file_exists(/system/etc/trigger_factory_reset start factory_reset_trigger ``` #### 3. 升级包中植入触发标志 在OTA包更新脚本(`updater-script`)末尾添加: ```python # 创建触发文件到system分区 package_extract_file("trigger_factory_reset", "/system/etc/trigger_factory_reset"); ``` #### 4. 擦除执行流程 当新系统首次启动时: 1. Init检测到`trigger_factory_reset`文件 2. 启动脚本写入recovery指令 3. 触发重启到恢复模式: ```log init: Received sys.powerctl='reboot,recovery' ``` 4. Recovery读取指令执行擦除: ```log [Recovery] Command: --wipe_data [Recovery] Formatting /data... ``` 5. 完成后自动重启进入干净系统 ### 关键技术点 1. **安全时机**: - 在`sys.boot_completed=1`后触发,确保基础服务可用 - 通过`oneshot`服务防止重复执行 2. **数据擦除机制**: - 使用Android标准擦除指令`--wipe_data` - 实际执行`make_ext4fs`格式化数据分区: ```c // recovery 源码 if (erase_volume("/data")) {...} ``` 3. **A/B适配**: - 操作在**新激活分区**执行(如`system_b`) - 不影响备用分区数据完整性 - 擦除后自动生成新用户数据镜像 ### 验证方法 1. 检查启动日志: ```log init: Starting factory_reset_trigger... init: Set sys.powerctl=reboot,recovery recovery: Wiping user data... ``` 2. 确认数据分区状态: ```bash adb shell df /data # 应显示新创建的加密空分区 ``` ### 注意事项 1. **加密处理**: 若设备启用FBE加密,需在擦除后重新生成密钥: ```c // 在recovery中调用 android::vold::Fstab fstab; ReadDefaultFstab(&fstab); WipeDataVolume(fstab); ``` 2. **厂商适配**: - MTK设备需额外处理`/persist`分区 - 高通平台需更新`vbmeta`签名 3. **用户提示**: 建议在升级前弹出警告: ```xml <warning>本次升级将清除所有用户数据</warning> ``` --- ### 相关问题 1. A/B升级如何保留特定用户数据不被擦除? 2. 恢复出厂设置后如何自动重装预置应用? 3. 系统擦除过程中发生断电如何修复? 4. 如何验证/cache/recovery/command的有效性? 5. 非A/B设备与本方案有何主要差异? [^1]: A/B系统分区结构及misc分区作用变化 [^3]: recovery执行完成后的重启控制流程 [^4]: 恢复出厂设置时的内核级处理过程
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值