Skip to content

Commit 138e15b

Browse files
author
Memfault Inc.
committed
Memfault BORT SDK 5.1.0 (Build 2456766)
1 parent 54aeef8 commit 138e15b

File tree

7 files changed

+126
-26
lines changed

7 files changed

+126
-26
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# Memfault Bort Changelog
22

3+
## v5.1.0 - September 13, 2024
4+
5+
### :construction: Fixes
6+
7+
- Fixed Stability Device Vitals: a change to the way metrics are collected in
8+
Bort 4.17.0 meant that `operational_hours` would erroneously be set to zero,
9+
resulting in incorrect Stability Vital charts.
10+
11+
### :chart_with_upwards_trend: Improvements
12+
13+
- Added new screen on/off battery drain metrics:
14+
`battery_screen_on_discharge_duration_ms`, `battery_screen_on_soc_pct_drop`,
15+
`battery_screen_off_discharge_duration_ms`, `battery_screen_off_soc_pct_drop`.
16+
These will replace `screen_off_battery_drain_%/hour`/
17+
`screen_on_battery_drain_%/hour` in the future, once they are supported in the
18+
Memfault dashboard (to more accurately track battery drain across the fleet).
19+
320
## v5.0.0 - September 12, 2024
421

522
### :boom: Breaking Changes

MemfaultPackages/bort/src/main/java/com/memfault/bort/metrics/BatterystatsSummaryCollector.kt

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.memfault.bort.metrics
22

33
import com.memfault.bort.TemporaryFileFactory
4+
import com.memfault.bort.metrics.BatterystatsSummaryCollector.Companion.DP
45
import com.memfault.bort.metrics.HighResTelemetry.DataType.DoubleType
56
import com.memfault.bort.metrics.HighResTelemetry.Datum
67
import com.memfault.bort.metrics.HighResTelemetry.MetricType
@@ -102,15 +103,25 @@ class BatterystatsSummaryCollector @Inject constructor(
102103
hrt.add(rollup)
103104
}
104105

105-
// Screen off drain
106+
// Screen off drain: old metric (to be deleted once we are using the new one, at some point)
106107
if (diff.screenOffRealtimeMs > 0 && diff.screenOffDrainPercent != null) {
107108
val screenOffBatteryDrainPerHour =
108109
JsonPrimitive(diff.screenOffDrainPercent.proRataValuePerHour(diff.screenOffRealtimeMs.milliseconds))
109110
addHrtRollup(name = SCREEN_OFF_BATTERY_DRAIN_PER_HOUR, value = screenOffBatteryDrainPerHour)
110111
report[SCREEN_OFF_BATTERY_DRAIN_PER_HOUR] = screenOffBatteryDrainPerHour
111112
}
112113

113-
// Screen on drain
114+
// Screen off drain: new metrics
115+
if (diff.screenOffDrainPercent != null) {
116+
val screenOffDischargeDurationMs = JsonPrimitive(diff.screenOffRealtimeMs)
117+
addHrtRollup(name = SCREEN_OFF_DISCHARGE_DURATION_MS, value = screenOffDischargeDurationMs)
118+
report[SCREEN_OFF_DISCHARGE_DURATION_MS] = screenOffDischargeDurationMs
119+
val screenOffPercentDrop = JsonPrimitive(diff.screenOffDrainPercent.roundTo(DP))
120+
addHrtRollup(name = SCREEN_OFF_SOC_PCT_DROP, value = screenOffPercentDrop)
121+
report[SCREEN_OFF_SOC_PCT_DROP] = screenOffPercentDrop
122+
}
123+
124+
// Screen on drain: old metric (to be deleted once we are using the new one, at some point)
114125
val screenOnRealtimeMs = diff.batteryRealtimeMs - diff.screenOffRealtimeMs
115126
if (screenOnRealtimeMs > 0 && diff.screenOnDrainPercent != null) {
116127
val screenOnBatteryDrainPerHour =
@@ -119,6 +130,16 @@ class BatterystatsSummaryCollector @Inject constructor(
119130
report[SCREEN_ON_BATTERY_DRAIN_PER_HOUR] = screenOnBatteryDrainPerHour
120131
}
121132

133+
// Screen on drain: new metrics
134+
if (diff.screenOnDrainPercent != null) {
135+
val screenOnDischargeDurationMs = JsonPrimitive(screenOnRealtimeMs)
136+
addHrtRollup(name = SCREEN_ON_DISCHARGE_DURATION_MS, value = screenOnDischargeDurationMs)
137+
report[SCREEN_ON_DISCHARGE_DURATION_MS] = screenOnDischargeDurationMs
138+
val screenOnPercentDrop = JsonPrimitive(diff.screenOnDrainPercent.roundTo(DP))
139+
addHrtRollup(name = SCREEN_ON_SOC_PCT_DROP, value = screenOnPercentDrop)
140+
report[SCREEN_ON_SOC_PCT_DROP] = screenOnPercentDrop
141+
}
142+
122143
if (summary.batteryState.estimatedBatteryCapacity > 0) {
123144
val estimatedCapacityMah = JsonPrimitive(summary.batteryState.estimatedBatteryCapacity)
124145
addHrtRollup(name = ESTIMATED_BATTERY_CAPACITY, value = estimatedCapacityMah)
@@ -218,6 +239,11 @@ class BatterystatsSummaryCollector @Inject constructor(
218239
const val MIN_BATTERY_CAPACITY = "min_battery_capacity_mah"
219240
const val MAX_BATTERY_CAPACITY = "max_battery_capacity_mah"
220241
const val BATTERY_STATE_OF_HEALTH = "battery_state_of_health_%"
242+
const val SCREEN_ON_DISCHARGE_DURATION_MS = "battery_screen_on_discharge_duration_ms"
243+
const val SCREEN_ON_SOC_PCT_DROP = "battery_screen_on_soc_pct_drop"
244+
const val SCREEN_OFF_DISCHARGE_DURATION_MS = "battery_screen_off_discharge_duration_ms"
245+
const val SCREEN_OFF_SOC_PCT_DROP = "battery_screen_off_soc_pct_drop"
246+
const val DP = 2
221247
}
222248
}
223249

@@ -310,7 +336,7 @@ private operator fun PowerUseSummary.minus(other: PowerUseSummary) = PowerUseSum
310336
maxCapacityMah = maxCapacityMah - other.maxCapacityMah,
311337
)
312338

313-
private fun Double.proRataValuePerHour(period: Duration, dp: Int = 2) =
339+
private fun Double.proRataValuePerHour(period: Duration, dp: Int = DP) =
314340
((this / period.inWholeMilliseconds.toDouble()) * 1.hours.inWholeMilliseconds.toDouble()).roundTo(dp)
315341

316342
fun Double.roundTo(n: Int): Double {

MemfaultPackages/bort/src/main/java/com/memfault/bort/metrics/CrashFreeHours.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ data class CrashFreeHoursState(
4646
)
4747

4848
class CrashFreeHoursMetricLogger @Inject constructor() {
49-
fun incrementOperationalHours(hours: Long) {
50-
OPERATIONAL_HOURS_METRIC.increment(hours)
49+
fun incrementOperationalHours(hours: Int) {
50+
OPERATIONAL_HOURS_METRIC.incrementBy(by = hours)
5151
}
5252

53-
fun incrementCrashFreeHours(hours: Long) {
54-
CRASH_FREE_HOURS_METRIC.increment(hours)
53+
fun incrementCrashFreeHours(hours: Int) {
54+
CRASH_FREE_HOURS_METRIC.incrementBy(by = hours)
5555
}
5656

5757
companion object {
@@ -95,7 +95,7 @@ class CrashFreeHours @Inject constructor(
9595
override fun process() {
9696
val now = timeProvider.now().elapsedRealtime.duration
9797
val elapsed = now - storage.state.hourStartedAtElapsedRealtimeMs.toDuration(MILLISECONDS)
98-
val elapsedHours = elapsed.inWholeHours
98+
val elapsedHours = elapsed.inWholeHours.toInt()
9999

100100
if (elapsedHours > 0) {
101101
metricLogger.incrementOperationalHours(elapsedHours)

MemfaultPackages/bort/src/main/java/com/memfault/bort/metrics/MetricsCollectionTask.kt

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class MetricsCollectionTask @Inject constructor(
153153
override fun convertAndValidateInputData(inputData: Data) = Unit
154154

155155
private suspend fun enqueueHeartbeatUpload(
156-
collectionTime: CombinedTime,
156+
initialCollectionTime: CombinedTime,
157157
lastHeartbeatUptime: BaseLinuxBootRelativeTime,
158158
propertiesStore: DevicePropertiesStore,
159159
) {
@@ -163,16 +163,16 @@ class MetricsCollectionTask @Inject constructor(
163163
val softwareVersionChanged = customMetrics.softwareVersionChanged(deviceSoftwareVersion)
164164

165165
val batteryStatsResult = batteryStatsCollector.collect(
166-
collectionTime = collectionTime,
166+
collectionTime = initialCollectionTime,
167167
lastHeartbeatUptime = lastHeartbeatUptime,
168168
)
169169
Logger.test("Metrics: properties_use_service = ${metricsSettings.propertiesUseMetricService}")
170170
Logger.test("Metrics: software version = $heartbeatSoftwareVersion -> $deviceSoftwareVersion")
171171

172172
// These write to Custom Metrics - do before finishing the heartbeat report.
173-
storageStatsCollector.collectStorageStats(collectionTime)
173+
storageStatsCollector.collectStorageStats(initialCollectionTime)
174174
networkStatsCollector.collectAndRecord(
175-
collectionTime = collectionTime,
175+
collectionTime = initialCollectionTime,
176176
lastHeartbeatUptime = lastHeartbeatUptime,
177177
)
178178

@@ -224,13 +224,16 @@ class MetricsCollectionTask @Inject constructor(
224224
val inMemoryMetrics = listOf(
225225
appStorageStatsCollector,
226226
databaseSizeCollector,
227-
).flatMap { collector -> collector.collect(collectionTime) }
227+
).flatMap { collector -> collector.collect(initialCollectionTime) }
228228
val inMemoryHeartbeats = inMemoryMetrics.heartbeatMetrics()
229229
val inMemoryInternalHeartbeats = inMemoryMetrics.internalHeartbeatMetrics()
230-
val inMemoryHrtRollups = inMemoryMetrics.hrtRollups(collectionTime)
230+
val inMemoryHrtRollups = inMemoryMetrics.hrtRollups(initialCollectionTime)
231231

232+
// Ensure that we set the "actual" collection time after all metrics have been collected, so that they will all
233+
// be included in the report.
234+
val actualCollectionTime = combinedTimeProvider.now()
232235
val heartbeatReport = customMetrics.collectHeartbeat(
233-
endTimestampMs = collectionTime.timestamp.toEpochMilli(),
236+
endTimestampMs = actualCollectionTime.timestamp.toEpochMilli(),
234237
forceEndAllReports = softwareVersionChanged,
235238
)
236239

@@ -243,7 +246,7 @@ class MetricsCollectionTask @Inject constructor(
243246
.takeIf { it.isPositive() }
244247

245248
// This duration can also be negative, but it's the same as we had before.
246-
val hourlyHeartbeatDurationFromUptime = collectionTime.elapsedRealtime.duration -
249+
val hourlyHeartbeatDurationFromUptime = actualCollectionTime.elapsedRealtime.duration -
247250
lastHeartbeatUptime.elapsedRealtime.duration
248251

249252
val hourlyHeartbeatDuration = hourlyHeartbeatDurationFromReport
@@ -253,15 +256,15 @@ class MetricsCollectionTask @Inject constructor(
253256
val heartbeatReportInternalMetrics = hourlyHeartbeatReport.internalMetrics
254257

255258
clientRateLimitCollector.collect(
256-
collectionTime = collectionTime,
259+
collectionTime = actualCollectionTime,
257260
internalHeartbeatReportMetrics = heartbeatReportInternalMetrics,
258261
)
259262

260263
// If there were no heartbeat internal metrics, then fallback to include some core values.
261264
val internalMetrics = heartbeatReportInternalMetrics.ifEmpty { fallbackInternalMetrics }
262265
uploadHeartbeat(
263266
batteryStatsFile = batteryStatsResult.batteryStatsFileToUpload,
264-
collectionTime = collectionTime,
267+
collectionTime = actualCollectionTime,
265268
heartbeatInterval = hourlyHeartbeatDuration,
266269
heartbeatReportMetrics = heartbeatReportMetrics +
267270
batteryStatsResult.aggregatedMetrics +
@@ -280,7 +283,7 @@ class MetricsCollectionTask @Inject constructor(
280283
hourlyHeartbeatReport.hrt?.let { hrtFile ->
281284
val hrtMetricsToAdd = batteryStatsResult.batteryStatsHrt +
282285
inMemoryHrtRollups +
283-
propertiesStore.hrtRollups(timestampMs = collectionTime.timestamp.toEpochMilli())
286+
propertiesStore.hrtRollups(timestampMs = actualCollectionTime.timestamp.toEpochMilli())
284287
if (hrtMetricsToAdd.isNotEmpty()) {
285288
mergeHrtIntoFile(hrtFile, hrtMetricsToAdd)
286289
}
@@ -295,7 +298,7 @@ class MetricsCollectionTask @Inject constructor(
295298
heartbeatReport.dailyHeartbeatReport?.let { report ->
296299
uploadHeartbeat(
297300
batteryStatsFile = null,
298-
collectionTime = collectionTime,
301+
collectionTime = actualCollectionTime,
299302
heartbeatInterval = (report.endTimestampMs - report.startTimestampMs)
300303
.toDuration(MILLISECONDS),
301304
heartbeatReportMetrics = report.metrics +
@@ -315,7 +318,7 @@ class MetricsCollectionTask @Inject constructor(
315318
.forEach { session ->
316319
uploadHeartbeat(
317320
batteryStatsFile = null,
318-
collectionTime = collectionTime,
321+
collectionTime = actualCollectionTime,
319322
heartbeatInterval = (session.endTimestampMs - session.startTimestampMs).toDuration(MILLISECONDS),
320323
heartbeatReportMetrics = session.metrics,
321324
heartbeatReportInternalMetrics = session.internalMetrics,

0 commit comments

Comments
 (0)