Good question, and it's actually more sophisticated than either of your two guesses. Modern Bosch (and most serious BMS implementations) use a technique called Coulomb counting combined with voltage measurement - essentially your "current × time" integration, though it's current integrated over time rather than current × voltage. The BMS tracks charge flowing in and out of the pack continuously, adjusting the state-of-charge estimate in real time. Voltage is used as a cross-check and for calibration at the endpoints (full charge and near-empty), where the voltage curve is steep enough to be useful as a reference. Mid-range, voltage tells you very little on lithium cells because the discharge curve is almost flat.
You're also right that this architecture does allow tracking of total capacity over time. That's exactly how battery degradation is monitored - the BMS compares the integrated charge it took to fill the pack against the rated capacity. Bosch stores this data and it's readable via their diagnostics, which is part of why dealers can pull a battery health report rather than just guessing.
One of my guesses was "integrating current × voltage" (over time). Not "current x time" integration. So, according to you, my guess was right. That said, I’m not entirely convinced this is the method used by Bosch. Here’s why: if it were, imagine leaving the battery at 30%, then 45%, then 32%, and so on. The cumulative error in the estimated charge would keep growing, since it would never be reset to zero.
For this reason, I suspect the state of charge is instead determined by comparing the voltage against a reference table.
What frustrates me most about my bike and Bosch systems is how closed the ecosystem is. Because of that, for example, my bike can’t be updated to newer apps like Flow. Yes, I know some people don’t care about having access to data, and would probably turn everything off if they could, leaving their phone at home. Good for them, but I’m the opposite. I’m a scientist, and I genuinely enjoy playing with numbers.
Unfortunately, there’s no available API set to develop alternative apps around this system. I’ve seen on this forum that some people are trying, but it’s clearly not straightforward. And all of this simply because Bosch chooses not to be transparent. In my view, this kind of approach shouldn’t be allowed.