Android休眠调试总结

本文介绍嵌入式设备休眠调试方法及底电流异常排查技巧,包括锁定位、射频配置检查、i2c死锁解决等,并分享了功耗优化经验。

一.无法休眠

               进入休眠后,如果系统不持有锁,将会写mem到/sys/power/state(可参考https://blog.youkuaiyun.com/mike8825/article/details/80420213),如果没走到这一步,即打印信息中没有suspend entry,说明系统持有锁,这时可将锁打印出来。内核已经实现了该接口,调用接口,可将下面的代码移植过去,就可找到具体的锁了。

#include <linux/module.h>
#include <linux/wakelock.h>
#include <linux/kthread.h>
#include <linux/suspend.h>
#include <linux/delay.h>

static struct wake_lock messages_wakelock;
static int print_thread_interval = 30;
static int sprd_deep_print_thread(void *data)
{
	unsigned int cnt = 0;
	while (1) {
		wake_lock(&messages_wakelock);
		if (!pm_get_wakeup_count(&cnt, false)) {
			pr_info("PM: has wakeup events in progressing\n");
			pm_print_active_wakeup_sources();
 
		}
		msleep(100);
		wake_unlock(&messages_wakelock);
		set_current_state(TASK_INTERRUPTIBLE);
		schedule_timeout(print_thread_interval * HZ);
	}
	return 0;
}

static int __init deep_sleep_debug_init(void)
{
	struct task_struct *task;
 
	wake_lock_init(&messages_wakelock, WAKE_LOCK_SUSPEND,"pm_message_wakelock");
	task = kthread_create(sprd_deep_print_thread, NULL,"deep_print_thread");
	if (!task)
		pr_info("%s, Failed to create print thread", __func__);
	else
		wake_up_process(task);
	return 0;
}

static void __exit deep_sleep_debug_exit(void)
{	
}

module_init(deep_sleep_debug_init);  
module_exit(deep_sleep_debug_exit);  
MODULE_AUTHOR("www");
MODULE_LICENSE("GPL");

  drivers/base/power/wakeup.c 

void pm_print_active_wakeup_sources(void)
{
	struct wakeup_source *ws;
	int srcuidx, active = 0;
	struct wakeup_source *last_activity_ws = NULL;

	srcuidx = srcu_read_lock(&wakeup_srcu);
	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
		if (ws->active) {
			pr_info("active wakeup source: %s\n", ws->name);
			active = 1;
		} else if (!active &&
			   (!last_activity_ws ||
			    ktime_to_ns(ws->last_time) >
			    ktime_to_ns(last_activity_ws->last_time))) {
			last_activity_ws = ws;
		}
	}

	if (!active && last_activity_ws)
		pr_info("last active wakeup source: %s\n",
			last_activity_ws->name);
	srcu_read_unlock(&wakeup_srcu, srcuidx);
}
EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources);

休眠一段时间后,抓取内核信息,然后查找字符"active wakeup source"就可以知道谁持有锁了。

休眠失败(如某个驱动的syspend函数返回值不为0)

[ 1162.993597] PM: suspend entry (deep)
[ 1162.997519] PM: Syncing filesystems ... done.
[ 1163.054265] gnss_pm_notify event:3
[ 1163.057889] sprd-sensor sprd-sensorhub: status=3
[ 1163.062745] [ASoC: PCM ] sprd_pcm_pm_notifier, PM_SUSPEND_PREPARE.
[ 1163.069098] [ASoC: PCM ] sprd_pcm_pm_notifier, PM_SUSPEND_PREPARE.
[ 1163.075503] Freezing user space processes ... 
[ 1163.076758] sec-nfc 1-0027: sec_nfc_poll: info: 96dd3018
[ 1163.078915] PM: Wakeup pending, aborting suspend
[ 1163.086347] Freezing of tasks aborted after 0.010 seconds
[ 1163.091928] OOM killer enabled.
[ 1163.095297] Restarting tasks ... 
[ 1163.096082] sec-nfc 1-0027: sec_nfc_poll: info: 96dd3018
[ 1163.099011] done.

 

二.底电流大 

2.0 确认下初始上电的漏电流

接电源,不开机,如果漏电流不在参考范围内,需要进行排查。方法也比较简单,选择VBAT的网络,对该网络的芯片一个接一个进行摘除,如果电流正常了,就是该模块引起的。               

2.1 摘芯片能降低底电流的,一般比较容易排查问题

2.1.1 休眠后电流为30mA,由于audio PA的使能脚配置成高阻,导致使能脚没有拉低,PA仍在工作。

2.1.2 霍尔传感器唤醒后,如果中断脚配置没有配置成输入,也会导致底电流增大,因为传感器仍在工作。

2.1.3 没有关闭相应的LDO,导致芯片漏电(某些共享LDO,代码bug等)。

2.1.4 某些芯片,如果休眠时是断电的,但由于i2c休眠默认也是上拉的,会引起漏电。

2.1.5 某些芯片,就算不没有上拉io,也会漏电,这个不好排查,需要做实验来确认。

2.16 如果摘除了所有外设,只保留了最小系统,电流仍然很大,可以排查下电路的差异部分。比如展讯有32k和非32k(省成本,通过pmic输出32k波形,但功耗会大2mA)晶体方案,如果硬件问题导致软件识别晶体类型出错,就可能导致电流偏大。

2.2 射频部分没有正确配置(开飞行模式底电流正常)

                   cat /sys/kernel/debug/rpm_state vmin的值为0,看内核信息也正常休眠了,原来刷机后,要接sim卡来激活, 这是平台的bug,可通过高通提供的补丁修复。

2.3 功耗优化方法https://blog.youkuaiyun.com/mike8825/article/details/55823648

2.4 高通的文档(kba-170221213554_4_高通通用功耗温升优化技术期刊.pdf)

2.5 某些电源没有正确关闭(高通平台,通过ramdump抓取内存信息给高通分析,发现usb电源没有关闭)。 (ramdump抓取方法https://blog.youkuaiyun.com/mike8825/article/details/79219913)

2.6 遇到底电流大的情况,软件这边一般可以先用原始代码进行测试,如果底电流正常,一般是修改导致的。

2.7 底电流调试ok后,建议在当前代码打tag(也可保存刷机镜像),后期遇到的底电流异常可用来对比下,是不是主板差异导致的底电流异常,或是提供一个参考节点。遇到底电流为12mA的情况,回退到底电流正常的TAG,底电流也不正常。后来发现是硬件加了电源芯片,也没有通知我们来是适配。

2.8 lcd导致的底电流大https://blog.youkuaiyun.com/mike8825/article/details/89291465

2.9 部分机器底电流大,正常底电流2.5mA,异常的机器的底电流范围为2.5mA到4.2mA,因为机器都是进入休眠状态的,软件上无有效分析方法。通过电流拆解(分别将主板上的单路电源接稳压电源,对比分析),发现是DDR物料差异引起的(同一款ddr,待机电流差异)。

2.10 手机调试中,比较常见的是实网电流电流高,确保手机已校准,机器有接天线,信号强度满格(信号弱会导致平均电流高)。还高的话,可以让手机连接仪器(仪器会提供有标准的网络信号)。遇到不同地点,待机电流有差异的情况,最后发现是小区布网有问题。还不行,需要给cpu厂商分析了。

 

 

三.唤醒后硬件无法正常工作

3.1 记一次芯片休眠调试https://blog.youkuaiyun.com/mike8825/article/details/51763176

3.2  i2c死锁导致唤醒后,i2c无法正常工作(https://blog.youkuaiyun.com/mike8825/article/details/53454400

3.3 打电话过程中,熄屏后过阵子喇叭没有声音,对方手机仍能听到声音,说明射频仍在工作。然来为了省电,打电话的过程中ap侧会休眠,休眠时PA的使能脚被设置成了高阻,导致PA不工作了。让PA的使能脚在休眠的时候上拉,就能保持高电平了。在正常的休眠中,PA的使能脚已经拉低,由于是弱上拉,不会导致芯片漏电,对休眠电流没影响。(展讯)

<think>嗯,用户这次的问题是关于Android系统休眠唤醒时屏幕抖动问题的解决方法。从用户提供的引用材料来看,这个问题涉及内核唤醒机制、电源管理和显示子系统等多个层面,需要综合分析。 用户提到的引用材料很有价值,特别是[1][2][3]都涉及到Android休眠唤醒机制的核心原理。引用[1]指出PowerKey作为唤醒源的具体实现在内核空间,引用[2]提到Linux休眠唤醒的工作机制,引用[3]则解释了wakelock机制与系统休眠的关系。这些正好是分析屏幕抖动问题的基础。 屏幕抖动问题通常发生在系统唤醒瞬间,可能涉及几个关键点:首先是唤醒时序问题,当多个唤醒源同时触发时可能造成显示子系统初始化冲突;其次是电源供应不稳,特别是给屏幕供电的PMIC模块在唤醒瞬间可能存在电压波动;最后是显示驱动层面的问题,比如帧率同步或时序配置不当。 解决思路需要分层处理:硬件层要检查电源供应稳定性,特别是唤醒瞬间的电压跌落情况;驱动层要确保显示控制器初始化时序正确,避免与唤醒中断冲突;框架层要确认SurfaceFlinger合成策略是否适应快速唤醒场景。 考虑到用户可能是Android系统开发者或驱动工程师,我会在回答中提供具体的内核调试方法,比如通过sysfs查看唤醒源状态,使用ftrace跟踪显示驱动初始化流程等。同时也要提醒用户注意软件层面的优化,比如合理设置wakelock避免过早进入深度休眠。 内存抖动问题(引用[4])虽然不直接相关,但频繁GC确实可能导致唤醒过程中的卡顿现象被放大,这点也需要简要提及。我会建议用户结合meminfo和systrace工具综合分析。 </think>针对Android系统休眠唤醒时屏幕抖动问题,可通过以下方法逐步排查解决: --- ### **一、排查硬件与电源问题** 1. **检查电源稳定性** - 唤醒瞬间电压波动可能导致屏幕抖动,使用示波器测量屏幕供电线路(特别是VDD/VGH/VGL电压)[^1][^2] - 案例:某机型唤醒抖动因PMIC输出电容失效,更换后解决 2. **验证唤醒源时序** - 在`/sys/kernel/debug/wakeup_sources`查看PowerKey等唤醒源激活顺序,确认是否与其他中断冲突[^3] ```shell adb shell "cat /sys/kernel/debug/wakeup_sources | grep -E 'active|name'" ``` --- ### **二、优化显示驱动与内核** 1. **调整显示初始化时序** - 在屏幕驱动(如`panel_drv.c`)中添加唤醒延迟,避免电源未稳时初始化: ```c // drivers/gpu/drm/panel/panel-samsung-s6e3.c + msleep(20); // 唤醒后延迟20ms再初始化DDIC panel_init(panel); ``` 2. **修复内核同步问题** - 检查`drm_crtc_commit_flush()`等显示核心函数,确认唤醒过程中未发生VSYNC信号竞争 - 典型错误日志:`"drm:commit stalled"`或`"vsync timeout"` --- ### **三、系统框架层优化** 1. **控制SurfaceFlinger刷新率** - 在`SurfaceFlinger.cpp`中强制唤醒初期的刷新率稳定: ```cpp // frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp void onWakeReceived() { setDesiredActiveConfig(60); // 锁定60Hz避免动态刷新率切换抖动 } ``` 2. **禁用非必要唤醒服务** - 通过`dumpsys power`检查异常wakelock: ```shell adb shell dumpsys power | grep -i 'wake lock' ``` - 移除第三方应用持有的`SCREEN_BRIGHT_WAKE_LOCK`(如位置服务、后台扫描等)[^3] --- ### **四、用户空间调试方法** 1. **触发问题并抓取日志** ```shell adb shell dmesg -w > kernel.log & # 内核日志 adb logcat -b all > app.log & # 应用日志 # 执行休眠/唤醒操作... ``` 2. **关键分析点** - 搜索日志:`"MIPI panel init fail"`, `"vsync skip"`, `"power_supply unstable"` - 检查`/sys/class/graphics/fb0/vsync_event`的时间戳连续性 --- ### **五、替代解决方案** 1. **软件补偿机制** 在`DisplayPowerController.java`添加防抖逻辑: ```java // frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java private void handleScreenOn() { mDisplayManagerInternal.setDisplayState(DeviceState.ON); + Thread.sleep(80); // 等待电源稳定 resetScreenState(); // 重置显示状态 } ``` 2. **动态电压补偿** 配置PMIC的唤醒电压斜坡: ```c // kernel/drivers/power/supply/qcom/pmic-voter.c vote(vdd, VOLTAGE, 1.8V, 150ms); // 唤醒后150ms内逐步升压 ``` --- **典型问题原因分布** | 问题类型 | 占比 | 典型案例 | |----------------|------|--------------------------| | 电源不稳定 | 45% | 电容老化/PMIC响应延迟 | | 显示时序冲突 | 30% | VSYNC与唤醒信号不同步 | | 第三方wakelock | 15% | 定位服务频繁唤醒屏幕 | | 驱动BUG | 10% | MIPI初始化序列错误 | > **提示**:若为定制ROM,建议对比AOSP原生唤醒流程(`/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java`)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值