Diagnostics: Probe the Oplus charge keys and make probe results tri-state - #69
Merged
Conversation
…tate
A device-support report from an Oppo F11 Pro (CPH1969, Android 11 /
ColorOS 11) arrived carrying no usable information about the family it
had just matched. Two causes, both fixed here.
The report probed only Samsung's protect_battery, GrapheneOS's
battery_charge_limit, and the lineagesettings provider. The two ColorOS
keys Amply already knows were never read, so with the live Oplus gate
keyed on ro.build.version.oplusrom - absent on every pre-rebrand build -
the report's sole Oplus signal was "none". OplusKeyProbes now reads both
system-namespace keys for presence on the reporter's IO path.
Deliberately not in DeviceInfo: that snapshot is rebuilt on every
dashboard refresh from the main thread, and these gate nothing.
The probes also folded SecurityException into false via
runCatching{}.getOrDefault(false), rendering a refused read identically
to a missing key. That already produced a wrong conclusion once: the
issue-#49 GrapheneOS report said has_battery_charge_limit=false while
the device was actively enforcing the limit, because the key is
@Protected. SettingProbe replaces the booleans with
present/absent/read_denied.
No capability gate changes. hasProtectBattery and hasBatteryChargeLimit
survive as derived == PRESENT accessors, so a refused read still fails
closed exactly as before and cannot authorize a device-wide Samsung
write. ABSENT is documented as "nothing found", not a proven negative:
the platform returns null on provider-acquisition and RemoteException
failures, so a fourth READ_FAILED state could not be trusted anyway.
Report schema 9 -> 10: has_protect_battery and has_battery_charge_limit
become probe_* with tri-state values, plus the two new Oplus lines.
Nothing in the repo parses the report format.
Also adds the missing smart_charge_protection_switch_state to
ContributionAllowlist - its twin was already listed, so a wizard run on
an Oplus device disclosed one half of a mutually-exclusive pair and
redacted the other, reading as a device with only the fixed cap. Drops
four unreferenced CANDIDATE_KEYS sets, one of which the new probe
supersedes.
d4rken
marked this pull request as ready for review
August 16, 2026 15:05
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
Device-support reports now say something useful about Oppo, OnePlus, and Realme phones that aren't on ColorOS 15, and they no longer claim a setting is missing when the system simply refused to let Amply look.
A report from an Oppo F11 Pro (ColorOS 11) arrived carrying nothing at all about charge protection. Amply recognised it as an Oplus device, then checked only Samsung's, GrapheneOS's, and LineageOS's settings, never the ColorOS ones it already knows about. Reports from those devices now include whether each of the two ColorOS charge-protection settings is there.
Separately, every one of these presence checks reported "not present" both when a setting genuinely didn't exist and when Android refused the read. Those are very different answers, and conflating them already produced a wrong conclusion: a GrapheneOS report said the charge limit was missing while that phone was actively holding at 80%. Reports now distinguish present, absent, and read refused.
No change to which devices Amply can control, and no new permissions. Only setting presence is reported, never any value.
One extra ColorOS setting is now recognised as safe to disclose automatically in the "Help add support" wizard, so a contribution from an Oplus device no longer arrives with half of a paired feature hidden behind a manual reveal.
Technical Context
ro.build.version.oplusrom, which doesn't exist on pre-rebrand ColorOS, so it was the report's only Oplus signal and it readnone. Absence was also indistinguishable from an SELinux-denied read, sinceSystemPropertyReaderreturns""on denial.OplusKeyProbeslives on the reporter's IO path rather than inDeviceInfo, deliberately.DeviceInfo.current()is rebuilt on every dashboard refresh from the main thread byChargingRepository,MainActivity, and the wizard VM; these two probes gate nothing, so putting them in the shared snapshot would pay two synchronous binder calls per refresh for diagnostics-only data.hasProtectBatterygates device-wide Samsung writes and both flags feedhasSupportLead. They survive as derived== PRESENTaccessors specifically so a refused read still fails closed exactly as the old booleans did. Codex reviewed this point directly and confirmed no gate widened; the assertion is pinned inDeviceInfoTest.ABSENTis documented as "nothing found", not a proven negative. A fourthREAD_FAILEDstate was considered and rejected: the platform swallows provider-acquisition andRemoteExceptionfailures internally and returns null, so such a state would be false precision.smart_charge_protection_switch_stateallowlist gap was independent of the adapter, which already handled both keys.SettingWritePolicyhad both; only the disclosure allowlist was missing one.CANDIDATE_KEYSsets had no references in main or test.