ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Android Settings 被悄悄修改?用 dumpsys 锁定元凶进程

Android Settings 被悄悄修改?用 dumpsys 锁定元凶进程 如果你负责维护一台安卓设备或者做自动化测试多半遇到过这种鬼故事明明没人点飞行模式它自己开了屏幕亮度隔几分钟被改一次某天某个设置项的值突然变成了脏数据。这种问题最难受的地方在于你找不到是谁下的手。我经常看到“settings是被哪个进程更新的”这类提问也见过有人把 SettingsProvider 进程杀掉重启、把设置App卸载重试结果问题依旧。其实系统自带的 dumpsys 命令就能帮我们做一次很靠谱的定位。这篇文章我就从实际排查经验出发把“用 dumpsys 查 settings 更新来源”这整条链路讲清楚包括命令怎么拆解、输出怎么读、哪些坑必须绕开尽量让你看完就能上手操作。1. 先搞清楚一件事settings更新到底意味着什么1.1 “改设置”在系统里的真实动作安卓里的 Settings 不是一个简单的配置文件而是一个由系统核心进程托管的统一数据源。当你通过Settings.System.putInt()、Settings.Global.putString()这类 API 写入一项设置时表面上看只是调了一下接口实际链路是当前进程通过Context.getContentResolver()拿到ContentProvider的 Binder 代理然后以一次 Binder 事务的方式把更新请求交给SettingsProvider进程处理最后由它把键值写入数据库文件再触发对应的系统广播。这里必须把“进程”和“线程”分清楚。一个进程里可能跑着十几个线程Binder 通信靠的是进程内的 Binder 线程池所以你在ps里看到的 PID 是进程的不是线程的。真正干活的是一个叫Binder:xxx_xx的线程它的归属进程才是我们最终要找的“元凶”。很多新手查了半天只看到Binder:364_1这种线程名就蒙了因为它并不是包名也不是一个可执行程序。1.2 为什么直接搜进程名常常搜不到常见的第一反应是adb shell ps -A | grep settings结果只会搜到com.android.providers.settings这个 Provider 进程本身。它只是被调用方不是主动改设置的人。真正的调用方可能是某个后台应用也可能是系统 UI甚至是一个正在跑自动化测试的脚本。它不会在进程名里带 “settings” 三个字。更隐蔽的情况是很多应用并不会直接去写 Settings而是先调系统 API再由system_server里的某个模块把值写进去。比如你试图修改飞行模式开关最终写入Settings.Global.AIRPLANE_MODE_ON的是系统服务而不是那个 App 自己的进程。这时候如果你只看进程列表会误以为系统在“自己发疯”其实问题出在更上层的调用来源上。所以我们需要从 Binder 通信的角度去回溯而不是对着进程名字猜。2. dumpsys 的基本功输出里藏着哪些值得盯的信息2.1 dumpsys settings 与 dumpsys activity provider 的分工很多人知道adb shell dumpsys settings能导出当前所有设置键值但它其实只告诉你“现在是多少”不告诉你“是谁改成这样的”。真正有用的信息分散在不同命令的快照里需要组合起来看。我常用的两个命令是adb shell dumpsys settings settings_after.txt adb shell dumpsys activity provider com.android.providers.settings/.SettingsProvider provider.txt第一条导出 Settings 数据库的完整状态第二条导出 SettingsProvider 这个 ContentProvider 在 ActivityManager 里登记的连接信息。后者会列出当前有哪些进程和它建立了 Provider 连接虽然不能直接打印“最后一次写入者是谁”但能帮我们缩小怀疑范围。如果是 Android 8.0 之后也可以尝试用adb shell dumpsys activity providers查看所有 Provider 的汇总信息但字段比较长建议输出到文件里再 grep。2.2 输出里值得盯着的字段dumpsys settings的输出通常是按 table 分组的常见的有system、secure、global、restored等。每一项的格式大致是_Id1 nameairplane_mode_on value1有些定制 ROM 会在后面额外打印packagecom.example.app意思是这个值最后是被谁写入的但原生 Android 默认不一定带这个字段。所以不要只依赖它要当成一条线索而不是铁证。dumpsys activity provider的输出里我最关心的部分是Client列表它会显示连接此 Provider 的进程 PID、UID 以及连接的关键时间。这个列表能告诉你哪些进程正在持有 SettingsProvider 的 Binder 引用。如果一个应用已经退出它的连接可能会消失但仍然会在 ActivityManager 的最近任务记录里留下痕迹。我自己做排查时会先做一次“前后对比”adb shell dumpsys settings before.txt # 等待异常复现比如飞行模式自动打开 adb shell dumpsys settings after.txt diff before.txt after.txt这样能精确知道哪些键被改了。接下来的任务才是去找改这些键的进程。3. 从 Binder 信息反向锁定真正动手的进程3.1 先看 Provider 连接列表排除干扰项拿到dumpsys activity provider输出后先找到com.android.providers.settings/.SettingsProvider这部分。里面会有类似下面的内容com.android.providers.settings/.SettingsProvider Provider prio0 schedule0 ... Client #1: packageandroid uid1000 pid812 Client #2: packagecom.example.suspect uid10234 pid5678这里packageandroid的大概率是系统进程比如system_server它随时连到 SettingsProvider 很正常不需要一看到android就紧张。真正要盯的是那些非系统包名、非系统 UID 的 client。如果一个第三方应用和 SettingsProvider 建立了连接而它又没有明显的理由访问设置那嫌疑度就很高。3.2 用 PID 反查进程名拿到嫌疑 PID 之后立刻反查进程信息adb shell ps -A | grep pid adb shell cat /proc/pid/cmdline adb shell cat /proc/pid/commcmdline在多数情况下会给出完整包名comm只给出短进程名。比如cmdline: com.example.suspect comm: suspect如果进程名带冒号比如com.example.suspect:remote说明这是一个多进程应用你得进一步区分是主进程还是子进程在操作。这种情况很常见很多 SDK 会把脏活放到:remote进程里干。3.3 给 Binder 线程补一张“快照”值已经被改了但 Provider 连接列表只能显示“现在连着的进程”不一定是“刚才写值那一刻的进程”。要提高命中率就要在异常复现的瞬间给可疑进程的 Binder 线程留个现场。在 root 可用的设备上我常用这种方式被动抓现场adb root kill -3 pid adb pull /data/anr/traces.txtkill -3会让目标进程把当前所有线程的栈 dump 到/data/anr下的 trace 文件里。Binder 线程会显示类似Binder:5678_2或binder_2的栈如果里面出现了android.content.ContentProviderProxy、SettingsProvider相关的调用帧那就等于拍到了“作案现场”。没有 root 时这套办法受限但dumpsys activity processes仍然能看到部分进程的 oom_adj 和调度状态配合 logcat 使用也能拼出大概时间线。4. 实战推演一个 App 反复改飞行模式设置的排查过程4.1 现象复现与最初猜测之前我接到过一个问题某台测试手机每隔几分钟自动开启飞行模式Wi-Fi 和蓝牙都会被连带关掉。我用adb shell dumpsys settings抓了一次现场发现airplane_mode_on从 0 变成了 1。但问题是那一刻后台至少跑了 20 个进程包括系统桌面、输入法、各种 SDK 服务完全不知道是谁动的。最初的猜测是系统 UI 因为某种状态机错误自己切换了飞行模式。于是我先过滤了dumpsys activity provider的输出结果 SettingsProvider 的 Client 列表里有几个第三方应用其中一个包名我之前没见过。这就是第一个突破口。4.2 用 dumpsys 调用追踪锁定目标我继续观察发现每次飞行模式自动打开前的几秒钟那个可疑包名都会在logcat里留下访问 SettingsProvider 的痕迹虽然日志不会直接写“我要开飞行模式”但它会打出类似CachedSetting: airplane_mode_on这类调试信息。这个标签不是所有系统都有但可遇不可求。为了实锤我做了两件事第一用cat /proc/pid/cmdline确认了进程身份第二在观测窗口里对可疑进程连按了几次kill -3拿到线程栈。在 trace 里找到了它主动调用Settings.Global.putInt(airplane_mode_on, 1)的完整调用栈问题直接坐实。4.3 用内容观察者的思路做旁证如果看栈你觉得太重还有一个更轻量的旁证方法。SettingsProvider 支持 ContentObserver 机制所以我可以临时在测试代码里注册一个观察者观察Settings.Global的airplane_mode_on变化。每次变化发生后立刻遍历当前进程中Binder线程调用栈里等待日志拿到“变化发生瞬间”还在活跃的进程名。实际上你可以在命令行里用一个循环脚本实时轮询这个键while true; do val$(adb shell settings get global airplane_mode_on 2/dev/null | tr -d \r) echo $(date %H:%M:%S) airplane_mode_on$val sleep 2 done一旦看到值从 0 变成 1马上看同一时间段的 logcat 和dumpsys activity provider快照基本能在十几秒内锁定嫌疑人。这个办法不依赖 root也适合线上快速判断。5. 让“谁改 settings”变成持续可观察的日志5.1 用 logcat 找到 SettingsProvider 的访问痕迹不是所有系统都会打印“哪个进程写了什么 key”但 Android 的SettingsProvider内部是有日志点位的只是默认不打开。你可以试着这样开启adb shell setprop log.tag.SettingsProvider VERBOSE adb logcat -s SettingsProvider:V这条命令有些 ROM 完全无视有些能打出content://settings/global的调用痕迹。如果日志里出现了insert、update、call等字样再结合日志前面的 PID就可以直接加工成一条证据链。在原生系统上如果setprop没有效果可以把SettingsProvider的日志级别调高再重启系统应用adb shell setprop persist.log.tag.SettingsProvider VERBOSE adb shell am force-stop com.android.providers.settings但我不建议在生产机上随便重启系统 Provider这条操作更适合测试机。5.2 用 dumpsys 与采样脚本配合做稳定监测dumpsys本身是快照命令不会持续输出。为了持续观察我习惯写一个小型采样脚本把三个关键快照循环记录下来while true; do adb shell dumpsys activity provider com.android.providers.settings/.SettingsProvider provider.log adb shell dumpsys settings settings.log adb shell ps -A process.log sleep 5 done日志会膨胀得很快所以我会在脚本里只保留最近的 N 行或者用 grep 把关键内容过滤出来再写文件。实际操作中把三个文件同时滚动记录异常出现后直接看同一时间戳的内容能大大缩短定位时间。这个办法虽然笨但非常稳适合没有源码权限的场景。6. 我在实际调试中踩过的几个坑6.1 dumpsys 输出在不同 Android 版本差异很大Android 7、9、11 三代系统的dumpsys activity provider字段都不完全一样。比如低版本直接能看到 Client 的 package 名高版本可能只显示Record #0需要配合dumpsys activity processes去解析。厂商 ROM 就更不用说了MIUI、ColorOS 这些系统会对 SettingsProvider 做二次封装输出里可能多出一些私有字段也可能砍掉原生字段。所以不要死记输出格式要在你手头那台设备上先跑一遍熟悉它的“正常长相”再去做异常对比。我会把第一次拿到的完整快照留档后面再 diff会节省很多时间。6.2 没有 root 时很多关键信息看不到从 Android 10 开始/proc目录对普通 shell 用户做了隔离。你用adb shell cat /proc/pid/cmdline时有可能会得到空文件或Permission denied。这时候只能退一步用dumpsys package查询包信息再用dumpsys activity processes看系统自己记录的进程状态。我的建议是如果是你完全掌控的测试机直接上 root如果是用户的真机那就老老实实靠 logcat 和 Provider 连接列表缩小范围不要为了取证把人家的机器搞出问题。6.3 调用来源可能被“中间人”伪装最坑的一类问题一个应用通过系统 API 间接修改设置Binder 的调用方是system_server或者com.android.phone而不是那个应用本身。比如某 App 呼叫了TelephonyManager的接口系统电话进程再去写飞行模式相关设置。这时候dumpsys activity provider里看到的 pid 可能是电话进程看起来非常正常真正的触发者藏在广播或回调栈里。这种场景下单靠 dumpsys 很难一锤定音必须配合 logcat 先看是谁发起的广播、谁请求了电话服务。我的排查习惯是先看设置变更时间点再看 logcat 里同一时间有哪个应用发出了ACTION_AIRPLANE_MODE_CHANGED相关的上游广播最后回到应用侧检查它是否申请了WRITE_SETTINGS权限。这一套下来绝大多数“疑似系统自己发疯”的案例都能找到背后的真凶。我个人的体会是dumpsys 不是万能的但它永远是排查路线上最稳定、最不用依赖第三方工具的第一步。你只要愿意多花十分钟把 Provider 连接列表、进程快照和 logcat 放在一起看就能比大多数人更快找到那个“偷偷改 settings 的进程”。如果以后你遇到一台设备上的设置项频繁被改建议别急着重置系统先按这套思路把现场固定下来定位率会高很多。
返回列表