Skip to content
All fingerprint checksEnvironment & locale

BatteryManager fields satisfy the spec's mutual-exclusion invariants

Check id battery-spec-invariants

What an ordinary browser yields

Pass — charging/discharging times and level derive from one real power state, so all four invariants hold (a no-battery desktop reporting charging:true, level:1, dischargingTime:Infinity passes).

What a detector infers

The probe calls navigator.getBattery() once and records four fields together: level, charging, chargingTime, dischargingTime. The Battery Status spec makes three of them mutually dependent rather than free — if charging is true, dischargingTime must be Infinity; if charging is false, chargingTime must be Infinity; and level is a ratio confined to 0–1. The check asserts exactly those three, and nothing else. The mechanism a detector exploits is that these fields are derived from one underlying power state in a real implementation, but a configuration layer that writes each property independently has no shared state to derive them from — so it can emit charging:true alongside a finite dischargingTime, a combination the spec forbids and no real BatteryManager produces. That inference is reliable because it needs no baseline and no population data: the contradiction is internal to the four values and self-evident from the spec text. Note what this check deliberately does not treat as suspicious: a desktop with no battery reporting charging:true, level:1, chargingTime:0, dischargingTime:Infinity is fully coherent — every invariant holds — and it passes. The probe also omits the `!level` guard that CreepJS uses, because that guard misreports a genuine 0% battery as "unsupported"; a real 0 is a reading, not an absence.

How to resolve it

Derive the battery fields from a single consistent state instead of setting them independently: charging:true implies dischargingTime === Infinity, charging:false implies chargingTime === Infinity, and level must stay within 0–1. If there is no reason to synthesize a power state, leave BatteryManager native. On engines without navigator.getBattery (Firefox, Safari), or when the call throws or exceeds the probe's 1500ms timeout, status.battery is null and this row is not emitted at all — absence of the row is not a failure.

Spec invariants as lie detectors

Read in the wild by

CreepJS

CreepJS, an open-source fingerprinting research demo rather than a production detector, calls navigator.getBattery() in its getStatus() collector and takes all four BatteryManager fields together — charging, chargingTime, dischargingTime and level — returning them in its status object and rendering them in the battery panel, with the readout gated on a truthy level.

src/status/index.ts (master): L85-88 `async function getBattery(): Promise<BatteryManager | null> { if (!('getBattery' in navigator)) return null; return navigator.getBattery() }`; L160-165 `const { charging, chargingTime, dischargingTime, level, } = batteryInfo || {}` inside getStatus() (getBattery() invoked in the Promise.all at L150, fields returned at L191-195); L275-276 render guard `<div class="block-text-large unblurred help" title="Navigator.getBattery()">${!level || isNaN(Number(level)) ? HTMLNote.UNSUPPORTED : …}`

Akamai

Lists the Battery API — charging status and level — as a hardware signal.

signal_categories.md - 2. Hardware - Battery API

Edioff/akamai-analysis — signal_categories.md

Every attribution traces to a published artifact. See the sources and their limits.

Nearby checks in Environment & locale

See all 23 checks in Environment & locale
Who builds this test

Clearcote is a browser built for fingerprint coherence

It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.

This audit takes no position on how Clearcote scores on Environment & locale checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.

See the other checks in Environment & locale — the family battery-spec-invariants belongs to.