Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug1844533 - Add background service to Android glean-sample-app #2549

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions glean-core/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-common:$versions.androidx_lifecycle"
implementation "androidx.lifecycle:lifecycle-process:$versions.androidx_lifecycle"
implementation "androidx.work:work-runtime-ktx:$versions.androidx_work"
implementation "androidx.work:work-multiprocess:$versions.androidx_work"

// We need a compileOnly dependency on the following block of testing
// libraries in order to expose the GleanTestRule to applications/libraries
Expand Down
5 changes: 5 additions & 0 deletions glean-core/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,10 @@
<activity android:name=".debug.GleanDebugActivity"
android:launchMode="singleInstance"
android:exported="true" />

<service
android:name="androidx.work.multiprocess.RemoteWorkerService"
android:exported="true"
android:process=":gleanUploadWorker" />
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,23 @@

package mozilla.telemetry.glean.scheduler

import android.content.ComponentName
import android.content.Context
import android.os.SystemClock
import android.provider.ContactsContract.Directory.PACKAGE_NAME
import androidx.annotation.VisibleForTesting
import androidx.work.Constraints
import androidx.work.ExistingWorkPolicy
import androidx.work.Data
import androidx.work.ListenableWorker
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.multiprocess.RemoteCoroutineWorker
import androidx.work.multiprocess.RemoteListenableWorker.ARGUMENT_CLASS_NAME
import androidx.work.multiprocess.RemoteListenableWorker.ARGUMENT_PACKAGE_NAME
import androidx.work.multiprocess.RemoteWorkerService
import mozilla.telemetry.glean.Glean
import mozilla.telemetry.glean.internal.PingUploadTask
import mozilla.telemetry.glean.internal.UploadTaskAction
Expand All @@ -39,18 +45,36 @@ internal fun buildConstraints(): Constraints = Constraints.Builder()
*
* @return [OneTimeWorkRequest] representing the task for the [WorkManager] to enqueue and run
*/
internal inline fun <reified W : Worker> buildWorkRequest(tag: String): OneTimeWorkRequest {
internal inline fun <reified W : RemoteCoroutineWorker> buildWorkRequest(tag: String): OneTimeWorkRequest {
return OneTimeWorkRequestBuilder<W>()
.addTag(tag)
.setConstraints(buildConstraints())
.build()
}

private fun buildOneTimeWorkRemoteWorkRequest(
componentName: ComponentName
, listenableWorkerClass: Class<out ListenableWorker>
): OneTimeWorkRequest {

// ARGUMENT_PACKAGE_NAME and ARGUMENT_CLASS_NAME are used to determine the service
// that a Worker binds to. By specifying these parameters, we can designate the process a
// Worker runs in.
val data: Data = Data.Builder()
.putString(ARGUMENT_PACKAGE_NAME, componentName.packageName)
.putString(ARGUMENT_CLASS_NAME, componentName.className)
.build()

return OneTimeWorkRequest.Builder(listenableWorkerClass)
.setInputData(data)
.build()
}

/**
* This class is the worker class used by [WorkManager] to handle uploading the ping to the server.
* @suppress This is internal only, don't show it in the docs.
*/
class PingUploadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
class PingUploadWorker(context: Context, params: WorkerParameters) : RemoteCoroutineWorker(context, params) {
companion object {
internal const val PING_WORKER_TAG = "mozac_service_glean_ping_upload_worker"

Expand All @@ -60,12 +84,15 @@ class PingUploadWorker(context: Context, params: WorkerParameters) : Worker(cont
* @param context the application [Context] to get the [WorkManager] instance for
*/
internal fun enqueueWorker(context: Context) {
WorkManager.getInstance(context).enqueueUniqueWork(
PING_WORKER_TAG,
ExistingWorkPolicy.KEEP,
buildWorkRequest<PingUploadWorker>(PING_WORKER_TAG),
val serviceName = RemoteWorkerService::class.java.name
val componentName = ComponentName(PACKAGE_NAME, serviceName)
val oneTimeWorkRequest = buildOneTimeWorkRemoteWorkRequest(
componentName,
PingUploadWorker::class.java
)

WorkManager.getInstance(context).enqueue(oneTimeWorkRequest)

// Only flush pings immediately if sending to a test endpoint,
// which means we're probably in instrumented tests.
if (Glean.isSendingToTestEndpoint) {
Expand Down Expand Up @@ -97,7 +124,7 @@ class PingUploadWorker(context: Context, params: WorkerParameters) : Worker(cont
*/
@OptIn(ExperimentalUnsignedTypes::class)
@Suppress("ReturnCount")
override fun doWork(): Result {
override suspend fun doRemoteWork(): Result {
do {
when (val action = gleanGetUploadTask()) {
is PingUploadTask.Upload -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.work.BackoffPolicy
import androidx.work.ListenableWorker
import androidx.work.NetworkType
import androidx.work.WorkerParameters
import kotlinx.coroutines.runBlocking
import mozilla.telemetry.glean.config.Configuration
import mozilla.telemetry.glean.getWorkerStatus
import mozilla.telemetry.glean.resetGlean
Expand Down Expand Up @@ -50,7 +52,10 @@ class PingUploadWorkerTest {

@Test
fun testDoWorkSuccess() {
val result = pingUploadWorker!!.doWork()
var result: ListenableWorker.Result
runBlocking {
result = pingUploadWorker!!.doRemoteWork()
}
Assert.assertTrue(result.toString().contains("Success"))
}

Expand Down
2 changes: 2 additions & 0 deletions samples/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ dependencies {

implementation "androidx.appcompat:appcompat:$rootProject.versions.androidx_appcompat"
implementation "androidx.browser:browser:$rootProject.versions.androidx_browser"
implementation "androidx.work:work-runtime:$rootProject.versions.androidx_work"
implementation "androidx.work:work-multiprocess:$rootProject.versions.androidx_work"

androidTestImplementation "androidx.test:core-ktx:$rootProject.versions.androidx_test"
androidTestImplementation "androidx.test:runner:$rootProject.versions.androidx_test"
Expand Down
12 changes: 12 additions & 0 deletions samples/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@
</intent-filter>
</activity>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- If you are using androidx.startup to initialize other components -->
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>

<!--
Sample activity alias for testing.
-->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package org.mozilla.samples.gleancore

import android.app.Application
import android.util.Log
import androidx.work.Configuration
import mozilla.telemetry.glean.Glean
import org.mozilla.samples.gleancore.GleanMetrics.Basic
import org.mozilla.samples.gleancore.GleanMetrics.Custom
Expand All @@ -17,7 +18,16 @@ import java.util.UUID

private const val TAG = "Glean"

class GleanApplication : Application() {
class GleanApplication : Application(), Configuration.Provider {

override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder()
// This is required for Glean to be able to enqueue the PingUploadWorker
// from both the daemon and the main app.
.setDefaultProcessName(packageName)
.setMinimumLoggingLevel(Log.INFO)
.build()
}

override fun onCreate() {
super.onCreate()
Expand Down
Loading