AutoSuspend: Unbreak cohabitation between standby & suspend (#9009)

* Make sure AutoStandby cohabitates nicely with AutoSuspend (regression since #8985), specifically:
* Disable standby during suspend.
* Ensure that leaving standby restores the scheduled suspend properly, with the appropriate remaining amount of time based on the last user input.
* Handle devices with an auxiliary battery better when scheduling suspend (assume it's only charging when the aux battery is charging, not the ereader's).
* Tweak debug logging to be able to remote debug corner-cases more easily without requiring code changes.
* Fix erroneous behavior when awoken from standby by something that doesn't trigger an InputEvent Event (e.g., rtc alarm, gyro, or random bullshit).
reviewable/pr9031/r1
NiLuJe 2 years ago committed by GitHub
parent 08d6fbc9db
commit 5ac9463c09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1012,7 +1012,6 @@ end
function Kobo:toggleGSensor(toggle)
if self:canToggleGSensor() and self.input then
-- Currently only supported on the Forma
if self.misc_ntx_gsensor_protocol then
self.input:toggleMiscEvNTX(toggle)
end

@ -1841,6 +1841,7 @@ Standby is re-enabled only after all issued prevents are paired with allowStandb
function UIManager:allowStandby()
assert(self._prevent_standby_count > 0, "allowing standby that isn't prevented; you have an allow/prevent mismatch somewhere")
self._prevent_standby_count = self._prevent_standby_count - 1
logger.dbg("UIManager:allowStandby, counter decreased to", self._prevent_standby_count)
end
--[[--
@ -1850,6 +1851,7 @@ i.e., something is happening in background, yet UI may tick.
]]
function UIManager:preventStandby()
self._prevent_standby_count = self._prevent_standby_count + 1
logger.dbg("UIManager:preventStandby, counter increased to", self._prevent_standby_count)
end
-- The allow/prevent calls above can interminently allow standbys, but we're not interested until

@ -36,6 +36,8 @@ local AutoSuspend = WidgetContainer:new{
is_standby_scheduled = false,
task = nil,
standby_task = nil,
leave_standby_task = nil,
going_to_suspend = false,
}
function AutoSuspend:_enabledStandby()
@ -57,7 +59,15 @@ function AutoSuspend:_schedule(shutdown_only)
end
local suspend_delay, shutdown_delay
if PluginShare.pause_auto_suspend or Device.powerd:isCharging() then
local is_charging
-- On devices with an auxiliary battery, we only care about the auxiliary battery being charged...
local powerd = Device:getPowerDevice()
if Device:hasAuxBattery() and powerd:isAuxBatteryConnected() then
is_charging = powerd:isAuxCharging()
else
is_charging = powerd:isCharging()
end
if PluginShare.pause_auto_suspend or is_charging then
suspend_delay = self.auto_suspend_timeout_seconds
shutdown_delay = self.autoshutdown_timeout_seconds
else
@ -87,23 +97,21 @@ end
function AutoSuspend:_unschedule()
if self.task then
logger.dbg("AutoSuspend: unschedule")
logger.dbg("AutoSuspend: unschedule suspend/shutdown timer")
UIManager:unschedule(self.task)
end
end
function AutoSuspend:_start()
if self:_enabled() or self:_enabledShutdown() then
self.last_action_tv = UIManager:getElapsedTimeSinceBoot()
logger.dbg("AutoSuspend: start (suspend/shutdown) at", self.last_action_tv:tonumber())
logger.dbg("AutoSuspend: start suspend/shutdown timer at", self.last_action_tv:tonumber())
self:_schedule()
end
end
function AutoSuspend:_start_standby()
if self:_enabledStandby() then
self.last_action_tv = UIManager:getElapsedTimeSinceBoot()
logger.dbg("AutoSuspend: start (standby) at", self.last_action_tv:tonumber())
logger.dbg("AutoSuspend: start standby timer at", self.last_action_tv:tonumber())
self:_schedule_standby()
end
end
@ -111,8 +119,7 @@ end
-- Variant that only re-engages the shutdown timer for onUnexpectedWakeupLimit
function AutoSuspend:_restart()
if self:_enabledShutdown() then
self.last_action_tv = UIManager:getElapsedTimeSinceBoot()
logger.dbg("AutoSuspend: restart at", self.last_action_tv:tonumber())
logger.dbg("AutoSuspend: restart shutdown timer at", self.last_action_tv:tonumber())
self:_schedule(true)
end
end
@ -143,7 +150,16 @@ function AutoSuspend:init()
self.standby_task = function()
self:_schedule_standby()
end
self.leave_standby_task = function()
-- Only if we're not already entering suspend...
if self.going_to_suspend then
return
end
UIManager:broadcastEvent(Event:new("LeaveStandby"))
end
self.last_action_tv = UIManager:getElapsedTimeSinceBoot()
self:_start()
self:_start_standby()
@ -162,6 +178,7 @@ function AutoSuspend:onCloseWidget()
self:_unschedule_standby()
self.standby_task = nil
self.leave_standby_task = nil
end
function AutoSuspend:onInputEvent()
@ -171,13 +188,18 @@ end
function AutoSuspend:_unschedule_standby()
if self.is_standby_scheduled and self.standby_task then
logger.dbg("AutoSuspend: unschedule standby")
logger.dbg("AutoSuspend: unschedule standby timer")
UIManager:unschedule(self.standby_task)
-- Restore the UIManager balance, as we run preventStandby right after scheduling this task.
UIManager:allowStandby()
self.is_standby_scheduled = false
end
-- Make sure we don't trigger a ghost LeaveStandby event...
if self.leave_standby_task then
UIManager:unschedule(self.leave_standby_task)
end
end
function AutoSuspend:_schedule_standby()
@ -202,17 +224,23 @@ function AutoSuspend:_schedule_standby()
standby_delay = self.auto_standby_timeout_seconds
elseif Device.powerd:isCharging() and not Device:canPowerSaveWhileCharging() then
-- Don't enter standby when charging on devices where charging prevents entering low power states.
-- NOTE: Minor simplification here, we currently don't do the hasAuxBattery dance like in _schedule,
-- because all the hasAuxBattery devices can currently enter PM states while charging ;).
--logger.dbg("AutoSuspend: charging, delaying standby")
standby_delay = self.auto_standby_timeout_seconds
else
local now_tv = UIManager:getElapsedTimeSinceBoot()
standby_delay = self.auto_standby_timeout_seconds - (now_tv - self.last_action_tv):tonumber()
-- If we somehow blow past the deadline on the first call of a scheduling cycle,
-- If we blow past the deadline on the first call of a scheduling cycle,
-- make sure we don't go straight to allowStandby, as we haven't called preventStandby yet...
-- (This shouldn't really ever happen, unless something is going seriously wrong somewhere).
if not self.is_standby_scheduled and standby_delay <= 0 then
standby_delay = 0.001
-- If this happens, it means we hit LeaveStandby or Resume *before* consuming new input events,
-- e.g., if there weren't any input events at all (woken up by an alarm),
-- or if the only input events we consumed did not trigger an InputEvent event (woken up by gyro events),
-- meaning self.last_action_tv is further in the past than it ought to.
-- Delay by the full amount to avoid further bad scheduling interactions.
standby_delay = self.auto_standby_timeout_seconds
end
end
@ -235,6 +263,7 @@ function AutoSuspend:_schedule_standby()
end
function AutoSuspend:preventStandby()
logger.dbg("AutoSuspend: preventStandby")
-- Tell UIManager that we want to prevent standby until our allowStandby scheduled task runs.
UIManager:preventStandby()
end
@ -265,22 +294,53 @@ function AutoSuspend:onSuspend()
if self:_enabledShutdown() and Device.wakeup_mgr then
Device.wakeup_mgr:addTask(self.autoshutdown_timeout_seconds, UIManager.poweroff_action)
end
-- Make sure we won't attempt to standby during suspend
-- (because _unschedule_standby calls allowStandby,
-- so we may trip UIManager's _standbyTransition and end up in AutoSuspend:onAllowStandby)...
-- NOTE: We only want to do this *once*, because we might get a series of Suspend events before actually getting a Resume!
-- (e.g., Power (button) -> Charging (USB plug) -> SleepCover).
if self:_enabledStandby() and not self.going_to_suspend then
UIManager:preventStandby()
end
-- And make sure onLeaveStandby, which will come *after* us if we suspended *during* standby,
-- won't re-schedule stuff right before entering suspend...
self.going_to_suspend = true
end
function AutoSuspend:onResume()
logger.dbg("AutoSuspend: onResume")
-- Restore standby balance after onSuspend
if self:_enabledStandby() and self.going_to_suspend then
UIManager:allowStandby()
end
self.going_to_suspend = false
if self:_enabledShutdown() and Device.wakeup_mgr then
Device.wakeup_mgr:removeTask(nil, nil, UIManager.poweroff_action)
end
-- Unschedule in case we tripped onUnexpectedWakeupLimit first...
self:_unschedule()
-- We should always follow an InputEvent, so last_action_tv is already up to date :).
self:_start()
self:_unschedule_standby()
self:_start_standby()
end
function AutoSuspend:onLeaveStandby()
self:_start_standby()
logger.dbg("AutoSuspend: onLeaveStandby")
-- Unschedule suspend and shutdown, as the realtime clock has ticked
self:_unschedule()
-- Reschedule suspend and shutdown (we'll recompute the delay based on the last user input, *not* the current time).
-- i.e., the goal is to behave as if we'd never unscheduled it, making sure we do *NOT* reset the delay to the full timeout.
self:_start()
-- Assuming _start didn't send us straight to onSuspend (i.e., we were woken from standby by the scheduled suspend task!)...
if not self.going_to_suspend then
-- Reschedule standby, too (we're guaranteed that no standby task is currently scheduled, hence the lack of unscheduling).
self:_start_standby()
end
end
function AutoSuspend:onUnexpectedWakeupLimit()
@ -489,7 +549,7 @@ Upon user input, the device needs a certain amount of time to wake up. Generally
self:pickTimeoutValue(touchmenu_instance,
_("Timeout for autostandby"), _("Enter time in minutes and seconds."),
"auto_standby_timeout_seconds", default_auto_standby_timeout_seconds,
{default_auto_standby_timeout_seconds, 15*60}, 0)
{3, 15*60}, 0)
end,
}
end
@ -512,7 +572,7 @@ function AutoSuspend:onAllowStandby()
wake_in = math.floor(scheduler_times[2]:tonumber()) + 1
end
if wake_in > 3 then -- don't go into standby, if scheduled wakeup is in less than 3 secs
if wake_in >= 3 then -- don't go into standby, if scheduled wakeup is in less than 3 secs
UIManager:broadcastEvent(Event:new("EnterStandby"))
logger.dbg("AutoSuspend: entering standby with a wakeup alarm in", wake_in, "s")
@ -521,12 +581,14 @@ function AutoSuspend:onAllowStandby()
logger.dbg("AutoSuspend: left standby after", Device.last_standby_tv:tonumber(), "s")
UIManager:broadcastEvent(Event:new("LeaveStandby"))
self:_unschedule() -- unschedule suspend and shutdown, as the realtime clock has ticked
self:_start() -- reschedule suspend and shutdown with the new time
-- We delay the LeaveStandby event (our onLeaveStandby handler is responsible for rescheduling everything properly),
-- to make sure UIManager will consume the input events that woke us up first
-- (in case we were woken up by user input, as opposed to an rtc wake alarm)!
-- (This ensures we'll use an up to date last_action_tv, and that it only ever gets updated from *user* input).
-- NOTE: UIManager consumes scheduled tasks before input events, so make sure we delay by a significant amount,
-- especially given that this delay will likely be used as the next input polling loop timeout...
UIManager:scheduleIn(1, self.leave_standby_task)
end
-- We don't reschedule standby here, as this will interfere with suspend.
-- Leave that to `onLeaveStandby`.
end
return AutoSuspend

Loading…
Cancel
Save