Skip to content

Fix: Open the right battery screen on unsupported devices - #78

Merged
d4rken merged 3 commits into
mainfrom
worktree-unmapped-battery-settings-intent
Aug 17, 2026
Merged

Fix: Open the right battery screen on unsupported devices#78
d4rken merged 3 commits into
mainfrom
worktree-unmapped-battery-settings-intent

Conversation

@d4rken

@d4rken d4rken commented Aug 17, 2026

Copy link
Copy Markdown
Member

What changed

On a phone Amply has no charge-protection support for, the "Open battery settings" button opened the system Battery saver screen. That screen has no charge-protection toggle in it, so the button led nowhere useful on exactly the devices that needed it most. It now opens the battery-usage screen, which on most manufacturer skins is where the built-in charging-protection option actually lives, and only falls back to Battery saver when a phone has no battery-usage screen at all.

This was reported on a HONOR phone but applied to every brand Amply carries no support for, including Motorola, Nothing, Sony, Fairphone, Vivo and Tecno. The same button in the "Help add support" wizard is fixed by the same change.

Also fixes a crash: if opening the battery screen failed, the retry could throw and take the action down with it.

The remaining two commits change no behavior. One corrects a wrong explanation in the code about how charging-power readings can be misreported, and pins the current behavior in a test. The other records a contributor's hardware measurements in the device-qualification ledger.

Technical Context

  • Root cause: ChargingRepository.nativeSettingsIntent() returned null when AdapterRegistry matched no adapter, and its single caller substituted ACTION_BATTERY_SAVER_SETTINGS outright. ACTION_POWER_USAGE_SUMMARY was therefore never attempted, despite all four lab adapters preferring it and the manifest already declaring its <queries> visibility. Same user-visible symptom the ledger records for LineageOS, reached by a different path (that one fell through to the Pixel component intent; this one had no adapter object to ask at all).
  • The chain moved to OemChargingShortcuts.genericBatterySettings so the null path and DisabledLabAdapter share one implementation instead of a sixth copy, and the repository's return type is now non-null so no caller can reintroduce its own fallback. The four OEM adapters keep their private copies deliberately: converting them would couple qualified per-device navigation to whatever "generic" comes to mean later.
  • The crash was pre-existing for unmapped devices: on a launch failure the handler retried the same Battery Saver intent outside runCatching, so when that intent was the one that failed, the second throw escaped. It now skips a fallback that would repeat the failed action, and guards and logs both attempts. Worth a close look, since it is the one place this PR changes control flow rather than intent selection.
  • POWER_USAGE_SUMMARY is verified to resolve only under Robolectric here. Whether it resolves on MagicOS 10 specifically is unconfirmed, so this is not proven to change what the original reporter sees. The change is correct for unmapped devices generally either way.
  • The StatsPowerCalculator commit deliberately does not fix the underlying defect. A firmware reporting CURRENT_NOW in mA where Android documents µA makes readings 1000× too small, which the plausibility cap cannot catch, but no lower bound can separate that from a genuine end-of-charge trickle either. The test states why not to add one; the real fix needs charge-level context this function does not have.

Refs #66

d4rken added 3 commits August 17, 2026 18:10
Tapping "Open battery settings" on any device Amply carries no adapter
for went straight to the system Battery saver screen, which holds no
charge-protection toggle. Reported on a HONOR Magic8 Pro (issue #66),
but it applied to every unmapped brand: Motorola, Nothing, Sony,
Fairphone, Vivo, Tecno, Huawei.

nativeSettingsIntent() returned null when the registry matched no
adapter, and its only caller substituted ACTION_BATTERY_SAVER_SETTINGS
outright. So ACTION_POWER_USAGE_SUMMARY was never attempted, even though
all four lab adapters deliberately prefer it and the manifest already
declares its <queries> visibility. Same user-visible symptom the ledger
records for LineageOS, reached by a different path: that one fell through
to the Pixel component intent, this one had no adapter object to ask.

The chain moves to OemChargingShortcuts.genericBatterySettings so the
null path and DisabledLabAdapter share one implementation rather than
gaining a sixth copy, and the repository's return type becomes non-null
so no caller has to invent a fallback again. The four OEM adapters keep
their own copies on purpose: converting them would couple qualified
per-device navigation to whatever "generic" comes to mean later.

Also closes a crash path in the same function, which pre-dated this fix
for unmapped devices. On a launch failure the handler retried the same
Battery saver intent outside runCatching, so when that intent was the one
that failed, the second throw escaped. It now skips a fallback that would
repeat the failed action, and guards and logs both attempts.

Whether POWER_USAGE_SUMMARY resolves on MagicOS 10 is still unverified,
so this is not confirmed to change what the reporter sees.
The KDoc and its test comment both claimed that a firmware reporting
CURRENT_NOW in mA where Android documents uA "would look like ~1000x too
much power", and that the plausibility cap therefore catches it. That is
backwards, which is why the cap never caught anything of the kind: mA
where uA is expected makes every reading 1000x too SMALL. A real 4 A
arrives as the integer 4000, renders as "4 mA", and computes to 18 mW,
which the UI formats as "0.0 W".

Reported on HONOR MagicOS 10 (issue #66), where the charge counter is
scaled the same way, so the ROM is a plausible instance. Not fixed here,
deliberately: no lower bound can separate that from a genuine
end-of-charge trickle, because a phone drawing single-digit mA at 100%
produces the identical value. A clamp would break correct readings on
healthy devices, so the pinning test states why not to add one, and the
real fix needs context this function does not have.

Also stops overstating what the upper cap does. It rejects results above
250 W, which is a backstop against gross over-reporting, not a unit
check: a 10x over-report of 4 A at 4.551 V lands at ~182 W and is
accepted, as the existing 20 A case already demonstrated.
The Magic8 Pro contributor (issue #66) ran the dumpsys measurement, and
the result is unfavourable in a way the mapping alone could not show.

The cap is real: 278 mAh went into the cell after the feature was
disabled, at a point where the broadcast had reported level 100 for over
a minute. That confirms the synthetic 100% against the broadcast rather
than the status bar, so the session-engine hazard recorded earlier is now
evidenced instead of inferred.

But it is only ~4%. The C636 variant is the 7100 mAh model, corroborated
by the contributor's own ~7121 mAh reading, so Smart battery capacity is
a top-of-charge voltage trim, not an 80%-cap equivalent, which would have
plateaued near 5800 mAh. Combined with Smart charge reaching 100%
overnight, neither HONOR key is a hard cap. An adapter here could offer
Adaptive on and off with no percentage at all, carrying the
allowsFullCharge honesty gap already recorded for Xiaomi. The open
question is therefore no longer whether the mapping works but whether
that adapter is worth building. Noted that the ~4% figure is a ratio, so
it survives the charge-counter unit question.

policyLatchesAtPlug behaviour observed: disabling the feature with the
cable connected moved nothing, and the charge only went in after a
replug. Same family as GrapheneOS, so an adapter would reuse the existing
pending-until-replug handling and could not support the reconnect
gesture.

Blocker 2's identity mechanism is resolved. MagicOS exposes app-readable
system features and core com.hihonor.* system packages, the same two
mechanisms that already carry LineageOS and GrapheneOS, so no property
read is needed. Version scoping is still unsolved: com.hihonor.magic.api.23
does not line up with ro.build.magic_api_level=42, so the features answer
"is this MagicOS" and not "is this MagicOS 10", and a features-only gate
would be the first unscoped one in the project.

Records the three app defects the device exposed, one fixed here, one
deferred by decision (the hasSupportLead gate hides the metadata report
on exactly the unrecognized ROMs whose codename it would carry), and one
awaiting a single contributor reading.
@d4rken d4rken added bug Something isn't working device support Request to add charge-control support for a device/OEM ROM: MagicOS Honor labels Aug 17, 2026
@d4rken
d4rken merged commit 8b2f429 into main Aug 17, 2026
22 of 23 checks passed
@d4rken
d4rken deleted the worktree-unmapped-battery-settings-intent branch August 17, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working device support Request to add charge-control support for a device/OEM ROM: MagicOS Honor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant