Commit 5d8c9003 authored by Michael Comella's avatar Michael Comella Committed by Jeff Boek
Browse files

For #12802: add StorageStats glean metrics.

parent 70c66185
......@@ -3236,3 +3236,82 @@ autoplay:
notification_emails:
- fenix-core@mozilla.com
expires: "2021-02-01"
storage.stats:
query_stats_duration:
send_in_pings:
- metrics
type: timing_distribution
description: >
How long it took to query the device for the StorageStats that contain the
file size information. The docs say it may be expensive so we want to
ensure it's not too expensive. This value is only available on Android
8+.
bugs:
- https://github.com/mozilla-mobile/fenix/issues/12802
data_reviews:
- todo
notification_emails:
- fenix-core@mozilla.com
- perf-android-fe@mozilla.com
- mcomella@mozilla.com
expires: "2020-12-21"
app_bytes:
send_in_pings:
- metrics
type: memory_distribution
description: >
The size of the app's APK and related files as installed: this is expected
to be larger than download size. This is the output of
[StorageStats.getAppBytes](https://developer.android.com/reference/android/app/usage/StorageStats#getAppBytes())
so see that for details. This value is only available on Android 8+. A
similar value may be available on the Google Play dashboard: we can use
this value to see if that value is reliable enough.
memory_unit: byte
bugs:
- https://github.com/mozilla-mobile/fenix/issues/12802
data_reviews:
- todo
notification_emails:
- fenix-core@mozilla.com
- perf-android-fe@mozilla.com
- mcomella@mozilla.com
expires: "2020-12-21"
cache_bytes:
send_in_pings:
- metrics
type: memory_distribution
description: >
The size of all cached data in the app. This is the output of
[StorageStats.getCacheBytes](https://developer.android.com/reference/android/app/usage/StorageStats#getCacheBytes())
so see that for details. This value is only available on Android 8+.
memory_unit: byte
bugs:
- https://github.com/mozilla-mobile/fenix/issues/12802
data_reviews:
- todo
notification_emails:
- fenix-core@mozilla.com
- perf-android-fe@mozilla.com
- mcomella@mozilla.com
expires: "2020-12-21"
data_dir_bytes:
send_in_pings:
- metrics
type: memory_distribution
description: >
The size of all data minus `cache_bytes`. This is the output of
[StorageStats.getDataBytes](https://developer.android.com/reference/android/app/usage/StorageStats#getDataBytes())
except we subtract the value of `cache_bytes` so the cache is not measured
redundantly; see that method for details. This value is only available on
Android 8+.
memory_unit: byte
bugs:
- https://github.com/mozilla-mobile/fenix/issues/12802
data_reviews:
- todo
notification_emails:
- fenix-core@mozilla.com
- perf-android-fe@mozilla.com
- mcomella@mozilla.com
expires: "2020-12-21"
......@@ -44,6 +44,7 @@ import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.metrics.MetricServiceType
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.perf.StorageStatsMetrics
import org.mozilla.fenix.perf.StartupTimeline
import org.mozilla.fenix.push.PushFxaIntegration
import org.mozilla.fenix.push.WebPushEngineIntegration
......@@ -205,12 +206,24 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
}
}
fun queueMetrics() {
if (SDK_INT >= Build.VERSION_CODES.O) { // required by StorageStatsMetrics.
taskQueue.runIfReadyOrQueue {
// Because it may be slow to capture the storage stats, it might be preferred to
// create a WorkManager task for this metric, however, I ran out of
// implementation time and WorkManager is harder to test.
StorageStatsMetrics.report(this.applicationContext)
}
}
}
initQueue()
// We init these items in the visual completeness queue to avoid them initing in the critical
// startup path, before the UI finishes drawing (i.e. visual completeness).
queueInitExperiments()
queueInitStorageAndServices()
queueMetrics()
}
private fun startMetricsIfEnabled() {
......
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.perf
import android.app.usage.StorageStats
import android.app.usage.StorageStatsManager
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PRIVATE
import androidx.annotation.WorkerThread
import androidx.core.content.getSystemService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.mozilla.fenix.GleanMetrics.StorageStats as Metrics
/**
* A collection of functions related to measuring the [StorageStats] of the application such as data
* dir size.
*
* Unfortunately, this API is only available on API 26+ so the data will only be reported for those
* platforms.
*/
@RequiresApi(Build.VERSION_CODES.O) // StorageStatsManager
object StorageStatsMetrics {
fun report(context: Context) {
GlobalScope.launch(Dispatchers.IO) {
reportSync(context)
}
}
// I couldn't get runBlockingTest to work correctly so I moved the functionality under test to
// a synchronous function.
@VisibleForTesting(otherwise = PRIVATE)
@WorkerThread // queryStatsForUid
fun reportSync(context: Context) {
// I don't expect this to ever be null so we don't report if so.
context.getSystemService<StorageStatsManager>()?.let { storageStatsManager ->
val appInfo = context.applicationInfo
val storageStats = Metrics.queryStatsDuration.measure {
// The docs say queryStatsForPackage may be slower if the app uses
// android:sharedUserId so we the suggested alternative.
//
// The docs say this may be slow:
// > This method may take several seconds to complete, so it should only be called
// > from a worker thread.
//
// So we call from a worker thread and measure the duration to make sure it's not
// too slow.
storageStatsManager.queryStatsForUid(appInfo.storageUuid, appInfo.uid)
}
// dataBytes includes the cache so we subtract it.
val justDataDirBytes = storageStats.dataBytes - storageStats.cacheBytes
Metrics.dataDirBytes.accumulate(justDataDirBytes)
Metrics.appBytes.accumulate(storageStats.appBytes)
Metrics.cacheBytes.accumulate(storageStats.cacheBytes)
}
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.perf
import android.app.usage.StorageStats
import android.app.usage.StorageStatsManager
import android.content.Context
import androidx.core.content.getSystemService
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.RelaxedMockK
import mozilla.components.service.glean.testing.GleanTestRule
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.GleanMetrics.StorageStats as Metrics
@RunWith(FenixRobolectricTestRunner::class) // gleanTestRule
class StorageStatsMetricsTest {
@get:Rule
val gleanTestRule = GleanTestRule(testContext)
@RelaxedMockK private lateinit var mockContext: Context
@RelaxedMockK private lateinit var storageStats: StorageStats
@Before
fun setUp() {
MockKAnnotations.init(this)
every {
mockContext.getSystemService<StorageStatsManager>()?.queryStatsForUid(any(), any())
} returns storageStats
}
@Test
fun `WHEN reporting THEN the values from the storageStats are accumulated`() {
every { storageStats.appBytes } returns 100
every { storageStats.cacheBytes } returns 200
every { storageStats.dataBytes } returns 1000
StorageStatsMetrics.reportSync(mockContext)
assertEquals(100, Metrics.appBytes.testGetValue().sum)
assertEquals(200, Metrics.cacheBytes.testGetValue().sum)
assertEquals(800, Metrics.dataDirBytes.testGetValue().sum)
}
@Test
fun `WHEN reporting THEN the query duration is measured`() {
StorageStatsMetrics.reportSync(mockContext)
assertTrue(Metrics.queryStatsDuration.testHasValue())
}
}
......@@ -311,6 +311,10 @@ The following metrics are added to the ping:
| search.default_engine.code |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |If the search engine is pre-loaded with Fenix this value will be the search engine identifier. If it's a custom search engine (defined: https://github.com/mozilla-mobile/fenix/issues/1607) the value will be "custom" |[1](https://github.com/mozilla-mobile/fenix/pull/1606), [2](https://github.com/mozilla-mobile/fenix/pull/5216)||2020-10-01 | |
| search.default_engine.name |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |If the search engine is pre-loaded with Fenix this value will be the search engine name. If it's a custom search engine (defined: https://github.com/mozilla-mobile/fenix/issues/1607) the value will be "custom" |[1](https://github.com/mozilla-mobile/fenix/pull/1606), [2](https://github.com/mozilla-mobile/fenix/pull/5216)||2020-10-01 | |
| search.default_engine.submission_url |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |If the search engine is pre-loaded with Fenix this value will be he base URL we use to build the search query for the search engine. For example: https://mysearchengine.com/?query=%s. If it's a custom search engine (defined: https://github.com/mozilla-mobile/fenix/issues/1607) the value will be "custom" |[1](https://github.com/mozilla-mobile/fenix/pull/1606), [2](https://github.com/mozilla-mobile/fenix/pull/5216)||2020-10-01 | |
| storage.stats.app_bytes |[memory_distribution](https://mozilla.github.io/glean/book/user/metrics/memory_distribution.html) |The size of the app's APK and related files as installed: this is expected to be larger than download size. This is the output of [StorageStats.getAppBytes](https://developer.android.com/reference/android/app/usage/StorageStats#getAppBytes()) so see that for details. This value is only available on Android 8+. A similar value may be available on the Google Play dashboard: we can use this value to see if that value is reliable enough. |[1](todo)||2020-12-21 | |
| storage.stats.cache_bytes |[memory_distribution](https://mozilla.github.io/glean/book/user/metrics/memory_distribution.html) |The size of all cached data in the app. This is the output of [StorageStats.getCacheBytes](https://developer.android.com/reference/android/app/usage/StorageStats#getCacheBytes()) so see that for details. This value is only available on Android 8+. |[1](todo)||2020-12-21 | |
| storage.stats.data_dir_bytes |[memory_distribution](https://mozilla.github.io/glean/book/user/metrics/memory_distribution.html) |The size of all data minus `cache_bytes`. This is the output of [StorageStats.getDataBytes](https://developer.android.com/reference/android/app/usage/StorageStats#getDataBytes()) except we subtract the value of `cache_bytes` so the cache is not measured redundantly; see that method for details. This value is only available on Android 8+. |[1](todo)||2020-12-21 | |
| storage.stats.query_stats_duration |[timing_distribution](https://mozilla.github.io/glean/book/user/metrics/timing_distribution.html) |How long it took to query the device for the StorageStats that contain the file size information. The docs say it may be expensive so we want to ensure it's not too expensive. This value is only available on Android 8+. |[1](todo)||2020-12-21 | |
## startup-timeline
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment