HonorLocalOTA

荣耀 MagicOS「软件更新」(com.hihonor.ouc)的 Xposed 模块。把被隐藏的本地更新入口恢复出来,并按需屏蔽新版本提示、强制向服务器请求全量包。

功能

功能 说明
本地更新入口 在「软件更新」的三点菜单里恢复本地安装包入口
屏蔽新版本提示 检查更新时压成「无新版本」,不再提示升级
强制全量包 向服务器请求完整包,而不是增量包

两个开关的相互关系:

  • 「屏蔽更新」与「强制全量包」互斥。打开「强制全量包」时「屏蔽更新」不生效,设置页会在旁边提示「已被强制全量包覆盖」。
  • 设计上不在程序里偷偷改写另一个开关,避免出现「是谁把开关关掉的」这种难以追溯的状态。

要求

  • LSPosed,且框架需支持 libxposed API 102(模块声明 minApiVersion=101、targetApiVersion=102)
  • root(原因见下一节)
  • 作用域:com.hihonor.ouc(「软件更新」)

关于框架版本:LSPosed/LSPosed 官方仓库的安装包停留在较早的版本,未必支持 API 102。若模块在 LSPosed 中不显示或无法启用,请先确认框架是否支持 libxposed 102。

为什么需要 root

模块的开关配置存在 /data/local_ota/module_switch.conf,这个目录的 SELinux 标签是 local_ota_file。

从系统策略里能查到,允许访问该标签的域只有四个:adbd、platform_app_all、shell、update_engine。

  • 「软件更新」自身跑在 platform_app_all 域,本来就能读写,所以 hook 侧不需要额外权限
  • 模块的设置页跑在 untrusted_app 域,不在放行名单里,连读取属性都不允许,因此必须通过 su 访问

没有 root 时,设置页会显示「root 不可用,开关无法保存」,而不会静默失败。

使用方法

  1. 安装 APK
  2. 在 LSPosed 中启用本模块
  3. 作用域勾选「软件更新」
  4. 重启手机,或强制停止「软件更新」
  5. 打开模块设置页,确认显示「root 可用」以及两个开关的状态

使用本地更新

  1. 把全量包复制到 /data/local_ota/
  2. 文件名必须包含 update_full_(系统用 .*update_full_.*\.zip 做全串匹配,名字不含这段就永远扫不到)
  3. 该分区剩余空间需不少于 30 GiB,否则系统会静默跳过
  4. 打开「软件更新」→ 三点菜单 → 本地更新

兼容性

在荣耀 AAK-AN00(MagicOS 11 / Android 17)上测试通过,对应「软件更新」版本 2.1.1.1(versionCode 205)。

不同机型、不同系统版本的「软件更新」类名与结构可能不同,模块按上述版本实现,未覆盖的情况可能不生效。

风险提示

  • 修改系统更新行为可能导致错过安全补丁,请自行权衡
  • 强制全量包会增加流量与存储占用
  • 本地更新会绕过正常的版本校验流程,请确认包来源可靠

许可

AGPL-3.0,详见 LICENSE。

Releases

2.6

Stable

11/8/2026, 3:52:09 PM

Automated build from 8883e72cb7fef85647111a9bc72aae6a0ac24843

versionCode 8 / versionName 2.6

Assets

1

2.5

Stable

11/8/2026, 3:34:44 PM

2.5 版更新

本次重点:解决「设置页那个红点关不掉」的问题。

问题定位
之前 2.4 挂的是 com.hihonor.ouc 里的 HnEventBadgeView.setBadgeVisible(boolean),
实测发现那个红点根本不在「软件更新」进程里,而在系统设置里:
页面 com.android.settings / Settings$HwSystemDashboardActivity
控件 id update_count,类 HwTextView,显示内容为 1
所以 2.4 对它是无效的 —— 两者不在同一个进程。

真正的链路(读代码逐环确认)
写入方:com.hihonor.ouc 的 r9.w1.V6(Context,int)
Settings.Secure.putInt(cr, "hn_new_system_update", n)
再发广播 com.hihonor.android.hnouc.HW_NEW_SYSTEM_UPDATE
读取方:com.android.settings 的 dashboard.r0.b()
Settings.Secure.getInt(cr, "hn_new_system_update", 0) → SystemUpdatePreference.Q(n)
→ n > 0 就把红点那个 FrameLayout 设为可见
也就是说:只要这个系统设置项不是 0,设置页的红点就会一直在。

本次改动

  1. 新增 hook r9.w1.V6(Context,int)
    屏蔽开关生效时,把要写入的「新版本条数」改成 0 再放行。
    不改写成「直接拦掉整个方法」,因为 V6 还负责「值变了才发广播」
    等副作用,拦掉会留下内部状态与系统设置项不一致。
    0 表示无新版本,这是原版自己的用法(安装完成后它自己也调 V6(ctx, 0) 清零)。
  2. 打开屏蔽开关时主动清一次
    走 root 执行 settings put secure hn_new_system_update 0。
    因为 hook 只在 OUC 下次写入时才生效,若系统设置项里已经留着旧值,
    用户会一直看到红点、误以为没生效。清不掉只在设置页提示,不算保存失败。
  3. 保留 2.4 的 HnEventBadgeView hook
    它负责「软件更新」自己界面内的角标,和设置页那个红点是两条不同链路,两者都要。

开关行为
红点跟随「屏蔽系统更新」这一个开关。关掉屏蔽、或打开「强制全量包」时,
hook 原样放行,红点恢复真实状态(这两项互相覆盖的关系沿用之前的实现)。

适用版本
com.hihonor.ouc 18.0.3.230(versionCode 180003230)
com.android.settings 14.0.0.210(versionCode 14000210)
其它版本未验证,混淆名与资源 id 都可能在系统更新后变化。

安装提醒
LSPosed 里必须手动勾选作用域「软件更新」,模块内 staticScope 声明为 true,
不勾选则 hook 完全不注入(表现为开关能读写但功能不生效)。
安装完成后建议重启一次「软件更新」,让新模块注入。

已知局限
只处理 Settings.Secure 的 hn_new_system_update 这一条链路。
若以后荣耀改从通知、快捷方式或其它字段驱动红点,需要重新定位。

构建信息
versionCode 7 / versionName 2.5
commit 16db7c52b54c8dcf8af2b37f6fc35e42ff84ceee

Assets

1

2.4

Stable

11/8/2026, 3:12:38 PM

Automated build from 87297a80f9f46a5305464556960b4bccb4fc20d8

versionCode 6 / versionName 2.4

Assets

1

2.3

Stable

11/8/2026, 12:49:25 PM

Automated build from d02f06d176196a871eaa0a0225cb8e60e21739b9

versionCode 5 / versionName 2.3

Assets

1

2.2

Stable

11/8/2026, 2:29:35 AM

首个版本。

  • 恢复荣耀「软件更新」的本地更新入口
  • 可分别开关「屏蔽新版本提示」与「强制请求全量包」
  • 适配「软件更新」2.1.1.1 (versionCode 205)
  • 测试机型 AAK-AN00

Assets

1