diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b9ca1db4a..4ba6d2ff1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ [Full changelog](https://github.com/mozilla/glean/compare/v26.0.0...master) * General: + * BUGFIX: baseline pings sent at startup with the `dirty_startup` reason will now include application lifetime metrics. * Glean will now detect when the upload enabled flag changes outside of the application, for example due to a change in a config file. This means that if upload is disabled while the application wasn't running (e.g. between the runs of a Python command using the Glean SDK), the database is correctly cleared and a deletion request ping is sent. See [#791](https://github.com/mozilla/glean/pull/791). * The `events` ping now includes a reason code: `startup`, `background` or `max_capacity`. * iOS: diff --git a/glean-core/android/src/main/java/mozilla/telemetry/glean/Glean.kt b/glean-core/android/src/main/java/mozilla/telemetry/glean/Glean.kt index 018b919ab3..b05048a45d 100644 --- a/glean-core/android/src/main/java/mozilla/telemetry/glean/Glean.kt +++ b/glean-core/android/src/main/java/mozilla/telemetry/glean/Glean.kt @@ -189,6 +189,16 @@ open class GleanInternalAPI internal constructor () { metricsPingScheduler = MetricsPingScheduler(applicationContext) metricsPingScheduler.schedule() + // Check if the "dirty flag" is set. That means the product was probably + // force-closed. If that's the case, submit a 'baseline' ping with the + // reason "dirty_startup". We only do that from the second run. + if (!isFirstRun && LibGleanFFI.INSTANCE.glean_is_dirty_flag_set().toBoolean()) { + submitPingByNameSync("baseline", "dirty_startup") + // Note: while in theory we should set the "dirty flag" to true + // here, in practice it's not needed: if it hits this branch, it + // means the value was `true` and nothing needs to be done. + } + // From the second time we run, after all startup pings are generated, // make sure to clear `lifetime: application` metrics and set them again. // Any new value will be sent in newly generated pings after startup. @@ -200,16 +210,6 @@ open class GleanInternalAPI internal constructor () { // Signal Dispatcher that init is complete Dispatchers.API.flushQueuedInitialTasks() - // Check if the "dirty flag" is set. That means the product was probably - // force-closed. If that's the case, submit a 'baseline' ping with the - // reason "dirty_startup". We only do that from the second run. - if (!isFirstRun && LibGleanFFI.INSTANCE.glean_is_dirty_flag_set().toBoolean()) { - submitPingByNameSync("baseline", "dirty_startup") - // Note: while in theory we should set the "dirty flag" to true - // here, in practice it's not needed: if it hits this branch, it - // means the value was `true` and nothing needs to be done. - } - // At this point, all metrics and events can be recorded. // This should only be called from the main thread. This is enforced by // the @MainThread decorator and the `assertOnUiThread` call. diff --git a/glean-core/android/src/test/java/mozilla/telemetry/glean/GleanTest.kt b/glean-core/android/src/test/java/mozilla/telemetry/glean/GleanTest.kt index 80e942d642..85dd9b2828 100644 --- a/glean-core/android/src/test/java/mozilla/telemetry/glean/GleanTest.kt +++ b/glean-core/android/src/test/java/mozilla/telemetry/glean/GleanTest.kt @@ -701,4 +701,46 @@ class GleanTest { assertEquals(0, server.requestCount) } + + @Test + fun `test sending of startup baseline ping with application lifetime metric`() { + // Set the dirty flag. + LibGleanFFI.INSTANCE.glean_set_dirty_flag(true.toByte()) + + val stringMetric = StringMetricType( + disabled = false, + category = "telemetry", + lifetime = Lifetime.Application, + name = "app_lifetime", + sendInPings = listOf("baseline") + ) + stringMetric.set("HELLOOOOO!") + + // Restart glean and don't clear the stores. + val server = getMockWebServer() + val context = getContextWithMockedInfo() + resetGlean(context, Glean.configuration.copy( + serverEndpoint = "http://" + server.hostName + ":" + server.port, + logPings = true + ), false) + + try { + // Trigger worker task to upload the pings in the background + triggerWorkManager(context) + + val request = server.takeRequest(20L, TimeUnit.SECONDS) + val docType = request.path.split("/")[3] + assertEquals("The received ping must be a 'baseline' ping", "baseline", docType) + + val baselineJson = JSONObject(request.body.readUtf8()) + assertEquals("dirty_startup", baselineJson.getJSONObject("ping_info")["reason"]) + checkPingSchema(baselineJson) + + val appLifetimeMetricsObject = baselineJson.getJSONObject("metrics") + val appLifetimeStringMetrics = appLifetimeMetricsObject.getJSONObject("string") + assertEquals("HELLOOOOO!", appLifetimeStringMetrics.get("telemetry.app_lifetime")) + } finally { + server.shutdown() + } + } }