Skip to content
This repository was archived by the owner on Nov 1, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions buildSrc/src/main/java/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ object Versions {

const val mozilla_appservices = "0.15.0"
const val servo = "0.0.1.20181017.aa95911"

const val work_testing = "1.0.0-beta05"
}

// Synchronized dependencies used by (some) modules
Expand Down Expand Up @@ -90,4 +92,6 @@ object Dependencies {
const val thirdparty_okhttp = "com.squareup.okhttp3:okhttp:${Versions.okhttp}"
const val thirdparty_okhttp_urlconnection = "com.squareup.okhttp3:okhttp-urlconnection:${Versions.okhttp}"
const val thirdparty_sentry = "io.sentry:sentry-android:${Versions.sentry}"

const val androidx_work_testing = "android.arch.work:work-testing:${Versions.work_testing}"
}
2 changes: 2 additions & 0 deletions components/service/glean/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dependencies {
implementation Dependencies.kotlin_stdlib
implementation Dependencies.kotlin_coroutines
implementation Dependencies.arch_lifecycle
implementation Dependencies.arch_workmanager

implementation project(':support-ktx')
implementation project(':support-base')
Expand All @@ -50,6 +51,7 @@ dependencies {
testImplementation Dependencies.testing_robolectric
testImplementation Dependencies.testing_mockito
testImplementation Dependencies.testing_mockwebserver
testImplementation Dependencies.androidx_work_testing
}

apply from: '../../../publish.gradle'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,35 @@

package mozilla.components.service.glean

import android.arch.lifecycle.ProcessLifecycleOwner
import android.content.Context
import android.content.pm.PackageManager
import android.support.annotation.VisibleForTesting
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

import java.util.UUID

import mozilla.components.service.glean.config.Configuration
import mozilla.components.service.glean.firstrun.FileFirstRunDetector
import mozilla.components.service.glean.GleanMetrics.GleanInternalMetrics
import mozilla.components.service.glean.net.HttpPingUploader
import mozilla.components.service.glean.ping.PingMaker
import mozilla.components.service.glean.scheduler.GleanLifecycleObserver
import mozilla.components.service.glean.scheduler.PingScheduler
import mozilla.components.service.glean.storages.ExperimentsStorageEngine
import mozilla.components.service.glean.storages.StorageEngineManager
import mozilla.components.service.glean.ping.BaselinePing
import mozilla.components.service.glean.scheduler.MetricsPingScheduler
import mozilla.components.service.glean.storages.StringsStorageEngine
import mozilla.components.service.glean.storages.UuidsStorageEngine
import mozilla.components.support.base.log.logger.Logger
import java.io.File
import java.util.UUID

@Suppress("TooManyFunctions")
open class GleanInternalAPI internal constructor () {
private val logger = Logger("glean/Glean")

// Include our singletons of StorageEngineManager and PingMaker
private lateinit var storageEngineManager: StorageEngineManager
private lateinit var pingMaker: PingMaker
internal lateinit var configuration: Configuration

// `internal` so this can be modified for testing
internal lateinit var httpPingUploader: HttpPingUploader

private val gleanLifecycleObserver by lazy { GleanLifecycleObserver() }
internal lateinit var pingScheduler: PingScheduler

// `internal` so this can be modified for testing
internal var initialized = false
private var uploadEnabled = true

// The application id detected by glean to be used as part of the submission
// endpoint.
internal lateinit var applicationId: String

// This object holds data related to any persistent information about the metrics ping,
// such as the last time it was sent and the store name
internal lateinit var metricsPingScheduler: MetricsPingScheduler

// This object encapsulates initialization and information related to the baseline ping
internal lateinit var baselinePing: BaselinePing

Expand All @@ -63,10 +41,10 @@ open class GleanInternalAPI internal constructor () {
*
* This should only be initialized once by the application, and not by
* libraries using glean. A message is logged to error and no changes are made
* to the state if initialize is called a more than once.
* to the state if initialize is called more than once.
*
* A LifecycleObserver will be added to send pings when the application goes
* into the background.
* A [PingScheduler] will be added as a [LifecycleObserver] to schedule pings when the
* application goes into the background.
*
* @param applicationContext [Context] to access application features, such
* as shared preferences
Expand All @@ -82,15 +60,12 @@ open class GleanInternalAPI internal constructor () {
}

storageEngineManager = StorageEngineManager(applicationContext = applicationContext)
pingMaker = PingMaker(storageEngineManager, applicationContext)
pingScheduler = PingScheduler(applicationContext, storageEngineManager)
this.configuration = configuration
httpPingUploader = HttpPingUploader()
initialized = true
applicationId = sanitizeApplicationId(applicationContext.packageName)

initializeCoreMetrics(applicationContext)

ProcessLifecycleOwner.get().lifecycle.addObserver(gleanLifecycleObserver)
initialized = true
}

/**
Expand Down Expand Up @@ -154,40 +129,6 @@ open class GleanInternalAPI internal constructor () {
ExperimentsStorageEngine.clearAllStores()
}

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal fun makePath(docType: String, uuid: UUID): String {
return "/submit/$applicationId/$docType/${Glean.SCHEMA_VERSION}/$uuid"
}

/**
* Collect and assemble the ping. Asynchronously submits the assembled
* payload to the designated server using [httpPingUploader] but only
* if upload is enabled, and there is ping data to send.
*/
internal fun sendPing(store: String, docType: String) {
if (!isInitialized()) {
logger.error("Attempted to send ping before Glean was initialized.")
return
}

// Do not send ping if upload is disabled
if (!getUploadEnabled()) {
logger.debug("Attempt to send ping \"$store\" but metrics disabled, aborting send.")
return
}

// Build and send the ping on an async thread. Since the pingMaker.collect() function
// returns null if there is nothing to send we can use this to avoid sending an empty ping
pingMaker.collect(store)?.let { pingContent ->
val uuid = UUID.randomUUID()
val path = makePath(docType, uuid)
// Asynchronously perform the HTTP upload off the main thread.
GlobalScope.launch(Dispatchers.IO) {
httpPingUploader.upload(path = path, data = pingContent, config = configuration)
}
}
}

/**
* Initialize the core metrics internally managed by Glean (e.g. client id).
*/
Expand Down Expand Up @@ -238,52 +179,16 @@ open class GleanInternalAPI internal constructor () {
throw AssertionError("Could not get own package info, aborting init")
}

// Set up information and scheduling for glean owned pings
metricsPingScheduler = MetricsPingScheduler(applicationContext)
// Create a baseline ping to initialize the associated metrics
baselinePing = BaselinePing(applicationContext)
}

/**
* Sanitizes the application id, generating a pipeline-friendly string that replaces
* non alphanumeric characters with dashes.
*
* @param applicationId the string representing the application id
*
* @return the sanitized version of the application id
* Helper function to force scheduling of pings immediately. Note that the 'metrics' ping
* schedule is still dependent on it's internal schedule of no more than once every 24 hours.
*/
internal fun sanitizeApplicationId(applicationId: String): String {
return applicationId.replace("[^a-zA-Z0-9]+".toRegex(), "-")
}

/**
* Handle an event and send the appropriate pings.
*
* Valid events are:
*
* - Background: When the application goes to the background.
* This triggers sending of the baseline ping.
* - Default: Event that triggers the default pings.
*
* @param pingEvent The type of the event.
*/
fun handleEvent(pingEvent: Glean.PingEvent) {
if (!isInitialized()) {
logger.error("Glean must be initialized before handling events.")
return
}

when (pingEvent) {
Glean.PingEvent.Background -> {
sendPing(BaselinePing.STORE_NAME, BaselinePing.STORE_NAME)
sendPing("events", "events")
}
Glean.PingEvent.Default -> {
if (metricsPingScheduler.canSendPing()) {
sendPing(MetricsPingScheduler.STORE_NAME, MetricsPingScheduler.STORE_NAME)
metricsPingScheduler.updateSentTimestamp()
}
}
}
fun schedulePingsNow() {
pingScheduler.onEnterBackground()
}

/**
Expand All @@ -297,19 +202,6 @@ open class GleanInternalAPI internal constructor () {
}

object Glean : GleanInternalAPI() {
/**
* Enumeration of different metric lifetimes.
*/
enum class PingEvent {
/**
* When the application goes into the background
*/
Background,
/**
* A periodic event to send the default ping
*/
Default
}

internal const val SCHEMA_VERSION = 2

Expand Down

This file was deleted.

Loading