diff --git a/.github/workflows/apply_spotless.yml b/.github/workflows/apply_spotless.yml
index 5ba1f6fe5..0c8dcce4f 100644
--- a/.github/workflows/apply_spotless.yml
+++ b/.github/workflows/apply_spotless.yml
@@ -50,6 +50,9 @@ jobs:
     - name: Run spotlessApply for Misc
       run: ./gradlew :misc:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace
 
+    - name: Run spotlessApply for XR
+      run: ./gradlew :xr:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace
+
     - name: Auto-commit if spotlessApply has changes
       uses: stefanzweifel/git-auto-commit-action@v5
       with:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 97b36f468..b5124ef05 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -47,3 +47,5 @@ jobs:
       run: ./gradlew :wear:build
     - name: Build misc snippets
       run: ./gradlew :misc:build
+    - name: Build XR snippets
+      run: ./gradlew :xr:build
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 15c560a20..033013972 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -42,6 +42,7 @@ hilt = "2.55"
 horologist = "0.6.22"
 junit = "4.13.2"
 kotlin = "2.1.10"
+kotlinxCoroutinesGuava = "1.9.0"
 kotlinxSerializationJson = "1.8.0"
 ksp = "2.1.10-1.0.30"
 maps-compose = "6.4.4"
@@ -55,6 +56,7 @@ playServicesWearable = "19.0.0"
 protolayout = "1.2.1"
 recyclerview = "1.4.0"
 # @keep
+androidx-xr = "1.0.0-alpha02"
 targetSdk = "34"
 tiles = "1.4.1"
 version-catalog-update = "0.8.5"
@@ -62,6 +64,7 @@ wear = "1.3.0"
 wearComposeFoundation = "1.4.1"
 wearComposeMaterial = "1.4.1"
 wearToolingPreview = "1.0.0"
+activityKtx = "1.10.0"
 
 [libraries]
 accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version.ref = "accompanist" }
@@ -136,6 +139,9 @@ androidx-window = { module = "androidx.window:window", version.ref = "androidx-w
 androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window-core" }
 androidx-window-java = {module = "androidx.window:window-java", version.ref = "androidx-window-java" }
 androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.10.0"
+androidx-xr-arcore = { module = "androidx.xr.arcore:arcore", version.ref = "androidx-xr" }
+androidx-xr-compose = { module = "androidx.xr.compose:compose", version.ref = "androidx-xr" }
+androidx-xr-scenecore = { module = "androidx.xr.scenecore:scenecore", version.ref = "androidx-xr" }
 android-identity-googleid = {module = "com.google.android.libraries.identity.googleid:googleid", version.ref = "android-googleid"}
 appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
 coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
@@ -154,9 +160,11 @@ horologist-compose-material = { module = "com.google.android.horologist:horologi
 junit = { module = "junit:junit", version.ref = "junit" }
 kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
 kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
+kotlinx-coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "kotlinxCoroutinesGuava" }
 kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
 kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
 play-services-wearable = { module = "com.google.android.gms:play-services-wearable", version.ref = "playServicesWearable" }
+androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityKtx" }
 
 [plugins]
 android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 3ca8e6446..6d2212b63 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -29,4 +29,5 @@ include(
         ":views",
         ":misc",
         ":identity:credentialmanager",
+        ":xr",
 )
diff --git a/xr/.gitignore b/xr/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/xr/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/xr/build.gradle.kts b/xr/build.gradle.kts
new file mode 100644
index 000000000..afbff0151
--- /dev/null
+++ b/xr/build.gradle.kts
@@ -0,0 +1,34 @@
+plugins {
+    alias(libs.plugins.android.application)
+    alias(libs.plugins.kotlin.android)
+}
+
+android {
+    namespace = "com.example.xr"
+    compileSdk = 35
+
+    defaultConfig {
+        applicationId = "com.example.xr"
+        minSdk = 34
+        targetSdk = 35
+        versionCode = 1
+        versionName = "1.0"
+    }
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_11
+        targetCompatibility = JavaVersion.VERSION_11
+    }
+    kotlinOptions {
+        jvmTarget = "11"
+    }
+}
+
+dependencies {
+    implementation(libs.androidx.xr.arcore)
+    implementation(libs.androidx.xr.scenecore)
+    implementation(libs.androidx.xr.compose)
+    implementation(libs.androidx.activity.ktx)
+    implementation(libs.guava)
+    implementation(libs.kotlinx.coroutines.guava)
+
+}
\ No newline at end of file
diff --git a/xr/src/main/AndroidManifest.xml b/xr/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..6d6399c14
--- /dev/null
+++ b/xr/src/main/AndroidManifest.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:tools="http://schemas.android.com/tools"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <application
+        android:label="XR"
+        tools:ignore="MissingApplicationIcon" />
+
+</manifest>
\ No newline at end of file
diff --git a/xr/src/main/java/com/example/xr/arcore/Hands.kt b/xr/src/main/java/com/example/xr/arcore/Hands.kt
new file mode 100644
index 000000000..26cc0ba8e
--- /dev/null
+++ b/xr/src/main/java/com/example/xr/arcore/Hands.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2025 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
+ *
+ *     https://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 com.example.xr.arcore
+
+import android.annotation.SuppressLint
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.lifecycle.lifecycleScope
+import androidx.xr.arcore.Hand
+import androidx.xr.arcore.HandJointType
+import androidx.xr.compose.platform.setSubspaceContent
+import androidx.xr.runtime.Session
+import androidx.xr.runtime.math.Pose
+import androidx.xr.runtime.math.Quaternion
+import androidx.xr.runtime.math.Vector3
+import androidx.xr.scenecore.Entity
+import androidx.xr.scenecore.GltfModel
+import androidx.xr.scenecore.GltfModelEntity
+import kotlinx.coroutines.guava.await
+import kotlinx.coroutines.launch
+
+class SampleHandsActivity : ComponentActivity() {
+    lateinit var session: Session
+    lateinit var scenecoreSession: androidx.xr.scenecore.Session
+    lateinit var sessionHelper: SessionLifecycleHelper
+
+    var palmEntity: Entity? = null
+    var indexFingerEntity: Entity? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setSubspaceContent { }
+
+        scenecoreSession = androidx.xr.scenecore.Session.create(this@SampleHandsActivity)
+        lifecycleScope.launch {
+            val model = GltfModel.create(scenecoreSession, "models/saturn_rings.glb").await()
+            palmEntity = GltfModelEntity.create(scenecoreSession, model).apply {
+                setScale(0.3f)
+                setHidden(true)
+            }
+            indexFingerEntity = GltfModelEntity.create(scenecoreSession, model).apply {
+                setScale(0.2f)
+                setHidden(true)
+            }
+        }
+
+        sessionHelper = SessionLifecycleHelper(
+            onCreateCallback = { session = it },
+            onResumeCallback = {
+                collectHands(session)
+            }
+        )
+        lifecycle.addObserver(sessionHelper)
+    }
+}
+
+fun SampleHandsActivity.collectHands(session: Session) {
+    lifecycleScope.launch {
+        // [START androidxr_arcore_hand_collect]
+        Hand.left(session)?.state?.collect { handState -> // or Hand.right(session)
+            // Hand state has been updated.
+            // Use the state of hand joints to update an entity's position.
+            renderPlanetAtHandPalm(handState)
+        }
+        // [END androidxr_arcore_hand_collect]
+    }
+    lifecycleScope.launch {
+        Hand.right(session)?.state?.collect { rightHandState ->
+            renderPlanetAtFingerTip(rightHandState)
+        }
+    }
+}
+
+@SuppressLint("RestrictedApi") // HandJointType is mistakenly @Restrict: b/397415504
+fun SampleHandsActivity.renderPlanetAtHandPalm(leftHandState: Hand.State) {
+    val palmEntity = palmEntity ?: return
+    // [START androidxr_arcore_hand_entityAtHandPalm]
+    val palmPose = leftHandState.handJoints[HandJointType.PALM] ?: return
+
+    // the down direction points in the same direction as the palm
+    val angle = Vector3.angleBetween(palmPose.rotation * Vector3.Down, Vector3.Up)
+    palmEntity.setHidden(angle > Math.toRadians(40.0))
+
+    val transformedPose =
+        scenecoreSession.perceptionSpace.transformPoseTo(
+            palmPose,
+            scenecoreSession.activitySpace,
+        )
+    val newPosition = transformedPose.translation + transformedPose.down * 0.05f
+    palmEntity.setPose(Pose(newPosition, transformedPose.rotation))
+    // [END androidxr_arcore_hand_entityAtHandPalm]
+}
+
+@SuppressLint("RestrictedApi") // HandJointType is mistakenly @Restrict: b/397415504
+fun SampleHandsActivity.renderPlanetAtFingerTip(rightHandState: Hand.State) {
+    val indexFingerEntity = indexFingerEntity ?: return
+
+    // [START androidxr_arcore_hand_entityAtIndexFingerTip]
+    val tipPose = rightHandState.handJoints[HandJointType.INDEX_TIP] ?: return
+
+    // the forward direction points towards the finger tip.
+    val angle = Vector3.angleBetween(tipPose.rotation * Vector3.Forward, Vector3.Up)
+    indexFingerEntity.setHidden(angle > Math.toRadians(40.0))
+
+    val transformedPose =
+        scenecoreSession.perceptionSpace.transformPoseTo(
+            tipPose,
+            scenecoreSession.activitySpace,
+        )
+    val position = transformedPose.translation + transformedPose.forward * 0.03f
+    val rotation = Quaternion.fromLookTowards(transformedPose.up, Vector3.Up)
+    indexFingerEntity.setPose(Pose(position, rotation))
+    // [END androidxr_arcore_hand_entityAtIndexFingerTip]
+}
diff --git a/xr/src/main/java/com/example/xr/arcore/SessionLifecycleHelper.kt b/xr/src/main/java/com/example/xr/arcore/SessionLifecycleHelper.kt
new file mode 100644
index 000000000..77462257f
--- /dev/null
+++ b/xr/src/main/java/com/example/xr/arcore/SessionLifecycleHelper.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2025 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
+ *
+ *     https://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 com.example.xr.arcore
+
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.xr.runtime.Session
+
+/**
+ * This is a dummy version of [SessionLifecycleHelper](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/common/SessionLifecycleHelper.kt).
+ * This will be removed when Session becomes a LifecycleOwner in cl/726643897.
+ */
+class SessionLifecycleHelper(
+    val onCreateCallback: (Session) -> Unit,
+
+    val onResumeCallback: (() -> Unit)? = null,
+) : DefaultLifecycleObserver