Android 休眠(二)真.永久不休眠
Platform: RK3288
OS: Android 7.1.2
Kernel: 4.4.143
adb shell settings get system screen_off_timeout
一.SettingsProvider def_screen_off_timeout
1.1.frameworks\base\packages\SettingsProvider\res\values\defaults.xml
欸为什么是0x7fffffff 换算 十进制 是2147483647
ro.rk.screenoff_time 也是2147483647
整形最大取值就是 2147483647 换算成天数也就是24.85
1 2 3 4 | <resources> <bool name= "def_dim_screen" > true </bool> <integer name= "def_screen_off_timeout" > 0x7fffffff </integer> <integer name= "def_sleep_timeout" >- 1 </integer> |
1.2.frameworks\base\packages\SettingsProvider\src\com\android\providers\settings\DatabaseHelper.java
1 2 3 | //add for factory as ro.rk.screenoff_time loadSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT, SystemProperties.getInt( "ro.rk.screenoff_time" , mContext.getResources().getInteger(R.integer.def_screen_off_timeout))); |
1.3.frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
a.观察 数据库 变化
1 2 3 4 5 6 7 8 9 10 11 12 | class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { super (handler); } void observe() { // Observe all users' changes …………………………………………………………………………………… resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.SCREEN_OFF_TIMEOUT), false , this , UserHandle.USER_ALL); |
b.updateSettings mLockScreenTimeout
1 2 3 4 5 6 7 8 | public void updateSettings() { ContentResolver resolver = mContext.getContentResolver(); boolean updateRotation = false ; synchronized (mLock) { ……………………………………………………………… // use screen off timeout setting as the timeout for the lockscreen mLockScreenTimeout = Settings.System.getIntForUser(resolver, Settings.System.SCREEN_OFF_TIMEOUT, 0 , UserHandle.USER_CURRENT); |
c.postDelayed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | private void updateLockScreenTimeout() { synchronized (mScreenLockTimeout) { boolean enable = (mAllowLockscreenWhenOn && mAwake && mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId)); if (mLockScreenTimerActive != enable) { if (enable) { if (localLOGV) Log.v(TAG, "setting lockscreen timer" ); mHandler.removeCallbacks(mScreenLockTimeout); // remove any pending requests mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout); } else { if (localLOGV) Log.v(TAG, "clearing lockscreen timer" ); mHandler.removeCallbacks(mScreenLockTimeout); } mLockScreenTimerActive = enable; } } } |
1.4.frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java
mScreenOffTimeoutSetting = Settings.System.getIntForUser(resolver,
Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT,
UserHandle.USER_CURRENT);
获取休眠时间
1 2 3 4 5 6 7 8 9 10 11 12 13 | private int getScreenOffTimeoutLocked( int sleepTimeout) { int timeout = mScreenOffTimeoutSetting; if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) { timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin); } if (mUserActivityTimeoutOverrideFromWindowManager >= 0 ) { timeout = ( int )Math.min(timeout, mUserActivityTimeoutOverrideFromWindowManager); } if (sleepTimeout >= 0 ) { timeout = Math.min(timeout, sleepTimeout); } return Math.max(timeout, mMinimumScreenOffTimeoutConfig); } |
//根据nextTimeOut延迟发送信息,信息被处理后,将重新调用updatePowerStateLocked,于是再次进入到该方法
//通过不断进入该方法,不断评估是否根据用户动作亮、熄屏等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | private void updateUserActivitySummaryLocked( long now, int dirty) { if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0 ) { mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT); long nextTimeout = 0 ; if (mWakefulness == WAKEFULNESS_AWAKE || mWakefulness == WAKEFULNESS_DREAMING || mWakefulness == WAKEFULNESS_DOZING) { //获取进入休眠状态的时间sleepTimeout //getSleepTimeoutLocked中会判断休眠时间和屏幕熄灭时间的关系 //如果休眠时间sleepTimeout小于屏幕熄灭时间screenOfftime, //则休眠时间被调整为屏幕熄灭时间,因为屏幕亮屏状态下,终端不能进入休眠 final int sleepTimeout = getSleepTimeoutLocked(); //获取屏幕熄灭的时间 final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout); //获取屏幕变暗的时间 final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout); …………………………………………………… if (mUserActivitySummary != 0 && nextTimeout >= 0 ) { Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT); msg.setAsynchronous( true ); mHandler.sendMessageAtTime(msg, nextTimeout); } } else { mUserActivitySummary = 0 ; } |
二.永久休眠 方案一 WakeLock
2.1.WakeLock 唤醒锁
a.权限
1 | <uses-permission android:name= "android.permission.WAKE_LOCK" /> |
c.WakeLock 常见的锁
WakeLock 分类如下:
PARTIAL_WAKE_LOCK: 保持CPU 运转 灭屏 关闭键盘背光的情况下
PROXIMITY_SCREEN_OFF_WAKE_LOCK: 基于距离感应器熄灭屏幕。最典型的运用场景是我们贴近耳朵打电话时,屏幕会自动熄灭
SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯
SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯
FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度
SCREEN_DIM_WAKE_LOCK/SCREEN_BRIGHT_WAKE_LOCK/FULL_WAKE_LOCK:这三种WakeLock都已经过时了 它们的目的是为了保持屏幕长亮 Android官方建议用getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);方式替换 因为比起申请WakeLock 这种方式更简单 还不需要特别申请android.permission.WAKE_LOCK权限
DOZE_WAKE_LOCK/DRAW_WAKE_LOCK:隐藏的分类,系统级别才会用到
WakeLock的flag如下:
ACQUIRE_CAUSES_WAKEUP:点亮屏幕 比如应用接收到通知后,屏幕亮起。
ON_AFTER_RELEASE:释放WakeLock后 屏幕不马上熄灭 保持屏幕亮起一段时间
UNIMPORTANT_FOR_LOGGING:隐藏的flag 系统级别才会用到
b.wakeLock.acquire(); wakeLock.release();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | package com.gatsby.wakelockservice; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.Log; public class WakeLockService extends Service { private WakeLock wakeLock = null ; @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null ; } @Override public void onCreate() { // TODO Auto-generated method stub super .onCreate(); acquireWakeLock(); } @Override public void onDestroy() { // TODO Auto-generated method stub super .onDestroy(); Log.d( "gatsby" , "WakeLockService onDestroy" ); releaseWakeLock(); } // 获取电源锁 private void acquireWakeLock() { if ( null == wakeLock) { PowerManager pm = (PowerManager) this .getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "gatsby-wakelockService-TAG" ); if ( null != wakeLock) { wakeLock.acquire(); } } } // 释放设备电源锁 private void releaseWakeLock() { if ( null != wakeLock) { wakeLock.release(); wakeLock = null ; } } } |
d.研究一下 源码 先欢迎 四年级学生森下下士 我们的老大哥 小草凡 Android 功耗分析之wakelock
先跟着老大哥 走了一遍源码
1 2 3 4 5 6 7 8 | frameworks/base/core/java/android/os/PowerManager.java acquire--->acquireLocked---->PowerManagerService.acquireWakeLock frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java acquireWakeLock--->acquireWakeLockInternal---->updatePowerStateLocked---->updateSuspendBlockerLocked---->mWakeLockSuspendBlocker.acquire---->PowerManagerService$SuspendBlockerImpl.acquire---->nativeAcquireSuspendBlocker frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp nativeAcquireSuspendBlocker---->acquire_wake_lock |
hardware\libhardware_legacy\power\power.c
1 2 3 4 5 6 7 8 9 | 12 - 06 12 : 10 : 48.884 457 855 I power : release_wake_lock id= 'PowerManagerService.WakeLocks' 12 - 06 12 : 10 : 59.157 457 457 I power : acquire_wake_lock lock= 1 id= 'PowerManagerService.WakeLocks' 12 - 06 12 : 10 : 59.193 457 923 I power : release_wake_lock id= 'PowerManagerService.WakeLocks' 12 - 06 12 : 10 : 59.852 457 568 I power : acquire_wake_lock lock= 1 id= 'PowerManagerService.Broadcasts' 12 - 06 12 : 10 : 59.854 457 568 I power : release_wake_lock id= 'PowerManagerService.Display' 12 - 06 12 : 11 : 00.347 457 457 I power : release_wake_lock id= 'PowerManagerService.Broadcasts' 12 - 06 12 : 11 : 04.196 457 587 I power : acquire_wake_lock lock= 1 id= 'PowerManagerService.WakeLocks' 12 - 06 12 : 11 : 04.341 457 457 I power : release_wake_lock id= 'PowerManagerService.WakeLocks' 12 - 06 12 : 11 : 04.854 457 598 I power : acquire_wake_lock lock= 1 id= 'KeyEvents' |
/sys/power/wake_lock写入节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | acquire_wake_lock( int lock, const char * id) { initialize_fds(); // ALOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id); if (g_error) return g_error; int fd; size_t len; ssize_t ret; if (lock != PARTIAL_WAKE_LOCK) { return -EINVAL; } fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK]; ret = write(fd, id, strlen(id)); if (ret < 0 ) { return -errno; } return ret; } int release_wake_lock( const char * id) { initialize_fds(); // ALOGI("release_wake_lock id='%s'\n", id); if (g_error) return g_error; ssize_t len = write(g_fds[RELEASE_WAKE_LOCK], id, strlen(id)); if (len < 0 ) { return -errno; } return len; } |
龙套三人组:听不见!重来!/根本听不见!再说一遍!/听不见!重来/这么小声还想开军舰! 到了power这里还不算完
到 android 休眠唤醒机制分析(一) — wake_lock
1 2 3 | static struct wake_lock xh_charge_display_lock; // 申请锁 wake_lock_init(&xh_charge_display_lock, WAKE_LOCK_SUSPEND, "xh_charge_display_lock" ); // WAKE_LOCK_SUSPEND 阻止进入深度休眠模式 wake_lock(&xh_charge_display_lock); //保管锁 |
2.2.patch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | diff --git a/packages/apps/Settings/AndroidManifest.xml b/packages/apps/Settings/AndroidManifest.xml index 52841dd..5764d40 100755 --- a/packages/apps/Settings/AndroidManifest.xml +++ b/packages/apps/Settings/AndroidManifest.xml @@ - 86 , 6 + 86 , 7 @@ <uses-permission android:name= "android.permission.PEERS_MAC_ADDRESS" /> <uses-permission android:name= "android.permission.MANAGE_NOTIFICATIONS" /> <uses-permission android:name= "android.permission.DELETE_PACKAGES" /> + <uses-permission android:name= "android.permission.WAKE_LOCK" /> <application android:label= "@string/settings_label" android:icon= "@mipmap/ic_launcher_settings" @@ - 3272 , 6 + 3273 , 9 @@ android:enabled= "@bool/config_has_help" /> <activity android:name= "com.android.settings.display.ScreenScaleActivity" /> + + <service android:name= ".WakeLockService" > + </service> <!-- This is the longest AndroidManifest.xml ever. --> </application> </manifest> diff --git a/packages/apps/Settings/src/com/android/settings/DisplaySettings.java b/packages/apps/Settings/src/com/android/settings/DisplaySettings.java index 2b52b39..f084af8 100755 --- a/packages/apps/Settings/src/com/android/settings/DisplaySettings.java +++ b/packages/apps/Settings/src/com/android/settings/DisplaySettings.java @@ - 110 , 7 + 110 , 8 @@ public class DisplaySettings extends SettingsPreferenceFragment implements private String mProduct; private ListPreference mDensityListPreference; DisplayMetrics dm; - private String density_olddegree; + private String density_olddegree; + private Intent wakelock_intent; @Override protected int getMetricsCategory() { @@ - 469 , 12 + 470 , 19 @@ public class DisplaySettings extends SettingsPreferenceFragment implements @Override public boolean onPreferenceChange(Preference preference, Object objValue) { final String key = preference.getKey(); - if (KEY_SCREEN_TIMEOUT.equals(key)) { + final Context context = preference.getContext(); + if (KEY_SCREEN_TIMEOUT.equals(key)) { + wakelock_intent = new Intent(context,WakeLockService. class ); try { int value = Integer.parseInt((String) objValue); if (value == 0 ) { value = Integer.MAX_VALUE; - } + context.startService(wakelock_intent); + SystemProperties.set( "persist.sys.wakeLock" , "1" ); + } else { + context.stopService(wakelock_intent); + SystemProperties.set( "persist.sys.wakeLock" , "0" ); + } Settings.System.putInt(getContentResolver(), SCREEN_OFF_TIMEOUT, value); updateTimeoutPreferenceDescription(value); } catch (NumberFormatException e) { diff --git a/packages/apps/Settings/src/com/android/settings/HdmiReceiver.java b/packages/apps/Settings/src/com/android/settings/HdmiReceiver.java index aff17a5..604997c 100755 --- a/packages/apps/Settings/src/com/android/settings/HdmiReceiver.java +++ b/packages/apps/Settings/src/com/android/settings/HdmiReceiver.java @@ - 33 , 6 + 33 , 11 @@ import android.widget.Toast; import android.os.DisplayOutputManager; import com.android.settings.R; + import android.provider.Settings; + import android.os.Bundle; + import android.content.Intent; + import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; + public class HdmiReceiver extends BroadcastReceiver { private final String HDMI_ACTION = "android.intent.action.HDMI_PLUG" ; private final String BOOT_ACTION= "android.intent.action.BOOT_COMPLETED" ; @@ - 47 , 6 + 52 , 8 @@ public class HdmiReceiver extends BroadcastReceiver { private SharedPreferences preferences; private File DualModeFile = new File( "/sys/class/graphics/fb0/dual_mode" ); private DisplayOutputManager mDisplayManagement = null ; + private Intent wakelock_intent; + @Override public void onReceive(Context context, Intent intent) { mcontext = context; @@ - 125 , 6 + 132 , 18 @@ public class HdmiReceiver extends BroadcastReceiver { SystemProperties.set( "sys.abc_switch" , "1" ); //InitDualModeTask initDualModeTask=new InitDualModeTask(); //initDualModeTask.execute(); + try { + long screen_off_timeout = Settings.System.getLong(context.getContentResolver(), SCREEN_OFF_TIMEOUT, 30000 ); + boolean enable_wakeLock = (SystemProperties.getInt( "persist.sys.wakeLock" , 0 ) == 1 ); + Log.d( "gatsby" , "HdmiReceiver HdmiReceiver enable_wakeLock ->" +enable_wakeLock); + wakelock_intent = new Intent(context,WakeLockService. class ); + if ((screen_off_timeout == Integer.MAX_VALUE) && enable_wakeLock){ + Log.d( "gatsby" , "HdmiReceiver HdmiReceiver" ); + context.startService(wakelock_intent); + } + } catch (NumberFormatException e) { + Log.e( "gatsby" , "could not persist screen timeout setting" , e); + } } } diff --git a/packages/apps/Settings/src/com/android/settings/WakeLockService.java b/packages/apps/Settings/src/com/android/settings/WakeLockService.java new file mode 100755 index 0000000 ..8c021cd --- /dev/ null +++ b/packages/apps/Settings/src/com/android/settings/WakeLockService.java @@ - 0 , 0 + 1 , 52 @@ + package com.android.settings; + + import android.app.Service; + import android.content.Context; + import android.content.Intent; + import android.os.IBinder; + import android.os.PowerManager; + import android.os.PowerManager.WakeLock; + import android.util.Log; + + public class WakeLockService extends Service { + + private WakeLock wakeLock = null ; + + @Override + public IBinder onBind(Intent arg0) { + return null ; + } + + @Override + public void onCreate() { + super .onCreate(); + Log.d( "gatsby" , "WakeLockService onCreate" ); + acquireWakeLock(); + } + + @Override + public void onDestroy() { + super .onDestroy(); + Log.d( "gatsby" , "WakeLockService onDestroy" ); + releaseWakeLock(); + } + + private void acquireWakeLock() { + if ( null == wakeLock) { + PowerManager pm = (PowerManager) this .getSystemService(Context.POWER_SERVICE); + wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, + "gatsby-wakelockService-TAG" ); + if ( null != wakeLock) { + wakeLock.acquire(); + } + } + } + + private void releaseWakeLock() { + if ( null != wakeLock) { + wakeLock.release(); + wakeLock = null ; + } + } + +} |
测试 方法
1 2 | am startservice -n com.android.settings/com.android.settings.WakeLockService am stopservice -n com.android.settings/com.android.settings.WakeLockService |
查看锁 dumpsys power|grep -i wake
通过adb命令查看WakeLock锁的个数: dumpsys power uid=1000是系统用户
三.永久不休眠 方案二 死循环休眠
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】