Add support for resource only watch face runtimes

Bug: 288890905
Test: Manual testing
Relnote: This patch adds WatchFaceRuntimeService and WatchFaceControlClient.createWatchFaceRuntimeControlClient along with guava wrappers. These add support for watch face runtimes which are a special kind of watch face that loads it's definition from another package. Currently WearOS only supports the runtime for the Android Watch Face Format (see https://developer.android.com/training/wearables/wff for more details).
Change-Id: I2799f9aa593a208ac5b40b768b37d7c072bb91c4
diff --git a/wear/watchface/watchface-client-guava/api/current.txt b/wear/watchface/watchface-client-guava/api/current.txt
index 471cc7f..9c9689d 100644
--- a/wear/watchface/watchface-client-guava/api/current.txt
+++ b/wear/watchface/watchface-client-guava/api/current.txt
@@ -7,6 +7,7 @@
     method @Deprecated public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
     method public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(String id, android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
     method public static final com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+    method public static final com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceRuntimeControlClientAsync(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName);
     method @Deprecated public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName);
     method public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient();
     method public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId);
@@ -19,15 +20,18 @@
 
   public static final class ListenableWatchFaceControlClient.Companion {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceRuntimeControlClientAsync(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName);
   }
 
   public final class ListenableWatchFaceMetadataClient {
     method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String resourceOnlyWatchFacePackageName);
     field public static final androidx.wear.watchface.client.ListenableWatchFaceMetadataClient.Companion Companion;
   }
 
   public static final class ListenableWatchFaceMetadataClient.Companion {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String resourceOnlyWatchFacePackageName);
   }
 
 }
diff --git a/wear/watchface/watchface-client-guava/api/restricted_current.txt b/wear/watchface/watchface-client-guava/api/restricted_current.txt
index 471cc7f..9c9689d 100644
--- a/wear/watchface/watchface-client-guava/api/restricted_current.txt
+++ b/wear/watchface/watchface-client-guava/api/restricted_current.txt
@@ -7,6 +7,7 @@
     method @Deprecated public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
     method public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(String id, android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
     method public static final com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+    method public static final com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceRuntimeControlClientAsync(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName);
     method @Deprecated public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName);
     method public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient();
     method public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId);
@@ -19,15 +20,18 @@
 
   public static final class ListenableWatchFaceControlClient.Companion {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceRuntimeControlClientAsync(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName);
   }
 
   public final class ListenableWatchFaceMetadataClient {
     method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String resourceOnlyWatchFacePackageName);
     field public static final androidx.wear.watchface.client.ListenableWatchFaceMetadataClient.Companion Companion;
   }
 
   public static final class ListenableWatchFaceMetadataClient.Companion {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String resourceOnlyWatchFacePackageName);
   }
 
 }
diff --git a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt
index e3de152..11fa95b 100644
--- a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt
+++ b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt
@@ -21,6 +21,8 @@
 import androidx.concurrent.futures.ResolvableFuture
 import androidx.core.util.Consumer
 import androidx.wear.watchface.Renderer
+import androidx.wear.watchface.WatchFaceRuntimeService
+import androidx.wear.watchface.client.WatchFaceControlClient.Companion.createWatchFaceControlClient
 import androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException
 import androidx.wear.watchface.complications.data.ComplicationData
 import androidx.wear.watchface.style.UserStyleData
@@ -119,6 +121,51 @@
                     )
                 )
             }
+
+        /**
+         * Similar [createWatchFaceControlClient] this constructs a [WatchFaceControlClient] which
+         * attempts to connect to the watch face runtime in the android package
+         * [runtimePackageName].
+         *
+         * A watch face runtime is a special type of watch face, which renders a watch face
+         * described by resources in another package [resourceOnlyWatchFacePackageName].
+         *
+         * Note only one watch face definition per resource only watch face package is supported.
+         *
+         * Currently Wear OS only supports the runtime for the Android Watch Face Format (see
+         * https://developer.android.com/training/wearables/wff for more details).
+         *
+         * @param context Calling application's [Context].
+         * @param runtimePackageName The name of the package containing the watch face runtime's
+         *   control service to bind to.
+         * @param resourceOnlyWatchFacePackageName The name of the package from which to load the
+         *   resource only watch face. This is exposed to the runtime via the
+         *  `resourceOnlyWatchFacePackageName` parameter passed to
+         *  [WatchFaceRuntimeService.createUserStyleSchema],
+         *  [WatchFaceRuntimeService.createComplicationSlotsManager],
+         *  [WatchFaceRuntimeService.createUserStyleFlavors] and
+         *  [WatchFaceRuntimeService.createWatchFace]).
+         * @return [ListenableFuture]<[ListenableWatchFaceControlClient]> which on success resolves
+         *   to a [ListenableWatchFaceControlClient] or throws a [ServiceNotBoundException] if the
+         *   watch face control service can not be bound.
+         */
+        @JvmStatic
+        public fun createWatchFaceRuntimeControlClientAsync(
+            context: Context,
+            runtimePackageName: String,
+            resourceOnlyWatchFacePackageName: String
+        ): ListenableFuture<ListenableWatchFaceControlClient> =
+            launchFutureCoroutine(
+                "ListenableWatchFaceControlClient.createWatchFaceRuntimeControlClient",
+            ) {
+                ListenableWatchFaceControlClient(
+                    WatchFaceControlClient.createWatchFaceRuntimeControlClient(
+                        context,
+                        runtimePackageName,
+                        resourceOnlyWatchFacePackageName
+                    )
+                )
+            }
     }
 
     @Suppress("DEPRECATION")
diff --git a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt
index 6d25efd..5a49cbb 100644
--- a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt
+++ b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt
@@ -53,6 +53,38 @@
                 WatchFaceMetadataClient.Companion.ParserProvider()
             )
 
+        /**
+         * Constructs a [WatchFaceMetadataClient] for fetching metadata for the specified resource
+         * only watch face runtime.  A resource only watch face runtime, is a special kind of watch
+         * face that is the runtime for a watch face defined by another package that contains only
+         * resources and no executable code.
+         *
+         * @param context Calling application's [Context].
+         * @param watchFaceName The [ComponentName] of the watch face to fetch meta data from.
+         * @param resourceOnlyWatchFacePackageName The package the runtime should load the resources
+         *   from.
+         * @return A [ListenableFuture] which resolves with [WatchFaceMetadataClient] if there is
+         *   one, otherwise it throws a [ServiceNotBoundException] if the underlying watch face
+         *   control service can not be bound or a [ServiceStartFailureException] if the watch face
+         *   dies during startup.
+         */
+        @Suppress("AsyncSuffixFuture")
+        @JvmStatic
+        public fun createForRuntime(
+            context: Context,
+            watchFaceName: ComponentName,
+            resourceOnlyWatchFacePackageName: String
+        ) =
+            ListenableWatchFaceControlClient.launchFutureCoroutine(
+                "ListenableWatchFaceMetadataClient.create"
+            ) {
+                WatchFaceMetadataClient.createForRuntime(
+                    context,
+                    watchFaceName,
+                    resourceOnlyWatchFacePackageName
+                )
+            }
+
         internal fun createImpl(
             context: Context,
             intent: Intent,
diff --git a/wear/watchface/watchface-client/api/current.txt b/wear/watchface/watchface-client/api/current.txt
index 41cfe98..9e3a9b6 100644
--- a/wear/watchface/watchface-client/api/current.txt
+++ b/wear/watchface/watchface-client/api/current.txt
@@ -206,6 +206,7 @@
     method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public default androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(String id, android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public static suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
+    method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public static suspend Object? createWatchFaceRuntimeControlClient(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
     method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName) throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient() throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId) throws android.os.RemoteException;
@@ -217,6 +218,7 @@
 
   public static final class WatchFaceControlClient.Companion {
     method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
+    method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public suspend Object? createWatchFaceRuntimeControlClient(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
   }
 
   public static final class WatchFaceControlClient.ServiceNotBoundException extends java.lang.Exception {
@@ -244,6 +246,7 @@
 
   public static final class WatchFaceMetadataClient.Companion {
     method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? create(android.content.Context context, android.content.ComponentName watchFaceName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient>) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
+    method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String runtimePackage, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient>) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
   }
 
   public static final class WatchFaceMetadataClient.ServiceNotBoundException extends java.lang.Exception {
diff --git a/wear/watchface/watchface-client/api/restricted_current.txt b/wear/watchface/watchface-client/api/restricted_current.txt
index 41cfe98..9e3a9b6 100644
--- a/wear/watchface/watchface-client/api/restricted_current.txt
+++ b/wear/watchface/watchface-client/api/restricted_current.txt
@@ -206,6 +206,7 @@
     method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public default androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(String id, android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public static suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
+    method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public static suspend Object? createWatchFaceRuntimeControlClient(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
     method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName) throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient() throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId) throws android.os.RemoteException;
@@ -217,6 +218,7 @@
 
   public static final class WatchFaceControlClient.Companion {
     method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
+    method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public suspend Object? createWatchFaceRuntimeControlClient(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
   }
 
   public static final class WatchFaceControlClient.ServiceNotBoundException extends java.lang.Exception {
@@ -244,6 +246,7 @@
 
   public static final class WatchFaceMetadataClient.Companion {
     method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? create(android.content.Context context, android.content.ComponentName watchFaceName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient>) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
+    method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String runtimePackage, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient>) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
   }
 
   public static final class WatchFaceMetadataClient.ServiceNotBoundException extends java.lang.Exception {
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt
index d6c8fb5..bf838f8 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt
@@ -61,7 +61,8 @@
             context,
             Intent(context, WatchFaceControlTestService::class.java).apply {
                 action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
-            }
+            },
+            resourceOnlyWatchFacePackageName = null
         )
     }
 
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/SerializationTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/SerializationTest.kt
index ff8ba4b..26eb17a 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/SerializationTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/SerializationTest.kt
@@ -22,6 +22,7 @@
 import android.graphics.Rect
 import android.os.Build
 import android.os.Parcel
+import android.support.wearable.watchface.SharedMemoryImage
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -29,8 +30,8 @@
 import androidx.wear.watchface.DrawMode
 import androidx.wear.watchface.RenderParameters
 import androidx.wear.watchface.client.ComplicationSlotState
+import androidx.wear.watchface.client.EditorState
 import androidx.wear.watchface.client.WatchFaceId
-import androidx.wear.watchface.client.asApiEditorState
 import androidx.wear.watchface.complications.SystemDataSources
 import androidx.wear.watchface.complications.data.ComplicationType
 import androidx.wear.watchface.complications.data.LongTextComplicationData
@@ -52,6 +53,26 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+// FIXME: We shouldn't have to define this here, we should be able to import it.
+internal fun EditorStateWireFormat.asApiEditorState(): EditorState {
+    return EditorState(
+        WatchFaceId(watchFaceInstanceId ?: ""),
+        UserStyleData(userStyle.mUserStyle),
+        previewComplicationData.associateBy(
+            { it.id },
+            { it.complicationData.toApiComplicationData() }
+        ),
+        commitChanges,
+        previewImageBundle?.let {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
+                SharedMemoryImage.ashmemReadImageBundle(it)
+            } else {
+                null
+            }
+        }
+    )
+}
+
 /** Tests that we can deserialize golden resources correctly to ensure backwards compatibility. */
 @RunWith(AndroidJUnit4::class)
 @MediumTest
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
index d758df7..52e8c10 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
@@ -34,6 +34,7 @@
 import androidx.wear.watchface.RenderParameters
 import androidx.wear.watchface.Renderer
 import androidx.wear.watchface.WatchFace
+import androidx.wear.watchface.WatchFaceRuntimeService
 import androidx.wear.watchface.WatchFaceService
 import androidx.wear.watchface.WatchFaceType
 import androidx.wear.watchface.WatchState
@@ -54,6 +55,7 @@
 import androidx.wear.watchface.samples.ExampleOpenGLBackgroundInitWatchFaceService
 import androidx.wear.watchface.samples.R
 import androidx.wear.watchface.style.CurrentUserStyleRepository
+import androidx.wear.watchface.style.UserStyleFlavors
 import androidx.wear.watchface.style.UserStyleSchema
 import androidx.wear.watchface.style.UserStyleSetting
 import androidx.wear.watchface.style.WatchFaceLayer
@@ -261,6 +263,74 @@
             .setOverlayStyle(watchFaceOverlayStyle)
 }
 
+@Suppress("Deprecation")
+internal class TestWatchFaceRuntimeService(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder
+) : WatchFaceRuntimeService() {
+
+    lateinit var lastResourceOnlyWatchFacePackageName: String
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    override fun createUserStyleSchema(resourceOnlyWatchFacePackageName: String) =
+        UserStyleSchema(emptyList())
+
+    override fun createComplicationSlotsManager(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String
+    ) = ComplicationSlotsManager(emptyList(), currentUserStyleRepository)
+
+    override fun createUserStyleFlavors(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        complicationSlotsManager: ComplicationSlotsManager,
+        resourceOnlyWatchFacePackageName: String
+    ) = UserStyleFlavors()
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String
+    ): WatchFace {
+        lastResourceOnlyWatchFacePackageName = resourceOnlyWatchFacePackageName
+
+        return WatchFace(
+            WatchFaceType.DIGITAL,
+            @Suppress("deprecation")
+            object :
+                Renderer.CanvasRenderer(
+                    surfaceHolder,
+                    currentUserStyleRepository,
+                    watchState,
+                    CanvasType.HARDWARE,
+                    16
+                ) {
+                override fun render(
+                    canvas: Canvas,
+                    bounds: Rect,
+                    zonedDateTime: ZonedDateTime
+                ) {
+                    // Actually rendering something isn't required.
+                }
+
+                override fun renderHighlightLayer(
+                    canvas: Canvas,
+                    bounds: Rect,
+                    zonedDateTime: ZonedDateTime
+                ) {
+                    // Actually rendering something isn't required.
+                }
+            }
+        )
+    }
+}
+
 internal class TestAsyncCanvasRenderInitWatchFaceService(
     testContext: Context,
     private var surfaceHolderOverride: SurfaceHolder,
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
index 6e145da..1769ce1 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
@@ -133,7 +133,8 @@
             context,
             Intent(context, WatchFaceControlTestService::class.java).apply {
                 action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
-            }
+            },
+            resourceOnlyWatchFacePackageName = null
         )
     }
 
@@ -503,6 +504,44 @@
     }
 
     @Test
+    @Suppress("deprecation") // getOrCreateInteractiveWatchFaceClient
+    fun resourceOnlyWatchFacePackageName() {
+        val watchFaceService = TestWatchFaceRuntimeService(context, surfaceHolder)
+        val service = runBlocking {
+            WatchFaceControlClient.createWatchFaceControlClientImpl(
+                context,
+                Intent(context, WatchFaceControlTestService::class.java).apply {
+                    action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
+                },
+                resourceOnlyWatchFacePackageName = "com.example.watchface"
+            )
+        }
+
+        val deferredInteractiveInstance = handlerCoroutineScope.async {
+            service.getOrCreateInteractiveWatchFaceClient(
+                "testId",
+                deviceConfig,
+                systemState,
+                userStyle = null,
+                complications
+            )
+        }
+
+        // Create the engine which triggers construction of the interactive instance.
+        handler.post {
+            engine = watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper
+        }
+
+        // Wait for the instance to be created.
+        val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+
+        assertThat(watchFaceService.lastResourceOnlyWatchFacePackageName)
+            .isEqualTo("com.example.watchface")
+
+        interactiveInstance.close()
+    }
+
+    @Test
     @Suppress("Deprecation")
     fun getInteractiveWatchFaceInstance() {
         val testId = "testId"
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
index 4414c28..833269b 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
@@ -27,6 +27,7 @@
 import androidx.annotation.RestrictTo
 import androidx.core.util.Consumer
 import androidx.wear.watchface.Renderer
+import androidx.wear.watchface.WatchFaceService
 import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
 import androidx.wear.watchface.complications.data.ComplicationData
 import androidx.wear.watchface.complications.data.ComplicationType
@@ -79,13 +80,52 @@
                 context,
                 Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
                     setPackage(watchFacePackageName)
-                }
+                },
+                null
+            )
+
+        /**
+         * Similar [createWatchFaceControlClient] this constructs a [WatchFaceControlClient] which
+         * attempts to connect to the watch face runtime in the android package
+         * [runtimePackageName].
+         *
+         * A watch face runtime is a special type of watch face, which renders a watch face
+         * described by resources in another package [resourceOnlyWatchFacePackageName].
+         *
+         * Currently Wear OS only supports the runtime for the Android Watch Face Format (see
+         * https://developer.android.com/training/wearables/wff for more details).
+         *
+         * @param context Calling application's [Context].
+         * @param runtimePackageName The name of the package containing the watch face runtime's
+         *   control service to bind to.
+         * @param resourceOnlyWatchFacePackageName The name of the package from which to load the
+         *   resource only watch face. This is exposed to the runtime via
+         *   [WatchFaceService.resourceOnlyWatchFacePackageName].  Note only one watch face
+         *   definition per resource only watch face package is supported.
+         * @return The [WatchFaceControlClient] if there is one.
+         * @throws [ServiceNotBoundException] if the watch face control service can not be bound or
+         *   a [ServiceStartFailureException] if the watch face dies during startup.
+         */
+        @JvmStatic
+        @Throws(ServiceNotBoundException::class, ServiceStartFailureException::class)
+        public suspend fun createWatchFaceRuntimeControlClient(
+            context: Context,
+            runtimePackageName: String,
+            resourceOnlyWatchFacePackageName: String
+        ): WatchFaceControlClient =
+            createWatchFaceControlClientImpl(
+                context,
+                Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
+                    setPackage(runtimePackageName)
+                },
+                resourceOnlyWatchFacePackageName
             )
 
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public suspend fun createWatchFaceControlClientImpl(
             context: Context,
-            intent: Intent
+            intent: Intent,
+            resourceOnlyWatchFacePackageName: String?
         ): WatchFaceControlClient {
             val deferredService = CompletableDeferred<IWatchFaceControlService>()
             val traceEvent = AsyncTraceEvent("WatchFaceControlClientImpl.bindService")
@@ -107,7 +147,12 @@
                 traceEvent.close()
                 throw ServiceNotBoundException()
             }
-            return WatchFaceControlClientImpl(context, deferredService.await(), serviceConnection)
+            return WatchFaceControlClientImpl(
+                context,
+                deferredService.await(),
+                serviceConnection,
+                resourceOnlyWatchFacePackageName
+            )
         }
     }
 
@@ -342,7 +387,8 @@
 internal constructor(
     private val context: Context,
     private val service: IWatchFaceControlService,
-    private val serviceConnection: ServiceConnection
+    private val serviceConnection: ServiceConnection,
+    private val resourceOnlyWatchFacePackageName: String?
 ) : WatchFaceControlClient {
     private var closed = false
 
@@ -499,8 +545,8 @@
                                 it.value.asWireComplicationData()
                             )
                         },
-                        null,
-                        null
+                        /* auxiliaryComponentPackageName = */ resourceOnlyWatchFacePackageName,
+                        /* auxiliaryComponentClassName = */ null
                     ),
                     object : IPendingInteractiveWatchFace.Stub() {
                         override fun getApiVersion() =
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
index f1569cb..a0dfe00 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
@@ -93,6 +93,43 @@
             )
         }
 
+        /**
+         * Constructs a [WatchFaceMetadataClient] for fetching metadata for the specified resource
+         * only watch face from its runtime. A resource only watch face runtime is a special watch
+         * face that knows how to load watch faces from resources in another package that contains
+         * only resources and no executable code.
+         *
+         * @param context Calling application's [Context].
+         * @param watchFaceName The [ComponentName] of the watch face to fetch meta data from.
+         * @param runtimePackage The package that contains the Resource only Watch Face runtime.
+         * @return The [WatchFaceMetadataClient] if there is one.
+         * @throws [ServiceNotBoundException] if the underlying watch face control service can not
+         *   be bound or a [ServiceStartFailureException] if the watch face dies during startup. If
+         *   the service's manifest contains an
+         *   androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data node then
+         *   [PackageManager.NameNotFoundException] is thrown if [watchFaceName] is invalid.
+         */
+        @Throws(
+            ServiceNotBoundException::class,
+            ServiceStartFailureException::class,
+            PackageManager.NameNotFoundException::class
+        )
+        @SuppressWarnings("MissingJvmstatic") // Can't really call a suspend fun from java.
+        public suspend fun createForRuntime(
+            context: Context,
+            watchFaceName: ComponentName,
+            runtimePackage: String
+        ): WatchFaceMetadataClient {
+            return createImpl(
+                context,
+                Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
+                    setPackage(runtimePackage)
+                },
+                watchFaceName,
+                parserProvider = null
+            )
+        }
+
         @Suppress("DEPRECATION") // getServiceInfo
         internal fun isXmlVersionCompatible(
             context: Context,
@@ -156,10 +193,10 @@
             context: Context,
             intent: Intent,
             watchFaceName: ComponentName,
-            parserProvider: ParserProvider
+            parserProvider: ParserProvider?
         ): WatchFaceMetadataClient {
             // Check if there's static metadata we can read (fast).
-            parserProvider.getParser(context, watchFaceName)?.let {
+            parserProvider?.getParser(context, watchFaceName)?.let {
                 return XmlWatchFaceMetadataClientImpl(
                     XmlSchemaAndComplicationSlotsDefinition.inflate(
                         context.packageManager.getResourcesForApplication(
diff --git a/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/control/data/WallpaperInteractiveWatchFaceInstanceParams.java b/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/control/data/WallpaperInteractiveWatchFaceInstanceParams.java
index 5e20ee5..f9ea844 100644
--- a/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/control/data/WallpaperInteractiveWatchFaceInstanceParams.java
+++ b/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/control/data/WallpaperInteractiveWatchFaceInstanceParams.java
@@ -145,6 +145,16 @@
         mIdAndComplicationDataWireFormats = idAndComplicationDataWireFormats;
     }
 
+    @Nullable
+    public String getAuxiliaryComponentClassName() {
+        return mAuxiliaryComponentClassName;
+    }
+
+    @Nullable
+    public String getAuxiliaryComponentPackageName() {
+        return mAuxiliaryComponentPackageName;
+    }
+
     /**
      * Serializes this WallpaperInteractiveWatchFaceInstanceParams to the specified {@link Parcel}.
      */
diff --git a/wear/watchface/watchface-guava/api/current.txt b/wear/watchface/watchface-guava/api/current.txt
index 2460e8b2..115e276 100644
--- a/wear/watchface/watchface-guava/api/current.txt
+++ b/wear/watchface/watchface-guava/api/current.txt
@@ -43,6 +43,12 @@
     method @UiThread public final void runUiThreadGlCommands(Runnable runnable);
   }
 
+  public abstract class ListenableWatchFaceRuntimeService extends androidx.wear.watchface.WatchFaceRuntimeService {
+    ctor public ListenableWatchFaceRuntimeService();
+    method protected final suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+    method protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.WatchFace> createWatchFaceFutureAsync(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName);
+  }
+
   public abstract class ListenableWatchFaceService extends androidx.wear.watchface.WatchFaceService {
     ctor public ListenableWatchFaceService();
     method protected suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
diff --git a/wear/watchface/watchface-guava/api/restricted_current.txt b/wear/watchface/watchface-guava/api/restricted_current.txt
index 2460e8b2..115e276 100644
--- a/wear/watchface/watchface-guava/api/restricted_current.txt
+++ b/wear/watchface/watchface-guava/api/restricted_current.txt
@@ -43,6 +43,12 @@
     method @UiThread public final void runUiThreadGlCommands(Runnable runnable);
   }
 
+  public abstract class ListenableWatchFaceRuntimeService extends androidx.wear.watchface.WatchFaceRuntimeService {
+    ctor public ListenableWatchFaceRuntimeService();
+    method protected final suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+    method protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.WatchFace> createWatchFaceFutureAsync(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName);
+  }
+
   public abstract class ListenableWatchFaceService extends androidx.wear.watchface.WatchFaceService {
     ctor public ListenableWatchFaceService();
     method protected suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRenderer2Test.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRenderer2Test.kt
index c831044..540297d 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRenderer2Test.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRenderer2Test.kt
@@ -137,11 +137,12 @@
                 initFuture,
                 sharedAssetsFuture
             )
+        val controlClient = createWatchFaceControlClientService()
 
         val deferredClient =
             handlerCoroutineScope.async {
                 @Suppress("deprecation")
-                watchFaceControlClientService.getOrCreateInteractiveWatchFaceClient(
+                controlClient.getOrCreateInteractiveWatchFaceClient(
                     "testId",
                     DeviceConfig(false, false, 0, 0),
                     WatchUiState(false, 0),
@@ -150,7 +151,7 @@
                 )
             }
 
-        handler.post { watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper }
+        handler.post { watchFaceService.onCreateEngine() }
 
         val client = awaitWithTimeout(deferredClient)
 
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRendererTest.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRendererTest.kt
index 66ce0b7..491c443 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRendererTest.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRendererTest.kt
@@ -109,11 +109,12 @@
         val initFuture = SettableFuture.create<Unit>()
         val watchFaceService =
             TestAsyncCanvasRenderInitWatchFaceService(context, surfaceHolder, initFuture)
+        val controlClient = createWatchFaceControlClientService()
 
         val deferredClient =
             handlerCoroutineScope.async {
                 @Suppress("deprecation")
-                watchFaceControlClientService.getOrCreateInteractiveWatchFaceClient(
+                controlClient.getOrCreateInteractiveWatchFaceClient(
                     "testId",
                     DeviceConfig(false, false, 0, 0),
                     WatchUiState(false, 0),
@@ -122,7 +123,7 @@
                 )
             }
 
-        handler.post { watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper }
+        handler.post { watchFaceService.onCreateEngine() }
 
         val client = awaitWithTimeout(deferredClient)
 
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRenderer2Test.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRenderer2Test.kt
index b7edca4..e64777b 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRenderer2Test.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRenderer2Test.kt
@@ -127,11 +127,12 @@
                 onBackgroundThreadGlContextFuture,
                 sharedAssetsFuture
             )
+        val controlClient = createWatchFaceControlClientService()
 
         val deferredClient =
             handlerCoroutineScope.async {
                 @Suppress("deprecation")
-                watchFaceControlClientService.getOrCreateInteractiveWatchFaceClient(
+                controlClient.getOrCreateInteractiveWatchFaceClient(
                     "testId",
                     DeviceConfig(false, false, 0, 0),
                     WatchUiState(false, 0),
@@ -140,7 +141,7 @@
                 )
             }
 
-        handler.post { watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper }
+        handler.post { watchFaceService.onCreateEngine() }
 
         val client = awaitWithTimeout(deferredClient)
         try {
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRendererTest.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRendererTest.kt
index 21de436..0c292bc 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRendererTest.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRendererTest.kt
@@ -106,11 +106,12 @@
                 onUiThreadGlSurfaceCreatedFuture,
                 onBackgroundThreadGlContextFuture
             )
+        val controlClient = createWatchFaceControlClientService()
 
         val deferredClient =
             handlerCoroutineScope.async {
                 @Suppress("deprecation")
-                watchFaceControlClientService.getOrCreateInteractiveWatchFaceClient(
+                controlClient.getOrCreateInteractiveWatchFaceClient(
                     "testId",
                     DeviceConfig(false, false, 0, 0),
                     WatchUiState(false, 0),
@@ -119,7 +120,7 @@
                 )
             }
 
-        handler.post { watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper }
+        handler.post { watchFaceService.onCreateEngine() }
 
         val client = awaitWithTimeout(deferredClient)
         try {
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableWatchFaceServiceTest.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableWatchFaceServiceTest.kt
index c4a385b..b082a31 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableWatchFaceServiceTest.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableWatchFaceServiceTest.kt
@@ -16,12 +16,18 @@
 
 package androidx.wear.watchface
 
+import android.content.Context
 import android.graphics.Canvas
 import android.graphics.Rect
+import android.os.Build
 import android.view.SurfaceHolder
+import androidx.annotation.RequiresApi
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import androidx.wear.watchface.client.DeviceConfig
+import androidx.wear.watchface.client.WatchUiState
 import androidx.wear.watchface.style.CurrentUserStyleRepository
+import androidx.wear.watchface.style.UserStyleFlavors
 import androidx.wear.watchface.style.UserStyleSchema
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.ListenableFuture
@@ -30,6 +36,7 @@
 import java.time.ZonedDateTime
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.async
 import org.junit.Assert
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -90,6 +97,56 @@
         )
 }
 
+private class TestAsyncListenableWatchFaceRuntimeService(
+    testContext: Context,
+    private var surfaceHolderOverride: SurfaceHolder,
+) : ListenableWatchFaceRuntimeService() {
+    lateinit var lastResourceOnlyWatchFacePackageName: String
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+    override fun createWatchFaceFutureAsync(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String
+    ): ListenableFuture<WatchFace> {
+        lastResourceOnlyWatchFacePackageName = resourceOnlyWatchFacePackageName
+
+        val future = SettableFuture.create<WatchFace>()
+        // Post a task to resolve the future.
+        getUiThreadHandler().post {
+            future.set(
+                WatchFace(
+                    WatchFaceType.DIGITAL,
+                    FakeRenderer(surfaceHolder, watchState, currentUserStyleRepository)
+                )
+                    .apply { setOverridePreviewReferenceInstant(REFERENCE_PREVIEW_TIME) }
+            )
+        }
+        return future
+    }
+
+    override fun createUserStyleSchema(resourceOnlyWatchFacePackageName: String) =
+        UserStyleSchema(emptyList())
+
+    override fun createComplicationSlotsManager(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String
+    ) = ComplicationSlotsManager(emptyList(), currentUserStyleRepository)
+
+    override fun createUserStyleFlavors(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        complicationSlotsManager: ComplicationSlotsManager,
+        resourceOnlyWatchFacePackageName: String
+    ) = UserStyleFlavors()
+}
+
 /**
  * Illustrates that createWatchFaceFuture can be resolved in a different task posted to the main
  * looper.
@@ -126,3 +183,39 @@
         assertThat(watchFace.overridePreviewReferenceInstant).isEqualTo(REFERENCE_PREVIEW_TIME)
     }
 }
+
+@RunWith(AndroidJUnit4::class)
+@RequiresApi(Build.VERSION_CODES.O_MR1)
+@MediumTest
+public class AsyncListenableWatchFaceRuntimeServiceTest : WatchFaceControlClientServiceTest() {
+
+    @Test
+    public fun asyncTest() {
+        val service = TestAsyncListenableWatchFaceRuntimeService(context, surfaceHolder)
+        val controlClient = createWatchFaceRuntimeControlClientService("com.example.wf")
+
+        val deferredClient =
+            handlerCoroutineScope.async {
+                @Suppress("deprecation")
+                controlClient.getOrCreateInteractiveWatchFaceClient(
+                    "testId",
+                    DeviceConfig(false, false, 0, 0),
+                    WatchUiState(false, 0),
+                    null,
+                    emptyMap()
+                )
+            }
+
+        handler.post { service.onCreateEngine() }
+
+        val client = awaitWithTimeout(deferredClient)
+
+        // To avoid a race condition, we need to wait for the watchface to be fully created, which
+        // this does.
+        client.complicationSlotsState
+
+        assertThat(service.lastResourceOnlyWatchFacePackageName).isEqualTo("com.example.wf")
+
+        client.close()
+    }
+}
\ No newline at end of file
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/WatchFaceControlTestService.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/WatchFaceControlTestService.kt
index 9ebc13c..029435f 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/WatchFaceControlTestService.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/WatchFaceControlTestService.kt
@@ -91,14 +91,27 @@
     val glSurface = Surface(surfaceTexture)
     val glSurfaceHolder = Mockito.mock(SurfaceHolder::class.java)
 
-    val watchFaceControlClientService = runBlocking {
-        WatchFaceControlClient.createWatchFaceControlClientImpl(
-            context,
-            Intent(context, WatchFaceControlTestService::class.java).apply {
-                action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
-            }
-        )
-    }
+    fun createWatchFaceControlClientService() =
+        runBlocking {
+            WatchFaceControlClient.createWatchFaceControlClientImpl(
+                context,
+                Intent(context, WatchFaceControlTestService::class.java).apply {
+                    action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
+                },
+                resourceOnlyWatchFacePackageName = null
+            )
+        }
+
+    fun createWatchFaceRuntimeControlClientService(resourceOnlyWatchFacePackageName: String) =
+        runBlocking {
+            WatchFaceControlClient.createWatchFaceControlClientImpl(
+                context,
+                Intent(context, WatchFaceControlTestService::class.java).apply {
+                    action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
+                },
+                resourceOnlyWatchFacePackageName
+            )
+        }
 
     @Before
     fun setUp() {
diff --git a/wear/watchface/watchface-guava/src/main/java/androidx/wear/watchface/ListenableWatchFaceService.kt b/wear/watchface/watchface-guava/src/main/java/androidx/wear/watchface/ListenableWatchFaceService.kt
index a1e25de..28542f4 100644
--- a/wear/watchface/watchface-guava/src/main/java/androidx/wear/watchface/ListenableWatchFaceService.kt
+++ b/wear/watchface/watchface-guava/src/main/java/androidx/wear/watchface/ListenableWatchFaceService.kt
@@ -73,3 +73,58 @@
         future.addListener({ it.resume(future.get()) }, { runnable -> runnable.run() })
     }
 }
+
+/**
+ * [ListenableFuture]-based compatibility wrapper around [WatchFaceRuntimeService]'s suspending
+ * [WatchFaceService.createWatchFace].
+ */
+public abstract class ListenableWatchFaceRuntimeService : WatchFaceRuntimeService() {
+    /**
+     * Override this factory method to create your WatchFaceImpl. This method will be called by the
+     * library on a background thread, if possible any expensive initialization should be done
+     * asynchronously. The [WatchFace] and its [Renderer] should be accessed exclusively from the
+     * UiThread afterwards. There is a memory barrier between construction and rendering so no
+     * special threading primitives are required.
+     *
+     * Warning the system will likely time out waiting for watch face initialization if it takes
+     * longer than [MAX_CREATE_WATCHFACE_TIME_MILLIS] milliseconds.
+     *
+     * Note cancellation of the returned future is not supported.
+     *
+     * @param surfaceHolder The [SurfaceHolder] to pass to the [Renderer]'s constructor.
+     * @param watchState The [WatchState] for the watch face.
+     * @param complicationSlotsManager The [ComplicationSlotsManager] returned by
+     *   [createComplicationSlotsManager].
+     * @param currentUserStyleRepository The [CurrentUserStyleRepository] constructed using the
+     *   [UserStyleSchema] returned by [createUserStyleSchema].
+     * @param resourceOnlyWatchFacePackageName The android package from which the watch face
+     *   definition should be loaded.
+     * @return A [ListenableFuture] for a [WatchFace] whose [Renderer] uses the provided
+     *   [surfaceHolder].
+     */
+    protected abstract fun createWatchFaceFutureAsync(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String
+    ): ListenableFuture<WatchFace>
+
+    final override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String
+    ): WatchFace = suspendCancellableCoroutine {
+        val future =
+            createWatchFaceFutureAsync(
+                surfaceHolder,
+                watchState,
+                complicationSlotsManager,
+                currentUserStyleRepository,
+                resourceOnlyWatchFacePackageName
+            )
+        future.addListener({ it.resume(future.get()) }, { runnable -> runnable.run() })
+    }
+}
\ No newline at end of file
diff --git a/wear/watchface/watchface/api/current.txt b/wear/watchface/watchface/api/current.txt
index cc6e5d8..ba61967 100644
--- a/wear/watchface/watchface/api/current.txt
+++ b/wear/watchface/watchface/api/current.txt
@@ -394,6 +394,18 @@
   @SuppressCompatibility @kotlin.RequiresOptIn(message="This is an experimental API that may change or be removed without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface WatchFaceExperimental {
   }
 
+  public abstract class WatchFaceRuntimeService extends androidx.wear.watchface.WatchFaceService {
+    ctor public WatchFaceRuntimeService();
+    method protected final androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
+    method @WorkerThread protected abstract androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName);
+    method protected final androidx.wear.watchface.style.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager);
+    method @WorkerThread protected abstract androidx.wear.watchface.style.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, String resourceOnlyWatchFacePackageName);
+    method protected final androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema();
+    method @WorkerThread protected abstract androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema(String resourceOnlyWatchFacePackageName);
+    method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+    method protected final suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+  }
+
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
     method @WorkerThread protected androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
diff --git a/wear/watchface/watchface/api/restricted_current.txt b/wear/watchface/watchface/api/restricted_current.txt
index cc6e5d8..ba61967 100644
--- a/wear/watchface/watchface/api/restricted_current.txt
+++ b/wear/watchface/watchface/api/restricted_current.txt
@@ -394,6 +394,18 @@
   @SuppressCompatibility @kotlin.RequiresOptIn(message="This is an experimental API that may change or be removed without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface WatchFaceExperimental {
   }
 
+  public abstract class WatchFaceRuntimeService extends androidx.wear.watchface.WatchFaceService {
+    ctor public WatchFaceRuntimeService();
+    method protected final androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
+    method @WorkerThread protected abstract androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName);
+    method protected final androidx.wear.watchface.style.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager);
+    method @WorkerThread protected abstract androidx.wear.watchface.style.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, String resourceOnlyWatchFacePackageName);
+    method protected final androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema();
+    method @WorkerThread protected abstract androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema(String resourceOnlyWatchFacePackageName);
+    method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+    method protected final suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+  }
+
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
     method @WorkerThread protected androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index 20f9460..9d57c46 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -249,7 +249,8 @@
                     }
                     .apply { setContext(context) }
 
-            val engine = watchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+            val engine = watchFaceService.createHeadlessEngine(componentName)
+                as WatchFaceService.EngineWrapper
             val headlessWatchFaceImpl = engine.createHeadlessInstance(params)
             return engine.deferredWatchFaceImpl.await().WFEditorDelegate(headlessWatchFaceImpl)
         }
@@ -665,11 +666,7 @@
     internal var lastDrawTimeMillis: Long = 0
     internal var nextDrawTimeMillis: Long = 0
 
-    internal val componentName =
-        ComponentName(
-            watchFaceHostApi.getContext().packageName,
-            watchFaceHostApi.getContext().javaClass.name
-        )
+    internal val componentName = watchFaceHostApi.getComponentName()
 
     internal fun getWatchFaceStyle() =
         WatchFaceStyle(
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
index 3f160d0..4d6b2dc 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
@@ -149,4 +149,7 @@
 
     /** Requests the system to capture an updated preview image. */
     public fun sendPreviewImageNeedsUpdateRequest() {}
+
+    /** Returns ComponentName of the watch face. */
+    public fun getComponentName(): ComponentName
 }
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index d9c04a97..9f2b9f2 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -299,6 +299,12 @@
     public companion object {
         private const val TAG = "WatchFaceService"
 
+        /**
+         * [ComponentName] is not allowed to have null for the class name (and it can be null for
+         * resource only watch faces), hence this placeholder.
+         */
+        private const val RESOURCE_ONLY_CLASS_NAME_PLACEHOLDER = "null"
+
         /** Whether to log every frame. */
         private const val LOG_VERBOSE = false
 
@@ -446,7 +452,7 @@
         }
     }
 
-    private val xmlSchemaAndComplicationSlotsDefinition:
+    internal val xmlSchemaAndComplicationSlotsDefinition:
         XmlSchemaAndComplicationSlotsDefinition by lazy {
         val resourceId = getXmlWatchFaceResourceId()
         if (resourceId == 0) {
@@ -477,6 +483,10 @@
             xmlSchemaAndComplicationSlotsDefinition.schema?.userStyleSettings ?: emptyList()
         )
 
+    internal open fun createUserStyleSchemaInternal(
+        resourceOnlyWatchFacePackageName: String?
+    ): UserStyleSchema = createUserStyleSchema()
+
     /**
      * If the WatchFaceService's manifest doesn't define a
      * androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data tag then override
@@ -501,6 +511,13 @@
                 getComplicationSlotInflationFactory(currentUserStyleRepository)
             )
 
+    internal open fun createComplicationSlotsManagerInternal(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String?
+    ): ComplicationSlotsManager = createComplicationSlotsManager(
+        currentUserStyleRepository
+    )
+
     /**
      * Used when inflating [ComplicationSlot]s from XML to provide a
      * [ComplicationSlotInflationFactory] which provides the [CanvasComplicationFactory] and where
@@ -566,6 +583,15 @@
         complicationSlotsManager: ComplicationSlotsManager
     ): UserStyleFlavors = xmlSchemaAndComplicationSlotsDefinition.flavors ?: UserStyleFlavors()
 
+    internal open fun createUserStyleFlavorsInternal(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        complicationSlotsManager: ComplicationSlotsManager,
+        resourceOnlyWatchFacePackageName: String?
+    ): UserStyleFlavors = createUserStyleFlavors(
+        currentUserStyleRepository,
+        complicationSlotsManager
+    )
+
     /**
      * Override this factory method to create your WatchFaceImpl. This method will be called by the
      * library on a background thread, if possible any expensive initialization should be done
@@ -592,13 +618,26 @@
         currentUserStyleRepository: CurrentUserStyleRepository
     ): WatchFace
 
+    internal open suspend fun createWatchFaceInternal(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String?
+    ): WatchFace = createWatchFace(
+        surfaceHolder,
+        watchState,
+        complicationSlotsManager,
+        currentUserStyleRepository,
+    )
+
     /** Creates an interactive engine for WallpaperService. */
     final override fun onCreateEngine(): Engine =
-        EngineWrapper(getUiThreadHandler(), getBackgroundThreadHandler(), false)
+        EngineWrapper(getUiThreadHandler(), getBackgroundThreadHandler(), null)
 
     /** Creates a headless engine. */
-    internal fun createHeadlessEngine(): Engine =
-        EngineWrapper(getUiThreadHandler(), getBackgroundThreadHandler(), true)
+    internal fun createHeadlessEngine(watchFaceName: ComponentName): Engine =
+        EngineWrapper(getUiThreadHandler(), getBackgroundThreadHandler(), watchFaceName)
 
     /** Returns the ui thread [Handler]. */
     public fun getUiThreadHandler(): Handler = getUiThreadHandlerImpl()
@@ -1149,7 +1188,7 @@
     public inner class EngineWrapper(
         private val uiThreadHandler: Handler,
         private val backgroundThreadHandler: Handler,
-        headless: Boolean
+        headlessComponentName: ComponentName?
     ) :
         WallpaperService.Engine(),
         WatchFaceHostApi,
@@ -1197,7 +1236,7 @@
                 // That's supposed to get sent very quickly, but in case it doesn't we initially
                 // assume we're not in ambient mode which should be correct most of the time.
                 isAmbient.value = false
-                isHeadless = headless
+                isHeadless = (headlessComponentName != null)
                 isLocked.value =
                     (getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager).isDeviceLocked
             }
@@ -1277,6 +1316,7 @@
         internal var immutableSystemStateDone = false
         internal var immutableChinHeightDone = false
         internal var systemHasSentWatchUiState = false
+        internal var resourceOnlyWatchFacePackageName: String? = headlessComponentName?.packageName
 
         private var asyncWatchFaceConstructionPending = false
 
@@ -1885,23 +1925,34 @@
         /** This will be called from a binder thread. */
         @WorkerThread
         internal fun getDefaultProviderPolicies(): Array<IdTypeAndDefaultProviderPolicyWireFormat> {
-            return createComplicationSlotsManager(
-                    CurrentUserStyleRepository(createUserStyleSchema())
+            return createComplicationSlotsManagerInternal(
+                    CurrentUserStyleRepository(
+                        createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)
+                    ),
+                    resourceOnlyWatchFacePackageName
                 )
                 .getDefaultProviderPolicies()
         }
 
         /** This will be called from a binder thread. */
         @WorkerThread
-        internal fun getUserStyleSchemaWireFormat() = createUserStyleSchema().toWireFormat()
+        internal fun getUserStyleSchemaWireFormat() =
+            createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName).toWireFormat()
 
         /** This will be called from a binder thread. */
         @WorkerThread
         internal fun getUserStyleFlavorsWireFormat(): UserStyleFlavorsWireFormat {
-            val currentUserStyleRepository = CurrentUserStyleRepository(createUserStyleSchema())
-            return createUserStyleFlavors(
+            val currentUserStyleRepository =
+                CurrentUserStyleRepository(
+                    createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)
+                )
+            return createUserStyleFlavorsInternal(
                     currentUserStyleRepository,
-                    createComplicationSlotsManager(currentUserStyleRepository)
+                    createComplicationSlotsManagerInternal(
+                        currentUserStyleRepository,
+                        resourceOnlyWatchFacePackageName
+                    ),
+                    resourceOnlyWatchFacePackageName
                 )
                 .toWireFormat()
         }
@@ -1910,7 +1961,11 @@
         @OptIn(ComplicationExperimental::class)
         @WorkerThread
         internal fun getComplicationSlotMetadataWireFormats() =
-            createComplicationSlotsManager(CurrentUserStyleRepository(createUserStyleSchema()))
+            createComplicationSlotsManagerInternal(
+                CurrentUserStyleRepository(
+                    createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)),
+                    resourceOnlyWatchFacePackageName
+                )
                 .complicationSlots
                 .map {
                     val systemDataSourceFallbackDefaultType =
@@ -2043,6 +2098,8 @@
                 setWatchUiState(params.watchUiState, fromSysUi = false)
                 initialUserStyle = params.userStyle
 
+                resourceOnlyWatchFacePackageName = params.auxiliaryComponentPackageName
+
                 mutableWatchState.watchFaceInstanceId.value = sanitizeWatchFaceId(params.instanceId)
                 val watchState = mutableWatchState.asWatchState()
 
@@ -2108,13 +2165,18 @@
                 val timeBefore = System.currentTimeMillis()
                 val currentUserStyleRepository =
                     TraceEvent("WatchFaceService.createUserStyleSchema").use {
-                        CurrentUserStyleRepository(createUserStyleSchema())
+                        CurrentUserStyleRepository(
+                            createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)
+                        )
                     }
                 initStyle(currentUserStyleRepository)
 
                 val complicationSlotsManager =
                     TraceEvent("WatchFaceService.createComplicationsManager").use {
-                        createComplicationSlotsManager(currentUserStyleRepository)
+                        createComplicationSlotsManagerInternal(
+                            currentUserStyleRepository,
+                            resourceOnlyWatchFacePackageName
+                        )
                     }
                 complicationSlotsManager.watchFaceHostApi = this@EngineWrapper
                 complicationSlotsManager.watchState = watchState
@@ -2130,7 +2192,11 @@
 
                 val userStyleFlavors =
                     TraceEvent("WatchFaceService.createUserStyleFlavors").use {
-                        createUserStyleFlavors(currentUserStyleRepository, complicationSlotsManager)
+                        createUserStyleFlavorsInternal(
+                            currentUserStyleRepository,
+                            complicationSlotsManager,
+                            resourceOnlyWatchFacePackageName
+                        )
                     }
 
                 deferredEarlyInitDetails.complete(
@@ -2164,13 +2230,13 @@
                         TraceEvent("WatchFaceService.createWatchFace").use {
                             // Note by awaiting deferredSurfaceHolder we ensure onSurfaceChanged has
                             // been called and we're passing the correct updated surface holder.
-                            // This is
-                            // important for GL rendering.
-                            createWatchFace(
+                            // This is important for GL rendering.
+                            createWatchFaceInternal(
                                 surfaceHolder,
                                 watchState,
                                 complicationSlotsManager,
-                                currentUserStyleRepository
+                                currentUserStyleRepository,
+                                resourceOnlyWatchFacePackageName
                             )
                         }
                     [email protected](watchFace)
@@ -2497,6 +2563,14 @@
             }
         }
 
+        override fun getComponentName(): ComponentName {
+            resourceOnlyWatchFacePackageName?.let {
+                return ComponentName(it, RESOURCE_ONLY_CLASS_NAME_PLACEHOLDER)
+            }
+
+            return ComponentName(getContext().packageName, getContext().javaClass.name)
+        }
+
         override fun onWatchFaceColorsChanged(watchFaceColors: WatchFaceColors?) {
             synchronized(lock) {
                 lastWatchFaceColors = watchFaceColors
@@ -2777,6 +2851,7 @@
             writer.println("surfaceDestroyed=$surfaceDestroyed")
             writer.println("lastComplications=${complicationsFlow.value.joinToString()}")
             writer.println("pendingUpdateTime=${pendingUpdateTime.isPending()}")
+            writer.println("Resource only package name $resourceOnlyWatchFacePackageName")
 
             synchronized(lock) {
                 forEachListener("dump") { writer.println("listener = ${it.asBinder()}") }
@@ -2824,6 +2899,183 @@
 }
 
 /**
+ * WatchFaceRuntimeService is a special kind of [WatchFaceService], which loads the watch face
+ * definition from another resource only watch face package (see the
+ * `resourceOnlyWatchFacePackageName` parameter passed to [createUserStyleSchema],
+ * [createComplicationSlotsManager], [createUserStyleFlavors] and
+ * [createWatchFace]).
+ *
+ * Note because a WatchFaceRuntimeService loads it's resources from another package, it will need
+ * the following permission:
+ *
+ * ```
+ *     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
+ *         tools:ignore="QueryAllPackagesPermission" />
+ * ```
+ *
+ * Currently Wear OS only supports the runtime for the Android Watch Face Format (see
+ * https://developer.android.com/training/wearables/wff for more details).
+ *
+ * Note only one watch face definition per resource only watch face package is supported.
+ */
+abstract class WatchFaceRuntimeService : WatchFaceService() {
+    /**
+     * If the WatchFaceService's manifest doesn't define a
+     * androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data tag then override
+     * this factory method to create a non-empty [UserStyleSchema]. A [CurrentUserStyleRepository]
+     * constructed with this schema will be passed to [createComplicationSlotsManager],
+     * [createUserStyleFlavors] and [createWatchFace]. This is called on a background thread.
+     *
+     * @param resourceOnlyWatchFacePackageName The android package from which the watch face
+     *   definition should be loaded.
+     * @return The [UserStyleSchema] to create a [CurrentUserStyleRepository] with, which is passed
+     *   to [createComplicationSlotsManager] and [createWatchFace].
+     */
+    @WorkerThread
+    protected abstract fun createUserStyleSchema(
+        resourceOnlyWatchFacePackageName: String
+    ): UserStyleSchema
+
+    override fun createUserStyleSchemaInternal(
+        resourceOnlyWatchFacePackageName: String?
+    ): UserStyleSchema = createUserStyleSchema(resourceOnlyWatchFacePackageName!!)
+
+    @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
+    final override fun createUserStyleSchema(): UserStyleSchema {
+        throw UnsupportedOperationException("resourceOnlyWatchFacePackageName must be specified")
+    }
+
+    /**
+     * If the WatchFaceService's manifest doesn't define a
+     * androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data tag then override
+     * this factory method to create a non-empty [ComplicationSlotsManager]. This manager will be
+     * passed to [createUserStyleFlavors] and [createWatchFace]. This will be called from a
+     * background thread but the ComplicationSlotsManager should be accessed exclusively from the
+     * UiThread afterwards.
+     *
+     * @param currentUserStyleRepository The [CurrentUserStyleRepository] constructed using the
+     *   [UserStyleSchema] returned by [createUserStyleSchema].
+     * @param resourceOnlyWatchFacePackageName The android package from which the watch face
+     *   definition should be loaded.
+     * @return The [ComplicationSlotsManager] to pass into [createWatchFace].
+     */
+    @WorkerThread
+    protected abstract fun createComplicationSlotsManager(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String
+    ): ComplicationSlotsManager
+
+    internal override fun createComplicationSlotsManagerInternal(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String?
+    ): ComplicationSlotsManager = createComplicationSlotsManager(
+        currentUserStyleRepository,
+        resourceOnlyWatchFacePackageName!!
+    )
+
+    @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
+    final override fun createComplicationSlotsManager(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+    ): ComplicationSlotsManager {
+        throw UnsupportedOperationException("resourceOnlyWatchFacePackageName must be specified")
+    }
+
+    /**
+     * If the WatchFaceService's manifest doesn't define a
+     * androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data tag then override
+     * this factory method to create non-empty [UserStyleFlavors]. This is called on a background
+     * thread. The system reads the flavors once and changes may be ignored until the APK is
+     * updated. Metadata tag "androidx.wear.watchface.FLAVORS_SUPPORTED" should be added to let the
+     * system know the service supports flavors.
+     *
+     * @param currentUserStyleRepository The [CurrentUserStyleRepository] constructed using the
+     *   [UserStyleSchema] returned by [createUserStyleSchema].
+     * @param complicationSlotsManager The [ComplicationSlotsManager] returned by
+     *   [createComplicationSlotsManager]
+     * @param resourceOnlyWatchFacePackageName The android package from which the watch face
+     *   definition should be loaded.
+     * @return The [UserStyleFlavors], which is exposed to the system.
+     */
+    @WorkerThread
+    protected abstract fun createUserStyleFlavors(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        complicationSlotsManager: ComplicationSlotsManager,
+        resourceOnlyWatchFacePackageName: String
+    ): UserStyleFlavors
+
+    internal override fun createUserStyleFlavorsInternal(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        complicationSlotsManager: ComplicationSlotsManager,
+        resourceOnlyWatchFacePackageName: String?
+    ): UserStyleFlavors = createUserStyleFlavors(
+        currentUserStyleRepository,
+        complicationSlotsManager,
+        resourceOnlyWatchFacePackageName!!
+    )
+
+    @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
+    final override fun createUserStyleFlavors(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        complicationSlotsManager: ComplicationSlotsManager
+    ): UserStyleFlavors {
+        throw UnsupportedOperationException("resourceOnlyWatchFacePackageName must be specified")
+    }
+
+    /**
+     * Override this factory method to create your WatchFaceImpl. This method will be called by the
+     * library on a background thread, if possible any expensive initialization should be done
+     * asynchronously. The [WatchFace] and its [Renderer] should be accessed exclusively from the
+     * UiThread afterwards. There is a memory barrier between construction and rendering so no
+     * special threading primitives are required.
+     *
+     * Warning the system will likely time out waiting for watch face initialization if it takes
+     * longer than [WatchFaceService.MAX_CREATE_WATCHFACE_TIME_MILLIS] milliseconds.
+     *
+     * @param surfaceHolder The [SurfaceHolder] to pass to the [Renderer]'s constructor.
+     * @param watchState The [WatchState] for the watch face.
+     * @param complicationSlotsManager The [ComplicationSlotsManager] returned by
+     *   [createComplicationSlotsManager].
+     * @param currentUserStyleRepository The [CurrentUserStyleRepository] constructed using the
+     *   [UserStyleSchema] returned by [createUserStyleSchema].
+     * @param resourceOnlyWatchFacePackageName The android package from which the watch face
+     *   definition should be loaded.
+     * @return A [WatchFace] whose [Renderer] uses the provided [surfaceHolder].
+     */
+    @WorkerThread
+    protected abstract suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String
+    ): WatchFace
+
+    internal override suspend fun createWatchFaceInternal(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String?
+    ): WatchFace = createWatchFace(
+        surfaceHolder,
+        watchState,
+        complicationSlotsManager,
+        currentUserStyleRepository,
+        resourceOnlyWatchFacePackageName!!
+    )
+
+    @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
+    final override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ): WatchFace {
+        throw UnsupportedOperationException("resourceOnlyWatchFacePackageName must be specified")
+    }
+}
+
+/**
  * Runs the supplied task on the handler thread. If we're not on the handler thread a task is
  * posted.
  *
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
index 07232b0..d4ef013 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
@@ -17,6 +17,7 @@
 package androidx.wear.watchface.control
 
 import android.annotation.SuppressLint
+import android.util.Log
 import androidx.annotation.UiThread
 import androidx.annotation.VisibleForTesting
 import androidx.wear.watchface.IndentingPrintWriter
@@ -49,6 +50,8 @@
     )
 
     companion object {
+        private const val TAG = "InteractiveInstanceManager"
+
         private val instances = HashMap<String, RefCountedInteractiveWatchFaceInstance>()
         private val pendingWallpaperInteractiveWatchFaceInstanceLock = Any()
         private var pendingWallpaperInteractiveWatchFaceInstance:
@@ -204,6 +207,18 @@
             // system thinks we should have. Note runBlocking is safe here because we never await.
             val engine = impl.engine!!
             engine.setUserStyle(value.params.userStyle)
+
+            if (engine.resourceOnlyWatchFacePackageName !=
+                    value.params.auxiliaryComponentPackageName
+            ) {
+                val message =
+                    "Existing instance has the resourceOnlyWatchFacePackageName of " +
+                        "${engine.resourceOnlyWatchFacePackageName}, which is different from the " +
+                        "argument watchFaceId of ${value.params.auxiliaryComponentPackageName}."
+                Log.e(TAG, message)
+                throw IllegalStateException(message)
+            }
+
             return impl
         }
 
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
index 3ca3862..98582e5 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
@@ -193,7 +193,8 @@
                 }
                 watchFaceService.onCreate()
                 val engine =
-                    watchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+                    watchFaceService.createHeadlessEngine(watchFaceName)
+                        as WatchFaceService.EngineWrapper
                 ServiceAndEngine(watchFaceService, engine)
             } else {
                 null
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index b953af0..d55140d 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -3319,12 +3319,14 @@
                 choreographer
             )
 
+        val componentName = ComponentName("test.watchface.app", "test.watchface.class")
         engineWrapper =
-            testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+            testWatchFaceService.createHeadlessEngine(componentName)
+                as WatchFaceService.EngineWrapper
 
         engineWrapper.createHeadlessInstance(
             HeadlessWatchFaceInstanceParams(
-                ComponentName("test.watchface.app", "test.watchface.class"),
+                componentName,
                 DeviceConfig(false, false, 100, 200),
                 100,
                 100,
@@ -3886,7 +3888,9 @@
                 choreographer
             )
 
-        testWatchFaceService.createHeadlessEngine()
+        testWatchFaceService.createHeadlessEngine(
+            ComponentName("test.watchface.app", "test.watchface.class")
+        )
 
         runPostedTasksFor(0)
 
@@ -4550,12 +4554,14 @@
         engineWrapper.onCreate(surfaceHolder)
         engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
 
+        val componentName = ComponentName("test.watchface.app", "test.watchface.class")
         val headlessEngineWrapper =
-            testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+            testWatchFaceService.createHeadlessEngine(componentName)
+                as WatchFaceService.EngineWrapper
 
         headlessEngineWrapper.createHeadlessInstance(
             HeadlessWatchFaceInstanceParams(
-                ComponentName("test.watchface.app", "test.watchface.class"),
+                componentName,
                 DeviceConfig(false, false, 100, 200),
                 100,
                 100,
@@ -4691,12 +4697,14 @@
         engineWrapper.onCreate(surfaceHolder)
         engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
 
+        val componentName = ComponentName("test.watchface.app", "test.watchface.class")
         val headlessEngineWrapper =
-            testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+            testWatchFaceService.createHeadlessEngine(componentName)
+                as WatchFaceService.EngineWrapper
 
         headlessEngineWrapper.createHeadlessInstance(
             HeadlessWatchFaceInstanceParams(
-                ComponentName("test.watchface.app", "test.watchface.class"),
+                componentName,
                 DeviceConfig(false, false, 100, 200),
                 100,
                 100,
@@ -4823,12 +4831,14 @@
         engineWrapper.onCreate(surfaceHolder)
         engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
 
+        val componentName = ComponentName("test.watchface.app", "test.watchface.class")
         val headlessEngineWrapper =
-            testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+            testWatchFaceService.createHeadlessEngine(componentName)
+                as WatchFaceService.EngineWrapper
 
         headlessEngineWrapper.createHeadlessInstance(
             HeadlessWatchFaceInstanceParams(
-                ComponentName("test.watchface.app", "test.watchface.class"),
+                componentName,
                 DeviceConfig(false, false, 100, 200),
                 100,
                 100,
@@ -5119,12 +5129,14 @@
                 choreographer
             )
 
+        val componentName = ComponentName("test.watchface.app", "test.watchface.class")
         engineWrapper =
-            testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+            testWatchFaceService.createHeadlessEngine(componentName)
+                as WatchFaceService.EngineWrapper
 
         engineWrapper.createHeadlessInstance(
             HeadlessWatchFaceInstanceParams(
-                ComponentName("test.watchface.app", "test.watchface.class"),
+                componentName,
                 DeviceConfig(false, false, 100, 200),
                 100,
                 100,
@@ -5187,12 +5199,14 @@
                 choreographer
             )
 
+        val componentName = ComponentName("test.watchface.app", "test.watchface.class")
         engineWrapper =
-            testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+            testWatchFaceService.createHeadlessEngine(componentName)
+                as WatchFaceService.EngineWrapper
 
         engineWrapper.createHeadlessInstance(
             HeadlessWatchFaceInstanceParams(
-                ComponentName("test.watchface.app", "test.watchface.class"),
+                componentName,
                 DeviceConfig(false, false, 100, 200),
                 100,
                 100,
@@ -6381,12 +6395,14 @@
                 forceIsVisible = true
             )
 
+        val componentName = ComponentName("test.watchface.app", "test.watchface.class")
         engineWrapper =
-            testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+            testWatchFaceService.createHeadlessEngine(componentName)
+                as WatchFaceService.EngineWrapper
 
         engineWrapper.createHeadlessInstance(
             HeadlessWatchFaceInstanceParams(
-                ComponentName("test.watchface.app", "test.watchface.class"),
+                componentName,
                 DeviceConfig(false, false, 100, 200),
                 100,
                 100,