RK3399 Android 10 系统OTA升级失败问题
为了远程升级现场机器的 Android10 系统固件,需要支持验证系统OTA升级功能,但是验证发现OTA升级会失败,这里记录下分析过程。
编译OTA包
source build/envsetup.sh
lunch
# compile android system
make installclean -j16
make -j16
# compile ota
make otapackage -j16
# optional
# ./mkimage.sh ota
说明:
mkimage.sh ota
不是必须的,而且可以说是有点重复的,可以看到下面这段脚本。
if [ $TARGET == $BOOT_OTA ]
then
if [ "$PRODUCT_USE_DYNAMIC_PARTITIONS" = "true" ]; then
make installclean && make -j4 && make dist -j4
cp -rf $OUT/obj/PACKAGING/super.img_intermediates/super.img $IMAGE_PATH/
fi
echo -n "create system.img boot.img oem.img vendor.img dtbo.img vbmeta.img for OTA..."
cp -rf $OUT/obj/PACKAGING/target_files_intermediates/*-target_files*/IMAGES/*.img $IMAGE_PATH/
rm -rf $IMAGE_PATH/cache.img
rm -rf $IMAGE_PATH/recovery-two-step.img
if [ "$PRODUCT_USE_DYNAMIC_PARTITIONS" = "true" ]; then
rm -rf $IMAGE_PATH/super_empty.img
fi
echo "done."
fi
mkimage.sh
会执行 make installclean && make -j4 && make dist -j4
,相当于重新编译安卓系统和发布内容(也包含ota包)。
OTA 升级失败
将编译好的ota包(位于 out/target/product/PRODUCT_NAME/*ota*.zip), 放置到系统 /sdcard 目录,重启触发update service,弹框提示升级,点击升级后,发现系统升级失败。甚至都没有进入升级动画。
于是接上串口,查看升级日志,发现显示以下异常。
[ 3.393649] healthd: BatteryTemperaturePath I:charge_status UNKNOWN, chanrged 1, status SUCCESS, capoacity 1
battery capacity is not enough for installing pafckage: 15% needed
从日志可以看到,recovery阶段检查当前电量为1,低于15%,导致升级失败。
recovery 源码分析
查找recovery相关代码如下:
bootable/recovery/recovery.cpp
if (retry_count == 0 && !is_battery_ok(&required_battery_level)) {
ui->Print("battery capacity is not enough for installing package: %d%% needed\n",
required_battery_level);
// Log the error code to last_install when installation skips due to
// low battery.
log_failure_code(kLowBattery, update_package);
status = INSTALL_SKIPPED;
}
recovery
会判断电量信息capacity
,如果电量低于15%,则会跳过升级。
由于我们的系统不是直接通过内核上报电量,而是需要系统启动后通过应用层串口通信获取电量,所以使用了一个fake_battery
驱动,该驱动默认电量为1,导致recovery阶段直接跳过OTA升级。
is_battery_ok
Result res = Result::UNKNOWN;
int32_t capacity = INT32_MIN;
if (health != nullptr) {
health
->getCapacity([&res, &capacity](auto out_res, auto out_capacity) {
res = out_res;
capacity = out_capacity;
})
.isOk(); // should not have transport error
}
LOG(INFO) << "charge_status " << toString(charge_status) << ", charged " << charged
<< ", status " << toString(res) << ", capacity " << capacity;
// At startup, the battery drivers in devices like N5X/N6P take some time to load
// the battery profile. Before the load finishes, it reports value 50 as a fake
// capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected
// to finish loading the battery profile earlier than 10 seconds after kernel startup.
if (res == Result::SUCCESS && capacity == 50) {
if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) {
sleep(1);
wait_second++;
continue;
}
}
// If we can't read battery percentage, it may be a device without battery. In this
// situation, use 100 as a fake battery percentage.
if (res != Result::SUCCESS) {
capacity = 100;
}
分析代码可知,当系统可以获取电量时,会以读取到的电量为准,如果获取失败会认为该系统无法获取电量,直接将电量设置为 100
.
解决方案
根据源码分析结果,可以考虑以下几种解决方案:
- 更新recovery,将电量的判断逻辑去掉
- 更新内核驱动,去掉fake_battery功能
- 更新内核驱动,fake_battery 默认电量调整为15以上的数值
经验证,第2种方案可行,理论上第3种方案也可以(未验证),但是综合考虑,由于电量上报功能不能去除,只能考虑第一种方案,将recovery的判断逻辑去掉。
--- a/bootable/recovery/recovery.cpp
+++ b/bootable/recovery/recovery.cpp
@@ -721,7 +721,8 @@ static bool is_battery_ok(int* required_battery_level) {
static constexpr int BATTERY_OK_PERCENTAGE = 20;
static constexpr int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15;
*required_battery_level = charged ? BATTERY_WITH_CHARGER_OK_PERCENTAGE : BATTERY_OK_PERCENTAGE;
- return capacity >= *required_battery_level;
+ // return capacity >= *required_battery_level;
+ return true;
}
}
远程更新 recovery
将更新的recovery镜像远程到设备,然后通过指令刷新镜像。之后就可以通过ota包进行升级。
dd if=recovery.img of=/dev/block/by-name/recovery bs=1024k
注意事项
- OTA升级包的制作时间需要比待升级的系统固件编译时间更新,否则也会升级失败。
版权声明:本博客所有文章除特殊声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明出处 litreily的博客!