fix: recover tile state from STATE_UNAVAILABLE after upgrade or boot#221
Open
dambrisco wants to merge 5 commits into
Open
fix: recover tile state from STATE_UNAVAILABLE after upgrade or boot#221dambrisco wants to merge 5 commits into
dambrisco wants to merge 5 commits into
Conversation
OS upgrades reboot the device and can leave the active tile in STATE_UNAVAILABLE: SystemUI restarts, the app process is in stopped state, and active tiles are not auto-bound. With no launcher activity and click disabled in UNAVAILABLE, the tile has no recovery path. Listening for BOOT_COMPLETED wakes the receiver after the user unlocks, which calls requestTileUpdate() and refreshes the tile. LOCKED_BOOT_COMPLETED is omitted intentionally: the SleepTileService is not directBootAware, so binding it before the user unlocks would fail anyway; BOOT_COMPLETED runs at first unlock, which is when the tile matters.
Active tiles depend on the app calling requestListeningState() at the right moments to keep state in sync. Recovery paths for stuck STATE_UNAVAILABLE (MY_PACKAGE_REPLACED, BOOT_COMPLETED, onTileAdded) are not enough on their own — users still report the tile being greyed out after a reboot. Removing ACTIVE_TILE makes SystemUI auto-bind the service whenever the QS panel is shown, so onStartListening runs and refreshTile sets STATE_INACTIVE/STATE_ACTIVE on every panel open. The tile cannot remain stuck at STATE_UNAVAILABLE because the system itself drives the refresh. Existing requestTileUpdate() calls are preserved; for passive tiles they just force an immediate listening cycle, which is still useful for push-style updates from SleepActionReceiver and SleepAudioService.
The receiver's intent-filter, the RECEIVE_BOOT_COMPLETED permission, and the onTileAdded override were all attempts to recover from STATE_UNAVAILABLE under the active-tile model. The passive tile in e429ae4 means SystemUI auto-binds and refreshes on every QS open, so those recovery paths can't fire usefully — they're just dead code that implies the tile cares about boot/package events when it no longer does. SleepActionReceiver still receives explicit action broadcasts (notification taps, ADB) via component-targeted intents, so the receiver itself stays.
After onClick → toggle() → show() → notify(), refreshTile() immediately called find() to determine the new state. notify() is async so activeNotifications didn't yet reflect the post, find() returned null, and the tile was set to STATE_INACTIVE despite the timer having started. The Boolean hint was only consulted to force INACTIVE on cancel; nothing forced ACTIVE on start, so the start path raced. show() and update() now return the notification they post, toggle() returns it (or null on cancel), and refreshTile() takes a Notification? that callers respond to directly. The find() fallback is preserved as the default for callers that aren't reacting to a state change (onStartListening on QS open).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes issues with the Sleep Tile becoming unavailable after app updates, app stops, and device reboots.
ACTIVE_TILEmade SystemUI bind the tile only when we calledrequestListeningState(). If that chain ever broke (broadcast not delivered, bind fails, application stopped, etc.), the tile defaulted toSTATE_UNAVAILABLE. By switching it to passive, SystemUI binds it every time the panel opens andrefreshTilerewrites the state on every display. A separate race setSTATE_INACTIVEafter a tap becausenotificationManager.notify()the just-posted notification wasn't yet visible tofind()—show()/update()/toggle()now return the postedNotificationsorefreshTilebypassesfind()on state changes.Confirmed that the tile survives both upgrades and reboots with these changes on my personal device using the apk built here: https://github.com/dambrisco/SleepTimer/actions/runs/25584393419
Fixes #179