Skip to content

Commit 04f58a6

Browse files
author
Memfault Inc.
committed
Memfault BORT SDK 4.16.0 (Build 2097172)
1 parent 9a547ce commit 04f58a6

File tree

77 files changed

+3012
-280
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+3012
-280
lines changed

CHANGELOG.md

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

3+
## v4.16.0 - May 28, 2024
4+
5+
### :rocket: New Features
6+
7+
- Added a `latestInReport` option to the `event` API in `Reporting`. This will
8+
create a heartbeat metric with the latest value.
9+
- New Bort diagnostics - to improve diagnosing any SDK integration issues:
10+
- The SDK validation tool is enhanced to check Bort's runtime state (whether
11+
Bort is enabled, job execution state, recent failures, etc), and report this
12+
in the validation tool output. This will also now report several key
13+
compile-time configuration parameters.
14+
- Bort will upload more types of error to the
15+
[Integration Hub Processing Log](https://mflt.io/processing-log)
16+
(`batterystats` parsing errors, file cleanup errors, and job failures).
17+
- Added a new [SDK Setting](https://mflt.io/sdk-settings) ("Allow OTA downloads
18+
based on the Network Type") to allow configuring OTA downloads to never happen
19+
on a roaming cellular network. This is unset by default, and will override the
20+
existing "Allow OTA downloads on Metered Networks" setting if configured.
21+
22+
### :chart_with_upwards_trend: Improvements
23+
24+
- Removed usages of `launch` when calling coroutines - avoiding any potential
25+
dangling work after a job/receiver has completed.
26+
27+
### :house: Internal
28+
29+
- Added support for V1 metrics to the Bort custom metrics database (this is not
30+
used yet, outside of Bort Lite).
31+
332
## v4.15.3 - May 20, 2024
433

534
### :chart_with_upwards_trend: Improvements

MemfaultPackages/bort-ota-lib/src/main/java/com/memfault/bort/FallbackOtaSettings.kt

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

33
import android.app.Application
4+
import androidx.work.NetworkType
45
import androidx.work.NetworkType.CONNECTED
56
import androidx.work.NetworkType.UNMETERED
67
import com.memfault.bort.reporting.Reporting
@@ -47,7 +48,9 @@ class FallbackOtaSettings @Inject constructor(
4748
updateCheckIntervalMs = bundledSdkSettings.otaUpdateCheckInterval.duration.inWholeMilliseconds,
4849
baseUrl = bundledSdkSettings.httpApiDeviceBaseUrl,
4950
projectApiKey = BuildConfig.MEMFAULT_PROJECT_API_KEY,
50-
downloadNetworkTypeConstraint = if (bundledSdkSettings.otaDownloadNetworkConstraintAllowMeteredConnection) {
51+
downloadNetworkTypeConstraint = NetworkType.entries.firstOrNull {
52+
it.name.equals(bundledSdkSettings.otaDownloadNetworkConstraint, ignoreCase = true)
53+
} ?: if (bundledSdkSettings.otaDownloadNetworkConstraintAllowMeteredConnection) {
5154
CONNECTED
5255
} else {
5356
UNMETERED

MemfaultPackages/bort-ota-lib/src/main/java/com/memfault/bort/ota/lib/OtaDownloadWorker.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import androidx.work.WorkerParameters
1414
import com.memfault.bort.ota.lib.download.DownloadOtaService.Companion.setupForegroundNotification
1515
import com.memfault.bort.ota.lib.download.NOTIFICATION_ID
1616
import com.memfault.bort.shared.Logger
17+
import com.memfault.bort.shared.NoOpJobReporter
1718
import com.memfault.bort.shared.runAndTrackExceptions
1819
import dagger.assisted.Assisted
1920
import dagger.assisted.AssistedInject
@@ -35,7 +36,7 @@ class OtaDownloadWorker @AssistedInject constructor(
3536
) : CoroutineWorker(appContext, params) {
3637
private var lastReportedPercentage = -1
3738

38-
override suspend fun doWork(): Result = runAndTrackExceptions(jobName = "OtaDownloadWorker") {
39+
override suspend fun doWork(): Result = runAndTrackExceptions(jobName = "OtaDownloadWorker", NoOpJobReporter) {
3940
downloadWorkerRun(updater, otaRulesProvider, isAbDevice, applicationContext, ::maybeSetForeground)
4041
}
4142

MemfaultPackages/bort-ota-lib/src/main/java/com/memfault/bort/ota/lib/OtaInstallWorker.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import androidx.work.OneTimeWorkRequestBuilder
1111
import androidx.work.WorkManager
1212
import androidx.work.WorkerParameters
1313
import com.memfault.bort.shared.Logger
14+
import com.memfault.bort.shared.NoOpJobReporter
1415
import com.memfault.bort.shared.runAndTrackExceptions
1516
import dagger.assisted.Assisted
1617
import dagger.assisted.AssistedInject
@@ -27,7 +28,7 @@ class OtaInstallWorker @AssistedInject constructor(
2728
private val updater: Updater,
2829
private val otaRulesProvider: OtaRulesProvider,
2930
) : CoroutineWorker(appContext, params) {
30-
override suspend fun doWork(): Result = runAndTrackExceptions(jobName = "OtaDownloadWorker") {
31+
override suspend fun doWork(): Result = runAndTrackExceptions(jobName = "OtaDownloadWorker", NoOpJobReporter) {
3132
installWorkerRun(updater, otaRulesProvider)
3233
}
3334

MemfaultPackages/bort-ota-lib/src/main/java/com/memfault/bort/ota/lib/PeriodicDownloadCompletionWorker.kt

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.work.ExistingPeriodicWorkPolicy
77
import androidx.work.PeriodicWorkRequestBuilder
88
import androidx.work.WorkManager
99
import androidx.work.WorkerParameters
10+
import com.memfault.bort.shared.NoOpJobReporter
1011
import com.memfault.bort.shared.runAndTrackExceptions
1112
import dagger.assisted.Assisted
1213
import dagger.assisted.AssistedInject
@@ -22,15 +23,16 @@ class PeriodicDownloadCompletionWorker @AssistedInject constructor(
2223
@Assisted params: WorkerParameters,
2324
private val updater: Updater,
2425
) : CoroutineWorker(appContext, params) {
25-
override suspend fun doWork(): Result = runAndTrackExceptions(jobName = "PeriodicSoftwareUpdateWorker") {
26-
// This doesn't do anything except wake up the app.
26+
override suspend fun doWork(): Result =
27+
runAndTrackExceptions(jobName = "PeriodicSoftwareUpdateWorker", NoOpJobReporter) {
28+
// This doesn't do anything except wake up the app.
2729

28-
if (updater.badCurrentUpdateState() !is State.UpdateDownloading) {
29-
// If not downloading, then cancel this task (we only need it to run again if download hasn't finished).
30-
cancel(appContext)
30+
if (updater.badCurrentUpdateState() !is State.UpdateDownloading) {
31+
// If not downloading, then cancel this task (we only need it to run again if download hasn't finished).
32+
cancel(appContext)
33+
}
34+
Result.success()
3135
}
32-
Result.success()
33-
}
3436

3537
companion object {
3638
fun schedule(context: Context) {

MemfaultPackages/bort-ota-lib/src/main/java/com/memfault/bort/ota/lib/PeriodicSoftwareUpdateWorker.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import androidx.work.WorkerParameters
1212
import com.memfault.bort.DEV_MODE_DISABLED
1313
import com.memfault.bort.shared.JitterDelayProvider
1414
import com.memfault.bort.shared.Logger
15+
import com.memfault.bort.shared.NoOpJobReporter
1516
import com.memfault.bort.shared.runAndTrackExceptions
1617
import dagger.assisted.Assisted
1718
import dagger.assisted.AssistedInject
@@ -26,10 +27,11 @@ class PeriodicSoftwareUpdateWorker @AssistedInject constructor(
2627
@Assisted params: WorkerParameters,
2728
private val updater: Updater,
2829
) : CoroutineWorker(appContext, params) {
29-
override suspend fun doWork(): Result = runAndTrackExceptions(jobName = "PeriodicSoftwareUpdateWorker") {
30-
updater.perform(Action.CheckForUpdate(background = true))
31-
Result.success()
32-
}
30+
override suspend fun doWork(): Result =
31+
runAndTrackExceptions(jobName = "PeriodicSoftwareUpdateWorker", NoOpJobReporter) {
32+
updater.perform(Action.CheckForUpdate(background = true))
33+
Result.success()
34+
}
3335

3436
companion object {
3537
fun schedule(

MemfaultPackages/bort-shared/src/main/java/com/memfault/bort/settings/FetchedSettings.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ data class FetchedSettings(
302302
@Serializable(with = DurationAsMillisecondsLong::class)
303303
val otaUpdateCheckInterval: BoxedDuration = 12.hours.boxed(),
304304

305+
@SerialName("ota.download_network_constraint")
306+
val otaDownloadNetworkConstraint: String = "UNSET",
307+
305308
@SerialName("ota.download_network_constraint_allow_metered_connection")
306309
val otaDownloadNetworkConstraintAllowMeteredConnection: Boolean = false,
307310

MemfaultPackages/bort-shared/src/main/java/com/memfault/bort/shared/WorkerLogger.kt

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,60 @@ package com.memfault.bort.shared
33
import androidx.work.ListenableWorker.Result
44
import com.memfault.bort.reporting.Reporting
55

6+
interface JobReporter {
7+
/**
8+
* Job started. Returns an ID which should be used to call [onJobFinished] when the job finishes.
9+
*/
10+
suspend fun onJobStarted(jobName: String): Long
11+
12+
/**
13+
* Job finished (successfully or not). Pass in the [identifier] returned by [onJobStarted].
14+
*/
15+
suspend fun onJobFinished(
16+
identifier: Long,
17+
result: String,
18+
)
19+
20+
/**
21+
* Record a job error. Called in addition to [onJobFinished].
22+
*/
23+
suspend fun onJobError(jobName: String, e: Throwable)
24+
}
25+
26+
object NoOpJobReporter : JobReporter {
27+
override suspend fun onJobStarted(jobName: String): Long = 0
28+
29+
override suspend fun onJobFinished(
30+
identifier: Long,
31+
result: String,
32+
) = Unit
33+
34+
override suspend fun onJobError(
35+
jobName: String,
36+
e: Throwable,
37+
) = Unit
38+
}
39+
640
/**
741
* To be used from [CoroutineWorker] implementations. Does the work, logging any unhandled exceptions as metrics (and
842
* rethrowing them).
943
*/
10-
suspend fun runAndTrackExceptions(jobName: String?, doWork: suspend () -> Result): Result = try {
11-
doWork()
12-
} catch (t: Throwable) {
13-
JOB_EXCEPTION_METRIC.add("$jobName: ${t.stackTraceToString().take(500)}")
14-
throw t
44+
suspend fun runAndTrackExceptions(
45+
jobName: String,
46+
jobReporter: JobReporter,
47+
doWork: suspend () -> Result,
48+
): Result {
49+
val id = jobReporter.onJobStarted(jobName)
50+
return try {
51+
val result = doWork()
52+
jobReporter.onJobFinished(id, result.toString())
53+
result
54+
} catch (t: Throwable) {
55+
jobReporter.onJobFinished(id, "Error: ${t.message}")
56+
jobReporter.onJobError(jobName, t)
57+
JOB_EXCEPTION_METRIC.add("$jobName: ${t.stackTraceToString().take(500)}")
58+
throw t
59+
}
1560
}
1661

1762
private val JOB_EXCEPTION_METRIC = Reporting.report().event(name = "job_error", countInReport = true, internal = true)
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{
2+
"formatVersion": 1,
3+
"database": {
4+
"version": 1,
5+
"identityHash": "146ad091c94a9a168ed3a20485e2db99",
6+
"entities": [
7+
{
8+
"tableName": "errors",
9+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timeMs` INTEGER NOT NULL, `type` TEXT NOT NULL, `eventData` TEXT NOT NULL, `uploaded` INTEGER NOT NULL)",
10+
"fields": [
11+
{
12+
"fieldPath": "id",
13+
"columnName": "id",
14+
"affinity": "INTEGER",
15+
"notNull": true
16+
},
17+
{
18+
"fieldPath": "timeMs",
19+
"columnName": "timeMs",
20+
"affinity": "INTEGER",
21+
"notNull": true
22+
},
23+
{
24+
"fieldPath": "type",
25+
"columnName": "type",
26+
"affinity": "TEXT",
27+
"notNull": true
28+
},
29+
{
30+
"fieldPath": "eventData",
31+
"columnName": "eventData",
32+
"affinity": "TEXT",
33+
"notNull": true
34+
},
35+
{
36+
"fieldPath": "uploaded",
37+
"columnName": "uploaded",
38+
"affinity": "INTEGER",
39+
"notNull": true
40+
}
41+
],
42+
"primaryKey": {
43+
"autoGenerate": true,
44+
"columnNames": ["id"]
45+
},
46+
"indices": [],
47+
"foreignKeys": []
48+
},
49+
{
50+
"tableName": "jobs",
51+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `jobName` TEXT NOT NULL, `startTimeMs` INTEGER NOT NULL, `endTimeMs` INTEGER, `result` TEXT)",
52+
"fields": [
53+
{
54+
"fieldPath": "id",
55+
"columnName": "id",
56+
"affinity": "INTEGER",
57+
"notNull": true
58+
},
59+
{
60+
"fieldPath": "jobName",
61+
"columnName": "jobName",
62+
"affinity": "TEXT",
63+
"notNull": true
64+
},
65+
{
66+
"fieldPath": "startTimeMs",
67+
"columnName": "startTimeMs",
68+
"affinity": "INTEGER",
69+
"notNull": true
70+
},
71+
{
72+
"fieldPath": "endTimeMs",
73+
"columnName": "endTimeMs",
74+
"affinity": "INTEGER",
75+
"notNull": false
76+
},
77+
{
78+
"fieldPath": "result",
79+
"columnName": "result",
80+
"affinity": "TEXT",
81+
"notNull": false
82+
}
83+
],
84+
"primaryKey": {
85+
"autoGenerate": true,
86+
"columnNames": ["id"]
87+
},
88+
"indices": [],
89+
"foreignKeys": []
90+
}
91+
],
92+
"views": [],
93+
"setupQueries": [
94+
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
95+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '146ad091c94a9a168ed3a20485e2db99')"
96+
]
97+
}
98+
}

MemfaultPackages/bort/src/debug/java/com/memfault/bort/TestSettingsProvider.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ class TestSettingsProvider @Inject constructor(
4141
fun override() = testOverrides.useTestSettingOverrides.getValue()
4242

4343
override val httpApiSettings = object : HttpApiSettings by settings.httpApiSettings {
44-
override val batchMarUploads: Boolean
45-
get() = if (override()) false else settings.httpApiSettings.batchMarUploads
46-
4744
// Specifically for Bort Lite tests, where the apk is targeting prod:
4845
override val deviceBaseUrl: String
4946
get() = "http://localhost:8000"

0 commit comments

Comments
 (0)