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
<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
//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.观察 数据库 变化
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
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
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);
获取休眠时间
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,于是再次进入到该方法
//通过不断进入该方法,不断评估是否根据用户动作亮、熄屏等
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.权限
<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();
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
先跟着老大哥 走了一遍源码
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
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写入节点
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
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
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; + } + } + +}
测试 方法
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是系统用户
三.永久不休眠 方案二 死循环休眠