1
0
Fork 0

For #12802: add StorageStats glean metrics.

master
Michael Comella 2020-07-21 15:28:02 -07:00 committed by Jeff Boek
parent 70c66185d8
commit 5d8c900391
5 changed files with 223 additions and 0 deletions

View File

@ -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"

View File

@ -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() {

View File

@ -0,0 +1,66 @@
/* 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)
}
}
}

View File

@ -0,0 +1,61 @@
/* 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())
}
}

File diff suppressed because one or more lines are too long