Make it simpler to delegate to instances of RemoteListenableDelegatingWorker.

* The API makes it possible to delegate to the default WorkerFactory when creating instances of RemoteListenableDelegatingWorker.
* This way you can move a `RemoteListenableWorker` from the main process to a remote process transparently, and even switch processes subsequently.

Test: Updated RemoteCoroutineWorkerTest.
Bug: 343527722
Relnote: Make it simpler to delegate to instances of `RemoteListenableDelegatingWorker`.
Change-Id: I7373f2fa1db38f5649cb6091bb69d58999b2ddba
diff --git a/work/work-multiprocess/src/androidTest/java/androidx/work/multiprocess/RemoteCoroutineWorkerTest.kt b/work/work-multiprocess/src/androidTest/java/androidx/work/multiprocess/RemoteCoroutineWorkerTest.kt
index aeb720f..60fed73 100644
--- a/work/work-multiprocess/src/androidTest/java/androidx/work/multiprocess/RemoteCoroutineWorkerTest.kt
+++ b/work/work-multiprocess/src/androidTest/java/androidx/work/multiprocess/RemoteCoroutineWorkerTest.kt
@@ -28,6 +28,7 @@
 import androidx.work.OneTimeWorkRequest
 import androidx.work.WorkInfo
 import androidx.work.WorkRequest
+import androidx.work.buildDelegatedRemoteRequestData
 import androidx.work.impl.Processor
 import androidx.work.impl.Scheduler
 import androidx.work.impl.WorkDatabase
@@ -38,7 +39,6 @@
 import androidx.work.impl.utils.taskexecutor.TaskExecutor
 import androidx.work.isRemoteWorkRequest
 import androidx.work.multiprocess.RemoteListenableDelegatingWorker.Companion.ARGUMENT_REMOTE_LISTENABLE_WORKER_NAME
-import androidx.work.usingRemoteService
 import java.util.concurrent.Executor
 import org.junit.Assert.assertEquals
 import org.junit.Before
@@ -163,13 +163,25 @@
             return
         }
 
-        val request = buildRequest<RemoteSuccessWorker>()
         val packageName = "PACKAGE_NAME"
         val className = "CLASS_NAME"
-        val data = request.workSpec.input.usingRemoteService(ComponentName(packageName, className))
+        val inputKey = "INPUT_KEY"
+        val inputValue = "InputValue"
+        val inputData = Data.Builder().putString(inputKey, inputValue).build()
+        val data =
+            buildDelegatedRemoteRequestData(
+                delegatedWorkerName = RemoteSuccessWorker::class.java.name,
+                componentName = ComponentName(packageName, className),
+                inputData
+            )
         assertEquals(data.isRemoteWorkRequest(), true)
+        assertEquals(
+            data.getString(ARGUMENT_REMOTE_LISTENABLE_WORKER_NAME),
+            RemoteSuccessWorker::class.java.name
+        )
         assertEquals(data.getString(RemoteListenableWorker.ARGUMENT_PACKAGE_NAME), packageName)
         assertEquals(data.getString(RemoteListenableWorker.ARGUMENT_CLASS_NAME), className)
+        assertEquals(data.getString(inputKey), inputValue)
     }
 
     private inline fun <reified T : ListenableWorker> buildRequest(): OneTimeWorkRequest {
diff --git a/work/work-runtime/api/current.txt b/work/work-runtime/api/current.txt
index 0b5be26..5cd8014 100644
--- a/work/work-runtime/api/current.txt
+++ b/work/work-runtime/api/current.txt
@@ -636,7 +636,8 @@
 
   public final class WorkerParametersExtensions {
     method public static boolean isRemoteWorkRequest(androidx.work.WorkerParameters);
-    method public static androidx.work.WorkerParameters usingRemoteService(androidx.work.WorkerParameters, android.content.ComponentName componentName);
+    method public static inline <reified T extends androidx.work.ListenableWorker> androidx.work.WorkerParameters usingRemoteService(androidx.work.WorkerParameters, android.content.ComponentName componentName);
+    method public static androidx.work.WorkerParameters usingRemoteService(androidx.work.WorkerParameters, String workerClassName, android.content.ComponentName componentName);
   }
 
 }
diff --git a/work/work-runtime/api/restricted_current.txt b/work/work-runtime/api/restricted_current.txt
index 0b5be26..5cd8014 100644
--- a/work/work-runtime/api/restricted_current.txt
+++ b/work/work-runtime/api/restricted_current.txt
@@ -636,7 +636,8 @@
 
   public final class WorkerParametersExtensions {
     method public static boolean isRemoteWorkRequest(androidx.work.WorkerParameters);
-    method public static androidx.work.WorkerParameters usingRemoteService(androidx.work.WorkerParameters, android.content.ComponentName componentName);
+    method public static inline <reified T extends androidx.work.ListenableWorker> androidx.work.WorkerParameters usingRemoteService(androidx.work.WorkerParameters, android.content.ComponentName componentName);
+    method public static androidx.work.WorkerParameters usingRemoteService(androidx.work.WorkerParameters, String workerClassName, android.content.ComponentName componentName);
   }
 
 }
diff --git a/work/work-runtime/src/main/java/androidx/work/WorkerParameters.kt b/work/work-runtime/src/main/java/androidx/work/WorkerParameters.kt
index 064e029..4814646 100644
--- a/work/work-runtime/src/main/java/androidx/work/WorkerParameters.kt
+++ b/work/work-runtime/src/main/java/androidx/work/WorkerParameters.kt
@@ -36,14 +36,33 @@
  * Returns a new instance of [WorkerParameters] representing a [WorkRequest] that can run in a
  * process corresponding to the provided [ComponentName].
  *
+ * @param T The [ListenableWorker] to delegate to.
  * @param componentName The [ComponentName] that identifies the `RemoteService` that hosts the
  *   [WorkRequest].
  * @return A new instance of [WorkerParameters]
  */
-fun WorkerParameters.usingRemoteService(componentName: ComponentName): WorkerParameters {
+inline fun <reified T : ListenableWorker> WorkerParameters.usingRemoteService(
+    componentName: ComponentName
+): WorkerParameters {
+    return usingRemoteService(T::class.java.name, componentName)
+}
+
+/**
+ * Returns a new instance of [WorkerParameters] representing a [WorkRequest] that can run in a
+ * process corresponding to the provided [ComponentName].
+ *
+ * @param workerClassName The fully qualified class name of the [ListenableWorker] to delegate to
+ * @param componentName The [ComponentName] that identifies the `RemoteService` that hosts the
+ *   [WorkRequest].
+ * @return A new instance of [WorkerParameters]
+ */
+fun WorkerParameters.usingRemoteService(
+    workerClassName: String,
+    componentName: ComponentName
+): WorkerParameters {
     return WorkerParameters(
         id,
-        inputData.usingRemoteService(componentName),
+        buildDelegatedRemoteRequestData(workerClassName, componentName, inputData),
         tags,
         runtimeExtras,
         runAttemptCount,
@@ -58,23 +77,23 @@
 }
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun Data.isRemoteWorkRequest(): Boolean {
-    return hasKey<String>(ARGUMENT_REMOTE_LISTENABLE_WORKER_NAME)
+fun buildDelegatedRemoteRequestData(
+    delegatedWorkerName: String,
+    componentName: ComponentName,
+    inputData: Data
+): Data {
+    val builder = Data.Builder()
+    builder
+        .putAll(data = inputData)
+        .putString(ARGUMENT_SERVICE_PACKAGE_NAME, componentName.packageName)
+        .putString(ARGUMENT_SERVICE_CLASS_NAME, componentName.className)
+        .putString(ARGUMENT_REMOTE_LISTENABLE_WORKER_NAME, delegatedWorkerName)
+    return builder.build()
 }
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun Data.usingRemoteService(componentName: ComponentName): Data {
-    val hasPackageName = hasKey<String>(ARGUMENT_SERVICE_PACKAGE_NAME)
-    val hasClassName = hasKey<String>(ARGUMENT_SERVICE_CLASS_NAME)
-    require(hasPackageName && hasClassName) {
-        "$this does not correspond to a request for work in a remote process."
-    }
-    val data =
-        Data.Builder()
-            .putAll(this)
-            .putString(ARGUMENT_SERVICE_PACKAGE_NAME, componentName.packageName)
-            .putString(ARGUMENT_SERVICE_CLASS_NAME, componentName.className)
-            .build()
-
-    return data
+fun Data.isRemoteWorkRequest(): Boolean {
+    return hasKey<String>(ARGUMENT_SERVICE_PACKAGE_NAME) &&
+        hasKey<String>(ARGUMENT_SERVICE_CLASS_NAME) &&
+        hasKey<String>(ARGUMENT_REMOTE_LISTENABLE_WORKER_NAME)
 }
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueUtils.kt b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueUtils.kt
index c2f1d2d..47d8d5a 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueUtils.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueUtils.kt
@@ -142,11 +142,6 @@
     }
 }
 
-// The name of the worker to delegate to when it's a Remote Listenable Worker
-
-internal const val REMOTE_DELEGATING_LISTENABLE_WORKER_CLASS_NAME =
-    "androidx.work.multiprocess.RemoteListenableDelegatingWorker"
-
 // Redefine the keys
 
 internal const val ARGUMENT_SERVICE_PACKAGE_NAME =
@@ -155,6 +150,16 @@
 internal const val ARGUMENT_SERVICE_CLASS_NAME =
     "androidx.work.impl.workers.RemoteListenableWorker.ARGUMENT_CLASS_NAME"
 
+/**
+ * Originally defined in `RemoteListenableDelegatingWorker.kt`. This method is being re-defined here
+ * to avoid a circular dependency on the multiprocess library.
+ *
+ * The fully qualified name of the class that is used when running in the context of a remote
+ * process. This constant is useful when migrating remote workers between processes.
+ */
+internal const val REMOTE_DELEGATING_LISTENABLE_WORKER_CLASS_NAME =
+    "androidx.work.multiprocess.RemoteListenableDelegatingWorker"
+
 // The RemoteListenableWorker class to delegate to.
 
 internal const val ARGUMENT_REMOTE_LISTENABLE_WORKER_NAME =