资讯动态

Android系统定制:修改省电模式默认值的Framework链路全解析

发布时间:2026/10/17 9:18:04 来源:尧图企业网站定制
最近接了一个定制需求某排号叫号屏项目要求系统开机后默认开启省电模式。客户说得轻巧但做过系统定制的人都知道这类“改默认值”的需求恰恰最考验对Android Framework启动链路的熟悉程度。表面上是把一个开关从关拨到开实际涉及Settings数据库、SettingsProvider初始化流程、Framework里的省电策略服务还有电池状态回调的时序问题。这篇文章围绕“Android Framework修改Settings省电模式默认值”这条主线把我实际踩过的坑、验证过的方案按可复现的方式整理出来。内容面向做ROM定制、设备系统集成、Framework开发的工程师也适合刚接触系统源码、想把设置项默认行为改明白的初学者。看完你至少能搞清楚三件事省电模式的默认值到底存在哪、有哪几条常规改法、每条改法的副作用是什么。1. 把需求看清楚省电模式默认值并不只是一个开关1.1 省电开关、省电策略、自动触发阈值要分清很多第一次接这类需求的人会以为省电模式就是Settings里一个Boolean改成true就完事。实际上省电模式在系统里至少拆成三层。第一层是开关状态一般存在Settings.Global.LOW_POWER里0是关、1是开。第二层是省电策略由BatterySaverPolicy或者更高层的省电服务管理包括是否限制后台任务、是否降低屏幕亮度、是否暂停同步等。第三层是自动触发阈值比如电量低于20%时自动进省电这个阈值同样写在Settings里字段通常是LOW_POWER_MODE_TRIGGER_LEVEL之类的名称。三层之间是独立存在的。你只把开关默认值改成1策略还是走系统默认策略你只改自动阈值开关状态没有变逻辑也不会突然激活。定制产品时通常需要同时确认三层都要符合预期否则就会出现“设置界面显示开但CPU调度根本没降频”这种半吊子状态。1.2 开发期临时设置与出厂默认设置是两条路在开发期间临时验证一条adb shell settings put global low_power 1就够用了。但这个命令只对当前这台机器的当前数据分区有效机器恢复出厂、换一台新机、OTA升级后都会被清掉。客户要的“默认开启”是量产概念任何一台机器第一次开机系统设置数据库初始化之后省电开关就应该已经是开着。这个需求必须落在系统启动早期的默认值初始化逻辑里而不是靠某个人手动执行命令。要改的链路是SettingsProvider → Settings数据库种子值 → 省电服务读取状态而不是UI界面上的勾选框。这里有一个新手常犯的误区直接去Settings应用里改开关控件的默认选中状态。那样做通常没用因为系统服务不读控件状态它只认Settings数据库里的值数据库里是0控件就算默认打勾界面刷新一下又会被读回0。2. 源码定位状态数据从哪来开机时谁在读它2.1 动手前先把这两类代码找到接到这种需求第一步不是闷头改代码而是先确认你手上这个版本里省电模式的开关状态定义在哪个模块。按照系统源码的模块划分重点关心两个地方。第一个是SettingsProvider模块路径大致在frameworks/base/packages/SettingsProvider。它负责系统启动早期初始化Settings数据库把res/values/defaults.xml里定义的默认值批量写入数据库。如果某个字段在defaults.xml里没有数据库里就不会有这个初始值。第二个是Framework服务层和电量相关的服务路径大致在frameworks/base/services/core/java/com/android/server/power。低电量省电相关的控制逻辑一般放在batterysaver子目录里核心类通常叫BatterySaverController或者类似的名字。这个类在开机时会主动读取Settings.Global.LOW_POWER然后决定是否启动省电策略。用两条命令就可以快速定位这两个方向的实际位置。grep -rn LOW_POWER frameworks/base/packages/SettingsProvider/ | head -n 30 grep -rn LOW_POWER frameworks/base/services/core/java/com/android/server/power/ | head -n 30不同分支的命名略有出入有的版本把一部分策略参数挪到了DeviceConfig里但开关状态的持久化位置基本还是Settings数据库。2.2 Settings.Global 与 Settings.Secure 别搞混着手修改前先用settings list命令确认这个字段到底挂在哪个表下面。# 连接设备或模拟器后执行 adb shell settings list global | grep -i low_power adb shell settings list secure | grep -i low_power adb shell settings list system | grep -i low_power大多数版本的省电开关在Settings.Global.LOW_POWER也就是Global表。Global表是设备级配置不区分用户Secure表按用户区分System表是历史遗留新代码基本不往里写东西。定制整机默认行为优先改Global表是对的。你在defaults.xml或DatabaseHelper里加初始值时也得注意loadSetting对应的字段常量是在Settings.Global下填错表会导致值写进去了但服务读的是另一个表。2.3 开机时序决定你改完会不会被覆盖系统启动顺序大致是SettingsProvider先启动、初始化数据库 → SystemServer拉起各个系统服务 → PowerManagerService跟着启动 → BatterySaverController读取省电开关状态 → 电池状态变化可能触发省电策略重算。如果数据库初始值已经写成1BatterySaverController启动时读到的就是1。但这个链路里有一个隐性风险很多带电池的设备在开机瞬间电池电量信息还没准备好系统可能会先按“充满电、不需要省电”的逻辑做一次状态重算把开关状态重置回去。这也是改了默认值之后仍然不生效的常见原因之一。改完代码不能只看Settings界面要用dumpsys去确认服务内部实际持有的状态。后面验证章节会专门讲这个。3. 方案一在SettingsProvider里改初始默认值这个方案是最正统的做法也是我推荐优先尝试的方案。思路是让Settings数据库第一次初始化时就把省电开关写成1之后系统服务会读到这个值用户也可以手动关掉完全符合“默认值”的语义。3.1 在defaults.xml里给开关加默认值在源码目录frameworks/base/packages/SettingsProvider/res/values/defaults.xml里先确认是否已经存在省电模式相关的默认项。如果不存在就自己加一条。内容类似下面这样。resources !-- 默认开启省电模式1为开启0为关闭 -- string namedef_low_power translatablefalse1/string /resources有些系统版本里可能已经有类似配置项只是名字不同比如config_batterySaverDefault之类。用grep -rn low_power\\|battery_saver res/values/defaults.xml先搜一下有就直接改值没有就新增。这里需要提醒一下新增项一定要配合数据库初始化代码使用光加这一条XML不会自动生效。3.2 在DatabaseHelper里补一行loadSettingdefaults.xml只是资源定义真正把资源值写进数据库的是SettingsProvider里的DatabaseHelper。在DatabaseHelper的初始化方法里找到加载Global表的那一段比如loadGlobalSettings()然后加入这样一行。loadSetting(stmt, Settings.Global.LOW_POWER, R.string.def_low_power);这个方法做的事很直接从资源里读字符串转成int写入数据库。R.string.def_low_power对应的就是刚才在defaults.xml里加的那条资源。如果你用的版本里已经有这条加载逻辑只是默认值是0那把资源改成1即可不用重复添加。改完这步理论上数据库初始化时就会写入LOW_POWER1。这个方案的优点是稳定、干净、尊重用户后续操作缺点是不好做产品线差异化同一个源码树编出来的固件要么全开要么全关。3.3 方案一的限制和验证要点这个方案最大的坑在于只对“第一次初始化数据库”有效。如果设备之前已经开过机Settings数据库文件已经生成里面已经有LOW_POWER0的记录你改默认值不会覆盖旧记录。所以测试前必须清掉数据分区再开机。验证方式很简单恢复出厂或fastboot -w后开机执行以下命令。adb shell settings get global low_power adb shell dumpsys battery_saver | grep -i enabled\\|active第一条返回1说明数据库默认值生效第二条能看到服务内部状态。手动关闭省电再重启开关状态应该保持关闭这才是“默认值”应该有的行为而不是每次开机都被强制打开。4. 方案二在Framework服务层兜底方案一适合“改默认值”这个语义但有些产品需求不是“默认开”而是“必须开”。比如医疗设备、某些公共终端业务上不希望用户关闭省电模式。这种场景需要在Framework服务层做兜底不让内部逻辑把状态重置回去。4.1 BatterySaverController的启动逻辑BatterySaverController在开机时会重新评估省电状态它会读Settings数据库里的开关值和几个辅助字段然后结合当前电量决定最终状态。如果业务逻辑在某个时机把它关掉了单靠数据库默认值是挡不住的。对这类需求常见做法是在启动或重启完成时主动调用一次开启操作。以Java代码为例思路类似下面这样具体类名和方法名以当前分支为准。Override public void onBootCompleted() { // 设备定制需求开机强制进入省电策略 if (needForceBatterySaver()) { setBatterySaverEnabled(true); } }这里的关键不是代码本身而是要想清楚“只在开机时写一次”还是“每次状态变化都强制写”。如果是每次状态变化都强制开启用户会在设置界面看到的场景是关闭开关、退出设置页、再进去开关又自动打开了。这种体验在某些设备上可能是需求但在大多数终端产品上会引发投诉。4.2 开机广播覆盖写入的变通做法很多设备厂商不太愿意动Framework源码更愿意在系统应用层做逻辑。这种情况下可以做一个开机广播接收器声明系统权限在BOOT_COMPLETED触发时执行一段写入逻辑。if (android.intent.action.BOOT_COMPLETED.equals(action)) { Settings.Global.putInt(resolver, Settings.Global.LOW_POWER, 1); }这么做的好处是不用重新编译Framework模块可以放在系统预置的App里适合很多没有源码权限但能预置应用的定制场景。缺点和上一条说的一样需要控制写入时机否则会把用户的手动关闭覆盖掉。4.3 三种修改层级怎么取舍我习惯把这类改动分成三个层级选型时对着产品需求对号入座。层级做法适用场景数据层SettingsProvider默认值默认开启但允许用户关闭量产固件统一行为服务层BatterySaverController强制写入不允许用户关闭终端业务强管控应用层开机广播写入不动Framework源码靠预置应用实现三个层级可以组合。举个例子默认值用数据层写入同时应用层在首次开机时做一次校正应对数据分区被异常重置的情况。组合使用时要先想好“覆盖规则”否则会出现代码写进去、重启又被清掉的现象。5. 方案三通过Overlay配置和系统属性做多SKU差异化前面两个方案都是直接改代码适合单一产品。但市面上很多项目是同一个源码树出多个SKU比如普通版和“低功耗省电版”需要不同默认状态。这种情况直接在DatabaseHelper里写死值会让分支维护很痛改成资源Overlay或者系统属性驱动会更灵活。5.1 用资源Overlay覆盖默认配置Android的原生系统大量使用config.xml里的资源项作为默认值如果省电开关有对应的资源项就可以在设备Overlay目录里覆盖。Overlay目录位置一般在device/厂商/产品/overlay/frameworks/base/core/res/res/values/config.xml。在覆盖文件里加入对应配置项内容类似下面这样。resources string nameconfig_battery_saver_default translatablefalse1/string /resources具体资源项名称在不同版本里有差异动手前先搜索当前源码里和“battery saver”相关的资源定义。这种做法的好处是同一个SettingsProvider代码不变普通版和设备版只是挂载不同的Overlay编译时通过产品配置文件指定使用哪个Overlay即可。5.2 用系统属性驱动开机写入资源Overlay还是需要在编译期确定如果工厂希望同一条固件通过写不同参数决定开机行为可以走系统属性方案。思路是init阶段读取一个自定义属性比如ro.config.battery_saver_default然后在系统启动后期把属性值写入Settings。在init.rc或者产品配置里设置属性初值。on property:ro.config.battery_saver_default1 setprop sys.battery_saver_write 1更可靠的做法是交给系统应用或者服务在启动时读取int def SystemProperties.getInt(ro.config.battery_saver_default, 0); Settings.Global.putInt(resolver, Settings.Global.LOW_POWER, def);这种方案的好处是运营灵活工厂可以根据出货区域或者客户要求刷不同的属性参数不需要维护多套代码。缺点是要保证属性在写入时机之前已经完成解析否则读到的还是0。5.3 方案选型的判断标准做完整机定制的人应该能感觉到改一个默认值本质上是在代码侵入性和产品灵活性之间做权衡。如果只有一款设备、需求明确方案一最直接如果产品线多、需求可能有变化方案三的属性驱动更省心。我见过太多项目一开始图简单直接改Framework后来要出另一个SKU时被迫拉分支维护后悔当初没有一步到位参数化。6. 完整实操一个排号屏设备的改造过程6.1 需求背景与方案选择项目背景是某排号叫号屏使用的场景是医院取号机旁边的显示终端。屏幕常年插电客户要求降低发热和耗电默认必须开启省电模式同时允许运维人员手动关闭去做现场调试。这个需求包含两个约束默认开启、但允许关闭。对应到前文方案选型就是数据层改默认值不应该用服务层强制写入。最终选了方案一SettingsProvider默认值外加开机应用层做一次性校正防止某些特殊流程把初始值覆盖掉。6.2 具体改动过程第一步是确认源码分支里省电开关的存储位置。执行settings list一看字段确实在Settings.Global.LOW_POWERdefaults.xml里没有现成项需要新增。在frameworks/base/packages/SettingsProvider/res/values/defaults.xml里加入string namedef_low_power translatablefalse1/string在DatabaseHelper.java的loadGlobalSettings()方法里加入loadSetting(stmt, Settings.Global.LOW_POWER, R.string.def_low_power);因为设备预置了一款叫“首次开机引导”的系统应用顺手在这个App里加了BOOT_COMPLETED监听的逻辑只有首次开机那次会执行Settings.Global.putInt(..., 1)之后不再覆盖。这样双保险能应对数据分区被部分清除的异常情况。6.3 编译验证与回滚观察编译SettingsProvider模块的方式source build/envsetup.sh lunch 产品代号-userdebug make SettingsProvider -j8如果只想快速验证可以只编模块然后重新打包system镜像。模块涉及Framework资源改动时多数情况下需要整编甚至重新生成镜像保险起见我把整个system分区重新打了一次。烧录前执行fastboot -w清掉data分区这一步必不可少。开机后按顺序验证adb shell settings get global low_power adb shell dumpsys power | grep -i battery\\|saver adb shell dumpsys battery_saver | grep -i enabled看到low_power返回1服务内部状态显示优先关闭设置正确再去设置界面确认开关状态。最后做了一轮手动关闭、重启确认重启后不会自己变回开启验证结果符合“默认值”的语义。7. 常见问题与避坑记录7.1 问题速查表问题现象可能原因解决方向改完默认值烧录后开关还是关data分区残留旧数据库记录清data分区或恢复出厂数据库里已经是1服务状态还是关BatterySaverController启动后状态被重算检查电量初始化时序必要时服务层兜底用户关闭后重启又变成开初始化脚本或开机广播每次都写入改成“只在无记录/首次开机时写入”设置界面显示与settings get结果不一致UI层缓存未刷新重启SystemUI或检查界面监听逻辑编译模块后镜像里没有变化SettingsProvider未被打进目标镜像检查编译产物是否更新必要时整编自动进入省电的阈值生效了但开关没开只改了阈值没改开关默认值同时确认三层状态符合预期7.2 几条个人经验改默认值的需求测试方案一定不能只在开发机上验证完就交付。开发机上如果之前已经手动执行过settings put数据库里的值早就变了改代码后不生效很容易让人误判成代码写错。正确的测试姿势是先恢复出厂或者清data分区再验证第一次开机的真实行为。另外任何时候都不要“每次开机都强制写”。这种逻辑短期看省事长期看会把用户的主动操作覆盖掉而且很隐蔽上线后很难排查。真正需要强制开启的产品宁可做显式的业务开关也不要藏在底层逻辑里。最后提醒一句不同Android版本对省电模式的定义方式变化挺大老版本可能就是一个Global字段加一个Policy类新版本会引入大量DeviceConfig参数。动手前一定先在你当前分支上把LOW_POWER相关的引用全部捋一遍明确改动到底影响哪一层再决定改哪里。按这个思路走省电模式默认值这类需求基本不会翻车。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑