Charging: Gate GrapheneOS charge control on Shizuku - #62
Merged
Conversation
The 0.3.2-beta0 on-device report (issue #49) disproved the landing assumption that global battery_charge_limit is world-readable and WSS-writable: the probe read absent while the same report showed the limit enforcing (charging state 4). GrapheneOS declares the key @Protected(read = SYSTEM_UI, readWrite = SETTINGS) (frameworks_base c30c6393) and its SettingsProvider throws SecurityException on reads and writes from every other package - including WRITE_SECURE_SETTINGS holders; the check is package-based and runs after the permission check (e87c93a2). The one exemption Amply can use is the shell UID ('ADB is used for testing'), which is exactly how the Shizuku user service executes settings get/put - the tester's successful adb runs were this path's evidence all along. The adapter now sets preferShizukuForWrites (existing Oplus plumbing: Shizuku-required banner, disabled controls, skipped WSS auto-grant) and drops the unprobeable key-presence gate - @Protected denies the probe whether the key exists or not, so presence is assumed on any GrapheneOS build, the Xiaomi-precedent assumption with the same accepted harmless-false-claim failure mode. An absent key now decodes as the factory off state per upstream source (BoolSetting default false) instead of refusing. Reads need no code change: the direct read comes back unreadable and readSyncDirectFirst already falls through to Shizuku. The shared Shizuku-required banner no longer claims state stays readable without Shizuku, which was only true for Oplus. On-device beta evidence recorded in the ledger: package detection verified from app context; unprivileged key access denied as designed (fail closed).
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.
What changed
GrapheneOS charge control now requires Shizuku. The first beta on a real device (#49) showed the app detecting GrapheneOS but refusing control with "charge limit setting is not present" — the setting exists, but GrapheneOS deliberately blocks all third-party access to it, including apps holding
WRITE_SECURE_SETTINGS. Only the shell (which Shizuku provides) is allowed through. With Shizuku connected, everything works as designed: switching the limit, the one-time full charge with the unplug/replug step, and exact state readback. Without Shizuku the app now shows the same "Shizuku required" guidance used on OnePlus/ColorOS devices instead of a wrong "setting not present" message. A fresh install that never touched the native toggle is now also handled (previously it would have refused sessions).Technical Context
@Protected(read = SYSTEM_UI, readWrite = SETTINGS)(frameworks_basec30c6393); their SettingsProvider throwsSecurityExceptionfor every other package after the WSS permission check, with the shell UID explicitly exempt ("ADB is used for testing",e87c93a2). The tester's earlier successful adb runs were unknowingly the Shizuku-path qualification.@Protecteddenies the unprivileged probe whether the key exists or not, so presence carries zero information from app context. Presence is now assumed on GrapheneOS (the Xiaomi-precedent assumption; accepted failure mode is a harmless false claim of configured control on a hypothetical build without the feature — the row would still be writable, but nothing enforces and the hardware decode stays honest).Unrestrictedbecause upstream source resolves it: GrapheneOS reads the key viaBoolSetting(..., default false), so absent is the factory off state.readSyncDirectFirstalready treats the blocked direct read as non-authoritative and falls through to the Shizuku backend. Deserves close review: the probe/gate change inGrapheneOsChargingAdapterand the shareddashboard_shizuku_required_bodycopy fix (its "can still show the current state" claim was Oplus-only truth).