Merge "Import translations. DO NOT MERGE ANYWHERE" into androidx-main
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
index 02bb86f..e5bccd9 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
@@ -44,6 +44,7 @@
 import androidx.camera.camera2.pipe.integration.interop.CaptureRequestOptions
 import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
 import androidx.camera.camera2.pipe.testing.VerifyResultListener
+import androidx.camera.core.CameraControl
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.FocusMeteringAction
 import androidx.camera.core.ImageAnalysis
@@ -59,6 +60,8 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth
+import com.google.common.util.concurrent.ListenableFuture
+import java.util.concurrent.ExecutionException
 import java.util.concurrent.TimeUnit
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asExecutor
@@ -67,10 +70,10 @@
 import org.hamcrest.CoreMatchers.equalTo
 import org.hamcrest.CoreMatchers.notNullValue
 import org.junit.After
+import org.junit.Assert
 import org.junit.Assume
 import org.junit.Assume.assumeThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -159,7 +162,6 @@
 
     // TODO: test all public API of the CameraControl to ensure the RequestOptions still exist
     //  after adding/removing the UseCase.
-    @Ignore("b/265316774")
     @Test
     fun removeUseCase_requestOptionsShouldSetToCamera(): Unit = runBlocking {
         // Arrange.
@@ -338,12 +340,25 @@
         )
     }
 
+    @Test
+    fun setZoomRatio_operationCanceledExceptionIfNoUseCase() {
+        assertFutureFailedWithOperationCancellation(cameraControl.setZoomRatio(1.5f))
+    }
+
+    private fun <T> assertFutureFailedWithOperationCancellation(future: ListenableFuture<T>) {
+        Assert.assertThrows(ExecutionException::class.java) {
+            future[3, TimeUnit.SECONDS]
+        }.apply {
+            Truth.assertThat(cause)
+                .isInstanceOf(CameraControl.OperationCanceledException::class.java)
+        }
+    }
+
     private fun CameraCharacteristics.getMaxRegionCount(
         option_max_regions: CameraCharacteristics.Key<Int>
     ) = get(option_max_regions) ?: 0
 
     private suspend fun arrangeRequestOptions() {
-        cameraControl.setExposureCompensationIndex(1)
         cameraControl.setZoomRatio(1.0f)
         cameraControl.camera2cameraControl.setCaptureRequestOptions(
             CaptureRequestOptions.Builder().setCaptureRequestOption(
@@ -351,6 +366,7 @@
                 CONTROL_CAPTURE_INTENT_CUSTOM
             ).build()
         ).await()
+        cameraControl.setExposureCompensationIndex(1)[5, TimeUnit.SECONDS]
 
         // Ensure the requests are already set to the CaptureRequest.
         waitForResult().verify(
@@ -420,4 +436,4 @@
         )
         cameraControl = camera.cameraControl as CameraControlAdapter
     }
-}
\ No newline at end of file
+}
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt
index 7469d0e..1c65179 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt
@@ -68,8 +68,12 @@
             signal.completeExceptionally(failureException)
             return
         }
-        if (_verifyBlock(requestMetadata, result)) {
-            signal.complete(Unit)
+        try {
+            if (_verifyBlock(requestMetadata, result)) {
+                signal.complete(Unit)
+            }
+        } catch (e: Throwable) {
+            signal.completeExceptionally(e)
         }
     }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
index f4ed9b6..b920ee1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
@@ -42,6 +42,7 @@
 import dagger.Module
 import dagger.Provides
 import dagger.Subcomponent
+import java.util.concurrent.CancellationException
 import javax.inject.Scope
 
 @Scope
@@ -135,11 +136,15 @@
 
         if (sessionConfigAdapter.isSessionConfigValid()) {
             useCaseSurfaceManager.setupAsync(graph, sessionConfigAdapter, surfaceToStreamMap)
-                .invokeOnCompletion {
-                    it?.let { Log.error(it) { "Surface setup error!" } }
+                .invokeOnCompletion { throwable ->
+                    // Only show logs for error cases, ignore CancellationException since the task
+                    // could be cancelled by UseCaseSurfaceManager#stopAsync().
+                    if (throwable != null && throwable !is CancellationException) {
+                        Log.error(throwable) { "Surface setup error!" }
+                    }
                 }
         } else {
-            Log.debug {
+            Log.error {
                 "Unable to create capture session due to conflicting configurations"
             }
         }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
index 23f0b97..43b6750 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
@@ -32,7 +32,9 @@
 import androidx.camera.core.FocusMeteringResult
 import androidx.camera.core.MeteringPoint
 import androidx.camera.core.Preview
+import androidx.camera.core.UseCase
 import androidx.camera.core.impl.CameraControlInternal
+import androidx.lifecycle.Observer
 import com.google.common.util.concurrent.ListenableFuture
 import dagger.Binds
 import dagger.Module
@@ -40,7 +42,10 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withTimeoutOrNull
 
 /**
@@ -53,20 +58,37 @@
     val threads: UseCaseThreads,
 ) : UseCaseCameraControl {
     private var _useCaseCamera: UseCaseCamera? = null
+
+    private val useCaseObserver = Observer<Set<UseCase>> { useCases ->
+        // reset to null since preview use case may not be active in current runningUseCases
+        previewAspectRatio = null
+        useCases.forEach { useCase ->
+            if (useCase is Preview) {
+                useCase.attachedSurfaceResolution?.apply {
+                    previewAspectRatio = Rational(width, height)
+                }
+            }
+        }
+    }
+
     override var useCaseCamera: UseCaseCamera?
         get() = _useCaseCamera
         set(value) {
+            if (_useCaseCamera != value) {
+                previewAspectRatio = null
+                // withContext does not switch thread properly with scope.launch so async is used
+                threads.sequentialScope.async {
+                    withContext(Dispatchers.Main) {
+                        _useCaseCamera?.runningUseCasesLiveData?.removeObserver(useCaseObserver)
+                    }
+                }
+            }
+
             _useCaseCamera = value
 
-            // reset to null since preview ratio may not be applicable for current runningUseCases
-            previewAspectRatio = null
-            _useCaseCamera?.runningUseCasesLiveData?.observeForever { useCases ->
-                useCases.forEach { useCase ->
-                    if (useCase is Preview) {
-                        useCase.attachedSurfaceResolution?.apply {
-                            previewAspectRatio = Rational(width, height)
-                        }
-                    }
+            threads.sequentialScope.async {
+                withContext(Dispatchers.Main) {
+                    _useCaseCamera?.runningUseCasesLiveData?.observeForever(useCaseObserver)
                 }
             }
         }
@@ -75,6 +97,7 @@
         cancelFocusAndMeteringAsync()
     }
 
+    @Volatile
     private var previewAspectRatio: Rational? = null
     private val sensorRect by lazy {
         // TODO("b/262225455"): use the actual crop sensor region like in camera-camera2
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index f57b581..0826055 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -116,6 +116,11 @@
             !attachedUseCases.contains(useCase)
         }
 
+        // Notify state attached to use cases
+        for (useCase in unattachedUseCases) {
+            useCase.onStateAttached()
+        }
+
         if (attachedUseCases.addAll(useCases)) {
             if (shouldAddRepeatingUseCase(getRunningUseCases())) {
                 addRepeatingUseCase()
@@ -124,9 +129,9 @@
             }
         }
 
-        // Notify state attached to use cases that weren't attached before
-        for (useCase in unattachedUseCases) {
-            useCase.onStateAttached()
+        unattachedUseCases.forEach { useCase ->
+            // Notify CameraControl is ready after the UseCaseCamera is created
+            useCase.onCameraControlReady()
         }
     }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
index 844552b..698da1b 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
@@ -21,7 +21,9 @@
 import androidx.camera.camera2.pipe.integration.adapter.asListenableFuture
 import androidx.camera.camera2.pipe.integration.compat.ZoomCompat
 import androidx.camera.camera2.pipe.integration.config.CameraScope
+import androidx.camera.core.CameraControl
 import androidx.camera.core.ZoomState
+import androidx.camera.core.impl.utils.futures.Futures
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import com.google.common.util.concurrent.ListenableFuture
@@ -117,17 +119,21 @@
 
     fun setZoomRatioAsync(ratio: Float): ListenableFuture<Void> {
         // TODO: report IllegalArgumentException if ratio not in range
-        return threads.sequentialScope.launch(start = CoroutineStart.UNDISPATCHED) {
+        return Futures.nonCancellationPropagating(
             useCaseCamera?.let {
-                val zoomValue = ZoomValue(
-                    ratio,
-                    minZoom,
-                    maxZoom
-                )
-                setZoomState(zoomValue)
-                update()
-            }
-        }.asListenableFuture()
+                threads.scope.launch(start = CoroutineStart.UNDISPATCHED) {
+                    val zoomValue = ZoomValue(
+                        ratio,
+                        minZoom,
+                        maxZoom
+                    )
+                    setZoomState(zoomValue)
+                    update()
+                }.asListenableFuture()
+            } ?: Futures.immediateFailedFuture(
+                CameraControl.OperationCanceledException("Camera is not active.")
+            )
+        )
     }
 
     private fun nearZero(num: Float): Boolean {
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt
index 50afb50..2cb7051 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt
@@ -51,6 +51,7 @@
 import androidx.camera.testing.fakes.FakeUseCase
 import androidx.lifecycle.MutableLiveData
 import androidx.test.filters.MediumTest
+import androidx.testutils.MainDispatcherRule
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import com.google.common.util.concurrent.FutureCallback
@@ -64,15 +65,22 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.asExecutor
 import kotlinx.coroutines.async
+import kotlinx.coroutines.cancel
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.withContext
+import org.junit.After
 import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Ignore
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.annotation.Config
@@ -101,11 +109,27 @@
     SENSOR_WIDTH,
     SENSOR_HEIGHT
 )
+// the following rectangles are for metering point (0, 0)
+private val M_RECT_PVIEW_RATIO_16x9_SENSOR_640x480 = Rect(
+    0, 60 - AREA_HEIGHT / 2,
+    AREA_WIDTH / 2, 60 + AREA_HEIGHT / 2
+)
+private val M_RECT_PVIEW_RATIO_4x3_SENSOR_1920x1080 = Rect(
+    240 - AREA_WIDTH_2 / 2, 0,
+    240 + AREA_WIDTH_2 / 2, AREA_HEIGHT_2 / 2
+)
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(RobolectricCameraPipeTestRunner::class)
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 @DoNotInstrument
 class FocusMeteringControlTest {
+    private val testScope = TestScope()
+    private val testDispatcher = StandardTestDispatcher(testScope.testScheduler)
+
+    @get:Rule
+    val mainDispatcherRule = MainDispatcherRule(testDispatcher)
+
     private val pointFactory = SurfaceOrientedMeteringPointFactory(1f, 1f)
     private lateinit var focusMeteringControl: FocusMeteringControl
 
@@ -128,6 +152,15 @@
         )
     }
 
+    // testUseCaseThreads is used to ensure that all coroutines are executed before some specific
+    // point. But fakeUseCaseThreads can not be replaced in all tests because testDispatcher
+    // does not work well with withTimeoutOrNull()
+    private val testUseCaseThreads = UseCaseThreads(
+        testScope,
+        testDispatcher.asExecutor(),
+        testDispatcher
+    )
+
     @Before
     fun setUp() {
         loadCameraProperties()
@@ -136,6 +169,18 @@
         focusMeteringControl = initFocusMeteringControl(CAMERA_ID_0)
     }
 
+    @After
+    fun tearDown() {
+        // CoroutineScope#cancel can throw exception if the scope has no job left
+        try {
+            // fakeUseCaseThreads may still be using Main dispatcher which sometimes
+            // causes Dispatchers.resetMain() to throw an exception:
+            // "IllegalStateException: Dispatchers.Main is used concurrently with setting it"
+            fakeUseCaseThreads.scope.cancel()
+            fakeUseCaseThreads.sequentialScope.cancel()
+        } catch (_: Exception) {}
+    }
+
     @Test
     fun meteringRegionsFromMeteringPoint_fovAspectRatioEqualToCropAspectRatio() {
         val meteringPoint = FakeMeteringPointFactory().createPoint(0.0f, 0.0f)
@@ -389,17 +434,22 @@
         // use 16:9 preview aspect ratio with sensor region of 4:3 (camera 0)
         focusMeteringControl = initFocusMeteringControl(
             CAMERA_ID_0,
-            setOf(createPreview(Size(1920, 1080)))
+            setOf(createPreview(Size(1920, 1080))),
+            testUseCaseThreads,
         )
 
+        testDispatcher.scheduler.advanceUntilIdle()
+
         startFocusMeteringAndAwait(
             FocusMeteringAction.Builder(point1).build()
         )
 
-        val adjustedRect = Rect(0, 60 - AREA_HEIGHT / 2, AREA_WIDTH / 2, 60 + AREA_HEIGHT / 2)
+        testDispatcher.scheduler.advanceUntilIdle()
+
         with(fakeRequestControl.focusMeteringCalls.last()) {
             assertWithMessage("Wrong number of AF regions").that(afRegions.size).isEqualTo(1)
-            assertWithMessage("Wrong AF region").that(afRegions[0].rect).isEqualTo(adjustedRect)
+            assertWithMessage("Wrong AF region")
+                .that(afRegions[0].rect).isEqualTo(M_RECT_PVIEW_RATIO_16x9_SENSOR_640x480)
         }
     }
 
@@ -408,20 +458,22 @@
         // use 4:3 preview aspect ratio with sensor region of 16:9 (camera 1)
         focusMeteringControl = initFocusMeteringControl(
             CAMERA_ID_1,
-            setOf(createPreview(Size(640, 480)))
+            setOf(createPreview(Size(640, 480))),
+            testUseCaseThreads,
         )
 
+        testDispatcher.scheduler.advanceUntilIdle()
+
         startFocusMeteringAndAwait(
             FocusMeteringAction.Builder(point1).build()
         )
 
-        val adjustedRect = Rect(
-            240 - AREA_WIDTH_2 / 2, 0,
-            240 + AREA_WIDTH_2 / 2, AREA_HEIGHT_2 / 2
-        )
+        testDispatcher.scheduler.advanceUntilIdle()
+
         with(fakeRequestControl.focusMeteringCalls.last()) {
             assertWithMessage("Wrong number of AF regions").that(afRegions.size).isEqualTo(1)
-            assertWithMessage("Wrong AF region").that(afRegions[0].rect).isEqualTo(adjustedRect)
+            assertWithMessage("Wrong AF region")
+                .that(afRegions[0].rect).isEqualTo(M_RECT_PVIEW_RATIO_4x3_SENSOR_1920x1080)
         }
     }
 
@@ -436,17 +488,22 @@
 
         focusMeteringControl = initFocusMeteringControl(
             CAMERA_ID_0,
-            setOf(createPreview(Size(640, 480)))
+            setOf(createPreview(Size(640, 480))),
+            testUseCaseThreads,
         )
 
+        testDispatcher.scheduler.advanceUntilIdle()
+
         startFocusMeteringAndAwait(
             FocusMeteringAction.Builder(point).build()
         )
 
-        val adjustedRect = Rect(0, 60 - AREA_HEIGHT / 2, AREA_WIDTH / 2, 60 + AREA_HEIGHT / 2)
+        testDispatcher.scheduler.advanceUntilIdle()
+
         with(fakeRequestControl.focusMeteringCalls.last()) {
             assertWithMessage("Wrong number of AF regions").that(afRegions.size).isEqualTo(1)
-            assertWithMessage("Wrong AF region").that(afRegions[0].rect).isEqualTo(adjustedRect)
+            assertWithMessage("Wrong AF region")
+                .that(afRegions[0].rect).isEqualTo(M_RECT_PVIEW_RATIO_16x9_SENSOR_640x480)
         }
     }
 
@@ -455,18 +512,57 @@
         // add 16:9 aspect ratio Preview with sensor region of 4:3 (camera 0), then remove Preview
         focusMeteringControl = initFocusMeteringControl(
             CAMERA_ID_0,
-            setOf(createPreview(Size(1920, 1080)))
+            setOf(createPreview(Size(1920, 1080))),
+            testUseCaseThreads,
         )
         fakeUseCaseCamera.runningUseCasesLiveData.value = emptySet()
 
+        testDispatcher.scheduler.advanceUntilIdle()
+
         startFocusMeteringAndAwait(
             FocusMeteringAction.Builder(point1).build()
         )
 
-        val adjustedRect = Rect(0, 60 - AREA_HEIGHT / 2, AREA_WIDTH / 2, 60 + AREA_HEIGHT / 2)
+        testDispatcher.scheduler.advanceUntilIdle()
+
+        // point1 = (0, 0) is considered as center point of metering rectangle.
+        // Since previewAspectRatio is not set, it will be same as cropRegionAspectRatio
+        // which is the size of SENSOR_1 in this test. So the point is not adjusted,
+        // and simply M_RECT_1 (metering rectangle of point1 with SENSOR_1) should be used.
         with(fakeRequestControl.focusMeteringCalls.last()) {
             assertWithMessage("Wrong number of AF regions").that(afRegions.size).isEqualTo(1)
-            assertWithMessage("Wrong AF region").that(afRegions[0].rect).isEqualTo(adjustedRect)
+            assertWithMessage("Wrong AF region").that(afRegions[0].rect).isEqualTo(M_RECT_1)
+        }
+    }
+
+    @Test
+    fun previewRatioIsNotChanged_whenSameUseCaseCameraSetTwice() {
+        // use 16:9 aspect ratio Preview with sensor region of 4:3 (camera 0)
+        val useCases = setOf<UseCase>(createPreview(Size(1920, 1080)))
+
+        focusMeteringControl = initFocusMeteringControl(
+            CAMERA_ID_0,
+            useCases,
+            UseCaseThreads(
+                testScope,
+                testDispatcher.asExecutor(),
+                testDispatcher
+            ),
+        )
+        focusMeteringControl.useCaseCamera = fakeUseCaseCamera
+
+        testDispatcher.scheduler.advanceUntilIdle()
+
+        startFocusMeteringAndAwait(
+            FocusMeteringAction.Builder(point1).build()
+        )
+
+        testDispatcher.scheduler.advanceUntilIdle()
+
+        with(fakeRequestControl.focusMeteringCalls.last()) {
+            assertWithMessage("Wrong number of AF regions").that(afRegions.size).isEqualTo(1)
+            assertWithMessage("Wrong AF region")
+                .that(afRegions[0].rect).isEqualTo(M_RECT_PVIEW_RATIO_16x9_SENSOR_640x480)
         }
     }
 
@@ -1035,8 +1131,9 @@
     private fun initFocusMeteringControl(
         cameraId: String,
         useCases: Set<UseCase> = emptySet(),
+        useCaseThreads: UseCaseThreads = fakeUseCaseThreads,
     ) = FocusMeteringControl(
-            cameraPropertiesMap[cameraId]!!, fakeUseCaseThreads
+            cameraPropertiesMap[cameraId]!!, useCaseThreads
         ).apply {
             fakeUseCaseCamera.runningUseCasesLiveData.value = useCases
             useCaseCamera = fakeUseCaseCamera
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 5c049d2..db443b2 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -692,7 +692,7 @@
          * use count to recover the additional increment here.
          */
         mCameraControlInternal.incrementUseCount();
-        notifyStateAttachedToUseCases(new ArrayList<>(useCases));
+        notifyStateAttachedAndCameraControlReady(new ArrayList<>(useCases));
         List<UseCaseInfo> useCaseInfos = new ArrayList<>(toUseCaseInfos(useCases));
         try {
             mExecutor.execute(() -> {
@@ -798,7 +798,7 @@
         return mCameraConfig;
     }
 
-    private void notifyStateAttachedToUseCases(List<UseCase> useCases) {
+    private void notifyStateAttachedAndCameraControlReady(List<UseCase> useCases) {
         for (UseCase useCase : useCases) {
             String useCaseId = getUseCaseId(useCase);
             if (mNotifyStateAttachedSet.contains(useCaseId)) {
@@ -807,6 +807,7 @@
 
             mNotifyStateAttachedSet.add(useCaseId);
             useCase.onStateAttached();
+            useCase.onCameraControlReady();
         }
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index 2e91175..d23ff83 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -616,7 +616,7 @@
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
-    protected void onCameraControlReady() {
+    public void onCameraControlReady() {
         trySetFlashModeToCameraControl();
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
index 613a40c..165bcba 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
@@ -564,7 +564,7 @@
      * @hide
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
-    protected void onCameraControlReady() {
+    public void onCameraControlReady() {
     }
 
     /**
@@ -695,15 +695,11 @@
      * session with the use case session config. The use case can receive the frame data from the
      * camera after the capture session is configured.
      *
-     * <p>The {@link CameraControlInternal} retrieved by {@link #getCameraControl()} is ready to
-     * use after this callback function is invoked.
-     *
      * @hide
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @CallSuper
     public void onStateAttached() {
-        onCameraControlReady();
     }
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index f97ec40..3d75d57 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -60,9 +60,11 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * A {@link CameraInternal} adapter which checks that the UseCases to make sure that the resolutions
@@ -85,10 +87,12 @@
 
     private final CameraId mId;
 
-    // This includes app provided use cases and the extra placeholder use cases (mExtraUseCases)
-    // created by CameraX.
+    // UseCases from the app. This does not include internal UseCases created by CameraX.
     @GuardedBy("mLock")
-    private final List<UseCase> mUseCases = new ArrayList<>();
+    private final Set<UseCase> mUseCases = new HashSet<>();
+    // UseCases sent to the camera including internal UseCases created by CameraX.
+    @GuardedBy("mLock")
+    private final Set<UseCase> mCameraUseCases = new HashSet<>();
 
     @GuardedBy("mLock")
     @Nullable
@@ -114,9 +118,11 @@
     @GuardedBy("mLock")
     private Config mInteropConfig = null;
 
-    // The extra placeholder use cases created by CameraX to make sure the camera can work normally.
+    // The placeholder UseCase created to meet combination criteria for Extensions. e.g. When
+    // Extensions require both Preview and ImageCapture and app only provides one of them,
+    // CameraX will create the other and track it with this variable.
     @GuardedBy("mLock")
-    private List<UseCase> mExtraUseCases = new ArrayList<>();
+    private UseCase mPlaceholderForExtensions;
 
     /**
      * Create a new {@link CameraUseCaseAdapter} instance.
@@ -186,124 +192,110 @@
      * @throws CameraException Thrown if the combination of newly added UseCases and the
      *                         currently added UseCases exceed the capability of the camera.
      */
-    public void addUseCases(@NonNull Collection<UseCase> useCases) throws CameraException {
+    public void addUseCases(@NonNull Collection<UseCase> appUseCasesToAdd) throws CameraException {
         synchronized (mLock) {
-            // TODO: merge UseCase for stream sharing. e.g. replace Preview and VideoCapture with a
-            //  StreamSharing UseCase.
-            List<UseCase> newUseCases = new ArrayList<>();
-            for (UseCase useCase : useCases) {
-                if (mUseCases.contains(useCase)) {
-                    Logger.d(TAG, "Attempting to attach already attached UseCase");
-                } else {
-                    newUseCases.add(useCase);
-                }
-            }
-
-            List<UseCase> allUseCases = new ArrayList<>(mUseCases);
-            List<UseCase> requiredExtraUseCases = emptyList();
-            List<UseCase> removedExtraUseCases = emptyList();
-
-            if (isCoexistingPreviewImageCaptureRequired()) {
-                // Collects all use cases that will be finally bound by the application
-                allUseCases.removeAll(mExtraUseCases);
-                allUseCases.addAll(newUseCases);
-
-                // Calculates the required extra use cases according to the use cases finally bound
-                // by the application and the existing extra use cases.
-                requiredExtraUseCases = calculateRequiredExtraUseCases(allUseCases,
-                        new ArrayList<>(mExtraUseCases));
-
-                // Calculates the new added extra use cases
-                List<UseCase> addedExtraUseCases = new ArrayList<>(requiredExtraUseCases);
-                addedExtraUseCases.removeAll(mExtraUseCases);
-
-                // Adds the new added extra use cases to the newUseCases list
-                newUseCases.addAll(addedExtraUseCases);
-
-                // Calculates the removed extra use cases
-                removedExtraUseCases = new ArrayList<>(mExtraUseCases);
-                removedExtraUseCases.removeAll(requiredExtraUseCases);
-            }
-
-            Map<UseCase, ConfigPair> configs = getConfigs(newUseCases,
-                    mCameraConfig.getUseCaseConfigFactory(), mUseCaseConfigFactory);
-
-            Map<UseCase, Size> suggestedResolutionsMap;
+            Set<UseCase> appUseCasesAfter = new HashSet<>(mUseCases);
+            appUseCasesAfter.addAll(appUseCasesToAdd);
             try {
-                // Removes the unnecessary extra use cases and then checks whether all uses cases
-                // including all the use cases finally bound by the application and the needed
-                // extra use cases can be supported by guaranteed supported configurations tables.
-                List<UseCase> boundUseCases = new ArrayList<>(mUseCases);
-                boundUseCases.removeAll(removedExtraUseCases);
-                suggestedResolutionsMap =
-                        calculateSuggestedResolutions(mCameraInternal.getCameraInfoInternal(),
-                                newUseCases, boundUseCases, configs);
+                updateUseCases(appUseCasesAfter);
             } catch (IllegalArgumentException e) {
                 throw new CameraException(e.getMessage());
             }
-            updateViewPort(suggestedResolutionsMap, useCases);
-            updateEffects(mEffects, useCases);
 
-            // Saves the updated extra use cases set after confirming the use case combination
-            // can be supported.
-            mExtraUseCases = requiredExtraUseCases;
-
-            // Detaches the unnecessary existing extra use cases
-            detachUnnecessaryUseCases(removedExtraUseCases);
-
-            // At this point the binding will succeed since all the calculations are done
-            // Do all attaching related work
-            for (UseCase useCase : newUseCases) {
-                ConfigPair configPair = configs.get(useCase);
-                useCase.bindToCamera(mCameraInternal, configPair.mExtendedConfig,
-                        configPair.mCameraConfig);
-                useCase.updateSuggestedResolution(
-                        Preconditions.checkNotNull(suggestedResolutionsMap.get(useCase)));
-            }
-
-            // The added use cases will include the app provided use cases and the new added extra
-            // use cases.
-            mUseCases.addAll(newUseCases);
-            if (mAttached) {
-                mCameraInternal.attachUseCases(newUseCases);
-            }
-
-            // Once all use cases are attached, they need to notify the CameraInternal of its state
-            for (UseCase useCase : newUseCases) {
-                useCase.notifyState();
-            }
         }
     }
 
     /**
      * Remove the specified collection of {@link UseCase} from the adapter.
      */
-    public void removeUseCases(@NonNull Collection<UseCase> useCases) {
+    public void removeUseCases(@NonNull Collection<UseCase> useCasesToRemove) {
         synchronized (mLock) {
-            detachUnnecessaryUseCases(new ArrayList<>(useCases));
-
-            // Calls addUseCases() function to calculate and add extra use cases if coexisting
-            // Preview and ImageCapture are required.
-            if (isCoexistingPreviewImageCaptureRequired()) {
-                // The useCases might include extra use cases when unbinding all use cases.
-                // Removes the unbound extra use cases from mExtraUseCases.
-                mExtraUseCases.removeAll(useCases);
-
-                try {
-                    // Calls addUseCases with empty list to add required extra fake use case.
-                    addUseCases(emptyList());
-                } catch (CameraException e) {
-                    // This should not happen because the extra fake use case should be only
-                    // added to replace the removed one which the use case combination can be
-                    // supported.
-                    throw new IllegalArgumentException("Failed to add extra fake Preview or "
-                            + "ImageCapture use case!");
-                }
-            }
+            Set<UseCase> appUseCasesAfter = new HashSet<>(mUseCases);
+            appUseCasesAfter.removeAll(useCasesToRemove);
+            updateUseCases(appUseCasesAfter);
         }
     }
 
     /**
+     * Updates the states based the new app UseCases.
+     *
+     * <p> This method calculates the new camera UseCases based on the input and the current state,
+     * attach/detach the camera UseCases, and save the updated state in following member variables:
+     * {@link #mCameraUseCases}, {@link #mUseCases} and {@link #mPlaceholderForExtensions}.
+     *
+     * @throws IllegalArgumentException if the UseCase combination is not supported. In that case,
+     *                                  it will not update the internal states.
+     */
+    void updateUseCases(@NonNull Set<UseCase> appUseCases) {
+        synchronized (mLock) {
+            // Calculate camera UseCases and keep the result in local variables in case they don't
+            // meet the stream combination rules.
+            UseCase placeholderForExtensions = calculatePlaceholderForExtensions(appUseCases);
+            Set<UseCase> cameraUseCases =
+                    calculateCameraUseCases(appUseCases, placeholderForExtensions);
+
+            // Calculate the action items.
+            Set<UseCase> cameraUseCasesToAttach = new HashSet<>(cameraUseCases);
+            cameraUseCasesToAttach.removeAll(mCameraUseCases);
+            Set<UseCase> cameraUseCasesToKeep = new HashSet<>(cameraUseCases);
+            cameraUseCasesToKeep.retainAll(mCameraUseCases);
+            Set<UseCase> cameraUseCasesToDetach = new HashSet<>(mCameraUseCases);
+            cameraUseCasesToDetach.removeAll(cameraUseCases);
+
+            // Calculate suggested resolutions. This step throws exception if the camera UseCases
+            // fails the supported stream combination rules.
+            Map<UseCase, ConfigPair> configs = getConfigs(cameraUseCasesToAttach,
+                    mCameraConfig.getUseCaseConfigFactory(), mUseCaseConfigFactory);
+            Map<UseCase, Size> suggestedResolutionsMap = calculateSuggestedResolutions(
+                    mCameraInternal.getCameraInfoInternal(), cameraUseCasesToAttach,
+                    cameraUseCasesToKeep, configs);
+
+            // Update properties.
+            updateViewPort(suggestedResolutionsMap, cameraUseCases);
+            updateEffects(mEffects, appUseCases);
+
+            // Detach unused UseCases.
+            for (UseCase useCase : cameraUseCasesToDetach) {
+                useCase.unbindFromCamera(mCameraInternal);
+            }
+            mCameraInternal.detachUseCases(cameraUseCasesToDetach);
+
+            // Attach new UseCases.
+            for (UseCase useCase : cameraUseCasesToAttach) {
+                ConfigPair configPair = requireNonNull(configs.get(useCase));
+                useCase.bindToCamera(mCameraInternal, configPair.mExtendedConfig,
+                        configPair.mCameraConfig);
+                useCase.updateSuggestedResolution(
+                        Preconditions.checkNotNull(suggestedResolutionsMap.get(useCase)));
+            }
+            if (mAttached) {
+                mCameraInternal.attachUseCases(cameraUseCasesToAttach);
+            }
+
+            // Once UseCases are detached/attached, notify the camera.
+            for (UseCase useCase : cameraUseCasesToAttach) {
+                useCase.notifyState();
+            }
+
+            // The changes are successful. Update the states of this class.
+            mUseCases.clear();
+            mUseCases.addAll(appUseCases);
+            mCameraUseCases.clear();
+            mCameraUseCases.addAll(cameraUseCases);
+            mPlaceholderForExtensions = placeholderForExtensions;
+        }
+    }
+
+    static Set<UseCase> calculateCameraUseCases(@NonNull Set<UseCase> appUseCases,
+            @Nullable UseCase placeholderForExtensions) {
+        Set<UseCase> useCases = new HashSet<>(appUseCases);
+        if (placeholderForExtensions != null) {
+            useCases.add(placeholderForExtensions);
+        }
+        return useCases;
+    }
+
+    /**
      * Returns the UseCases currently associated with the adapter.
      *
      * <p> The UseCases may or may not be actually attached to the underlying
@@ -316,6 +308,14 @@
         }
     }
 
+    @VisibleForTesting
+    @NonNull
+    Set<UseCase> getCameraUseCases() {
+        synchronized (mLock) {
+            return new HashSet<>(mCameraUseCases);
+        }
+    }
+
     /**
      * Attach the UseCases to the {@link CameraInternal} camera so that the UseCases can receive
      * data if they are active.
@@ -395,8 +395,8 @@
 
     private Map<UseCase, Size> calculateSuggestedResolutions(
             @NonNull CameraInfoInternal cameraInfoInternal,
-            @NonNull List<UseCase> newUseCases,
-            @NonNull List<UseCase> currentUseCases,
+            @NonNull Collection<UseCase> newUseCases,
+            @NonNull Collection<UseCase> currentUseCases,
             @NonNull Map<UseCase, ConfigPair> configPairMap) {
         List<AttachedSurfaceInfo> existingSurfaces = new ArrayList<>();
         String cameraId = cameraInfoInternal.getCameraId();
@@ -533,7 +533,7 @@
     }
 
     // Get a map of the configs for the use cases from the respective factories
-    private Map<UseCase, ConfigPair> getConfigs(List<UseCase> useCases,
+    private Map<UseCase, ConfigPair> getConfigs(Collection<UseCase> useCases,
             UseCaseConfigFactory extendedFactory, UseCaseConfigFactory cameraFactory) {
         Map<UseCase, ConfigPair> configs = new HashMap<>();
         for (UseCase useCase : useCases) {
@@ -658,64 +658,30 @@
     }
 
     /**
-     * Calculates the new required extra use cases according to the use cases bound by the
-     * application and the existing extra use cases.
+     * Calculate the internal created placeholder UseCase for Extensions.
      *
-     * @param boundUseCases The use cases bound by the application.
-     * @param extraUseCases The originally existing extra use cases.
-     * @return new required extra use cases
+     * @param appUseCases UseCase provided by the app.
      */
-    @NonNull
-    private List<UseCase> calculateRequiredExtraUseCases(@NonNull List<UseCase> boundUseCases,
-            @NonNull List<UseCase> extraUseCases) {
-        List<UseCase> requiredExtraUseCases = new ArrayList<>(extraUseCases);
-        boolean isExtraPreviewRequired = isExtraPreviewRequired(boundUseCases);
-        boolean isExtraImageCaptureRequired = isExtraImageCaptureRequired(
-                boundUseCases);
-        UseCase existingExtraPreview = null;
-        UseCase existingExtraImageCapture = null;
-
-        for (UseCase useCase : extraUseCases) {
-            if (isPreview(useCase)) {
-                existingExtraPreview = useCase;
-            } else if (isImageCapture(useCase)) {
-                existingExtraImageCapture = useCase;
-            }
-        }
-
-        if (isExtraPreviewRequired && existingExtraPreview == null) {
-            requiredExtraUseCases.add(createExtraPreview());
-        } else if (!isExtraPreviewRequired && existingExtraPreview != null) {
-            requiredExtraUseCases.remove(existingExtraPreview);
-        }
-
-        if (isExtraImageCaptureRequired && existingExtraImageCapture == null) {
-            requiredExtraUseCases.add(createExtraImageCapture());
-        } else if (!isExtraImageCaptureRequired && existingExtraImageCapture != null) {
-            requiredExtraUseCases.remove(existingExtraImageCapture);
-        }
-
-        return requiredExtraUseCases;
-    }
-
-    /**
-     * Detaches unnecessary use cases from camera.
-     */
-    private void detachUnnecessaryUseCases(@NonNull List<UseCase> unnecessaryUseCases) {
+    @Nullable
+    UseCase calculatePlaceholderForExtensions(@NonNull Set<UseCase> appUseCases) {
         synchronized (mLock) {
-            if (!unnecessaryUseCases.isEmpty()) {
-                mCameraInternal.detachUseCases(unnecessaryUseCases);
-
-                for (UseCase useCase : unnecessaryUseCases) {
-                    if (mUseCases.contains(useCase)) {
-                        useCase.unbindFromCamera(mCameraInternal);
+            UseCase placeholder = null;
+            if (isCoexistingPreviewImageCaptureRequired()) {
+                if (isExtraPreviewRequired(appUseCases)) {
+                    if (isPreview(mPlaceholderForExtensions)) {
+                        placeholder = mPlaceholderForExtensions;
                     } else {
-                        Logger.e(TAG, "Attempting to detach non-attached UseCase: " + useCase);
+                        placeholder = createExtraPreview();
+                    }
+                } else if (isExtraImageCaptureRequired(appUseCases)) {
+                    if (isImageCapture(mPlaceholderForExtensions)) {
+                        placeholder = mPlaceholderForExtensions;
+                    } else {
+                        placeholder = createExtraImageCapture();
                     }
                 }
-
-                mUseCases.removeAll(unnecessaryUseCases);
             }
+            return placeholder;
         }
     }
 
@@ -730,7 +696,7 @@
      * Returns true if the input use case list contains a {@link ImageCapture} but does not
      * contain an {@link Preview}.
      */
-    private boolean isExtraPreviewRequired(@NonNull List<UseCase> useCases) {
+    private boolean isExtraPreviewRequired(@NonNull Collection<UseCase> useCases) {
         boolean hasPreview = false;
         boolean hasImageCapture = false;
 
@@ -749,7 +715,7 @@
      * Returns true if the input use case list contains a {@link Preview} but does not contain an
      * {@link ImageCapture}.
      */
-    private boolean isExtraImageCaptureRequired(@NonNull List<UseCase> useCases) {
+    private boolean isExtraImageCaptureRequired(@NonNull Collection<UseCase> useCases) {
         boolean hasPreview = false;
         boolean hasImageCapture = false;
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
new file mode 100644
index 0000000..07c714b
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.streamsharing;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.ImageCapture;
+import androidx.camera.core.UseCase;
+import androidx.camera.core.impl.CameraInternal;
+import androidx.camera.core.impl.Config;
+import androidx.camera.core.impl.MutableOptionsBundle;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.UseCaseConfigFactory;
+
+import java.util.Set;
+
+/**
+ * A {@link UseCase} that shares one PRIV stream to multiple children {@link UseCase}s.
+ */
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+public class StreamSharing extends UseCase {
+
+    @SuppressWarnings("UnusedVariable")
+    private final VirtualCamera mVirtualCamera;
+
+    private static final StreamSharingConfig DEFAULT_CONFIG =
+            new StreamSharingBuilder().getUseCaseConfig();
+
+    /**
+     * Constructs a {@link StreamSharing} with a parent {@link CameraInternal}, children
+     * {@link UseCase}s, and a {@link UseCaseConfigFactory} for getting default {@link UseCase}
+     * configurations.
+     */
+    public StreamSharing(@NonNull CameraInternal parentCamera,
+            @NonNull Set<UseCase> children,
+            @NonNull UseCaseConfigFactory useCaseConfigFactory) {
+        this(new VirtualCamera(parentCamera, children, useCaseConfigFactory));
+    }
+
+    StreamSharing(@NonNull VirtualCamera virtualCamera) {
+        super(DEFAULT_CONFIG);
+        mVirtualCamera = virtualCamera;
+    }
+
+    @Nullable
+    @Override
+    public UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
+            @NonNull UseCaseConfigFactory factory) {
+        // The shared stream optimizes for VideoCapture.
+        Config captureConfig = factory.getConfig(
+                UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE,
+                ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
+
+        if (applyDefaultConfig) {
+            captureConfig = Config.mergeConfigs(captureConfig, DEFAULT_CONFIG.getConfig());
+        }
+        return captureConfig == null ? null :
+                getUseCaseConfigBuilder(captureConfig).getUseCaseConfig();
+    }
+
+    @NonNull
+    @Override
+    public UseCaseConfig.Builder<?, ?, ?> getUseCaseConfigBuilder(@NonNull Config config) {
+        return new StreamSharingBuilder(MutableOptionsBundle.from(config));
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java
new file mode 100644
index 0000000..0ae8daa
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.streamsharing;
+
+import static androidx.camera.core.internal.TargetConfig.OPTION_TARGET_CLASS;
+import static androidx.camera.core.internal.TargetConfig.OPTION_TARGET_NAME;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.UseCase;
+import androidx.camera.core.impl.CaptureConfig;
+import androidx.camera.core.impl.MutableConfig;
+import androidx.camera.core.impl.MutableOptionsBundle;
+import androidx.camera.core.impl.OptionsBundle;
+import androidx.camera.core.impl.SessionConfig;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.internal.TargetConfig;
+
+import java.util.UUID;
+
+/**
+ * A empty builder for {@link StreamSharing}.
+ *
+ * <p> {@link StreamSharing} does not need a public builder. Instead, the caller should call the
+ * constructor directly. This class exists because the {@link UseCase#getUseCaseConfigBuilder}
+ * method requires a builder for each {@link UseCase}.
+ */
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+class StreamSharingBuilder implements
+        UseCaseConfig.Builder<StreamSharing, StreamSharingConfig, StreamSharingBuilder> {
+
+    private static final String UNSUPPORTED_MESSAGE =
+            "Operation not supported by StreamSharingBuilder.";
+
+    private final MutableOptionsBundle mMutableConfig;
+
+    StreamSharingBuilder() {
+        this(MutableOptionsBundle.create());
+    }
+
+    StreamSharingBuilder(@NonNull MutableOptionsBundle mutableConfig) {
+        mMutableConfig = mutableConfig;
+        Class<?> oldConfigClass =
+                mutableConfig.retrieveOption(TargetConfig.OPTION_TARGET_CLASS, null);
+        if (oldConfigClass != null && !oldConfigClass.equals(StreamSharing.class)) {
+            throw new IllegalArgumentException(
+                    "Invalid target class configuration for "
+                            + StreamSharingBuilder.this
+                            + ": "
+                            + oldConfigClass);
+        }
+        setTargetClass(StreamSharing.class);
+    }
+
+    @NonNull
+    @Override
+    public MutableConfig getMutableConfig() {
+        return mMutableConfig;
+    }
+
+    @NonNull
+    @Override
+    public StreamSharing build() {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setDefaultSessionConfig(@NonNull SessionConfig sessionConfig) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setDefaultCaptureConfig(@NonNull CaptureConfig captureConfig) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setSessionOptionUnpacker(
+            @NonNull SessionConfig.OptionUnpacker optionUnpacker) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setCaptureOptionUnpacker(
+            @NonNull CaptureConfig.OptionUnpacker optionUnpacker) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setSurfaceOccupancyPriority(int priority) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setCameraSelector(@NonNull CameraSelector cameraSelector) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setZslDisabled(boolean disabled) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setHighResolutionDisabled(boolean disabled) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingConfig getUseCaseConfig() {
+        return new StreamSharingConfig(OptionsBundle.from(mMutableConfig));
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setTargetClass(@NonNull Class<StreamSharing> targetClass) {
+        getMutableConfig().insertOption(OPTION_TARGET_CLASS, targetClass);
+        // If no name is set yet, then generate a unique name
+        if (null == getMutableConfig().retrieveOption(OPTION_TARGET_NAME, null)) {
+            String targetName = targetClass.getCanonicalName() + "-" + UUID.randomUUID();
+            setTargetName(targetName);
+        }
+        return this;
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setTargetName(@NonNull String targetName) {
+        getMutableConfig().insertOption(OPTION_TARGET_NAME, targetName);
+        return this;
+    }
+
+    @NonNull
+    @Override
+    public StreamSharingBuilder setUseCaseEventCallback(
+            @NonNull UseCase.EventCallback eventCallback) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingConfig.java
new file mode 100644
index 0000000..adf1207
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharingConfig.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.streamsharing;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.Config;
+import androidx.camera.core.impl.ImageOutputConfig;
+import androidx.camera.core.impl.MutableConfig;
+import androidx.camera.core.impl.OptionsBundle;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.internal.ThreadConfig;
+
+/**
+ * Configuration for a {@link StreamSharing} use case.
+ *
+ * <p> This class is a thin wrapper of the underlying {@link OptionsBundle}. Instead of adding
+ * getters and setters to this class, one should modify the config using
+ * {@link MutableConfig#insertOption} and {@link MutableConfig#retrieveOption} directly.
+ */
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+class StreamSharingConfig implements UseCaseConfig<StreamSharing>,
+        ImageOutputConfig,
+        ThreadConfig {
+
+    private final OptionsBundle mConfig;
+
+    /** Creates a new configuration instance. */
+    StreamSharingConfig(@NonNull OptionsBundle config) {
+        mConfig = config;
+    }
+
+    @NonNull
+    @Override
+    public Config getConfig() {
+        return mConfig;
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCamera.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCamera.java
new file mode 100644
index 0000000..adf2588
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCamera.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.streamsharing;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.UseCase;
+import androidx.camera.core.impl.CameraControlInternal;
+import androidx.camera.core.impl.CameraInfoInternal;
+import androidx.camera.core.impl.CameraInternal;
+import androidx.camera.core.impl.Observable;
+import androidx.camera.core.impl.UseCaseConfigFactory;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * A virtual implementation of {@link CameraInternal}.
+ *
+ * <p> This class manages children {@link UseCase} and connects/disconnects them to the
+ * parent {@link StreamSharing}. It also forwards parent camera properties/events to the children.
+ */
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+class VirtualCamera implements CameraInternal {
+
+    private static final String UNSUPPORTED_MESSAGE = "Operation not supported by VirtualCamera.";
+
+    @SuppressWarnings("UnusedVariable")
+    @NonNull
+    private final Set<UseCase> mChildren;
+    @SuppressWarnings("UnusedVariable")
+    @NonNull
+    private final UseCaseConfigFactory mUseCaseConfigFactory;
+    @NonNull
+    private final CameraInternal mParentCamera;
+
+    /**
+     * @param parentCamera         the parent {@link CameraInternal} instance. For example, the
+     *                             real camera.
+     * @param children             the children {@link UseCase}.
+     * @param useCaseConfigFactory the factory for configuring children {@link UseCase}.
+     */
+    VirtualCamera(@NonNull CameraInternal parentCamera,
+            @NonNull Set<UseCase> children,
+            @NonNull UseCaseConfigFactory useCaseConfigFactory) {
+        mParentCamera = parentCamera;
+        mUseCaseConfigFactory = useCaseConfigFactory;
+        mChildren = children;
+    }
+
+    // --- API for StreamSharing ---
+
+    // TODO(b/264936250): Add methods for interacting with the StreamSharing UseCase.
+
+    // --- Handle children state change ---
+
+    // TODO(b/264936250): Handle children state changes.
+
+    @Override
+    public void onUseCaseActive(@NonNull UseCase useCase) {
+
+    }
+
+    @Override
+    public void onUseCaseInactive(@NonNull UseCase useCase) {
+
+    }
+
+    @Override
+    public void onUseCaseUpdated(@NonNull UseCase useCase) {
+
+    }
+
+    @Override
+    public void onUseCaseReset(@NonNull UseCase useCase) {
+
+    }
+
+    // --- Forward parent camera properties and events ---
+
+    @NonNull
+    @Override
+    public CameraControlInternal getCameraControlInternal() {
+        return mParentCamera.getCameraControlInternal();
+    }
+
+    @NonNull
+    @Override
+    public CameraInfoInternal getCameraInfoInternal() {
+        return mParentCamera.getCameraInfoInternal();
+    }
+
+    @NonNull
+    @Override
+    public Observable<State> getCameraState() {
+        return mParentCamera.getCameraState();
+    }
+
+    // --- Unused overrides ---
+
+    @Override
+    public void open() {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @Override
+    public void close() {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @NonNull
+    @Override
+    public ListenableFuture<Void> release() {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @Override
+    public void attachUseCases(@NonNull Collection<UseCase> useCases) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+
+    @Override
+    public void detachUseCases(@NonNull Collection<UseCase> useCases) {
+        throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/package-info.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/package-info.java
new file mode 100644
index 0000000..fd92612
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+package androidx.camera.core.streamsharing;
+
+import androidx.annotation.RestrictTo;
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt
index f34f0c1..216b5d3 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt
@@ -445,7 +445,7 @@
         cameraUseCaseAdapter.addUseCases(listOf(preview))
 
         // Checks whether an extra ImageCapture is added.
-        assertThat(containsImageCapture(cameraUseCaseAdapter.useCases)).isTrue()
+        assertThat(containsImageCapture(cameraUseCaseAdapter.cameraUseCases)).isTrue()
     }
 
     @Test
@@ -462,7 +462,7 @@
         cameraUseCaseAdapter.addUseCases(listOf(preview))
 
         // Checks whether an extra ImageCapture is added.
-        assertThat(containsImageCapture(cameraUseCaseAdapter.useCases))
+        assertThat(containsImageCapture(cameraUseCaseAdapter.cameraUseCases))
         val imageCapture = ImageCapture.Builder().build()
 
         // Adds an ImageCapture
@@ -496,7 +496,7 @@
         cameraUseCaseAdapter.removeUseCases(listOf(imageCapture))
 
         // Checks whether an extra ImageCapture is added.
-        assertThat(containsImageCapture(cameraUseCaseAdapter.useCases)).isTrue()
+        assertThat(containsImageCapture(cameraUseCaseAdapter.cameraUseCases)).isTrue()
     }
 
     @Test
@@ -513,7 +513,7 @@
         cameraUseCaseAdapter.addUseCases(listOf(imageCapture))
 
         // Checks whether an extra Preview is added.
-        assertThat(containsPreview(cameraUseCaseAdapter.useCases)).isTrue()
+        assertThat(containsPreview(cameraUseCaseAdapter.cameraUseCases)).isTrue()
     }
 
     @Test
@@ -530,7 +530,7 @@
         cameraUseCaseAdapter.addUseCases(listOf(imageCapture))
 
         // Checks whether an extra Preview is added.
-        assertThat(containsPreview(cameraUseCaseAdapter.useCases))
+        assertThat(containsPreview(cameraUseCaseAdapter.cameraUseCases))
         val preview = Preview.Builder().build()
 
         // Adds an Preview
@@ -563,7 +563,7 @@
         cameraUseCaseAdapter.removeUseCases(listOf(preview))
 
         // Checks whether an extra Preview is added.
-        assertThat(containsPreview(cameraUseCaseAdapter.useCases)).isTrue()
+        assertThat(containsPreview(cameraUseCaseAdapter.cameraUseCases)).isTrue()
     }
 
     @Test
@@ -671,7 +671,7 @@
         }
     }
 
-    private fun containsPreview(useCases: List<UseCase>): Boolean {
+    private fun containsPreview(useCases: Collection<UseCase>): Boolean {
         for (useCase in useCases) {
             if (useCase is Preview) {
                 return true
@@ -680,7 +680,7 @@
         return false
     }
 
-    private fun containsImageCapture(useCases: List<UseCase>): Boolean {
+    private fun containsImageCapture(useCases: Collection<UseCase>): Boolean {
         for (useCase in useCases) {
             if (useCase is ImageCapture) {
                 return true
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt
new file mode 100644
index 0000000..3f61631
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/StreamSharingTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.streamsharing
+
+import android.os.Build
+import androidx.camera.core.Preview
+import androidx.camera.core.impl.UseCaseConfigFactory
+import androidx.camera.core.internal.TargetConfig.OPTION_TARGET_CLASS
+import androidx.camera.core.internal.TargetConfig.OPTION_TARGET_NAME
+import androidx.camera.testing.fakes.FakeCamera
+import androidx.camera.testing.fakes.FakeUseCase
+import androidx.camera.testing.fakes.FakeUseCaseConfigFactory
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+/**
+ * Unit tests for [StreamSharing].
+ */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class StreamSharingTest {
+
+    private val parentCamera = FakeCamera()
+    private val preview = Preview.Builder().build()
+    private val video = FakeUseCase()
+    private val useCaseConfigFactory = FakeUseCaseConfigFactory()
+    private lateinit var streamSharing: StreamSharing
+
+    @Before
+    fun setUp() {
+        streamSharing = StreamSharing(parentCamera, setOf(preview, video), useCaseConfigFactory)
+    }
+
+    @Test
+    fun getDefaultConfig_usesVideoCaptureType() {
+        val config = streamSharing.getDefaultConfig(true, useCaseConfigFactory)!!
+
+        assertThat(useCaseConfigFactory.lastRequestedCaptureType)
+            .isEqualTo(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE)
+        assertThat(
+            config.retrieveOption(
+                OPTION_TARGET_CLASS,
+                null
+            )
+        ).isEqualTo(StreamSharing::class.java)
+        assertThat(
+            config.retrieveOption(
+                OPTION_TARGET_NAME,
+                null
+            )
+        ).startsWith("androidx.camera.core.streamsharing.StreamSharing-")
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
new file mode 100644
index 0000000..901a70a
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.streamsharing
+
+import android.os.Build
+import androidx.camera.core.Preview
+import androidx.camera.testing.fakes.FakeCamera
+import androidx.camera.testing.fakes.FakeUseCase
+import androidx.camera.testing.fakes.FakeUseCaseConfigFactory
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+/**
+ * Unit tests for [VirtualCamera].
+ */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class VirtualCameraTest {
+
+    private val parentCamera = FakeCamera()
+    private val preview = Preview.Builder().build()
+    private val video = FakeUseCase()
+    private val useCaseConfigFactory = FakeUseCaseConfigFactory()
+    private lateinit var virtualCamera: VirtualCamera
+
+    @Before
+    fun setUp() {
+        virtualCamera = VirtualCamera(parentCamera, setOf(preview, video), useCaseConfigFactory)
+    }
+
+    @Test
+    fun virtualCameraInheritsParentProperties() {
+        assertThat(virtualCamera.cameraState).isEqualTo(parentCamera.cameraState)
+        assertThat(virtualCamera.cameraInfo).isEqualTo(parentCamera.cameraInfo)
+        assertThat(virtualCamera.cameraControl).isEqualTo(parentCamera.cameraControl)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfigFactory.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfigFactory.java
index c6d68fe9..8cae469 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfigFactory.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfigFactory.java
@@ -36,6 +36,10 @@
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public final class FakeUseCaseConfigFactory implements UseCaseConfigFactory {
+
+    @NonNull
+    private CaptureType mLastRequestedCaptureType;
+
     /**
      * Returns the configuration for the given capture type, or <code>null</code> if the
      * configuration cannot be produced.
@@ -45,6 +49,7 @@
     public Config getConfig(
             @NonNull CaptureType captureType,
             @CaptureMode int captureMode) {
+        mLastRequestedCaptureType = captureType;
         MutableOptionsBundle mutableConfig = MutableOptionsBundle.create();
 
         mutableConfig.insertOption(OPTION_CAPTURE_CONFIG_UNPACKER, (config, builder) -> {});
@@ -52,4 +57,9 @@
 
         return OptionsBundle.from(mutableConfig);
     }
+
+    @NonNull
+    public CaptureType getLastRequestedCaptureType() {
+        return mLastRequestedCaptureType;
+    }
 }
diff --git a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt
index 2acc74e..a4daab7 100644
--- a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt
+++ b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt
@@ -21,7 +21,9 @@
 import androidx.camera.integration.avsync.ui.widget.AdvancedFloatingActionButton
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
@@ -89,21 +91,23 @@
 ) {
     Box(modifier = Modifier.fillMaxSize()) {
         LightingScreen(isOn = isSignalActive)
-        SignalControl(
-            enabled = isGeneratorReady,
-            isStarted = isSignalStarted,
-            onStartClick = onSignalStartClick,
-            onStopClick = onSignalStopClick,
-        )
-        RecordingControl(
-            enabled = isRecorderReady,
-            isStarted = isRecording,
-            isPaused = isPaused,
-            onStartClick = onRecordingStartClick,
-            onStopClick = onRecordingStopClick,
-            onPauseClick = onRecordingPauseClick,
-            onResumeClick = onRecordingResumeClick,
-        )
+        ControlPanel {
+            SignalControl(
+                enabled = isGeneratorReady,
+                isStarted = isSignalStarted,
+                onStartClick = onSignalStartClick,
+                onStopClick = onSignalStopClick,
+            )
+            RecordingControl(
+                enabled = isRecorderReady,
+                isStarted = isRecording,
+                isPaused = isPaused,
+                onStartClick = onRecordingStartClick,
+                onStopClick = onRecordingStopClick,
+                onPauseClick = onRecordingPauseClick,
+                onResumeClick = onRecordingResumeClick,
+            )
+        }
     }
 }
 
@@ -116,6 +120,19 @@
 }
 
 @Composable
+private fun ControlPanel(
+    modifier: Modifier = Modifier,
+    content: @Composable () -> Unit
+) {
+    Column(modifier = modifier.fillMaxSize()) {
+        Spacer(modifier = Modifier.weight(2f))
+        Box(modifier = Modifier.weight(1f)) {
+            content()
+        }
+    }
+}
+
+@Composable
 private fun SignalControl(
     modifier: Modifier = Modifier,
     enabled: Boolean,
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
index b776820..d6f99b5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
@@ -35,7 +35,7 @@
     <string name="unchecked_action_title" msgid="802503415474307811">"Белгиси алынды"</string>
     <string name="on_action_title" msgid="4129601573763429611">"Күйүк"</string>
     <string name="off_action_title" msgid="8669201170189204848">"Өчүк"</string>
-    <string name="settings_action_title" msgid="8616900063253887861">"Жөндөөлөр"</string>
+    <string name="settings_action_title" msgid="8616900063253887861">"Параметрлер"</string>
     <string name="accept_action_title" msgid="4899660585470647578">"Кабыл алуу"</string>
     <string name="reject_action_title" msgid="6730366705938402668">"Четке кагуу"</string>
     <string name="ok_action_title" msgid="7128494973966098611">"Макул"</string>
@@ -63,7 +63,7 @@
     <string name="third_item_checked_toast_msg" msgid="3022450599567347361">"Үчүнчү нерсе текшерилди"</string>
     <string name="fifth_item_checked_toast_msg" msgid="1627599668504718594">"Бешинчи элемент текшерилди"</string>
     <string name="sixth_item_toast_msg" msgid="6117028866385793707">"Алтынчы нерсе чыкылдатылды"</string>
-    <string name="settings_toast_msg" msgid="7697794473002342727">"Жөндөөлөр чыкылдатылды"</string>
+    <string name="settings_toast_msg" msgid="7697794473002342727">"Параметрлер чыкылдатылды"</string>
     <string name="parked_toast_msg" msgid="2532422265890824446">"Токтотулган аракети"</string>
     <string name="more_toast_msg" msgid="5938288138225509885">"Дагы чыкылдатылган"</string>
     <string name="commute_toast_msg" msgid="4112684360647638688">"Өтүү баскычы басылды"</string>
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index c01d3ec..272d94d 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -185,10 +185,8 @@
   }
 
   @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface OverscrollEffect {
-    method public suspend Object? consumePostFling(long velocity, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    method public void consumePostScroll(long initialDragDelta, long overscrollDelta, int source);
-    method public suspend Object? consumePreFling(long velocity, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity>);
-    method public long consumePreScroll(long scrollDelta, int source);
+    method public suspend Object? applyToFling(long velocity, kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Velocity,? super kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity>,?> performFling, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public long applyToScroll(long delta, int source, kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,androidx.compose.ui.geometry.Offset> performScroll);
     method public androidx.compose.ui.Modifier getEffectModifier();
     method public boolean isInProgress();
     property public abstract androidx.compose.ui.Modifier effectModifier;
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/OverscrollSample.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/OverscrollSample.kt
index 072596d..696fa74 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/OverscrollSample.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/OverscrollSample.kt
@@ -31,11 +31,9 @@
 import androidx.compose.foundation.overscroll
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
@@ -61,37 +59,35 @@
     // on the scrollable container.
     class OffsetOverscrollEffect(val scope: CoroutineScope) : OverscrollEffect {
         private val overscrollOffset = Animatable(0f)
-        override fun consumePreScroll(
-            scrollDelta: Offset,
-            source: NestedScrollSource
+
+        override fun applyToScroll(
+            delta: Offset,
+            source: NestedScrollSource,
+            performScroll: (Offset) -> Offset
         ): Offset {
             // in pre scroll we relax the overscroll if needed
             // relaxation: when we are in progress of the overscroll and user scrolls in the
             // different direction = substract the overscroll first
-            val sameDirection = sign(scrollDelta.y) == sign(overscrollOffset.value)
-            return if (abs(overscrollOffset.value) > 0.5 && !sameDirection) {
+            val sameDirection = sign(delta.y) == sign(overscrollOffset.value)
+            val consumedByPreScroll = if (abs(overscrollOffset.value) > 0.5 && !sameDirection) {
                 val prevOverscrollValue = overscrollOffset.value
-                val newOverscrollValue = overscrollOffset.value + scrollDelta.y
+                val newOverscrollValue = overscrollOffset.value + delta.y
                 if (sign(prevOverscrollValue) != sign(newOverscrollValue)) {
                     // sign changed, coerce to start scrolling and exit
                     scope.launch { overscrollOffset.snapTo(0f) }
-                    Offset(x = 0f, y = scrollDelta.y + prevOverscrollValue)
+                    Offset(x = 0f, y = delta.y + prevOverscrollValue)
                 } else {
                     scope.launch {
-                        overscrollOffset.snapTo(overscrollOffset.value + scrollDelta.y)
+                        overscrollOffset.snapTo(overscrollOffset.value + delta.y)
                     }
-                    scrollDelta.copy(x = 0f)
+                    delta.copy(x = 0f)
                 }
             } else {
                 Offset.Zero
             }
-        }
-
-        override fun consumePostScroll(
-            initialDragDelta: Offset,
-            overscrollDelta: Offset,
-            source: NestedScrollSource
-        ) {
+            val leftForScroll = delta - consumedByPreScroll
+            val consumedByScroll = performScroll(leftForScroll)
+            val overscrollDelta = leftForScroll - consumedByScroll
             // if it is a drag, not a fling, add the delta left to our over scroll value
             if (abs(overscrollDelta.y) > 0.5 && source == NestedScrollSource.Drag) {
                 scope.launch {
@@ -99,21 +95,25 @@
                     overscrollOffset.snapTo(overscrollOffset.value + overscrollDelta.y * 0.1f)
                 }
             }
+            return consumedByPreScroll + consumedByScroll
         }
 
-        override suspend fun consumePreFling(velocity: Velocity): Velocity = Velocity.Zero
-
-        override suspend fun consumePostFling(velocity: Velocity) {
+        override suspend fun applyToFling(
+            velocity: Velocity,
+            performFling: suspend (Velocity) -> Velocity
+        ) {
+            val consumed = performFling(velocity)
             // when the fling happens - we just gradually animate our overscroll to 0
+            val remaining = velocity - consumed
             overscrollOffset.animateTo(
                 targetValue = 0f,
-                initialVelocity = velocity.y,
+                initialVelocity = remaining.y,
                 animationSpec = spring()
             )
         }
 
         override val isInProgress: Boolean
-            get() = overscrollOffset.isRunning
+            get() = overscrollOffset.value != 0f
 
         // as we're building an offset modifiers, let's offset of our value we calculated
         override val effectModifier: Modifier = Modifier.offset {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt
index c205a4a..9b004e2 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt
@@ -366,11 +366,10 @@
 
         rule.runOnIdle {
             val offset = Offset(0f, 5f)
-            controller.consumePostScroll(
-                initialDragDelta = offset,
-                overscrollDelta = offset,
+            controller.applyToScroll(
+                offset,
                 source = NestedScrollSource.Drag
-            )
+            ) { Offset.Zero }
             // we have to disable further invalidation requests as otherwise while the overscroll
             // effect is considered active (as it is in a pulled state) this will infinitely
             // schedule next invalidation right from the drawing. this will make our test infra
@@ -406,17 +405,23 @@
         rule.runOnIdle {
             repeat(2) {
                 val offset = Offset(-10f, -10f)
-                assertThat(
-                    effect.consumePreScroll(offset, NestedScrollSource.Drag)
-                ).isEqualTo(Offset.Zero)
-                effect.consumePostScroll(offset, offset, NestedScrollSource.Drag)
+                var offsetConsumed: Offset? = null
+
+                effect.applyToScroll(offset, NestedScrollSource.Drag) {
+                    offsetConsumed = offset - it
+                    Offset.Zero
+                }
+                assertThat(offsetConsumed).isEqualTo(Offset.Zero)
             }
             val velocity = Velocity(-5f, -5f)
             runBlocking {
-                assertThat(
-                    effect.consumePreFling(velocity)
-                ).isEqualTo(Velocity.Zero)
-                effect.consumePostFling(velocity)
+                var velocityConsumed: Velocity? = null
+
+                effect.applyToFling(velocity) {
+                    velocityConsumed = velocity - it
+                    Velocity.Zero
+                }
+                assertThat(velocityConsumed!!).isEqualTo(Velocity.Zero)
             }
         }
     }
@@ -433,18 +438,24 @@
         rule.runOnIdle {
             repeat(2) {
                 val offset = Offset(0f, 10f)
-                assertThat(
-                    effect.consumePreScroll(offset, NestedScrollSource.Drag)
-                ).isEqualTo(Offset.Zero)
-                effect.consumePostScroll(offset, offset, NestedScrollSource.Drag)
+                var offsetConsumed: Offset? = null
+
+                effect.applyToScroll(offset, NestedScrollSource.Drag) {
+                    offsetConsumed = offset - it
+                    Offset.Zero
+                }
+                assertThat(offsetConsumed).isEqualTo(Offset.Zero)
             }
 
             val velocity = Velocity(0f, 5f)
             runBlocking {
-                assertThat(
-                    effect.consumePreFling(velocity)
-                ).isEqualTo(Velocity.Zero)
-                effect.consumePostFling(velocity)
+                var velocityConsumed: Velocity? = null
+
+                effect.applyToFling(velocity) {
+                    velocityConsumed = velocity - it
+                    Velocity.Zero
+                }
+                assertThat(velocityConsumed!!).isEqualTo(Velocity.Zero)
             }
         }
     }
@@ -648,32 +659,32 @@
 
         var preFlingVelocity = Velocity.Zero
 
-        override fun consumePreScroll(
-            scrollDelta: Offset,
-            source: NestedScrollSource
+        override fun applyToScroll(
+            delta: Offset,
+            source: NestedScrollSource,
+            performScroll: (Offset) -> Offset
         ): Offset {
-            lastPreScrollDelta = scrollDelta
+            lastPreScrollDelta = delta
             preScrollSource = source
 
-            return if (consumePreCycles) scrollDelta / 10f else Offset.Zero
-        }
+            val consumed = if (consumePreCycles) delta / 10f else Offset.Zero
 
-        override fun consumePostScroll(
-            initialDragDelta: Offset,
-            overscrollDelta: Offset,
-            source: NestedScrollSource
-        ) {
-            lastInitialDragDelta = initialDragDelta
-            lastOverscrollDelta = overscrollDelta
+            val consumedByScroll = performScroll(delta - consumed)
+
+            lastInitialDragDelta = delta
+            lastOverscrollDelta = delta - consumedByScroll - consumed
             lastNestedScrollSource = source
+
+            return delta - lastOverscrollDelta
         }
 
-        override suspend fun consumePreFling(velocity: Velocity): Velocity {
+        override suspend fun applyToFling(
+            velocity: Velocity,
+            performFling: suspend (Velocity) -> Velocity
+        ) {
             preFlingVelocity = velocity
-            return if (consumePreCycles) velocity / 10f else Velocity.Zero
-        }
-
-        override suspend fun consumePostFling(velocity: Velocity) {
+            val consumed = if (consumePreCycles) velocity / 10f else Velocity.Zero
+            performFling(velocity - consumed)
             lastVelocity = velocity
         }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
index 5baa306..2345c6e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
@@ -2552,31 +2552,6 @@
     }
 }
 
-@OptIn(ExperimentalFoundationApi::class)
-private val NoOpOverscrollEffect = object : OverscrollEffect {
-
-    override fun consumePreScroll(
-        scrollDelta: Offset,
-        source: NestedScrollSource
-    ): Offset = Offset.Zero
-
-    override fun consumePostScroll(
-        initialDragDelta: Offset,
-        overscrollDelta: Offset,
-        source: NestedScrollSource
-    ) {
-    }
-
-    override suspend fun consumePreFling(velocity: Velocity): Velocity = Velocity.Zero
-
-    override suspend fun consumePostFling(velocity: Velocity) {}
-
-    override val isInProgress: Boolean
-        get() = false
-
-    override val effectModifier: Modifier get() = Modifier
-}
-
 // Very low tolerance on the difference
 internal val VelocityTrackerCalculationThreshold = 1
 
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt
index b651632..b5e0699 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt
@@ -63,12 +63,10 @@
 internal actual fun rememberOverscrollEffect(): OverscrollEffect {
     val context = LocalContext.current
     val config = LocalOverscrollConfiguration.current
-    return remember(context, config) {
-        if (config != null) {
-            AndroidEdgeEffectOverscrollEffect(context, config)
-        } else {
-            NoOpOverscrollEffect
-        }
+    return if (config != null) {
+        remember(context, config) { AndroidEdgeEffectOverscrollEffect(context, config) }
+    } else {
+        NoOpOverscrollEffect
     }
 }
 
@@ -132,41 +130,44 @@
 
     private var scrollCycleInProgress: Boolean = false
 
-    override fun consumePreScroll(
-        scrollDelta: Offset,
-        source: NestedScrollSource
+    override fun applyToScroll(
+        delta: Offset,
+        source: NestedScrollSource,
+        performScroll: (Offset) -> Offset
     ): Offset {
+        // Early return
         if (containerSize.isEmpty()) {
-            return Offset.Zero
+            return performScroll(delta)
         }
+
         if (!scrollCycleInProgress) {
             stopOverscrollAnimation()
             scrollCycleInProgress = true
         }
         val pointer = pointerPosition ?: containerSize.center
         val consumedPixelsY = when {
-            scrollDelta.y == 0f -> 0f
+            delta.y == 0f -> 0f
             topEffect.distanceCompat != 0f -> {
-                pullTop(scrollDelta, pointer).also {
+                pullTop(delta, pointer).also {
                     if (topEffect.distanceCompat == 0f) topEffect.onRelease()
                 }
             }
             bottomEffect.distanceCompat != 0f -> {
-                pullBottom(scrollDelta, pointer).also {
+                pullBottom(delta, pointer).also {
                     if (bottomEffect.distanceCompat == 0f) bottomEffect.onRelease()
                 }
             }
             else -> 0f
         }
         val consumedPixelsX = when {
-            scrollDelta.x == 0f -> 0f
+            delta.x == 0f -> 0f
             leftEffect.distanceCompat != 0f -> {
-                pullLeft(scrollDelta, pointer).also {
+                pullLeft(delta, pointer).also {
                     if (leftEffect.distanceCompat == 0f) leftEffect.onRelease()
                 }
             }
             rightEffect.distanceCompat != 0f -> {
-                pullRight(scrollDelta, pointer).also {
+                pullRight(delta, pointer).also {
                     if (rightEffect.distanceCompat == 0f) rightEffect.onRelease()
                 }
             }
@@ -174,39 +175,39 @@
         }
         val consumedOffset = Offset(consumedPixelsX, consumedPixelsY)
         if (consumedOffset != Offset.Zero) invalidateOverscroll()
-        return consumedOffset
-    }
 
-    override fun consumePostScroll(
-        initialDragDelta: Offset,
-        overscrollDelta: Offset,
-        source: NestedScrollSource
-    ) {
-        if (containerSize.isEmpty()) {
-            return
-        }
+        val leftForDelta = delta - consumedOffset
+        val consumedByDelta = performScroll(leftForDelta)
+        val leftForOverscroll = leftForDelta - consumedByDelta
+
         var needsInvalidation = false
         if (source == NestedScrollSource.Drag) {
-            val pointer = pointerPosition ?: containerSize.center
-            if (overscrollDelta.x > 0) {
-                pullLeft(overscrollDelta, pointer)
-            } else if (overscrollDelta.x < 0) {
-                pullRight(overscrollDelta, pointer)
+            if (leftForOverscroll.x > 0) {
+                pullLeft(leftForOverscroll, pointer)
+            } else if (leftForOverscroll.x < 0) {
+                pullRight(leftForOverscroll, pointer)
             }
-            if (overscrollDelta.y > 0) {
-                pullTop(overscrollDelta, pointer)
-            } else if (overscrollDelta.y < 0) {
-                pullBottom(overscrollDelta, pointer)
+            if (leftForOverscroll.y > 0) {
+                pullTop(leftForOverscroll, pointer)
+            } else if (leftForOverscroll.y < 0) {
+                pullBottom(leftForOverscroll, pointer)
             }
-            needsInvalidation = overscrollDelta != Offset.Zero || needsInvalidation
+            needsInvalidation = leftForOverscroll != Offset.Zero
         }
-        needsInvalidation = releaseOppositeOverscroll(initialDragDelta) || needsInvalidation
+        needsInvalidation = releaseOppositeOverscroll(delta) || needsInvalidation
         if (needsInvalidation) invalidateOverscroll()
+
+        return consumedOffset + consumedByDelta
     }
 
-    override suspend fun consumePreFling(velocity: Velocity): Velocity {
+    override suspend fun applyToFling(
+        velocity: Velocity,
+        performFling: suspend (Velocity) -> Velocity
+    ) {
+        // Early return
         if (containerSize.isEmpty()) {
-            return Velocity.Zero
+            performFling(velocity)
+            return
         }
         val consumedX = if (velocity.x > 0f && leftEffect.distanceCompat != 0f) {
             leftEffect.onAbsorbCompat(velocity.x.roundToInt())
@@ -228,25 +229,23 @@
         }
         val consumed = Velocity(consumedX, consumedY)
         if (consumed != Velocity.Zero) invalidateOverscroll()
-        return consumed
-    }
 
-    override suspend fun consumePostFling(velocity: Velocity) {
-        if (containerSize.isEmpty()) {
-            return
-        }
+        val remainingVelocity = velocity - consumed
+        val consumedByVelocity = performFling(remainingVelocity)
+        val leftForOverscroll = remainingVelocity - consumedByVelocity
+
         scrollCycleInProgress = false
-        if (velocity.x > 0) {
-            leftEffect.onAbsorbCompat(velocity.x.roundToInt())
-        } else if (velocity.x < 0) {
-            rightEffect.onAbsorbCompat(-velocity.x.roundToInt())
+        if (leftForOverscroll.x > 0) {
+            leftEffect.onAbsorbCompat(leftForOverscroll.x.roundToInt())
+        } else if (leftForOverscroll.x < 0) {
+            rightEffect.onAbsorbCompat(-leftForOverscroll.x.roundToInt())
         }
-        if (velocity.y > 0) {
-            topEffect.onAbsorbCompat(velocity.y.roundToInt())
-        } else if (velocity.y < 0) {
-            bottomEffect.onAbsorbCompat(-velocity.y.roundToInt())
+        if (leftForOverscroll.y > 0) {
+            topEffect.onAbsorbCompat(leftForOverscroll.y.roundToInt())
+        } else if (leftForOverscroll.y < 0) {
+            bottomEffect.onAbsorbCompat(-leftForOverscroll.y.roundToInt())
         }
-        if (velocity != Velocity.Zero) invalidateOverscroll()
+        if (leftForOverscroll != Velocity.Zero) invalidateOverscroll()
         animateToRelease()
     }
 
@@ -489,32 +488,6 @@
     }
 }
 
-@OptIn(ExperimentalFoundationApi::class)
-private val NoOpOverscrollEffect = object : OverscrollEffect {
-
-    override fun consumePreScroll(
-        scrollDelta: Offset,
-        source: NestedScrollSource
-    ): Offset = Offset.Zero
-
-    override fun consumePostScroll(
-        initialDragDelta: Offset,
-        overscrollDelta: Offset,
-        source: NestedScrollSource
-    ) {
-    }
-
-    override suspend fun consumePreFling(velocity: Velocity): Velocity = Velocity.Zero
-
-    override suspend fun consumePostFling(velocity: Velocity) {}
-
-    override val isInProgress: Boolean
-        get() = false
-
-    override val effectModifier: Modifier
-        get() = Modifier
-}
-
 /**
  * There is an unwanted behavior in the stretch overscroll effect we have to workaround:
  * When the effect is started it is getting the current RenderNode bounds and clips the content
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Overscroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Overscroll.kt
index b56d000..c8c89557 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Overscroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Overscroll.kt
@@ -24,107 +24,115 @@
 import androidx.compose.ui.unit.Velocity
 
 /**
- * Controller to control the overscroll logic for a particular scrollable container.
+ * An OverscrollEffect represents a visual effect that displays when the edges of a scrolling
+ * container have been reached with a scroll or fling. For the default platform effect that should
+ * be used in most cases, see
+ * [androidx.compose.foundation.gestures.ScrollableDefaults.overscrollEffect].
  *
- * This entity defines the "how" of the overscroll effect. If the default platform effect is needed,
- * consider using [ScrollableDefaults.overscrollEffect]. In order for overscroll to work,
- * the controller is supposed to be passed to [scrollable] modifier to receive the data of
- * overscroll, and then applied where appropriate using [overscroll] modifier.
+ * OverscrollEffect conceptually 'decorates' scroll / fling events: consuming some of the delta or
+ * velocity before and/or after the event is consumed by the scrolling container. [applyToScroll]
+ * applies overscroll to a scroll event, and [applyToFling] applies overscroll to a fling.
+ *
+ * Higher level components such as [androidx.compose.foundation.lazy.LazyColumn] will automatically
+ * configure an OverscrollEffect for you. To use a custom OverscrollEffect you first need to
+ * provide it with scroll and/or fling events - usually by providing it to a
+ * [androidx.compose.foundation.gestures.scrollable]. Then you can draw the effect on top of the
+ * scrolling content using [Modifier.overscroll].
  *
  * @sample androidx.compose.foundation.samples.OverscrollSample
  */
 @ExperimentalFoundationApi
 @Stable
 interface OverscrollEffect {
-
     /**
-     * Consume any overscroll before the scroll happens if needed.
+     * Applies overscroll to [performScroll]. [performScroll] should represent a drag / scroll, and
+     * returns the amount of delta consumed, so in simple cases the amount of overscroll to show
+     * should be equal to `delta - performScroll(delta)`. The OverscrollEffect can optionally
+     * consume some delta before calling [performScroll], such as to release any existing tension.
+     * The implementation *must* call [performScroll] exactly once. This function should return the
+     * sum of all the delta that was consumed during this operation - both by the overscroll and
+     * [performScroll].
      *
-     * This is usually relevant when the overscroll expresses the effect that is needed to be
-     * negated first before actually scrolling. The example might be a spring-based overscroll,
-     * where you want to release the tension of a spring before starting to scroll to provide a
-     * good user-experience.
-     *
-     * @param scrollDelta the original delta to scroll
-     * @param source source of the scroll event
-     *
-     * @return the amount of scroll consumed that won't be available for scrollable container
-     * anymore
+     * @param delta total scroll delta available
+     * @param source the source of the delta
+     * @param performScroll the scroll action that the overscroll is applied to. The [Offset]
+     * parameter represents how much delta is available, and the return value is how much delta was
+     * consumed. Any delta that was not consumed should be used to show the overscroll effect.
+     * @return the delta consumed from [delta] by the operation of this function - including that
+     * consumed by [performScroll].
      */
-    fun consumePreScroll(
-        scrollDelta: Offset,
-        source: NestedScrollSource
+    fun applyToScroll(
+        delta: Offset,
+        source: NestedScrollSource,
+        performScroll: (Offset) -> Offset
     ): Offset
 
     /**
-     * Process scroll delta that is available after the scroll iteration is over.
+     * Applies overscroll to [performFling]. [performFling] should represent a fling (the release
+     * of a drag or scroll), and returns the amount of [Velocity] consumed, so in simple cases the
+     * amount of overscroll to show should be equal to `velocity - performFling(velocity)`. The
+     * OverscrollEffect can optionally consume some [Velocity] before calling [performFling], such
+     * as to release any existing tension. The implementation *must* call [performFling] exactly
+     * once.
      *
-     * This is the main method to show an overscroll, as [overscrollDelta] will be a
-     * non-[zero][Offset.Zero] only if the scroll is happening at the bound of a scrollable
-     * container.
-     *
-     * @param initialDragDelta initial drag delta before any consumption was made
-     * @param overscrollDelta the amount of overscroll left after the scroll process
-     * @param source source of the scroll event
+     * @param velocity total [Velocity] available
+     * @param performFling the [Velocity] consuming lambda that the overscroll is applied to. The
+     * [Velocity] parameter represents how much [Velocity] is available, and the return value is how
+     * much [Velocity] was consumed. Any [Velocity] that was not consumed should be used to show the
+     * overscroll effect.
      */
-    fun consumePostScroll(
-        initialDragDelta: Offset,
-        overscrollDelta: Offset,
-        source: NestedScrollSource
-    )
+    suspend fun applyToFling(velocity: Velocity, performFling: suspend (Velocity) -> Velocity)
 
     /**
-     * Consume any velocity for the over scroll effect before the fling happens.
+     * Whether this OverscrollEffect is currently displaying overscroll.
      *
-     * relevant when the overscroll expresses the effect that is needed to be
-     * negated (or increased) first before actually scrolling. The example might be a spring-based
-     * overscroll, where you want to release the tension of a spring before starting to scroll to
-     * provide a good user-experience.
-     *
-     * @param velocity velocity available to a scrolling container before flinging
-     *
-     * @return the amount of velocity that overscroll effect consumed that won't be available for
-     * fling operation
-     */
-    suspend fun consumePreFling(velocity: Velocity): Velocity
-
-    /**
-     * Feed and process velocity overscroll to show an effect.
-     *
-     * @param velocity the amount of velocity that is left for overscroll after the fling happened.
-     */
-    suspend fun consumePostFling(velocity: Velocity)
-
-    /**
-     * Whether over scroll within this controller is currently on progress or not, e.g. if the
-     * overscroll effect is playing animation or shown/interactable in any other way.
-     *
-     * @return true if there is over scroll happening at the time of the call, false otherwise
+     * @return true if this OverscrollEffect is currently displaying overscroll
      */
     val isInProgress: Boolean
 
     /**
-     * A modifier that will apply the overscroll effect as desired
+     * A [Modifier] that will draw this OverscrollEffect
      */
     val effectModifier: Modifier
 }
 
+/**
+ * Renders overscroll from the provided [overscrollEffect].
+ *
+ * This modifier is a convenience method to call [OverscrollEffect.effectModifier], which
+ * renders the actual effect. Note that this modifier is only responsible for the visual part of
+ * overscroll - on its own it will not handle input events. In addition to using this modifier you
+ * also need to propagate events to the [overscrollEffect], most commonly by using a
+ * [androidx.compose.foundation.gestures.scrollable].
+ *
+ * @sample androidx.compose.foundation.samples.OverscrollSample
+ *
+ * @param overscrollEffect the [OverscrollEffect] to render
+ */
+@ExperimentalFoundationApi
+fun Modifier.overscroll(overscrollEffect: OverscrollEffect): Modifier =
+    this.then(overscrollEffect.effectModifier)
+
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
 internal expect fun rememberOverscrollEffect(): OverscrollEffect
 
-/**
- * Modifier to apply the overscroll as specified by [OverscrollEffect]
- *
- * This modifier is a convenience method to call [OverscrollEffect.effectModifier], which
- * performs the actual overscroll logic. Note that this modifier is the representation of the
- * overscroll on the UI, to make overscroll events to be propagated to the [OverscrollEffect],
- * you have to pass it to [scrollable].
- *
- * @sample androidx.compose.foundation.samples.OverscrollSample
- *
- * @param overscrollEffect controller that defines the behavior and the overscroll state.
- */
-@ExperimentalFoundationApi
-fun Modifier.overscroll(overscrollEffect: OverscrollEffect): Modifier =
-    this.then(overscrollEffect.effectModifier)
\ No newline at end of file
+@OptIn(ExperimentalFoundationApi::class)
+internal object NoOpOverscrollEffect : OverscrollEffect {
+    override fun applyToScroll(
+        delta: Offset,
+        source: NestedScrollSource,
+        performScroll: (Offset) -> Offset
+    ): Offset = performScroll(delta)
+
+    override suspend fun applyToFling(
+        velocity: Velocity,
+        performFling: suspend (Velocity) -> Velocity
+    ) { performFling(velocity) }
+
+    override val isInProgress: Boolean
+        get() = false
+
+    override val effectModifier: Modifier
+        get() = Modifier
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index 4057fb4..54ce08b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -355,61 +355,37 @@
      */
     fun ScrollScope.dispatchScroll(availableDelta: Offset, source: NestedScrollSource): Offset {
         val scrollDelta = availableDelta.singleAxisOffset()
-        val overscrollPreConsumed = overscrollPreConsumeDelta(scrollDelta, source)
 
-        val afterPreOverscroll = scrollDelta - overscrollPreConsumed
-        val nestedScrollDispatcher = nestedScrollDispatcher.value
-        val preConsumedByParent = nestedScrollDispatcher
-            .dispatchPreScroll(afterPreOverscroll, source)
+        val performScroll: (Offset) -> Offset = { delta ->
+            val nestedScrollDispatcher = nestedScrollDispatcher.value
+            val preConsumedByParent = nestedScrollDispatcher
+                .dispatchPreScroll(delta, source)
 
-        val scrollAvailable = afterPreOverscroll - preConsumedByParent
-        // Consume on a single axis
-        val axisConsumed =
-            scrollBy(scrollAvailable.reverseIfNeeded().toFloat()).toOffset().reverseIfNeeded()
+            val scrollAvailable = delta - preConsumedByParent
+            // Consume on a single axis
+            val axisConsumed =
+                scrollBy(scrollAvailable.reverseIfNeeded().toFloat()).toOffset().reverseIfNeeded()
 
-        val leftForParent = scrollAvailable - axisConsumed
-        val parentConsumed = nestedScrollDispatcher.dispatchPostScroll(
-            axisConsumed,
-            leftForParent,
-            source
-        )
-        overscrollPostConsumeDelta(
-            scrollAvailable,
-            leftForParent - parentConsumed,
-            source
-        )
+            val leftForParent = scrollAvailable - axisConsumed
+            val parentConsumed = nestedScrollDispatcher.dispatchPostScroll(
+                axisConsumed,
+                leftForParent,
+                source
+            )
 
-        return overscrollPreConsumed + preConsumedByParent + axisConsumed + parentConsumed
+            preConsumedByParent + axisConsumed + parentConsumed
+        }
+
+        return if (overscrollEffect != null && shouldDispatchOverscroll) {
+            overscrollEffect.applyToScroll(scrollDelta, source, performScroll)
+        } else {
+            performScroll(scrollDelta)
+        }
     }
 
     private val shouldDispatchOverscroll
         get() = scrollableState.canScrollForward || scrollableState.canScrollBackward
 
-    fun overscrollPreConsumeDelta(
-        scrollDelta: Offset,
-        source: NestedScrollSource
-    ): Offset {
-        return if (overscrollEffect != null && shouldDispatchOverscroll) {
-            overscrollEffect.consumePreScroll(scrollDelta, source)
-        } else {
-            Offset.Zero
-        }
-    }
-
-    private fun overscrollPostConsumeDelta(
-        consumedByChain: Offset,
-        availableForOverscroll: Offset,
-        source: NestedScrollSource
-    ) {
-        if (overscrollEffect != null && shouldDispatchOverscroll) {
-            overscrollEffect.consumePostScroll(
-                consumedByChain,
-                availableForOverscroll,
-                source
-            )
-        }
-    }
-
     fun performRawScroll(scroll: Offset): Offset {
         return if (scrollableState.isScrollInProgress) {
             Offset.Zero
@@ -424,25 +400,25 @@
         registerNestedFling(true)
 
         val availableVelocity = initialVelocity.singleAxisVelocity()
-        val preOverscrollConsumed =
-            if (overscrollEffect != null && shouldDispatchOverscroll) {
-                overscrollEffect.consumePreFling(availableVelocity)
-            } else {
-                Velocity.Zero
-            }
-        val velocity = (availableVelocity - preOverscrollConsumed)
-        val preConsumedByParent = nestedScrollDispatcher
-            .value.dispatchPreFling(velocity)
-        val available = velocity - preConsumedByParent
-        val velocityLeft = doFlingAnimation(available)
-        val consumedPost =
-            nestedScrollDispatcher.value.dispatchPostFling(
-                (available - velocityLeft),
-                velocityLeft
-            )
-        val totalLeft = velocityLeft - consumedPost
+
+        val performFling: suspend (Velocity) -> Velocity = { velocity ->
+            val preConsumedByParent = nestedScrollDispatcher
+                .value.dispatchPreFling(velocity)
+            val available = velocity - preConsumedByParent
+            val velocityLeft = doFlingAnimation(available)
+            val consumedPost =
+                nestedScrollDispatcher.value.dispatchPostFling(
+                    (available - velocityLeft),
+                    velocityLeft
+                )
+            val totalLeft = velocityLeft - consumedPost
+            velocity - totalLeft
+        }
+
         if (overscrollEffect != null && shouldDispatchOverscroll) {
-            overscrollEffect.consumePostFling(totalLeft)
+            overscrollEffect.applyToFling(availableVelocity, performFling)
+        } else {
+            performFling(availableVelocity)
         }
 
         // Self stopped flinging, reset
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/DesktopOverscroll.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/DesktopOverscroll.kt
index 75bbcfc..bd9dcef 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/DesktopOverscroll.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/DesktopOverscroll.kt
@@ -17,37 +17,7 @@
 package androidx.compose.foundation
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
-import androidx.compose.ui.unit.Velocity
 
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
-internal actual fun rememberOverscrollEffect(): OverscrollEffect {
-    return remember {
-        DesktopEdgeEffectOverscrollEffect()
-    }
-}
-
-@OptIn(ExperimentalFoundationApi::class)
-private class DesktopEdgeEffectOverscrollEffect() : OverscrollEffect {
-    override fun consumePreScroll(
-        scrollDelta: Offset,
-        source: NestedScrollSource
-    ): Offset = Offset.Zero
-
-    override fun consumePostScroll(
-        initialDragDelta: Offset,
-        overscrollDelta: Offset,
-        source: NestedScrollSource
-    ) {}
-
-    override suspend fun consumePreFling(velocity: Velocity): Velocity = Velocity.Zero
-
-    override suspend fun consumePostFling(velocity: Velocity) {}
-
-    override val isInProgress = false
-    override val effectModifier = Modifier
-}
+internal actual fun rememberOverscrollEffect(): OverscrollEffect = NoOpOverscrollEffect
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
index 44bff47..c5cbba9 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
@@ -267,7 +267,7 @@
                 sheetContent = {
                     Box(
                         Modifier
-                            .fillMaxSize()
+                            .fillMaxSize(0.6f)
                             .testTag(sheetTag)
                     )
                 }
@@ -1137,4 +1137,74 @@
             )
             .assertWidthIsEqualTo(expectedSheetWidth)
     }
+
+    @Test
+    fun modalBottomSheet_shortSheet_anchorChangeHandler_previousTargetNotInAnchors_reconciles() {
+        val sheetState = ModalBottomSheetState(ModalBottomSheetValue.Hidden)
+        var hasSheetContent by mutableStateOf(false) // Start out with empty sheet content
+        lateinit var scope: CoroutineScope
+        rule.setContent {
+            scope = rememberCoroutineScope()
+            ModalBottomSheetLayout(
+                sheetState = sheetState,
+                sheetContent = {
+                    if (hasSheetContent) {
+                        Box(Modifier.fillMaxHeight(0.4f))
+                    }
+                },
+                content = {}
+            )
+        }
+
+        assertThat(sheetState.currentValue).isEqualTo(ModalBottomSheetValue.Hidden)
+        assertThat(sheetState.swipeableState.hasAnchorForValue(ModalBottomSheetValue.HalfExpanded))
+            .isFalse()
+        assertThat(sheetState.swipeableState.hasAnchorForValue(ModalBottomSheetValue.Expanded))
+            .isFalse()
+
+        scope.launch { sheetState.show() }
+        rule.waitForIdle()
+
+        assertThat(sheetState.isVisible).isTrue()
+        assertThat(sheetState.currentValue).isEqualTo(sheetState.targetValue)
+
+        hasSheetContent = true // Recompose with sheet content
+        rule.waitForIdle()
+        assertThat(sheetState.currentValue).isEqualTo(ModalBottomSheetValue.Expanded)
+    }
+
+    @Test
+    fun modalBottomSheet_tallSheet_anchorChangeHandler_previousTargetNotInAnchors_reconciles() {
+        val sheetState = ModalBottomSheetState(ModalBottomSheetValue.Hidden)
+        var hasSheetContent by mutableStateOf(false) // Start out with empty sheet content
+        lateinit var scope: CoroutineScope
+        rule.setContent {
+            scope = rememberCoroutineScope()
+            ModalBottomSheetLayout(
+                sheetState = sheetState,
+                sheetContent = {
+                    if (hasSheetContent) {
+                        Box(Modifier.fillMaxHeight(0.6f))
+                    }
+                },
+                content = {}
+            )
+        }
+
+        assertThat(sheetState.currentValue).isEqualTo(ModalBottomSheetValue.Hidden)
+        assertThat(sheetState.swipeableState.hasAnchorForValue(ModalBottomSheetValue.HalfExpanded))
+            .isFalse()
+        assertThat(sheetState.swipeableState.hasAnchorForValue(ModalBottomSheetValue.Expanded))
+            .isFalse()
+
+        scope.launch { sheetState.show() }
+        rule.waitForIdle()
+
+        assertThat(sheetState.isVisible).isTrue()
+        assertThat(sheetState.currentValue).isEqualTo(sheetState.targetValue)
+
+        hasSheetContent = true // Recompose with sheet content
+        rule.waitForIdle()
+        assertThat(sheetState.currentValue).isEqualTo(ModalBottomSheetValue.HalfExpanded)
+    }
 }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
index 18ac04d..1ded9e5 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
@@ -33,6 +33,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
+import androidx.test.filters.RequiresDevice
 import com.google.common.truth.Truth
 import com.nhaarman.mockitokotlin2.any
 import com.nhaarman.mockitokotlin2.doReturn
@@ -82,6 +83,7 @@
         rule.waitUntil { job.isCompleted }
     }
 
+    @RequiresDevice // b/264895456
     @Test
     fun snackbarHost_fifoQueueContract() {
         var resultedInvocation = ""
@@ -111,6 +113,7 @@
         Truth.assertThat(resultedInvocation).isEqualTo("0123456789")
     }
 
+    @RequiresDevice // b/264895456
     @Test
     @LargeTest
     fun snackbarHost_returnedResult() {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
index d15e372..5ea9086 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
@@ -499,9 +499,9 @@
                     when (state) {
                         Hidden -> fullHeight
                         HalfExpanded -> when {
-                            sheetSize.height < fullHeight / 2 -> null
+                            sheetSize.height < fullHeight / 2f -> null
                             sheetState.isSkipHalfExpanded -> null
-                            else -> sheetSize.height / 2f
+                            else -> fullHeight / 2f
                         }
 
                         Expanded -> if (sheetSize.height != 0) {
@@ -656,7 +656,7 @@
     animateTo: (target: ModalBottomSheetValue, velocity: Float) -> Unit,
     snapTo: (target: ModalBottomSheetValue) -> Unit,
 ) = AnchorChangeHandler<ModalBottomSheetValue> { previousTarget, previousAnchors, newAnchors ->
-    val previousTargetOffset = previousAnchors.getValue(previousTarget)
+    val previousTargetOffset = previousAnchors[previousTarget]
     val newTarget = when (previousTarget) {
         Hidden -> Hidden
         HalfExpanded, Expanded -> {
diff --git a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/AnimateXAsStateClockTest.kt b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/AnimateXAsStateClockTest.kt
index 323824b..f58d39d 100644
--- a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/AnimateXAsStateClockTest.kt
+++ b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/AnimateXAsStateClockTest.kt
@@ -77,7 +77,7 @@
         checkUpdatedState(clock, label = "DpAnimation",
             newInitialValue = 1.dp, newTargetValue = 2.dp,
             composeState = { state!!.value })
-        rule.runOnUiThread { clock.setStateParameters(listOf(3.dp), listOf(4.dp)) }
+        rule.runOnUiThread { clock.setStateParameters(3f, 4.0) }
         checkUpdatedState(clock, label = "DpAnimation",
             newInitialValue = 3.dp, newTargetValue = 4.dp,
             composeState = { state!!.value })
@@ -89,7 +89,7 @@
             composeState = { state!!.value })
         // Invalid parameters are ignored.
         rule.runOnUiThread {
-            clock.setStateParameters(111.dp, 111)
+            clock.setStateParameters(111.dp, "")
             clock.setStateParameters(111.dp, null)
             clock.setStateParameters(listOf(111.dp), listOf(111L))
             clock.setStateParameters(listOf(null), listOf(null))
diff --git a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/UtilsTest.kt b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/UtilsTest.kt
new file mode 100644
index 0000000..a5cf31a
--- /dev/null
+++ b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/UtilsTest.kt
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.tooling.animation.clock
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.tooling.animation.states.TargetState
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+class UtilsTest {
+
+    @Test
+    fun currentValueIsNull() {
+        val value = parseParametersToValue<Int?>(null, 20, 30)
+        assertNull(value)
+    }
+
+    @Test
+    fun par2IsNull() {
+        val value = parseParametersToValue(10, 20, null)
+        assertNull(value)
+    }
+
+    @Test
+    fun currentValueHasDifferentType() {
+        val value = parseParametersToValue(10f, 20, 30)
+        assertNull(value)
+    }
+
+    @Test
+    fun par1HasDifferentType() {
+        val value = parseParametersToValue(10, 20f, 30)
+        assertNull(value)
+    }
+
+    @Test
+    fun par2HasDifferentType() {
+        val value = parseParametersToValue(10, 20, 30f)
+        assertNull(value)
+    }
+
+    @Test
+    fun listsHasNull() {
+        val value = parseParametersToValue(IntSize(10, 20), listOf(10, null), listOf(20, 30))
+        assertNull(value)
+    }
+
+    @Test
+    fun listsAreEmpty() {
+        val value = parseParametersToValue(IntSize(10, 20), emptyList<Int>(), emptyList<Int>())
+        assertNull(value)
+    }
+
+    @Test
+    fun offsetOutOfBounds() {
+        val value = parseParametersToValue(
+            Offset(10f, 20f),
+            listOf(30f),
+            listOf(50f, 60f)
+        )
+        assertNull(value)
+    }
+
+    @Test
+    fun offsetIncorrectType() {
+        val value = parseParametersToValue(
+            Offset(10f, 20f),
+            listOf("a", "b"),
+            listOf(50f, 60f)
+        )
+        assertNull(value)
+    }
+
+    @Test
+    fun intIsParsed() {
+        val value = parseParametersToValue(10, 20, 30)
+        assertEquals(TargetState(20, 30), value)
+    }
+
+    @Test
+    fun intIsParsedAsList() {
+        val value = parseParametersToValue(10, listOf(20), listOf(30))
+        assertEquals(TargetState(20, 30), value)
+    }
+
+    @Test
+    fun stringIsParsed() {
+        val value = parseParametersToValue("a", "b", "c")
+        assertEquals(TargetState("b", "c"), value)
+    }
+
+    @Test
+    fun stringIsParsedAsList() {
+        val value = parseParametersToValue("a", listOf("b"), listOf("c"))
+        assertEquals(TargetState("b", "c"), value)
+    }
+
+    @Test
+    fun booleanIsParsed() {
+        val value = parseParametersToValue(currentValue = false, par1 = true, par2 = false)
+        assertEquals(TargetState(initial = true, target = false), value)
+    }
+
+    @Test
+    fun booleanIsParsedAsList() {
+        val value = parseParametersToValue(false, listOf(true), listOf(false))
+        assertEquals(TargetState(initial = true, target = false), value)
+    }
+
+    @Test
+    fun dpIsParsed() {
+        val value = parseParametersToValue(10.dp, 20.dp, 30.dp)
+        assertEquals(TargetState(20.dp, 30.dp), value)
+    }
+
+    @Test
+    fun dpIsParsedAsDoubleAndFloat() {
+        val value = parseParametersToValue(10.dp, 20.0, 30f)
+        assertEquals(TargetState(20.dp, 30.dp), value)
+    }
+
+    @Test
+    fun dpIsParsedAsList() {
+        val value = parseParametersToValue(10.dp, listOf(20f), listOf(30f))
+        assertEquals(TargetState(20.dp, 30.dp), value)
+    }
+
+    @Test
+    fun dpIsParsedAsDoubleAndFloatList() {
+        val value = parseParametersToValue(10.dp, listOf(20.0), listOf(30f))
+        assertEquals(TargetState(20.dp, 30.dp), value)
+    }
+
+    @Test
+    fun intSizeIsParsed() {
+        val value = parseParametersToValue(
+            IntSize(10, 20),
+            IntSize(30, 40),
+            IntSize(50, 60)
+        )
+        assertEquals(TargetState(IntSize(30, 40), IntSize(50, 60)), value)
+    }
+
+    @Test
+    fun intSizeIsParsedAsList() {
+        val value = parseParametersToValue(
+            IntSize(10, 20),
+            listOf(30, 40),
+            listOf(50, 60)
+        )
+        assertEquals(TargetState(IntSize(30, 40), IntSize(50, 60)), value)
+    }
+
+    @Test
+    fun intOffsetIsParsed() {
+        val value = parseParametersToValue(
+            IntOffset(10, 20),
+            IntOffset(30, 40),
+            IntOffset(50, 60)
+        )
+        assertEquals(TargetState(IntOffset(30, 40), IntOffset(50, 60)), value)
+    }
+
+    @Test
+    fun intOffsetIsParsedAsList() {
+        val value = parseParametersToValue(
+            IntOffset(10, 20),
+            listOf(30, 40),
+            listOf(50, 60)
+        )
+        assertEquals(TargetState(IntOffset(30, 40), IntOffset(50, 60)), value)
+    }
+
+    @Test
+    fun sizeIsParsed() {
+        val value = parseParametersToValue(
+            Size(10f, 20f),
+            Size(30f, 40f),
+            Size(50f, 60f)
+        )
+        assertEquals(TargetState(Size(30f, 40f), Size(50f, 60f)), value)
+    }
+
+    @Test
+    fun sizeIsParsedAsList() {
+        val value = parseParametersToValue(
+            Size(10f, 20f),
+            listOf(30f, 40f),
+            listOf(50f, 60f)
+        )
+        assertEquals(TargetState(Size(30f, 40f), Size(50f, 60f)), value)
+    }
+
+    @Test
+    fun offsetIsParsed() {
+        val value = parseParametersToValue(
+            Offset(10f, 20f),
+            Offset(30f, 40f),
+            Offset(50f, 60f)
+        )
+        assertEquals(TargetState(Offset(30f, 40f), Offset(50f, 60f)), value)
+    }
+
+    @Test
+    fun offsetIsParsedAsList() {
+        val value = parseParametersToValue(
+            Offset(10f, 20f),
+            listOf(30f, 40f),
+            listOf(50f, 60f)
+        )
+        assertEquals(TargetState(Offset(30f, 40f), Offset(50f, 60f)), value)
+    }
+
+    @Test
+    fun rectIsParsed() {
+        val value = parseParametersToValue(
+            Rect(10f, 20f, 30f, 40f),
+            Rect(50f, 60f, 70f, 80f),
+            Rect(90f, 100f, 110f, 120f)
+        )
+        assertEquals(
+            TargetState(
+                Rect(50f, 60f, 70f, 80f),
+                Rect(90f, 100f, 110f, 120f)
+            ), value
+        )
+    }
+
+    @Test
+    fun rectIsParsedAsList() {
+        val value = parseParametersToValue(
+            Rect(10f, 20f, 30f, 40f),
+            listOf(50f, 60f, 70f, 80f),
+            listOf(90f, 100f, 110f, 120f)
+        )
+        assertEquals(
+            TargetState(
+                Rect(50f, 60f, 70f, 80f),
+                Rect(90f, 100f, 110f, 120f)
+            ), value
+        )
+    }
+
+    @Test
+    fun colorIsParsed() {
+        val value = parseParametersToValue(
+            Color(0.1f, 0.2f, 0.3f, 0.4f),
+            Color(0.5f, 0.6f, 0.7f, 0.8f),
+            Color(0.55f, 0.65f, 0.75f, 0.85f)
+        )
+        assertEquals(
+            TargetState(
+                Color(0.5f, 0.6f, 0.7f, 0.8f),
+                Color(0.55f, 0.65f, 0.75f, 0.85f)
+            ), value
+        )
+    }
+
+    @Test
+    fun colorIsParsedAsList() {
+        val value = parseParametersToValue(
+            Color(0.1f, 0.2f, 0.3f, 0.4f),
+            listOf(0.5f, 0.6f, 0.7f, 0.8f),
+            listOf(0.55f, 0.65f, 0.75f, 0.85f)
+        )
+        assertEquals(
+            TargetState(
+                Color(0.5f, 0.6f, 0.7f, 0.8f),
+                Color(0.55f, 0.65f, 0.75f, 0.85f)
+            ), value
+        )
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/clock/Utils.kt b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/clock/Utils.kt
index fad8a64..ae1f769 100644
--- a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/clock/Utils.kt
+++ b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/clock/Utils.kt
@@ -98,11 +98,13 @@
                     animationSpec.initialStartOffset.offsetMillis
                 else 0L
             }
+
             is InfiniteRepeatableSpec<*> -> {
                 if (animationSpec.initialStartOffset.offsetType == StartOffsetType.Delay)
                     animationSpec.initialStartOffset.offsetMillis
                 else 0L
             }
+
             is VectorizedDurationBasedAnimationSpec<*> -> animationSpec.delayMillis
             else -> 0L
         }.toLong()
@@ -154,19 +156,42 @@
     )
 }
 
+/**
+ * [parseParametersToValue] makes sure what [currentValue], [par1], [par2] have the same types and
+ * returned [TargetState] always has correct and the same type as [currentValue].
+ */
 @Suppress("UNCHECKED_CAST")
 internal fun <T> parseParametersToValue(currentValue: T, par1: Any, par2: Any?): TargetState<T>? {
 
     currentValue ?: return null
 
+    /** Check if [par1] and [par2] are not null and have the same type. */
     fun parametersAreValid(par1: Any?, par2: Any?): Boolean {
         return par1 != null && par2 != null && par1::class == par2::class
     }
 
+    /** Check if all parameters have the same type. */
     fun parametersHasTheSameType(value: Any, par1: Any, par2: Any): Boolean {
         return value::class == par1::class && value::class == par2::class
     }
 
+    fun getDp(par: Any): Dp? {
+        return (par as? Dp) ?: (par as? Float)?.dp ?: (par as? Double)?.dp ?: (par as? Int)?.dp
+    }
+
+    fun parseDp(par1: Any, par2: Any?): TargetState<Dp>? {
+        if (currentValue !is Dp || par2 == null) return null
+        return if (par1 is Dp && par2 is Dp)
+            TargetState(par1, par2) else {
+            val dp1 = getDp(par1)
+            val dp2 = getDp(par2)
+            if (dp1 != null && dp2 != null)
+                TargetState(dp1, dp2) else null
+        }
+    }
+    // Dp could be presented as Float/Double/Int - try to parse it.
+    parseDp(par1, par2)?.let { return it as TargetState<T> }
+
     if (!parametersAreValid(par1, par2)) return null
 
     if (parametersHasTheSameType(currentValue, par1, par2!!)) {
@@ -227,12 +252,8 @@
                     ),
                 )
 
-                is Dp -> {
-                    if (parametersHasTheSameType(currentValue, par1[0]!!, par2[0]!!))
-                        TargetState(par1[0], par2[0]) else TargetState(
-                        (par1[0] as Float).dp, (par2[0] as Float).dp
-                    )
-                }
+                is Dp ->
+                    parseDp(par1[0]!!, par2[0]!!)
 
                 else -> {
                     if (parametersAreValid(par1[0], par2[0]) &&
diff --git a/health/connect/connect-client/src/main/java/androidx/health/platform/client/SdkConfig.java b/health/connect/connect-client/src/main/java/androidx/health/platform/client/SdkConfig.java
index 92456f9..3e2ec2d 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/platform/client/SdkConfig.java
+++ b/health/connect/connect-client/src/main/java/androidx/health/platform/client/SdkConfig.java
@@ -25,7 +25,7 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 public final class SdkConfig {
     // should be increased everytime a new SDK is released
-    public static final int SDK_VERSION = 9;
+    public static final int SDK_VERSION = 10;
 
     private SdkConfig() {}
 }
diff --git a/media2/media2-widget/src/main/res/values-ky/strings.xml b/media2/media2-widget/src/main/res/values-ky/strings.xml
index 2452d322..c53b918 100644
--- a/media2/media2-widget/src/main/res/values-ky/strings.xml
+++ b/media2/media2-widget/src/main/res/values-ky/strings.xml
@@ -35,7 +35,7 @@
     <string name="mcv2_overflow_left_button_desc" msgid="2749567167276435888">"Мурунку баскыч тизмесине кайтуу"</string>
     <string name="mcv2_overflow_right_button_desc" msgid="7388732945289831383">"Дагы баскычтарды көрүү"</string>
     <string name="mcv2_seek_bar_desc" msgid="24915699029009384">"Ойнотуу көрсөткүчү"</string>
-    <string name="mcv2_settings_button_desc" msgid="811917224044739656">"Жөндөөлөр"</string>
+    <string name="mcv2_settings_button_desc" msgid="811917224044739656">"Параметрлер"</string>
     <string name="mcv2_cc_is_on" msgid="5427119422911561783">"Коштомо жазуу күйүк. Жашыруу үчүн чыкылдатыңыз."</string>
     <string name="mcv2_cc_is_off" msgid="2380791179816122456">"Коштомо жазуу өчүк. Аны көрсөтүү үчүн чыкылдатыңыз."</string>
     <string name="mcv2_replay_button_desc" msgid="3128622733570179596">"Кайрадан ойнотуу"</string>
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
index 66b5d74..c6be61c 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
@@ -28,7 +28,7 @@
  */
 fun compileWithPrivacySandboxKspCompiler(
     sources: List<Source>,
-    platformStubs: PlatformStubs = PlatformStubs.SDK_RUNTIME_LIBRARY,
+    addSdkRuntimeLibraryStubs: Boolean = true,
     extraProcessorOptions: Map<String, String> = mapOf(),
 ): TestCompilationResult {
     val provider = PrivacySandboxKspCompiler.Provider()
@@ -41,17 +41,12 @@
     }
 
     return CompilationTestHelper.compileAll(
-        sources + platformStubs.sources,
+        if (addSdkRuntimeLibraryStubs) sources + syntheticSdkRuntimeLibraryStubs else sources,
         symbolProcessorProviders = listOf(provider),
         processorOptions = processorOptions,
     )
 }
 
-enum class PlatformStubs(val sources: List<Source>) {
-    API_33(syntheticApi33PrivacySandboxStubs),
-    SDK_RUNTIME_LIBRARY(syntheticSdkRuntimeLibraryStubs),
-}
-
 // SDK Runtime library is not available in AndroidX prebuilts, so while that's the case we use fake
 // stubs to run our compilation tests.
 private val syntheticSdkRuntimeLibraryStubs = listOf(
@@ -95,60 +90,3 @@
         |""".trimMargin()
     )
 )
-
-// PrivacySandbox platform APIs are not available in AndroidX prebuilts nor are they stable, so
-// while that's the case we use fake stubs to run our compilation tests.
-val syntheticApi33PrivacySandboxStubs = listOf(
-    Source.java(
-        "android.app.sdksandbox.SandboxedSdk", """
-        |package android.app.sdksandbox;
-        |
-        |import android.os.IBinder;
-        |
-        |public final class SandboxedSdk {
-        |    public SandboxedSdk(IBinder sdkInterface) {}
-        |    public IBinder getInterface() { throw new RuntimeException("Stub!"); }
-        |}
-        |""".trimMargin()
-    ),
-    Source.java(
-        "android.app.sdksandbox.SandboxedSdkProvider", """
-        |package android.app.sdksandbox;
-        |
-        |import android.content.Context;
-        |import android.os.Bundle;
-        |import android.view.View;
-        |
-        |public abstract class SandboxedSdkProvider {
-        |    public final void attachContext(Context context) {
-        |        throw new RuntimeException("Stub!");
-        |    }
-        |    public final Context getContext() {
-        |        throw new RuntimeException("Stub!");
-        |    }
-        |    public abstract SandboxedSdk onLoadSdk(Bundle params)
-        |        throws LoadSdkException;
-        |
-        |    public void beforeUnloadSdk() {}
-        |
-        |    public abstract View getView(
-        |        Context windowContext, Bundle params, int width, int height);
-        |}
-        |""".trimMargin()
-    ),
-    Source.java(
-        "android.app.sdksandbox.LoadSdkException", """
-        |package android.app.sdksandbox;
-        |
-        |@SuppressWarnings("serial")
-        |public final class LoadSdkException extends Exception {}
-        |""".trimMargin()
-    ),
-    Source.java(
-        "android.app.sdksandbox.SandboxedSdkContext", """
-        |package android.app.sdksandbox;
-        |
-        |public final class SandboxedSdkContext {}
-        |""".trimMargin()
-    ),
-)
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt
index 0c98829..aeeca05 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt
@@ -34,7 +34,7 @@
 
         val result = compileWithPrivacySandboxKspCompiler(
             inputSources,
-            platformStubs = PlatformStubs.API_33,
+            addSdkRuntimeLibraryStubs = false,
             extraProcessorOptions = mapOf("skip_sdk_runtime_compat_library" to "true")
         )
         assertThat(result).succeeds()
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
index d4b5887..60fa29f 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
@@ -118,10 +118,10 @@
     var isAutoScrollActive by remember { mutableStateOf(false) }
 
     AutoScrollSideEffect(
-        timeToDisplaySlideMillis,
-        slideCount,
-        carouselState,
-        focusState,
+        timeToDisplaySlideMillis = timeToDisplaySlideMillis,
+        slideCount = slideCount,
+        carouselState = carouselState,
+        doAutoScroll = shouldPerformAutoScroll(focusState),
         onAutoScrollChange = { isAutoScrollActive = it })
 
     Box(modifier = modifier
@@ -159,6 +159,13 @@
     }
 }
 
+@Composable
+private fun shouldPerformAutoScroll(focusState: FocusState?): Boolean {
+    val carouselIsFocused = focusState?.isFocused ?: false
+    val carouselHasFocus = focusState?.hasFocus ?: false
+    return (carouselIsFocused || carouselHasFocus).not()
+}
+
 @Suppress("IllegalExperimentalApiUsage")
 @OptIn(ExperimentalAnimationApi::class)
 private suspend fun AnimatedVisibilityScope.onAnimationCompletion(action: suspend () -> Unit) {
@@ -172,14 +179,11 @@
     timeToDisplaySlideMillis: Long,
     slideCount: Int,
     carouselState: CarouselState,
-    focusState: FocusState?,
+    doAutoScroll: Boolean,
     onAutoScrollChange: (isAutoScrollActive: Boolean) -> Unit = {},
 ) {
     val currentTimeToDisplaySlideMillis by rememberUpdatedState(timeToDisplaySlideMillis)
     val currentSlideCount by rememberUpdatedState(slideCount)
-    val carouselIsFocused = focusState?.isFocused ?: false
-    val carouselHasFocus = focusState?.hasFocus ?: false
-    val doAutoScroll = (carouselIsFocused || carouselHasFocus).not()
 
     if (doAutoScroll) {
         LaunchedEffect(carouselState) {
diff --git a/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnTest.kt b/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnTest.kt
index 9eb2282..b07f84b3 100644
--- a/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnTest.kt
+++ b/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnTest.kt
@@ -964,6 +964,42 @@
         assertThat(state.centerItemIndex).isEqualTo(24)
         assertThat(state.centerItemScrollOffset).isEqualTo(0)
     }
+
+    @Test
+    fun centerItemIndexPublishesUpdatesOnChangeOnly() {
+        lateinit var state: ScalingLazyListState
+        var recompositionCount = 0
+
+        rule.setContent {
+            state = rememberScalingLazyListState(initialCenterItemIndex = 0)
+
+            WithTouchSlop(0f) {
+                state.centerItemIndex
+                recompositionCount++
+
+                ScalingLazyColumn(
+                    state = state,
+                    modifier = Modifier.testTag(TEST_TAG).requiredSize(
+                        itemSizeDp * 3.5f + defaultItemSpacingDp * 2.5f
+                    ),
+                    autoCentering = AutoCenteringParams(itemIndex = 0)
+                ) {
+                    items(5) {
+                        Box(Modifier.requiredSize(itemSizeDp))
+                    }
+                }
+            }
+        }
+        // TODO(b/210654937): Remove the waitUntil once we no longer need 2 stage initialization
+        rule.waitUntil { state.initialized.value }
+
+        rule.onNodeWithTag(TEST_TAG).performTouchInput {
+            swipeUp(endY = bottom - (itemSizePx.toFloat() + defaultItemSpacingPx.toFloat()))
+        }
+
+        rule.waitForIdle()
+        assertThat(recompositionCount).isEqualTo(2)
+    }
 }
 
 internal const val TestTouchSlop = 18f
diff --git a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyListState.kt b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyListState.kt
index fd00b16..9241dd6 100644
--- a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyListState.kt
+++ b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyListState.kt
@@ -104,14 +104,17 @@
     private val incompleteScrollOffset = mutableStateOf<Int?>(null)
     private val incompleteScrollAnimated = mutableStateOf(false)
 
+    private val _centerItemIndex = derivedStateOf {
+        (layoutInfo as? DefaultScalingLazyListLayoutInfo)?.let {
+            if (it.initialized) it.centerItemIndex else null
+        } ?: initialCenterItemIndex
+    }
+
     /**
      * The index of the item positioned closest to the viewport center
      */
     public val centerItemIndex: Int
-        get() =
-            (layoutInfo as? DefaultScalingLazyListLayoutInfo)?.let {
-                if (it.initialized) it.centerItemIndex else null
-            } ?: initialCenterItemIndex
+        get() = _centerItemIndex.value
 
     internal val topAutoCenteringItemSizePx: Int by derivedStateOf {
         if (extraPaddingPx.value == null || scalingParams.value == null ||
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyColumnTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyColumnTest.kt
index 2549f15..a0ef0e1 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyColumnTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyColumnTest.kt
@@ -964,6 +964,42 @@
         assertThat(state.centerItemIndex).isEqualTo(24)
         assertThat(state.centerItemScrollOffset).isEqualTo(0)
     }
+
+    @Test
+    fun centerItemIndexPublishesUpdatesOnChangeOnly() {
+        lateinit var state: ScalingLazyListState
+        var recompositionCount = 0
+
+        rule.setContent {
+            state = rememberScalingLazyListState(initialCenterItemIndex = 0)
+
+            WithTouchSlop(0f) {
+                state.centerItemIndex
+                recompositionCount++
+
+                ScalingLazyColumn(
+                    state = state,
+                    modifier = Modifier.testTag(TEST_TAG).requiredSize(
+                        itemSizeDp * 3.5f + defaultItemSpacingDp * 2.5f
+                    ),
+                    autoCentering = AutoCenteringParams(itemIndex = 0)
+                ) {
+                    items(5) {
+                        Box(Modifier.requiredSize(itemSizeDp))
+                    }
+                }
+            }
+        }
+        // TODO(b/210654937): Remove the waitUntil once we no longer need 2 stage initialization
+        rule.waitUntil { state.initialized.value }
+
+        rule.onNodeWithTag(TEST_TAG).performTouchInput {
+            swipeUp(endY = bottom - (itemSizePx.toFloat() + defaultItemSpacingPx.toFloat()))
+        }
+
+        rule.waitForIdle()
+        assertThat(recompositionCount).isEqualTo(2)
+    }
 }
 
 internal const val TestTouchSlop = 18f
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListState.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListState.kt
index ea0ba02..2926cca 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListState.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListState.kt
@@ -110,14 +110,17 @@
     private val incompleteScrollOffset = mutableStateOf<Int?>(null)
     private val incompleteScrollAnimated = mutableStateOf(false)
 
+    private val _centerItemIndex = derivedStateOf {
+        (layoutInfo as? DefaultScalingLazyListLayoutInfo)?.let {
+            if (it.initialized) it.centerItemIndex else null
+        } ?: initialCenterItemIndex
+    }
+
     /**
      * The index of the item positioned closest to the viewport center
      */
     public val centerItemIndex: Int
-        get() =
-            (layoutInfo as? DefaultScalingLazyListLayoutInfo)?.let {
-                if (it.initialized) it.centerItemIndex else null
-            } ?: initialCenterItemIndex
+        get() = _centerItemIndex.value
 
     internal val topAutoCenteringItemSizePx: Int by derivedStateOf {
         if (extraPaddingPx.value == null || scalingParams.value == null ||
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt
index 84af177e..ed29bf9 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/SwipeToDismissBox.kt
@@ -40,6 +40,7 @@
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
@@ -159,18 +160,13 @@
                 Modifiers(
                     contentForeground = Modifier
                         .fillMaxSize()
-                        .graphicsLayer(
-                            translationX = translationX,
-                            scaleX = scale,
-                            scaleY = scale,
-                        )
-                        .then(
-                            if (isRound && translationX > 0) {
-                                Modifier.clip(CircleShape)
-                            } else {
-                                Modifier
-                            }
-                        )
+                        .graphicsLayer {
+                            this.translationX = translationX
+                            scaleX = scale
+                            scaleY = scale
+                            clip = isRound && translationX > 0
+                            shape = if (isRound) CircleShape else RectangleShape
+                        }
                         .background(backgroundScrimColor),
                     scrimForeground =
                     Modifier
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index e6f50d0..080e325 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -1 +1,984 @@
 // Signature format: 4.0
+package androidx.wear.protolayout {
+
+  public final class ActionBuilders {
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidBooleanExtra booleanExtra(boolean);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidDoubleExtra doubleExtra(double);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidIntExtra intExtra(int);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidLongExtra longExtra(long);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidStringExtra stringExtra(String);
+  }
+
+  public static interface ActionBuilders.Action {
+  }
+
+  public static interface ActionBuilders.Action.Builder {
+    method public androidx.wear.protolayout.ActionBuilders.Action build();
+  }
+
+  public static final class ActionBuilders.AndroidActivity {
+    method public String getClassName();
+    method public java.util.Map<java.lang.String!,androidx.wear.protolayout.ActionBuilders.AndroidExtra!> getKeyToExtraMapping();
+    method public String getPackageName();
+  }
+
+  public static final class ActionBuilders.AndroidActivity.Builder {
+    ctor public ActionBuilders.AndroidActivity.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity.Builder addKeyToExtraMapping(String, androidx.wear.protolayout.ActionBuilders.AndroidExtra);
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity.Builder setClassName(String);
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity.Builder setPackageName(String);
+  }
+
+  public static final class ActionBuilders.AndroidBooleanExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public boolean getValue();
+  }
+
+  public static final class ActionBuilders.AndroidBooleanExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidBooleanExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidBooleanExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidBooleanExtra.Builder setValue(boolean);
+  }
+
+  public static final class ActionBuilders.AndroidDoubleExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public double getValue();
+  }
+
+  public static final class ActionBuilders.AndroidDoubleExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidDoubleExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidDoubleExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidDoubleExtra.Builder setValue(double);
+  }
+
+  public static interface ActionBuilders.AndroidExtra {
+  }
+
+  public static interface ActionBuilders.AndroidExtra.Builder {
+    method public androidx.wear.protolayout.ActionBuilders.AndroidExtra build();
+  }
+
+  public static final class ActionBuilders.AndroidIntExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public int getValue();
+  }
+
+  public static final class ActionBuilders.AndroidIntExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidIntExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidIntExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidIntExtra.Builder setValue(int);
+  }
+
+  public static final class ActionBuilders.AndroidLongExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public long getValue();
+  }
+
+  public static final class ActionBuilders.AndroidLongExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidLongExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidLongExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidLongExtra.Builder setValue(long);
+  }
+
+  public static final class ActionBuilders.AndroidStringExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public String getValue();
+  }
+
+  public static final class ActionBuilders.AndroidStringExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidStringExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidStringExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidStringExtra.Builder setValue(String);
+  }
+
+  public static final class ActionBuilders.LaunchAction implements androidx.wear.protolayout.ActionBuilders.Action {
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity? getAndroidActivity();
+  }
+
+  public static final class ActionBuilders.LaunchAction.Builder implements androidx.wear.protolayout.ActionBuilders.Action.Builder {
+    ctor public ActionBuilders.LaunchAction.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.LaunchAction build();
+    method public androidx.wear.protolayout.ActionBuilders.LaunchAction.Builder setAndroidActivity(androidx.wear.protolayout.ActionBuilders.AndroidActivity);
+  }
+
+  public static final class ActionBuilders.LoadAction implements androidx.wear.protolayout.ActionBuilders.Action {
+    method public androidx.wear.protolayout.StateBuilders.State? getRequestState();
+  }
+
+  public static final class ActionBuilders.LoadAction.Builder implements androidx.wear.protolayout.ActionBuilders.Action.Builder {
+    ctor public ActionBuilders.LoadAction.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.LoadAction build();
+    method public androidx.wear.protolayout.ActionBuilders.LoadAction.Builder setRequestState(androidx.wear.protolayout.StateBuilders.State);
+  }
+
+  public final class ColorBuilders {
+    method public static androidx.wear.protolayout.ColorBuilders.ColorProp argb(@ColorInt int);
+  }
+
+  public static final class ColorBuilders.ColorProp {
+    method @ColorInt public int getArgb();
+  }
+
+  public static final class ColorBuilders.ColorProp.Builder {
+    ctor public ColorBuilders.ColorProp.Builder();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp build();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp.Builder setArgb(@ColorInt int);
+  }
+
+  public final class DeviceParametersBuilders {
+    field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
+    field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
+    field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
+    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
+    field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters {
+    method public int getDevicePlatform();
+    method @FloatRange(from=0.0, fromInclusive=false, toInclusive=false) public float getScreenDensity();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
+    method public int getScreenShape();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters.Builder {
+    ctor public DeviceParametersBuilders.DeviceParameters.Builder();
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters build();
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setDevicePlatform(int);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenDensity(@FloatRange(from=0.0, fromInclusive=false, toInclusive=false) float);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenHeightDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenShape(int);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenWidthDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+  }
+
+  public final class DimensionBuilders {
+    method public static androidx.wear.protolayout.DimensionBuilders.DegreesProp degrees(float);
+    method public static androidx.wear.protolayout.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(int);
+    method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(float);
+    method public static androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp expand();
+    method public static androidx.wear.protolayout.DimensionBuilders.SpProp sp(@Dimension(unit=androidx.annotation.Dimension.SP) float);
+    method public static androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp wrap();
+  }
+
+  public static interface DimensionBuilders.ContainerDimension {
+  }
+
+  public static interface DimensionBuilders.ContainerDimension.Builder {
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension build();
+  }
+
+  public static final class DimensionBuilders.DegreesProp {
+    method public float getValue();
+  }
+
+  public static final class DimensionBuilders.DegreesProp.Builder {
+    ctor public DimensionBuilders.DegreesProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp.Builder setValue(float);
+  }
+
+  public static final class DimensionBuilders.DpProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension androidx.wear.protolayout.DimensionBuilders.SpacerDimension {
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.DpProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder androidx.wear.protolayout.DimensionBuilders.SpacerDimension.Builder {
+    ctor public DimensionBuilders.DpProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp.Builder setValue(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public static final class DimensionBuilders.EmProp {
+    method public float getValue();
+  }
+
+  public static final class DimensionBuilders.EmProp.Builder {
+    ctor public DimensionBuilders.EmProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.EmProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.EmProp.Builder setValue(float);
+  }
+
+  public static final class DimensionBuilders.ExpandedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension {
+  }
+
+  public static final class DimensionBuilders.ExpandedDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder {
+    ctor public DimensionBuilders.ExpandedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp build();
+  }
+
+  public static interface DimensionBuilders.ImageDimension {
+  }
+
+  public static interface DimensionBuilders.ImageDimension.Builder {
+    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension build();
+  }
+
+  public static final class DimensionBuilders.ProportionalDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ImageDimension {
+    method @IntRange(from=0) public int getAspectRatioHeight();
+    method @IntRange(from=0) public int getAspectRatioWidth();
+  }
+
+  public static final class DimensionBuilders.ProportionalDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder {
+    ctor public DimensionBuilders.ProportionalDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp.Builder setAspectRatioHeight(@IntRange(from=0) int);
+    method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp.Builder setAspectRatioWidth(@IntRange(from=0) int);
+  }
+
+  public static final class DimensionBuilders.SpProp {
+    method @Dimension(unit=androidx.annotation.Dimension.SP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.SpProp.Builder {
+    ctor public DimensionBuilders.SpProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp.Builder setValue(@Dimension(unit=androidx.annotation.Dimension.SP) float);
+  }
+
+  public static interface DimensionBuilders.SpacerDimension {
+  }
+
+  public static interface DimensionBuilders.SpacerDimension.Builder {
+    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension build();
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension {
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder {
+    ctor public DimensionBuilders.WrappedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp build();
+  }
+
+  public final class LayoutElementBuilders {
+    field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
+    field public static final int ARC_ANCHOR_END = 3; // 0x3
+    field public static final int ARC_ANCHOR_START = 1; // 0x1
+    field public static final int ARC_ANCHOR_UNDEFINED = 0; // 0x0
+    field public static final int CONTENT_SCALE_MODE_CROP = 2; // 0x2
+    field public static final int CONTENT_SCALE_MODE_FILL_BOUNDS = 3; // 0x3
+    field public static final int CONTENT_SCALE_MODE_FIT = 1; // 0x1
+    field public static final int CONTENT_SCALE_MODE_UNDEFINED = 0; // 0x0
+    field public static final int FONT_VARIANT_BODY = 2; // 0x2
+    field public static final int FONT_VARIANT_TITLE = 1; // 0x1
+    field public static final int FONT_VARIANT_UNDEFINED = 0; // 0x0
+    field public static final int FONT_WEIGHT_BOLD = 700; // 0x2bc
+    field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
+    field public static final int FONT_WEIGHT_UNDEFINED = 0; // 0x0
+    field public static final int HORIZONTAL_ALIGN_CENTER = 2; // 0x2
+    field public static final int HORIZONTAL_ALIGN_END = 5; // 0x5
+    field public static final int HORIZONTAL_ALIGN_LEFT = 1; // 0x1
+    field public static final int HORIZONTAL_ALIGN_RIGHT = 3; // 0x3
+    field public static final int HORIZONTAL_ALIGN_START = 4; // 0x4
+    field public static final int HORIZONTAL_ALIGN_UNDEFINED = 0; // 0x0
+    field public static final int SPAN_VERTICAL_ALIGN_BOTTOM = 1; // 0x1
+    field public static final int SPAN_VERTICAL_ALIGN_TEXT_BASELINE = 2; // 0x2
+    field public static final int SPAN_VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
+    field public static final int TEXT_ALIGN_CENTER = 2; // 0x2
+    field public static final int TEXT_ALIGN_END = 3; // 0x3
+    field public static final int TEXT_ALIGN_START = 1; // 0x1
+    field public static final int TEXT_ALIGN_UNDEFINED = 0; // 0x0
+    field public static final int TEXT_OVERFLOW_ELLIPSIZE_END = 2; // 0x2
+    field public static final int TEXT_OVERFLOW_TRUNCATE = 1; // 0x1
+    field public static final int TEXT_OVERFLOW_UNDEFINED = 0; // 0x0
+    field public static final int VERTICAL_ALIGN_BOTTOM = 3; // 0x3
+    field public static final int VERTICAL_ALIGN_CENTER = 2; // 0x2
+    field public static final int VERTICAL_ALIGN_TOP = 1; // 0x1
+    field public static final int VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class LayoutElementBuilders.Arc implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement!> getContents();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlign();
+  }
+
+  public static final class LayoutElementBuilders.Arc.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Arc.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(int);
+  }
+
+  public static final class LayoutElementBuilders.ArcAdapter implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getContent();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getRotateContents();
+  }
+
+  public static final class LayoutElementBuilders.ArcAdapter.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcAdapter.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter.Builder setContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter.Builder setRotateContents(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter.Builder setRotateContents(boolean);
+  }
+
+  public static final class LayoutElementBuilders.ArcAnchorTypeProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.ArcAnchorTypeProp.Builder {
+    ctor public LayoutElementBuilders.ArcAnchorTypeProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp.Builder setValue(int);
+  }
+
+  public static interface LayoutElementBuilders.ArcLayoutElement {
+  }
+
+  public static interface LayoutElementBuilders.ArcLayoutElement.Builder {
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement build();
+  }
+
+  public static final class LayoutElementBuilders.ArcLine implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
+  }
+
+  public static final class LayoutElementBuilders.ArcLine.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcLine.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class LayoutElementBuilders.ArcSpacer implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
+  }
+
+  public static final class LayoutElementBuilders.ArcSpacer.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcSpacer.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class LayoutElementBuilders.ArcText implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
+  }
+
+  public static final class LayoutElementBuilders.ArcText.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcText.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setText(String);
+  }
+
+  public static final class LayoutElementBuilders.Box implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getHeight();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp? getHorizontalAlignment();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlignment();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Box.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Box.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setHorizontalAlignment(androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setVerticalAlignment(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setVerticalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+  }
+
+  public static final class LayoutElementBuilders.ColorFilter {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getTint();
+  }
+
+  public static final class LayoutElementBuilders.ColorFilter.Builder {
+    ctor public LayoutElementBuilders.ColorFilter.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ColorFilter build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ColorFilter.Builder setTint(androidx.wear.protolayout.ColorBuilders.ColorProp);
+  }
+
+  public static final class LayoutElementBuilders.Column implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getHeight();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp? getHorizontalAlignment();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Column.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Column.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setHorizontalAlignment(androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+  }
+
+  public static final class LayoutElementBuilders.ContentScaleModeProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.ContentScaleModeProp.Builder {
+    ctor public LayoutElementBuilders.ContentScaleModeProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.FontStyle {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getItalic();
+    method public androidx.wear.protolayout.DimensionBuilders.EmProp? getLetterSpacing();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp? getSize();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getUnderline();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp? getWeight();
+  }
+
+  public static final class LayoutElementBuilders.FontStyle.Builder {
+    ctor public LayoutElementBuilders.FontStyle.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setItalic(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setItalic(boolean);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setLetterSpacing(androidx.wear.protolayout.DimensionBuilders.EmProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setSize(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setUnderline(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setUnderline(boolean);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setWeight(androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setWeight(int);
+  }
+
+  public static class LayoutElementBuilders.FontStyles {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+  }
+
+  public static final class LayoutElementBuilders.FontWeightProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.FontWeightProp.Builder {
+    ctor public LayoutElementBuilders.FontWeightProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.HorizontalAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.HorizontalAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.HorizontalAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.Image implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.ColorFilter? getColorFilter();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp? getContentScaleMode();
+    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getResourceId();
+    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Image.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Image.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setColorFilter(androidx.wear.protolayout.LayoutElementBuilders.ColorFilter);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setContentScaleMode(androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setContentScaleMode(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ImageDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setResourceId(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setResourceId(String);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ImageDimension);
+  }
+
+  public static final class LayoutElementBuilders.Layout {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.Layout fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getRoot();
+  }
+
+  public static final class LayoutElementBuilders.Layout.Builder {
+    ctor public LayoutElementBuilders.Layout.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Layout build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Layout.Builder setRoot(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+  }
+
+  public static interface LayoutElementBuilders.LayoutElement {
+  }
+
+  public static interface LayoutElementBuilders.LayoutElement.Builder {
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement build();
+  }
+
+  public static final class LayoutElementBuilders.Row implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlignment();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Row.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Row.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setVerticalAlignment(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setVerticalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+  }
+
+  public static final class LayoutElementBuilders.Spacer implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Spacer.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Spacer.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
+  }
+
+  public static interface LayoutElementBuilders.Span {
+  }
+
+  public static interface LayoutElementBuilders.Span.Builder {
+    method public androidx.wear.protolayout.LayoutElementBuilders.Span build();
+  }
+
+  public static final class LayoutElementBuilders.SpanImage implements androidx.wear.protolayout.LayoutElementBuilders.Span {
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp? getAlignment();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getResourceId();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.SpanImage.Builder implements androidx.wear.protolayout.LayoutElementBuilders.Span.Builder {
+    ctor public LayoutElementBuilders.SpanImage.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setAlignment(androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.SpanModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setResourceId(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setResourceId(String);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class LayoutElementBuilders.SpanText implements androidx.wear.protolayout.LayoutElementBuilders.Span {
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
+  }
+
+  public static final class LayoutElementBuilders.SpanText.Builder implements androidx.wear.protolayout.LayoutElementBuilders.Span.Builder {
+    ctor public LayoutElementBuilders.SpanText.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.SpanModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setText(String);
+  }
+
+  public static final class LayoutElementBuilders.SpanVerticalAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.SpanVerticalAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.SpanVerticalAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.Spannable implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop? getMaxLines();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp? getMultilineAlignment();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp? getOverflow();
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.Span!> getSpans();
+  }
+
+  public static final class LayoutElementBuilders.Spannable.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Spannable.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder addSpan(androidx.wear.protolayout.LayoutElementBuilders.Span);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMaxLines(androidx.wear.protolayout.TypeBuilders.Int32Prop);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMaxLines(@IntRange(from=1) int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMultilineAlignment(androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMultilineAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setOverflow(androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setOverflow(int);
+  }
+
+  public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop? getMaxLines();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp? getMultilineAlignment();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp? getOverflow();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
+  }
+
+  public static final class LayoutElementBuilders.Text.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Text.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(androidx.wear.protolayout.TypeBuilders.Int32Prop);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(@IntRange(from=1) int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMultilineAlignment(androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMultilineAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setOverflow(androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setOverflow(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setText(String);
+  }
+
+  public static final class LayoutElementBuilders.TextAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.TextAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.TextAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.TextOverflowProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.TextOverflowProp.Builder {
+    ctor public LayoutElementBuilders.TextOverflowProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.VerticalAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.VerticalAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.VerticalAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp.Builder setValue(int);
+  }
+
+  public final class ModifiersBuilders {
+  }
+
+  public static final class ModifiersBuilders.ArcModifiers {
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable? getClickable();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics? getSemantics();
+  }
+
+  public static final class ModifiersBuilders.ArcModifiers.Builder {
+    ctor public ModifiersBuilders.ArcModifiers.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers build();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers.Builder setClickable(androidx.wear.protolayout.ModifiersBuilders.Clickable);
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers.Builder setSemantics(androidx.wear.protolayout.ModifiersBuilders.Semantics);
+  }
+
+  public static final class ModifiersBuilders.Background {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.ModifiersBuilders.Corner? getCorner();
+  }
+
+  public static final class ModifiersBuilders.Background.Builder {
+    ctor public ModifiersBuilders.Background.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Background build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Background.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Background.Builder setCorner(androidx.wear.protolayout.ModifiersBuilders.Corner);
+  }
+
+  public static final class ModifiersBuilders.Border {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getWidth();
+  }
+
+  public static final class ModifiersBuilders.Border.Builder {
+    ctor public ModifiersBuilders.Border.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Border build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Border.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Border.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class ModifiersBuilders.Clickable {
+    method public String getId();
+    method public androidx.wear.protolayout.ActionBuilders.Action? getOnClick();
+  }
+
+  public static final class ModifiersBuilders.Clickable.Builder {
+    ctor public ModifiersBuilders.Clickable.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable.Builder setId(String);
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable.Builder setOnClick(androidx.wear.protolayout.ActionBuilders.Action);
+  }
+
+  public static final class ModifiersBuilders.Corner {
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getRadius();
+  }
+
+  public static final class ModifiersBuilders.Corner.Builder {
+    ctor public ModifiersBuilders.Corner.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Corner build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Corner.Builder setRadius(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class ModifiersBuilders.ElementMetadata {
+    method public byte[] getTagData();
+  }
+
+  public static final class ModifiersBuilders.ElementMetadata.Builder {
+    ctor public ModifiersBuilders.ElementMetadata.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.ElementMetadata build();
+    method public androidx.wear.protolayout.ModifiersBuilders.ElementMetadata.Builder setTagData(byte[]);
+  }
+
+  public static final class ModifiersBuilders.Modifiers {
+    method public androidx.wear.protolayout.ModifiersBuilders.Background? getBackground();
+    method public androidx.wear.protolayout.ModifiersBuilders.Border? getBorder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable? getClickable();
+    method public androidx.wear.protolayout.ModifiersBuilders.ElementMetadata? getMetadata();
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding? getPadding();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics? getSemantics();
+  }
+
+  public static final class ModifiersBuilders.Modifiers.Builder {
+    ctor public ModifiersBuilders.Modifiers.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setBackground(androidx.wear.protolayout.ModifiersBuilders.Background);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setBorder(androidx.wear.protolayout.ModifiersBuilders.Border);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setClickable(androidx.wear.protolayout.ModifiersBuilders.Clickable);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setMetadata(androidx.wear.protolayout.ModifiersBuilders.ElementMetadata);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setPadding(androidx.wear.protolayout.ModifiersBuilders.Padding);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setSemantics(androidx.wear.protolayout.ModifiersBuilders.Semantics);
+  }
+
+  public static final class ModifiersBuilders.Padding {
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getBottom();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getEnd();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getRtlAware();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getStart();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getTop();
+  }
+
+  public static final class ModifiersBuilders.Padding.Builder {
+    ctor public ModifiersBuilders.Padding.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setAll(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setBottom(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setEnd(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setRtlAware(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setRtlAware(boolean);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setStart(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setTop(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class ModifiersBuilders.Semantics {
+    method public String getContentDescription();
+  }
+
+  public static final class ModifiersBuilders.Semantics.Builder {
+    ctor public ModifiersBuilders.Semantics.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics.Builder setContentDescription(String);
+  }
+
+  public static final class ModifiersBuilders.SpanModifiers {
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable? getClickable();
+  }
+
+  public static final class ModifiersBuilders.SpanModifiers.Builder {
+    ctor public ModifiersBuilders.SpanModifiers.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers build();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers.Builder setClickable(androidx.wear.protolayout.ModifiersBuilders.Clickable);
+  }
+
+  public final class ResourceBuilders {
+    field public static final int IMAGE_FORMAT_RGB_565 = 1; // 0x1
+    field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class ResourceBuilders.AndroidImageResourceByResId {
+    method @DrawableRes public int getResourceId();
+  }
+
+  public static final class ResourceBuilders.AndroidImageResourceByResId.Builder {
+    ctor public ResourceBuilders.AndroidImageResourceByResId.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId build();
+    method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId.Builder setResourceId(@DrawableRes int);
+  }
+
+  public static final class ResourceBuilders.ImageResource {
+    method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId? getAndroidResourceByResId();
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource? getInlineResource();
+  }
+
+  public static final class ResourceBuilders.ImageResource.Builder {
+    ctor public ResourceBuilders.ImageResource.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.ImageResource build();
+    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId);
+    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setInlineResource(androidx.wear.protolayout.ResourceBuilders.InlineImageResource);
+  }
+
+  public static final class ResourceBuilders.InlineImageResource {
+    method public byte[] getData();
+    method public int getFormat();
+    method @Dimension(unit=androidx.annotation.Dimension.PX) public int getHeightPx();
+    method @Dimension(unit=androidx.annotation.Dimension.PX) public int getWidthPx();
+  }
+
+  public static final class ResourceBuilders.InlineImageResource.Builder {
+    ctor public ResourceBuilders.InlineImageResource.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource build();
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setData(byte[]);
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setFormat(int);
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setHeightPx(@Dimension(unit=androidx.annotation.Dimension.PX) int);
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setWidthPx(@Dimension(unit=androidx.annotation.Dimension.PX) int);
+  }
+
+  public static final class ResourceBuilders.Resources {
+    method public java.util.Map<java.lang.String!,androidx.wear.protolayout.ResourceBuilders.ImageResource!> getIdToImageMapping();
+    method public String getVersion();
+  }
+
+  public static final class ResourceBuilders.Resources.Builder {
+    ctor public ResourceBuilders.Resources.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.Resources.Builder addIdToImageMapping(String, androidx.wear.protolayout.ResourceBuilders.ImageResource);
+    method public androidx.wear.protolayout.ResourceBuilders.Resources build();
+    method public androidx.wear.protolayout.ResourceBuilders.Resources.Builder setVersion(String);
+  }
+
+  public final class StateBuilders {
+  }
+
+  public static final class StateBuilders.State {
+    method public String getLastClickableId();
+  }
+
+  public static final class StateBuilders.State.Builder {
+    ctor public StateBuilders.State.Builder();
+    method public androidx.wear.protolayout.StateBuilders.State build();
+  }
+
+  public final class TimelineBuilders {
+  }
+
+  public static final class TimelineBuilders.TimeInterval {
+    method public long getEndMillis();
+    method public long getStartMillis();
+  }
+
+  public static final class TimelineBuilders.TimeInterval.Builder {
+    ctor public TimelineBuilders.TimeInterval.Builder();
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval build();
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval.Builder setEndMillis(long);
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval.Builder setStartMillis(long);
+  }
+
+  public static final class TimelineBuilders.Timeline {
+    method public static androidx.wear.protolayout.TimelineBuilders.Timeline fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public java.util.List<androidx.wear.protolayout.TimelineBuilders.TimelineEntry!> getTimelineEntries();
+  }
+
+  public static final class TimelineBuilders.Timeline.Builder {
+    ctor public TimelineBuilders.Timeline.Builder();
+    method public androidx.wear.protolayout.TimelineBuilders.Timeline.Builder addTimelineEntry(androidx.wear.protolayout.TimelineBuilders.TimelineEntry);
+    method public androidx.wear.protolayout.TimelineBuilders.Timeline build();
+  }
+
+  public static final class TimelineBuilders.TimelineEntry {
+    method public static androidx.wear.protolayout.TimelineBuilders.TimelineEntry fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Layout? getLayout();
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval? getValidity();
+  }
+
+  public static final class TimelineBuilders.TimelineEntry.Builder {
+    ctor public TimelineBuilders.TimelineEntry.Builder();
+    method public androidx.wear.protolayout.TimelineBuilders.TimelineEntry build();
+    method public androidx.wear.protolayout.TimelineBuilders.TimelineEntry.Builder setLayout(androidx.wear.protolayout.LayoutElementBuilders.Layout);
+    method public androidx.wear.protolayout.TimelineBuilders.TimelineEntry.Builder setValidity(androidx.wear.protolayout.TimelineBuilders.TimeInterval);
+  }
+
+  public final class TypeBuilders {
+  }
+
+  public static final class TypeBuilders.BoolProp {
+    method public boolean getValue();
+  }
+
+  public static final class TypeBuilders.BoolProp.Builder {
+    ctor public TypeBuilders.BoolProp.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp build();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp.Builder setValue(boolean);
+  }
+
+  public static final class TypeBuilders.FloatProp {
+    method public float getValue();
+  }
+
+  public static final class TypeBuilders.FloatProp.Builder {
+    ctor public TypeBuilders.FloatProp.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.FloatProp build();
+    method public androidx.wear.protolayout.TypeBuilders.FloatProp.Builder setValue(float);
+  }
+
+  public static final class TypeBuilders.Int32Prop {
+    method public int getValue();
+  }
+
+  public static final class TypeBuilders.Int32Prop.Builder {
+    ctor public TypeBuilders.Int32Prop.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop build();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop.Builder setValue(int);
+  }
+
+  public static final class TypeBuilders.StringProp {
+    method public String getValue();
+  }
+
+  public static final class TypeBuilders.StringProp.Builder {
+    ctor public TypeBuilders.StringProp.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp build();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp.Builder setValue(String);
+  }
+
+}
+
diff --git a/wear/protolayout/protolayout/api/public_plus_experimental_current.txt b/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
index e6f50d0..f69773d 100644
--- a/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
+++ b/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
@@ -1 +1,1002 @@
 // Signature format: 4.0
+package androidx.wear.protolayout {
+
+  public final class ActionBuilders {
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidBooleanExtra booleanExtra(boolean);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidDoubleExtra doubleExtra(double);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidIntExtra intExtra(int);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidLongExtra longExtra(long);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidStringExtra stringExtra(String);
+  }
+
+  public static interface ActionBuilders.Action {
+  }
+
+  public static interface ActionBuilders.Action.Builder {
+    method public androidx.wear.protolayout.ActionBuilders.Action build();
+  }
+
+  public static final class ActionBuilders.AndroidActivity {
+    method public String getClassName();
+    method public java.util.Map<java.lang.String!,androidx.wear.protolayout.ActionBuilders.AndroidExtra!> getKeyToExtraMapping();
+    method public String getPackageName();
+  }
+
+  public static final class ActionBuilders.AndroidActivity.Builder {
+    ctor public ActionBuilders.AndroidActivity.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity.Builder addKeyToExtraMapping(String, androidx.wear.protolayout.ActionBuilders.AndroidExtra);
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity.Builder setClassName(String);
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity.Builder setPackageName(String);
+  }
+
+  public static final class ActionBuilders.AndroidBooleanExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public boolean getValue();
+  }
+
+  public static final class ActionBuilders.AndroidBooleanExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidBooleanExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidBooleanExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidBooleanExtra.Builder setValue(boolean);
+  }
+
+  public static final class ActionBuilders.AndroidDoubleExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public double getValue();
+  }
+
+  public static final class ActionBuilders.AndroidDoubleExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidDoubleExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidDoubleExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidDoubleExtra.Builder setValue(double);
+  }
+
+  public static interface ActionBuilders.AndroidExtra {
+  }
+
+  public static interface ActionBuilders.AndroidExtra.Builder {
+    method public androidx.wear.protolayout.ActionBuilders.AndroidExtra build();
+  }
+
+  public static final class ActionBuilders.AndroidIntExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public int getValue();
+  }
+
+  public static final class ActionBuilders.AndroidIntExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidIntExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidIntExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidIntExtra.Builder setValue(int);
+  }
+
+  public static final class ActionBuilders.AndroidLongExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public long getValue();
+  }
+
+  public static final class ActionBuilders.AndroidLongExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidLongExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidLongExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidLongExtra.Builder setValue(long);
+  }
+
+  public static final class ActionBuilders.AndroidStringExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public String getValue();
+  }
+
+  public static final class ActionBuilders.AndroidStringExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidStringExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidStringExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidStringExtra.Builder setValue(String);
+  }
+
+  public static final class ActionBuilders.LaunchAction implements androidx.wear.protolayout.ActionBuilders.Action {
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity? getAndroidActivity();
+  }
+
+  public static final class ActionBuilders.LaunchAction.Builder implements androidx.wear.protolayout.ActionBuilders.Action.Builder {
+    ctor public ActionBuilders.LaunchAction.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.LaunchAction build();
+    method public androidx.wear.protolayout.ActionBuilders.LaunchAction.Builder setAndroidActivity(androidx.wear.protolayout.ActionBuilders.AndroidActivity);
+  }
+
+  public static final class ActionBuilders.LoadAction implements androidx.wear.protolayout.ActionBuilders.Action {
+    method public androidx.wear.protolayout.StateBuilders.State? getRequestState();
+  }
+
+  public static final class ActionBuilders.LoadAction.Builder implements androidx.wear.protolayout.ActionBuilders.Action.Builder {
+    ctor public ActionBuilders.LoadAction.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.LoadAction build();
+    method public androidx.wear.protolayout.ActionBuilders.LoadAction.Builder setRequestState(androidx.wear.protolayout.StateBuilders.State);
+  }
+
+  public final class ColorBuilders {
+    method public static androidx.wear.protolayout.ColorBuilders.ColorProp argb(@ColorInt int);
+  }
+
+  public static final class ColorBuilders.ColorProp {
+    method @ColorInt public int getArgb();
+  }
+
+  public static final class ColorBuilders.ColorProp.Builder {
+    ctor public ColorBuilders.ColorProp.Builder();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp build();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp.Builder setArgb(@ColorInt int);
+  }
+
+  public final class DeviceParametersBuilders {
+    field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
+    field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
+    field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
+    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
+    field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters {
+    method public int getDevicePlatform();
+    method @FloatRange(from=0.0, fromInclusive=false, toInclusive=false) public float getScreenDensity();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
+    method public int getScreenShape();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters.Builder {
+    ctor public DeviceParametersBuilders.DeviceParameters.Builder();
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters build();
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setDevicePlatform(int);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenDensity(@FloatRange(from=0.0, fromInclusive=false, toInclusive=false) float);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenHeightDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenShape(int);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenWidthDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+  }
+
+  public final class DimensionBuilders {
+    method public static androidx.wear.protolayout.DimensionBuilders.DegreesProp degrees(float);
+    method public static androidx.wear.protolayout.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(int);
+    method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(float);
+    method public static androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp expand();
+    method public static androidx.wear.protolayout.DimensionBuilders.SpProp sp(@Dimension(unit=androidx.annotation.Dimension.SP) float);
+    method public static androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp wrap();
+  }
+
+  public static interface DimensionBuilders.ContainerDimension {
+  }
+
+  public static interface DimensionBuilders.ContainerDimension.Builder {
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension build();
+  }
+
+  public static final class DimensionBuilders.DegreesProp {
+    method public float getValue();
+  }
+
+  public static final class DimensionBuilders.DegreesProp.Builder {
+    ctor public DimensionBuilders.DegreesProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp.Builder setValue(float);
+  }
+
+  public static final class DimensionBuilders.DpProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension androidx.wear.protolayout.DimensionBuilders.SpacerDimension {
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.DpProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder androidx.wear.protolayout.DimensionBuilders.SpacerDimension.Builder {
+    ctor public DimensionBuilders.DpProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp.Builder setValue(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public static final class DimensionBuilders.EmProp {
+    method public float getValue();
+  }
+
+  public static final class DimensionBuilders.EmProp.Builder {
+    ctor public DimensionBuilders.EmProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.EmProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.EmProp.Builder setValue(float);
+  }
+
+  public static final class DimensionBuilders.ExpandedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension {
+  }
+
+  public static final class DimensionBuilders.ExpandedDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder {
+    ctor public DimensionBuilders.ExpandedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp build();
+  }
+
+  public static interface DimensionBuilders.ImageDimension {
+  }
+
+  public static interface DimensionBuilders.ImageDimension.Builder {
+    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension build();
+  }
+
+  public static final class DimensionBuilders.ProportionalDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ImageDimension {
+    method @IntRange(from=0) public int getAspectRatioHeight();
+    method @IntRange(from=0) public int getAspectRatioWidth();
+  }
+
+  public static final class DimensionBuilders.ProportionalDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder {
+    ctor public DimensionBuilders.ProportionalDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp.Builder setAspectRatioHeight(@IntRange(from=0) int);
+    method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp.Builder setAspectRatioWidth(@IntRange(from=0) int);
+  }
+
+  public static final class DimensionBuilders.SpProp {
+    method @Dimension(unit=androidx.annotation.Dimension.SP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.SpProp.Builder {
+    ctor public DimensionBuilders.SpProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp.Builder setValue(@Dimension(unit=androidx.annotation.Dimension.SP) float);
+  }
+
+  public static interface DimensionBuilders.SpacerDimension {
+  }
+
+  public static interface DimensionBuilders.SpacerDimension.Builder {
+    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension build();
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension {
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder {
+    ctor public DimensionBuilders.WrappedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp build();
+  }
+
+  public final class LayoutElementBuilders {
+    field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
+    field public static final int ARC_ANCHOR_END = 3; // 0x3
+    field public static final int ARC_ANCHOR_START = 1; // 0x1
+    field public static final int ARC_ANCHOR_UNDEFINED = 0; // 0x0
+    field public static final int CONTENT_SCALE_MODE_CROP = 2; // 0x2
+    field public static final int CONTENT_SCALE_MODE_FILL_BOUNDS = 3; // 0x3
+    field public static final int CONTENT_SCALE_MODE_FIT = 1; // 0x1
+    field public static final int CONTENT_SCALE_MODE_UNDEFINED = 0; // 0x0
+    field public static final int FONT_VARIANT_BODY = 2; // 0x2
+    field public static final int FONT_VARIANT_TITLE = 1; // 0x1
+    field public static final int FONT_VARIANT_UNDEFINED = 0; // 0x0
+    field public static final int FONT_WEIGHT_BOLD = 700; // 0x2bc
+    field @androidx.wear.protolayout.expression.ProtoLayoutExperimental public static final int FONT_WEIGHT_MEDIUM = 500; // 0x1f4
+    field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
+    field public static final int FONT_WEIGHT_UNDEFINED = 0; // 0x0
+    field public static final int HORIZONTAL_ALIGN_CENTER = 2; // 0x2
+    field public static final int HORIZONTAL_ALIGN_END = 5; // 0x5
+    field public static final int HORIZONTAL_ALIGN_LEFT = 1; // 0x1
+    field public static final int HORIZONTAL_ALIGN_RIGHT = 3; // 0x3
+    field public static final int HORIZONTAL_ALIGN_START = 4; // 0x4
+    field public static final int HORIZONTAL_ALIGN_UNDEFINED = 0; // 0x0
+    field public static final int SPAN_VERTICAL_ALIGN_BOTTOM = 1; // 0x1
+    field public static final int SPAN_VERTICAL_ALIGN_TEXT_BASELINE = 2; // 0x2
+    field public static final int SPAN_VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
+    field public static final int TEXT_ALIGN_CENTER = 2; // 0x2
+    field public static final int TEXT_ALIGN_END = 3; // 0x3
+    field public static final int TEXT_ALIGN_START = 1; // 0x1
+    field public static final int TEXT_ALIGN_UNDEFINED = 0; // 0x0
+    field public static final int TEXT_OVERFLOW_ELLIPSIZE_END = 2; // 0x2
+    field public static final int TEXT_OVERFLOW_TRUNCATE = 1; // 0x1
+    field public static final int TEXT_OVERFLOW_UNDEFINED = 0; // 0x0
+    field public static final int VERTICAL_ALIGN_BOTTOM = 3; // 0x3
+    field public static final int VERTICAL_ALIGN_CENTER = 2; // 0x2
+    field public static final int VERTICAL_ALIGN_TOP = 1; // 0x1
+    field public static final int VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class LayoutElementBuilders.Arc implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement!> getContents();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlign();
+  }
+
+  public static final class LayoutElementBuilders.Arc.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Arc.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(int);
+  }
+
+  public static final class LayoutElementBuilders.ArcAdapter implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getContent();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getRotateContents();
+  }
+
+  public static final class LayoutElementBuilders.ArcAdapter.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcAdapter.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter.Builder setContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter.Builder setRotateContents(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter.Builder setRotateContents(boolean);
+  }
+
+  public static final class LayoutElementBuilders.ArcAnchorTypeProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.ArcAnchorTypeProp.Builder {
+    ctor public LayoutElementBuilders.ArcAnchorTypeProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp.Builder setValue(int);
+  }
+
+  public static interface LayoutElementBuilders.ArcLayoutElement {
+  }
+
+  public static interface LayoutElementBuilders.ArcLayoutElement.Builder {
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement build();
+  }
+
+  public static final class LayoutElementBuilders.ArcLine implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
+  }
+
+  public static final class LayoutElementBuilders.ArcLine.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcLine.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class LayoutElementBuilders.ArcSpacer implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
+  }
+
+  public static final class LayoutElementBuilders.ArcSpacer.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcSpacer.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class LayoutElementBuilders.ArcText implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
+  }
+
+  public static final class LayoutElementBuilders.ArcText.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcText.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setText(String);
+  }
+
+  public static final class LayoutElementBuilders.Box implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getHeight();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp? getHorizontalAlignment();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlignment();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Box.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Box.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setHorizontalAlignment(androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setVerticalAlignment(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setVerticalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+  }
+
+  public static final class LayoutElementBuilders.ColorFilter {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getTint();
+  }
+
+  public static final class LayoutElementBuilders.ColorFilter.Builder {
+    ctor public LayoutElementBuilders.ColorFilter.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ColorFilter build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ColorFilter.Builder setTint(androidx.wear.protolayout.ColorBuilders.ColorProp);
+  }
+
+  public static final class LayoutElementBuilders.Column implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getHeight();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp? getHorizontalAlignment();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Column.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Column.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setHorizontalAlignment(androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+  }
+
+  public static final class LayoutElementBuilders.ContentScaleModeProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.ContentScaleModeProp.Builder {
+    ctor public LayoutElementBuilders.ContentScaleModeProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.FontStyle {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getItalic();
+    method public androidx.wear.protolayout.DimensionBuilders.EmProp? getLetterSpacing();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp? getSize();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getUnderline();
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp? getVariant();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp? getWeight();
+  }
+
+  public static final class LayoutElementBuilders.FontStyle.Builder {
+    ctor public LayoutElementBuilders.FontStyle.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setItalic(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setItalic(boolean);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setLetterSpacing(androidx.wear.protolayout.DimensionBuilders.EmProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setSize(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setUnderline(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setUnderline(boolean);
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp);
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setVariant(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setWeight(androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setWeight(int);
+  }
+
+  public static class LayoutElementBuilders.FontStyles {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+  }
+
+  @androidx.wear.protolayout.expression.ProtoLayoutExperimental public static final class LayoutElementBuilders.FontVariantProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.FontVariantProp.Builder {
+    ctor public LayoutElementBuilders.FontVariantProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.FontWeightProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.FontWeightProp.Builder {
+    ctor public LayoutElementBuilders.FontWeightProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.HorizontalAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.HorizontalAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.HorizontalAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.Image implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.ColorFilter? getColorFilter();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp? getContentScaleMode();
+    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getResourceId();
+    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Image.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Image.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setColorFilter(androidx.wear.protolayout.LayoutElementBuilders.ColorFilter);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setContentScaleMode(androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setContentScaleMode(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ImageDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setResourceId(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setResourceId(String);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ImageDimension);
+  }
+
+  public static final class LayoutElementBuilders.Layout {
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public static androidx.wear.protolayout.LayoutElementBuilders.Layout? fromByteArray(byte[]);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.Layout fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getRoot();
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public byte[] toByteArray();
+  }
+
+  public static final class LayoutElementBuilders.Layout.Builder {
+    ctor public LayoutElementBuilders.Layout.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Layout build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Layout.Builder setRoot(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+  }
+
+  public static interface LayoutElementBuilders.LayoutElement {
+  }
+
+  public static interface LayoutElementBuilders.LayoutElement.Builder {
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement build();
+  }
+
+  public static final class LayoutElementBuilders.Row implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlignment();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Row.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Row.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setVerticalAlignment(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setVerticalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+  }
+
+  public static final class LayoutElementBuilders.Spacer implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Spacer.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Spacer.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
+  }
+
+  public static interface LayoutElementBuilders.Span {
+  }
+
+  public static interface LayoutElementBuilders.Span.Builder {
+    method public androidx.wear.protolayout.LayoutElementBuilders.Span build();
+  }
+
+  public static final class LayoutElementBuilders.SpanImage implements androidx.wear.protolayout.LayoutElementBuilders.Span {
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp? getAlignment();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getResourceId();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.SpanImage.Builder implements androidx.wear.protolayout.LayoutElementBuilders.Span.Builder {
+    ctor public LayoutElementBuilders.SpanImage.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setAlignment(androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.SpanModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setResourceId(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setResourceId(String);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class LayoutElementBuilders.SpanText implements androidx.wear.protolayout.LayoutElementBuilders.Span {
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
+  }
+
+  public static final class LayoutElementBuilders.SpanText.Builder implements androidx.wear.protolayout.LayoutElementBuilders.Span.Builder {
+    ctor public LayoutElementBuilders.SpanText.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.SpanModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setText(String);
+  }
+
+  public static final class LayoutElementBuilders.SpanVerticalAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.SpanVerticalAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.SpanVerticalAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.Spannable implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop? getMaxLines();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp? getMultilineAlignment();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp? getOverflow();
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.Span!> getSpans();
+  }
+
+  public static final class LayoutElementBuilders.Spannable.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Spannable.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder addSpan(androidx.wear.protolayout.LayoutElementBuilders.Span);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMaxLines(androidx.wear.protolayout.TypeBuilders.Int32Prop);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMaxLines(@IntRange(from=1) int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMultilineAlignment(androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMultilineAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setOverflow(androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setOverflow(int);
+  }
+
+  public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop? getMaxLines();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp? getMultilineAlignment();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp? getOverflow();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
+  }
+
+  public static final class LayoutElementBuilders.Text.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Text.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(androidx.wear.protolayout.TypeBuilders.Int32Prop);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(@IntRange(from=1) int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMultilineAlignment(androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMultilineAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setOverflow(androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setOverflow(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setText(String);
+  }
+
+  public static final class LayoutElementBuilders.TextAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.TextAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.TextAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.TextOverflowProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.TextOverflowProp.Builder {
+    ctor public LayoutElementBuilders.TextOverflowProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.VerticalAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.VerticalAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.VerticalAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp.Builder setValue(int);
+  }
+
+  public final class ModifiersBuilders {
+  }
+
+  public static final class ModifiersBuilders.ArcModifiers {
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable? getClickable();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics? getSemantics();
+  }
+
+  public static final class ModifiersBuilders.ArcModifiers.Builder {
+    ctor public ModifiersBuilders.ArcModifiers.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers build();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers.Builder setClickable(androidx.wear.protolayout.ModifiersBuilders.Clickable);
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers.Builder setSemantics(androidx.wear.protolayout.ModifiersBuilders.Semantics);
+  }
+
+  public static final class ModifiersBuilders.Background {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.ModifiersBuilders.Corner? getCorner();
+  }
+
+  public static final class ModifiersBuilders.Background.Builder {
+    ctor public ModifiersBuilders.Background.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Background build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Background.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Background.Builder setCorner(androidx.wear.protolayout.ModifiersBuilders.Corner);
+  }
+
+  public static final class ModifiersBuilders.Border {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getWidth();
+  }
+
+  public static final class ModifiersBuilders.Border.Builder {
+    ctor public ModifiersBuilders.Border.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Border build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Border.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Border.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class ModifiersBuilders.Clickable {
+    method public String getId();
+    method public androidx.wear.protolayout.ActionBuilders.Action? getOnClick();
+  }
+
+  public static final class ModifiersBuilders.Clickable.Builder {
+    ctor public ModifiersBuilders.Clickable.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable.Builder setId(String);
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable.Builder setOnClick(androidx.wear.protolayout.ActionBuilders.Action);
+  }
+
+  public static final class ModifiersBuilders.Corner {
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getRadius();
+  }
+
+  public static final class ModifiersBuilders.Corner.Builder {
+    ctor public ModifiersBuilders.Corner.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Corner build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Corner.Builder setRadius(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class ModifiersBuilders.ElementMetadata {
+    method public byte[] getTagData();
+  }
+
+  public static final class ModifiersBuilders.ElementMetadata.Builder {
+    ctor public ModifiersBuilders.ElementMetadata.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.ElementMetadata build();
+    method public androidx.wear.protolayout.ModifiersBuilders.ElementMetadata.Builder setTagData(byte[]);
+  }
+
+  public static final class ModifiersBuilders.Modifiers {
+    method public androidx.wear.protolayout.ModifiersBuilders.Background? getBackground();
+    method public androidx.wear.protolayout.ModifiersBuilders.Border? getBorder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable? getClickable();
+    method public androidx.wear.protolayout.ModifiersBuilders.ElementMetadata? getMetadata();
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding? getPadding();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics? getSemantics();
+  }
+
+  public static final class ModifiersBuilders.Modifiers.Builder {
+    ctor public ModifiersBuilders.Modifiers.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setBackground(androidx.wear.protolayout.ModifiersBuilders.Background);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setBorder(androidx.wear.protolayout.ModifiersBuilders.Border);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setClickable(androidx.wear.protolayout.ModifiersBuilders.Clickable);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setMetadata(androidx.wear.protolayout.ModifiersBuilders.ElementMetadata);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setPadding(androidx.wear.protolayout.ModifiersBuilders.Padding);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setSemantics(androidx.wear.protolayout.ModifiersBuilders.Semantics);
+  }
+
+  public static final class ModifiersBuilders.Padding {
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getBottom();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getEnd();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getRtlAware();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getStart();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getTop();
+  }
+
+  public static final class ModifiersBuilders.Padding.Builder {
+    ctor public ModifiersBuilders.Padding.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setAll(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setBottom(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setEnd(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setRtlAware(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setRtlAware(boolean);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setStart(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setTop(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class ModifiersBuilders.Semantics {
+    method public String getContentDescription();
+  }
+
+  public static final class ModifiersBuilders.Semantics.Builder {
+    ctor public ModifiersBuilders.Semantics.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics.Builder setContentDescription(String);
+  }
+
+  public static final class ModifiersBuilders.SpanModifiers {
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable? getClickable();
+  }
+
+  public static final class ModifiersBuilders.SpanModifiers.Builder {
+    ctor public ModifiersBuilders.SpanModifiers.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers build();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers.Builder setClickable(androidx.wear.protolayout.ModifiersBuilders.Clickable);
+  }
+
+  public final class ResourceBuilders {
+    field public static final int IMAGE_FORMAT_RGB_565 = 1; // 0x1
+    field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class ResourceBuilders.AndroidImageResourceByResId {
+    method @DrawableRes public int getResourceId();
+  }
+
+  public static final class ResourceBuilders.AndroidImageResourceByResId.Builder {
+    ctor public ResourceBuilders.AndroidImageResourceByResId.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId build();
+    method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId.Builder setResourceId(@DrawableRes int);
+  }
+
+  public static final class ResourceBuilders.ImageResource {
+    method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId? getAndroidResourceByResId();
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource? getInlineResource();
+  }
+
+  public static final class ResourceBuilders.ImageResource.Builder {
+    ctor public ResourceBuilders.ImageResource.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.ImageResource build();
+    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId);
+    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setInlineResource(androidx.wear.protolayout.ResourceBuilders.InlineImageResource);
+  }
+
+  public static final class ResourceBuilders.InlineImageResource {
+    method public byte[] getData();
+    method public int getFormat();
+    method @Dimension(unit=androidx.annotation.Dimension.PX) public int getHeightPx();
+    method @Dimension(unit=androidx.annotation.Dimension.PX) public int getWidthPx();
+  }
+
+  public static final class ResourceBuilders.InlineImageResource.Builder {
+    ctor public ResourceBuilders.InlineImageResource.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource build();
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setData(byte[]);
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setFormat(int);
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setHeightPx(@Dimension(unit=androidx.annotation.Dimension.PX) int);
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setWidthPx(@Dimension(unit=androidx.annotation.Dimension.PX) int);
+  }
+
+  public static final class ResourceBuilders.Resources {
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public static androidx.wear.protolayout.ResourceBuilders.Resources? fromByteArray(byte[]);
+    method public java.util.Map<java.lang.String!,androidx.wear.protolayout.ResourceBuilders.ImageResource!> getIdToImageMapping();
+    method public String getVersion();
+    method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public byte[] toByteArray();
+  }
+
+  public static final class ResourceBuilders.Resources.Builder {
+    ctor public ResourceBuilders.Resources.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.Resources.Builder addIdToImageMapping(String, androidx.wear.protolayout.ResourceBuilders.ImageResource);
+    method public androidx.wear.protolayout.ResourceBuilders.Resources build();
+    method public androidx.wear.protolayout.ResourceBuilders.Resources.Builder setVersion(String);
+  }
+
+  public final class StateBuilders {
+  }
+
+  public static final class StateBuilders.State {
+    method public String getLastClickableId();
+  }
+
+  public static final class StateBuilders.State.Builder {
+    ctor public StateBuilders.State.Builder();
+    method public androidx.wear.protolayout.StateBuilders.State build();
+  }
+
+  public final class TimelineBuilders {
+  }
+
+  public static final class TimelineBuilders.TimeInterval {
+    method public long getEndMillis();
+    method public long getStartMillis();
+  }
+
+  public static final class TimelineBuilders.TimeInterval.Builder {
+    ctor public TimelineBuilders.TimeInterval.Builder();
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval build();
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval.Builder setEndMillis(long);
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval.Builder setStartMillis(long);
+  }
+
+  public static final class TimelineBuilders.Timeline {
+    method public static androidx.wear.protolayout.TimelineBuilders.Timeline fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public java.util.List<androidx.wear.protolayout.TimelineBuilders.TimelineEntry!> getTimelineEntries();
+  }
+
+  public static final class TimelineBuilders.Timeline.Builder {
+    ctor public TimelineBuilders.Timeline.Builder();
+    method public androidx.wear.protolayout.TimelineBuilders.Timeline.Builder addTimelineEntry(androidx.wear.protolayout.TimelineBuilders.TimelineEntry);
+    method public androidx.wear.protolayout.TimelineBuilders.Timeline build();
+  }
+
+  public static final class TimelineBuilders.TimelineEntry {
+    method public static androidx.wear.protolayout.TimelineBuilders.TimelineEntry fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Layout? getLayout();
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval? getValidity();
+  }
+
+  public static final class TimelineBuilders.TimelineEntry.Builder {
+    ctor public TimelineBuilders.TimelineEntry.Builder();
+    method public androidx.wear.protolayout.TimelineBuilders.TimelineEntry build();
+    method public androidx.wear.protolayout.TimelineBuilders.TimelineEntry.Builder setLayout(androidx.wear.protolayout.LayoutElementBuilders.Layout);
+    method public androidx.wear.protolayout.TimelineBuilders.TimelineEntry.Builder setValidity(androidx.wear.protolayout.TimelineBuilders.TimeInterval);
+  }
+
+  public final class TypeBuilders {
+  }
+
+  public static final class TypeBuilders.BoolProp {
+    method public boolean getValue();
+  }
+
+  public static final class TypeBuilders.BoolProp.Builder {
+    ctor public TypeBuilders.BoolProp.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp build();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp.Builder setValue(boolean);
+  }
+
+  public static final class TypeBuilders.FloatProp {
+    method public float getValue();
+  }
+
+  public static final class TypeBuilders.FloatProp.Builder {
+    ctor public TypeBuilders.FloatProp.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.FloatProp build();
+    method public androidx.wear.protolayout.TypeBuilders.FloatProp.Builder setValue(float);
+  }
+
+  public static final class TypeBuilders.Int32Prop {
+    method public int getValue();
+  }
+
+  public static final class TypeBuilders.Int32Prop.Builder {
+    ctor public TypeBuilders.Int32Prop.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop build();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop.Builder setValue(int);
+  }
+
+  public static final class TypeBuilders.StringProp {
+    method public String getValue();
+  }
+
+  public static final class TypeBuilders.StringProp.Builder {
+    ctor public TypeBuilders.StringProp.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp build();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp.Builder setValue(String);
+  }
+
+}
+
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index e6f50d0..080e325 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -1 +1,984 @@
 // Signature format: 4.0
+package androidx.wear.protolayout {
+
+  public final class ActionBuilders {
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidBooleanExtra booleanExtra(boolean);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidDoubleExtra doubleExtra(double);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidIntExtra intExtra(int);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidLongExtra longExtra(long);
+    method public static androidx.wear.protolayout.ActionBuilders.AndroidStringExtra stringExtra(String);
+  }
+
+  public static interface ActionBuilders.Action {
+  }
+
+  public static interface ActionBuilders.Action.Builder {
+    method public androidx.wear.protolayout.ActionBuilders.Action build();
+  }
+
+  public static final class ActionBuilders.AndroidActivity {
+    method public String getClassName();
+    method public java.util.Map<java.lang.String!,androidx.wear.protolayout.ActionBuilders.AndroidExtra!> getKeyToExtraMapping();
+    method public String getPackageName();
+  }
+
+  public static final class ActionBuilders.AndroidActivity.Builder {
+    ctor public ActionBuilders.AndroidActivity.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity.Builder addKeyToExtraMapping(String, androidx.wear.protolayout.ActionBuilders.AndroidExtra);
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity.Builder setClassName(String);
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity.Builder setPackageName(String);
+  }
+
+  public static final class ActionBuilders.AndroidBooleanExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public boolean getValue();
+  }
+
+  public static final class ActionBuilders.AndroidBooleanExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidBooleanExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidBooleanExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidBooleanExtra.Builder setValue(boolean);
+  }
+
+  public static final class ActionBuilders.AndroidDoubleExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public double getValue();
+  }
+
+  public static final class ActionBuilders.AndroidDoubleExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidDoubleExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidDoubleExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidDoubleExtra.Builder setValue(double);
+  }
+
+  public static interface ActionBuilders.AndroidExtra {
+  }
+
+  public static interface ActionBuilders.AndroidExtra.Builder {
+    method public androidx.wear.protolayout.ActionBuilders.AndroidExtra build();
+  }
+
+  public static final class ActionBuilders.AndroidIntExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public int getValue();
+  }
+
+  public static final class ActionBuilders.AndroidIntExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidIntExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidIntExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidIntExtra.Builder setValue(int);
+  }
+
+  public static final class ActionBuilders.AndroidLongExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public long getValue();
+  }
+
+  public static final class ActionBuilders.AndroidLongExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidLongExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidLongExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidLongExtra.Builder setValue(long);
+  }
+
+  public static final class ActionBuilders.AndroidStringExtra implements androidx.wear.protolayout.ActionBuilders.AndroidExtra {
+    method public String getValue();
+  }
+
+  public static final class ActionBuilders.AndroidStringExtra.Builder implements androidx.wear.protolayout.ActionBuilders.AndroidExtra.Builder {
+    ctor public ActionBuilders.AndroidStringExtra.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidStringExtra build();
+    method public androidx.wear.protolayout.ActionBuilders.AndroidStringExtra.Builder setValue(String);
+  }
+
+  public static final class ActionBuilders.LaunchAction implements androidx.wear.protolayout.ActionBuilders.Action {
+    method public androidx.wear.protolayout.ActionBuilders.AndroidActivity? getAndroidActivity();
+  }
+
+  public static final class ActionBuilders.LaunchAction.Builder implements androidx.wear.protolayout.ActionBuilders.Action.Builder {
+    ctor public ActionBuilders.LaunchAction.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.LaunchAction build();
+    method public androidx.wear.protolayout.ActionBuilders.LaunchAction.Builder setAndroidActivity(androidx.wear.protolayout.ActionBuilders.AndroidActivity);
+  }
+
+  public static final class ActionBuilders.LoadAction implements androidx.wear.protolayout.ActionBuilders.Action {
+    method public androidx.wear.protolayout.StateBuilders.State? getRequestState();
+  }
+
+  public static final class ActionBuilders.LoadAction.Builder implements androidx.wear.protolayout.ActionBuilders.Action.Builder {
+    ctor public ActionBuilders.LoadAction.Builder();
+    method public androidx.wear.protolayout.ActionBuilders.LoadAction build();
+    method public androidx.wear.protolayout.ActionBuilders.LoadAction.Builder setRequestState(androidx.wear.protolayout.StateBuilders.State);
+  }
+
+  public final class ColorBuilders {
+    method public static androidx.wear.protolayout.ColorBuilders.ColorProp argb(@ColorInt int);
+  }
+
+  public static final class ColorBuilders.ColorProp {
+    method @ColorInt public int getArgb();
+  }
+
+  public static final class ColorBuilders.ColorProp.Builder {
+    ctor public ColorBuilders.ColorProp.Builder();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp build();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp.Builder setArgb(@ColorInt int);
+  }
+
+  public final class DeviceParametersBuilders {
+    field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
+    field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
+    field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
+    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
+    field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters {
+    method public int getDevicePlatform();
+    method @FloatRange(from=0.0, fromInclusive=false, toInclusive=false) public float getScreenDensity();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
+    method public int getScreenShape();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters.Builder {
+    ctor public DeviceParametersBuilders.DeviceParameters.Builder();
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters build();
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setDevicePlatform(int);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenDensity(@FloatRange(from=0.0, fromInclusive=false, toInclusive=false) float);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenHeightDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenShape(int);
+    method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder setScreenWidthDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+  }
+
+  public final class DimensionBuilders {
+    method public static androidx.wear.protolayout.DimensionBuilders.DegreesProp degrees(float);
+    method public static androidx.wear.protolayout.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(int);
+    method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(float);
+    method public static androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp expand();
+    method public static androidx.wear.protolayout.DimensionBuilders.SpProp sp(@Dimension(unit=androidx.annotation.Dimension.SP) float);
+    method public static androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp wrap();
+  }
+
+  public static interface DimensionBuilders.ContainerDimension {
+  }
+
+  public static interface DimensionBuilders.ContainerDimension.Builder {
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension build();
+  }
+
+  public static final class DimensionBuilders.DegreesProp {
+    method public float getValue();
+  }
+
+  public static final class DimensionBuilders.DegreesProp.Builder {
+    ctor public DimensionBuilders.DegreesProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp.Builder setValue(float);
+  }
+
+  public static final class DimensionBuilders.DpProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension androidx.wear.protolayout.DimensionBuilders.SpacerDimension {
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.DpProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder androidx.wear.protolayout.DimensionBuilders.SpacerDimension.Builder {
+    ctor public DimensionBuilders.DpProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp.Builder setValue(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public static final class DimensionBuilders.EmProp {
+    method public float getValue();
+  }
+
+  public static final class DimensionBuilders.EmProp.Builder {
+    ctor public DimensionBuilders.EmProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.EmProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.EmProp.Builder setValue(float);
+  }
+
+  public static final class DimensionBuilders.ExpandedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension {
+  }
+
+  public static final class DimensionBuilders.ExpandedDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder {
+    ctor public DimensionBuilders.ExpandedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp build();
+  }
+
+  public static interface DimensionBuilders.ImageDimension {
+  }
+
+  public static interface DimensionBuilders.ImageDimension.Builder {
+    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension build();
+  }
+
+  public static final class DimensionBuilders.ProportionalDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ImageDimension {
+    method @IntRange(from=0) public int getAspectRatioHeight();
+    method @IntRange(from=0) public int getAspectRatioWidth();
+  }
+
+  public static final class DimensionBuilders.ProportionalDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder {
+    ctor public DimensionBuilders.ProportionalDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp.Builder setAspectRatioHeight(@IntRange(from=0) int);
+    method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp.Builder setAspectRatioWidth(@IntRange(from=0) int);
+  }
+
+  public static final class DimensionBuilders.SpProp {
+    method @Dimension(unit=androidx.annotation.Dimension.SP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.SpProp.Builder {
+    ctor public DimensionBuilders.SpProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp.Builder setValue(@Dimension(unit=androidx.annotation.Dimension.SP) float);
+  }
+
+  public static interface DimensionBuilders.SpacerDimension {
+  }
+
+  public static interface DimensionBuilders.SpacerDimension.Builder {
+    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension build();
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension {
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder {
+    ctor public DimensionBuilders.WrappedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp build();
+  }
+
+  public final class LayoutElementBuilders {
+    field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
+    field public static final int ARC_ANCHOR_END = 3; // 0x3
+    field public static final int ARC_ANCHOR_START = 1; // 0x1
+    field public static final int ARC_ANCHOR_UNDEFINED = 0; // 0x0
+    field public static final int CONTENT_SCALE_MODE_CROP = 2; // 0x2
+    field public static final int CONTENT_SCALE_MODE_FILL_BOUNDS = 3; // 0x3
+    field public static final int CONTENT_SCALE_MODE_FIT = 1; // 0x1
+    field public static final int CONTENT_SCALE_MODE_UNDEFINED = 0; // 0x0
+    field public static final int FONT_VARIANT_BODY = 2; // 0x2
+    field public static final int FONT_VARIANT_TITLE = 1; // 0x1
+    field public static final int FONT_VARIANT_UNDEFINED = 0; // 0x0
+    field public static final int FONT_WEIGHT_BOLD = 700; // 0x2bc
+    field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
+    field public static final int FONT_WEIGHT_UNDEFINED = 0; // 0x0
+    field public static final int HORIZONTAL_ALIGN_CENTER = 2; // 0x2
+    field public static final int HORIZONTAL_ALIGN_END = 5; // 0x5
+    field public static final int HORIZONTAL_ALIGN_LEFT = 1; // 0x1
+    field public static final int HORIZONTAL_ALIGN_RIGHT = 3; // 0x3
+    field public static final int HORIZONTAL_ALIGN_START = 4; // 0x4
+    field public static final int HORIZONTAL_ALIGN_UNDEFINED = 0; // 0x0
+    field public static final int SPAN_VERTICAL_ALIGN_BOTTOM = 1; // 0x1
+    field public static final int SPAN_VERTICAL_ALIGN_TEXT_BASELINE = 2; // 0x2
+    field public static final int SPAN_VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
+    field public static final int TEXT_ALIGN_CENTER = 2; // 0x2
+    field public static final int TEXT_ALIGN_END = 3; // 0x3
+    field public static final int TEXT_ALIGN_START = 1; // 0x1
+    field public static final int TEXT_ALIGN_UNDEFINED = 0; // 0x0
+    field public static final int TEXT_OVERFLOW_ELLIPSIZE_END = 2; // 0x2
+    field public static final int TEXT_OVERFLOW_TRUNCATE = 1; // 0x1
+    field public static final int TEXT_OVERFLOW_UNDEFINED = 0; // 0x0
+    field public static final int VERTICAL_ALIGN_BOTTOM = 3; // 0x3
+    field public static final int VERTICAL_ALIGN_CENTER = 2; // 0x2
+    field public static final int VERTICAL_ALIGN_TOP = 1; // 0x1
+    field public static final int VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class LayoutElementBuilders.Arc implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement!> getContents();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlign();
+  }
+
+  public static final class LayoutElementBuilders.Arc.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Arc.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(int);
+  }
+
+  public static final class LayoutElementBuilders.ArcAdapter implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getContent();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getRotateContents();
+  }
+
+  public static final class LayoutElementBuilders.ArcAdapter.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcAdapter.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter.Builder setContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter.Builder setRotateContents(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAdapter.Builder setRotateContents(boolean);
+  }
+
+  public static final class LayoutElementBuilders.ArcAnchorTypeProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.ArcAnchorTypeProp.Builder {
+    ctor public LayoutElementBuilders.ArcAnchorTypeProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp.Builder setValue(int);
+  }
+
+  public static interface LayoutElementBuilders.ArcLayoutElement {
+  }
+
+  public static interface LayoutElementBuilders.ArcLayoutElement.Builder {
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement build();
+  }
+
+  public static final class LayoutElementBuilders.ArcLine implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
+  }
+
+  public static final class LayoutElementBuilders.ArcLine.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcLine.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class LayoutElementBuilders.ArcSpacer implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
+  }
+
+  public static final class LayoutElementBuilders.ArcSpacer.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcSpacer.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class LayoutElementBuilders.ArcText implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
+  }
+
+  public static final class LayoutElementBuilders.ArcText.Builder implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement.Builder {
+    ctor public LayoutElementBuilders.ArcText.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcText.Builder setText(String);
+  }
+
+  public static final class LayoutElementBuilders.Box implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getHeight();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp? getHorizontalAlignment();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlignment();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Box.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Box.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setHorizontalAlignment(androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setVerticalAlignment(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setVerticalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Box.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+  }
+
+  public static final class LayoutElementBuilders.ColorFilter {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getTint();
+  }
+
+  public static final class LayoutElementBuilders.ColorFilter.Builder {
+    ctor public LayoutElementBuilders.ColorFilter.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ColorFilter build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ColorFilter.Builder setTint(androidx.wear.protolayout.ColorBuilders.ColorProp);
+  }
+
+  public static final class LayoutElementBuilders.Column implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getHeight();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp? getHorizontalAlignment();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Column.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Column.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setHorizontalAlignment(androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Column.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+  }
+
+  public static final class LayoutElementBuilders.ContentScaleModeProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.ContentScaleModeProp.Builder {
+    ctor public LayoutElementBuilders.ContentScaleModeProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.FontStyle {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getItalic();
+    method public androidx.wear.protolayout.DimensionBuilders.EmProp? getLetterSpacing();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp? getSize();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getUnderline();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp? getWeight();
+  }
+
+  public static final class LayoutElementBuilders.FontStyle.Builder {
+    ctor public LayoutElementBuilders.FontStyle.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setItalic(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setItalic(boolean);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setLetterSpacing(androidx.wear.protolayout.DimensionBuilders.EmProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setSize(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setUnderline(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setUnderline(boolean);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setWeight(androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder setWeight(int);
+  }
+
+  public static class LayoutElementBuilders.FontStyles {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+  }
+
+  public static final class LayoutElementBuilders.FontWeightProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.FontWeightProp.Builder {
+    ctor public LayoutElementBuilders.FontWeightProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontWeightProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.HorizontalAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.HorizontalAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.HorizontalAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.Image implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.ColorFilter? getColorFilter();
+    method public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp? getContentScaleMode();
+    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getResourceId();
+    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Image.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Image.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setColorFilter(androidx.wear.protolayout.LayoutElementBuilders.ColorFilter);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setContentScaleMode(androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setContentScaleMode(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ImageDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setResourceId(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setResourceId(String);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Image.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ImageDimension);
+  }
+
+  public static final class LayoutElementBuilders.Layout {
+    method public static androidx.wear.protolayout.LayoutElementBuilders.Layout fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getRoot();
+  }
+
+  public static final class LayoutElementBuilders.Layout.Builder {
+    ctor public LayoutElementBuilders.Layout.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Layout build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Layout.Builder setRoot(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+  }
+
+  public static interface LayoutElementBuilders.LayoutElement {
+  }
+
+  public static interface LayoutElementBuilders.LayoutElement.Builder {
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement build();
+  }
+
+  public static final class LayoutElementBuilders.Row implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlignment();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Row.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Row.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder addContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setVerticalAlignment(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setVerticalAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Row.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+  }
+
+  public static final class LayoutElementBuilders.Spacer implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.Spacer.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Spacer.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
+  }
+
+  public static interface LayoutElementBuilders.Span {
+  }
+
+  public static interface LayoutElementBuilders.Span.Builder {
+    method public androidx.wear.protolayout.LayoutElementBuilders.Span build();
+  }
+
+  public static final class LayoutElementBuilders.SpanImage implements androidx.wear.protolayout.LayoutElementBuilders.Span {
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp? getAlignment();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getHeight();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getResourceId();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getWidth();
+  }
+
+  public static final class LayoutElementBuilders.SpanImage.Builder implements androidx.wear.protolayout.LayoutElementBuilders.Span.Builder {
+    ctor public LayoutElementBuilders.SpanImage.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setAlignment(androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.SpanModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setResourceId(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setResourceId(String);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanImage.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class LayoutElementBuilders.SpanText implements androidx.wear.protolayout.LayoutElementBuilders.Span {
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers? getModifiers();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
+  }
+
+  public static final class LayoutElementBuilders.SpanText.Builder implements androidx.wear.protolayout.LayoutElementBuilders.Span.Builder {
+    ctor public LayoutElementBuilders.SpanText.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.SpanModifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setText(String);
+  }
+
+  public static final class LayoutElementBuilders.SpanVerticalAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.SpanVerticalAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.SpanVerticalAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.SpanVerticalAlignmentProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.Spannable implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop? getMaxLines();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp? getMultilineAlignment();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp? getOverflow();
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.Span!> getSpans();
+  }
+
+  public static final class LayoutElementBuilders.Spannable.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Spannable.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder addSpan(androidx.wear.protolayout.LayoutElementBuilders.Span);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMaxLines(androidx.wear.protolayout.TypeBuilders.Int32Prop);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMaxLines(@IntRange(from=1) int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMultilineAlignment(androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setMultilineAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setOverflow(androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setOverflow(int);
+  }
+
+  public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop? getMaxLines();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp? getMultilineAlignment();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp? getOverflow();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
+  }
+
+  public static final class LayoutElementBuilders.Text.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public LayoutElementBuilders.Text.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(androidx.wear.protolayout.TypeBuilders.Int32Prop);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(@IntRange(from=1) int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMultilineAlignment(androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMultilineAlignment(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setOverflow(androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setOverflow(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setText(String);
+  }
+
+  public static final class LayoutElementBuilders.TextAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.TextAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.TextAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextAlignmentProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.TextOverflowProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.TextOverflowProp.Builder {
+    ctor public LayoutElementBuilders.TextOverflowProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.TextOverflowProp.Builder setValue(int);
+  }
+
+  public static final class LayoutElementBuilders.VerticalAlignmentProp {
+    method public int getValue();
+  }
+
+  public static final class LayoutElementBuilders.VerticalAlignmentProp.Builder {
+    ctor public LayoutElementBuilders.VerticalAlignmentProp.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp build();
+    method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp.Builder setValue(int);
+  }
+
+  public final class ModifiersBuilders {
+  }
+
+  public static final class ModifiersBuilders.ArcModifiers {
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable? getClickable();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics? getSemantics();
+  }
+
+  public static final class ModifiersBuilders.ArcModifiers.Builder {
+    ctor public ModifiersBuilders.ArcModifiers.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers build();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers.Builder setClickable(androidx.wear.protolayout.ModifiersBuilders.Clickable);
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers.Builder setSemantics(androidx.wear.protolayout.ModifiersBuilders.Semantics);
+  }
+
+  public static final class ModifiersBuilders.Background {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.ModifiersBuilders.Corner? getCorner();
+  }
+
+  public static final class ModifiersBuilders.Background.Builder {
+    ctor public ModifiersBuilders.Background.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Background build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Background.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Background.Builder setCorner(androidx.wear.protolayout.ModifiersBuilders.Corner);
+  }
+
+  public static final class ModifiersBuilders.Border {
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getWidth();
+  }
+
+  public static final class ModifiersBuilders.Border.Builder {
+    ctor public ModifiersBuilders.Border.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Border build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Border.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Border.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class ModifiersBuilders.Clickable {
+    method public String getId();
+    method public androidx.wear.protolayout.ActionBuilders.Action? getOnClick();
+  }
+
+  public static final class ModifiersBuilders.Clickable.Builder {
+    ctor public ModifiersBuilders.Clickable.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable.Builder setId(String);
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable.Builder setOnClick(androidx.wear.protolayout.ActionBuilders.Action);
+  }
+
+  public static final class ModifiersBuilders.Corner {
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getRadius();
+  }
+
+  public static final class ModifiersBuilders.Corner.Builder {
+    ctor public ModifiersBuilders.Corner.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Corner build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Corner.Builder setRadius(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class ModifiersBuilders.ElementMetadata {
+    method public byte[] getTagData();
+  }
+
+  public static final class ModifiersBuilders.ElementMetadata.Builder {
+    ctor public ModifiersBuilders.ElementMetadata.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.ElementMetadata build();
+    method public androidx.wear.protolayout.ModifiersBuilders.ElementMetadata.Builder setTagData(byte[]);
+  }
+
+  public static final class ModifiersBuilders.Modifiers {
+    method public androidx.wear.protolayout.ModifiersBuilders.Background? getBackground();
+    method public androidx.wear.protolayout.ModifiersBuilders.Border? getBorder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable? getClickable();
+    method public androidx.wear.protolayout.ModifiersBuilders.ElementMetadata? getMetadata();
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding? getPadding();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics? getSemantics();
+  }
+
+  public static final class ModifiersBuilders.Modifiers.Builder {
+    ctor public ModifiersBuilders.Modifiers.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setBackground(androidx.wear.protolayout.ModifiersBuilders.Background);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setBorder(androidx.wear.protolayout.ModifiersBuilders.Border);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setClickable(androidx.wear.protolayout.ModifiersBuilders.Clickable);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setMetadata(androidx.wear.protolayout.ModifiersBuilders.ElementMetadata);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setPadding(androidx.wear.protolayout.ModifiersBuilders.Padding);
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder setSemantics(androidx.wear.protolayout.ModifiersBuilders.Semantics);
+  }
+
+  public static final class ModifiersBuilders.Padding {
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getBottom();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getEnd();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp? getRtlAware();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getStart();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getTop();
+  }
+
+  public static final class ModifiersBuilders.Padding.Builder {
+    ctor public ModifiersBuilders.Padding.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setAll(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setBottom(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setEnd(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setRtlAware(androidx.wear.protolayout.TypeBuilders.BoolProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setRtlAware(boolean);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setStart(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.ModifiersBuilders.Padding.Builder setTop(androidx.wear.protolayout.DimensionBuilders.DpProp);
+  }
+
+  public static final class ModifiersBuilders.Semantics {
+    method public String getContentDescription();
+  }
+
+  public static final class ModifiersBuilders.Semantics.Builder {
+    ctor public ModifiersBuilders.Semantics.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics build();
+    method public androidx.wear.protolayout.ModifiersBuilders.Semantics.Builder setContentDescription(String);
+  }
+
+  public static final class ModifiersBuilders.SpanModifiers {
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable? getClickable();
+  }
+
+  public static final class ModifiersBuilders.SpanModifiers.Builder {
+    ctor public ModifiersBuilders.SpanModifiers.Builder();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers build();
+    method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers.Builder setClickable(androidx.wear.protolayout.ModifiersBuilders.Clickable);
+  }
+
+  public final class ResourceBuilders {
+    field public static final int IMAGE_FORMAT_RGB_565 = 1; // 0x1
+    field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class ResourceBuilders.AndroidImageResourceByResId {
+    method @DrawableRes public int getResourceId();
+  }
+
+  public static final class ResourceBuilders.AndroidImageResourceByResId.Builder {
+    ctor public ResourceBuilders.AndroidImageResourceByResId.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId build();
+    method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId.Builder setResourceId(@DrawableRes int);
+  }
+
+  public static final class ResourceBuilders.ImageResource {
+    method public androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId? getAndroidResourceByResId();
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource? getInlineResource();
+  }
+
+  public static final class ResourceBuilders.ImageResource.Builder {
+    ctor public ResourceBuilders.ImageResource.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.ImageResource build();
+    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setAndroidResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId);
+    method public androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder setInlineResource(androidx.wear.protolayout.ResourceBuilders.InlineImageResource);
+  }
+
+  public static final class ResourceBuilders.InlineImageResource {
+    method public byte[] getData();
+    method public int getFormat();
+    method @Dimension(unit=androidx.annotation.Dimension.PX) public int getHeightPx();
+    method @Dimension(unit=androidx.annotation.Dimension.PX) public int getWidthPx();
+  }
+
+  public static final class ResourceBuilders.InlineImageResource.Builder {
+    ctor public ResourceBuilders.InlineImageResource.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource build();
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setData(byte[]);
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setFormat(int);
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setHeightPx(@Dimension(unit=androidx.annotation.Dimension.PX) int);
+    method public androidx.wear.protolayout.ResourceBuilders.InlineImageResource.Builder setWidthPx(@Dimension(unit=androidx.annotation.Dimension.PX) int);
+  }
+
+  public static final class ResourceBuilders.Resources {
+    method public java.util.Map<java.lang.String!,androidx.wear.protolayout.ResourceBuilders.ImageResource!> getIdToImageMapping();
+    method public String getVersion();
+  }
+
+  public static final class ResourceBuilders.Resources.Builder {
+    ctor public ResourceBuilders.Resources.Builder();
+    method public androidx.wear.protolayout.ResourceBuilders.Resources.Builder addIdToImageMapping(String, androidx.wear.protolayout.ResourceBuilders.ImageResource);
+    method public androidx.wear.protolayout.ResourceBuilders.Resources build();
+    method public androidx.wear.protolayout.ResourceBuilders.Resources.Builder setVersion(String);
+  }
+
+  public final class StateBuilders {
+  }
+
+  public static final class StateBuilders.State {
+    method public String getLastClickableId();
+  }
+
+  public static final class StateBuilders.State.Builder {
+    ctor public StateBuilders.State.Builder();
+    method public androidx.wear.protolayout.StateBuilders.State build();
+  }
+
+  public final class TimelineBuilders {
+  }
+
+  public static final class TimelineBuilders.TimeInterval {
+    method public long getEndMillis();
+    method public long getStartMillis();
+  }
+
+  public static final class TimelineBuilders.TimeInterval.Builder {
+    ctor public TimelineBuilders.TimeInterval.Builder();
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval build();
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval.Builder setEndMillis(long);
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval.Builder setStartMillis(long);
+  }
+
+  public static final class TimelineBuilders.Timeline {
+    method public static androidx.wear.protolayout.TimelineBuilders.Timeline fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public java.util.List<androidx.wear.protolayout.TimelineBuilders.TimelineEntry!> getTimelineEntries();
+  }
+
+  public static final class TimelineBuilders.Timeline.Builder {
+    ctor public TimelineBuilders.Timeline.Builder();
+    method public androidx.wear.protolayout.TimelineBuilders.Timeline.Builder addTimelineEntry(androidx.wear.protolayout.TimelineBuilders.TimelineEntry);
+    method public androidx.wear.protolayout.TimelineBuilders.Timeline build();
+  }
+
+  public static final class TimelineBuilders.TimelineEntry {
+    method public static androidx.wear.protolayout.TimelineBuilders.TimelineEntry fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Layout? getLayout();
+    method public androidx.wear.protolayout.TimelineBuilders.TimeInterval? getValidity();
+  }
+
+  public static final class TimelineBuilders.TimelineEntry.Builder {
+    ctor public TimelineBuilders.TimelineEntry.Builder();
+    method public androidx.wear.protolayout.TimelineBuilders.TimelineEntry build();
+    method public androidx.wear.protolayout.TimelineBuilders.TimelineEntry.Builder setLayout(androidx.wear.protolayout.LayoutElementBuilders.Layout);
+    method public androidx.wear.protolayout.TimelineBuilders.TimelineEntry.Builder setValidity(androidx.wear.protolayout.TimelineBuilders.TimeInterval);
+  }
+
+  public final class TypeBuilders {
+  }
+
+  public static final class TypeBuilders.BoolProp {
+    method public boolean getValue();
+  }
+
+  public static final class TypeBuilders.BoolProp.Builder {
+    ctor public TypeBuilders.BoolProp.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp build();
+    method public androidx.wear.protolayout.TypeBuilders.BoolProp.Builder setValue(boolean);
+  }
+
+  public static final class TypeBuilders.FloatProp {
+    method public float getValue();
+  }
+
+  public static final class TypeBuilders.FloatProp.Builder {
+    ctor public TypeBuilders.FloatProp.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.FloatProp build();
+    method public androidx.wear.protolayout.TypeBuilders.FloatProp.Builder setValue(float);
+  }
+
+  public static final class TypeBuilders.Int32Prop {
+    method public int getValue();
+  }
+
+  public static final class TypeBuilders.Int32Prop.Builder {
+    ctor public TypeBuilders.Int32Prop.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop build();
+    method public androidx.wear.protolayout.TypeBuilders.Int32Prop.Builder setValue(int);
+  }
+
+  public static final class TypeBuilders.StringProp {
+    method public String getValue();
+  }
+
+  public static final class TypeBuilders.StringProp.Builder {
+    ctor public TypeBuilders.StringProp.Builder();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp build();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp.Builder setValue(String);
+  }
+
+}
+
diff --git a/wear/protolayout/protolayout/build.gradle b/wear/protolayout/protolayout/build.gradle
index 8332043..44f4597 100644
--- a/wear/protolayout/protolayout/build.gradle
+++ b/wear/protolayout/protolayout/build.gradle
@@ -23,10 +23,24 @@
 
 dependencies {
     annotationProcessor(libs.nullaway)
+    api("androidx.annotation:annotation:1.2.0")
+
+    implementation("androidx.annotation:annotation-experimental:1.2.0")
+    implementation(project(":wear:protolayout:protolayout-proto"))
+    implementation(project(":wear:protolayout:protolayout-expression"))
+
+    compileOnly(libs.kotlinStdlib) // For annotation-experimental
 }
 
 android {
     namespace "androidx.wear.protolayout"
+
+    defaultConfig {
+        minSdkVersion 25
+    }
+    buildTypes.all {
+        consumerProguardFiles "proguard-rules.pro"
+    }
 }
 
 androidx {
diff --git a/wear/protolayout/protolayout/proguard-rules.pro b/wear/protolayout/protolayout/proguard-rules.pro
new file mode 100644
index 0000000..014faf2
--- /dev/null
+++ b/wear/protolayout/protolayout/proguard-rules.pro
@@ -0,0 +1,20 @@
+#  Copyright (C) 2022 The Android Open Source Project
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+# libproto uses reflection to deserialize a Proto, which Proguard can't accurately detect.
+# Keep all the class members of any generated messages to ensure we can deserialize properly inside
+# these classes.
+-keepclassmembers class * extends androidx.wear.protolayout.protobuf.GeneratedMessageLite {
+  <fields>;
+}
\ No newline at end of file
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ActionBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ActionBuilders.java
new file mode 100644
index 0000000..55e8cfe
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ActionBuilders.java
@@ -0,0 +1,747 @@
+/*
+ * Copyright 2021-2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import static androidx.wear.protolayout.expression.Preconditions.checkNotNull;
+
+import android.annotation.SuppressLint;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.ActionProto;
+import androidx.wear.protolayout.StateBuilders.State;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/** Builders for actions that can be performed when a user interacts with layout elements. */
+public final class ActionBuilders {
+  private ActionBuilders() {}
+
+  /** Shortcut for building an {@link AndroidStringExtra}. */
+  @NonNull
+  public static AndroidStringExtra stringExtra(@NonNull String value) {
+    return new AndroidStringExtra.Builder().setValue(value).build();
+  }
+
+  /** Shortcut for building an {@link AndroidIntExtra}. */
+  @NonNull
+  public static AndroidIntExtra intExtra(int value) {
+    return new AndroidIntExtra.Builder().setValue(value).build();
+  }
+
+  /** Shortcut for building an {@link AndroidLongExtra}. */
+  @NonNull
+  public static AndroidLongExtra longExtra(long value) {
+    return new AndroidLongExtra.Builder().setValue(value).build();
+  }
+
+  /** Shortcut for building an {@link AndroidDoubleExtra}. */
+  @NonNull
+  public static AndroidDoubleExtra doubleExtra(double value) {
+    return new AndroidDoubleExtra.Builder().setValue(value).build();
+  }
+
+  /** Shortcut for building an {@link AndroidBooleanExtra}. */
+  @NonNull
+  public static AndroidBooleanExtra booleanExtra(boolean value) {
+    return new AndroidBooleanExtra.Builder().setValue(value).build();
+  }
+
+  /** A string value that can be added to an Android intent's extras. */
+  public static final class AndroidStringExtra implements AndroidExtra {
+    private final ActionProto.AndroidStringExtra mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    AndroidStringExtra(
+        ActionProto.AndroidStringExtra impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @NonNull
+    public String getValue() {
+      return mImpl.getValue();
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static AndroidStringExtra fromProto(@NonNull ActionProto.AndroidStringExtra proto) {
+      return new AndroidStringExtra(proto, null);
+    }
+
+    @NonNull
+    ActionProto.AndroidStringExtra toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ActionProto.AndroidExtra toAndroidExtraProto() {
+      return ActionProto.AndroidExtra.newBuilder().setStringVal(mImpl).build();
+    }
+
+    /** Builder for {@link AndroidStringExtra}. */
+    public static final class Builder implements AndroidExtra.Builder {
+      private final ActionProto.AndroidStringExtra.Builder mImpl =
+          ActionProto.AndroidStringExtra.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1281351679);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@NonNull String value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, value.hashCode());
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public AndroidStringExtra build() {
+        return new AndroidStringExtra(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** An integer value that can be added to an Android intent's extras. */
+  public static final class AndroidIntExtra implements AndroidExtra {
+    private final ActionProto.AndroidIntExtra mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    AndroidIntExtra(ActionProto.AndroidIntExtra impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    public int getValue() {
+      return mImpl.getValue();
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static AndroidIntExtra fromProto(@NonNull ActionProto.AndroidIntExtra proto) {
+      return new AndroidIntExtra(proto, null);
+    }
+
+    @NonNull
+    ActionProto.AndroidIntExtra toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ActionProto.AndroidExtra toAndroidExtraProto() {
+      return ActionProto.AndroidExtra.newBuilder().setIntVal(mImpl).build();
+    }
+
+    /** Builder for {@link AndroidIntExtra}. */
+    public static final class Builder implements AndroidExtra.Builder {
+      private final ActionProto.AndroidIntExtra.Builder mImpl =
+          ActionProto.AndroidIntExtra.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1929293734);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(int value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public AndroidIntExtra build() {
+        return new AndroidIntExtra(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A long value that can be added to an Android intent's extras. */
+  public static final class AndroidLongExtra implements AndroidExtra {
+    private final ActionProto.AndroidLongExtra mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    AndroidLongExtra(ActionProto.AndroidLongExtra impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    public long getValue() {
+      return mImpl.getValue();
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static AndroidLongExtra fromProto(@NonNull ActionProto.AndroidLongExtra proto) {
+      return new AndroidLongExtra(proto, null);
+    }
+
+    @NonNull
+    ActionProto.AndroidLongExtra toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ActionProto.AndroidExtra toAndroidExtraProto() {
+      return ActionProto.AndroidExtra.newBuilder().setLongVal(mImpl).build();
+    }
+
+    /** Builder for {@link AndroidLongExtra}. */
+    public static final class Builder implements AndroidExtra.Builder {
+      private final ActionProto.AndroidLongExtra.Builder mImpl =
+          ActionProto.AndroidLongExtra.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-874743180);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(long value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, Long.hashCode(value));
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public AndroidLongExtra build() {
+        return new AndroidLongExtra(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A double value that can be added to an Android intent's extras. */
+  public static final class AndroidDoubleExtra implements AndroidExtra {
+    private final ActionProto.AndroidDoubleExtra mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    AndroidDoubleExtra(
+        ActionProto.AndroidDoubleExtra impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    public double getValue() {
+      return mImpl.getValue();
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static AndroidDoubleExtra fromProto(@NonNull ActionProto.AndroidDoubleExtra proto) {
+      return new AndroidDoubleExtra(proto, null);
+    }
+
+    @NonNull
+    ActionProto.AndroidDoubleExtra toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ActionProto.AndroidExtra toAndroidExtraProto() {
+      return ActionProto.AndroidExtra.newBuilder().setDoubleVal(mImpl).build();
+    }
+
+    /** Builder for {@link AndroidDoubleExtra}. */
+    public static final class Builder implements AndroidExtra.Builder {
+      private final ActionProto.AndroidDoubleExtra.Builder mImpl =
+          ActionProto.AndroidDoubleExtra.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-278689892);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(double value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, Double.hashCode(value));
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public AndroidDoubleExtra build() {
+        return new AndroidDoubleExtra(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A boolean value that can be added to an Android intent's extras. */
+  public static final class AndroidBooleanExtra implements AndroidExtra {
+    private final ActionProto.AndroidBooleanExtra mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+     AndroidBooleanExtra(
+        ActionProto.AndroidBooleanExtra impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    public boolean getValue() {
+      return mImpl.getValue();
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static AndroidBooleanExtra fromProto(@NonNull ActionProto.AndroidBooleanExtra proto) {
+      return new AndroidBooleanExtra(proto, null);
+    }
+
+    @NonNull
+    ActionProto.AndroidBooleanExtra toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ActionProto.AndroidExtra toAndroidExtraProto() {
+      return ActionProto.AndroidExtra.newBuilder().setBooleanVal(mImpl).build();
+    }
+
+    /** Builder for {@link AndroidBooleanExtra}. */
+    public static final class Builder implements AndroidExtra.Builder {
+      private final ActionProto.AndroidBooleanExtra.Builder mImpl =
+          ActionProto.AndroidBooleanExtra.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1238672683);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @SuppressLint("MissingGetterMatchingBuilder")
+      @NonNull
+      public Builder setValue(boolean value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, Boolean.hashCode(value));
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public AndroidBooleanExtra build() {
+        return new AndroidBooleanExtra(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * Interface defining an item that can be included in the extras of an intent that will be sent to
+   * an Android activity. Supports types in android.os.PersistableBundle, excluding arrays.
+   */
+  public interface AndroidExtra {
+    /**
+     * Get the protocol buffer representation of this object.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    ActionProto.AndroidExtra toAndroidExtraProto();
+
+    /**
+     * Get the fingerprint for this object or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    Fingerprint getFingerprint();
+
+    /** Builder to create {@link AndroidExtra} objects. */
+    @SuppressLint("StaticFinalBuilder")
+    interface Builder {
+
+      /** Builds an instance with values accumulated in this Builder. */
+      @NonNull
+      AndroidExtra build();
+    }
+  }
+
+  @NonNull
+  static AndroidExtra androidExtraFromProto(@NonNull ActionProto.AndroidExtra proto) {
+    if (proto.hasStringVal()) {
+      return AndroidStringExtra.fromProto(proto.getStringVal());
+    }
+    if (proto.hasIntVal()) {
+      return AndroidIntExtra.fromProto(proto.getIntVal());
+    }
+    if (proto.hasLongVal()) {
+      return AndroidLongExtra.fromProto(proto.getLongVal());
+    }
+    if (proto.hasDoubleVal()) {
+      return AndroidDoubleExtra.fromProto(proto.getDoubleVal());
+    }
+    if (proto.hasBooleanVal()) {
+      return AndroidBooleanExtra.fromProto(proto.getBooleanVal());
+    }
+    throw new IllegalStateException("Proto was not a recognised instance of AndroidExtra");
+  }
+
+  /** A launch action to send an intent to an Android activity. */
+  public static final class AndroidActivity {
+    private final ActionProto.AndroidActivity mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+     AndroidActivity(ActionProto.AndroidActivity impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the package name to send the intent to, for example, "com.google.weather". Intended for
+     * testing purposes only.
+     */
+    @NonNull
+    public String getPackageName() {
+      return mImpl.getPackageName();
+    }
+
+    /**
+     * Gets the fully qualified class name (including the package) to send the intent to, for
+     * example, "com.google.weather.WeatherOverviewActivity". Intended for testing purposes only.
+     */
+    @NonNull
+    public String getClassName() {
+      return mImpl.getClassName();
+    }
+
+    /** Gets the extras to be included in the intent. Intended for testing purposes only. */
+    @NonNull
+    public Map<String, AndroidExtra> getKeyToExtraMapping() {
+      Map<String, AndroidExtra> map = new HashMap<>();
+      for (Entry<String, ActionProto.AndroidExtra> entry : mImpl.getKeyToExtraMap().entrySet()) {
+        map.put(entry.getKey(), ActionBuilders.androidExtraFromProto(entry.getValue()));
+      }
+      return Collections.unmodifiableMap(map);
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static AndroidActivity fromProto(@NonNull ActionProto.AndroidActivity proto) {
+      return new AndroidActivity(proto, null);
+    }
+
+    @NonNull
+    ActionProto.AndroidActivity toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link AndroidActivity} */
+    public static final class Builder {
+      private final ActionProto.AndroidActivity.Builder mImpl =
+          ActionProto.AndroidActivity.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1939606345);
+
+      public Builder() {}
+
+      /** Sets the package name to send the intent to, for example, "com.google.weather". */
+      @NonNull
+      public Builder setPackageName(@NonNull String packageName) {
+        mImpl.setPackageName(packageName);
+        mFingerprint.recordPropertyUpdate(1, packageName.hashCode());
+        return this;
+      }
+
+      /**
+       * Sets the fully qualified class name (including the package) to send the intent to, for
+       * example, "com.google.weather.WeatherOverviewActivity".
+       */
+      @NonNull
+      public Builder setClassName(@NonNull String className) {
+        mImpl.setClassName(className);
+        mFingerprint.recordPropertyUpdate(2, className.hashCode());
+        return this;
+      }
+
+      /** Adds an entry into the extras to be included in the intent. */
+      @SuppressLint("MissingGetterMatchingBuilder")
+      @NonNull
+      public Builder addKeyToExtraMapping(@NonNull String key, @NonNull AndroidExtra extra) {
+        mImpl.putKeyToExtra(key, extra.toAndroidExtraProto());
+        mFingerprint.recordPropertyUpdate(
+            key.hashCode(), checkNotNull(extra.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public AndroidActivity build() {
+        return new AndroidActivity(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * An action used to launch another activity on the system. This can hold multiple different
+   * underlying action types, which will be picked based on what the underlying runtime believes to
+   * be suitable.
+   */
+  public static final class LaunchAction implements Action {
+    private final ActionProto.LaunchAction mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+     LaunchAction(ActionProto.LaunchAction impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets an action to launch an Android activity. Intended for testing purposes only. */
+    @Nullable
+    public AndroidActivity getAndroidActivity() {
+      if (mImpl.hasAndroidActivity()) {
+        return AndroidActivity.fromProto(mImpl.getAndroidActivity());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static LaunchAction fromProto(@NonNull ActionProto.LaunchAction proto) {
+      return new LaunchAction(proto, null);
+    }
+
+    @NonNull
+    ActionProto.LaunchAction toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ActionProto.Action toActionProto() {
+      return ActionProto.Action.newBuilder().setLaunchAction(mImpl).build();
+    }
+
+    /** Builder for {@link LaunchAction}. */
+    public static final class Builder implements Action.Builder {
+      private final ActionProto.LaunchAction.Builder mImpl = ActionProto.LaunchAction.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(175064445);
+
+      public Builder() {}
+
+      /** Sets an action to launch an Android activity. */
+      @NonNull
+      public Builder setAndroidActivity(@NonNull AndroidActivity androidActivity) {
+        mImpl.setAndroidActivity(androidActivity.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(androidActivity.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public LaunchAction build() {
+        return new LaunchAction(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** An action used to load (or reload) the tile contents. */
+  public static final class LoadAction implements Action {
+    private final ActionProto.LoadAction mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+     LoadAction(ActionProto.LoadAction impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the state to load the next tile with. This will be included in the {@link
+     * androidx.wear.tiles.RequestBuilders.TileRequest} sent after this action is invoked by a
+     * {@link androidx.wear.tiles.ModifiersBuilders.Clickable}. Intended for testing purposes only.
+     */
+    @Nullable
+    public State getRequestState() {
+      if (mImpl.hasRequestState()) {
+        return State.fromProto(mImpl.getRequestState());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static LoadAction fromProto(@NonNull ActionProto.LoadAction proto) {
+      return new LoadAction(proto, null);
+    }
+
+    @NonNull
+    ActionProto.LoadAction toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ActionProto.Action toActionProto() {
+      return ActionProto.Action.newBuilder().setLoadAction(mImpl).build();
+    }
+
+    /** Builder for {@link LoadAction}. */
+    public static final class Builder implements Action.Builder {
+      private final ActionProto.LoadAction.Builder mImpl = ActionProto.LoadAction.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1728161517);
+
+      public Builder() {}
+
+      /**
+       * Sets the state to load the next tile with. This will be included in the {@link
+       * androidx.wear.tiles.RequestBuilders.TileRequest} sent after this action is invoked by a
+       * {@link androidx.wear.tiles.ModifiersBuilders.Clickable}.
+       */
+      @NonNull
+      public Builder setRequestState(@NonNull State requestState) {
+        mImpl.setRequestState(requestState.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(requestState.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public LoadAction build() {
+        return new LoadAction(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** Interface defining an action that can be used by a layout element. */
+  public interface Action {
+    /**
+     * Get the protocol buffer representation of this object.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    ActionProto.Action toActionProto();
+
+    /**
+     * Get the fingerprint for this object or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    Fingerprint getFingerprint();
+
+    /** Builder to create {@link Action} objects. */
+    @SuppressLint("StaticFinalBuilder")
+    interface Builder {
+
+      /** Builds an instance with values accumulated in this Builder. */
+      @NonNull
+      Action build();
+    }
+  }
+
+  @NonNull
+  static Action actionFromProto(@NonNull ActionProto.Action proto) {
+    if (proto.hasLaunchAction()) {
+      return LaunchAction.fromProto(proto.getLaunchAction());
+    }
+    if (proto.hasLoadAction()) {
+      return LoadAction.fromProto(proto.getLoadAction());
+    }
+    throw new IllegalStateException("Proto was not a recognised instance of Action");
+  }
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ColorBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ColorBuilders.java
new file mode 100644
index 0000000..f07ee06
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ColorBuilders.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2021-2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.ColorProto;
+
+/** Builders for color utilities for layout elements. */
+public final class ColorBuilders {
+  private ColorBuilders() {}
+
+  /** Shortcut for building a {@link ColorProp} using an ARGB value. */
+  @NonNull
+  public static ColorProp argb(@ColorInt int colorArgb) {
+    return new ColorProp.Builder().setArgb(colorArgb).build();
+  }
+
+  /** A property defining a color. */
+  public static final class ColorProp {
+    private final ColorProto.ColorProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ColorProp(ColorProto.ColorProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the color value, in ARGB format. Intended for testing purposes only. */
+    @ColorInt
+    public int getArgb() {
+      return mImpl.getArgb();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ColorProp fromProto(@NonNull ColorProto.ColorProp proto) {
+      return new ColorProp(proto, null);
+    }
+
+    @NonNull
+    ColorProto.ColorProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link ColorProp} */
+    public static final class Builder {
+      private final ColorProto.ColorProp.Builder mImpl = ColorProto.ColorProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1332287496);
+
+      public Builder() {}
+
+      /** Sets the color value, in ARGB format. */
+      @NonNull
+      public Builder setArgb(@ColorInt int argb) {
+        mImpl.setArgb(argb);
+        mFingerprint.recordPropertyUpdate(1, argb);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public ColorProp build() {
+        return new ColorProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/DeviceParametersBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/DeviceParametersBuilders.java
new file mode 100644
index 0000000..b46c5a4
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/DeviceParametersBuilders.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2021-2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import static androidx.annotation.Dimension.DP;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.FloatRange;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.wear.protolayout.proto.DeviceParametersProto;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Builders for request messages used to fetch tiles and resources. */
+public final class DeviceParametersBuilders {
+  private DeviceParametersBuilders() {}
+
+  /**
+   * The platform of the device requesting a tile.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({DEVICE_PLATFORM_UNDEFINED, DEVICE_PLATFORM_WEAR_OS})
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface DevicePlatform {}
+
+  /** Device platform is undefined. */
+  public static final int DEVICE_PLATFORM_UNDEFINED = 0;
+
+  /** Device is a Wear OS device. */
+  public static final int DEVICE_PLATFORM_WEAR_OS = 1;
+
+  /**
+   * The shape of a screen.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({SCREEN_SHAPE_UNDEFINED, SCREEN_SHAPE_ROUND, SCREEN_SHAPE_RECT})
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface ScreenShape {}
+
+  /** Screen shape is undefined. */
+  public static final int SCREEN_SHAPE_UNDEFINED = 0;
+
+  /** A round screen (typically found on most Wear devices). */
+  public static final int SCREEN_SHAPE_ROUND = 1;
+
+  /** Rectangular screens. */
+  public static final int SCREEN_SHAPE_RECT = 2;
+
+  /**
+   * Parameters describing the device requesting a tile update. This contains physical and logical
+   * characteristics about the device (e.g. screen size and density, etc).
+   */
+  public static final class DeviceParameters {
+    private final DeviceParametersProto.DeviceParameters mImpl;
+
+    private DeviceParameters(DeviceParametersProto.DeviceParameters impl) {
+      this.mImpl = impl;
+    }
+
+    /** Gets width of the device's screen in DP. */
+    @Dimension(unit = DP)
+    public int getScreenWidthDp() {
+      return mImpl.getScreenWidthDp();
+    }
+
+    /** Gets height of the device's screen in DP. */
+    @Dimension(unit = DP)
+    public int getScreenHeightDp() {
+      return mImpl.getScreenHeightDp();
+    }
+
+    /**
+     * Gets density of the display. This value is the scaling factor to get from DP to Pixels (px =
+     * dp * density).
+     */
+    @FloatRange(from = 0.0, fromInclusive = false, toInclusive = false)
+    public float getScreenDensity() {
+      return mImpl.getScreenDensity();
+    }
+
+    /** Gets the platform of the device. */
+    @DevicePlatform
+    public int getDevicePlatform() {
+      return mImpl.getDevicePlatform().getNumber();
+    }
+
+    /** Gets the shape of the device's screen. */
+    @ScreenShape
+    public int getScreenShape() {
+      return mImpl.getScreenShape().getNumber();
+    }
+
+    @NonNull
+    static DeviceParameters fromProto(@NonNull DeviceParametersProto.DeviceParameters proto) {
+      return new DeviceParameters(proto);
+    }
+
+    @NonNull
+    DeviceParametersProto.DeviceParameters toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link DeviceParameters} */
+    public static final class Builder {
+      private final DeviceParametersProto.DeviceParameters.Builder mImpl =
+          DeviceParametersProto.DeviceParameters.newBuilder();
+
+      public Builder() {}
+
+      /** Sets width of the device's screen in DP. */
+      @NonNull
+      public Builder setScreenWidthDp(@Dimension(unit = DP) int screenWidthDp) {
+        mImpl.setScreenWidthDp(screenWidthDp);
+        return this;
+      }
+
+      /** Sets height of the device's screen in DP. */
+      @NonNull
+      public Builder setScreenHeightDp(@Dimension(unit = DP) int screenHeightDp) {
+        mImpl.setScreenHeightDp(screenHeightDp);
+        return this;
+      }
+
+      /**
+       * Sets density of the display. This value is the scaling factor to get from DP to Pixels (px
+       * = dp * density).
+       */
+      @NonNull
+      public Builder setScreenDensity(
+          @FloatRange(from = 0.0, fromInclusive = false, toInclusive = false) float screenDensity) {
+        mImpl.setScreenDensity(screenDensity);
+        return this;
+      }
+
+      /** Sets the platform of the device. */
+      @NonNull
+      public Builder setDevicePlatform(@DevicePlatform int devicePlatform) {
+        mImpl.setDevicePlatform(DeviceParametersProto.DevicePlatform.forNumber(devicePlatform));
+        return this;
+      }
+
+      /** Sets the shape of the device's screen. */
+      @NonNull
+      public Builder setScreenShape(@ScreenShape int screenShape) {
+        mImpl.setScreenShape(DeviceParametersProto.ScreenShape.forNumber(screenShape));
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public DeviceParameters build() {
+        return DeviceParameters.fromProto(mImpl.build());
+      }
+    }
+  }
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/DimensionBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/DimensionBuilders.java
new file mode 100644
index 0000000..e900f65
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/DimensionBuilders.java
@@ -0,0 +1,692 @@
+/*
+ * Copyright 2021-2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import static androidx.annotation.Dimension.DP;
+import static androidx.annotation.Dimension.SP;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.DisplayMetrics;
+import androidx.annotation.Dimension;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.DimensionProto;
+
+/** Builders for dimensions for layout elements. */
+public final class DimensionBuilders {
+  private DimensionBuilders() {}
+
+  private static final ExpandedDimensionProp EXPAND = new ExpandedDimensionProp.Builder().build();
+  private static final WrappedDimensionProp WRAP = new WrappedDimensionProp.Builder().build();
+
+  /** Shortcut for building a {@link DpProp} using a measurement in DP. */
+  @NonNull
+  public static DpProp dp(@Dimension(unit = DP) float valueDp) {
+    return new DpProp.Builder().setValue(valueDp).build();
+  }
+
+  /** Shortcut for building a {@link SpProp} using a measurement in SP. */
+  @NonNull
+  public static SpProp sp(@Dimension(unit = SP) float valueSp) {
+    return new SpProp.Builder().setValue(valueSp).build();
+  }
+
+  /** Shortcut for building a {@link EmProp} using a measurement in EM. */
+  @NonNull
+  public static EmProp em(int valueEm) {
+    return new EmProp.Builder().setValue(valueEm).build();
+  }
+
+  /** Shortcut for building a {@link EmProp} using a measurement in EM. */
+  @NonNull
+  public static EmProp em(float valueEm) {
+    return new EmProp.Builder().setValue(valueEm).build();
+  }
+
+  /** Shortcut for building an {@link DegreesProp} using a measurement in degrees. */
+  @NonNull
+  public static DegreesProp degrees(float valueDegrees) {
+    return new DegreesProp.Builder().setValue(valueDegrees).build();
+  }
+
+  /**
+   * Shortcut for building an {@link ExpandedDimensionProp} that will expand to the size of its
+   * parent.
+   */
+  @NonNull
+  public static ExpandedDimensionProp expand() {
+    return EXPAND;
+  }
+
+  /**
+   * Shortcut for building an {@link WrappedDimensionProp} that will shrink to the size of its
+   * children.
+   */
+  @NonNull
+  public static WrappedDimensionProp wrap() {
+    return WRAP;
+  }
+
+  /** A type for linear dimensions, measured in dp. */
+  public static final class DpProp implements ContainerDimension, ImageDimension, SpacerDimension {
+    private final DimensionProto.DpProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    DpProp(DimensionProto.DpProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value, in dp. Intended for testing purposes only. */
+    @Dimension(unit = DP)
+    public float getValue() {
+      return mImpl.getValue();
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static DpProp fromProto(@NonNull DimensionProto.DpProp proto) {
+      return new DpProp(proto, null);
+    }
+
+    @NonNull
+    DimensionProto.DpProp toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public DimensionProto.ContainerDimension toContainerDimensionProto() {
+      return DimensionProto.ContainerDimension.newBuilder().setLinearDimension(mImpl).build();
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public DimensionProto.ImageDimension toImageDimensionProto() {
+      return DimensionProto.ImageDimension.newBuilder().setLinearDimension(mImpl).build();
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public DimensionProto.SpacerDimension toSpacerDimensionProto() {
+      return DimensionProto.SpacerDimension.newBuilder().setLinearDimension(mImpl).build();
+    }
+
+    /** Builder for {@link DpProp}. */
+    public static final class Builder
+        implements ContainerDimension.Builder, ImageDimension.Builder, SpacerDimension.Builder {
+      private final DimensionProto.DpProp.Builder mImpl = DimensionProto.DpProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(752970309);
+
+      public Builder() {}
+
+      /** Sets the value, in dp. */
+      @NonNull
+      public Builder setValue(@Dimension(unit = DP) float value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(value));
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public DpProp build() {
+        return new DpProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A type for font sizes, measured in sp. */
+  public static final class SpProp {
+    private final DimensionProto.SpProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    SpProp(DimensionProto.SpProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value, in sp. Intended for testing purposes only. */
+    @Dimension(unit = SP)
+    public float getValue() {
+      return mImpl.getValue();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static SpProp fromProto(@NonNull DimensionProto.SpProp proto) {
+      return new SpProp(proto, null);
+    }
+
+    @NonNull
+    DimensionProto.SpProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link SpProp} */
+    public static final class Builder {
+      private final DimensionProto.SpProp.Builder mImpl = DimensionProto.SpProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-2144685857);
+
+      public Builder() {}
+
+      /** Sets the value, in sp. */
+      @NonNull
+      public Builder setValue(@Dimension(unit = SP) float value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(2, Float.floatToIntBits(value));
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public SpProp build() {
+        return new SpProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A type for font spacing, measured in em. */
+  public static final class EmProp {
+    private final DimensionProto.EmProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    EmProp(DimensionProto.EmProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value, in em. Intended for testing purposes only. */
+    public float getValue() {
+      return mImpl.getValue();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static EmProp fromProto(@NonNull DimensionProto.EmProp proto) {
+      return new EmProp(proto, null);
+    }
+
+    @NonNull
+    DimensionProto.EmProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link EmProp} */
+    public static final class Builder {
+      private final DimensionProto.EmProp.Builder mImpl = DimensionProto.EmProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1628313311);
+
+      public Builder() {}
+
+      /** Sets the value, in em. */
+      @NonNull
+      public Builder setValue(float value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(value));
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public EmProp build() {
+        return new EmProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A type for angular dimensions, measured in degrees. */
+  public static final class DegreesProp {
+    private final DimensionProto.DegreesProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    DegreesProp(DimensionProto.DegreesProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value, in degrees. Intended for testing purposes only. */
+    public float getValue() {
+      return mImpl.getValue();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static DegreesProp fromProto(@NonNull DimensionProto.DegreesProp proto) {
+      return new DegreesProp(proto, null);
+    }
+
+    @NonNull
+    DimensionProto.DegreesProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link DegreesProp} */
+    public static final class Builder {
+      private final DimensionProto.DegreesProp.Builder mImpl =
+          DimensionProto.DegreesProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(405060347);
+
+      public Builder() {}
+
+      /** Sets the value, in degrees. */
+      @NonNull
+      public Builder setValue(float value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(value));
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public DegreesProp build() {
+        return new DegreesProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * A type for a dimension that fills all the space it can (i.e. MATCH_PARENT in Android parlance).
+   */
+  public static final class ExpandedDimensionProp implements ContainerDimension, ImageDimension {
+    private final DimensionProto.ExpandedDimensionProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ExpandedDimensionProp(
+        DimensionProto.ExpandedDimensionProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ExpandedDimensionProp fromProto(@NonNull DimensionProto.ExpandedDimensionProp proto) {
+      return new ExpandedDimensionProp(proto, null);
+    }
+
+    @NonNull
+    DimensionProto.ExpandedDimensionProp toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public DimensionProto.ContainerDimension toContainerDimensionProto() {
+      return DimensionProto.ContainerDimension.newBuilder().setExpandedDimension(mImpl).build();
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public DimensionProto.ImageDimension toImageDimensionProto() {
+      return DimensionProto.ImageDimension.newBuilder().setExpandedDimension(mImpl).build();
+    }
+
+    /** Builder for {@link ExpandedDimensionProp}. */
+    public static final class Builder
+        implements ContainerDimension.Builder, ImageDimension.Builder {
+      private final DimensionProto.ExpandedDimensionProp.Builder mImpl =
+          DimensionProto.ExpandedDimensionProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1053378170);
+
+      public Builder() {}
+
+      @Override
+      @NonNull
+      public ExpandedDimensionProp build() {
+        return new ExpandedDimensionProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * A type for a dimension that sizes itself to the size of its children (i.e. WRAP_CONTENT in
+   * Android parlance).
+   */
+  public static final class WrappedDimensionProp implements ContainerDimension {
+    private final DimensionProto.WrappedDimensionProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    WrappedDimensionProp(
+        DimensionProto.WrappedDimensionProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static WrappedDimensionProp fromProto(@NonNull DimensionProto.WrappedDimensionProp proto) {
+      return new WrappedDimensionProp(proto, null);
+    }
+
+    @NonNull
+    DimensionProto.WrappedDimensionProp toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public DimensionProto.ContainerDimension toContainerDimensionProto() {
+      return DimensionProto.ContainerDimension.newBuilder().setWrappedDimension(mImpl).build();
+    }
+
+    /** Builder for {@link WrappedDimensionProp}. */
+    public static final class Builder implements ContainerDimension.Builder {
+      private final DimensionProto.WrappedDimensionProp.Builder mImpl =
+          DimensionProto.WrappedDimensionProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-113456542);
+
+      public Builder() {}
+
+      @Override
+      @NonNull
+      public WrappedDimensionProp build() {
+        return new WrappedDimensionProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * A type for a dimension that scales itself proportionally to another dimension such that the
+   * aspect ratio defined by the given width and height values is preserved.
+   *
+   * <p>Note that the width and height are unitless; only their ratio is relevant. This allows for
+   * specifying an element's size using common ratios (e.g. width=4, height=3), or to allow an
+   * element to be resized proportionally based on the size of an underlying asset (e.g. an 800x600
+   * image being added to a smaller container and resized accordingly).
+   */
+  public static final class ProportionalDimensionProp implements ImageDimension {
+    private final DimensionProto.ProportionalDimensionProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ProportionalDimensionProp(
+        DimensionProto.ProportionalDimensionProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the width to be used when calculating the aspect ratio to preserve. Intended for testing
+     * purposes only.
+     */
+    @IntRange(from = 0)
+    public int getAspectRatioWidth() {
+      return mImpl.getAspectRatioWidth();
+    }
+
+    /**
+     * Gets the height to be used when calculating the aspect ratio ratio to preserve. Intended for
+     * testing purposes only.
+     */
+    @IntRange(from = 0)
+    public int getAspectRatioHeight() {
+      return mImpl.getAspectRatioHeight();
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ProportionalDimensionProp fromProto(
+        @NonNull DimensionProto.ProportionalDimensionProp proto) {
+      return new ProportionalDimensionProp(proto, null);
+    }
+
+    @NonNull
+    DimensionProto.ProportionalDimensionProp toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public DimensionProto.ImageDimension toImageDimensionProto() {
+      return DimensionProto.ImageDimension.newBuilder().setProportionalDimension(mImpl).build();
+    }
+
+    /** Builder for {@link ProportionalDimensionProp}. */
+    public static final class Builder implements ImageDimension.Builder {
+      private final DimensionProto.ProportionalDimensionProp.Builder mImpl =
+          DimensionProto.ProportionalDimensionProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-2079046835);
+
+      public Builder() {}
+
+      /** Sets the width to be used when calculating the aspect ratio to preserve. */
+      @NonNull
+      public Builder setAspectRatioWidth(@IntRange(from = 0) int aspectRatioWidth) {
+        mImpl.setAspectRatioWidth(aspectRatioWidth);
+        mFingerprint.recordPropertyUpdate(1, aspectRatioWidth);
+        return this;
+      }
+
+      /** Sets the height to be used when calculating the aspect ratio ratio to preserve. */
+      @NonNull
+      public Builder setAspectRatioHeight(@IntRange(from = 0) int aspectRatioHeight) {
+        mImpl.setAspectRatioHeight(aspectRatioHeight);
+        mFingerprint.recordPropertyUpdate(2, aspectRatioHeight);
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public ProportionalDimensionProp build() {
+        return new ProportionalDimensionProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** Interface defining a dimension that can be applied to a container. */
+  public interface ContainerDimension {
+    /**
+     * Get the protocol buffer representation of this object.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    DimensionProto.ContainerDimension toContainerDimensionProto();
+
+    /**
+     * Get the fingerprint for this object or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    Fingerprint getFingerprint();
+
+    /** Builder to create {@link ContainerDimension} objects. */
+    @SuppressLint("StaticFinalBuilder")
+    interface Builder {
+
+      /** Builds an instance with values accumulated in this Builder. */
+      @NonNull
+      ContainerDimension build();
+    }
+  }
+
+  @NonNull
+  static ContainerDimension containerDimensionFromProto(
+      @NonNull DimensionProto.ContainerDimension proto) {
+    if (proto.hasLinearDimension()) {
+      return DpProp.fromProto(proto.getLinearDimension());
+    }
+    if (proto.hasExpandedDimension()) {
+      return ExpandedDimensionProp.fromProto(proto.getExpandedDimension());
+    }
+    if (proto.hasWrappedDimension()) {
+      return WrappedDimensionProp.fromProto(proto.getWrappedDimension());
+    }
+    throw new IllegalStateException("Proto was not a recognised instance of ContainerDimension");
+  }
+
+  /** Interface defining a dimension that can be applied to an image. */
+  public interface ImageDimension {
+    /**
+     * Get the protocol buffer representation of this object.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    DimensionProto.ImageDimension toImageDimensionProto();
+
+    /**
+     * Get the fingerprint for this object or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    Fingerprint getFingerprint();
+
+    /** Builder to create {@link ImageDimension} objects. */
+    @SuppressLint("StaticFinalBuilder")
+    interface Builder {
+
+      /** Builds an instance with values accumulated in this Builder. */
+      @NonNull
+      ImageDimension build();
+    }
+  }
+
+  @NonNull
+  static ImageDimension imageDimensionFromProto(@NonNull DimensionProto.ImageDimension proto) {
+    if (proto.hasLinearDimension()) {
+      return DpProp.fromProto(proto.getLinearDimension());
+    }
+    if (proto.hasExpandedDimension()) {
+      return ExpandedDimensionProp.fromProto(proto.getExpandedDimension());
+    }
+    if (proto.hasProportionalDimension()) {
+      return ProportionalDimensionProp.fromProto(proto.getProportionalDimension());
+    }
+    throw new IllegalStateException("Proto was not a recognised instance of ImageDimension");
+  }
+
+  /** Interface defining a dimension that can be applied to a spacer. */
+  public interface SpacerDimension {
+    /**
+     * Get the protocol buffer representation of this object.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    DimensionProto.SpacerDimension toSpacerDimensionProto();
+
+    /**
+     * Get the fingerprint for this object or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    Fingerprint getFingerprint();
+
+    /** Builder to create {@link SpacerDimension} objects. */
+    @SuppressLint("StaticFinalBuilder")
+    interface Builder {
+
+      /** Builds an instance with values accumulated in this Builder. */
+      @NonNull
+      SpacerDimension build();
+    }
+  }
+
+  @NonNull
+  static SpacerDimension spacerDimensionFromProto(@NonNull DimensionProto.SpacerDimension proto) {
+    if (proto.hasLinearDimension()) {
+      return DpProp.fromProto(proto.getLinearDimension());
+    }
+    throw new IllegalStateException("Proto was not a recognised instance of SpacerDimension");
+  }
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
new file mode 100644
index 0000000..b123903
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
@@ -0,0 +1,4163 @@
+/*
+ * Copyright 2021-2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import static androidx.wear.protolayout.expression.Preconditions.checkNotNull;
+
+import android.annotation.SuppressLint;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.DimensionBuilders.ContainerDimension;
+import androidx.wear.protolayout.DimensionBuilders.DegreesProp;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.DimensionBuilders.EmProp;
+import androidx.wear.protolayout.DimensionBuilders.ImageDimension;
+import androidx.wear.protolayout.DimensionBuilders.SpProp;
+import androidx.wear.protolayout.DimensionBuilders.SpacerDimension;
+import androidx.wear.protolayout.ModifiersBuilders.ArcModifiers;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.ModifiersBuilders.SpanModifiers;
+import androidx.wear.protolayout.TypeBuilders.BoolProp;
+import androidx.wear.protolayout.TypeBuilders.Int32Prop;
+import androidx.wear.protolayout.TypeBuilders.StringProp;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.expression.ProtoLayoutExperimental;
+import androidx.wear.protolayout.proto.AlignmentProto;
+import androidx.wear.protolayout.proto.FingerprintProto;
+import androidx.wear.protolayout.proto.FingerprintProto.TreeFingerprint;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+import androidx.wear.protolayout.proto.TypesProto;
+import androidx.wear.protolayout.protobuf.InvalidProtocolBufferException;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Builders for composable layout elements that can be combined together to create renderable
+ * UI */
+public final class LayoutElementBuilders {
+  private LayoutElementBuilders() {}
+
+  /**
+   * The weight to be applied to the font.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({FONT_WEIGHT_UNDEFINED, FONT_WEIGHT_NORMAL, FONT_WEIGHT_MEDIUM, FONT_WEIGHT_BOLD})
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface FontWeight {}
+
+  /** Font weight is undefined. */
+  public static final int FONT_WEIGHT_UNDEFINED = 0;
+
+  /** Normal font weight. */
+  public static final int FONT_WEIGHT_NORMAL = 400;
+
+  /** Medium font weight. */
+  @ProtoLayoutExperimental public static final int FONT_WEIGHT_MEDIUM = 500;
+
+  /** Bold font weight. */
+  public static final int FONT_WEIGHT_BOLD = 700;
+
+  /**
+   * The variant of a font. Some renderers may use different fonts for title and body text, which
+   * can be selected using this field.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({FONT_VARIANT_UNDEFINED, FONT_VARIANT_TITLE, FONT_VARIANT_BODY})
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface FontVariant {}
+
+  /** Font variant is undefined. */
+  public static final int FONT_VARIANT_UNDEFINED = 0;
+
+  /** Font variant suited for title text. */
+  public static final int FONT_VARIANT_TITLE = 1;
+
+  /** Font variant suited for body text. */
+  public static final int FONT_VARIANT_BODY = 2;
+
+  /**
+   * The alignment of a {@link SpanImage} within the line height of the surrounding {@link
+   * Spannable}.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({
+    SPAN_VERTICAL_ALIGN_UNDEFINED,
+    SPAN_VERTICAL_ALIGN_BOTTOM,
+    SPAN_VERTICAL_ALIGN_TEXT_BASELINE
+  })
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface SpanVerticalAlignment {}
+
+  /** Alignment is undefined. */
+  public static final int SPAN_VERTICAL_ALIGN_UNDEFINED = 0;
+
+  /**
+   * Align to the bottom of the line (descent of the largest text in this line). If there is no text
+   * in the line containing this image, this will align to the bottom of the line, where the line
+   * height is defined as the height of the largest image in the line.
+   */
+  public static final int SPAN_VERTICAL_ALIGN_BOTTOM = 1;
+
+  /**
+   * Align to the baseline of the text. Note that if the line in the {@link Spannable} which
+   * contains this image does not contain any text, the effects of using this alignment are
+   * undefined.
+   */
+  public static final int SPAN_VERTICAL_ALIGN_TEXT_BASELINE = 2;
+
+  /**
+   * How text that will not fit inside the bounds of a {@link Text} element will be handled.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({TEXT_OVERFLOW_UNDEFINED, TEXT_OVERFLOW_TRUNCATE, TEXT_OVERFLOW_ELLIPSIZE_END})
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface TextOverflow {}
+
+  /** Overflow behavior is undefined. */
+  public static final int TEXT_OVERFLOW_UNDEFINED = 0;
+
+  /**
+   * Truncate the text to fit inside of the {@link Text} element's bounds. If text is truncated, it
+   * will be truncated on a word boundary.
+   */
+  public static final int TEXT_OVERFLOW_TRUNCATE = 1;
+
+  /**
+   * Truncate the text to fit in the {@link Text} element's bounds, but add an ellipsis (i.e. ...)
+   * to the end of the text if it has been truncated.
+   */
+  public static final int TEXT_OVERFLOW_ELLIPSIZE_END = 2;
+
+  /**
+   * How content which does not match the dimensions of its bounds (e.g. an image resource being
+   * drawn inside an {@link Image}) will be resized to fit its bounds.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({
+    CONTENT_SCALE_MODE_UNDEFINED,
+    CONTENT_SCALE_MODE_FIT,
+    CONTENT_SCALE_MODE_CROP,
+    CONTENT_SCALE_MODE_FILL_BOUNDS
+  })
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface ContentScaleMode {}
+
+  /** Content scaling is undefined. */
+  public static final int CONTENT_SCALE_MODE_UNDEFINED = 0;
+
+  /**
+   * Content will be scaled to fit inside its bounds, proportionally. As an example, If a 10x5 image
+   * was going to be drawn inside a 50x50 {@link Image} element, the actual image resource would be
+   * drawn as a 50x25 image, centered within the 50x50 bounds.
+   */
+  public static final int CONTENT_SCALE_MODE_FIT = 1;
+
+  /**
+   * Content will be resized proportionally so it completely fills its bounds, and anything outside
+   * of the bounds will be cropped. As an example, if a 10x5 image was going to be drawn inside a
+   * 50x50 {@link Image} element, the image resource would be drawn as a 100x50 image, centered
+   * within its bounds (and with 25px cropped from both the left and right sides).
+   */
+  public static final int CONTENT_SCALE_MODE_CROP = 2;
+
+  /**
+   * Content will be resized to fill its bounds, without taking into account the aspect ratio. If a
+   * 10x5 image was going to be drawn inside a 50x50 {@link Image} element, the image would be drawn
+   * as a 50x50 image, stretched vertically.
+   */
+  public static final int CONTENT_SCALE_MODE_FILL_BOUNDS = 3;
+
+  /** An extensible {@code FontWeight} property. */
+  public static final class FontWeightProp {
+    private final LayoutElementProto.FontWeightProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    FontWeightProp(
+        LayoutElementProto.FontWeightProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @FontWeight
+    public int getValue() {
+      return mImpl.getValue().getNumber();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static FontWeightProp fromProto(@NonNull LayoutElementProto.FontWeightProp proto) {
+      return new FontWeightProp(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.FontWeightProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link FontWeightProp} */
+    public static final class Builder {
+      private final LayoutElementProto.FontWeightProp.Builder mImpl =
+          LayoutElementProto.FontWeightProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1793388920);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@FontWeight int value) {
+        mImpl.setValue(LayoutElementProto.FontWeight.forNumber(value));
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public FontWeightProp build() {
+        return new FontWeightProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** An extensible {@code FontVariant} property. */
+  @ProtoLayoutExperimental
+  public static final class FontVariantProp {
+    private final LayoutElementProto.FontVariantProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    FontVariantProp(
+        LayoutElementProto.FontVariantProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @FontVariant
+    public int getValue() {
+      return mImpl.getValue().getNumber();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static FontVariantProp fromProto(@NonNull LayoutElementProto.FontVariantProp proto) {
+      return new FontVariantProp(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.FontVariantProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link FontVariantProp} */
+    public static final class Builder {
+      private final LayoutElementProto.FontVariantProp.Builder mImpl =
+          LayoutElementProto.FontVariantProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-293831500);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@FontVariant int value) {
+        mImpl.setValue(LayoutElementProto.FontVariant.forNumber(value));
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public FontVariantProp build() {
+        return new FontVariantProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** An extensible {@code SpanVerticalAlignment} property. */
+  public static final class SpanVerticalAlignmentProp {
+    private final LayoutElementProto.SpanVerticalAlignmentProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    SpanVerticalAlignmentProp(
+        LayoutElementProto.SpanVerticalAlignmentProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @SpanVerticalAlignment
+    public int getValue() {
+      return mImpl.getValue().getNumber();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static SpanVerticalAlignmentProp fromProto(
+        @NonNull LayoutElementProto.SpanVerticalAlignmentProp proto) {
+      return new SpanVerticalAlignmentProp(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.SpanVerticalAlignmentProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link SpanVerticalAlignmentProp} */
+    public static final class Builder {
+      private final LayoutElementProto.SpanVerticalAlignmentProp.Builder mImpl =
+          LayoutElementProto.SpanVerticalAlignmentProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1008812329);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@SpanVerticalAlignment int value) {
+        mImpl.setValue(LayoutElementProto.SpanVerticalAlignment.forNumber(value));
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public SpanVerticalAlignmentProp build() {
+        return new SpanVerticalAlignmentProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** The styling of a font (e.g. font size, and metrics). */
+  public static final class FontStyle {
+    private final LayoutElementProto.FontStyle mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    FontStyle(LayoutElementProto.FontStyle impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the size of the font, in scaled pixels (sp). If not specified, defaults to the size of
+     * the system's "body" font. Intended for testing purposes only.
+     */
+    @Nullable
+    public SpProp getSize() {
+      if (mImpl.hasSize()) {
+        return SpProp.fromProto(mImpl.getSize());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets whether the text should be rendered in a italic typeface. If not specified, defaults to
+     * "false". Intended for testing purposes only.
+     */
+    @Nullable
+    public BoolProp getItalic() {
+      if (mImpl.hasItalic()) {
+        return BoolProp.fromProto(mImpl.getItalic());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets whether the text should be rendered with an underline. If not specified, defaults to
+     * "false". Intended for testing purposes only.
+     */
+    @Nullable
+    public BoolProp getUnderline() {
+      if (mImpl.hasUnderline()) {
+        return BoolProp.fromProto(mImpl.getUnderline());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the text color. If not defined, defaults to white. Intended for testing purposes only.
+     */
+    @Nullable
+    public ColorProp getColor() {
+      if (mImpl.hasColor()) {
+        return ColorProp.fromProto(mImpl.getColor());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the weight of the font. If the provided value is not supported on a platform, the
+     * nearest supported value will be used. If not defined, or when set to an invalid value,
+     * defaults to "normal". Intended for testing purposes only.
+     */
+    @Nullable
+    public FontWeightProp getWeight() {
+      if (mImpl.hasWeight()) {
+        return FontWeightProp.fromProto(mImpl.getWeight());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the text letter-spacing. Positive numbers increase the space between letters while
+     * negative numbers tighten the space. If not specified, defaults to 0. Intended for testing
+     * purposes only.
+     */
+    @Nullable
+    public EmProp getLetterSpacing() {
+      if (mImpl.hasLetterSpacing()) {
+        return EmProp.fromProto(mImpl.getLetterSpacing());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the variant of a font. Some renderers may use different fonts for title and body
+     * text, which can be selected using this field. If not specified, defaults to "body".
+     * Intended for testing purposes only.
+     */
+    @ProtoLayoutExperimental
+    @Nullable
+    public FontVariantProp getVariant() {
+      if (mImpl.hasVariant()) {
+        return FontVariantProp.fromProto(mImpl.getVariant());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static FontStyle fromProto(@NonNull LayoutElementProto.FontStyle proto) {
+      return new FontStyle(proto, null);
+    }
+
+    /**
+     * Returns the internal proto instance.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.FontStyle toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link FontStyle} */
+    public static final class Builder {
+      private final LayoutElementProto.FontStyle.Builder mImpl =
+          LayoutElementProto.FontStyle.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(181264306);
+
+      public Builder() {}
+
+      /**
+       * Sets the size of the font, in scaled pixels (sp). If not specified, defaults to the size of
+       * the system's "body" font.
+       */
+      @NonNull
+      public Builder setSize(@NonNull SpProp size) {
+        mImpl.setSize(size.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(size.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets whether the text should be rendered in a italic typeface. If not specified, defaults
+       * to "false".
+       */
+      @NonNull
+      public Builder setItalic(@NonNull BoolProp italic) {
+        mImpl.setItalic(italic.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(italic.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets whether the text should be rendered in a italic typeface. If not specified,
+       * defaults to "false".
+       */
+      @SuppressLint("MissingGetterMatchingBuilder")
+      @NonNull
+      public Builder setItalic(boolean italic) {
+        mImpl.setItalic(TypesProto.BoolProp.newBuilder().setValue(italic));
+        return this;
+      }
+      /**
+       * Sets whether the text should be rendered with an underline. If not specified, defaults to
+       * "false".
+       */
+      @NonNull
+      public Builder setUnderline(@NonNull BoolProp underline) {
+        mImpl.setUnderline(underline.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(underline.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets whether the text should be rendered with an underline. If not specified,
+       * defaults to "false".
+       */
+      @SuppressLint("MissingGetterMatchingBuilder")
+      @NonNull
+      public Builder setUnderline(boolean underline) {
+        mImpl.setUnderline(TypesProto.BoolProp.newBuilder().setValue(underline));
+        return this;
+      }
+
+      /**
+       * Sets the text color. If not defined, defaults to white.
+       */
+      @NonNull
+      public Builder setColor(@NonNull ColorProp color) {
+        mImpl.setColor(color.toProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(color.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the weight of the font. If the provided value is not supported on a platform, the
+       * nearest supported value will be used. If not defined, or when set to an invalid value,
+       * defaults to "normal".
+       */
+      @NonNull
+      public Builder setWeight(@NonNull FontWeightProp weight) {
+        mImpl.setWeight(weight.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(weight.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets the weight of the font. If the provided value is not supported on a platform,
+       * the nearest supported value will be used. If not defined, or when set to an invalid
+       * value, defaults to "normal".
+       */
+      @NonNull
+      public Builder setWeight(@FontWeight int weight) {
+        mImpl.setWeight(
+                LayoutElementProto.FontWeightProp.newBuilder()
+                        .setValue(LayoutElementProto.FontWeight.forNumber(weight)));
+        return this;
+      }
+
+      /**
+       * Sets the text letter-spacing. Positive numbers increase the space between letters while
+       * negative numbers tighten the space. If not specified, defaults to 0.
+       */
+      @NonNull
+      public Builder setLetterSpacing(@NonNull EmProp letterSpacing) {
+        mImpl.setLetterSpacing(letterSpacing.toProto());
+        mFingerprint.recordPropertyUpdate(
+            6, checkNotNull(letterSpacing.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the variant of a font. Some renderers may use different fonts for title and body
+       * text, which can be selected using this field. If not specified, defaults to "body".
+       */
+      @ProtoLayoutExperimental
+      @NonNull
+      public Builder setVariant(@NonNull FontVariantProp variant) {
+        mImpl.setVariant(variant.toProto());
+        return this;
+      }
+      /**
+       * Sets the variant of a font. Some renderers may use different fonts for title and body
+       * text, which can be selected using this field. If not specified, defaults to "body".
+       */
+      @ProtoLayoutExperimental
+      @NonNull
+      public Builder setVariant(@FontVariant int variant) {
+        mImpl.setVariant(
+                LayoutElementProto.FontVariantProp.newBuilder()
+                        .setValue(LayoutElementProto.FontVariant.forNumber(variant)));
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public FontStyle build() {
+        return new FontStyle(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** An extensible {@code TextOverflow} property. */
+  public static final class TextOverflowProp {
+    private final LayoutElementProto.TextOverflowProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    TextOverflowProp(
+        LayoutElementProto.TextOverflowProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @TextOverflow
+    public int getValue() {
+      return mImpl.getValue().getNumber();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static TextOverflowProp fromProto(@NonNull LayoutElementProto.TextOverflowProp proto) {
+      return new TextOverflowProp(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.TextOverflowProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link TextOverflowProp} */
+    public static final class Builder {
+      private final LayoutElementProto.TextOverflowProp.Builder mImpl =
+          LayoutElementProto.TextOverflowProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1183432233);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@TextOverflow int value) {
+        mImpl.setValue(LayoutElementProto.TextOverflow.forNumber(value));
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public TextOverflowProp build() {
+        return new TextOverflowProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A text string. */
+  public static final class Text implements LayoutElement {
+    private final LayoutElementProto.Text mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Text(LayoutElementProto.Text impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the text to render. Intended for testing purposes only. */
+    @Nullable
+    public StringProp getText() {
+      if (mImpl.hasText()) {
+        return StringProp.fromProto(mImpl.getText());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the style of font to use (size, bold etc). If not specified, defaults to the platform's
+     * default body font. Intended for testing purposes only.
+     */
+    @Nullable
+    public FontStyle getFontStyle() {
+      if (mImpl.hasFontStyle()) {
+        return FontStyle.fromProto(mImpl.getFontStyle());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Modifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return Modifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the maximum number of lines that can be represented by the {@link Text} element. If not
+     * defined, the {@link Text} element will be treated as a single-line element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Int32Prop getMaxLines() {
+      if (mImpl.hasMaxLines()) {
+        return Int32Prop.fromProto(mImpl.getMaxLines());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets alignment of the text within its bounds. Note that a {@link Text} element will size
+     * itself to wrap its contents, so this option is meaningless for single-line text (for that,
+     * use alignment of the outer container). For multi-line text, however, this will set the
+     * alignment of lines relative to the {@link Text} element bounds. If not defined, defaults to
+     * TEXT_ALIGN_CENTER. Intended for testing purposes only.
+     */
+    @Nullable
+    public TextAlignmentProp getMultilineAlignment() {
+      if (mImpl.hasMultilineAlignment()) {
+        return TextAlignmentProp.fromProto(mImpl.getMultilineAlignment());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets how to handle text which overflows the bound of the {@link Text} element. A {@link Text}
+     * element will grow as large as possible inside its parent container (while still respecting
+     * max_lines); if it cannot grow large enough to render all of its text, the text which cannot
+     * fit inside its container will be truncated. If not defined, defaults to
+     * TEXT_OVERFLOW_TRUNCATE. Intended for testing purposes only.
+     */
+    @Nullable
+    public TextOverflowProp getOverflow() {
+      if (mImpl.hasOverflow()) {
+        return TextOverflowProp.fromProto(mImpl.getOverflow());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the explicit height between lines of text. This is equivalent to the vertical distance
+     * between subsequent baselines. If not specified, defaults the font's recommended interline
+     * spacing. Intended for testing purposes only.
+     */
+    @Nullable
+    public SpProp getLineHeight() {
+      if (mImpl.hasLineHeight()) {
+        return SpProp.fromProto(mImpl.getLineHeight());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Text fromProto(@NonNull LayoutElementProto.Text proto) {
+      return new Text(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.Text toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+      return LayoutElementProto.LayoutElement.newBuilder().setText(mImpl).build();
+    }
+
+    /** Builder for {@link Text}. */
+    public static final class Builder implements LayoutElement.Builder {
+      private final LayoutElementProto.Text.Builder mImpl = LayoutElementProto.Text.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1976530157);
+
+      public Builder() {}
+
+      /**
+       * Sets the text to render.
+       */
+      @NonNull
+      public Builder setText(@NonNull StringProp text) {
+        mImpl.setText(text.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(text.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /** Sets the text to render. */
+      @NonNull
+      public Builder setText(@NonNull String text) {
+        mImpl.setText(TypesProto.StringProp.newBuilder().setValue(text));
+        return this;
+      }
+
+      /**
+       * Sets the style of font to use (size, bold etc). If not specified, defaults to the
+       * platform's default body font.
+       */
+      @NonNull
+      public Builder setFontStyle(@NonNull FontStyle fontStyle) {
+        mImpl.setFontStyle(fontStyle.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(fontStyle.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull Modifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the maximum number of lines that can be represented by the {@link Text} element. If
+       * not defined, the {@link Text} element will be treated as a single-line element.
+       */
+      @NonNull
+      public Builder setMaxLines(@NonNull Int32Prop maxLines) {
+        mImpl.setMaxLines(maxLines.toProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(maxLines.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets the maximum number of lines that can be represented by the {@link Text} element.
+       * If not defined, the {@link Text} element will be treated as a single-line element.
+       */
+      @NonNull
+      public Builder setMaxLines(@IntRange(from = 1) int maxLines) {
+        mImpl.setMaxLines(TypesProto.Int32Prop.newBuilder().setValue(maxLines));
+        return this;
+      }
+
+      /**
+       * Sets alignment of the text within its bounds. Note that a {@link Text} element will size
+       * itself to wrap its contents, so this option is meaningless for single-line text (for that,
+       * use alignment of the outer container). For multi-line text, however, this will set the
+       * alignment of lines relative to the {@link Text} element bounds. If not defined, defaults to
+       * TEXT_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setMultilineAlignment(@NonNull TextAlignmentProp multilineAlignment) {
+        mImpl.setMultilineAlignment(multilineAlignment.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(multilineAlignment.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets alignment of the text within its bounds. Note that a {@link Text} element will
+       * size itself to wrap its contents, so this option is meaningless for single-line text
+       * (for that, use alignment of the outer container). For multi-line text, however, this
+       * will set the alignment of lines relative to the {@link Text} element bounds. If not
+       * defined, defaults to TEXT_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setMultilineAlignment(@TextAlignment int multilineAlignment) {
+        mImpl.setMultilineAlignment(
+                AlignmentProto.TextAlignmentProp.newBuilder()
+                        .setValue(
+                                AlignmentProto.TextAlignment.forNumber(
+                                        multilineAlignment)));
+        return this;
+      }
+
+      /**
+       * Sets how to handle text which overflows the bound of the {@link Text} element. A {@link
+       * Text} element will grow as large as possible inside its parent container (while still
+       * respecting max_lines); if it cannot grow large enough to render all of its text, the text
+       * which cannot fit inside its container will be truncated. If not defined, defaults to
+       * TEXT_OVERFLOW_TRUNCATE.
+       */
+      @NonNull
+      public Builder setOverflow(@NonNull TextOverflowProp overflow) {
+        mImpl.setOverflow(overflow.toProto());
+        mFingerprint.recordPropertyUpdate(
+            6, checkNotNull(overflow.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets how to handle text which overflows the bound of the {@link Text} element. A
+       * {@link Text} element will grow as large as possible inside its parent container
+       * (while still respecting max_lines); if it cannot grow large enough to render all of
+       * its text, the text which cannot fit inside its container will be truncated. If not
+       * defined, defaults to TEXT_OVERFLOW_TRUNCATE.
+       */
+      @NonNull
+      public Builder setOverflow(@TextOverflow int overflow) {
+        mImpl.setOverflow(
+                LayoutElementProto.TextOverflowProp.newBuilder()
+                        .setValue(LayoutElementProto.TextOverflow.forNumber(overflow)));
+        return this;
+      }
+
+      /**
+       * Sets the explicit height between lines of text. This is equivalent to the vertical distance
+       * between subsequent baselines. If not specified, defaults the font's recommended interline
+       * spacing.
+       */
+      @NonNull
+      public Builder setLineHeight(@NonNull SpProp lineHeight) {
+        mImpl.setLineHeight(lineHeight.toProto());
+        mFingerprint.recordPropertyUpdate(
+            7, checkNotNull(lineHeight.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public Text build() {
+        return new Text(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** An extensible {@code ContentScaleMode} property. */
+  public static final class ContentScaleModeProp {
+    private final LayoutElementProto.ContentScaleModeProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ContentScaleModeProp(
+        LayoutElementProto.ContentScaleModeProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @ContentScaleMode
+    public int getValue() {
+      return mImpl.getValue().getNumber();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ContentScaleModeProp fromProto(@NonNull LayoutElementProto.ContentScaleModeProp proto) {
+      return new ContentScaleModeProp(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.ContentScaleModeProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link ContentScaleModeProp} */
+    public static final class Builder {
+      private final LayoutElementProto.ContentScaleModeProp.Builder mImpl =
+          LayoutElementProto.ContentScaleModeProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-893830536);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@ContentScaleMode int value) {
+        mImpl.setValue(LayoutElementProto.ContentScaleMode.forNumber(value));
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public ContentScaleModeProp build() {
+        return new ContentScaleModeProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** Filtering parameters used for images. This can be used to apply a color tint to images. */
+  public static final class ColorFilter {
+    private final LayoutElementProto.ColorFilter mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ColorFilter(LayoutElementProto.ColorFilter impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the tint color to use. If specified, the image will be tinted, using SRC_IN blending
+     * (that is, all color information will be stripped from the target image, and only the alpha
+     * channel will be blended with the requested color).
+     *
+     * <p>Note that only Android image resources can be tinted; Inline images will not be tinted,
+     * and this property will have no effect. Intended for testing purposes only.
+     */
+    @Nullable
+    public ColorProp getTint() {
+      if (mImpl.hasTint()) {
+        return ColorProp.fromProto(mImpl.getTint());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ColorFilter fromProto(@NonNull LayoutElementProto.ColorFilter proto) {
+      return new ColorFilter(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.ColorFilter toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link ColorFilter} */
+    public static final class Builder {
+      private final LayoutElementProto.ColorFilter.Builder mImpl =
+          LayoutElementProto.ColorFilter.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(181311326);
+
+      public Builder() {}
+
+      /**
+       * Sets the tint color to use. If specified, the image will be tinted, using SRC_IN blending
+       * (that is, all color information will be stripped from the target image, and only the alpha
+       * channel will be blended with the requested color).
+       *
+       * <p>Note that only Android image resources can be tinted; Inline images will not be tinted,
+       * and this property will have no effect.
+       */
+      @NonNull
+      public Builder setTint(@NonNull ColorProp tint) {
+        mImpl.setTint(tint.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(tint.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public ColorFilter build() {
+        return new ColorFilter(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * An image.
+   *
+   * <p>Images used in this element must exist in the resource bundle that corresponds to this
+   * layout. Images must have their dimension specified, and will be rendered at this width and
+   * height, regardless of their native dimension.
+   */
+  public static final class Image implements LayoutElement {
+    private final LayoutElementProto.Image mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Image(LayoutElementProto.Image impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the resource_id of the image to render. This must exist in the supplied resource bundle.
+     * Intended for testing purposes only.
+     */
+    @Nullable
+    public StringProp getResourceId() {
+      if (mImpl.hasResourceId()) {
+        return StringProp.fromProto(mImpl.getResourceId());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the width of this image. If not defined, the image will not be rendered. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public ImageDimension getWidth() {
+      if (mImpl.hasWidth()) {
+        return DimensionBuilders.imageDimensionFromProto(mImpl.getWidth());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the height of this image. If not defined, the image will not be rendered. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public ImageDimension getHeight() {
+      if (mImpl.hasHeight()) {
+        return DimensionBuilders.imageDimensionFromProto(mImpl.getHeight());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets how to scale the image resource inside the bounds specified by width/height if its size
+     * does not match those bounds. Defaults to CONTENT_SCALE_MODE_FIT. Intended for testing
+     * purposes only.
+     */
+    @Nullable
+    public ContentScaleModeProp getContentScaleMode() {
+      if (mImpl.hasContentScaleMode()) {
+        return ContentScaleModeProp.fromProto(mImpl.getContentScaleMode());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Modifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return Modifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets filtering parameters for this image. If not specified, defaults to no filtering.
+     * Intended for testing purposes only.
+     */
+    @Nullable
+    public ColorFilter getColorFilter() {
+      if (mImpl.hasColorFilter()) {
+        return ColorFilter.fromProto(mImpl.getColorFilter());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Image fromProto(@NonNull LayoutElementProto.Image proto) {
+      return new Image(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.Image toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+      return LayoutElementProto.LayoutElement.newBuilder().setImage(mImpl).build();
+    }
+
+    /** Builder for {@link Image}. */
+    public static final class Builder implements LayoutElement.Builder {
+      private final LayoutElementProto.Image.Builder mImpl = LayoutElementProto.Image.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-543078544);
+
+      public Builder() {}
+
+      /**
+       * Sets the resource_id of the image to render. This must exist in the supplied resource
+       * bundle.
+       */
+      @NonNull
+      public Builder setResourceId(@NonNull StringProp resourceId) {
+        mImpl.setResourceId(resourceId.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(resourceId.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets the resource_id of the image to render. This must exist in the supplied resource
+       * bundle.
+       */
+      @NonNull
+      public Builder setResourceId(@NonNull String resourceId) {
+        mImpl.setResourceId(TypesProto.StringProp.newBuilder().setValue(resourceId));
+        return this;
+      }
+
+      /**
+       * Sets the width of this image. If not defined, the image will not be rendered.
+       */
+      @NonNull
+      public Builder setWidth(@NonNull ImageDimension width) {
+        mImpl.setWidth(width.toImageDimensionProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(width.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the height of this image. If not defined, the image will not be rendered.
+       */
+      @NonNull
+      public Builder setHeight(@NonNull ImageDimension height) {
+        mImpl.setHeight(height.toImageDimensionProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(height.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets how to scale the image resource inside the bounds specified by width/height if its
+       * size does not match those bounds. Defaults to CONTENT_SCALE_MODE_FIT.
+       */
+      @NonNull
+      public Builder setContentScaleMode(@NonNull ContentScaleModeProp contentScaleMode) {
+        mImpl.setContentScaleMode(contentScaleMode.toProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(contentScaleMode.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets how to scale the image resource inside the bounds specified by width/height if
+       * its size does not match those bounds. Defaults to CONTENT_SCALE_MODE_FIT.
+       */
+      @NonNull
+      public Builder setContentScaleMode(@ContentScaleMode int contentScaleMode) {
+        mImpl.setContentScaleMode(
+                LayoutElementProto.ContentScaleModeProp.newBuilder()
+                        .setValue(
+                                LayoutElementProto.ContentScaleMode.forNumber(
+                                        contentScaleMode)));
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull Modifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets filtering parameters for this image. If not specified, defaults to no filtering. */
+      @NonNull
+      public Builder setColorFilter(@NonNull ColorFilter colorFilter) {
+        mImpl.setColorFilter(colorFilter.toProto());
+        mFingerprint.recordPropertyUpdate(
+            6, checkNotNull(colorFilter.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public Image build() {
+        return new Image(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A simple spacer, typically used to provide padding between adjacent elements. */
+  public static final class Spacer implements LayoutElement {
+    private final LayoutElementProto.Spacer mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Spacer(LayoutElementProto.Spacer impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the width of this {@link Spacer}. When this is added as the direct child of an {@link
+     * Arc}, this must be specified as an angular dimension, otherwise a linear dimension must be
+     * used. If not defined, defaults to 0. Intended for testing purposes only.
+     */
+    @Nullable
+    public SpacerDimension getWidth() {
+      if (mImpl.hasWidth()) {
+        return DimensionBuilders.spacerDimensionFromProto(mImpl.getWidth());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the height of this spacer. If not defined, defaults to 0. Intended for testing purposes
+     * only.
+     */
+    @Nullable
+    public SpacerDimension getHeight() {
+      if (mImpl.hasHeight()) {
+        return DimensionBuilders.spacerDimensionFromProto(mImpl.getHeight());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Modifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return Modifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Spacer fromProto(@NonNull LayoutElementProto.Spacer proto) {
+      return new Spacer(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.Spacer toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+      return LayoutElementProto.LayoutElement.newBuilder().setSpacer(mImpl).build();
+    }
+
+    /** Builder for {@link Spacer}. */
+    public static final class Builder implements LayoutElement.Builder {
+      private final LayoutElementProto.Spacer.Builder mImpl =
+          LayoutElementProto.Spacer.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1748084575);
+
+      public Builder() {}
+
+      /**
+       * Sets the width of this {@link Spacer}. When this is added as the direct child of an {@link
+       * Arc}, this must be specified as an angular dimension, otherwise a linear dimension must be
+       * used. If not defined, defaults to 0.
+       */
+      @NonNull
+      public Builder setWidth(@NonNull SpacerDimension width) {
+        mImpl.setWidth(width.toSpacerDimensionProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(width.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the height of this spacer. If not defined, defaults to 0.
+       */
+      @NonNull
+      public Builder setHeight(@NonNull SpacerDimension height) {
+        mImpl.setHeight(height.toSpacerDimensionProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(height.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull Modifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+
+      @Override
+      @NonNull
+      public Spacer build() {
+        return new Spacer(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * A container which stacks all of its children on top of one another. This also allows to add a
+   * background color, or to have a border around them with some padding.
+   */
+  public static final class Box implements LayoutElement {
+    private final LayoutElementProto.Box mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Box(LayoutElementProto.Box impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the child element(s) to wrap. Intended for testing purposes only. */
+    @NonNull
+    public List<LayoutElement> getContents() {
+      List<LayoutElement> list = new ArrayList<>();
+      for (LayoutElementProto.LayoutElement item : mImpl.getContentsList()) {
+        list.add(LayoutElementBuilders.layoutElementFromProto(item));
+      }
+      return Collections.unmodifiableList(list);
+    }
+
+    /**
+     * Gets the height of this {@link Box}. If not defined, this will size itself to fit all of its
+     * children (i.e. a WrappedDimension). Intended for testing purposes only.
+     */
+    @Nullable
+    public ContainerDimension getHeight() {
+      if (mImpl.hasHeight()) {
+        return DimensionBuilders.containerDimensionFromProto(mImpl.getHeight());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the width of this {@link Box}. If not defined, this will size itself to fit all of its
+     * children (i.e. a WrappedDimension). Intended for testing purposes only.
+     */
+    @Nullable
+    public ContainerDimension getWidth() {
+      if (mImpl.hasWidth()) {
+        return DimensionBuilders.containerDimensionFromProto(mImpl.getWidth());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the horizontal alignment of the element inside this {@link Box}. If not defined,
+     * defaults to HORIZONTAL_ALIGN_CENTER. Intended for testing purposes only.
+     */
+    @Nullable
+    public HorizontalAlignmentProp getHorizontalAlignment() {
+      if (mImpl.hasHorizontalAlignment()) {
+        return HorizontalAlignmentProp.fromProto(mImpl.getHorizontalAlignment());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the vertical alignment of the element inside this {@link Box}. If not defined, defaults
+     * to VERTICAL_ALIGN_CENTER. Intended for testing purposes only.
+     */
+    @Nullable
+    public VerticalAlignmentProp getVerticalAlignment() {
+      if (mImpl.hasVerticalAlignment()) {
+        return VerticalAlignmentProp.fromProto(mImpl.getVerticalAlignment());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Modifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return Modifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Box fromProto(@NonNull LayoutElementProto.Box proto) {
+      return new Box(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.Box toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+      return LayoutElementProto.LayoutElement.newBuilder().setBox(mImpl).build();
+    }
+
+    /** Builder for {@link Box}. */
+    public static final class Builder implements LayoutElement.Builder {
+      private final LayoutElementProto.Box.Builder mImpl = LayoutElementProto.Box.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1881256071);
+
+      public Builder() {}
+
+      /** Adds one item to the child element(s) to wrap. */
+      @NonNull
+      public Builder addContent(@NonNull LayoutElement content) {
+        mImpl.addContents(content.toLayoutElementProto());
+        mFingerprint.addChildNode(checkNotNull(content.getFingerprint()));
+        return this;
+      }
+
+      /**
+       * Sets the height of this {@link Box}. If not defined, this will size itself to fit all of
+       * its children (i.e. a WrappedDimension).
+       */
+      @NonNull
+      public Builder setHeight(@NonNull ContainerDimension height) {
+        mImpl.setHeight(height.toContainerDimensionProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(height.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the width of this {@link Box}. If not defined, this will size itself to fit all of its
+       * children (i.e. a WrappedDimension).
+       */
+      @NonNull
+      public Builder setWidth(@NonNull ContainerDimension width) {
+        mImpl.setWidth(width.toContainerDimensionProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(width.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the horizontal alignment of the element inside this {@link Box}. If not defined,
+       * defaults to HORIZONTAL_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setHorizontalAlignment(@NonNull HorizontalAlignmentProp horizontalAlignment) {
+        mImpl.setHorizontalAlignment(horizontalAlignment.toProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(horizontalAlignment.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the horizontal alignment of the element inside this {@link Box}. If not defined,
+       * defaults to HORIZONTAL_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setHorizontalAlignment(@HorizontalAlignment int horizontalAlignment) {
+        mImpl.setHorizontalAlignment(
+                AlignmentProto.HorizontalAlignmentProp.newBuilder()
+                        .setValue(
+                                AlignmentProto.HorizontalAlignment.forNumber(
+                                        horizontalAlignment)));
+        return this;
+      }
+
+      /**
+       * Sets the vertical alignment of the element inside this {@link Box}. If not defined,
+       * defaults to VERTICAL_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setVerticalAlignment(@NonNull VerticalAlignmentProp verticalAlignment) {
+        mImpl.setVerticalAlignment(verticalAlignment.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(verticalAlignment.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets the vertical alignment of the element inside this {@link Box}. If not defined,
+       * defaults to VERTICAL_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setVerticalAlignment(@VerticalAlignment int verticalAlignment) {
+        mImpl.setVerticalAlignment(
+                AlignmentProto.VerticalAlignmentProp.newBuilder()
+                        .setValue(
+                                AlignmentProto.VerticalAlignment.forNumber(
+                                        verticalAlignment)));
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull Modifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            6, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+
+      @Override
+      @NonNull
+      public Box build() {
+        return new Box(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * A portion of text which can be added to a {@link Span}. Two different {@link SpanText} elements
+   * on the same line will be aligned to the same baseline, regardless of the size of each {@link
+   * SpanText}.
+   */
+  public static final class SpanText implements Span {
+    private final LayoutElementProto.SpanText mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    SpanText(LayoutElementProto.SpanText impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the text to render. Intended for testing purposes only. */
+    @Nullable
+    public StringProp getText() {
+      if (mImpl.hasText()) {
+        return StringProp.fromProto(mImpl.getText());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the style of font to use (size, bold etc). If not specified, defaults to the platform's
+     * default body font. Intended for testing purposes only.
+     */
+    @Nullable
+    public FontStyle getFontStyle() {
+      if (mImpl.hasFontStyle()) {
+        return FontStyle.fromProto(mImpl.getFontStyle());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public SpanModifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return SpanModifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static SpanText fromProto(@NonNull LayoutElementProto.SpanText proto) {
+      return new SpanText(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.SpanText toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.Span toSpanProto() {
+      return LayoutElementProto.Span.newBuilder().setText(mImpl).build();
+    }
+
+    /** Builder for {@link SpanText}. */
+    public static final class Builder implements Span.Builder {
+      private final LayoutElementProto.SpanText.Builder mImpl =
+          LayoutElementProto.SpanText.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-221774557);
+
+      public Builder() {}
+
+      /**
+       * Sets the text to render.
+       */
+      @NonNull
+      public Builder setText(@NonNull StringProp text) {
+        mImpl.setText(text.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(text.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /** Sets the text to render. */
+      @NonNull
+      public Builder setText(@NonNull String text) {
+        mImpl.setText(TypesProto.StringProp.newBuilder().setValue(text));
+        return this;
+      }
+
+      /**
+       * Sets the style of font to use (size, bold etc). If not specified, defaults to the
+       * platform's default body font.
+       */
+      @NonNull
+      public Builder setFontStyle(@NonNull FontStyle fontStyle) {
+        mImpl.setFontStyle(fontStyle.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(fontStyle.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull SpanModifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public SpanText build() {
+        return new SpanText(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** An image which can be added to a {@link Span}. */
+  public static final class SpanImage implements Span {
+    private final LayoutElementProto.SpanImage mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    SpanImage(LayoutElementProto.SpanImage impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the resource_id of the image to render. This must exist in the supplied resource bundle.
+     * Intended for testing purposes only.
+     */
+    @Nullable
+    public StringProp getResourceId() {
+      if (mImpl.hasResourceId()) {
+        return StringProp.fromProto(mImpl.getResourceId());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the width of this image. If not defined, the image will not be rendered. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public DpProp getWidth() {
+      if (mImpl.hasWidth()) {
+        return DpProp.fromProto(mImpl.getWidth());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the height of this image. If not defined, the image will not be rendered. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public DpProp getHeight() {
+      if (mImpl.hasHeight()) {
+        return DpProp.fromProto(mImpl.getHeight());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public SpanModifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return SpanModifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets alignment of this image within the line height of the surrounding {@link Spannable}. If
+     * undefined, defaults to SPAN_VERTICAL_ALIGN_BOTTOM. Intended for testing purposes only.
+     */
+    @Nullable
+    public SpanVerticalAlignmentProp getAlignment() {
+      if (mImpl.hasAlignment()) {
+        return SpanVerticalAlignmentProp.fromProto(mImpl.getAlignment());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static SpanImage fromProto(@NonNull LayoutElementProto.SpanImage proto) {
+      return new SpanImage(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.SpanImage toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.Span toSpanProto() {
+      return LayoutElementProto.Span.newBuilder().setImage(mImpl).build();
+    }
+
+    /** Builder for {@link SpanImage}. */
+    public static final class Builder implements Span.Builder {
+      private final LayoutElementProto.SpanImage.Builder mImpl =
+          LayoutElementProto.SpanImage.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(502289772);
+
+      public Builder() {}
+
+      /**
+       * Sets the resource_id of the image to render. This must exist in the supplied resource
+       * bundle.
+       */
+      @NonNull
+      public Builder setResourceId(@NonNull StringProp resourceId) {
+        mImpl.setResourceId(resourceId.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(resourceId.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets the resource_id of the image to render. This must exist in the supplied resource
+       * bundle.
+       */
+      @NonNull
+      public Builder setResourceId(@NonNull String resourceId) {
+        mImpl.setResourceId(TypesProto.StringProp.newBuilder().setValue(resourceId));
+        return this;
+      }
+
+      /**
+       * Sets the width of this image. If not defined, the image will not be rendered.
+       */
+      @NonNull
+      public Builder setWidth(@NonNull DpProp width) {
+        mImpl.setWidth(width.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(width.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the height of this image. If not defined, the image will not be rendered.
+       */
+      @NonNull
+      public Builder setHeight(@NonNull DpProp height) {
+        mImpl.setHeight(height.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(height.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull SpanModifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets alignment of this image within the line height of the surrounding {@link Spannable}.
+       * If undefined, defaults to SPAN_VERTICAL_ALIGN_BOTTOM.
+       */
+      @NonNull
+      public Builder setAlignment(@NonNull SpanVerticalAlignmentProp alignment) {
+        mImpl.setAlignment(alignment.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(alignment.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets alignment of this image within the line height of the surrounding {@link
+       * Spannable}. If undefined, defaults to SPAN_VERTICAL_ALIGN_BOTTOM.
+       */
+      @NonNull
+      public Builder setAlignment(@SpanVerticalAlignment int alignment) {
+        mImpl.setAlignment(
+                LayoutElementProto.SpanVerticalAlignmentProp.newBuilder()
+                        .setValue(
+                                LayoutElementProto.SpanVerticalAlignment.forNumber(
+                                        alignment)));
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public SpanImage build() {
+        return new SpanImage(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * Interface defining a single {@link Span}. Each {@link Span} forms part of a larger {@link
+   * Spannable} widget. At the moment, the only widgets which can be added to {@link Spannable}
+   * containers are {@link SpanText} and {@link SpanImage} elements.
+   */
+  public interface Span {
+    /**
+     * Get the protocol buffer representation of this object.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    LayoutElementProto.Span toSpanProto();
+
+    /**
+     * Get the fingerprint for this object or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    Fingerprint getFingerprint();
+
+    /** Builder to create {@link Span} objects. */
+    @SuppressLint("StaticFinalBuilder")
+    interface Builder {
+
+      /** Builds an instance with values accumulated in this Builder. */
+      @NonNull
+      Span build();
+    }
+  }
+
+  @NonNull
+  static Span spanFromProto(@NonNull LayoutElementProto.Span proto) {
+    if (proto.hasText()) {
+      return SpanText.fromProto(proto.getText());
+    }
+    if (proto.hasImage()) {
+      return SpanImage.fromProto(proto.getImage());
+    }
+    throw new IllegalStateException("Proto was not a recognised instance of Span");
+  }
+
+  /**
+   * A container of {@link Span} elements. Currently, this supports {@link SpanImage} and {@link
+   * SpanText} elements, where each individual {@link Span} can have different styling applied to it
+   * but the resulting text will flow naturally. This allows sections of a paragraph of text to have
+   * different styling applied to it, for example, making one or two words bold or italic.
+   */
+  public static final class Spannable implements LayoutElement {
+    private final LayoutElementProto.Spannable mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Spannable(LayoutElementProto.Spannable impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the {@link Span} elements that form this {@link Spannable}. Intended for testing
+     * purposes only.
+     */
+    @NonNull
+    public List<Span> getSpans() {
+      List<Span> list = new ArrayList<>();
+      for (LayoutElementProto.Span item : mImpl.getSpansList()) {
+        list.add(LayoutElementBuilders.spanFromProto(item));
+      }
+      return Collections.unmodifiableList(list);
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Modifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return Modifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the maximum number of lines that can be represented by the {@link Spannable} element. If
+     * not defined, the {@link Spannable} element will be treated as a single-line element. Intended
+     * for testing purposes only.
+     */
+    @Nullable
+    public Int32Prop getMaxLines() {
+      if (mImpl.hasMaxLines()) {
+        return Int32Prop.fromProto(mImpl.getMaxLines());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets alignment of the {@link Spannable} content within its bounds. Note that a {@link
+     * Spannable} element will size itself to wrap its contents, so this option is meaningless for
+     * single-line content (for that, use alignment of the outer container). For multi-line content,
+     * however, this will set the alignment of lines relative to the {@link Spannable} element
+     * bounds. If not defined, defaults to TEXT_ALIGN_CENTER. Intended for testing purposes only.
+     */
+    @Nullable
+    public HorizontalAlignmentProp getMultilineAlignment() {
+      if (mImpl.hasMultilineAlignment()) {
+        return HorizontalAlignmentProp.fromProto(mImpl.getMultilineAlignment());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets how to handle content which overflows the bound of the {@link Spannable} element. A
+     * {@link Spannable} element will grow as large as possible inside its parent container (while
+     * still respecting max_lines); if it cannot grow large enough to render all of its content, the
+     * content which cannot fit inside its container will be truncated. If not defined, defaults to
+     * TEXT_OVERFLOW_TRUNCATE. Intended for testing purposes only.
+     */
+    @Nullable
+    public TextOverflowProp getOverflow() {
+      if (mImpl.hasOverflow()) {
+        return TextOverflowProp.fromProto(mImpl.getOverflow());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the explicit height between lines of text. This is equivalent to the vertical distance
+     * between subsequent baselines. If not specified, defaults the font's recommended interline
+     * spacing. Intended for testing purposes only.
+     */
+    @Nullable
+    public SpProp getLineHeight() {
+      if (mImpl.hasLineHeight()) {
+        return SpProp.fromProto(mImpl.getLineHeight());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Spannable fromProto(@NonNull LayoutElementProto.Spannable proto) {
+      return new Spannable(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.Spannable toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+      return LayoutElementProto.LayoutElement.newBuilder().setSpannable(mImpl).build();
+    }
+
+    /** Builder for {@link Spannable}. */
+    public static final class Builder implements LayoutElement.Builder {
+      private final LayoutElementProto.Spannable.Builder mImpl =
+          LayoutElementProto.Spannable.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1690284372);
+
+      public Builder() {}
+
+      /** Adds one item to the {@link Span} elements that form this {@link Spannable}. */
+      @NonNull
+      public Builder addSpan(@NonNull Span span) {
+        mImpl.addSpans(span.toSpanProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(span.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull Modifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the maximum number of lines that can be represented by the {@link Spannable} element.
+       * If not defined, the {@link Spannable} element will be treated as a single-line element.
+       */
+      @NonNull
+      public Builder setMaxLines(@NonNull Int32Prop maxLines) {
+        mImpl.setMaxLines(maxLines.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(maxLines.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets the maximum number of lines that can be represented by the {@link Spannable}
+       * element. If not defined, the {@link Spannable} element will be treated as a
+       * single-line element.
+       */
+      @NonNull
+      public Builder setMaxLines(@IntRange(from = 1) int maxLines) {
+        mImpl.setMaxLines(TypesProto.Int32Prop.newBuilder().setValue(maxLines));
+        return this;
+      }
+
+      /**
+       * Sets alignment of the {@link Spannable} content within its bounds. Note that a {@link
+       * Spannable} element will size itself to wrap its contents, so this option is meaningless for
+       * single-line content (for that, use alignment of the outer container). For multi-line
+       * content, however, this will set the alignment of lines relative to the {@link Spannable}
+       * element bounds. If not defined, defaults to TEXT_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setMultilineAlignment(@NonNull HorizontalAlignmentProp multilineAlignment) {
+        mImpl.setMultilineAlignment(multilineAlignment.toProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(multilineAlignment.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets alignment of the {@link Spannable} content within its bounds. Note that a {@link
+       * Spannable} element will size itself to wrap its contents, so this option is
+       * meaningless for single-line content (for that, use alignment of the outer container).
+       * For multi-line content, however, this will set the alignment of lines relative to the
+       * {@link Spannable} element bounds. If not defined, defaults to TEXT_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setMultilineAlignment(@HorizontalAlignment int multilineAlignment) {
+        mImpl.setMultilineAlignment(
+                AlignmentProto.HorizontalAlignmentProp.newBuilder()
+                        .setValue(
+                                AlignmentProto.HorizontalAlignment.forNumber(
+                                        multilineAlignment)));
+        return this;
+      }
+
+      /**
+       * Sets how to handle content which overflows the bound of the {@link Spannable} element. A
+       * {@link Spannable} element will grow as large as possible inside its parent container (while
+       * still respecting max_lines); if it cannot grow large enough to render all of its content,
+       * the content which cannot fit inside its container will be truncated. If not defined,
+       * defaults to TEXT_OVERFLOW_TRUNCATE.
+       */
+      @NonNull
+      public Builder setOverflow(@NonNull TextOverflowProp overflow) {
+        mImpl.setOverflow(overflow.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(overflow.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets how to handle content which overflows the bound of the {@link Spannable}
+       * element. A {@link Spannable} element will grow as large as possible inside its parent
+       * container (while still respecting max_lines); if it cannot grow large enough to
+       * render all of its content, the content which cannot fit inside its container will be
+       * truncated. If not defined, defaults to TEXT_OVERFLOW_TRUNCATE.
+       */
+      @NonNull
+      public Builder setOverflow(@TextOverflow int overflow) {
+        mImpl.setOverflow(
+                LayoutElementProto.TextOverflowProp.newBuilder()
+                        .setValue(LayoutElementProto.TextOverflow.forNumber(overflow)));
+        return this;
+      }
+
+      /**
+       * Sets the explicit height between lines of text. This is equivalent to the vertical distance
+       * between subsequent baselines. If not specified, defaults the font's recommended interline
+       * spacing.
+       */
+      @NonNull
+      public Builder setLineHeight(@NonNull SpProp lineHeight) {
+        mImpl.setLineHeight(lineHeight.toProto());
+        mFingerprint.recordPropertyUpdate(
+            7, checkNotNull(lineHeight.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public Spannable build() {
+        return new Spannable(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * A column of elements. Each child element will be laid out vertically, one after another (i.e.
+   * stacking down). This element will size itself to the smallest size required to hold all of its
+   * children (e.g. if it contains three elements sized 10x10, 20x20 and 30x30, the resulting column
+   * will be 30x60).
+   *
+   * <p>If specified, horizontal_alignment can be used to control the gravity inside the container,
+   * affecting the horizontal placement of children whose width are smaller than the resulting
+   * column width.
+   */
+  public static final class Column implements LayoutElement {
+    private final LayoutElementProto.Column mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Column(LayoutElementProto.Column impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the list of child elements to place inside this {@link Column}. Intended for testing
+     * purposes only.
+     */
+    @NonNull
+    public List<LayoutElement> getContents() {
+      List<LayoutElement> list = new ArrayList<>();
+      for (LayoutElementProto.LayoutElement item : mImpl.getContentsList()) {
+        list.add(LayoutElementBuilders.layoutElementFromProto(item));
+      }
+      return Collections.unmodifiableList(list);
+    }
+
+    /**
+     * Gets the horizontal alignment of elements inside this column, if they are narrower than the
+     * resulting width of the column. If not defined, defaults to HORIZONTAL_ALIGN_CENTER. Intended
+     * for testing purposes only.
+     */
+    @Nullable
+    public HorizontalAlignmentProp getHorizontalAlignment() {
+      if (mImpl.hasHorizontalAlignment()) {
+        return HorizontalAlignmentProp.fromProto(mImpl.getHorizontalAlignment());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the width of this column. If not defined, this will size itself to fit all of its
+     * children (i.e. a WrappedDimension). Intended for testing purposes only.
+     */
+    @Nullable
+    public ContainerDimension getWidth() {
+      if (mImpl.hasWidth()) {
+        return DimensionBuilders.containerDimensionFromProto(mImpl.getWidth());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the height of this column. If not defined, this will size itself to fit all of its
+     * children (i.e. a WrappedDimension). Intended for testing purposes only.
+     */
+    @Nullable
+    public ContainerDimension getHeight() {
+      if (mImpl.hasHeight()) {
+        return DimensionBuilders.containerDimensionFromProto(mImpl.getHeight());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Modifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return Modifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Column fromProto(@NonNull LayoutElementProto.Column proto) {
+      return new Column(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.Column toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+      return LayoutElementProto.LayoutElement.newBuilder().setColumn(mImpl).build();
+    }
+
+    /** Builder for {@link Column}. */
+    public static final class Builder implements LayoutElement.Builder {
+      private final LayoutElementProto.Column.Builder mImpl =
+          LayoutElementProto.Column.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1411218529);
+
+      public Builder() {}
+
+      /** Adds one item to the list of child elements to place inside this {@link Column}. */
+      @NonNull
+      public Builder addContent(@NonNull LayoutElement content) {
+        mImpl.addContents(content.toLayoutElementProto());
+        mFingerprint.addChildNode(checkNotNull(content.getFingerprint()));
+        return this;
+      }
+
+      /**
+       * Sets the horizontal alignment of elements inside this column, if they are narrower than the
+       * resulting width of the column. If not defined, defaults to HORIZONTAL_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setHorizontalAlignment(@NonNull HorizontalAlignmentProp horizontalAlignment) {
+        mImpl.setHorizontalAlignment(horizontalAlignment.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(horizontalAlignment.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the horizontal alignment of elements inside this column, if they are narrower
+       * than the resulting width of the column. If not defined, defaults to
+       * HORIZONTAL_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setHorizontalAlignment(@HorizontalAlignment int horizontalAlignment) {
+        mImpl.setHorizontalAlignment(
+                AlignmentProto.HorizontalAlignmentProp.newBuilder()
+                        .setValue(
+                                AlignmentProto.HorizontalAlignment.forNumber(
+                                        horizontalAlignment)));
+        return this;
+      }
+
+      /**
+       * Sets the width of this column. If not defined, this will size itself to fit all of its
+       * children (i.e. a WrappedDimension).
+       */
+      @NonNull
+      public Builder setWidth(@NonNull ContainerDimension width) {
+        mImpl.setWidth(width.toContainerDimensionProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(width.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the height of this column. If not defined, this will size itself to fit all of its
+       * children (i.e. a WrappedDimension).
+       */
+      @NonNull
+      public Builder setHeight(@NonNull ContainerDimension height) {
+        mImpl.setHeight(height.toContainerDimensionProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(height.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull Modifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+
+      @Override
+      @NonNull
+      public Column build() {
+        return new Column(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * A row of elements. Each child will be laid out horizontally, one after another (i.e. stacking
+   * to the right). This element will size itself to the smallest size required to hold all of its
+   * children (e.g. if it contains three elements sized 10x10, 20x20 and 30x30, the resulting row
+   * will be 60x30).
+   *
+   * <p>If specified, vertical_alignment can be used to control the gravity inside the container,
+   * affecting the vertical placement of children whose width are smaller than the resulting row
+   * height.
+   */
+  public static final class Row implements LayoutElement {
+    private final LayoutElementProto.Row mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Row(LayoutElementProto.Row impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the list of child elements to place inside this {@link Row}. Intended for testing
+     * purposes only.
+     */
+    @NonNull
+    public List<LayoutElement> getContents() {
+      List<LayoutElement> list = new ArrayList<>();
+      for (LayoutElementProto.LayoutElement item : mImpl.getContentsList()) {
+        list.add(LayoutElementBuilders.layoutElementFromProto(item));
+      }
+      return Collections.unmodifiableList(list);
+    }
+
+    /**
+     * Gets the vertical alignment of elements inside this row, if they are narrower than the
+     * resulting height of the row. If not defined, defaults to VERTICAL_ALIGN_CENTER. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public VerticalAlignmentProp getVerticalAlignment() {
+      if (mImpl.hasVerticalAlignment()) {
+        return VerticalAlignmentProp.fromProto(mImpl.getVerticalAlignment());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the width of this row. If not defined, this will size itself to fit all of its children
+     * (i.e. a WrappedDimension). Intended for testing purposes only.
+     */
+    @Nullable
+    public ContainerDimension getWidth() {
+      if (mImpl.hasWidth()) {
+        return DimensionBuilders.containerDimensionFromProto(mImpl.getWidth());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the height of this row. If not defined, this will size itself to fit all of its children
+     * (i.e. a WrappedDimension). Intended for testing purposes only.
+     */
+    @Nullable
+    public ContainerDimension getHeight() {
+      if (mImpl.hasHeight()) {
+        return DimensionBuilders.containerDimensionFromProto(mImpl.getHeight());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Modifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return Modifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Row fromProto(@NonNull LayoutElementProto.Row proto) {
+      return new Row(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.Row toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+      return LayoutElementProto.LayoutElement.newBuilder().setRow(mImpl).build();
+    }
+
+    /** Builder for {@link Row}. */
+    public static final class Builder implements LayoutElement.Builder {
+      private final LayoutElementProto.Row.Builder mImpl = LayoutElementProto.Row.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1537205448);
+
+      public Builder() {}
+
+      /** Adds one item to the list of child elements to place inside this {@link Row}. */
+      @NonNull
+      public Builder addContent(@NonNull LayoutElement content) {
+        mImpl.addContents(content.toLayoutElementProto());
+        mFingerprint.addChildNode(checkNotNull(content.getFingerprint()));
+        return this;
+      }
+
+      /**
+       * Sets the vertical alignment of elements inside this row, if they are narrower than the
+       * resulting height of the row. If not defined, defaults to VERTICAL_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setVerticalAlignment(@NonNull VerticalAlignmentProp verticalAlignment) {
+        mImpl.setVerticalAlignment(verticalAlignment.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(verticalAlignment.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the vertical alignment of elements inside this row, if they are narrower than
+       * the resulting height of the row. If not defined, defaults to VERTICAL_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setVerticalAlignment(@VerticalAlignment int verticalAlignment) {
+        mImpl.setVerticalAlignment(
+                AlignmentProto.VerticalAlignmentProp.newBuilder()
+                        .setValue(
+                                AlignmentProto.VerticalAlignment.forNumber(
+                                        verticalAlignment)));
+        return this;
+      }
+
+      /**
+       * Sets the width of this row. If not defined, this will size itself to fit all of its
+       * children (i.e. a WrappedDimension).
+       */
+      @NonNull
+      public Builder setWidth(@NonNull ContainerDimension width) {
+        mImpl.setWidth(width.toContainerDimensionProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(width.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the height of this row. If not defined, this will size itself to fit all of its
+       * children (i.e. a WrappedDimension).
+       */
+      @NonNull
+      public Builder setHeight(@NonNull ContainerDimension height) {
+        mImpl.setHeight(height.toContainerDimensionProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(height.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull Modifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+
+      @Override
+      @NonNull
+      public Row build() {
+        return new Row(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * An arc container. This container will fill itself to a circle, which fits inside its parent
+   * container, and all of its children will be placed on that circle. The fields anchor_angle and
+   * anchor_type can be used to specify where to draw children within this circle.
+   */
+  public static final class Arc implements LayoutElement {
+    private final LayoutElementProto.Arc mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Arc(LayoutElementProto.Arc impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets contents of this container. Intended for testing purposes only. */
+    @NonNull
+    public List<ArcLayoutElement> getContents() {
+      List<ArcLayoutElement> list = new ArrayList<>();
+      for (LayoutElementProto.ArcLayoutElement item : mImpl.getContentsList()) {
+        list.add(LayoutElementBuilders.arcLayoutElementFromProto(item));
+      }
+      return Collections.unmodifiableList(list);
+    }
+
+    /**
+     * Gets the angle for the anchor, used with anchor_type to determine where to draw children.
+     * Note that 0 degrees is the 12 o clock position on a device, and the angle sweeps clockwise.
+     * If not defined, defaults to 0 degrees.
+     *
+     * <p>Values do not have to be clamped to the range 0-360; values less than 0 degrees will sweep
+     * anti-clockwise (i.e. -90 degrees is equivalent to 270 degrees), and values >360 will be be
+     * placed at X mod 360 degrees. Intended for testing purposes only.
+     */
+    @Nullable
+    public DegreesProp getAnchorAngle() {
+      if (mImpl.hasAnchorAngle()) {
+        return DegreesProp.fromProto(mImpl.getAnchorAngle());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets how to align the contents of this container relative to anchor_angle. If not defined,
+     * defaults to ARC_ANCHOR_CENTER. Intended for testing purposes only.
+     */
+    @Nullable
+    public ArcAnchorTypeProp getAnchorType() {
+      if (mImpl.hasAnchorType()) {
+        return ArcAnchorTypeProp.fromProto(mImpl.getAnchorType());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets vertical alignment of elements within the arc. If the {@link Arc}'s thickness is larger
+     * than the thickness of the element being drawn, this controls whether the element should be
+     * drawn towards the inner or outer edge of the arc, or drawn in the center. If not defined,
+     * defaults to VERTICAL_ALIGN_CENTER. Intended for testing purposes only.
+     */
+    @Nullable
+    public VerticalAlignmentProp getVerticalAlign() {
+      if (mImpl.hasVerticalAlign()) {
+        return VerticalAlignmentProp.fromProto(mImpl.getVerticalAlign());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Modifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return Modifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Arc fromProto(@NonNull LayoutElementProto.Arc proto) {
+      return new Arc(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.Arc toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+      return LayoutElementProto.LayoutElement.newBuilder().setArc(mImpl).build();
+    }
+
+    /** Builder for {@link Arc}. */
+    public static final class Builder implements LayoutElement.Builder {
+      private final LayoutElementProto.Arc.Builder mImpl = LayoutElementProto.Arc.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(299028337);
+
+      public Builder() {}
+
+      /** Adds one item to contents of this container. */
+      @NonNull
+      public Builder addContent(@NonNull ArcLayoutElement content) {
+        mImpl.addContents(content.toArcLayoutElementProto());
+        mFingerprint.addChildNode(checkNotNull(content.getFingerprint()));
+        return this;
+      }
+
+      /**
+       * Sets the angle for the anchor, used with anchor_type to determine where to draw children.
+       * Note that 0 degrees is the 12 o clock position on a device, and the angle sweeps clockwise.
+       * If not defined, defaults to 0 degrees.
+       *
+       * <p>Values do not have to be clamped to the range 0-360; values less than 0 degrees will
+       * sweep anti-clockwise (i.e. -90 degrees is equivalent to 270 degrees), and values >360 will
+       * be be placed at X mod 360 degrees.
+       */
+      @NonNull
+      public Builder setAnchorAngle(@NonNull DegreesProp anchorAngle) {
+        mImpl.setAnchorAngle(anchorAngle.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(anchorAngle.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets how to align the contents of this container relative to anchor_angle. If not defined,
+       * defaults to ARC_ANCHOR_CENTER.
+       */
+      @NonNull
+      public Builder setAnchorType(@NonNull ArcAnchorTypeProp anchorType) {
+        mImpl.setAnchorType(anchorType.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(anchorType.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets how to align the contents of this container relative to anchor_angle. If not
+       * defined, defaults to ARC_ANCHOR_CENTER.
+       */
+      @NonNull
+      public Builder setAnchorType(@ArcAnchorType int anchorType) {
+        mImpl.setAnchorType(
+                AlignmentProto.ArcAnchorTypeProp.newBuilder()
+                        .setValue(AlignmentProto.ArcAnchorType.forNumber(anchorType)));
+        return this;
+      }
+
+      /**
+       * Sets vertical alignment of elements within the arc. If the {@link Arc}'s thickness is
+       * larger than the thickness of the element being drawn, this controls whether the element
+       * should be drawn towards the inner or outer edge of the arc, or drawn in the center. If not
+       * defined, defaults to VERTICAL_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setVerticalAlign(@NonNull VerticalAlignmentProp verticalAlign) {
+        mImpl.setVerticalAlign(verticalAlign.toProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(verticalAlign.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+      /**
+       * Sets vertical alignment of elements within the arc. If the {@link Arc}'s thickness is
+       * larger than the thickness of the element being drawn, this controls whether the
+       * element should be drawn towards the inner or outer edge of the arc, or drawn in the
+       * center. If not defined, defaults to VERTICAL_ALIGN_CENTER.
+       */
+      @NonNull
+      public Builder setVerticalAlign(@VerticalAlignment int verticalAlign) {
+        mImpl.setVerticalAlign(
+                AlignmentProto.VerticalAlignmentProp.newBuilder()
+                        .setValue(
+                                AlignmentProto.VerticalAlignment.forNumber(
+                                        verticalAlign)));
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull Modifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+
+      @Override
+      @NonNull
+      public Arc build() {
+        return new Arc(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A text element that can be used in an {@link Arc}. */
+  public static final class ArcText implements ArcLayoutElement {
+    private final LayoutElementProto.ArcText mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ArcText(LayoutElementProto.ArcText impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the text to render. Intended for testing purposes only. */
+    @Nullable
+    public StringProp getText() {
+      if (mImpl.hasText()) {
+        return StringProp.fromProto(mImpl.getText());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the style of font to use (size, bold etc). If not specified, defaults to the platform's
+     * default body font. Intended for testing purposes only.
+     */
+    @Nullable
+    public FontStyle getFontStyle() {
+      if (mImpl.hasFontStyle()) {
+        return FontStyle.fromProto(mImpl.getFontStyle());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public ArcModifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return ArcModifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ArcText fromProto(@NonNull LayoutElementProto.ArcText proto) {
+      return new ArcText(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.ArcText toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.ArcLayoutElement toArcLayoutElementProto() {
+      return LayoutElementProto.ArcLayoutElement.newBuilder().setText(mImpl).build();
+    }
+
+    /** Builder for {@link ArcText}. */
+    public static final class Builder implements ArcLayoutElement.Builder {
+      private final LayoutElementProto.ArcText.Builder mImpl =
+          LayoutElementProto.ArcText.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(434391973);
+
+      public Builder() {}
+
+      /**
+       * Sets the text to render.
+       */
+      @NonNull
+      public Builder setText(@NonNull StringProp text) {
+        mImpl.setText(text.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(text.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets the text to render. */
+      @NonNull
+      public Builder setText(@NonNull String text) {
+        mImpl.setText(TypesProto.StringProp.newBuilder().setValue(text));
+        return this;
+      }
+
+      /**
+       * Sets the style of font to use (size, bold etc). If not specified, defaults to the
+       * platform's default body font.
+       */
+      @NonNull
+      public Builder setFontStyle(@NonNull FontStyle fontStyle) {
+        mImpl.setFontStyle(fontStyle.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(fontStyle.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull ArcModifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+
+      @Override
+      @NonNull
+      public ArcText build() {
+        return new ArcText(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A line that can be used in an {@link Arc} and renders as a round progress bar. */
+  public static final class ArcLine implements ArcLayoutElement {
+    private final LayoutElementProto.ArcLine mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ArcLine(LayoutElementProto.ArcLine impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the length of this line, in degrees. If not defined, defaults to 0. Intended for testing
+     * purposes only.
+     */
+    @Nullable
+    public DegreesProp getLength() {
+      if (mImpl.hasLength()) {
+        return DegreesProp.fromProto(mImpl.getLength());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the thickness of this line. If not defined, defaults to 0. Intended for testing purposes
+     * only.
+     */
+    @Nullable
+    public DpProp getThickness() {
+      if (mImpl.hasThickness()) {
+        return DpProp.fromProto(mImpl.getThickness());
+      } else {
+        return null;
+      }
+    }
+
+    /** Gets the color of this line. Intended for testing purposes only. */
+    @Nullable
+    public ColorProp getColor() {
+      if (mImpl.hasColor()) {
+        return ColorProp.fromProto(mImpl.getColor());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public ArcModifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return ArcModifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ArcLine fromProto(@NonNull LayoutElementProto.ArcLine proto) {
+      return new ArcLine(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.ArcLine toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.ArcLayoutElement toArcLayoutElementProto() {
+      return LayoutElementProto.ArcLayoutElement.newBuilder().setLine(mImpl).build();
+    }
+
+    /** Builder for {@link ArcLine}. */
+    public static final class Builder implements ArcLayoutElement.Builder {
+      private final LayoutElementProto.ArcLine.Builder mImpl =
+          LayoutElementProto.ArcLine.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1371793535);
+
+      public Builder() {}
+
+      /**
+       * Sets the length of this line, in degrees. If not defined, defaults to 0.
+       */
+      @NonNull
+      public Builder setLength(@NonNull DegreesProp length) {
+        mImpl.setLength(length.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(length.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the thickness of this line. If not defined, defaults to 0.
+       */
+      @NonNull
+      public Builder setThickness(@NonNull DpProp thickness) {
+        mImpl.setThickness(thickness.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(thickness.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the color of this line.
+       */
+      @NonNull
+      public Builder setColor(@NonNull ColorProp color) {
+        mImpl.setColor(color.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(color.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull ArcModifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+
+      @Override
+      @NonNull
+      public ArcLine build() {
+        return new ArcLine(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A simple spacer used to provide padding between adjacent elements in an {@link Arc}. */
+  public static final class ArcSpacer implements ArcLayoutElement {
+    private final LayoutElementProto.ArcSpacer mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ArcSpacer(LayoutElementProto.ArcSpacer impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the length of this spacer, in degrees. If not defined, defaults to 0. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public DegreesProp getLength() {
+      if (mImpl.hasLength()) {
+        return DegreesProp.fromProto(mImpl.getLength());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the thickness of this spacer, in DP. If not defined, defaults to 0. Intended for testing
+     * purposes only.
+     */
+    @Nullable
+    public DpProp getThickness() {
+      if (mImpl.hasThickness()) {
+        return DpProp.fromProto(mImpl.getThickness());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public ArcModifiers getModifiers() {
+      if (mImpl.hasModifiers()) {
+        return ArcModifiers.fromProto(mImpl.getModifiers());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ArcSpacer fromProto(@NonNull LayoutElementProto.ArcSpacer proto) {
+      return new ArcSpacer(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.ArcSpacer toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.ArcLayoutElement toArcLayoutElementProto() {
+      return LayoutElementProto.ArcLayoutElement.newBuilder().setSpacer(mImpl).build();
+    }
+
+    /** Builder for {@link ArcSpacer}. */
+    public static final class Builder implements ArcLayoutElement.Builder {
+      private final LayoutElementProto.ArcSpacer.Builder mImpl =
+          LayoutElementProto.ArcSpacer.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-179760535);
+
+      public Builder() {}
+
+      /**
+       * Sets the length of this spacer, in degrees. If not defined, defaults to 0.
+       */
+      @NonNull
+      public Builder setLength(@NonNull DegreesProp length) {
+        mImpl.setLength(length.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(length.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the thickness of this spacer, in DP. If not defined, defaults to 0.
+       */
+      @NonNull
+      public Builder setThickness(@NonNull DpProp thickness) {
+        mImpl.setThickness(thickness.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(thickness.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+      @NonNull
+      public Builder setModifiers(@NonNull ArcModifiers modifiers) {
+        mImpl.setModifiers(modifiers.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public ArcSpacer build() {
+        return new ArcSpacer(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A container that allows a standard {@link LayoutElement} to be added to an {@link Arc}. */
+  public static final class ArcAdapter implements ArcLayoutElement {
+    private final LayoutElementProto.ArcAdapter mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ArcAdapter(LayoutElementProto.ArcAdapter impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the element to adapt to an {@link Arc}. Intended for testing purposes only. */
+    @Nullable
+    public LayoutElement getContent() {
+      if (mImpl.hasContent()) {
+        return LayoutElementBuilders.layoutElementFromProto(mImpl.getContent());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets whether this adapter's contents should be rotated, according to its position in the arc
+     * or not. As an example, assume that an {@link Image} has been added to the arc, and ends up at
+     * the 3 o clock position. If rotate_contents = true, the image will be placed at the 3 o clock
+     * position, and will be rotated clockwise through 90 degrees. If rotate_contents = false, the
+     * image will be placed at the 3 o clock position, but itself will not be rotated. If not
+     * defined, defaults to false. Intended for testing purposes only.
+     */
+    @Nullable
+    public BoolProp getRotateContents() {
+      if (mImpl.hasRotateContents()) {
+        return BoolProp.fromProto(mImpl.getRotateContents());
+      } else {
+        return null;
+      }
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ArcAdapter fromProto(@NonNull LayoutElementProto.ArcAdapter proto) {
+      return new ArcAdapter(proto, null);
+    }
+
+    @NonNull
+    LayoutElementProto.ArcAdapter toProto() {
+      return mImpl;
+    }
+
+    /** @hide */
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.ArcLayoutElement toArcLayoutElementProto() {
+      return LayoutElementProto.ArcLayoutElement.newBuilder().setAdapter(mImpl).build();
+    }
+
+    /** Builder for {@link ArcAdapter}. */
+    public static final class Builder implements ArcLayoutElement.Builder {
+      private final LayoutElementProto.ArcAdapter.Builder mImpl =
+          LayoutElementProto.ArcAdapter.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1696473935);
+
+      public Builder() {}
+
+      /** Sets the element to adapt to an {@link Arc}. */
+      @NonNull
+      public Builder setContent(@NonNull LayoutElement content) {
+        mImpl.setContent(content.toLayoutElementProto());
+        mFingerprint.addChildNode(checkNotNull(content.getFingerprint()));
+        return this;
+      }
+
+      /**
+       * Sets whether this adapter's contents should be rotated, according to its position in the
+       * arc or not. As an example, assume that an {@link Image} has been added to the arc, and ends
+       * up at the 3 o clock position. If rotate_contents = true, the image will be placed at the 3
+       * o clock position, and will be rotated clockwise through 90 degrees. If rotate_contents =
+       * false, the image will be placed at the 3 o clock position, but itself will not be rotated.
+       * If not defined, defaults to false.
+       */
+      @NonNull
+      public Builder setRotateContents(@NonNull BoolProp rotateContents) {
+        mImpl.setRotateContents(rotateContents.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(rotateContents.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets whether this adapter's contents should be rotated, according to its position in
+       * the arc or not. As an example, assume that an {@link Image} has been added to the
+       * arc, and ends up at the 3 o clock position. If rotate_contents = true, the image will
+       * be placed at the 3 o clock position, and will be rotated clockwise through 90
+       * degrees. If rotate_contents = false, the image will be placed at the 3 o clock
+       * position, but itself will not be rotated. If not defined, defaults to false.
+       */
+      @SuppressLint("MissingGetterMatchingBuilder")
+      @NonNull
+      public Builder setRotateContents(boolean rotateContents) {
+        mImpl.setRotateContents(TypesProto.BoolProp.newBuilder().setValue(rotateContents));
+        return this;
+      }
+
+      @Override
+      @NonNull
+      public ArcAdapter build() {
+        return new ArcAdapter(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * Interface defining the root of all layout elements. This exists to act as a holder for all of
+   * the actual layout elements above.
+   */
+  public interface LayoutElement {
+    /**
+     * Get the protocol buffer representation of this object.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    LayoutElementProto.LayoutElement toLayoutElementProto();
+
+    /**
+     * Get the fingerprint for this object or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    Fingerprint getFingerprint();
+
+    /** Builder to create {@link LayoutElement} objects. */
+    @SuppressLint("StaticFinalBuilder")
+    interface Builder {
+
+      /** Builds an instance with values accumulated in this Builder. */
+      @NonNull
+      LayoutElement build();
+    }
+  }
+
+  @NonNull
+  static LayoutElement layoutElementFromProto(@NonNull LayoutElementProto.LayoutElement proto) {
+    if (proto.hasColumn()) {
+      return Column.fromProto(proto.getColumn());
+    }
+    if (proto.hasRow()) {
+      return Row.fromProto(proto.getRow());
+    }
+    if (proto.hasBox()) {
+      return Box.fromProto(proto.getBox());
+    }
+    if (proto.hasSpacer()) {
+      return Spacer.fromProto(proto.getSpacer());
+    }
+    if (proto.hasText()) {
+      return Text.fromProto(proto.getText());
+    }
+    if (proto.hasImage()) {
+      return Image.fromProto(proto.getImage());
+    }
+    if (proto.hasArc()) {
+      return Arc.fromProto(proto.getArc());
+    }
+    if (proto.hasSpannable()) {
+      return Spannable.fromProto(proto.getSpannable());
+    }
+    throw new IllegalStateException("Proto was not a recognised instance of LayoutElement");
+  }
+
+  /**
+   * Interface defining the root of all elements that can be used in an {@link Arc}. This exists to
+   * act as a holder for all of the actual arc layout elements above.
+   */
+  public interface ArcLayoutElement {
+    /**
+     * Get the protocol buffer representation of this object.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    LayoutElementProto.ArcLayoutElement toArcLayoutElementProto();
+
+    /**
+     * Get the fingerprint for this object or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    Fingerprint getFingerprint();
+
+    /** Builder to create {@link ArcLayoutElement} objects. */
+    @SuppressLint("StaticFinalBuilder")
+    interface Builder {
+
+      /** Builds an instance with values accumulated in this Builder. */
+      @NonNull
+      ArcLayoutElement build();
+    }
+  }
+
+  @NonNull
+  static ArcLayoutElement arcLayoutElementFromProto(
+      @NonNull LayoutElementProto.ArcLayoutElement proto) {
+    if (proto.hasText()) {
+      return ArcText.fromProto(proto.getText());
+    }
+    if (proto.hasLine()) {
+      return ArcLine.fromProto(proto.getLine());
+    }
+    if (proto.hasSpacer()) {
+      return ArcSpacer.fromProto(proto.getSpacer());
+    }
+    if (proto.hasAdapter()) {
+      return ArcAdapter.fromProto(proto.getAdapter());
+    }
+    throw new IllegalStateException("Proto was not a recognised instance of ArcLayoutElement");
+  }
+
+  /** A complete layout. */
+  public static final class Layout {
+    private final LayoutElementProto.Layout mImpl;
+
+    private Layout(LayoutElementProto.Layout impl) {
+      this.mImpl = impl;
+    }
+
+    /** Gets the root element in the layout. Intended for testing purposes only. */
+    @Nullable
+    public LayoutElement getRoot() {
+      if (mImpl.hasRoot()) {
+        return LayoutElementBuilders.layoutElementFromProto(mImpl.getRoot());
+      } else {
+        return null;
+      }
+    }
+
+    /** Creates a {@link Layout} object containing the given layout element. */
+    @NonNull
+    public static Layout fromLayoutElement(@NonNull LayoutElement layoutElement) {
+      return new Builder().setRoot(layoutElement).build();
+    }
+
+    /** Converts to byte array representation. */
+    @NonNull
+    @ProtoLayoutExperimental
+    public byte[] toByteArray() {
+      return mImpl.toByteArray();
+    }
+
+    /** Converts from byte array representation. */
+    @SuppressWarnings("ProtoParseWithRegistry")
+    @Nullable
+    @ProtoLayoutExperimental
+    public static Layout fromByteArray(@NonNull byte[] byteArray) {
+      try {
+        return fromProto(LayoutElementProto.Layout.parseFrom(byteArray));
+      } catch (InvalidProtocolBufferException e) {
+        return null;
+      }
+    }
+
+    @NonNull
+    static Layout fromProto(@NonNull LayoutElementProto.Layout proto) {
+      return new Layout(proto);
+    }
+
+    /**
+     * Returns the internal proto instance.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public LayoutElementProto.Layout toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Layout} */
+    public static final class Builder {
+      private final LayoutElementProto.Layout.Builder mImpl =
+          LayoutElementProto.Layout.newBuilder();
+
+      public Builder() {}
+
+      /** Sets the root element in the layout. */
+      @NonNull
+      public Builder setRoot(@NonNull LayoutElement root) {
+        mImpl.setRoot(root.toLayoutElementProto());
+        @Nullable Fingerprint fingerprint = root.getFingerprint();
+        if (fingerprint != null) {
+          mImpl.setFingerprint(
+              TreeFingerprint.newBuilder().setRoot(fingerprintToProto(fingerprint)));
+        }
+        return this;
+      }
+
+      private static FingerprintProto.NodeFingerprint fingerprintToProto(Fingerprint fingerprint) {
+        FingerprintProto.NodeFingerprint.Builder builder =
+            FingerprintProto.NodeFingerprint.newBuilder();
+        if (fingerprint.selfTypeValue() != 0) {
+          builder.setSelfTypeValue(fingerprint.selfTypeValue());
+        }
+        if (fingerprint.selfPropsValue() != 0) {
+          builder.setSelfPropsValue(fingerprint.selfPropsValue());
+        }
+        if (fingerprint.childNodesValue() != 0) {
+          builder.setChildNodesValue(fingerprint.childNodesValue());
+        }
+        for (Fingerprint childNode : fingerprint.childNodes()) {
+          builder.addChildNodes(fingerprintToProto(childNode));
+        }
+        return builder.build();
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Layout build() {
+        return Layout.fromProto(mImpl.build());
+      }
+    }
+  }
+
+  /**
+   * The horizontal alignment of an element within its container.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({
+    HORIZONTAL_ALIGN_UNDEFINED,
+    HORIZONTAL_ALIGN_LEFT,
+    HORIZONTAL_ALIGN_CENTER,
+    HORIZONTAL_ALIGN_RIGHT,
+    HORIZONTAL_ALIGN_START,
+    HORIZONTAL_ALIGN_END
+  })
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface HorizontalAlignment {}
+
+  /** Horizontal alignment is undefined. */
+  public static final int HORIZONTAL_ALIGN_UNDEFINED = 0;
+
+  /** Horizontally align to the left. */
+  public static final int HORIZONTAL_ALIGN_LEFT = 1;
+
+  /** Horizontally align to center. */
+  public static final int HORIZONTAL_ALIGN_CENTER = 2;
+
+  /** Horizontally align to the right. */
+  public static final int HORIZONTAL_ALIGN_RIGHT = 3;
+
+  /** Horizontally align to the content start (left in LTR layouts, right in RTL layouts). */
+  public static final int HORIZONTAL_ALIGN_START = 4;
+
+  /** Horizontally align to the content end (right in LTR layouts, left in RTL layouts). */
+  public static final int HORIZONTAL_ALIGN_END = 5;
+
+  /**
+   * The vertical alignment of an element within its container.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({
+    VERTICAL_ALIGN_UNDEFINED,
+    VERTICAL_ALIGN_TOP,
+    VERTICAL_ALIGN_CENTER,
+    VERTICAL_ALIGN_BOTTOM
+  })
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface VerticalAlignment {}
+
+  /** Vertical alignment is undefined. */
+  public static final int VERTICAL_ALIGN_UNDEFINED = 0;
+
+  /** Vertically align to the top. */
+  public static final int VERTICAL_ALIGN_TOP = 1;
+
+  /** Vertically align to center. */
+  public static final int VERTICAL_ALIGN_CENTER = 2;
+
+  /** Vertically align to the bottom. */
+  public static final int VERTICAL_ALIGN_BOTTOM = 3;
+
+  /**
+   * Alignment of a text element.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({TEXT_ALIGN_UNDEFINED, TEXT_ALIGN_START, TEXT_ALIGN_CENTER, TEXT_ALIGN_END})
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface TextAlignment {}
+
+  /** Alignment is undefined. */
+  public static final int TEXT_ALIGN_UNDEFINED = 0;
+
+  /**
+   * Align to the "start" of the {@link androidx.wear.tiles.LayoutElementBuilders.Text} element
+   * (left in LTR layouts, right in RTL layouts).
+   */
+  public static final int TEXT_ALIGN_START = 1;
+
+  /** Align to the center of the {@link androidx.wear.tiles.LayoutElementBuilders.Text} element. */
+  public static final int TEXT_ALIGN_CENTER = 2;
+
+  /**
+   * Align to the "end" of the {@link androidx.wear.tiles.LayoutElementBuilders.Text} element (right
+   * in LTR layouts, left in RTL layouts).
+   */
+  public static final int TEXT_ALIGN_END = 3;
+
+  /**
+   * The anchor position of an {@link androidx.wear.tiles.LayoutElementBuilders.Arc}'s elements.
+   * This is used to specify how elements added to an {@link
+   * androidx.wear.tiles.LayoutElementBuilders.Arc} should be laid out with respect to anchor_angle.
+   *
+   * <p>As an example, assume that the following diagrams are wrapped to an arc, and each represents
+   * an {@link androidx.wear.tiles.LayoutElementBuilders.Arc} element containing a single {@link
+   * androidx.wear.tiles.LayoutElementBuilders.Text} element. The {@link
+   * androidx.wear.tiles.LayoutElementBuilders.Text} element's anchor_angle is "0" for all cases.
+   *
+   * <pre>{@code
+   * ARC_ANCHOR_START:
+   * -180                                0                                    180
+   *                                     Hello World!
+   *
+   *
+   * ARC_ANCHOR_CENTER:
+   * -180                                0                                    180
+   *                                Hello World!
+   *
+   * ARC_ANCHOR_END:
+   * -180                                0                                    180
+   *                          Hello World!
+   *
+   * }</pre>
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({ARC_ANCHOR_UNDEFINED, ARC_ANCHOR_START, ARC_ANCHOR_CENTER, ARC_ANCHOR_END})
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface ArcAnchorType {}
+
+  /** Anchor position is undefined. */
+  public static final int ARC_ANCHOR_UNDEFINED = 0;
+
+  /**
+   * Anchor at the start of the elements. This will cause elements added to an arc to begin at the
+   * given anchor_angle, and sweep around to the right.
+   */
+  public static final int ARC_ANCHOR_START = 1;
+
+  /**
+   * Anchor at the center of the elements. This will cause the center of the whole set of elements
+   * added to an arc to be pinned at the given anchor_angle.
+   */
+  public static final int ARC_ANCHOR_CENTER = 2;
+
+  /**
+   * Anchor at the end of the elements. This will cause the set of elements inside the arc to end at
+   * the specified anchor_angle, i.e. all elements should be to the left of anchor_angle.
+   */
+  public static final int ARC_ANCHOR_END = 3;
+
+  /** An extensible {@code HorizontalAlignment} property. */
+  public static final class HorizontalAlignmentProp {
+    private final AlignmentProto.HorizontalAlignmentProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    HorizontalAlignmentProp(
+        AlignmentProto.HorizontalAlignmentProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @HorizontalAlignment
+    public int getValue() {
+      return mImpl.getValue().getNumber();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static HorizontalAlignmentProp fromProto(
+        @NonNull AlignmentProto.HorizontalAlignmentProp proto) {
+      return new HorizontalAlignmentProp(proto, null);
+    }
+
+    @NonNull
+    AlignmentProto.HorizontalAlignmentProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link HorizontalAlignmentProp} */
+    public static final class Builder {
+      private final AlignmentProto.HorizontalAlignmentProp.Builder mImpl =
+          AlignmentProto.HorizontalAlignmentProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-384830516);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@HorizontalAlignment int value) {
+        mImpl.setValue(AlignmentProto.HorizontalAlignment.forNumber(value));
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public HorizontalAlignmentProp build() {
+        return new HorizontalAlignmentProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** An extensible {@code VerticalAlignment} property. */
+  public static final class VerticalAlignmentProp {
+    private final AlignmentProto.VerticalAlignmentProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    VerticalAlignmentProp(
+        AlignmentProto.VerticalAlignmentProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @VerticalAlignment
+    public int getValue() {
+      return mImpl.getValue().getNumber();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static VerticalAlignmentProp fromProto(@NonNull AlignmentProto.VerticalAlignmentProp proto) {
+      return new VerticalAlignmentProp(proto, null);
+    }
+
+    @NonNull
+    AlignmentProto.VerticalAlignmentProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link VerticalAlignmentProp} */
+    public static final class Builder {
+      private final AlignmentProto.VerticalAlignmentProp.Builder mImpl =
+          AlignmentProto.VerticalAlignmentProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1443510393);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@VerticalAlignment int value) {
+        mImpl.setValue(AlignmentProto.VerticalAlignment.forNumber(value));
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public VerticalAlignmentProp build() {
+        return new VerticalAlignmentProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** An extensible {@code TextAlignment} property. */
+  public static final class TextAlignmentProp {
+    private final AlignmentProto.TextAlignmentProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    TextAlignmentProp(
+        AlignmentProto.TextAlignmentProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @TextAlignment
+    public int getValue() {
+      return mImpl.getValue().getNumber();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static TextAlignmentProp fromProto(@NonNull AlignmentProto.TextAlignmentProp proto) {
+      return new TextAlignmentProp(proto, null);
+    }
+
+    @NonNull
+    AlignmentProto.TextAlignmentProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link TextAlignmentProp} */
+    public static final class Builder {
+      private final AlignmentProto.TextAlignmentProp.Builder mImpl =
+          AlignmentProto.TextAlignmentProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(797507251);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@TextAlignment int value) {
+        mImpl.setValue(AlignmentProto.TextAlignment.forNumber(value));
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public TextAlignmentProp build() {
+        return new TextAlignmentProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** An extensible {@code ArcAnchorType} property. */
+  public static final class ArcAnchorTypeProp {
+    private final AlignmentProto.ArcAnchorTypeProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ArcAnchorTypeProp(
+        AlignmentProto.ArcAnchorTypeProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @ArcAnchorType
+    public int getValue() {
+      return mImpl.getValue().getNumber();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ArcAnchorTypeProp fromProto(@NonNull AlignmentProto.ArcAnchorTypeProp proto) {
+      return new ArcAnchorTypeProp(proto, null);
+    }
+
+    @NonNull
+    AlignmentProto.ArcAnchorTypeProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link ArcAnchorTypeProp} */
+    public static final class Builder {
+      private final AlignmentProto.ArcAnchorTypeProp.Builder mImpl =
+          AlignmentProto.ArcAnchorTypeProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(1193249074);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@ArcAnchorType int value) {
+        mImpl.setValue(AlignmentProto.ArcAnchorType.forNumber(value));
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public ArcAnchorTypeProp build() {
+        return new ArcAnchorTypeProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** Font styles, currently set up to match Wear's font styling. */
+  public static class FontStyles {
+    private static final int LARGE_SCREEN_WIDTH_DP = 210;
+
+    private FontStyles() {
+    }
+
+    private static boolean isLargeScreen(@NonNull DeviceParameters deviceParameters) {
+      return deviceParameters.getScreenWidthDp() >= LARGE_SCREEN_WIDTH_DP;
+    }
+
+    /** Font style for large display text. */
+    @NonNull
+    public static FontStyle.Builder display1(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setWeight(FONT_WEIGHT_BOLD)
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 54 : 50));
+    }
+
+    /** Font style for medium display text. */
+    @NonNull
+    public static FontStyle.Builder display2(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setWeight(FONT_WEIGHT_BOLD)
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 44 : 40));
+    }
+
+    /** Font style for small display text. */
+    @NonNull
+    public static FontStyle.Builder display3(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setWeight(FONT_WEIGHT_BOLD)
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 34 : 30));
+    }
+
+    /** Font style for large title text. */
+    @NonNull
+    public static FontStyle.Builder title1(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setWeight(FONT_WEIGHT_BOLD)
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 26 : 24));
+    }
+
+    /** Font style for medium title text. */
+    @NonNull
+    public static FontStyle.Builder title2(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setWeight(FONT_WEIGHT_BOLD)
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 22 : 20));
+    }
+
+    /** Font style for small title text. */
+    @NonNull
+    public static FontStyle.Builder title3(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setWeight(FONT_WEIGHT_BOLD)
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 18 : 16));
+    }
+
+    /** Font style for large body text. */
+    @NonNull
+    public static FontStyle.Builder body1(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 18 : 16));
+    }
+
+    /** Font style for medium body text. */
+    @NonNull
+    public static FontStyle.Builder body2(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 16 : 14));
+    }
+
+    /** Font style for button text. */
+    @NonNull
+    public static FontStyle.Builder button(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setWeight(FONT_WEIGHT_BOLD)
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 16 : 14));
+    }
+
+    /** Font style for large caption text. */
+    @NonNull
+    public static FontStyle.Builder caption1(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 16 : 14));
+    }
+
+    /** Font style for medium caption text. */
+    @NonNull
+    public static FontStyle.Builder caption2(@NonNull DeviceParameters deviceParameters) {
+      return new FontStyle.Builder()
+              .setSize(DimensionBuilders.sp(isLargeScreen(deviceParameters) ? 14 : 12));
+    }
+  }
+
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java
new file mode 100644
index 0000000..cc326a4
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java
@@ -0,0 +1,1117 @@
+/*
+ * Copyright 2021-2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import static androidx.annotation.Dimension.DP;
+import static androidx.wear.protolayout.expression.Preconditions.checkNotNull;
+
+import android.annotation.SuppressLint;
+import androidx.annotation.ColorInt;
+import androidx.annotation.Dimension;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.expression.DynamicBuilders;
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.ColorProto;
+import androidx.wear.protolayout.proto.DimensionProto;
+import androidx.wear.protolayout.proto.ModifiersProto;
+import androidx.wear.protolayout.proto.TypesProto;
+import androidx.wear.protolayout.ActionBuilders.Action;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.TypeBuilders.BoolProp;
+import androidx.wear.protolayout.protobuf.ByteString;
+import java.util.Arrays;
+
+/** Builders for modifiers for composable layout elements. */
+public final class ModifiersBuilders {
+  private ModifiersBuilders() {}
+
+  /**
+   * A modifier for an element which can have associated Actions for click events. When an element
+   * with a ClickableModifier is clicked it will fire the associated action.
+   */
+  public static final class Clickable {
+    private final ModifiersProto.Clickable mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Clickable(ModifiersProto.Clickable impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the ID associated with this action. Intended for testing purposes only. */
+    @NonNull
+    public String getId() {
+      return mImpl.getId();
+    }
+
+    /**
+     * Gets the action to perform when the element this modifier is attached to is clicked. Intended
+     * for testing purposes only.
+     */
+    @Nullable
+    public Action getOnClick() {
+      if (mImpl.hasOnClick()) {
+        return ActionBuilders.actionFromProto(mImpl.getOnClick());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Clickable fromProto(@NonNull ModifiersProto.Clickable proto) {
+      return new Clickable(proto, null);
+    }
+
+    /**
+     * Returns the internal proto instance.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ModifiersProto.Clickable toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Clickable} */
+    public static final class Builder {
+      private final ModifiersProto.Clickable.Builder mImpl = ModifiersProto.Clickable.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(595587995);
+
+      public Builder() {}
+
+      /** Sets the ID associated with this action. */
+      @NonNull
+      public Builder setId(@NonNull String id) {
+        mImpl.setId(id);
+        mFingerprint.recordPropertyUpdate(1, id.hashCode());
+        return this;
+      }
+
+      /** Sets the action to perform when the element this modifier is attached to is clicked. */
+      @NonNull
+      public Builder setOnClick(@NonNull Action onClick) {
+        mImpl.setOnClick(onClick.toActionProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(onClick.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Clickable build() {
+        return new Clickable(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * A modifier for an element which has accessibility semantics associated with it. This should
+   * generally be used sparingly, and in most cases should only be applied to the top-level layout
+   * element or to Clickables.
+   */
+  public static final class Semantics {
+    private final ModifiersProto.Semantics mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Semantics(ModifiersProto.Semantics impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the content description associated with this element. This will be dictated when the
+     * element is focused by the screen reader. Intended for testing purposes only.
+     */
+    @NonNull
+    public String getContentDescription() {
+      if (mImpl.hasContentDescription()) {
+        return mImpl.getContentDescription().getValue();
+      }
+      return mImpl.getObsoleteContentDescription();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Semantics fromProto(@NonNull ModifiersProto.Semantics proto) {
+      return new Semantics(proto, null);
+    }
+
+    @NonNull
+    ModifiersProto.Semantics toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Semantics} */
+    public static final class Builder {
+      private final ModifiersProto.Semantics.Builder mImpl = ModifiersProto.Semantics.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1479823155);
+
+      public Builder() {}
+
+      /**
+       * Sets the content description associated with this element. This will be dictated when the
+       * element is focused by the screen reader.
+       */
+      @NonNull
+      public Builder setContentDescription(@NonNull String contentDescription) {
+        mImpl.setObsoleteContentDescription(contentDescription);
+        mImpl.mergeContentDescription(
+            TypesProto.StringProp.newBuilder().setValue(contentDescription).build());
+        mFingerprint.recordPropertyUpdate(4, contentDescription.hashCode());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Semantics build() {
+        return new Semantics(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A modifier to apply padding around an element. */
+  public static final class Padding {
+    private final ModifiersProto.Padding mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Padding(ModifiersProto.Padding impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the padding on the end of the content, depending on the layout direction, in DP and the
+     * value of "rtl_aware". Intended for testing purposes only.
+     */
+    @Nullable
+    public DpProp getEnd() {
+      if (mImpl.hasEnd()) {
+        return DpProp.fromProto(mImpl.getEnd());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the padding on the start of the content, depending on the layout direction, in DP and
+     * the value of "rtl_aware". Intended for testing purposes only.
+     */
+    @Nullable
+    public DpProp getStart() {
+      if (mImpl.hasStart()) {
+        return DpProp.fromProto(mImpl.getStart());
+      } else {
+        return null;
+      }
+    }
+
+    /** Gets the padding at the top, in DP. Intended for testing purposes only. */
+    @Nullable
+    public DpProp getTop() {
+      if (mImpl.hasTop()) {
+        return DpProp.fromProto(mImpl.getTop());
+      } else {
+        return null;
+      }
+    }
+
+    /** Gets the padding at the bottom, in DP. Intended for testing purposes only. */
+    @Nullable
+    public DpProp getBottom() {
+      if (mImpl.hasBottom()) {
+        return DpProp.fromProto(mImpl.getBottom());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets whether the start/end padding is aware of RTL support. If true, the values for start/end
+     * will follow the layout direction (i.e. start will refer to the right hand side of the
+     * container if the device is using an RTL locale). If false, start/end will always map to
+     * left/right, accordingly. Intended for testing purposes only.
+     */
+    @Nullable
+    public BoolProp getRtlAware() {
+      if (mImpl.hasRtlAware()) {
+        return BoolProp.fromProto(mImpl.getRtlAware());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Padding fromProto(@NonNull ModifiersProto.Padding proto) {
+      return new Padding(proto, null);
+    }
+
+    @NonNull
+    ModifiersProto.Padding toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Padding} */
+    public static final class Builder {
+      private final ModifiersProto.Padding.Builder mImpl = ModifiersProto.Padding.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1120275440);
+
+      public Builder() {}
+
+      /**
+       * Sets the padding on the end of the content, depending on the layout direction, in DP and
+       * the value of "rtl_aware".
+       */
+      @NonNull
+      public Builder setEnd(@NonNull DpProp end) {
+        mImpl.setEnd(end.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(end.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the padding on the start of the content, depending on the layout direction, in DP and
+       * the value of "rtl_aware".
+       */
+      @NonNull
+      public Builder setStart(@NonNull DpProp start) {
+        mImpl.setStart(start.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(start.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the padding at the top, in DP.
+       */
+      @NonNull
+      public Builder setTop(@NonNull DpProp top) {
+        mImpl.setTop(top.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(top.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the padding at the bottom, in DP.
+       */
+      @NonNull
+      public Builder setBottom(@NonNull DpProp bottom) {
+        mImpl.setBottom(bottom.toProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(bottom.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets whether the start/end padding is aware of RTL support. If true, the values for
+       * start/end will follow the layout direction (i.e. start will refer to the right hand side of
+       * the container if the device is using an RTL locale). If false, start/end will always map to
+       * left/right, accordingly.
+       */
+      @NonNull
+      public Builder setRtlAware(@NonNull BoolProp rtlAware) {
+        mImpl.setRtlAware(rtlAware.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(rtlAware.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets whether the start/end padding is aware of RTL support. If true, the values for
+       * start/end will follow the layout direction (i.e. start will refer to the right hand side of
+       * the container if the device is using an RTL locale). If false, start/end will always map to
+       * left/right, accordingly.
+       */
+      @SuppressLint("MissingGetterMatchingBuilder")
+      @NonNull
+      public Builder setRtlAware(boolean rtlAware) {
+        mImpl.setRtlAware(TypesProto.BoolProp.newBuilder().setValue(rtlAware));
+        mFingerprint.recordPropertyUpdate(5, Boolean.hashCode(rtlAware));
+        return this;
+      }
+
+      /**
+       * Sets the padding for all sides of the content, in DP.
+       */
+      @NonNull
+      @SuppressLint("MissingGetterMatchingBuilder")
+      public Builder setAll(@NonNull DpProp value) {
+        return setStart(value).setEnd(value).setTop(value).setBottom(value);
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Padding build() {
+        return new Padding(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A modifier to apply a border around an element. */
+  public static final class Border {
+    private final ModifiersProto.Border mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Border(ModifiersProto.Border impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the width of the border, in DP. Intended for testing purposes only. */
+    @Nullable
+    public DpProp getWidth() {
+      if (mImpl.hasWidth()) {
+        return DpProp.fromProto(mImpl.getWidth());
+      } else {
+        return null;
+      }
+    }
+
+    /** Gets the color of the border. Intended for testing purposes only. */
+    @Nullable
+    public ColorProp getColor() {
+      if (mImpl.hasColor()) {
+        return ColorProp.fromProto(mImpl.getColor());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Border fromProto(@NonNull ModifiersProto.Border proto) {
+      return new Border(proto, null);
+    }
+
+    @NonNull
+    ModifiersProto.Border toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Border} */
+    public static final class Builder {
+      private final ModifiersProto.Border.Builder mImpl = ModifiersProto.Border.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(2085330827);
+
+      public Builder() {}
+
+      /**
+       * Sets the width of the border, in DP.
+       */
+      @NonNull
+      public Builder setWidth(@NonNull DpProp width) {
+        mImpl.setWidth(width.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(width.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the color of the border.
+       */
+      @NonNull
+      public Builder setColor(@NonNull ColorProp color) {
+        mImpl.setColor(color.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(color.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Border build() {
+        return new Border(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** The corner of a {@link androidx.wear.tiles.LayoutElementBuilders.Box} element. */
+  public static final class Corner {
+    private final ModifiersProto.Corner mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Corner(ModifiersProto.Corner impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the radius of the corner in DP. Intended for testing purposes only. */
+    @Nullable
+    public DpProp getRadius() {
+      if (mImpl.hasRadius()) {
+        return DpProp.fromProto(mImpl.getRadius());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Corner fromProto(@NonNull ModifiersProto.Corner proto) {
+      return new Corner(proto, null);
+    }
+
+    @NonNull
+    ModifiersProto.Corner toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Corner} */
+    public static final class Builder {
+      private final ModifiersProto.Corner.Builder mImpl = ModifiersProto.Corner.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-623478338);
+
+      public Builder() {}
+
+      /**
+       * Sets the radius of the corner in DP.
+       */
+      @NonNull
+      public Builder setRadius(@NonNull DpProp radius) {
+        mImpl.setRadius(radius.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(radius.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Corner build() {
+        return new Corner(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A modifier to apply a background to an element. */
+  public static final class Background {
+    private final ModifiersProto.Background mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Background(ModifiersProto.Background impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the background color for this element. If not defined, defaults to being transparent.
+     * Intended for testing purposes only.
+     */
+    @Nullable
+    public ColorProp getColor() {
+      if (mImpl.hasColor()) {
+        return ColorProp.fromProto(mImpl.getColor());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the corner properties of this element. This only affects the drawing of this element if
+     * it has a background color or border. If not defined, defaults to having a square corner.
+     * Intended for testing purposes only.
+     */
+    @Nullable
+    public Corner getCorner() {
+      if (mImpl.hasCorner()) {
+        return Corner.fromProto(mImpl.getCorner());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Background fromProto(@NonNull ModifiersProto.Background proto) {
+      return new Background(proto, null);
+    }
+
+    @NonNull
+    ModifiersProto.Background toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Background} */
+    public static final class Builder {
+      private final ModifiersProto.Background.Builder mImpl =
+          ModifiersProto.Background.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(374507572);
+
+      public Builder() {}
+
+      /**
+       * Sets the background color for this element. If not defined, defaults to being transparent.
+       */
+      @NonNull
+      public Builder setColor(@NonNull ColorProp color) {
+        mImpl.setColor(color.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(color.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the corner properties of this element. This only affects the drawing of this element
+       * if it has a background color or border. If not defined, defaults to having a square corner.
+       */
+      @NonNull
+      public Builder setCorner(@NonNull Corner corner) {
+        mImpl.setCorner(corner.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(corner.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Background build() {
+        return new Background(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * Metadata about an element. For use by libraries building higher-level components only. This can
+   * be used to track component metadata.
+   */
+  public static final class ElementMetadata {
+    private final ModifiersProto.ElementMetadata mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ElementMetadata(
+        ModifiersProto.ElementMetadata impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets property describing the element with which it is associated. For use by libraries
+     * building higher-level components only. This can be used to track component metadata.
+     */
+    @NonNull
+    public byte[] getTagData() {
+      return mImpl.getTagData().toByteArray();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ElementMetadata fromProto(@NonNull ModifiersProto.ElementMetadata proto) {
+      return new ElementMetadata(proto, null);
+    }
+
+    /**
+     * Returns the internal proto instance.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ModifiersProto.ElementMetadata toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link ElementMetadata} */
+    public static final class Builder {
+      private final ModifiersProto.ElementMetadata.Builder mImpl =
+          ModifiersProto.ElementMetadata.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-589294723);
+
+      public Builder() {}
+
+      /**
+       * Sets property describing the element with which it is associated. For use by libraries
+       * building higher-level components only. This can be used to track component metadata.
+       */
+      @NonNull
+      public Builder setTagData(@NonNull byte[] tagData) {
+        mImpl.setTagData(ByteString.copyFrom(tagData));
+        mFingerprint.recordPropertyUpdate(1, Arrays.hashCode(tagData));
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public ElementMetadata build() {
+        return new ElementMetadata(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * {@link Modifiers} for an element. These may change the way they are drawn (e.g. {@link Padding}
+   * or {@link Background}), or change their behaviour (e.g. {@link Clickable}, or {@link
+   * Semantics}).
+   */
+  public static final class Modifiers {
+    private final ModifiersProto.Modifiers mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Modifiers(ModifiersProto.Modifiers impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets the clickable property of the modified element. It allows its wrapped element to have
+     * actions associated with it, which will be executed when the element is tapped. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Clickable getClickable() {
+      if (mImpl.hasClickable()) {
+        return Clickable.fromProto(mImpl.getClickable());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the semantics of the modified element. This can be used to add metadata to the modified
+     * element (eg. screen reader content descriptions). Intended for testing purposes only.
+     */
+    @Nullable
+    public Semantics getSemantics() {
+      if (mImpl.hasSemantics()) {
+        return Semantics.fromProto(mImpl.getSemantics());
+      } else {
+        return null;
+      }
+    }
+
+    /** Gets the padding of the modified element. Intended for testing purposes only. */
+    @Nullable
+    public Padding getPadding() {
+      if (mImpl.hasPadding()) {
+        return Padding.fromProto(mImpl.getPadding());
+      } else {
+        return null;
+      }
+    }
+
+    /** Gets the border of the modified element. Intended for testing purposes only. */
+    @Nullable
+    public Border getBorder() {
+      if (mImpl.hasBorder()) {
+        return Border.fromProto(mImpl.getBorder());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets the background (with optional corner radius) of the modified element. Intended for
+     * testing purposes only.
+     */
+    @Nullable
+    public Background getBackground() {
+      if (mImpl.hasBackground()) {
+        return Background.fromProto(mImpl.getBackground());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets metadata about an element. For use by libraries building higher-level components only.
+     * This can be used to track component metadata.
+     */
+    @Nullable
+    public ElementMetadata getMetadata() {
+      if (mImpl.hasMetadata()) {
+        return ElementMetadata.fromProto(mImpl.getMetadata());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    /**
+     * Creates a new wrapper instance from the proto. Intended for testing purposes only. An object
+     * created using this method can't be added to any other wrapper.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static Modifiers fromProto(@NonNull ModifiersProto.Modifiers proto) {
+      return new Modifiers(proto, null);
+    }
+
+    /**
+     * Returns the internal proto instance.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ModifiersProto.Modifiers toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Modifiers} */
+    public static final class Builder {
+      private final ModifiersProto.Modifiers.Builder mImpl = ModifiersProto.Modifiers.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-170942531);
+
+      public Builder() {}
+
+      /**
+       * Sets the clickable property of the modified element. It allows its wrapped element to have
+       * actions associated with it, which will be executed when the element is tapped.
+       */
+      @NonNull
+      public Builder setClickable(@NonNull Clickable clickable) {
+        mImpl.setClickable(clickable.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(clickable.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets the semantics of the modified element. This can be used to add metadata to the
+       * modified element (eg. screen reader content descriptions).
+       */
+      @NonNull
+      public Builder setSemantics(@NonNull Semantics semantics) {
+        mImpl.setSemantics(semantics.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(semantics.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets the padding of the modified element. */
+      @NonNull
+      public Builder setPadding(@NonNull Padding padding) {
+        mImpl.setPadding(padding.toProto());
+        mFingerprint.recordPropertyUpdate(
+            3, checkNotNull(padding.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets the border of the modified element. */
+      @NonNull
+      public Builder setBorder(@NonNull Border border) {
+        mImpl.setBorder(border.toProto());
+        mFingerprint.recordPropertyUpdate(
+            4, checkNotNull(border.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Sets the background (with optional corner radius) of the modified element. */
+      @NonNull
+      public Builder setBackground(@NonNull Background background) {
+        mImpl.setBackground(background.toProto());
+        mFingerprint.recordPropertyUpdate(
+            5, checkNotNull(background.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets metadata about an element. For use by libraries building higher-level components only.
+       * This can be used to track component metadata.
+       */
+      @NonNull
+      public Builder setMetadata(@NonNull ElementMetadata metadata) {
+        mImpl.setMetadata(metadata.toProto());
+        mFingerprint.recordPropertyUpdate(
+            6, checkNotNull(metadata.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Modifiers build() {
+        return new Modifiers(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * {@link Modifiers} that can be used with ArcLayoutElements. These may change the way they are
+   * drawn, or change their behaviour.
+   */
+  public static final class ArcModifiers {
+    private final ModifiersProto.ArcModifiers mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    ArcModifiers(ModifiersProto.ArcModifiers impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets allows its wrapped element to have actions associated with it, which will be executed
+     * when the element is tapped. Intended for testing purposes only.
+     */
+    @Nullable
+    public Clickable getClickable() {
+      if (mImpl.hasClickable()) {
+        return Clickable.fromProto(mImpl.getClickable());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets adds metadata for the modified element, for example, screen reader content descriptions.
+     * Intended for testing purposes only.
+     */
+    @Nullable
+    public Semantics getSemantics() {
+      if (mImpl.hasSemantics()) {
+        return Semantics.fromProto(mImpl.getSemantics());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static ArcModifiers fromProto(@NonNull ModifiersProto.ArcModifiers proto) {
+      return new ArcModifiers(proto, null);
+    }
+
+    /**
+     * Returns the internal proto instance.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ModifiersProto.ArcModifiers toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link ArcModifiers} */
+    public static final class Builder {
+      private final ModifiersProto.ArcModifiers.Builder mImpl =
+          ModifiersProto.ArcModifiers.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1648736168);
+
+      public Builder() {}
+
+      /**
+       * Sets allows its wrapped element to have actions associated with it, which will be executed
+       * when the element is tapped.
+       */
+      @NonNull
+      public Builder setClickable(@NonNull Clickable clickable) {
+        mImpl.setClickable(clickable.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(clickable.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /**
+       * Sets adds metadata for the modified element, for example, screen reader content
+       * descriptions.
+       */
+      @NonNull
+      public Builder setSemantics(@NonNull Semantics semantics) {
+        mImpl.setSemantics(semantics.toProto());
+        mFingerprint.recordPropertyUpdate(
+            2, checkNotNull(semantics.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public ArcModifiers build() {
+        return new ArcModifiers(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /**
+   * {@link Modifiers} that can be used with {@link androidx.wear.tiles.LayoutElementBuilders.Span}
+   * elements. These may change the way they are drawn, or change their behaviour.
+   */
+  public static final class SpanModifiers {
+    private final ModifiersProto.SpanModifiers mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    SpanModifiers(ModifiersProto.SpanModifiers impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /**
+     * Gets allows its wrapped element to have actions associated with it, which will be executed
+     * when the element is tapped. Intended for testing purposes only.
+     */
+    @Nullable
+    public Clickable getClickable() {
+      if (mImpl.hasClickable()) {
+        return Clickable.fromProto(mImpl.getClickable());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static SpanModifiers fromProto(@NonNull ModifiersProto.SpanModifiers proto) {
+      return new SpanModifiers(proto, null);
+    }
+
+    /**
+     * Returns the internal proto instance.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ModifiersProto.SpanModifiers toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link SpanModifiers} */
+    public static final class Builder {
+      private final ModifiersProto.SpanModifiers.Builder mImpl =
+          ModifiersProto.SpanModifiers.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1318656482);
+
+      public Builder() {}
+
+      /**
+       * Sets allows its wrapped element to have actions associated with it, which will be executed
+       * when the element is tapped.
+       */
+      @NonNull
+      public Builder setClickable(@NonNull Clickable clickable) {
+        mImpl.setClickable(clickable.toProto());
+        mFingerprint.recordPropertyUpdate(
+            1, checkNotNull(clickable.getFingerprint()).aggregateValueAsInt());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public SpanModifiers build() {
+        return new SpanModifiers(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
new file mode 100644
index 0000000..8e23d72
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2021-2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import static androidx.annotation.Dimension.PX;
+
+import android.annotation.SuppressLint;
+import androidx.annotation.Dimension;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.expression.ProtoLayoutExperimental;
+import androidx.wear.protolayout.proto.ResourceProto;
+import androidx.wear.protolayout.protobuf.ByteString;
+import androidx.wear.protolayout.protobuf.InvalidProtocolBufferException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/** Builders for the resources for a layout. */
+public final class ResourceBuilders {
+  private ResourceBuilders() {}
+
+  /**
+   * Format describing the contents of an image data byte array.
+   *
+   * @hide
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  @IntDef({IMAGE_FORMAT_UNDEFINED, IMAGE_FORMAT_RGB_565})
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface ImageFormat {}
+
+  /** An undefined image format. */
+  public static final int IMAGE_FORMAT_UNDEFINED = 0;
+
+  /**
+   * An image format where each pixel is stored on 2 bytes, with red using 5 bits, green using 6
+   * bits and blue using 5 bits of precision.
+   */
+  public static final int IMAGE_FORMAT_RGB_565 = 1;
+
+  /** An image resource which maps to an Android drawable by resource ID. */
+  public static final class AndroidImageResourceByResId {
+    private final ResourceProto.AndroidImageResourceByResId mImpl;
+
+    private AndroidImageResourceByResId(ResourceProto.AndroidImageResourceByResId impl) {
+      this.mImpl = impl;
+    }
+
+    /**
+     * Gets the Android resource ID of this image. This must refer to a drawable under R.drawable.
+     * Intended for testing purposes only.
+     */
+    @DrawableRes
+    public int getResourceId() {
+      return mImpl.getResourceId();
+    }
+
+    @NonNull
+    static AndroidImageResourceByResId fromProto(
+        @NonNull ResourceProto.AndroidImageResourceByResId proto) {
+      return new AndroidImageResourceByResId(proto);
+    }
+
+    @NonNull
+    ResourceProto.AndroidImageResourceByResId toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link AndroidImageResourceByResId} */
+    public static final class Builder {
+      private final ResourceProto.AndroidImageResourceByResId.Builder mImpl =
+          ResourceProto.AndroidImageResourceByResId.newBuilder();
+
+      public Builder() {}
+
+      /**
+       * Sets the Android resource ID of this image. This must refer to a drawable under R.drawable.
+       */
+      @NonNull
+      public Builder setResourceId(@DrawableRes int resourceId) {
+        mImpl.setResourceId(resourceId);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public AndroidImageResourceByResId build() {
+        return AndroidImageResourceByResId.fromProto(mImpl.build());
+      }
+    }
+  }
+
+  /**
+   * An image resource whose data is fully inlined, with no dependency on a system or app resource.
+   */
+  public static final class InlineImageResource {
+    private final ResourceProto.InlineImageResource mImpl;
+
+    private InlineImageResource(ResourceProto.InlineImageResource impl) {
+      this.mImpl = impl;
+    }
+
+    /** Gets the byte array representing the image. Intended for testing purposes only. */
+    @NonNull
+    public byte[] getData() {
+      return mImpl.getData().toByteArray();
+    }
+
+    /**
+     * Gets the native width of the image, in pixels. Only required for formats (e.g.
+     * IMAGE_FORMAT_RGB_565) where the image data does not include size. Intended for testing
+     * purposes only.
+     */
+    @Dimension(unit = PX)
+    public int getWidthPx() {
+      return mImpl.getWidthPx();
+    }
+
+    /**
+     * Gets the native height of the image, in pixels. Only required for formats (e.g.
+     * IMAGE_FORMAT_RGB_565) where the image data does not include size. Intended for testing
+     * purposes only.
+     */
+    @Dimension(unit = PX)
+    public int getHeightPx() {
+      return mImpl.getHeightPx();
+    }
+
+    /**
+     * Gets the format of the byte array data representing the image. May be left unspecified or set
+     * to IMAGE_FORMAT_UNDEFINED in which case the platform will attempt to extract this from the
+     * raw image data. If the platform does not support the format, the image will not be decoded or
+     * displayed. Intended for testing purposes only.
+     */
+    @ImageFormat
+    public int getFormat() {
+      return mImpl.getFormat().getNumber();
+    }
+
+    @NonNull
+    static InlineImageResource fromProto(@NonNull ResourceProto.InlineImageResource proto) {
+      return new InlineImageResource(proto);
+    }
+
+    @NonNull
+    ResourceProto.InlineImageResource toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link InlineImageResource} */
+    public static final class Builder {
+      private final ResourceProto.InlineImageResource.Builder mImpl =
+          ResourceProto.InlineImageResource.newBuilder();
+
+      public Builder() {}
+
+      /** Sets the byte array representing the image. */
+      @NonNull
+      public Builder setData(@NonNull byte[] data) {
+        mImpl.setData(ByteString.copyFrom(data));
+        return this;
+      }
+
+      /**
+       * Sets the native width of the image, in pixels. Only required for formats (e.g.
+       * IMAGE_FORMAT_RGB_565) where the image data does not include size.
+       */
+      @NonNull
+      public Builder setWidthPx(@Dimension(unit = PX) int widthPx) {
+        mImpl.setWidthPx(widthPx);
+        return this;
+      }
+
+      /**
+       * Sets the native height of the image, in pixels. Only required for formats (e.g.
+       * IMAGE_FORMAT_RGB_565) where the image data does not include size.
+       */
+      @NonNull
+      public Builder setHeightPx(@Dimension(unit = PX) int heightPx) {
+        mImpl.setHeightPx(heightPx);
+        return this;
+      }
+
+      /**
+       * Sets the format of the byte array data representing the image. May be left unspecified or
+       * set to IMAGE_FORMAT_UNDEFINED in which case the platform will attempt to extract this from
+       * the raw image data. If the platform does not support the format, the image will not be
+       * decoded or displayed.
+       */
+      @NonNull
+      public Builder setFormat(@ImageFormat int format) {
+        mImpl.setFormat(ResourceProto.ImageFormat.forNumber(format));
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public InlineImageResource build() {
+        return InlineImageResource.fromProto(mImpl.build());
+      }
+    }
+  }
+
+  /**
+   * An image resource, which can be used by layouts. This holds multiple underlying resource types,
+   * which the underlying runtime will pick according to what it thinks is appropriate.
+   */
+  public static final class ImageResource {
+    private final ResourceProto.ImageResource mImpl;
+
+    private ImageResource(ResourceProto.ImageResource impl) {
+      this.mImpl = impl;
+    }
+
+    /**
+     * Gets an image resource that maps to an Android drawable by resource ID. Intended for testing
+     * purposes only.
+     */
+    @Nullable
+    public AndroidImageResourceByResId getAndroidResourceByResId() {
+      if (mImpl.hasAndroidResourceByResId()) {
+        return AndroidImageResourceByResId.fromProto(mImpl.getAndroidResourceByResId());
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Gets an image resource that contains the image data inline. Intended for testing purposes
+     * only.
+     */
+    @Nullable
+    public InlineImageResource getInlineResource() {
+      if (mImpl.hasInlineResource()) {
+        return InlineImageResource.fromProto(mImpl.getInlineResource());
+      } else {
+        return null;
+      }
+    }
+
+    @NonNull
+    static ImageResource fromProto(@NonNull ResourceProto.ImageResource proto) {
+      return new ImageResource(proto);
+    }
+
+    @NonNull
+    ResourceProto.ImageResource toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link ImageResource} */
+    public static final class Builder {
+      private final ResourceProto.ImageResource.Builder mImpl =
+          ResourceProto.ImageResource.newBuilder();
+
+      public Builder() {}
+
+      /** Sets an image resource that maps to an Android drawable by resource ID. */
+      @NonNull
+      public Builder setAndroidResourceByResId(
+          @NonNull AndroidImageResourceByResId androidResourceByResId) {
+        mImpl.setAndroidResourceByResId(androidResourceByResId.toProto());
+        return this;
+      }
+
+      /** Sets an image resource that contains the image data inline. */
+      @NonNull
+      public Builder setInlineResource(@NonNull InlineImageResource inlineResource) {
+        mImpl.setInlineResource(inlineResource.toProto());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public ImageResource build() {
+        return ImageResource.fromProto(mImpl.build());
+      }
+    }
+  }
+
+  /** The resources for a layout. */
+  public static final class Resources {
+    private final ResourceProto.Resources mImpl;
+
+    private Resources(ResourceProto.Resources impl) {
+      this.mImpl = impl;
+    }
+
+    /**
+     * Gets the version of this {@link Resources} instance.
+     *
+     * <p>Each tile specifies the version of resources it requires. After fetching a tile, the
+     * renderer will use the resources version specified by the tile to separately fetch the
+     * resources.
+     *
+     * <p>This value must match the version of the resources required by the tile for the tile to
+     * render successfully, and must match the resource version specified in {@link
+     * androidx.wear.tiles.RequestBuilders.ResourcesRequest} which triggered this request. Intended
+     * for testing purposes only.
+     */
+    @NonNull
+    public String getVersion() {
+      return mImpl.getVersion();
+    }
+
+    /**
+     * Gets a map of resource_ids to images, which can be used by layouts. Intended for testing
+     * purposes only.
+     */
+    @NonNull
+    public Map<String, ImageResource> getIdToImageMapping() {
+      Map<String, ImageResource> map = new HashMap<>();
+      for (Entry<String, ResourceProto.ImageResource> entry : mImpl.getIdToImageMap().entrySet()) {
+        map.put(entry.getKey(), ImageResource.fromProto(entry.getValue()));
+      }
+      return Collections.unmodifiableMap(map);
+    }
+
+    /** Converts to byte array representation. */
+    @NonNull
+    @ProtoLayoutExperimental
+    public byte[] toByteArray() {
+      return mImpl.toByteArray();
+    }
+
+    /** Converts from byte array representation. */
+    @SuppressWarnings("ProtoParseWithRegistry")
+    @Nullable
+    @ProtoLayoutExperimental
+    public static Resources fromByteArray(@NonNull byte[] byteArray) {
+      try {
+        return fromProto(ResourceProto.Resources.parseFrom(byteArray));
+      } catch (InvalidProtocolBufferException e) {
+        return null;
+      }
+    }
+
+    @NonNull
+    static Resources fromProto(@NonNull ResourceProto.Resources proto) {
+      return new Resources(proto);
+    }
+
+    /**
+     * Returns the internal proto instance.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public ResourceProto.Resources toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Resources} */
+    public static final class Builder {
+      private final ResourceProto.Resources.Builder mImpl = ResourceProto.Resources.newBuilder();
+
+      public Builder() {}
+
+      /**
+       * Sets the version of this {@link Resources} instance.
+       *
+       * <p>Each tile specifies the version of resources it requires. After fetching a tile, the
+       * renderer will use the resources version specified by the tile to separately fetch the
+       * resources.
+       *
+       * <p>This value must match the version of the resources required by the tile for the tile to
+       * render successfully, and must match the resource version specified in {@link
+       * androidx.wear.tiles.RequestBuilders.ResourcesRequest} which triggered this request.
+       */
+      @NonNull
+      public Builder setVersion(@NonNull String version) {
+        mImpl.setVersion(version);
+        return this;
+      }
+
+      /** Adds an entry into a map of resource_ids to images, which can be used by layouts. */
+      @SuppressLint("MissingGetterMatchingBuilder")
+      @NonNull
+      public Builder addIdToImageMapping(@NonNull String id, @NonNull ImageResource image) {
+        mImpl.putIdToImage(id, image.toProto());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Resources build() {
+        return Resources.fromProto(mImpl.build());
+      }
+    }
+  }
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/StateBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/StateBuilders.java
new file mode 100644
index 0000000..d29e831
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/StateBuilders.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2021-2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.StateProto;
+
+/** Builders for state of a tile. */
+public final class StateBuilders {
+  private StateBuilders() {}
+
+  /** {@link State} information. */
+  public static final class State {
+    private final StateProto.State mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    State(StateProto.State impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the ID of the clickable that was last clicked. */
+    @NonNull
+    public String getLastClickableId() {
+      return mImpl.getLastClickableId();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static State fromProto(@NonNull StateProto.State proto) {
+      return new State(proto, null);
+    }
+
+    @NonNull
+    StateProto.State toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link State} */
+    public static final class Builder {
+      private final StateProto.State.Builder mImpl = StateProto.State.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(616326811);
+
+      public Builder() {}
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public State build() {
+        return new State(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TimelineBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TimelineBuilders.java
new file mode 100644
index 0000000..5ddacfc
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TimelineBuilders.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2021-2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.wear.protolayout.proto.TimelineProto;
+import androidx.wear.protolayout.LayoutElementBuilders.Layout;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Builders for a timeline with entries representing content that should be displayed within given
+ * time intervals.
+ */
+public final class TimelineBuilders {
+  private TimelineBuilders() {}
+
+  /** A time interval, typically used to describe the validity period of a {@link TimelineEntry}. */
+  public static final class TimeInterval {
+    private final TimelineProto.TimeInterval mImpl;
+
+    private TimeInterval(TimelineProto.TimeInterval impl) {
+      this.mImpl = impl;
+    }
+
+    /**
+     * Gets starting point of the time interval, in milliseconds since the Unix epoch. Intended for
+     * testing purposes only.
+     */
+    public long getStartMillis() {
+      return mImpl.getStartMillis();
+    }
+
+    /**
+     * Gets end point of the time interval, in milliseconds since the Unix epoch. Intended for
+     * testing purposes only.
+     */
+    public long getEndMillis() {
+      return mImpl.getEndMillis();
+    }
+
+    @NonNull
+    static TimeInterval fromProto(@NonNull TimelineProto.TimeInterval proto) {
+      return new TimeInterval(proto);
+    }
+
+    @NonNull
+    TimelineProto.TimeInterval toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link TimeInterval} */
+    public static final class Builder {
+      private final TimelineProto.TimeInterval.Builder mImpl =
+          TimelineProto.TimeInterval.newBuilder();
+
+      public Builder() {}
+
+      /** Sets starting point of the time interval, in milliseconds since the Unix epoch. */
+      @NonNull
+      public Builder setStartMillis(long startMillis) {
+        mImpl.setStartMillis(startMillis);
+        return this;
+      }
+
+      /** Sets end point of the time interval, in milliseconds since the Unix epoch. */
+      @NonNull
+      public Builder setEndMillis(long endMillis) {
+        mImpl.setEndMillis(endMillis);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public TimeInterval build() {
+        return TimeInterval.fromProto(mImpl.build());
+      }
+    }
+  }
+
+  /** One piece of renderable content along with the time that it is valid for. */
+  public static final class TimelineEntry {
+    private final TimelineProto.TimelineEntry mImpl;
+
+    private TimelineEntry(TimelineProto.TimelineEntry impl) {
+      this.mImpl = impl;
+    }
+
+    /** Gets the validity period for this timeline entry. Intended for testing purposes only. */
+    @Nullable
+    public TimeInterval getValidity() {
+      if (mImpl.hasValidity()) {
+        return TimeInterval.fromProto(mImpl.getValidity());
+      } else {
+        return null;
+      }
+    }
+
+    /** Gets the contents of this timeline entry. Intended for testing purposes only. */
+    @Nullable
+    public Layout getLayout() {
+      if (mImpl.hasLayout()) {
+        return Layout.fromProto(mImpl.getLayout());
+      } else {
+        return null;
+      }
+    }
+
+    /** Returns the {@link TimelineEntry} object containing the given layout element. */
+    @NonNull
+    public static TimelineEntry fromLayoutElement(
+        @NonNull LayoutElementBuilders.LayoutElement layoutElement) {
+      return new Builder().setLayout(Layout.fromLayoutElement(layoutElement)).build();
+    }
+
+    @NonNull
+    static TimelineEntry fromProto(@NonNull TimelineProto.TimelineEntry proto) {
+      return new TimelineEntry(proto);
+    }
+
+    @NonNull
+    TimelineProto.TimelineEntry toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link TimelineEntry} */
+    public static final class Builder {
+      private final TimelineProto.TimelineEntry.Builder mImpl =
+          TimelineProto.TimelineEntry.newBuilder();
+
+      public Builder() {}
+
+      /** Sets the validity period for this timeline entry. */
+      @NonNull
+      public Builder setValidity(@NonNull TimeInterval validity) {
+        mImpl.setValidity(validity.toProto());
+        return this;
+      }
+
+      /** Sets the contents of this timeline entry. */
+      @NonNull
+      public Builder setLayout(@NonNull Layout layout) {
+        mImpl.setLayout(layout.toProto());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public TimelineEntry build() {
+        return TimelineEntry.fromProto(mImpl.build());
+      }
+    }
+  }
+
+  /**
+   * A collection of {@link TimelineEntry} items.
+   *
+   * <p>{@link TimelineEntry} items can be used to update a layout on-screen at known times, without
+   * having to explicitly update a layout. This allows for cases where, say, a calendar can be used
+   * to show the next event, and automatically switch to showing the next event when one has passed.
+   *
+   * <p>The active {@link TimelineEntry} is switched, at most, once a minute. In the case where the
+   * validity periods of {@link TimelineEntry} items overlap, the item with the shortest* validity
+   * period will be shown. This allows a layout provider to show a "default" layout, and override it
+   * at set points without having to explicitly insert the default layout between the "override"
+   * layout.
+   */
+  public static final class Timeline {
+    private final TimelineProto.Timeline mImpl;
+
+    private Timeline(TimelineProto.Timeline impl) {
+      this.mImpl = impl;
+    }
+
+    /** Gets the entries in a timeline. Intended for testing purposes only. */
+    @NonNull
+    public List<TimelineEntry> getTimelineEntries() {
+      List<TimelineEntry> list = new ArrayList<>();
+      for (TimelineProto.TimelineEntry item : mImpl.getTimelineEntriesList()) {
+        list.add(TimelineEntry.fromProto(item));
+      }
+      return Collections.unmodifiableList(list);
+    }
+
+    /** Returns the {@link Timeline} object containing the given layout element. */
+    @NonNull
+    public static Timeline fromLayoutElement(
+        @NonNull LayoutElementBuilders.LayoutElement layoutElement) {
+      return new Builder().addTimelineEntry(TimelineEntry.fromLayoutElement(layoutElement)).build();
+    }
+
+    @NonNull
+    static Timeline fromProto(@NonNull TimelineProto.Timeline proto) {
+      return new Timeline(proto);
+    }
+
+    @NonNull
+    TimelineProto.Timeline toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Timeline} */
+    public static final class Builder {
+      private final TimelineProto.Timeline.Builder mImpl = TimelineProto.Timeline.newBuilder();
+
+      public Builder() {}
+
+      /** Adds one item to the entries in a timeline. */
+      @NonNull
+      public Builder addTimelineEntry(@NonNull TimelineEntry timelineEntry) {
+        mImpl.addTimelineEntries(timelineEntry.toProto());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Timeline build() {
+        return Timeline.fromProto(mImpl.build());
+      }
+    }
+  }
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TypeBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TypeBuilders.java
new file mode 100644
index 0000000..cb605fc
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TypeBuilders.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2021-2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import android.annotation.SuppressLint;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.TypesProto;
+
+/** Builders for extensible primitive types used by layout elements. */
+public final class TypeBuilders {
+  private TypeBuilders() {}
+
+  /** An int32 type. */
+  public static final class Int32Prop {
+    private final TypesProto.Int32Prop mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    Int32Prop(TypesProto.Int32Prop impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    public int getValue() {
+      return mImpl.getValue();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static Int32Prop fromProto(@NonNull TypesProto.Int32Prop proto) {
+      return new Int32Prop(proto, null);
+    }
+
+    @NonNull
+    TypesProto.Int32Prop toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link Int32Prop} */
+    public static final class Builder {
+      private final TypesProto.Int32Prop.Builder mImpl = TypesProto.Int32Prop.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-1809132005);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(int value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, value);
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public Int32Prop build() {
+        return new Int32Prop(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A string type. */
+  public static final class StringProp {
+    private final TypesProto.StringProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    StringProp(TypesProto.StringProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    @NonNull
+    public String getValue() {
+      return mImpl.getValue();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static StringProp fromProto(@NonNull TypesProto.StringProp proto) {
+      return new StringProp(proto, null);
+    }
+
+    @NonNull
+    TypesProto.StringProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link StringProp} */
+    public static final class Builder {
+      private final TypesProto.StringProp.Builder mImpl = TypesProto.StringProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-319420356);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(@NonNull String value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, value.hashCode());
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public StringProp build() {
+        return new StringProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A float type. */
+  public static final class FloatProp {
+    private final TypesProto.FloatProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    FloatProp(TypesProto.FloatProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    public float getValue() {
+      return mImpl.getValue();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static FloatProp fromProto(@NonNull TypesProto.FloatProp proto) {
+      return new FloatProp(proto, null);
+    }
+
+    @NonNull
+    TypesProto.FloatProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link FloatProp} */
+    public static final class Builder {
+      private final TypesProto.FloatProp.Builder mImpl = TypesProto.FloatProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(399943127);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @NonNull
+      public Builder setValue(float value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(value));
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public FloatProp build() {
+        return new FloatProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+
+  /** A boolean type. */
+  public static final class BoolProp {
+    private final TypesProto.BoolProp mImpl;
+    @Nullable private final Fingerprint mFingerprint;
+
+    BoolProp(TypesProto.BoolProp impl, @Nullable Fingerprint fingerprint) {
+      this.mImpl = impl;
+      this.mFingerprint = fingerprint;
+    }
+
+    /** Gets the value. Intended for testing purposes only. */
+    public boolean getValue() {
+      return mImpl.getValue();
+    }
+
+    /**
+     * Get the fingerprint for this object, or null if unknown.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public Fingerprint getFingerprint() {
+      return mFingerprint;
+    }
+
+    @NonNull
+    static BoolProp fromProto(@NonNull TypesProto.BoolProp proto) {
+      return new BoolProp(proto, null);
+    }
+
+    @NonNull
+    TypesProto.BoolProp toProto() {
+      return mImpl;
+    }
+
+    /** Builder for {@link BoolProp} */
+    public static final class Builder {
+      private final TypesProto.BoolProp.Builder mImpl = TypesProto.BoolProp.newBuilder();
+      private final Fingerprint mFingerprint = new Fingerprint(-278424864);
+
+      public Builder() {}
+
+      /** Sets the value. */
+      @SuppressLint("MissingGetterMatchingBuilder")
+      @NonNull
+      public Builder setValue(boolean value) {
+        mImpl.setValue(value);
+        mFingerprint.recordPropertyUpdate(1, Boolean.hashCode(value));
+        return this;
+      }
+
+      /** Builds an instance from accumulated values. */
+      @NonNull
+      public BoolProp build() {
+        return new BoolProp(mImpl.build(), mFingerprint);
+      }
+    }
+  }
+}