Merge "DO NOT MERGE: Merge remote-tracking branch 'aosp/androidx-platform-dev-temp' into merge_platform_dev" into androidx-main
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/TwilightManager.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/TwilightManager.java
index 895d3a7..6d445c09 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/TwilightManager.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/TwilightManager.java
@@ -152,7 +152,6 @@
         // calculate yesterday's twilight
         calculator.calculateTwilight(now - DateUtils.DAY_IN_MILLIS,
                 location.getLatitude(), location.getLongitude());
-        final long yesterdaySunset = calculator.sunset;
 
         // calculate today's twilight
         calculator.calculateTwilight(now, location.getLatitude(), location.getLongitude());
@@ -184,10 +183,6 @@
 
         // Update the twilight state
         state.isNight = isNight;
-        state.yesterdaySunset = yesterdaySunset;
-        state.todaySunrise = todaySunrise;
-        state.todaySunset = todaySunset;
-        state.tomorrowSunrise = tomorrowSunrise;
         state.nextUpdate = nextUpdate;
     }
 
@@ -196,10 +191,6 @@
      */
     private static class TwilightState {
         boolean isNight;
-        long yesterdaySunset;
-        long todaySunrise;
-        long todaySunset;
-        long tomorrowSunrise;
         long nextUpdate;
 
         TwilightState() {
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatButton.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatButton.java
index 61e92ca..cc087b5 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatButton.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatButton.java
@@ -17,6 +17,7 @@
 package androidx.appcompat.widget;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.appcompat.widget.ViewUtils.SDK_LEVEL_SUPPORTS_AUTOSIZE;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -205,7 +206,7 @@
 
     @Override
     public void setTextSize(int unit, float size) {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             super.setTextSize(unit, size);
         } else {
             if (mTextHelper != null) {
@@ -217,7 +218,9 @@
     @Override
     protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
         super.onTextChanged(text, start, lengthBefore, lengthAfter);
-        if (mTextHelper != null && !PLATFORM_SUPPORTS_AUTOSIZE && mTextHelper.isAutoSizeEnabled()) {
+        boolean useTextHelper = mTextHelper != null && !SDK_LEVEL_SUPPORTS_AUTOSIZE
+                && mTextHelper.isAutoSizeEnabled();
+        if (useTextHelper) {
             mTextHelper.autoSizeText();
         }
     }
@@ -229,7 +232,7 @@
     @Override
     public void setAutoSizeTextTypeWithDefaults(
             @TextViewCompat.AutoSizeTextType int autoSizeTextType) {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             super.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
         } else {
             if (mTextHelper != null) {
@@ -248,7 +251,7 @@
             int autoSizeMaxTextSize,
             int autoSizeStepGranularity,
             int unit) throws IllegalArgumentException {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             super.setAutoSizeTextTypeUniformWithConfiguration(
                     autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
         } else {
@@ -266,7 +269,7 @@
     @Override
     public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
             throws IllegalArgumentException {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             super.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
         } else {
             if (mTextHelper != null) {
@@ -284,7 +287,7 @@
     // Suppress lint error for TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM [WrongConstant]
     @SuppressLint("WrongConstant")
     public int getAutoSizeTextType() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             return super.getAutoSizeTextType() == TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM
                     ? TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM
                     : TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
@@ -302,7 +305,7 @@
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
     public int getAutoSizeStepGranularity() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             return super.getAutoSizeStepGranularity();
         } else {
             if (mTextHelper != null) {
@@ -318,7 +321,7 @@
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
     public int getAutoSizeMinTextSize() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             return super.getAutoSizeMinTextSize();
         } else {
             if (mTextHelper != null) {
@@ -334,7 +337,7 @@
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
     public int getAutoSizeMaxTextSize() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             return super.getAutoSizeMaxTextSize();
         } else {
             if (mTextHelper != null) {
@@ -350,7 +353,7 @@
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
     public int[] getAutoSizeTextAvailableSizes() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             return super.getAutoSizeTextAvailableSizes();
         } else {
             if (mTextHelper != null) {
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java
index 7cb7684..97651c1 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java
@@ -41,6 +41,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.annotation.UiThread;
 import androidx.appcompat.R;
 import androidx.core.view.ContentInfoCompat;
 import androidx.core.view.OnReceiveContentListener;
@@ -83,6 +84,8 @@
     private final TextViewOnReceiveContentListener mDefaultOnReceiveContentListener;
     @NonNull
     private final AppCompatEmojiEditTextHelper mAppCompatEmojiEditTextHelper;
+    @Nullable
+    private SuperCaller mSuperCaller;
 
     public AppCompatEditText(@NonNull Context context) {
         this(context, null);
@@ -302,6 +305,16 @@
                 super.getCustomSelectionActionModeCallback());
     }
 
+    @UiThread
+    @NonNull
+    @RequiresApi(26)
+    private SuperCaller getSuperCaller() {
+        if (mSuperCaller == null) {
+            mSuperCaller = new SuperCaller();
+        }
+        return mSuperCaller;
+    }
+
     /**
      * Sets the {@link TextClassifier} for this TextView.
      */
@@ -309,7 +322,7 @@
     @RequiresApi(api = 26)
     public void setTextClassifier(@Nullable TextClassifier textClassifier) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P || mTextClassifierHelper == null) {
-            super.setTextClassifier(textClassifier);
+            getSuperCaller().setTextClassifier(textClassifier);
             return;
         }
         mTextClassifierHelper.setTextClassifier(textClassifier);
@@ -327,7 +340,7 @@
         // The null check is necessary because getTextClassifier is called when we are invoking
         // the super class's constructor.
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P || mTextClassifierHelper == null) {
-            return super.getTextClassifier();
+            return getSuperCaller().getTextClassifier();
         }
         return mTextClassifierHelper.getTextClassifier();
     }
@@ -496,4 +509,17 @@
         mTextHelper.setCompoundDrawableTintMode(tintMode);
         mTextHelper.applyCompoundDrawablesTints();
     }
+
+    @RequiresApi(api = 26)
+    class SuperCaller {
+
+        @Nullable
+        public TextClassifier getTextClassifier() {
+            return AppCompatEditText.super.getTextClassifier();
+        }
+
+        public void setTextClassifier(TextClassifier textClassifier) {
+            AppCompatEditText.super.setTextClassifier(textClassifier);
+        }
+    }
 }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
index 0e60fde..66becc3 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
@@ -17,7 +17,7 @@
 package androidx.appcompat.widget;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
-import static androidx.core.widget.AutoSizeableTextView.PLATFORM_SUPPORTS_AUTOSIZE;
+import static androidx.appcompat.widget.ViewUtils.SDK_LEVEL_SUPPORTS_AUTOSIZE;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -248,7 +248,7 @@
 
         mAutoSizeTextHelper.loadFromAttributes(attrs, defStyleAttr);
 
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             // Delegate auto-size functionality to the framework implementation.
             if (mAutoSizeTextHelper.getAutoSizeTextType()
                     != TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) {
@@ -561,7 +561,7 @@
     /** @hide */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (!PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (!SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             autoSizeText();
         }
     }
@@ -569,7 +569,7 @@
     /** @hide */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     void setTextSize(int unit, float size) {
-        if (!PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (!SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             if (!isAutoSizeEnabled()) {
                 setTextSizeInternal(unit, size);
             }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
index 6984591..0492dbe 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
@@ -17,6 +17,7 @@
 package androidx.appcompat.widget;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.appcompat.widget.ViewUtils.SDK_LEVEL_SUPPORTS_AUTOSIZE;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -41,6 +42,7 @@
 import androidx.annotation.Px;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.annotation.UiThread;
 import androidx.appcompat.content.res.AppCompatResources;
 import androidx.core.graphics.TypefaceCompat;
 import androidx.core.text.PrecomputedTextCompat;
@@ -93,6 +95,9 @@
     private boolean mIsSetTypefaceProcessing = false;
 
     @Nullable
+    private SuperCaller mSuperCaller = null;
+
+    @Nullable
     private Future<PrecomputedTextCompat> mPrecomputedTextFuture;
 
     public AppCompatTextView(@NonNull Context context) {
@@ -256,7 +261,7 @@
 
     @Override
     public void setTextSize(int unit, float size) {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
             super.setTextSize(unit, size);
         } else {
             if (mTextHelper != null) {
@@ -268,7 +273,9 @@
     @Override
     protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
         super.onTextChanged(text, start, lengthBefore, lengthAfter);
-        if (mTextHelper != null && !PLATFORM_SUPPORTS_AUTOSIZE && mTextHelper.isAutoSizeEnabled()) {
+        boolean useHelper = mTextHelper != null && !SDK_LEVEL_SUPPORTS_AUTOSIZE
+                && mTextHelper.isAutoSizeEnabled();
+        if (useHelper) {
             mTextHelper.autoSizeText();
         }
     }
@@ -284,8 +291,8 @@
     @Override
     public void setAutoSizeTextTypeWithDefaults(
             @TextViewCompat.AutoSizeTextType int autoSizeTextType) {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            super.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
+            getSuperCaller().setAutoSizeTextTypeWithDefaults(autoSizeTextType);
         } else {
             if (mTextHelper != null) {
                 mTextHelper.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
@@ -307,9 +314,9 @@
             int autoSizeMaxTextSize,
             int autoSizeStepGranularity,
             int unit) throws IllegalArgumentException {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            super.setAutoSizeTextTypeUniformWithConfiguration(
-                    autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
+            getSuperCaller().setAutoSizeTextTypeUniformWithConfiguration(autoSizeMinTextSize,
+                    autoSizeMaxTextSize, autoSizeStepGranularity, unit);
         } else {
             if (mTextHelper != null) {
                 mTextHelper.setAutoSizeTextTypeUniformWithConfiguration(
@@ -329,8 +336,8 @@
     @Override
     public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
             throws IllegalArgumentException {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            super.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
+            getSuperCaller().setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
         } else {
             if (mTextHelper != null) {
                 mTextHelper.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
@@ -350,8 +357,9 @@
     // Suppress lint error for TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM [WrongConstant]
     @SuppressLint("WrongConstant")
     public int getAutoSizeTextType() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            return super.getAutoSizeTextType() == TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
+            return getSuperCaller().getAutoSizeTextType() == TextView
+                    .AUTO_SIZE_TEXT_TYPE_UNIFORM
                     ? TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM
                     : TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
         } else {
@@ -371,8 +379,8 @@
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
     public int getAutoSizeStepGranularity() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            return super.getAutoSizeStepGranularity();
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
+            return getSuperCaller().getAutoSizeStepGranularity();
         } else {
             if (mTextHelper != null) {
                 return mTextHelper.getAutoSizeStepGranularity();
@@ -390,8 +398,8 @@
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
     public int getAutoSizeMinTextSize() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            return super.getAutoSizeMinTextSize();
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
+            return getSuperCaller().getAutoSizeMinTextSize();
         } else {
             if (mTextHelper != null) {
                 return mTextHelper.getAutoSizeMinTextSize();
@@ -409,8 +417,8 @@
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
     public int getAutoSizeMaxTextSize() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            return super.getAutoSizeMaxTextSize();
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
+            return getSuperCaller().getAutoSizeMaxTextSize();
         } else {
             if (mTextHelper != null) {
                 return mTextHelper.getAutoSizeMaxTextSize();
@@ -428,8 +436,8 @@
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
     public int[] getAutoSizeTextAvailableSizes() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            return super.getAutoSizeTextAvailableSizes();
+        if (SDK_LEVEL_SUPPORTS_AUTOSIZE) {
+            return getSuperCaller().getAutoSizeTextAvailableSizes();
         } else {
             if (mTextHelper != null) {
                 return mTextHelper.getAutoSizeTextAvailableSizes();
@@ -448,7 +456,7 @@
     @Override
     public void setFirstBaselineToTopHeight(@Px @IntRange(from = 0) int firstBaselineToTopHeight) {
         if (Build.VERSION.SDK_INT >= 28) {
-            super.setFirstBaselineToTopHeight(firstBaselineToTopHeight);
+            getSuperCaller().setFirstBaselineToTopHeight(firstBaselineToTopHeight);
         } else {
             TextViewCompat.setFirstBaselineToTopHeight(this, firstBaselineToTopHeight);
         }
@@ -458,7 +466,7 @@
     public void setLastBaselineToBottomHeight(
             @Px @IntRange(from = 0) int lastBaselineToBottomHeight) {
         if (Build.VERSION.SDK_INT >= 28) {
-            super.setLastBaselineToBottomHeight(lastBaselineToBottomHeight);
+            getSuperCaller().setLastBaselineToBottomHeight(lastBaselineToBottomHeight);
         } else {
             TextViewCompat.setLastBaselineToBottomHeight(this,
                     lastBaselineToBottomHeight);
@@ -559,7 +567,7 @@
     @RequiresApi(api = 26)
     public void setTextClassifier(@Nullable TextClassifier textClassifier) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P || mTextClassifierHelper == null) {
-            super.setTextClassifier(textClassifier);
+            getSuperCaller().setTextClassifier(textClassifier);
             return;
         }
         mTextClassifierHelper.setTextClassifier(textClassifier);
@@ -571,13 +579,13 @@
      * {@link android.view.textclassifier.TextClassificationManager}.
      */
     @Override
-    @NonNull
     @RequiresApi(api = 26)
+    @NonNull
     public TextClassifier getTextClassifier() {
         // The null check is necessary because getTextClassifier is called when we are invoking
         // the super class's constructor.
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P || mTextClassifierHelper == null) {
-            return super.getTextClassifier();
+            return getSuperCaller().getTextClassifier();
         }
         return mTextClassifierHelper.getTextClassifier();
     }
@@ -776,4 +784,113 @@
         }
 
     }
+
+    @UiThread
+    @RequiresApi(api = 26)
+    SuperCaller getSuperCaller() {
+        if (mSuperCaller == null) {
+            if (Build.VERSION.SDK_INT >= 28) {
+                mSuperCaller = new SuperCallerApi28();
+            } else if (Build.VERSION.SDK_INT >= 26) {
+                mSuperCaller = new SuperCallerApi26();
+            }
+        }
+        return mSuperCaller;
+    }
+
+
+
+    private interface SuperCaller {
+        // api 26
+        int getAutoSizeMaxTextSize();
+        int getAutoSizeMinTextSize();
+        int getAutoSizeStepGranularity();
+        int[] getAutoSizeTextAvailableSizes();
+        int getAutoSizeTextType();
+        TextClassifier getTextClassifier();
+        void setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize,
+                int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit);
+        void setAutoSizeTextTypeUniformWithPresetSizes(int[] presetSizes, int unit);
+        void setAutoSizeTextTypeWithDefaults(int autoSizeTextType);
+        void setTextClassifier(@Nullable TextClassifier textClassifier);
+
+        // api 28
+        void setFirstBaselineToTopHeight(@Px int firstBaselineToTopHeight);
+        void setLastBaselineToBottomHeight(@Px int lastBaselineToBottomHeight);
+    }
+
+    @RequiresApi(api = 26)
+    class SuperCallerApi26 implements SuperCaller {
+        @Override
+        public int getAutoSizeMaxTextSize() {
+            return AppCompatTextView.super.getAutoSizeMaxTextSize();
+        }
+
+        @Override
+        public int getAutoSizeMinTextSize() {
+            return AppCompatTextView.super.getAutoSizeMinTextSize();
+        }
+
+        @Override
+        public int getAutoSizeStepGranularity() {
+            return AppCompatTextView.super.getAutoSizeStepGranularity();
+        }
+
+        @Override
+        public int[] getAutoSizeTextAvailableSizes() {
+            return AppCompatTextView.super.getAutoSizeTextAvailableSizes();
+        }
+
+        @Override
+        public int getAutoSizeTextType() {
+            return AppCompatTextView.super.getAutoSizeTextType();
+        }
+
+        @Override
+        public TextClassifier getTextClassifier() {
+            return AppCompatTextView.super.getTextClassifier();
+        }
+
+        @Override
+        public void setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize,
+                int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit) {
+            AppCompatTextView.super.setAutoSizeTextTypeUniformWithConfiguration(autoSizeMinTextSize,
+                    autoSizeMaxTextSize, autoSizeStepGranularity, unit);
+        }
+
+        @Override
+        public void setAutoSizeTextTypeUniformWithPresetSizes(int[] presetSizes, int unit) {
+            AppCompatTextView.super.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
+        }
+
+        @Override
+        public void setAutoSizeTextTypeWithDefaults(int autoSizeTextType) {
+            AppCompatTextView.super.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
+        }
+
+        @Override
+        public void setTextClassifier(@Nullable TextClassifier textClassifier) {
+            AppCompatTextView.super.setTextClassifier(textClassifier);
+        }
+
+        @Override
+        public void setFirstBaselineToTopHeight(int firstBaselineToTopHeight) {}
+
+        @Override
+        public void setLastBaselineToBottomHeight(int lastBaselineToBottomHeight) {}
+    }
+
+    @RequiresApi(api = 28)
+    class SuperCallerApi28 extends SuperCallerApi26 {
+
+        @Override
+        public void setFirstBaselineToTopHeight(@Px int firstBaselineToTopHeight) {
+            AppCompatTextView.super.setFirstBaselineToTopHeight(firstBaselineToTopHeight);
+        }
+
+        @Override
+        public void setLastBaselineToBottomHeight(@Px int lastBaselineToBottomHeight) {
+            AppCompatTextView.super.setLastBaselineToBottomHeight(lastBaselineToBottomHeight);
+        }
+    }
 }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ViewUtils.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ViewUtils.java
index ef43e50..bc9043a 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ViewUtils.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ViewUtils.java
@@ -16,6 +16,7 @@
 
 package androidx.appcompat.widget;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
 import android.graphics.Rect;
@@ -23,6 +24,7 @@
 import android.util.Log;
 import android.view.View;
 
+import androidx.annotation.ChecksSdkIntAtLeast;
 import androidx.annotation.RestrictTo;
 import androidx.core.view.ViewCompat;
 
@@ -38,6 +40,13 @@
 
     private static Method sComputeFitSystemWindowsMethod;
 
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    @ChecksSdkIntAtLeast(api = 27)
+    static final boolean SDK_LEVEL_SUPPORTS_AUTOSIZE = Build.VERSION.SDK_INT >= 27;
+
     static {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
             try {
diff --git a/appsearch/appsearch-debug-view/samples/src/main/java/androidx/appsearch/debugview/samples/NotesActivity.java b/appsearch/appsearch-debug-view/samples/src/main/java/androidx/appsearch/debugview/samples/NotesActivity.java
index bddb5fb..ab3e69c 100644
--- a/appsearch/appsearch-debug-view/samples/src/main/java/androidx/appsearch/debugview/samples/NotesActivity.java
+++ b/appsearch/appsearch-debug-view/samples/src/main/java/androidx/appsearch/debugview/samples/NotesActivity.java
@@ -122,7 +122,7 @@
     @Override
     public boolean onOptionsItemSelected(@NonNull MenuItem item) {
         switch (item.getItemId()) {
-            case (R.id.app_search_debug):
+            case R.id.app_search_debug:
                 Intent intent = new Intent(this, AppSearchDebugActivity.class);
                 intent.putExtra(AppSearchDebugActivity.DB_INTENT_KEY, DB_NAME);
                 intent.putExtra(AppSearchDebugActivity.STORAGE_TYPE_INTENT_KEY,
diff --git a/asynclayoutinflater/asynclayoutinflater/build.gradle b/asynclayoutinflater/asynclayoutinflater/build.gradle
index 35aad6d..cbb041d 100644
--- a/asynclayoutinflater/asynclayoutinflater/build.gradle
+++ b/asynclayoutinflater/asynclayoutinflater/build.gradle
@@ -8,7 +8,10 @@
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
     api("androidx.core:core:1.1.0")
-    implementation("androidx.appcompat:appcompat:1.1.0")
+    constraints {
+        implementation("androidx.appcompat:appcompat:1.1.0") // Optional dependency via compile-only
+    }
+    compileOnly("androidx.appcompat:appcompat:1.1.0")
 
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0")
     androidTestImplementation(libs.testCore)
diff --git a/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AsyncLayoutInflater.java b/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AsyncLayoutInflater.java
index e4e04b4..698bdc2 100644
--- a/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AsyncLayoutInflater.java
+++ b/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AsyncLayoutInflater.java
@@ -71,6 +71,15 @@
  */
 public final class AsyncLayoutInflater {
     private static final String TAG = "AsyncLayoutInflater";
+    private static boolean sAppCompatPresent;
+    static {
+        try {
+            Class.forName("androidx.appcompat.app.AppCompatActivity");
+            sAppCompatPresent = true;
+        } catch (ClassNotFoundException ex) {
+            sAppCompatPresent = false;
+        }
+    }
 
     LayoutInflater mInflater;
     LayoutInflater mInflaterDeprecated;
@@ -82,7 +91,7 @@
         mInflater = new BasicInflater(context);
         mHandler = new Handler(Looper.myLooper(), mHandlerCallback);
         mInflateThread = InflateThread.getInstance();
-        if (context instanceof AppCompatActivity) {
+        if (sAppCompatPresent && context instanceof AppCompatActivity) {
             AppCompatDelegate delegate = AppCompatDelegate.create((Activity) context,
                     (AppCompatCallback) context);
             if (delegate instanceof LayoutInflater.Factory2) {
diff --git a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothManager.kt b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothManager.kt
new file mode 100644
index 0000000..6019e4f
--- /dev/null
+++ b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothManager.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright 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.bluetooth.core
+
+import android.bluetooth.BluetoothAdapter as FwkBluetoothAdapter
+import android.bluetooth.BluetoothDevice as FwkBluetoothDevice
+import android.bluetooth.BluetoothGattServer as FwkBluetoothGattServer
+import android.bluetooth.BluetoothGattServerCallback as FwkBluetoothGattServerCallback
+import android.bluetooth.BluetoothManager as FwkBluetoothManager
+import android.bluetooth.BluetoothProfile as FwkBluetoothProfile
+
+import android.Manifest
+import android.content.Context
+import android.content.pm.PackageManager
+
+import androidx.annotation.RequiresFeature
+import androidx.annotation.RequiresPermission
+
+/**
+ * High level manager used to obtain an instance of an {@link BluetoothAdapter}
+ * and to conduct overall Bluetooth Management.
+ * <p>
+ * Use {@link android.content.Context#getSystemService(java.lang.String)}
+ * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager},
+ * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}.
+ * </p>
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>
+ * For more information about using BLUETOOTH, read the <a href=
+ * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
+ * guide.
+ * </p>
+ * </div>
+ *
+ * @see Context#getSystemService
+ * @see BluetoothAdapter#getDefaultAdapter()
+ *
+ * @hide
+ */
+// @SystemService(Context.BLUETOOTH_SERVICE)
+@RequiresFeature(
+    name = PackageManager.FEATURE_BLUETOOTH,
+    enforcement = "android.content.pm.PackageManager#hasSystemFeature"
+)
+class BluetoothManager(context: Context) {
+
+    companion object {
+        private const val TAG = "BluetoothManager"
+        private const val DBG = false
+    }
+
+    private val fwkBluetoothManager =
+        context.getSystemService(Context.BLUETOOTH_SERVICE) as FwkBluetoothManager
+
+    /**
+     * Get the BLUETOOTH Adapter for this device.
+     *
+     * @return the BLUETOOTH Adapter
+     */
+    // TODO(ofy) Change FwkBluetoothAdapter to core.BluetoothAdapter when it is available
+//    @RequiresNoPermission
+    fun getAdapter(): FwkBluetoothAdapter {
+        return fwkBluetoothManager.adapter
+    }
+
+    /**
+     * Get the current connection state of the profile to the remote device.
+     *
+     *
+     * This is not specific to any application configuration but represents
+     * the connection state of the local Bluetooth adapter for certain profile.
+     * This can be used by applications like status bar which would just like
+     * to know the state of Bluetooth.
+     *
+     * @param device Remote bluetooth device.
+     * @param profile GATT or GATT_SERVER
+     * @return State of the profile connection. One of [FwkBluetoothProfile.STATE_CONNECTED],
+     * [FwkBluetoothProfile.STATE_CONNECTING], [FwkBluetoothProfile.STATE_DISCONNECTED],
+     * [FwkBluetoothProfile.STATE_DISCONNECTING]
+     * // TODO(ofy) Change FwkBluetoothProfile to core.BluetoothProfile when it is available
+     */
+//    @RequiresLegacyBluetoothPermission
+//    @RequiresBluetoothConnectPermission
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+    // TODO(ofy) Change FwkBluetoothDevice to core.BluetoothDevice when it is available
+    fun getConnectionState(device: FwkBluetoothDevice, profile: Int): Int {
+        return fwkBluetoothManager.getConnectionState(device, profile)
+    }
+
+    /**
+     * Get connected devices for the specified profile.
+     *
+     *
+     *  Return the set of devices which are in state [FwkBluetoothProfile.STATE_CONNECTED]
+     *  // TODO(ofy) Change FwkBluetoothProfile to core.BluetoothProfile when it is available
+     *
+     *
+     * This is not specific to any application configuration but represents
+     * the connection state of Bluetooth for this profile.
+     * This can be used by applications like status bar which would just like
+     * to know the state of Bluetooth.
+     *
+     * @param profile GATT or GATT_SERVER
+     * @return List of devices. The list will be empty on error.
+     */
+//    @RequiresLegacyBluetoothPermission
+//    @RequiresBluetoothConnectPermission
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+    // TODO(ofy) Change FwkBluetoothDevice to core.BluetoothDevice when it is available
+    fun getConnectedDevices(profile: Int): List<FwkBluetoothDevice> {
+        return fwkBluetoothManager.getConnectedDevices(profile)
+    }
+
+    /**
+     * Get a list of devices that match any of the given connection
+     * states.
+     *
+     *
+     *  If none of the devices match any of the given states,
+     * an empty list will be returned.
+     *
+     *
+     * This is not specific to any application configuration but represents
+     * the connection state of the local Bluetooth adapter for this profile.
+     * This can be used by applications like status bar which would just like
+     * to know the state of the local adapter.
+     *
+     * @param profile GATT or GATT_SERVER
+     * @param states Array of states. States can be one of [FwkBluetoothProfile.STATE_CONNECTED],
+     * [FwkBluetoothProfile.STATE_CONNECTING], [FwkBluetoothProfile.STATE_DISCONNECTED],
+     * [FwkBluetoothProfile.STATE_DISCONNECTING],
+     * // TODO(ofy) Change FwkBluetoothProfile to core.BluetoothProfile when it is available
+     * @return List of devices. The list will be empty on error.
+     */
+//    @RequiresLegacyBluetoothPermission
+//    @RequiresBluetoothConnectPermission
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+    // TODO(ofy) Change FwkBluetoothDevice to core.BluetoothDevice when it is available
+    fun getDevicesMatchingConnectionStates(
+        profile: Int,
+        states: IntArray
+    ): List<FwkBluetoothDevice> {
+        return fwkBluetoothManager.getDevicesMatchingConnectionStates(profile, states)
+    }
+
+    /**
+     * Open a GATT Server
+     * The callback is used to deliver results to Caller, such as connection status as well
+     * as the results of any other GATT server operations.
+     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
+     * to conduct GATT server operations.
+     *
+     * @param context App context
+     * @param callback GATT server callback handler that will receive asynchronous callbacks.
+     * @return BluetoothGattServer instance
+     */
+//    @RequiresBluetoothConnectPermission
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+    // TODO(ofy) Change FwkBluetoothGattServerCallback to core.BluetoothGattServerCallback when it is available
+    // TODO(ofy) Change FwkBluetoothGattServer to core.BluetoothGattServer when it is available
+    fun openGattServer(
+        context: Context,
+        callback: FwkBluetoothGattServerCallback
+    ): FwkBluetoothGattServer {
+        return fwkBluetoothManager.openGattServer(context, callback)
+    }
+}
diff --git a/bluetooth/bluetooth/build.gradle b/bluetooth/bluetooth/build.gradle
index a83aefa..7c49ae8 100644
--- a/bluetooth/bluetooth/build.gradle
+++ b/bluetooth/bluetooth/build.gradle
@@ -24,7 +24,10 @@
 
 dependencies {
     implementation(libs.kotlinStdlib)
-    implementation 'androidx.annotation:annotation:1.4.0'
+
+    api(project(":bluetooth:bluetooth-core"))
+
+    implementation "androidx.annotation:annotation:1.4.0"
 }
 
 androidx {
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattCharacteristic.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattCharacteristic.kt
index beff108..206a2c0 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattCharacteristic.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattCharacteristic.kt
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothGattCharacteristic as FwkBluetoothGattCharacteristic
 import androidx.annotation.RequiresApi
 import androidx.bluetooth.utils.Bundleable
+import androidx.bluetooth.utils.Utils
 
 import java.util.UUID
 /**
@@ -110,6 +111,12 @@
     val descriptors
         get() = impl.descriptors
 
+    var service
+        get() = impl.service
+        internal set(value) {
+            impl.service = value
+        }
+
     constructor (uuid: UUID, properties: Int, permissions: Int) : this(
         FwkBluetoothGattCharacteristic(uuid, properties, permissions)
     )
@@ -135,6 +142,7 @@
         val instanceId: Int
         var writeType: Int
         val descriptors: List<BluetoothGattDescriptor>
+        var service: BluetoothGattService?
 
         fun addDescriptor(descriptor: BluetoothGattDescriptor): Boolean
         fun getDescriptor(uuid: UUID): BluetoothGattDescriptor?
@@ -158,13 +166,11 @@
 
             val CREATOR: Bundleable.Creator<BluetoothGattCharacteristic> =
                 object : Bundleable.Creator<BluetoothGattCharacteristic> {
-                    @Suppress("deprecation")
                     override fun fromBundle(bundle: Bundle): BluetoothGattCharacteristic {
-                        assert(Build.VERSION.SDK_INT < 24)
                         val uuid = bundle.getString(keyForField(FIELD_FWK_CHARACTERISTIC_UUID))
                             ?: throw IllegalArgumentException("Bundle doesn't include uuid")
                         val instanceId =
-                            bundle.getInt(keyForField(FIELD_FWK_CHARACTERISTIC_INSTANCE_ID), -1)
+                            bundle.getInt(keyForField(FIELD_FWK_CHARACTERISTIC_INSTANCE_ID), 0)
                         val properties =
                             bundle.getInt(keyForField(FIELD_FWK_CHARACTERISTIC_PROPERTIES), -1)
                         val permissions =
@@ -182,18 +188,21 @@
                         if (writeType == -1) {
                             throw IllegalArgumentException("Bundle doesn't include writeType")
                         }
-                        if (instanceId == -1) {
-                            throw IllegalArgumentException("Bundle doesn't include instanceId")
-                        }
                         if (keySize == -1) {
                             throw IllegalArgumentException("Bundle doesn't include keySize")
                         }
 
                         val fwkCharacteristic =
-                            FwkBluetoothGattCharacteristic(
+                            FwkBluetoothGattCharacteristic::class.java.getConstructor(
+                                UUID::class.java,
+                                Integer.TYPE,
+                                Integer.TYPE,
+                                Integer.TYPE,
+                            ).newInstance(
                                 UUID.fromString(uuid),
+                                instanceId,
                                 properties,
-                                permissions
+                                permissions,
                             )
                         fwkCharacteristic.writeType = writeType
 
@@ -205,9 +214,11 @@
 
                         val gattCharacteristic = BluetoothGattCharacteristic(fwkCharacteristic)
 
-                        bundle.getParcelableArrayList<Bundle>(
-                            keyForField(FIELD_FWK_CHARACTERISTIC_DESCRIPTORS)
-                        )?.forEach { it ->
+                        Utils.getParcelableArrayListFromBundle(
+                            bundle,
+                            keyForField(FIELD_FWK_CHARACTERISTIC_DESCRIPTORS),
+                            Bundle::class.java
+                        ).forEach {
                             gattCharacteristic.addDescriptor(
                                 BluetoothGattDescriptor.CREATOR.fromBundle(it)
                             )
@@ -218,6 +229,50 @@
                 }
         }
 
+        init {
+            fwkCharacteristic.descriptors.forEach {
+                val descriptor = BluetoothGattDescriptor(it)
+                _descriptors.add(descriptor)
+                descriptor.characteristic = characteristic
+            }
+        }
+
+        override val uuid: UUID
+            get() = fwkCharacteristic.uuid
+        override val properties
+            get() = fwkCharacteristic.properties
+        override val permissions
+            get() = fwkCharacteristic.permissions
+        override val instanceId
+            get() = fwkCharacteristic.instanceId
+        override var writeType: Int
+            get() = fwkCharacteristic.writeType
+            set(value) {
+                fwkCharacteristic.writeType = value
+            }
+        private var _descriptors = mutableListOf<BluetoothGattDescriptor>()
+        override val descriptors
+            get() = _descriptors.toList()
+        override var service: BluetoothGattService? = null
+
+        override fun addDescriptor(
+            descriptor: BluetoothGattDescriptor,
+        ): Boolean {
+            return if (fwkCharacteristic.addDescriptor(descriptor.fwkDescriptor)) {
+                _descriptors.add(descriptor)
+                descriptor.characteristic = characteristic
+                true
+            } else {
+                false
+            }
+        }
+
+        override fun getDescriptor(uuid: UUID): BluetoothGattDescriptor? {
+            return _descriptors.firstOrNull {
+                it.uuid == uuid
+            }
+        }
+
         override fun toBundle(): Bundle {
             assert(Build.VERSION.SDK_INT < 24)
 
@@ -243,56 +298,13 @@
 
             return bundle
         }
-
-        init {
-            fwkCharacteristic.descriptors.forEach {
-                val descriptor = BluetoothGattDescriptor(it)
-                mDescriptors.add(descriptor)
-                descriptor.characteristic = characteristic
-            }
-        }
-
-        override val uuid: UUID
-            get() = fwkCharacteristic.uuid
-        override val properties
-            get() = fwkCharacteristic.properties
-        override val permissions
-            get() = fwkCharacteristic.permissions
-        override val instanceId
-            get() = fwkCharacteristic.instanceId
-        override var writeType: Int
-            get() = fwkCharacteristic.writeType
-            set(value) {
-                fwkCharacteristic.writeType = value
-            }
-        private var mDescriptors = mutableListOf<BluetoothGattDescriptor>()
-        override val descriptors
-            get() = mDescriptors.toList()
-
-        override fun addDescriptor(
-            descriptor: BluetoothGattDescriptor,
-        ): Boolean {
-            return if (fwkCharacteristic.addDescriptor(descriptor.fwkDescriptor)) {
-                mDescriptors.add(descriptor)
-                descriptor.characteristic = characteristic
-                true
-            } else {
-                false
-            }
-        }
-
-        override fun getDescriptor(uuid: UUID): BluetoothGattDescriptor? {
-            return mDescriptors.firstOrNull {
-                it.uuid == uuid
-            }
-        }
     }
 
     @RequiresApi(Build.VERSION_CODES.N)
     private class GattCharacteristicImplApi24(
         fwkCharacteristic: FwkBluetoothGattCharacteristic,
         characteristic: BluetoothGattCharacteristic,
-        ) : GattCharacteristicImplApi21(fwkCharacteristic, characteristic) {
+    ) : GattCharacteristicImplApi21(fwkCharacteristic, characteristic) {
         companion object {
             internal const val FIELD_FWK_CHARACTERISTIC = 0
 
@@ -301,8 +313,10 @@
                     @Suppress("deprecation")
                     override fun fromBundle(bundle: Bundle): BluetoothGattCharacteristic {
                         val fwkCharacteristic =
-                            bundle.getParcelable<FwkBluetoothGattCharacteristic>(
-                                keyForField(FIELD_FWK_CHARACTERISTIC)
+                            Utils.getParcelableFromBundle(
+                                bundle,
+                                keyForField(FIELD_FWK_CHARACTERISTIC),
+                                FwkBluetoothGattCharacteristic::class.java
                             ) ?: throw IllegalArgumentException(
                                 "Bundle doesn't include framework characteristic"
                             )
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattDescriptor.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattDescriptor.kt
index e381b4e..3c3c87a 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattDescriptor.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattDescriptor.kt
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothGattDescriptor as FwkBluetoothGattDescriptor
 import androidx.annotation.RequiresApi
 import androidx.bluetooth.utils.Bundleable
+import androidx.bluetooth.utils.Utils
 
 import java.util.UUID
 /**
@@ -110,7 +111,6 @@
 
             val CREATOR: Bundleable.Creator<BluetoothGattDescriptor> =
                 object : Bundleable.Creator<BluetoothGattDescriptor> {
-                    @Suppress("DEPRECATION")
                     override fun fromBundle(bundle: Bundle): BluetoothGattDescriptor {
                         val permissions =
                             bundle.getInt(
@@ -121,21 +121,25 @@
                             keyForField(FIELD_FWK_DESCRIPTOR_UUID),
                         ) ?: throw IllegalArgumentException("Bundle doesn't include uuid")
 
+                        val instanceId = bundle.getInt(
+                            keyForField(FIELD_FWK_DESCRIPTOR_INSTANCE),
+                            0
+                        )
+
                         if (permissions == -1) {
                             throw IllegalArgumentException("Bundle doesn't include permission")
                         }
 
-                        val descriptor =
-                            FwkBluetoothGattDescriptor(
-                                UUID.fromString(uuid),
-                                permissions
-                            )
-
-                        descriptor.javaClass.getDeclaredField("mInstance").setInt(
-                            descriptor, bundle.getInt(
-                                keyForField(FIELD_FWK_DESCRIPTOR_INSTANCE), 0
-                            )
+                        val descriptor = FwkBluetoothGattDescriptor::class.java.getConstructor(
+                            UUID::class.java,
+                            Integer.TYPE,
+                            Integer.TYPE
+                        ).newInstance(
+                            UUID.fromString(uuid),
+                            instanceId,
+                            permissions
                         )
+
                         return BluetoothGattDescriptor(descriptor)
                     }
                 }
@@ -166,11 +170,12 @@
             internal const val FIELD_FWK_DESCRIPTOR = 0
             val CREATOR: Bundleable.Creator<BluetoothGattDescriptor> =
                 object : Bundleable.Creator<BluetoothGattDescriptor> {
-                    @Suppress("DEPRECATION")
                     override fun fromBundle(bundle: Bundle): BluetoothGattDescriptor {
                         val fwkDescriptor =
-                            bundle.getParcelable<android.bluetooth.BluetoothGattDescriptor>(
-                                keyForField(FIELD_FWK_DESCRIPTOR)
+                            Utils.getParcelableFromBundle(
+                                bundle,
+                                keyForField(FIELD_FWK_DESCRIPTOR),
+                                FwkBluetoothGattDescriptor::class.java
                             ) ?: throw IllegalArgumentException("Bundle doesn't contain descriptor")
 
                         return BluetoothGattDescriptor(fwkDescriptor)
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattService.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattService.kt
new file mode 100644
index 0000000..dec3d94
--- /dev/null
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattService.kt
@@ -0,0 +1,309 @@
+/*
+ * Copyright 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.bluetooth
+
+import android.os.Build
+import android.os.Bundle
+import android.bluetooth.BluetoothGattService as FwkBluetoothGattService
+import androidx.annotation.RequiresApi
+import androidx.bluetooth.utils.Bundleable
+import androidx.bluetooth.utils.Utils
+
+import java.util.UUID
+
+/**
+ * @hide
+ */
+class BluetoothGattService internal constructor(service: FwkBluetoothGattService) :
+    Bundleable {
+
+    companion object {
+        val CREATOR: Bundleable.Creator<BluetoothGattService> =
+            if (Build.VERSION.SDK_INT >= 24) {
+                GattServiceImplApi24.CREATOR
+            } else {
+                GattServiceImplApi21.CREATOR
+            }
+
+        internal fun keyForField(field: Int): String {
+            return field.toString(Character.MAX_RADIX)
+        }
+    }
+
+    constructor(uuid: UUID, type: Int) : this(
+        FwkBluetoothGattService(
+            uuid,
+            type
+        )
+    )
+
+    private val impl =
+        if (Build.VERSION.SDK_INT >= 24) {
+            GattServiceImplApi24(service, this)
+        } else {
+            GattServiceImplApi21(service, this)
+        }
+
+    internal val fwkService: FwkBluetoothGattService
+        get() = impl.fwkService
+    val instanceId: Int
+        get() = impl.instanceId
+    val type: Int
+        get() = impl.type
+    val uuid: UUID
+        get() = impl.uuid
+
+    val characteristics: List<BluetoothGattCharacteristic>
+        get() = impl.characteristics
+
+    val includedServices: List<BluetoothGattService>
+        get() = impl.includedServices
+
+    fun addCharacteristic(characteristic: BluetoothGattCharacteristic): Boolean {
+        return impl.addCharacteristic(characteristic)
+    }
+
+    fun getCharacteristic(uuid: UUID): BluetoothGattCharacteristic? {
+        return impl.getCharacteristic(uuid)
+    }
+
+    fun addService(service: BluetoothGattService): Boolean {
+        return impl.addService(service)
+    }
+
+    fun getIncludedService(uuid: UUID): BluetoothGattService? {
+        return impl.getIncludedService(uuid)
+    }
+
+    override fun toBundle(): Bundle {
+        return impl.toBundle()
+    }
+
+    private interface GattServiceImpl {
+        val fwkService: FwkBluetoothGattService
+        val instanceId: Int
+        val type: Int
+        val uuid: UUID
+        val includedServices: List<BluetoothGattService>
+        val characteristics: List<BluetoothGattCharacteristic>
+
+        fun addCharacteristic(characteristic: BluetoothGattCharacteristic): Boolean
+
+        fun getCharacteristic(uuid: UUID): BluetoothGattCharacteristic?
+
+        fun addService(service: BluetoothGattService): Boolean
+
+        fun getIncludedService(uuid: UUID): BluetoothGattService?
+
+        fun toBundle(): Bundle
+    }
+
+    private open class GattServiceImplApi21(
+        final override val fwkService: FwkBluetoothGattService,
+        service: BluetoothGattService
+    ) : GattServiceImpl {
+
+        companion object {
+            internal const val FIELD_FWK_SERVICE_INSTANCE_ID = 1
+            internal const val FIELD_FWK_SERVICE_TYPE = 2
+            internal const val FIELD_FWK_SERVICE_CHARACTERISTICS = 3
+            internal const val FIELD_FWK_SERVICE_SERVICES = 4
+            internal const val FIELD_FWK_SERVICE_UUID = 5
+
+            val CREATOR: Bundleable.Creator<BluetoothGattService> =
+                object : Bundleable.Creator<BluetoothGattService> {
+                    override fun fromBundle(bundle: Bundle): BluetoothGattService {
+                        val uuid = bundle.getString(keyForField(FIELD_FWK_SERVICE_UUID))
+                            ?: throw IllegalArgumentException("Bundle doesn't include uuid")
+                        val instanceId =
+                            bundle.getInt(keyForField(FIELD_FWK_SERVICE_INSTANCE_ID), 0)
+                        val type = bundle.getInt(keyForField(FIELD_FWK_SERVICE_TYPE), -1)
+                        if (type == -1) {
+                            throw IllegalArgumentException("Bundle doesn't include service type")
+                        }
+
+                        val fwkService = FwkBluetoothGattService::class.java.getConstructor(
+                            UUID::class.java,
+                            Integer.TYPE,
+                            Integer.TYPE,
+                        ).newInstance(UUID.fromString(uuid), instanceId, type)
+
+                        val gattService = BluetoothGattService(fwkService)
+
+                        Utils.getParcelableArrayListFromBundle(
+                            bundle,
+                            keyForField(FIELD_FWK_SERVICE_CHARACTERISTICS),
+                            Bundle::class.java
+                        ).forEach {
+                            gattService.addCharacteristic(
+                                BluetoothGattCharacteristic.CREATOR.fromBundle(
+                                    it
+                                )
+                            )
+                        }
+
+                        Utils.getParcelableArrayListFromBundle(
+                            bundle,
+                            keyForField(FIELD_FWK_SERVICE_SERVICES),
+                            Bundle::class.java
+                        ).forEach {
+                            val includedServices = Utils.getParcelableArrayListFromBundle(
+                                it,
+                                keyForField(FIELD_FWK_SERVICE_SERVICES),
+                                Bundle::class.java
+                            )
+                            if (includedServices.isNotEmpty()) {
+                                throw IllegalArgumentException(
+                                    "Included service shouldn't pass " +
+                                        "its included services to bundle"
+                                )
+                            }
+
+                            val includedCharacteristics = Utils.getParcelableArrayListFromBundle(
+                                it,
+                                keyForField(FIELD_FWK_SERVICE_CHARACTERISTICS),
+                                Bundle::class.java
+                            )
+
+                            if (includedCharacteristics.isNotEmpty()) {
+                                throw IllegalArgumentException(
+                                    "Included service shouldn't pass characteristic to bundle"
+                                )
+                            }
+
+                            gattService.addService(BluetoothGattService.CREATOR.fromBundle(it))
+                        }
+
+                        return gattService
+                    }
+                }
+        }
+
+        override val instanceId: Int
+            get() = fwkService.instanceId
+        override val type: Int
+            get() = fwkService.type
+        override val uuid: UUID
+            get() = fwkService.uuid
+
+        private val _includedServices = mutableListOf<BluetoothGattService>()
+        override val includedServices: List<BluetoothGattService>
+            get() = _includedServices.toList()
+
+        private val _characteristics = mutableListOf<BluetoothGattCharacteristic>()
+        override val characteristics
+            get() = _characteristics.toList()
+
+        init {
+            this.fwkService.characteristics.forEach {
+                val characteristic = BluetoothGattCharacteristic(it)
+                _characteristics.add(characteristic)
+                characteristic.service = service
+            }
+            this.fwkService.includedServices.forEach {
+                _includedServices.add(BluetoothGattService(it))
+            }
+        }
+
+        override fun addCharacteristic(characteristic: BluetoothGattCharacteristic): Boolean {
+            return if (fwkService.addCharacteristic(characteristic.fwkCharacteristic)) {
+                _characteristics.add(characteristic)
+                true
+            } else {
+                false
+            }
+        }
+
+        override fun getCharacteristic(uuid: UUID): BluetoothGattCharacteristic? {
+            return _characteristics.firstOrNull {
+                it.uuid == uuid
+            }
+        }
+
+        override fun addService(service: BluetoothGattService): Boolean {
+            return if (fwkService.addService(service.fwkService)) {
+                _includedServices.add(service)
+                true
+            } else {
+                false
+            }
+        }
+
+        override fun getIncludedService(uuid: UUID): BluetoothGattService? {
+            return _includedServices.firstOrNull {
+                it.uuid == uuid
+            }
+        }
+
+        override fun toBundle(): Bundle {
+            val bundle = Bundle()
+            bundle.putString(keyForField(FIELD_FWK_SERVICE_UUID), uuid.toString())
+            bundle.putInt(keyForField(FIELD_FWK_SERVICE_INSTANCE_ID), instanceId)
+            bundle.putInt(keyForField(FIELD_FWK_SERVICE_TYPE), type)
+            bundle.putParcelableArrayList(
+                keyForField(FIELD_FWK_SERVICE_CHARACTERISTICS),
+                ArrayList(_characteristics.map { it.toBundle() })
+            )
+            bundle.putParcelableArrayList(
+                keyForField(FIELD_FWK_SERVICE_SERVICES),
+
+                // Cut all included services & characteristics of included services. Developers
+                // should directly send the included services if need it
+                ArrayList(_includedServices.map {
+                    BluetoothGattService(it.uuid,
+                                         it.type).toBundle()
+                })
+            )
+
+            return bundle
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.N)
+    private class GattServiceImplApi24(
+        fwkService: FwkBluetoothGattService,
+        service: BluetoothGattService
+    ) : GattServiceImplApi21(fwkService, service) {
+
+        companion object {
+            internal const val FIELD_FWK_SERVICE = 0
+
+            @Suppress("DEPRECATION")
+            val CREATOR: Bundleable.Creator<BluetoothGattService> =
+                object : Bundleable.Creator<BluetoothGattService> {
+                    override fun fromBundle(bundle: Bundle): BluetoothGattService {
+                        val fwkService =
+                            Utils.getParcelableFromBundle(
+                                bundle,
+                                keyForField(FIELD_FWK_SERVICE),
+                                FwkBluetoothGattService::class.java
+                            )
+                                ?: throw IllegalArgumentException(
+                                    "Bundle doesn't contain framework service"
+                                )
+                        return BluetoothGattService(fwkService)
+                    }
+                }
+        }
+
+        override fun toBundle(): Bundle {
+            val bundle = Bundle()
+            bundle.putParcelable(keyForField(FIELD_FWK_SERVICE), fwkService)
+            return bundle
+        }
+    }
+}
\ No newline at end of file
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/utils/Utils.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/utils/Utils.kt
new file mode 100644
index 0000000..a7cf935
--- /dev/null
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/utils/Utils.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 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.bluetooth.utils
+
+import android.os.Build
+import android.os.Bundle
+import android.os.Parcelable
+
+/**
+ * @hide
+ */
+class Utils {
+    companion object {
+
+        // TODO: Migrate to BundleCompat when available
+        @Suppress("DEPRECATION")
+        fun <T : Parcelable> getParcelableFromBundle(
+            bundle: Bundle,
+            key: String,
+            clazz: Class<T>
+        ): T? {
+            bundle.classLoader = clazz.classLoader
+            if (Build.VERSION.SDK_INT >= 33) {
+                // TODO: Return framework's getParcelable when SDK 33 is available
+                // return bundle.getParcelable(key, clazz)
+                TODO()
+            } else {
+                val parcelable: T?
+                try {
+                    parcelable = bundle.getParcelable(key)
+                } catch (e: Exception) {
+                    return null
+                }
+                return parcelable
+            }
+        }
+
+        @Suppress("DEPRECATION")
+        fun <T : Parcelable> getParcelableArrayListFromBundle(
+            bundle: Bundle,
+            key: String,
+            clazz: Class<T>
+        ): List<T> {
+            bundle.classLoader = clazz.classLoader
+            if (Build.VERSION.SDK_INT >= 33) {
+                // TODO: Return framework's getParcelableArrayList when SDK 33 is available
+                // return bundle.getParcelableArrayList(key, clazz)
+                TODO()
+            } else {
+                val parcelable: List<T>
+                try {
+                    parcelable = bundle.getParcelableArrayList(key) ?: emptyList()
+                } catch (e: Exception) {
+                    return emptyList()
+                }
+                return parcelable
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/bluetoothx/BtxFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/bluetoothx/BtxFragment.kt
index db47746..8edffdd 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/bluetoothx/BtxFragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/bluetoothx/BtxFragment.kt
@@ -17,10 +17,12 @@
 package androidx.bluetooth.integration.testapp.ui.bluetoothx
 
 import android.os.Bundle
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import android.widget.Toast
+
+import androidx.bluetooth.core.BluetoothManager
 import androidx.bluetooth.integration.testapp.R
 import androidx.bluetooth.integration.testapp.databinding.FragmentBtxBinding
 import androidx.fragment.app.Fragment
@@ -64,7 +66,24 @@
     }
 
     private fun scan() {
-        Toast.makeText(context, getString(R.string.scan_not_yet_implemented), Toast.LENGTH_SHORT)
-            .show()
+        Log.d(TAG, "scan() called")
+
+        val bluetoothManager = BluetoothManager(requireContext())
+
+        @Suppress("UNUSED_VARIABLE")
+        // TODO(ofy) Use below
+        val bluetoothAdapter = bluetoothManager.getAdapter()
+
+        // TODO(ofy) Convert to BluetoothX classes
+//        val bleScanner = bluetoothAdapter?.bluetoothLeScanner
+//
+//        val scanSettings = ScanSettings.Builder()
+//            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+//            .build()
+//
+//        bleScanner?.startScan(null, scanSettings, scanCallback)
+//
+//        Toast.makeText(context, getString(R.string.scan_start_message), Toast.LENGTH_LONG)
+//            .show()
     }
 }
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/framework/FwkFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/framework/FwkFragment.kt
index 5261a18..5257eae 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/framework/FwkFragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/framework/FwkFragment.kt
@@ -128,6 +128,8 @@
     // Permissions are handled by MainActivity requestBluetoothPermissions
     @SuppressLint("MissingPermission")
     private fun startAdvertise() {
+        Log.d(TAG, "startAdvertise() called")
+
         val bluetoothManager =
             context?.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager
 
diff --git a/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
index 9828cdd..ff75011 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
@@ -28,7 +28,6 @@
     <string name="scan_using_btx">Scan using BluetoothX APIs</string>
 
     <string name="scan_start_message">Scan should have started. Results are in Logcat</string>
-    <string name="scan_not_yet_implemented">Scan not yet implemented.</string>
 
     <string name="advertise_using_fwk">Advertise using Framework Bluetooth APIs</string>
     <string name="advertise_start_message">Advertise started</string>
diff --git a/buildSrc/SampleLibraryMetadata.json b/buildSrc/SampleLibraryMetadata.json
deleted file mode 100644
index ad6e17d..0000000
--- a/buildSrc/SampleLibraryMetadata.json
+++ /dev/null
@@ -1,26 +0,0 @@
-[
-  {
-    "groupId": "androidx.paging",
-    "artifactId": "paging-compose",
-    "releaseNotesUrl": "https://developer.android.com/jetpack/androidx/releases/paging",
-    "sourceDir": "paging/compose"
-  },
-  {
-    "groupId": "androidx.paging",
-    "artifactId": "paging-runtime",
-    "releaseNotesUrl": "https://developer.android.com/jetpack/androidx/releases/paging",
-    "sourceDir": "paging"
-  },
-  {
-    "groupId": "androidx.paging",
-    "artifactId": "paging-rxjava2",
-    "releaseNotesUrl": "https://developer.android.com/jetpack/androidx/releases/paging",
-    "sourceDir": "paging/rxjava2"
-  },
-  {
-    "groupId": "androidx.paging",
-    "artifactId": "paging-rxjava3",
-    "releaseNotesUrl": "https://developer.android.com/jetpack/androidx/releases/paging",
-    "sourceDir": "paging/rxjava3"
-  }
-]
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index cd7042e..54cf195 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -25,6 +25,8 @@
 import androidx.build.SupportConfig.DEFAULT_MIN_SDK_VERSION
 import androidx.build.SupportConfig.INSTRUMENTATION_RUNNER
 import androidx.build.SupportConfig.TARGET_SDK_VERSION
+import androidx.build.buildInfo.CreateAggregateLibraryBuildInfoFileTask
+import androidx.build.buildInfo.CreateLibraryBuildInfoFileTask
 import androidx.build.checkapi.JavaApiTaskConfig
 import androidx.build.checkapi.KmpApiTaskConfig
 import androidx.build.checkapi.LibraryApiTaskConfig
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
index fe5eb3d..a48da4f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
@@ -18,6 +18,7 @@
 
 import androidx.build.AndroidXImplPlugin.Companion.ZIP_CONSTRAINED_TEST_CONFIGS_WITH_APKS_TASK
 import androidx.build.AndroidXImplPlugin.Companion.ZIP_TEST_CONFIGS_WITH_APKS_TASK
+import androidx.build.buildInfo.CreateAggregateLibraryBuildInfoFileTask
 import androidx.build.dependencyTracker.AffectedModuleDetector
 import androidx.build.gradle.isRoot
 import androidx.build.license.CheckExternalDependencyLicensesTask
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
index bfc7189..c677c36 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
@@ -131,15 +131,12 @@
             // Consider re-enabling the following checks. Disabled as part of
             // error-prone upgrade
             "-Xep:InlineMeSuggester:OFF",
-            "-Xep:UnusedVariable:OFF",
-            "-Xep:UnusedMethod:OFF",
             "-Xep:NarrowCalculation:OFF",
             "-Xep:LongDoubleConversion:OFF",
             "-Xep:UnicodeEscape:OFF",
             "-Xep:JavaUtilDate:OFF",
             "-Xep:UnrecognisedJavadocTag:OFF",
             "-Xep:ObjectEqualsForPrimitives:OFF",
-            "-Xep:UnnecessaryParentheses:OFF",
             "-Xep:DoNotCallSuggester:OFF",
             "-Xep:EqualsNull:OFF",
             "-Xep:MalformedInlineTag:OFF",
@@ -202,10 +199,10 @@
             // "-Xep:StringSplitter:ERROR", // disabled with upgrade to 2.14.0
             "-Xep:ReferenceEquality:ERROR",
             "-Xep:AssertionFailureIgnored:ERROR",
-            // "-Xep:UnnecessaryParentheses:ERROR", // disabled with upgrade to 2.14.0
+            "-Xep:UnnecessaryParentheses:ERROR",
             "-Xep:EqualsGetClass:ERROR",
-            // "-Xep:UnusedVariable:ERROR", // disabled with upgrade to 2.14.0
-            // "-Xep:UnusedMethod:ERROR", // disabled with upgrade to 2.14.0
+            "-Xep:UnusedVariable:ERROR",
+            "-Xep:UnusedMethod:ERROR",
             "-Xep:UndefinedEquals:ERROR",
             "-Xep:ThreadLocalUsage:ERROR",
             "-Xep:FutureReturnValueIgnored:ERROR",
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/CreateAggregateLibraryBuildInfoFileTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateAggregateLibraryBuildInfoFileTask.kt
similarity index 96%
rename from buildSrc/private/src/main/kotlin/androidx/build/CreateAggregateLibraryBuildInfoFileTask.kt
rename to buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateAggregateLibraryBuildInfoFileTask.kt
index 69c5636..ef6b47f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/CreateAggregateLibraryBuildInfoFileTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateAggregateLibraryBuildInfoFileTask.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 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.
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.build
+package androidx.build.buildInfo
 
+import androidx.build.getDistributionDirectory
 import androidx.build.jetpad.LibraryBuildInfoFile
 import com.google.gson.Gson
 import org.gradle.api.DefaultTask
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt
similarity index 96%
rename from buildSrc/private/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt
rename to buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt
index 6f8ef25..39518f4 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 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.
@@ -14,8 +14,13 @@
  * limitations under the License.
  */
 
-package androidx.build
+package androidx.build.buildInfo
 
+import androidx.build.AndroidXExtension
+import androidx.build.getBuildInfoDirectory
+import androidx.build.getGroupZipPath
+import androidx.build.getProjectZipPath
+import androidx.build.getSupportRootFolder
 import androidx.build.gitclient.Commit
 import androidx.build.gitclient.GitClient
 import androidx.build.gitclient.GitCommitRange
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
index 6ae1586..3012509 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
@@ -22,7 +22,9 @@
 import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.RegularFile
+import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Provider
 import org.gradle.api.provider.SetProperty
 import org.gradle.api.tasks.CacheableTask
 import org.gradle.api.tasks.Classpath
@@ -91,7 +93,7 @@
      * SHOW_LIBRARY_METADATA: set to "true" to display the data
      */
     @get:[InputFile PathSensitive(PathSensitivity.NONE)]
-    lateinit var libraryMetadataFile: RegularFile
+    abstract val libraryMetadataFile: RegularFileProperty
 
     @Input
     var showLibraryMetadata: Boolean = false
@@ -169,7 +171,7 @@
     val excludedPackages: ListProperty<String>
     val excludedPackagesForJava: ListProperty<String>
     val excludedPackagesForKotlin: ListProperty<String>
-    var libraryMetadataFile: RegularFile
+    var libraryMetadataFile: Provider<RegularFile>
     var showLibraryMetadata: Boolean
 }
 
@@ -180,7 +182,7 @@
     excludedPackages: Set<String>,
     excludedPackagesForJava: Set<String>,
     excludedPackagesForKotlin: Set<String>,
-    libraryMetadataFile: RegularFile,
+    libraryMetadataFile: Provider<RegularFile>,
     showLibraryMetadata: Boolean,
 ) {
     val workQueue = workerExecutor.noIsolation()
@@ -206,7 +208,7 @@
 
             // b/183989795 tracks moving these away from an environment variables
             it.environment("DEVSITE_TENANT", "androidx")
-            it.environment("LIBRARY_METADATA_FILE", parameters.libraryMetadataFile.toString())
+            it.environment("LIBRARY_METADATA_FILE", parameters.libraryMetadataFile.get().toString())
             it.environment("SHOW_LIBRARY_METADATA", parameters.showLibraryMetadata)
 
             if (parameters.excludedPackages.get().isNotEmpty())
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dackka/GenerateMetadataTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/dackka/GenerateMetadataTask.kt
new file mode 100644
index 0000000..b3e914b
--- /dev/null
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dackka/GenerateMetadataTask.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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.build.dackka
+
+import java.io.File
+import java.io.Serializable
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import org.json.JSONArray
+
+@CacheableTask
+abstract class GenerateMetadataTask : DefaultTask() {
+
+    /**
+     * List of [MetadataEntry] objects to convert to JSON
+     */
+    @get:Input
+    abstract val metadataEntries: ListProperty<MetadataEntry>
+
+    /**
+     * Location of the generated JSON file
+     */
+    @get:OutputFile
+    abstract val destinationFile: RegularFileProperty
+
+    @TaskAction
+    fun generate() {
+        val jsonMapping = generateJsonMapping(metadataEntries)
+        val json = JSONArray(jsonMapping)
+
+        val outputFile = File(destinationFile.get().toString())
+        outputFile.writeText(json.toString(2))
+    }
+
+    /**
+     * Converts a list of [MetadataEntry] objects into a list of maps.
+     */
+    @OptIn(ExperimentalStdlibApi::class)
+    private fun generateJsonMapping(
+        metadata: ListProperty<MetadataEntry>
+    ): List<Map<String, String>> {
+        return buildList {
+            metadata.get().forEach { entry ->
+                val map = mapOf(
+                    "groupId" to entry.groupId,
+                    "artifactId" to entry.artifactId,
+                    "releaseNotesUrl" to entry.releaseNotesUrl,
+                    "sourceDir" to entry.sourceDir
+                )
+                add(map)
+            }
+        }
+    }
+}
+
+/**
+ * Helper data class to store the metadata information for each library/path.
+ */
+data class MetadataEntry(
+    val groupId: String,
+    val artifactId: String,
+    val releaseNotesUrl: String,
+    val sourceDir: String,
+) : Serializable
\ No newline at end of file
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
index b548a83..eab7764 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
@@ -18,6 +18,8 @@
 
 import androidx.build.SupportConfig
 import androidx.build.dackka.DackkaTask
+import androidx.build.dackka.GenerateMetadataTask
+import androidx.build.dackka.MetadataEntry
 import androidx.build.dependencies.KOTLIN_VERSION
 import androidx.build.doclava.DacOptions
 import androidx.build.doclava.DoclavaTask
@@ -52,8 +54,10 @@
 import org.gradle.api.file.ArchiveOperations
 import org.gradle.api.file.DuplicatesStrategy
 import org.gradle.api.file.FileCollection
+import org.gradle.api.file.RegularFile
 import org.gradle.api.model.ObjectFactory
 import org.gradle.api.plugins.JavaBasePlugin
+import org.gradle.api.provider.Provider
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.Internal
 import org.gradle.api.tasks.PathSensitive
@@ -80,7 +84,7 @@
     lateinit var samplesSourcesConfiguration: Configuration
     lateinit var dependencyClasspath: FileCollection
 
-    @get:javax.inject.Inject
+    @get:Inject
     abstract val archiveOperations: ArchiveOperations
 
     override fun apply(project: Project) {
@@ -353,10 +357,19 @@
             dependencies.add(project.dependencies.create(project.getLibraryByName("dackka")))
         }
 
+        val generateMetadataTask = project.tasks.register(
+            "generateMetadata",
+            GenerateMetadataTask::class.java
+        ) { task ->
+            task.metadataEntries.set(gatherMetadataEntries())
+            task.destinationFile.set(getMetadataRegularFile(project))
+        }
+
         val dackkaTask = project.tasks.register("dackkaDocs", DackkaTask::class.java) { task ->
             task.apply {
                 dependsOn(unzipDocsTask)
                 dependsOn(unzipSamplesTask)
+                dependsOn(generateMetadataTask)
 
                 description = "Generates reference documentation using a Google devsite Dokka" +
                     " plugin. Places docs in $generatedDocsDir"
@@ -372,10 +385,7 @@
                 excludedPackages = hiddenPackages.toSet()
                 excludedPackagesForJava = hiddenPackagesJava
                 excludedPackagesForKotlin = emptySet()
-
-                // TODO(b/239095864): replace this placeholder file with a dynamically generated one
-                libraryMetadataFile = project.rootProject.layout.projectDirectory
-                    .file("buildSrc/SampleLibraryMetadata.json")
+                libraryMetadataFile.set(getMetadataRegularFile(project))
 
                 // TODO(b/223712700): change to `true` once bug is resolved
                 showLibraryMetadata = false
@@ -663,6 +673,12 @@
     }
 }
 
+/**
+ * Location of the library metadata JSON file that's used by Dackka, represented as a [RegularFile]
+ */
+private fun getMetadataRegularFile(project: Project): Provider<RegularFile> =
+    project.layout.buildDirectory.file("SampleLibraryMetadata.json")
+
 private const val DOCLAVA_DEPENDENCY = "com.android:doclava:1.0.6"
 
 // List of packages to exclude from both Java and Kotlin refdoc generation
@@ -682,3 +698,42 @@
 private val hiddenPackagesJava = setOf(
     "androidx.*compose.*"
 )
+
+/**
+ * Converts AndroidX library and path data to a list of [MetadataEntry].
+ *
+ * TODO(b/239095864) replace static data with dynamically generated data from libraries
+ */
+private fun gatherMetadataEntries(): List<MetadataEntry> {
+    val pagingCompose = MetadataEntry(
+        groupId = "androidx.paging",
+        artifactId = "paging-compose",
+        releaseNotesUrl = "https://developer.android.com/jetpack/androidx/releases/paging",
+        sourceDir = "paging/compose"
+    )
+    val pagingRuntime = MetadataEntry(
+        groupId = "androidx.paging",
+        artifactId = "paging-runtime",
+        releaseNotesUrl = "https://developer.android.com/jetpack/androidx/releases/paging",
+        sourceDir = "paging"
+    )
+    val pagingRxJava2 = MetadataEntry(
+        groupId = "androidx.paging",
+        artifactId = "paging-rxjava2",
+        releaseNotesUrl = "https://developer.android.com/jetpack/androidx/releases/paging",
+        sourceDir = "paging/rxjava2"
+    )
+    val pagingRxJava3 = MetadataEntry(
+        groupId = "androidx.paging",
+        artifactId = "paging-rxjava3",
+        releaseNotesUrl = "https://developer.android.com/jetpack/androidx/releases/paging",
+        sourceDir = "paging/rxjava3"
+    )
+
+    return listOf(
+        pagingCompose,
+        pagingRuntime,
+        pagingRxJava2,
+        pagingRxJava3,
+    )
+}
\ No newline at end of file
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt b/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt
index fb2a49d..30d0223 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt
@@ -22,6 +22,12 @@
 import org.gradle.api.tasks.TaskProvider
 import org.gradle.jvm.tasks.Jar
 import java.io.File
+import org.gradle.api.Task
+import org.gradle.kotlin.dsl.findByType
+import org.gradle.kotlin.dsl.get
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
 
 /**
  * Allow java and Android libraries to bundle other projects inside the project jar/aar.
@@ -102,7 +108,15 @@
             it as Jar
             it.from(repackage.map { files(zipTree(it.archiveFile.get().asFile)) })
         }
-        configurations.getByName("apiElements") {
+        addArchivesToConfiguration("apiElements", jarTask)
+        addArchivesToConfiguration("runtimeElements", jarTask)
+    }
+
+    private fun Project.addArchivesToConfiguration(
+        configName: String,
+        jarTask: TaskProvider<Task>
+    ) {
+        configurations.getByName(configName) {
             it.outgoing.artifacts.clear()
             it.outgoing.artifact(
                 jarTask.flatMap { jarTask ->
@@ -111,15 +125,46 @@
                 }
             )
         }
-        configurations.getByName("runtimeElements") {
-            it.outgoing.artifacts.clear()
-            it.outgoing.artifact(
-                jarTask.flatMap { jarTask ->
-                    jarTask as Jar
-                    jarTask.archiveFile
-                }
-            )
+    }
+
+    /**
+     * KMP Version of [Project.forInsideJar]. See those docs for details.
+     *
+     * TODO(b/237104605): bundleInside is a global configuration.  Should figure out how to make it
+     * work properly with kmp and source sets so it can reside inside a sourceSet dependency.
+     */
+    @JvmStatic
+    fun Project.forInsideJarKmp(from: String, to: String) {
+        val kmpExtension = extensions.findByType<KotlinMultiplatformExtension>()
+            ?: error("kmp only")
+        val bundle = configurations.create("bundleInside")
+        val repackage = configureRepackageTaskForType(
+            listOf(Relocation(from, to)),
+            bundle
+        )
+
+        // To account for KMP structure we need to find the jvm specific target
+        // and add the repackaged archive files to only their compilations.
+        val jvmTarget = kmpExtension.targets.firstOrNull {
+            it.platformType == KotlinPlatformType.jvm
+        } as? KotlinJvmTarget ?: error("cannot find jvm target")
+        jvmTarget.compilations["main"].defaultSourceSet {
+            dependencies {
+                compileOnly(files(repackage.flatMap { it.archiveFile }))
+            }
         }
+        jvmTarget.compilations["test"].defaultSourceSet {
+            dependencies {
+                implementation(files(repackage.flatMap { it.archiveFile }))
+            }
+        }
+        val jarTask = tasks.named(jvmTarget.artifactsTaskName)
+        jarTask.configure {
+            it as Jar
+            it.from(repackage.map { files(zipTree(it.archiveFile.get().asFile)) })
+        }
+        addArchivesToConfiguration("jvmApiElements", jarTask)
+        addArchivesToConfiguration("jvmRuntimeElements", jarTask)
     }
 
     /**
diff --git a/buildSrc/shared.gradle b/buildSrc/shared.gradle
index 95df1b7..a253d3d 100644
--- a/buildSrc/shared.gradle
+++ b/buildSrc/shared.gradle
@@ -53,6 +53,8 @@
         exclude(group:"net.java.dev.msv", module:"xsdlib")
     }
     cacheableApi(libs.androidGradlePluginz)
+    // AGP has removes dependency on json-simple in http://b/239967984, so add explicitly
+    cacheableImplementation(libs.jsonSimple)
     cacheableImplementation(libs.dexMemberList)
     cacheableApi(libs.kotlinGradlePluginz)
     cacheableImplementation(gradleApi())
@@ -68,7 +70,6 @@
     cacheableApi(libs.javapoet)
     // room kotlintestapp uses the ksp plugin but it does not publish a plugin marker yet
     cacheableApi(libs.kspGradlePluginz)
-    cacheableApi(libs.japicmpPluginz)
     // dependencies whose resolutions we don't need to cache
     compileOnly(findGradleKotlinDsl()) // Only one file in this configuration, no need to cache it
     implementation(project(":jetpad-integration")) // Doesn't have a .pom, so not slow to load
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 6fea985..d788991 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -47,7 +47,7 @@
     testImplementation(project(":camera:camera-testing"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
     testImplementation(libs.junit) // Needed for Assert.assertThrows
-    testImplementation("org.apache.maven:maven-ant-tasks:2.1.3")
+    testImplementation("org.codehaus.plexus:plexus-utils:3.4.1")
 
     androidTestImplementation(libs.multidex)
     androidTestImplementation(libs.testExtJunit)
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
index 9d1274e..173093a 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
@@ -131,7 +131,7 @@
         mExecutor = executor;
         mScheduledExecutorService = scheduledExecutorService;
         mProcessorState = ProcessorState.UNINITIALIZED;
-        mSessionProcessorCaptureCallback = new SessionProcessorCaptureCallback(mExecutor);
+        mSessionProcessorCaptureCallback = new SessionProcessorCaptureCallback();
 
         mInstanceId = sNextInstanceId++;
         Logger.d(TAG, "New ProcessingCaptureSession (id=" + mInstanceId + ")");
@@ -574,15 +574,8 @@
 
     private static class SessionProcessorCaptureCallback
             implements SessionProcessor.CaptureCallback {
-        private List<CameraCaptureCallback> mCameraCaptureCallbacks = Collections.emptyList();
-        private final Executor mExecutor;
-        SessionProcessorCaptureCallback(@NonNull Executor executor) {
-            mExecutor = executor;
-        }
 
-        public void setCameraCaptureCallbacks(
-                @NonNull List<CameraCaptureCallback> cameraCaptureCallbacks) {
-            mCameraCaptureCallbacks = cameraCaptureCallbacks;
+        SessionProcessorCaptureCallback() {
         }
 
         @Override
@@ -595,22 +588,10 @@
 
         @Override
         public void onCaptureFailed(int captureSequenceId) {
-            mExecutor.execute(() -> {
-                for (CameraCaptureCallback cameraCaptureCallback : mCameraCaptureCallbacks) {
-                    cameraCaptureCallback.onCaptureFailed(new CameraCaptureFailure(
-                            CameraCaptureFailure.Reason.ERROR));
-                }
-            });
         }
 
         @Override
         public void onCaptureSequenceCompleted(int captureSequenceId) {
-            mExecutor.execute(() -> {
-                for (CameraCaptureCallback cameraCaptureCallback : mCameraCaptureCallbacks) {
-                    cameraCaptureCallback.onCaptureCompleted(
-                            CameraCaptureResult.EmptyCameraCaptureResult.create());
-                }
-            });
         }
 
         @Override
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
index 51582d9..bac238d 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
@@ -64,7 +64,7 @@
 import androidx.camera.testing.fakes.FakeCameraFactory;
 import androidx.test.core.app.ApplicationProvider;
 
-import org.apache.maven.artifact.ant.shaded.ReflectionUtils;
+import org.codehaus.plexus.util.ReflectionUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
index a9a073f..3ed7e19 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
@@ -49,7 +49,7 @@
 import androidx.camera.testing.fakes.FakeUseCaseConfig;
 import androidx.test.core.app.ApplicationProvider;
 
-import org.apache.maven.artifact.ant.shaded.ReflectionUtils;
+import org.codehaus.plexus.util.ReflectionUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
index cbeb084..fd72902 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
@@ -88,7 +88,7 @@
 import androidx.camera.video.VideoSpec;
 import androidx.test.core.app.ApplicationProvider;
 
-import org.apache.maven.artifact.ant.shaded.ReflectionUtils;
+import org.codehaus.plexus.util.ReflectionUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
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 21dc7a8..ebd3c66 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
@@ -271,13 +271,13 @@
     static final ExifRotationAvailability EXIF_ROTATION_AVAILABILITY =
             new ExifRotationAvailability();
 
-    private final ImageReaderProxy.OnImageAvailableListener mClosingListener = (imageReader -> {
+    private final ImageReaderProxy.OnImageAvailableListener mClosingListener = imageReader -> {
         try (ImageProxy image = imageReader.acquireLatestImage()) {
             Log.d(TAG, "Discarding ImageProxy which was inadvertently acquired: " + image);
         } catch (IllegalStateException e) {
             Log.e(TAG, "Failed to acquire latest image.", e);
         }
-    });
+    };
 
     @NonNull
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/Exif.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/Exif.java
index 315b3fc..d18b15d 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/Exif.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/Exif.java
@@ -688,11 +688,6 @@
         static Converter fromKilometersPerHour(double kph) {
             return new Converter(kph * 0.621371);
         }
-
-        static Converter fromMetersPerSecond(double mps) {
-            return new Converter(mps * 2.23694);
-        }
-
         static Converter fromMilesPerHour(double mph) {
             return new Converter(mph);
         }
@@ -708,18 +703,6 @@
                 mMph = mph;
             }
 
-            double toKilometersPerHour() {
-                return mMph / 0.621371;
-            }
-
-            double toMilesPerHour() {
-                return mMph;
-            }
-
-            double toKnots() {
-                return mMph / 1.15078;
-            }
-
             double toMetersPerSecond() {
                 return mMph / 2.23694;
             }
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
index 779c524..8fc8a6b 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
@@ -506,6 +506,7 @@
          * Monitors which {@link LifecycleOwner} receives an ON_START event and then stop
          * other {@link LifecycleCamera} to keep only one active at a time.
          */
+        @SuppressWarnings("unused") // Called via OnLifecycleEvent
         @OnLifecycleEvent(Lifecycle.Event.ON_START)
         public void onStart(LifecycleOwner lifecycleOwner) {
             mLifecycleCameraRepository.setActive(lifecycleOwner);
@@ -514,6 +515,7 @@
         /**
          * Monitors which {@link LifecycleOwner} receives an ON_STOP event.
          */
+        @SuppressWarnings("unused") // Called via OnLifecycleEvent
         @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
         public void onStop(LifecycleOwner lifecycleOwner) {
             mLifecycleCameraRepository.setInactive(lifecycleOwner);
@@ -524,6 +526,7 @@
          * removes any {@link LifecycleCamera} associated with it from this
          * repository when that lifecycle is destroyed.
          */
+        @SuppressWarnings("unused") // Called via OnLifecycleEvent
         @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
         public void onDestroy(LifecycleOwner lifecycleOwner) {
             mLifecycleCameraRepository.unregisterLifecycle(lifecycleOwner);
diff --git a/camera/camera-viewfinder/lint-baseline.xml b/camera/camera-viewfinder/lint-baseline.xml
deleted file mode 100644
index a6c7750..0000000
--- a/camera/camera-viewfinder/lint-baseline.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.4.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (7.4.0-alpha05)" variant="all" version="7.4.0-alpha05">
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    ListenableFuture&lt;O> apply(@Nullable I input) throws Exception;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/camera/viewfinder/internal/utils/futures/AsyncFunction.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    void onFailure(Throwable t);"
-        errorLine2="                   ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/camera/viewfinder/internal/utils/futures/FutureCallback.java"/>
-    </issue>
-
-</issues>
diff --git a/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/internal/utils/futures/AsyncFunction.java b/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/internal/utils/futures/AsyncFunction.java
index 0fc6283..182384d 100644
--- a/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/internal/utils/futures/AsyncFunction.java
+++ b/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/internal/utils/futures/AsyncFunction.java
@@ -16,6 +16,7 @@
 
 package androidx.camera.viewfinder.internal.utils.futures;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
@@ -48,5 +49,6 @@
      * <p>Throwing an exception from this method is equivalent to returning a failing {@code
      * Future}.
      */
+    @NonNull
     ListenableFuture<O> apply(@Nullable I input) throws Exception;
 }
diff --git a/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/internal/utils/futures/FutureCallback.java b/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/internal/utils/futures/FutureCallback.java
index 58474e3..acedaa2 100644
--- a/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/internal/utils/futures/FutureCallback.java
+++ b/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/internal/utils/futures/FutureCallback.java
@@ -16,6 +16,7 @@
 
 package androidx.camera.viewfinder.internal.utils.futures;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
 import com.google.common.util.concurrent.ListenableFuture;
@@ -48,5 +49,5 @@
      * <p>If the future's {@link Future#get() get} method throws an {@link ExecutionException}, then
      * the cause is passed to this method. Any other thrown object is passed unaltered.
      */
-    void onFailure(Throwable t);
+    void onFailure(@NonNull Throwable t);
 }
diff --git a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt
index 2762f2d..e0997e1 100644
--- a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt
+++ b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt
@@ -41,7 +41,7 @@
 private const val ACTIVE_LENGTH_SEC: Double = 0.5
 private const val ACTIVE_INTERVAL_SEC: Double = 1.0
 private const val ACTIVE_DELAY_SEC: Double = 0.0
-private const val VOLUME_PERCENTAGE: Double = 0.6
+private const val VOLUME_PERCENTAGE: Double = 1.0
 private const val TAG = "SignalGeneratorViewModel"
 
 enum class ActivationSignal {
diff --git a/car/app/OWNERS b/car/app/OWNERS
index cc30871..7450fe1 100644
--- a/car/app/OWNERS
+++ b/car/app/OWNERS
@@ -1,13 +1,16 @@
 # Bug component: 460472
 davedandeneau@google.com #{LAST_RESORT_SUGGESTION}
-calcoholado@google.com #{LAST_RESORT_SUGGESTION}
+calcoholado@google.com
 
-# API ownership
+# API owners
 per-file app/api/*=file:/car/app/API_OWNERS
 per-file app-automotive/api/*=file:/car/app/API_OWNERS
 per-file app-projected/api/*=file:/car/app/API_OWNERS
 per-file app-testing/api/*=file:/car/app/API_OWNERS
 
 # Feature owners
-per-file app/*=kodlee@google.com
-per-file app-automotive/*=kodlee@google.com
+per-file app/**=kodlee@google.com
+per-file app-automotive/**=kodlee@google.com
+per-file app-projected/**=kodlee@google.com
+per-file app-testing/**=kodlee@google.com
+per-file app-samples/**=jech@google.com
diff --git a/car/app/app-automotive/src/main/aidl/androidx/car/app/activity/renderer/IInsetsListener.aidl b/car/app/app-automotive/src/main/aidl/androidx/car/app/activity/renderer/IInsetsListener.aidl
index 23a3896..6030bce 100644
--- a/car/app/app-automotive/src/main/aidl/androidx/car/app/activity/renderer/IInsetsListener.aidl
+++ b/car/app/app-automotive/src/main/aidl/androidx/car/app/activity/renderer/IInsetsListener.aidl
@@ -26,7 +26,15 @@
 interface IInsetsListener {
   /**
    * Notifies that the {@link Insets} have changed.
+   *
+   * @deprecated Use onWindowInsetsChanged(Insets, Insets) instead.
    */
   void onInsetsChanged(in Insets insets) = 1;
+
+  /**
+   * Notifies that the {@link Insets} of the window have changed.
+   */
+  void onWindowInsetsChanged(in Insets insets, in Insets safeInsets) = 2;
+
 }
 
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
index 13b2553..a0d711a 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
@@ -28,6 +28,7 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -35,10 +36,13 @@
 import android.view.PixelCopy;
 import android.view.View;
 import android.view.WindowInsets;
+import android.view.WindowManager;
 import android.widget.ImageView;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 import androidx.car.app.SessionInfo;
 import androidx.car.app.activity.renderer.ICarAppActivity;
@@ -56,6 +60,7 @@
 import androidx.car.app.serialization.Bundleable;
 import androidx.car.app.serialization.BundlerException;
 import androidx.car.app.utils.ThreadUtils;
+import androidx.core.view.DisplayCutoutCompat;
 import androidx.core.view.WindowCompat;
 import androidx.core.view.WindowInsetsCompat;
 import androidx.fragment.app.FragmentActivity;
@@ -129,8 +134,10 @@
                             .getInsets(WindowInsetsCompat.Type.systemBars()
                                     | WindowInsetsCompat.Type.ime())
                             .toPlatformInsets();
-
-                    requireNonNull(mViewModel).updateWindowInsets(insets);
+                    DisplayCutoutCompat displayCutout =
+                            WindowInsetsCompat.toWindowInsetsCompat(windowInsets)
+                                    .getDisplayCutout();
+                    requireNonNull(mViewModel).updateWindowInsets(insets, displayCutout);
 
                     // Insets are handled by the host. Only local content need padding.
                     mActivityContainerView.setPadding(0, 0, 0, 0);
@@ -228,6 +235,18 @@
                 }
             };
 
+    @RequiresApi(Build.VERSION_CODES.R)
+    private static class Api30Impl {
+        private Api30Impl() {
+        }
+
+        @DoNotInline
+        static WindowInsets getDecorViewInsets(WindowInsets insets) {
+            return new WindowInsets.Builder(insets).setInsets(
+                    WindowInsets.Type.displayCutout(), Insets.NONE).build();
+        }
+    }
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -241,6 +260,19 @@
         mSurfaceSnapshotView = requireViewById(R.id.template_view_snapshot);
 
         mActivityContainerView.setOnApplyWindowInsetsListener(mWindowInsetsListener);
+
+        WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
+        layoutParams.layoutInDisplayCutoutMode =
+                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+        // Remove display cut-out insets on DecorView
+        getWindow().getDecorView().setOnApplyWindowInsetsListener((view, insets) -> {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+                insets = Api30Impl.getDecorViewInsets(insets);
+            }
+            return view.onApplyWindowInsets(insets);
+        });
+
         // IMPORTANT: The SystemUiVisibility applied here must match the insets provided to the
         // host in OnApplyWindowInsetsListener above. Failing to do so would cause a mismatch
         // between the insets applied to the content on the hosts side vs. the actual visible
@@ -422,4 +454,4 @@
         String serviceName = infos.get(0).serviceInfo.name;
         return new ComponentName(this, serviceName);
     }
-}
+}
\ No newline at end of file
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
index a1737df..999fe1a 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
@@ -35,6 +35,8 @@
 import androidx.car.app.activity.renderer.IInsetsListener;
 import androidx.car.app.activity.renderer.IRendererCallback;
 import androidx.car.app.utils.ThreadUtils;
+import androidx.car.app.versioning.CarAppApiLevels;
+import androidx.core.view.DisplayCutoutCompat;
 import androidx.lifecycle.AndroidViewModel;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
@@ -57,12 +59,12 @@
     private final MutableLiveData<ErrorHandler.ErrorType> mError = new MutableLiveData<>();
     private final MutableLiveData<State> mState = new MutableLiveData<>(State.IDLE);
     private ServiceConnectionManager mServiceConnectionManager;
-    @Nullable
-    private IRendererCallback mIRendererCallback;
-    @Nullable
-    private IInsetsListener mIInsetsListener;
-    @Nullable
-    private Insets mInsets = Insets.NONE;
+
+    @Nullable private IRendererCallback mIRendererCallback;
+    @Nullable private IInsetsListener mIInsetsListener;
+    @Nullable private Insets mInsets = Insets.NONE;
+    @Nullable private DisplayCutoutCompat mDisplayCutout;
+
     private static WeakReference<Activity> sActivity = new WeakReference<>(null);
 
     /** Possible view states */
@@ -242,31 +244,57 @@
      * Updates the insets for this {@link CarAppActivity}
      *
      * @param insets latest received {@link Insets}
+     * @param displayCutout latest received {@link DisplayCutoutCompat}
      */
-    public void updateWindowInsets(@NonNull Insets insets) {
-        if (!Objects.equals(mInsets, insets)) {
-            mInsets = insets;
-            // If listener is not set yet, the inset will be dispatched once the listener is set.
-            if (mIInsetsListener != null) {
-                dispatchInsetsUpdates();
-            }
+    public void updateWindowInsets(@NonNull Insets insets,
+            @Nullable DisplayCutoutCompat displayCutout) {
+        if (Objects.equals(mInsets, insets) && Objects.equals(mDisplayCutout, displayCutout)) {
+            return;
+        }
+        mInsets = insets;
+        mDisplayCutout = displayCutout;
+        // If listener is not set yet, the inset will be dispatched once the listener is set.
+        if (mIInsetsListener != null) {
+            dispatchInsetsUpdates();
         }
     }
 
-    @SuppressWarnings("NullAway")
+    /**
+     * Dispatches the insets updates for this {@link CarAppActivity}
+     */
+    @SuppressWarnings({"NullAway", "deprecation"})
     private void dispatchInsetsUpdates() {
-        getServiceDispatcher().dispatch("onInsetsChanged",
-                () -> requireNonNull(mIInsetsListener).onInsetsChanged(mInsets)
-        );
+        if (mServiceConnectionManager.getHandshakeInfo().getHostCarAppApiLevel()
+                >= CarAppApiLevels.LEVEL_5) {
+            getServiceDispatcher().dispatch("onWindowInsetsChanged",
+                    () -> requireNonNull(mIInsetsListener).onWindowInsetsChanged(mInsets,
+                            getSafeInsets(mDisplayCutout))
+            );
+            return;
+        }
+        getServiceDispatcher().dispatch("onInsetsChanges",
+                () -> requireNonNull(mIInsetsListener).onInsetsChanged(mInsets));
     }
 
     /**
      * Updates the listener that will handle insets changes. If a non-null listener is set, it will
      * be assumed that inset changes are handled by the host, and
-     * {@link #updateWindowInsets(Insets)} will return <code>false</code>
+     * {@link #updateWindowInsets(Insets, DisplayCutoutCompat)} will return <code>false</code>
      */
     public void setInsetsListener(@Nullable IInsetsListener listener) {
         mIInsetsListener = listener;
         dispatchInsetsUpdates();
     }
+
+    /**
+     * Returns the safe insets of a given {@link DisplayCutoutCompat}.
+     *
+     * @param displayCutout the {@link DisplayCutoutCompat} for which the safe insets is calculated.
+     */
+    private Insets getSafeInsets(@Nullable DisplayCutoutCompat displayCutout) {
+        return displayCutout == null ? Insets.NONE :
+                Insets.of(displayCutout.getSafeInsetLeft(), displayCutout.getSafeInsetTop(),
+                        displayCutout.getSafeInsetRight(),
+                        displayCutout.getSafeInsetBottom());
+    }
 }
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
index 5367451..5d97b81 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
@@ -307,7 +307,8 @@
             activityContainer.onApplyWindowInsets(windowInsets);
 
             // Verify that the host is notified and insets are not handled locally
-            verify(insetsListener).onInsetsChanged(eq(systemWindowInsets.toPlatformInsets()));
+            verify(insetsListener).onWindowInsetsChanged(eq(systemWindowInsets.toPlatformInsets()),
+                    eq(Insets.NONE.toPlatformInsets()));
             assertThat(activityContainer.getPaddingBottom()).isEqualTo(0);
             assertThat(activityContainer.getPaddingTop()).isEqualTo(0);
             assertThat(activityContainer.getPaddingLeft()).isEqualTo(0);
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java
index 353977c..6fb9203 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java
@@ -20,15 +20,21 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
 
 import android.app.Application;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.os.RemoteException;
 
+import androidx.car.app.HandshakeInfo;
 import androidx.car.app.activity.renderer.ICarAppActivity;
+import androidx.car.app.activity.renderer.IInsetsListener;
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
@@ -56,6 +62,8 @@
     private ShadowLooper mMainLooper;
     private final ServiceConnectionManager mServiceConnectionManager =
             mock(ServiceConnectionManager.class);
+    private final IInsetsListener mIInsetsListener = mock(IInsetsListener.class);
+    private final ServiceDispatcher mServiceDispatcher = mock(ServiceDispatcher.class);
 
     @Before
     public void setUp() {
@@ -139,4 +147,30 @@
                 .isEqualTo(CarAppViewModel.State.IDLE);
         assertThat(mCarAppViewModel.getError().getValue()).isNull();
     }
+
+    @Test
+    public void dispatchInsetsUpdates_whenApiLevel5Above_shouldCallWindowsInsetsChanged() throws
+            RemoteException {
+        mCarAppViewModel.setServiceConnectionManager(mServiceConnectionManager);
+        when(mServiceConnectionManager.getHandshakeInfo()).thenReturn(createHandshakeInfo("TestApp",
+                5));
+        when(mServiceConnectionManager.getServiceDispatcher()).thenReturn(mServiceDispatcher);
+        mCarAppViewModel.setInsetsListener(mIInsetsListener);
+        verify(mServiceDispatcher).dispatch(eq("onWindowInsetsChanged"), any());
+    }
+
+    @Test
+    public void dispatchInsetsUpdates_whenApiLevelBelow5_shouldCallInsetsChanges() throws
+            RemoteException {
+        mCarAppViewModel.setServiceConnectionManager(mServiceConnectionManager);
+        when(mServiceConnectionManager.getHandshakeInfo()).thenReturn(
+                createHandshakeInfo("TestApp", 4));
+        when(mServiceConnectionManager.getServiceDispatcher()).thenReturn(mServiceDispatcher);
+        mCarAppViewModel.setInsetsListener(mIInsetsListener);
+        verify(mServiceDispatcher).dispatch(eq("onInsetsChanges"), any());
+    }
+
+    private static HandshakeInfo createHandshakeInfo(String packageName, int hostApiLevel) {
+        return new HandshakeInfo(packageName, hostApiLevel);
+    }
 }
diff --git a/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/car/NavigationScreen.java b/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/car/NavigationScreen.java
index 962d81d..3d72083 100644
--- a/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/car/NavigationScreen.java
+++ b/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/car/NavigationScreen.java
@@ -141,6 +141,19 @@
 
         // Set the action strip.
         ActionStrip.Builder actionStripBuilder = new ActionStrip.Builder();
+        if (mIsNavigating) {
+            actionStripBuilder.addAction(
+                    new Action.Builder()
+                            .setIcon(
+                                    new CarIcon.Builder(
+                                            IconCompat.createWithResource(
+                                                    getCarContext(),
+                                                    R.drawable.ic_add_stop))
+                                            .build())
+                            .setOnClickListener(this::openFavorites)
+                            .build());
+        }
+
         actionStripBuilder.addAction(mSettingsAction);
         actionStripBuilder.addAction(
                 new Action.Builder()
@@ -298,15 +311,16 @@
                 .pushForResult(
                         new FavoritesScreen(getCarContext(), mSettingsAction, mSurfaceRenderer),
                         (obj) -> {
-                            if (obj != null) {
-                                // Need to copy over each element to satisfy Java type safety.
-                                List<?> results = (List<?>) obj;
-                                List<Instruction> instructions = new ArrayList<Instruction>();
-                                for (Object result : results) {
-                                    instructions.add((Instruction) result);
-                                }
-                                mListener.executeScript(instructions);
+                            if (obj == null || mIsNavigating) {
+                                return;
                             }
+                            // Need to copy over each element to satisfy Java type safety.
+                            List<?> results = (List<?>) obj;
+                            List<Instruction> instructions = new ArrayList<Instruction>();
+                            for (Object result : results) {
+                                instructions.add((Instruction) result);
+                            }
+                            mListener.executeScript(instructions);
                         });
     }
 
diff --git a/car/app/app-samples/navigation/common/src/main/res/drawable/ic_add_stop.xml b/car/app/app-samples/navigation/common/src/main/res/drawable/ic_add_stop.xml
new file mode 100644
index 0000000..3db7a4e
--- /dev/null
+++ b/car/app/app-samples/navigation/common/src/main/res/drawable/ic_add_stop.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 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.
+  -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M11,12H13V10H15V8H13V6H11V8H9V10H11ZM12,22Q11.65,22 11.4,21.8Q11.15,21.6 11.025,21.275Q10.55,19.875 9.838,18.65Q9.125,17.425 7.85,15.775Q6.575,14.125 5.787,12.625Q5,11.125 5,9Q5,6.075 7.038,4.037Q9.075,2 12,2Q14.925,2 16.962,4.037Q19,6.075 19,9Q19,11.275 18.138,12.787Q17.275,14.3 16.15,15.775Q14.8,17.575 14.113,18.762Q13.425,19.95 12.975,21.275Q12.85,21.625 12.588,21.812Q12.325,22 12,22ZM12,18.425Q12.425,17.575 12.963,16.75Q13.5,15.925 14.55,14.55Q15.625,13.15 16.312,11.962Q17,10.775 17,9Q17,6.925 15.538,5.463Q14.075,4 12,4Q9.925,4 8.463,5.463Q7,6.925 7,9Q7,10.775 7.688,11.962Q8.375,13.15 9.45,14.55Q10.5,15.925 11.038,16.75Q11.575,17.575 12,18.425ZM12,9Q12,9 12,9Q12,9 12,9Q12,9 12,9Q12,9 12,9Q12,9 12,9Q12,9 12,9Q12,9 12,9Q12,9 12,9Q12,9 12,9Q12,9 12,9Q12,9 12,9Q12,9 12,9Z"/>
+</vector>
\ No newline at end of file
diff --git a/car/app/app/src/main/java/androidx/car/app/SessionInfoIntentEncoder.java b/car/app/app/src/main/java/androidx/car/app/SessionInfoIntentEncoder.java
index ed4db93..23850f4 100644
--- a/car/app/app/src/main/java/androidx/car/app/SessionInfoIntentEncoder.java
+++ b/car/app/app/src/main/java/androidx/car/app/SessionInfoIntentEncoder.java
@@ -22,7 +22,6 @@
 
 import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.car.app.serialization.Bundleable;
 import androidx.car.app.serialization.BundlerException;
@@ -102,13 +101,6 @@
         private Api29() {
         }
 
-        /** Wrapper for {@link Intent#getIdentifier()}. */
-        @DoNotInline
-        @Nullable
-        static String getIdentifier(@NonNull Intent intent) {
-            return intent.getIdentifier();
-        }
-
         /** Wrapper for {@link Intent#setIdentifier(String)}. */
         @DoNotInline
         static void setIdentifier(@NonNull Intent intent, @NonNull String identifier) {
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
index e4a620d..2835ce3 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
@@ -34,7 +34,23 @@
  * Represents information about a trip including destinations, steps, and travel estimates.
  *
  * <p>This information data <b>may</b> be displayed in different places in the car such as the
- * instrument cluster screens and heads-up display.
+ * instrument cluster screens, heads-up display and floating navigation bar.
+ *
+ * <p>Floating navigation bar can show current navigating info in three templates including
+ * {@link MapTemplate}, {@link RoutePreviewNavigationTemplate} and
+ * {@link PlaceListNavigationTemplate}. The navigating steps can be added with the use of
+ * {@link Builder#addStep}. There are three navigation info showing in the floating nav bar
+ * including:
+ * <ul>
+ *     <li>The current road description get from {@link Step#getCue} of the current {@link Step},
+ *     which gets from the first element of {@link #getSteps()}</li>
+ *     <li>The remaining distance of the current road get from
+ *     {@link TravelEstimate#getRemainingDistance} of the first element of
+ *     {@link #getStepTravelEstimates()} </li>
+ *     <li>The turn icon get from {@link Maneuver#getIcon()} from the {@link Maneuver} of the
+ *     current {@link Step}
+ *     </li>
+ * </ul>
  */
 @CarProtocol
 public final class Trip {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
index ca257d7..bacd5b2 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
@@ -572,6 +572,85 @@
         """
     )
 
+    @Test // Regression test for b/180168881
+    fun testFunctionReferenceWithinInferredComposableLambda(): Unit = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+
+            fun Problem() {
+                fun foo() { }
+                val lambda: @Composable ()->Unit = {
+                    ::foo
+                }
+            }
+        """,
+        """
+            fun Problem() {
+              fun foo() { }
+              val lambda = composableLambdaInstance(<>, false) { %composer: Composer?, %changed: Int ->
+                sourceInformation(%composer, "C:Test.kt")
+                if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
+                  if (isTraceInProgress()) {
+                    traceEventStart(<>, %changed, -1, <>)
+                  }
+                  foo
+                  if (isTraceInProgress()) {
+                    traceEventEnd()
+                  }
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
+        """
+    )
+
+    @Test
+    fun testFunctionReferenceNonComposableMemoization(): Unit = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+            @Composable fun Example(x: Int) {
+                fun foo() { use(x) }
+                val shouldMemoize: ()->(()->Unit) = { ::foo }
+            }
+        """,
+        """
+            @Composable
+            fun Example(x: Int, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(Example)<{>:Test.kt")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                if (isTraceInProgress()) {
+                  traceEventStart(<>, %dirty, -1, <>)
+                }
+                fun foo() {
+                  use(x)
+                }
+                val shouldMemoize = remember(x, {
+                  {
+                    foo
+                  }
+                }, %composer, 0b1110 and %dirty)
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                Example(x, %composer, %changed or 0b0001)
+              }
+            }
+        """,
+        """
+            fun use(x: Any) = println(x)
+        """.trimIndent()
+    )
+
     @Test // regression of b/162575428
     fun testComposableInAFunctionParameter(): Unit = verifyComposeIrTransform(
         """
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt
index 38c974d..6c1f46b 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt
@@ -74,4 +74,20 @@
         """
         )
     }
+
+    // Regression test for b/180168881
+    fun testFunctionReferenceWithinInferredComposableLambda() = ensureSetup {
+        testCompile(
+            """
+                import androidx.compose.runtime.Composable
+
+                fun Problem() {
+                    fun foo() { }
+                    val lambda: @Composable ()->Unit = {
+                        ::foo
+                    }
+                }
+        """
+        )
+    }
 }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
index c3410b9..1dfb82a 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
@@ -451,6 +451,9 @@
 
     override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
         // Memoize the instance created by using the :: operator
+        if (expression.symbol.owner.isLocal) {
+            declarationContextStack.recordLocalCapture(expression.symbol.owner)
+        }
         val result = super.visitFunctionReference(expression)
         val functionContext = currentFunctionContext ?: return result
         if (expression.valueArgumentsCount != 0) {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/SystemGestureExclusionTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/SystemGestureExclusionTest.kt
index 8edfcfa..17e0b89 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/SystemGestureExclusionTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/SystemGestureExclusionTest.kt
@@ -56,7 +56,7 @@
      * Make sure that when an exclusion rect using the bounds of a layout is used, the
      * gesture should not be consumed by the system.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     @Test
     fun excludeBounds() {
         val composeView = setComposeContent {
@@ -75,7 +75,7 @@
      * Make sure that when an exclusion rect using a supplied rect, the
      * gesture should not be consumed by the system.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     @Test
     fun excludeRect() {
         val composeView = setComposeContent {
@@ -92,7 +92,7 @@
         }
     }
 
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     @Test
     fun removeWhenModifierRemovedBounds() {
         var setExclusion by mutableStateOf(true)
@@ -113,7 +113,7 @@
         }
     }
 
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     @Test
     fun removeWhenModifierRemovedRect() {
         var setExclusion by mutableStateOf(true)
@@ -136,7 +136,7 @@
         }
     }
 
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     @Test
     fun excludeMultipleBounds() {
         val composeView = setComposeContent {
@@ -170,7 +170,7 @@
         }
     }
 
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     @Test
     fun removeOneOfMultiple() {
         var setExclusion by mutableStateOf(true)
@@ -191,7 +191,7 @@
         }
     }
 
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     @Test
     fun replaceWithEmptyRect() {
         var useEmpty by mutableStateOf(false)
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt
index 36e42e22..df702a8 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt
@@ -40,13 +40,13 @@
  * @see View.setSystemGestureExclusionRects
  */
 fun Modifier.systemGestureExclusion() =
-    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
         this
     } else {
         composed(inspectorInfo = debugInspectorInfo {
             name = "systemGestureExclusion"
         }) {
-            excludeFromSystemGestureR(null)
+            excludeFromSystemGestureQ(null)
         }
     }
 
@@ -61,21 +61,21 @@
  * @see View.setSystemGestureExclusionRects
  */
 fun Modifier.systemGestureExclusion(exclusion: (LayoutCoordinates) -> Rect) =
-    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
         this
     } else {
         composed(inspectorInfo = debugInspectorInfo {
             name = "systemGestureExclusion"
             properties["exclusion"] = exclusion
         }) {
-            excludeFromSystemGestureR(exclusion)
+            excludeFromSystemGestureQ(exclusion)
         }
     }
 
 @Suppress("NOTHING_TO_INLINE", "ComposableModifierFactory", "ModifierFactoryExtensionFunction")
-@RequiresApi(Build.VERSION_CODES.R)
+@RequiresApi(Build.VERSION_CODES.Q)
 @Composable
-private inline fun excludeFromSystemGestureR(
+private inline fun excludeFromSystemGestureQ(
     noinline exclusion: ((LayoutCoordinates) -> Rect)?
 ): Modifier {
     val view = LocalView.current
@@ -88,7 +88,7 @@
     return modifier
 }
 
-@RequiresApi(Build.VERSION_CODES.R)
+@RequiresApi(Build.VERSION_CODES.Q)
 private class ExcludeFromSystemGestureModifier(
     val view: View,
     val exclusion: ((LayoutCoordinates) -> Rect)?
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 88e4217..c8b9406 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -364,7 +364,6 @@
   }
 
   public final class MenuDefaults {
-    method @androidx.compose.runtime.Composable public void Divider(optional androidx.compose.ui.Modifier modifier, optional long color, optional float thickness);
     method public androidx.compose.foundation.layout.PaddingValues getDropdownMenuItemContentPadding();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.MenuItemColors itemColors(optional long textColor, optional long leadingIconColor, optional long trailingIconColor, optional long disabledTextColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
     property public final androidx.compose.foundation.layout.PaddingValues DropdownMenuItemContentPadding;
@@ -629,7 +628,6 @@
   }
 
   public final class TabRowDefaults {
-    method @androidx.compose.runtime.Composable public void Divider(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
     method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method @androidx.compose.runtime.Composable public long getContentColor();
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index e9d5bf1..9717b74 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -311,11 +311,13 @@
   @androidx.compose.material3.ExperimentalMaterial3Api public final class DrawerDefaults {
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method public float getDismissibleDrawerElevation();
+    method public float getMaximumDrawerWidth();
     method public float getModalDrawerElevation();
     method public float getPermanentDrawerElevation();
     method @androidx.compose.runtime.Composable public long getScrimColor();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
     property public final float DismissibleDrawerElevation;
+    property public final float MaximumDrawerWidth;
     property public final float ModalDrawerElevation;
     property public final float PermanentDrawerElevation;
     property @androidx.compose.runtime.Composable public final long containerColor;
@@ -544,7 +546,6 @@
   }
 
   public final class MenuDefaults {
-    method @androidx.compose.runtime.Composable public void Divider(optional androidx.compose.ui.Modifier modifier, optional long color, optional float thickness);
     method public androidx.compose.foundation.layout.PaddingValues getDropdownMenuItemContentPadding();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.MenuItemColors itemColors(optional long textColor, optional long leadingIconColor, optional long trailingIconColor, optional long disabledTextColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
     property public final androidx.compose.foundation.layout.PaddingValues DropdownMenuItemContentPadding;
@@ -600,11 +601,14 @@
   }
 
   public final class NavigationDrawerKt {
-    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void DismissibleNavigationDrawer(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalNavigationDrawer(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void NavigationDrawer(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void DismissibleDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void DismissibleNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void NavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void NavigationDrawerItem(kotlin.jvm.functions.Function0<kotlin.Unit> label, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.NavigationDrawerItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
-    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void PermanentNavigationDrawer(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void PermanentDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void PermanentNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.DrawerState rememberDrawerState(androidx.compose.material3.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
   }
 
@@ -868,7 +872,6 @@
   }
 
   public final class TabRowDefaults {
-    method @androidx.compose.runtime.Composable public void Divider(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
     method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method @androidx.compose.runtime.Composable public long getContentColor();
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 88e4217..c8b9406 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -364,7 +364,6 @@
   }
 
   public final class MenuDefaults {
-    method @androidx.compose.runtime.Composable public void Divider(optional androidx.compose.ui.Modifier modifier, optional long color, optional float thickness);
     method public androidx.compose.foundation.layout.PaddingValues getDropdownMenuItemContentPadding();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.MenuItemColors itemColors(optional long textColor, optional long leadingIconColor, optional long trailingIconColor, optional long disabledTextColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
     property public final androidx.compose.foundation.layout.PaddingValues DropdownMenuItemContentPadding;
@@ -629,7 +628,6 @@
   }
 
   public final class TabRowDefaults {
-    method @androidx.compose.runtime.Composable public void Divider(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
     method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method @androidx.compose.runtime.Composable public long getContentColor();
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/DrawerSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/DrawerSamples.kt
index c772307..0384ae3 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/DrawerSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/DrawerSamples.kt
@@ -23,18 +23,22 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Email
 import androidx.compose.material.icons.filled.Face
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.material3.Button
+import androidx.compose.material3.DismissibleDrawerSheet
 import androidx.compose.material3.DismissibleNavigationDrawer
 import androidx.compose.material3.DrawerValue
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
+import androidx.compose.material3.ModalDrawerSheet
 import androidx.compose.material3.ModalNavigationDrawer
 import androidx.compose.material3.NavigationDrawerItem
 import androidx.compose.material3.NavigationDrawerItemDefaults
+import androidx.compose.material3.PermanentDrawerSheet
 import androidx.compose.material3.PermanentNavigationDrawer
 import androidx.compose.material3.Text
 import androidx.compose.material3.rememberDrawerState
@@ -59,17 +63,20 @@
     ModalNavigationDrawer(
         drawerState = drawerState,
         drawerContent = {
-            items.forEach { item ->
-                NavigationDrawerItem(
-                    icon = { Icon(item, contentDescription = null) },
-                    label = { Text(item.name) },
-                    selected = item == selectedItem.value,
-                    onClick = {
-                        scope.launch { drawerState.close() }
-                        selectedItem.value = item
-                    },
-                    modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
-                )
+            ModalDrawerSheet {
+                Spacer(Modifier.height(12.dp))
+                items.forEach { item ->
+                    NavigationDrawerItem(
+                        icon = { Icon(item, contentDescription = null) },
+                        label = { Text(item.name) },
+                        selected = item == selectedItem.value,
+                        onClick = {
+                            scope.launch { drawerState.close() }
+                            selectedItem.value = item
+                        },
+                        modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
+                    )
+                }
             }
         },
         content = {
@@ -98,16 +105,19 @@
     val selectedItem = remember { mutableStateOf(items[0]) }
     PermanentNavigationDrawer(
         drawerContent = {
-            items.forEach { item ->
-                NavigationDrawerItem(
-                    icon = { Icon(item, contentDescription = null) },
-                    label = { Text(item.name) },
-                    selected = item == selectedItem.value,
-                    onClick = {
-                        selectedItem.value = item
-                    },
-                    modifier = Modifier.padding(horizontal = 12.dp)
-                )
+            PermanentDrawerSheet(Modifier.width(240.dp)) {
+                Spacer(Modifier.height(12.dp))
+                items.forEach { item ->
+                    NavigationDrawerItem(
+                        icon = { Icon(item, contentDescription = null) },
+                        label = { Text(item.name) },
+                        selected = item == selectedItem.value,
+                        onClick = {
+                            selectedItem.value = item
+                        },
+                        modifier = Modifier.padding(horizontal = 12.dp)
+                    )
+                }
             }
         },
         content = {
@@ -141,17 +151,20 @@
     DismissibleNavigationDrawer(
         drawerState = drawerState,
         drawerContent = {
-            items.forEach { item ->
-                NavigationDrawerItem(
-                    icon = { Icon(item, contentDescription = null) },
-                    label = { Text(item.name) },
-                    selected = item == selectedItem.value,
-                    onClick = {
-                        scope.launch { drawerState.close() }
-                        selectedItem.value = item
-                    },
-                    modifier = Modifier.padding(horizontal = 12.dp)
-                )
+            DismissibleDrawerSheet {
+                Spacer(Modifier.height(12.dp))
+                items.forEach { item ->
+                    NavigationDrawerItem(
+                        icon = { Icon(item, contentDescription = null) },
+                        label = { Text(item.name) },
+                        selected = item == selectedItem.value,
+                        onClick = {
+                            scope.launch { drawerState.close() }
+                            selectedItem.value = item
+                        },
+                        modifier = Modifier.padding(horizontal = 12.dp)
+                    )
+                }
             }
         },
         content = {
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
index 03fa547..2455922 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
@@ -25,11 +25,11 @@
 import androidx.compose.material.icons.outlined.Edit
 import androidx.compose.material.icons.outlined.Email
 import androidx.compose.material.icons.outlined.Settings
+import androidx.compose.material3.Divider
 import androidx.compose.material3.DropdownMenu
 import androidx.compose.material3.DropdownMenuItem
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
-import androidx.compose.material3.MenuDefaults
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
@@ -71,7 +71,7 @@
                         contentDescription = null
                     )
                 })
-            MenuDefaults.Divider()
+            Divider()
             DropdownMenuItem(
                 text = { Text("Send Feedback") },
                 onClick = { /* Handle send feedback! */ },
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationBarSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationBarSamples.kt
index 0fe4ec5..6ba4de2 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationBarSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationBarSamples.kt
@@ -38,7 +38,7 @@
     NavigationBar {
         items.forEachIndexed { index, item ->
             NavigationBarItem(
-                icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = item) },
                 label = { Text(item) },
                 selected = selectedItem == index,
                 onClick = { selectedItem = index }
@@ -55,7 +55,7 @@
     NavigationBar {
         items.forEachIndexed { index, item ->
             NavigationBarItem(
-                icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = item) },
                 label = { Text(item) },
                 selected = selectedItem == index,
                 onClick = { selectedItem = index },
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ProgressIndicatorSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ProgressIndicatorSamples.kt
index 4030777..be5261a 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ProgressIndicatorSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ProgressIndicatorSamples.kt
@@ -20,6 +20,7 @@
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredHeight
 import androidx.compose.material3.CircularProgressIndicator
 import androidx.compose.material3.LinearProgressIndicator
@@ -33,6 +34,8 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
 import androidx.compose.ui.unit.dp
 
 @Sampled
@@ -45,9 +48,18 @@
     )
 
     Column(horizontalAlignment = Alignment.CenterHorizontally) {
-        LinearProgressIndicator(progress = animatedProgress)
+        LinearProgressIndicator(
+            modifier = Modifier.semantics(mergeDescendants = true) {}.padding(10.dp),
+            progress = animatedProgress,
+        )
         Spacer(Modifier.requiredHeight(30.dp))
         OutlinedButton(
+            modifier = Modifier.semantics {
+                val progressPercent = (progress * 100).toInt()
+                if (progressPercent in progressBreakpoints) {
+                    stateDescription = "Progress $progressPercent%"
+                }
+            },
             onClick = {
                 if (progress < 1f) progress += 0.1f
             }
@@ -61,7 +73,9 @@
 @Composable
 fun IndeterminateLinearProgressIndicatorSample() {
     Column(horizontalAlignment = Alignment.CenterHorizontally) {
-        LinearProgressIndicator()
+        LinearProgressIndicator(
+            modifier = Modifier.semantics(mergeDescendants = true) {}.padding(10.dp)
+        )
     }
 }
 
@@ -78,6 +92,12 @@
         CircularProgressIndicator(progress = animatedProgress)
         Spacer(Modifier.requiredHeight(30.dp))
         OutlinedButton(
+            modifier = Modifier.semantics {
+                val progressPercent = (progress * 100).toInt()
+                if (progressPercent in progressBreakpoints) {
+                    stateDescription = "Progress $progressPercent%"
+                }
+            },
             onClick = {
                 if (progress < 1f) progress += 0.1f
             }
@@ -94,3 +114,5 @@
         CircularProgressIndicator()
     }
 }
+
+private val progressBreakpoints = listOf(20, 40, 60, 80, 100)
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt
index 34d1fe9..f9d6b30 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt
@@ -98,7 +98,7 @@
     TextField(
         value = text,
         onValueChange = { text = it },
-        placeholder = { Text("placeholder") },
+        label = { Text("Label") },
         leadingIcon = { Icon(Icons.Filled.Favorite, contentDescription = "Localized description") },
         trailingIcon = { Icon(Icons.Filled.Info, contentDescription = "Localized description") }
     )
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ButtonScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ButtonScreenshotTest.kt
index faf3749..c08194bc 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ButtonScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ButtonScreenshotTest.kt
@@ -17,8 +17,12 @@
 
 import android.os.Build
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
@@ -209,4 +213,96 @@
             .captureToImage()
             .assertAgainstGolden(screenshotRule, "text_button_disabled_light_theme")
     }
+
+    @Test
+    fun button_withIcon_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Button(
+                onClick = { /* Do something! */ },
+                contentPadding = ButtonDefaults.ButtonWithIconContentPadding
+            ) {
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = "Localized description",
+                    modifier = Modifier.size(ButtonDefaults.IconSize)
+                )
+                Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                Text("Like")
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_withIcon_lightTheme")
+    }
+
+    @Test
+    fun disabled_button_withIcon_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Button(
+                onClick = { /* Do something! */ },
+                contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
+                enabled = false,
+                modifier = Modifier.testTag("button")
+            ) {
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = "Localized description",
+                    modifier = Modifier.size(ButtonDefaults.IconSize)
+                )
+                Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                Text("Like")
+            }
+        }
+
+        rule.onNodeWithTag("button")
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_withIcon_disabled_lightTheme")
+    }
+
+    @Test
+    fun button_withIcon_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            Button(
+                onClick = { /* Do something! */ },
+                contentPadding = ButtonDefaults.ButtonWithIconContentPadding
+            ) {
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = "Localized description",
+                    modifier = Modifier.size(ButtonDefaults.IconSize)
+                )
+                Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                Text("Like")
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_withIcon_darkTheme")
+    }
+
+    @Test
+    fun disabled_button_withIcon_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            Button(
+                onClick = { /* Do something! */ },
+                contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
+                enabled = false,
+                modifier = Modifier.testTag("button")
+            ) {
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = "Localized description",
+                    modifier = Modifier.size(ButtonDefaults.IconSize)
+                )
+                Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                Text("Like")
+            }
+        }
+
+        rule.onNodeWithTag("button")
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_withIcon_disabled_darkTheme")
+    }
 }
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ButtonTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ButtonTest.kt
index 5403fe4..fdeecd4 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ButtonTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ButtonTest.kt
@@ -17,6 +17,10 @@
 
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -25,14 +29,18 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHasClickAction
 import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsEqualTo
 import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
@@ -51,13 +59,13 @@
     fun defaultSemantics() {
         rule.setMaterialContent(lightColorScheme()) {
             Box {
-                Button(modifier = Modifier.testTag("myButton"), onClick = {}) {
+                Button(modifier = Modifier.testTag(ButtonTestTag), onClick = {}) {
                     Text("myButton")
                 }
             }
         }
 
-        rule.onNodeWithTag("myButton")
+        rule.onNodeWithTag(ButtonTestTag)
             .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
             .assertIsEnabled()
     }
@@ -66,13 +74,13 @@
     fun disabledSemantics() {
         rule.setMaterialContent(lightColorScheme()) {
             Box {
-                Button(modifier = Modifier.testTag("myButton"), onClick = {}, enabled = false) {
+                Button(modifier = Modifier.testTag(ButtonTestTag), onClick = {}, enabled = false) {
                     Text("myButton")
                 }
             }
         }
 
-        rule.onNodeWithTag("myButton")
+        rule.onNodeWithTag(ButtonTestTag)
             .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
             .assertIsNotEnabled()
     }
@@ -85,7 +93,7 @@
 
         rule.setMaterialContent(lightColorScheme()) {
             Box {
-                Button(onClick = onClick, modifier = Modifier.testTag("myButton")) {
+                Button(onClick = onClick, modifier = Modifier.testTag(ButtonTestTag)) {
                     Text(text)
                 }
             }
@@ -93,7 +101,7 @@
 
         // TODO(b/129400818): this actually finds the text, not the button as
         // merge semantics aren't implemented yet
-        rule.onNodeWithTag("myButton")
+        rule.onNodeWithTag(ButtonTestTag)
             // remove this and the todo
             //    rule.onNodeWithText(text)
             .performClick()
@@ -105,18 +113,20 @@
 
     @Test
     fun canBeDisabled() {
-        val tag = "myButton"
-
         rule.setMaterialContent(lightColorScheme()) {
             var enabled by remember { mutableStateOf(true) }
             val onClick = { enabled = false }
             Box {
-                Button(modifier = Modifier.testTag(tag), onClick = onClick, enabled = enabled) {
+                Button(
+                    modifier = Modifier.testTag(ButtonTestTag),
+                    onClick = onClick,
+                    enabled = enabled
+                ) {
                     Text("Hello")
                 }
             }
         }
-        rule.onNodeWithTag(tag)
+        rule.onNodeWithTag(ButtonTestTag)
             // Confirm the button starts off enabled, with a click action
             .assertHasClickAction()
             .assertIsEnabled()
@@ -165,4 +175,84 @@
             assertThat(button2Counter).isEqualTo(1)
         }
     }
+
+    @Test
+    fun button_positioning() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Button(
+                onClick = { /* Do something! */ },
+                modifier = Modifier.testTag(ButtonTestTag)
+            ) {
+                Text(
+                    "Button",
+                    modifier = Modifier
+                        .testTag(TextTestTag)
+                        .semantics(mergeDescendants = true) {}
+                )
+            }
+        }
+
+        val buttonBounds = rule.onNodeWithTag(ButtonTestTag).getUnclippedBoundsInRoot()
+        val textBounds = rule.onNodeWithTag(TextTestTag).getUnclippedBoundsInRoot()
+
+        (textBounds.left - buttonBounds.left).assertIsEqualTo(
+            24.dp,
+            "padding between the start of the button and the start of the text."
+        )
+
+        (buttonBounds.right - textBounds.right).assertIsEqualTo(
+            24.dp,
+            "padding between the end of the text and the end of the button."
+        )
+    }
+
+    @Test
+    fun button_withIcon_positioning() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Button(
+                onClick = { /* Do something! */ },
+                contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
+                modifier = Modifier.testTag(ButtonTestTag)
+            ) {
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = "Localized description",
+                    modifier = Modifier
+                        .size(ButtonDefaults.IconSize)
+                        .testTag(IconTestTag)
+                        .semantics(mergeDescendants = true) {}
+                )
+                Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                Text(
+                    "Like",
+                    modifier = Modifier
+                        .testTag(TextTestTag)
+                        .semantics(mergeDescendants = true) {}
+                )
+            }
+        }
+
+        val textBounds = rule.onNodeWithTag(TextTestTag).getUnclippedBoundsInRoot()
+        val iconBounds = rule.onNodeWithTag(IconTestTag).getUnclippedBoundsInRoot()
+        val buttonBounds = rule.onNodeWithTag(ButtonTestTag).getUnclippedBoundsInRoot()
+
+        (iconBounds.left - buttonBounds.left).assertIsEqualTo(
+            16.dp,
+            "Padding between start of button and start of icon."
+        )
+
+        (textBounds.left - iconBounds.right).assertIsEqualTo(
+            ButtonDefaults.IconSpacing,
+            "Padding between end of icon and start of text."
+        )
+
+        (buttonBounds.right - textBounds.right).assertIsEqualTo(
+            24.dp,
+            "padding between end of text and end of button."
+        )
+    }
 }
+
+private const val ButtonTestTag = "button"
+private const val IconTestTag = "icon"
+private const val TextTestTag = "text"
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DismissibleNavigationDrawerTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DismissibleNavigationDrawerTest.kt
index 377cce0..6ede817 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DismissibleNavigationDrawerTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DismissibleNavigationDrawerTest.kt
@@ -71,7 +71,13 @@
             DismissibleNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("content"))
+                    DismissibleDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag("content")
+                        )
+                    }
                 },
                 content = {}
             )
@@ -88,7 +94,13 @@
             DismissibleNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("content"))
+                    DismissibleDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag("content")
+                        )
+                    }
                 },
                 content = {}
             )
@@ -105,7 +117,13 @@
             DismissibleNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("content"))
+                    DismissibleDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag("content")
+                        )
+                    }
                 },
                 content = {}
             )
@@ -123,7 +141,12 @@
             DismissibleNavigationDrawer(
                 drawerState = rememberDrawerState(DrawerValue.Open),
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("navigationDrawerTag"))
+                    DismissibleDrawerSheet(Modifier.testTag("navigationDrawerTag")) {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                        )
+                    }
                 },
                 content = {}
             )
@@ -144,7 +167,13 @@
             DismissibleNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    DismissibleDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag(DrawerTestTag)
+                        )
+                    }
                 },
                 content = {}
             )
@@ -173,7 +202,13 @@
             DismissibleNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    DismissibleDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag(DrawerTestTag)
+                        )
+                    }
                 },
                 content = {}
             )
@@ -202,7 +237,13 @@
             DismissibleNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    DismissibleDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag(DrawerTestTag)
+                        )
+                    }
                 },
                 content = {}
             )
@@ -231,7 +272,13 @@
             DismissibleNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    DismissibleDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag(DrawerTestTag)
+                        )
+                    }
                 },
                 content = {}
             )
@@ -264,10 +311,19 @@
                 DismissibleNavigationDrawer(
                     drawerState = drawerState,
                     drawerContent = {
-                        Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                        DismissibleDrawerSheet {
+                            Box(
+                                Modifier
+                                    .fillMaxSize()
+                                    .testTag(DrawerTestTag)
+                            )
+                        }
                     },
                     content = {
-                        Box(Modifier.fillMaxSize().clickable { bodyClicks += 1 })
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .clickable { bodyClicks += 1 })
                     }
                 )
             }
@@ -300,10 +356,20 @@
                 DismissibleNavigationDrawer(
                     drawerState = drawerState,
                     drawerContent = {
-                        Box(Modifier.fillMaxSize().background(color = Color.Magenta))
+                        DismissibleDrawerSheet {
+                            Box(
+                                Modifier
+                                    .fillMaxSize()
+                                    .background(color = Color.Magenta)
+                            )
+                        }
                     },
                     content = {
-                        Box(Modifier.fillMaxSize().background(color = Color.Red))
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .background(color = Color.Red)
+                        )
                     }
                 )
             }
@@ -339,14 +405,20 @@
                 DismissibleNavigationDrawer(
                     drawerState = drawerState,
                     drawerContent = {
-                        Box(
-                            Modifier.fillMaxSize()
-                                .testTag("content")
-                                .background(color = Color.Magenta)
-                        )
+                        DismissibleDrawerSheet(Modifier.testTag("content")) {
+                            Box(
+                                Modifier
+                                    .fillMaxSize()
+                                    .background(color = Color.Magenta)
+                            )
+                        }
                     },
                     content = {
-                        Box(Modifier.fillMaxSize().background(color = Color.Red))
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .background(color = Color.Red)
+                        )
                     }
                 )
             }
@@ -381,10 +453,20 @@
                     DismissibleNavigationDrawer(
                         drawerState = drawerState,
                         drawerContent = {
-                            Box(Modifier.fillMaxSize().background(color = Color.Magenta))
+                            DismissibleDrawerSheet {
+                                Box(
+                                    Modifier
+                                        .fillMaxSize()
+                                        .background(color = Color.Magenta)
+                                )
+                            }
                         },
                         content = {
-                            Box(Modifier.fillMaxSize().background(color = Color.Red))
+                            Box(
+                                Modifier
+                                    .fillMaxSize()
+                                    .background(color = Color.Red)
+                            )
                         }
                     )
                 }
@@ -418,7 +500,12 @@
                 DismissibleNavigationDrawer(
                     drawerState = drawerState,
                     drawerContent = {
-                        Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                        DismissibleDrawerSheet(Modifier.testTag(DrawerTestTag)) {
+                            Box(
+                                Modifier
+                                    .fillMaxSize()
+                            )
+                        }
                     },
                     content = {}
                 )
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerUiTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerUiTest.kt
index 71c734b..84d67c1 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerUiTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DividerUiTest.kt
@@ -95,7 +95,7 @@
         rule.setContent {
             sizePx = with(LocalDensity.current) { size.toPx().roundToInt() }
             dividerColor =
-                MaterialTheme.colorScheme.onSurfaceVariant
+                MaterialTheme.colorScheme.surfaceVariant
             Box(modifier = Modifier.size(size).background(Color.Black)) {
                 Divider(
                     modifier = Modifier.testTag(testTag).fillMaxWidth(),
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
index 210c05a..ea3f79f 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
@@ -125,7 +125,7 @@
                         )
                     },
                     trailingIcon = { Text("F11", textAlign = TextAlign.Center) })
-                MenuDefaults.Divider()
+                Divider()
                 DropdownMenuItem(
                     text = { Text("Send Feedback") },
                     onClick = { },
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalNavigationDrawerScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalNavigationDrawerScreenshotTest.kt
index 175d8e5..94b8187 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalNavigationDrawerScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalNavigationDrawerScreenshotTest.kt
@@ -19,6 +19,7 @@
 import android.os.Build
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.testutils.assertAgainstGolden
@@ -55,7 +56,11 @@
             Box(Modifier.requiredSize(400.dp, 32.dp).testTag(ContainerTestTag)) {
                 ModalNavigationDrawer(
                     drawerState = rememberDrawerState(drawerValue),
-                    drawerContent = {},
+                    drawerContent = {
+                        ModalDrawerSheet {
+                          Spacer(modifier = Modifier.fillMaxSize())
+                        }
+                    },
                     content = {
                         Box(
                             Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background)
@@ -72,7 +77,11 @@
                 Box(Modifier.requiredSize(400.dp, 32.dp).testTag(ContainerTestTag)) {
                     ModalNavigationDrawer(
                         drawerState = rememberDrawerState(drawerValue),
-                        drawerContent = {},
+                        drawerContent = {
+                            ModalDrawerSheet {
+                              Spacer(modifier = Modifier.fillMaxSize())
+                            }
+                        },
                         content = {
                             Box(
                                 Modifier.fillMaxSize()
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalNavigationDrawerTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalNavigationDrawerTest.kt
index 03fefeb..a637fd5 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalNavigationDrawerTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalNavigationDrawerTest.kt
@@ -78,7 +78,13 @@
             ModalNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("content"))
+                    ModalDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag("content")
+                        )
+                    }
                 },
                 content = {}
             )
@@ -95,7 +101,13 @@
             ModalNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("content"))
+                    ModalDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag("content")
+                        )
+                    }
                 },
                 content = {}
             )
@@ -112,7 +124,13 @@
             ModalNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("content"))
+                    ModalDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag("content")
+                        )
+                    }
                 },
                 content = {}
             )
@@ -130,7 +148,12 @@
             ModalNavigationDrawer(
                 drawerState = rememberDrawerState(DrawerValue.Open),
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("navigationDrawerTag"))
+                    ModalDrawerSheet(Modifier.testTag("navigationDrawerTag")) {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                        )
+                    }
                 },
                 content = {}
             )
@@ -151,7 +174,13 @@
             ModalNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    ModalDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag(DrawerTestTag)
+                        )
+                    }
                 },
                 content = {}
             )
@@ -180,7 +209,13 @@
             ModalNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    ModalDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag(DrawerTestTag)
+                        )
+                    }
                 },
                 content = {}
             )
@@ -209,7 +244,13 @@
             ModalNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    ModalDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag(DrawerTestTag)
+                        )
+                    }
                 },
                 content = {}
             )
@@ -238,7 +279,13 @@
             ModalNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    ModalDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag(DrawerTestTag)
+                        )
+                    }
                 },
                 content = {}
             )
@@ -270,10 +317,19 @@
             ModalNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().clickable { drawerClicks += 1 })
+                    ModalDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .clickable { drawerClicks += 1 })
+                    }
                 },
                 content = {
-                    Box(Modifier.testTag(DrawerTestTag).fillMaxSize().clickable { bodyClicks += 1 })
+                    Box(
+                        Modifier
+                            .testTag(DrawerTestTag)
+                            .fillMaxSize()
+                            .clickable { bodyClicks += 1 })
                 }
             )
         }
@@ -310,10 +366,19 @@
             ModalNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    ModalDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag(DrawerTestTag)
+                        )
+                    }
                 },
                 content = {
-                    Box(Modifier.fillMaxSize().clickable { bodyClicks += 1 })
+                    Box(
+                        Modifier
+                            .fillMaxSize()
+                            .clickable { bodyClicks += 1 })
                 }
             )
         }
@@ -346,10 +411,20 @@
                 ModalNavigationDrawer(
                     drawerState = drawerState,
                     drawerContent = {
-                        Box(Modifier.fillMaxSize().background(color = Color.Magenta))
+                        ModalDrawerSheet {
+                            Box(
+                                Modifier
+                                    .fillMaxSize()
+                                    .background(color = Color.Magenta)
+                            )
+                        }
                     },
                     content = {
-                        Box(Modifier.fillMaxSize().background(color = Color.Red))
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .background(color = Color.Red)
+                        )
                     }
                 )
             }
@@ -385,14 +460,20 @@
                 ModalNavigationDrawer(
                     drawerState = drawerState,
                     drawerContent = {
-                        Box(
-                            Modifier.fillMaxSize()
-                                .testTag("content")
-                                .background(color = Color.Magenta)
-                        )
+                        ModalDrawerSheet(Modifier.testTag("content")) {
+                            Box(
+                                Modifier
+                                    .fillMaxSize()
+                                    .background(color = Color.Magenta)
+                            )
+                        }
                     },
                     content = {
-                        Box(Modifier.fillMaxSize().background(color = Color.Red))
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .background(color = Color.Red)
+                        )
                     }
                 )
             }
@@ -427,10 +508,20 @@
                     ModalNavigationDrawer(
                         drawerState = drawerState,
                         drawerContent = {
-                            Box(Modifier.fillMaxSize().background(color = Color.Magenta))
+                            ModalDrawerSheet {
+                                Box(
+                                    Modifier
+                                        .fillMaxSize()
+                                        .background(color = Color.Magenta)
+                                )
+                            }
                         },
                         content = {
-                            Box(Modifier.fillMaxSize().background(color = Color.Red))
+                            Box(
+                                Modifier
+                                    .fillMaxSize()
+                                    .background(color = Color.Red)
+                            )
                         }
                     )
                 }
@@ -463,7 +554,12 @@
             ModalNavigationDrawer(
                 drawerState = drawerState,
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    ModalDrawerSheet(Modifier.testTag(DrawerTestTag)) {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                        )
+                    }
                 },
                 content = {}
             )
@@ -500,10 +596,20 @@
                 modifier = Modifier.testTag(topTag),
                 drawerState = rememberDrawerState(DrawerValue.Open),
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag(DrawerTestTag))
+                    ModalDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag(DrawerTestTag)
+                        )
+                    }
                 },
                 content = {
-                    Box(Modifier.fillMaxSize().testTag("body"))
+                    Box(
+                        Modifier
+                            .fillMaxSize()
+                            .testTag("body")
+                    )
                 }
             )
             closeDrawer = getString(Strings.CloseDrawer)
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt
index d708735..d6a0e95 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt
@@ -32,6 +32,7 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getOrNull
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertCountEquals
@@ -57,6 +58,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -126,6 +128,45 @@
     }
 
     @Test
+    fun navigationBarItem_clearsSemanticsOfIcon_whenLabelIsPresent() {
+        rule.setMaterialContent(lightColorScheme()) {
+            NavigationBar {
+                NavigationBarItem(
+                    modifier = Modifier.testTag("item1"),
+                    icon = {
+                        Icon(Icons.Filled.Favorite, "Favorite")
+                    },
+                    label = {
+                        Text("Favorite")
+                    },
+                    selected = true,
+                    alwaysShowLabel = false,
+                    onClick = {}
+                )
+                NavigationBarItem(
+                    modifier = Modifier.testTag("item2"),
+                    icon = {
+                        Icon(Icons.Filled.Favorite, "Favorite")
+                    },
+                    label = {
+                        Text("Favorite")
+                    },
+                    selected = false,
+                    alwaysShowLabel = false,
+                    onClick = {}
+                )
+            }
+        }
+
+        val node1 = rule.onNodeWithTag("item1").fetchSemanticsNode()
+        val node2 = rule.onNodeWithTag("item2").fetchSemanticsNode()
+
+        assertThat(node1.config.getOrNull(SemanticsProperties.ContentDescription)).isNull()
+        assertThat(node2.config.getOrNull(SemanticsProperties.ContentDescription))
+            .isEqualTo(listOf("Favorite"))
+    }
+
+    @Test
     fun navigationBar_size() {
         val height = NavigationBarTokens.ContainerHeight
         rule.setMaterialContentForSizeAssertions {
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationRailTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationRailTest.kt
index 99b4032..06ebee9 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationRailTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationRailTest.kt
@@ -34,6 +34,7 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getOrNull
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertCountEquals
@@ -128,6 +129,45 @@
     }
 
     @Test
+    fun navigationRailItem_clearsSemanticsOfIcon_whenLabelIsPresent() {
+        rule.setMaterialContent(lightColorScheme()) {
+            NavigationRail {
+                NavigationRailItem(
+                    modifier = Modifier.testTag("item1"),
+                    icon = {
+                        Icon(Icons.Filled.Favorite, "Favorite")
+                    },
+                    label = {
+                        Text("Favorite")
+                    },
+                    selected = true,
+                    alwaysShowLabel = false,
+                    onClick = {}
+                )
+                NavigationRailItem(
+                    modifier = Modifier.testTag("item2"),
+                    icon = {
+                        Icon(Icons.Filled.Favorite, "Favorite")
+                    },
+                    label = {
+                        Text("Favorite")
+                    },
+                    selected = false,
+                    alwaysShowLabel = false,
+                    onClick = {}
+                )
+            }
+        }
+
+        val node1 = rule.onNodeWithTag("item1").fetchSemanticsNode()
+        val node2 = rule.onNodeWithTag("item2").fetchSemanticsNode()
+
+        Truth.assertThat(node1.config.getOrNull(SemanticsProperties.ContentDescription)).isNull()
+        Truth.assertThat(node2.config.getOrNull(SemanticsProperties.ContentDescription))
+            .isEqualTo(listOf("Favorite"))
+    }
+
+    @Test
     fun navigationRail_width() {
         val defaultWidth = NavigationRailItemWidth
         rule.setMaterialContentForSizeAssertions {
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/PermanentNavigationDrawerTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/PermanentNavigationDrawerTest.kt
index ca9a847..988821e 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/PermanentNavigationDrawerTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/PermanentNavigationDrawerTest.kt
@@ -53,7 +53,13 @@
         rule.setMaterialContent(lightColorScheme()) {
             PermanentNavigationDrawer(
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("content"))
+                    PermanentDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag("content")
+                        )
+                    }
                 },
                 content = {}
             )
@@ -69,7 +75,13 @@
             CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
                 PermanentNavigationDrawer(
                     drawerContent = {
-                        Box(Modifier.fillMaxSize().testTag("content"))
+                        PermanentDrawerSheet {
+                            Box(
+                                Modifier
+                                    .fillMaxSize()
+                                    .testTag("content")
+                            )
+                        }
                     },
                     content = {}
                 )
@@ -87,7 +99,13 @@
         rule.setMaterialContent(lightColorScheme()) {
             PermanentNavigationDrawer(
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("content"))
+                    PermanentDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag("content")
+                        )
+                    }
                 },
                 content = {}
             )
@@ -104,7 +122,13 @@
         rule.setMaterialContent(lightColorScheme()) {
             PermanentNavigationDrawer(
                 drawerContent = {
-                    Box(Modifier.fillMaxSize().testTag("navigationDrawerTag"))
+                    PermanentDrawerSheet {
+                        Box(
+                            Modifier
+                                .fillMaxSize()
+                                .testTag("navigationDrawerTag")
+                        )
+                    }
                 },
                 content = {}
             )
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
index b64fdee..42039cf 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
@@ -294,7 +294,7 @@
             val titles = listOf("TAB 1", "TAB 2")
             val tabRowHeight = 100.dp
 
-            val divider = @Composable { TabRowDefaults.Divider(Modifier.testTag("divider")) }
+            val divider = @Composable { Divider(Modifier.testTag("divider")) }
 
             Box(Modifier.testTag("tabRow")) {
                 TabRow(
@@ -532,7 +532,7 @@
             val titles = listOf("TAB 1", "TAB 2")
             val tabRowHeight = 100.dp
 
-            val divider = @Composable { TabRowDefaults.Divider(Modifier.testTag("divider")) }
+            val divider = @Composable { Divider(Modifier.testTag("divider")) }
 
             Box(Modifier.testTag("tabRow")) {
                 ScrollableTabRow(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
index 3db8c5d..c78fa9f 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
@@ -52,7 +52,6 @@
 import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpOffset
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntRect
@@ -261,27 +260,6 @@
         horizontal = DropdownMenuItemHorizontalPadding,
         vertical = 0.dp
     )
-
-    /**
-     * Default [Divider], which can be optionally positioned at the bottom of the
-     * [DropdownMenuItemContent].
-     *
-     * @param modifier modifier for the divider's layout
-     * @param color color of the divider
-     * @param thickness thickness of the divider
-     */
-    @Composable
-    fun Divider(
-        modifier: Modifier = Modifier,
-        color: Color = MenuTokens.DividerColor.toColor(),
-        thickness: Dp = MenuTokens.DividerHeight,
-    ) {
-        androidx.compose.material3.Divider(
-            modifier = modifier,
-            color = color,
-            thickness = thickness,
-        )
-    }
 }
 
 /**
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
index 72f36d4..5de0980 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
@@ -56,6 +56,7 @@
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
@@ -159,7 +160,11 @@
 ) {
     val styledIcon = @Composable {
         val iconColor by colors.iconColor(selected = selected)
-        CompositionLocalProvider(LocalContentColor provides iconColor, content = icon)
+        // If there's a label, don't have a11y services repeat the icon description.
+        val clearSemantics = alwaysShowLabel || selected
+        Box(modifier = if (clearSemantics) Modifier.clearAndSetSemantics {} else Modifier) {
+            CompositionLocalProvider(LocalContentColor provides iconColor, content = icon)
+        }
     }
 
     val styledLabel: @Composable (() -> Unit)? = label?.let {
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
index a4ff31d..b948f05 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
@@ -29,6 +29,7 @@
 import androidx.compose.foundation.layout.PaddingValues
 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.fillMaxWidth
 import androidx.compose.foundation.layout.height
@@ -242,49 +243,39 @@
  * @param modifier the [Modifier] to be applied to this drawer
  * @param drawerState state of the drawer
  * @param gesturesEnabled whether or not the drawer can be interacted by gestures
- * @param drawerShape defines the shape of this drawer's container
- * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
- * primary color overlay is applied on top of the container. A higher tonal elevation value will
- * result in a darker color in light theme and lighter color in dark theme. See also: [Surface].
- * @param drawerContainerColor the color used for the background of this drawer. Use
- * [Color.Transparent] to have no color.
- * @param drawerContentColor the preferred color for content inside this drawer. Defaults to either
- * the matching content color for [drawerContainerColor], or to the current [LocalContentColor] if
- * [drawerContainerColor] is not a color from the theme.
  * @param scrimColor color of the scrim that obscures content when the drawer is open
  * @param content content of the rest of the UI
  */
 @Composable
 @ExperimentalMaterial3Api
 fun ModalNavigationDrawer(
-    drawerContent: @Composable ColumnScope.() -> Unit,
+    drawerContent: @Composable () -> Unit,
     modifier: Modifier = Modifier,
     drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
     gesturesEnabled: Boolean = true,
-    drawerShape: Shape = DrawerDefaults.shape,
-    drawerTonalElevation: Dp = DrawerDefaults.ModalDrawerElevation,
-    drawerContainerColor: Color = DrawerDefaults.containerColor,
-    drawerContentColor: Color = contentColorFor(drawerContainerColor),
     scrimColor: Color = DrawerDefaults.scrimColor,
     content: @Composable () -> Unit
 ) {
     val scope = rememberCoroutineScope()
+    val navigationMenu = getString(Strings.NavigationMenu)
     val minValue = -with(LocalDensity.current) { NavigationDrawerTokens.ContainerWidth.toPx() }
     val maxValue = 0f
 
     val anchors = mapOf(minValue to DrawerValue.Closed, maxValue to DrawerValue.Open)
     val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
     Box(
-        modifier.fillMaxSize().swipeable(
-            state = drawerState.swipeableState,
-            anchors = anchors,
-            thresholds = { _, _ -> FractionalThreshold(0.5f) },
-            orientation = Orientation.Horizontal,
-            enabled = gesturesEnabled,
-            reverseDirection = isRtl,
-            velocityThreshold = DrawerVelocityThreshold,
-            resistance = null
-        )
+        modifier
+            .fillMaxSize()
+            .swipeable(
+                state = drawerState.swipeableState,
+                anchors = anchors,
+                thresholds = { _, _ -> FractionalThreshold(0.5f) },
+                orientation = Orientation.Horizontal,
+                enabled = gesturesEnabled,
+                reverseDirection = isRtl,
+                velocityThreshold = DrawerVelocityThreshold,
+                resistance = null
+            )
     ) {
         Box {
             content()
@@ -304,10 +295,8 @@
             },
             color = scrimColor
         )
-        val navigationMenu = getString(Strings.NavigationMenu)
-        Surface(
-            modifier = Modifier
-                .sizeIn(maxWidth = NavigationDrawerTokens.ContainerWidth)
+        Box(
+            Modifier
                 .offset { IntOffset(drawerState.offset.value.roundToInt(), 0) }
                 .semantics {
                     paneTitle = navigationMenu
@@ -322,12 +311,8 @@
                         }
                     }
                 },
-            shape = drawerShape,
-            color = drawerContainerColor,
-            contentColor = drawerContentColor,
-            tonalElevation = drawerTonalElevation
         ) {
-            Column(Modifier.fillMaxSize(), content = drawerContent)
+            drawerContent()
         }
     }
 }
@@ -341,23 +326,15 @@
             "        modifier,\n" +
             "        drawerState,\n" +
             "        gesturesEnabled,\n" +
-            "        drawerShape,\n" +
-            "        drawerTonalElevation,\n" +
-            "        drawerContainerColor,\n" +
-            "        drawerContentColor,\n" +
             "        scrimColor,\n" +
             "        content)"
     )
 )
 fun NavigationDrawer(
-    drawerContent: @Composable ColumnScope.() -> Unit,
+    drawerContent: @Composable () -> Unit,
     modifier: Modifier = Modifier,
     drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
     gesturesEnabled: Boolean = true,
-    drawerShape: Shape = DrawerDefaults.shape,
-    drawerTonalElevation: Dp = DrawerDefaults.ModalDrawerElevation,
-    drawerContainerColor: Color = DrawerDefaults.containerColor,
-    drawerContentColor: Color = contentColorFor(drawerContainerColor),
     scrimColor: Color = DrawerDefaults.scrimColor,
     content: @Composable () -> Unit
 ) {
@@ -366,10 +343,6 @@
         modifier,
         drawerState,
         gesturesEnabled,
-        drawerShape,
-        drawerTonalElevation,
-        drawerContainerColor,
-        drawerContentColor,
         scrimColor,
         content
     )
@@ -393,36 +366,25 @@
  * @param modifier the [Modifier] to be applied to this drawer
  * @param drawerState state of the drawer
  * @param gesturesEnabled whether or not the drawer can be interacted by gestures
- * @param drawerShape defines the shape of this drawer's container
- * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
- * primary color overlay is applied on top of the container. A higher tonal elevation value will
- * result in a darker color in light theme and lighter color in dark theme. See also: [Surface].
- * @param drawerContainerColor the color used for the background of this drawer. Use
- * [Color.Transparent] to have no color.
- * @param drawerContentColor the preferred color for content inside this drawer. Defaults to either
- * the matching content color for [drawerContainerColor], or to the current [LocalContentColor] if
- * [drawerContainerColor] is not a color from the theme.
  * @param content content of the rest of the UI
  */
 @Composable
 @ExperimentalMaterial3Api
 fun DismissibleNavigationDrawer(
-    drawerContent: @Composable ColumnScope.() -> Unit,
+    drawerContent: @Composable () -> Unit,
     modifier: Modifier = Modifier,
     drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
     gesturesEnabled: Boolean = true,
-    drawerShape: Shape = RectangleShape,
-    drawerTonalElevation: Dp = DrawerDefaults.DismissibleDrawerElevation,
-    drawerContainerColor: Color = MaterialTheme.colorScheme.surface,
-    drawerContentColor: Color = contentColorFor(drawerContainerColor),
     content: @Composable () -> Unit
 ) {
-    val scope = rememberCoroutineScope()
     val drawerWidth = NavigationDrawerTokens.ContainerWidth
     val drawerWidthPx = with(LocalDensity.current) { drawerWidth.toPx() }
     val minValue = -drawerWidthPx
     val maxValue = 0f
 
+    val scope = rememberCoroutineScope()
+    val navigationMenu = getString(Strings.NavigationMenu)
+
     val anchors = mapOf(minValue to DrawerValue.Closed, maxValue to DrawerValue.Open)
     val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
     Box(
@@ -438,29 +400,20 @@
         )
     ) {
         Layout(content = {
-            val navigationMenu = getString(Strings.NavigationMenu)
-            Surface(
-                modifier = Modifier
-                    .sizeIn(maxWidth = drawerWidth)
-                    .semantics {
-                        paneTitle = navigationMenu
-                        if (drawerState.isOpen) {
-                            dismiss {
-                                if (
-                                    drawerState.swipeableState
-                                        .confirmStateChange(DrawerValue.Closed)
-                                ) {
-                                    scope.launch { drawerState.close() }
-                                }; true
-                            }
-                        }
-                    },
-                shape = drawerShape,
-                color = drawerContainerColor,
-                contentColor = drawerContentColor,
-                tonalElevation = drawerTonalElevation
-            ) {
-                Column(Modifier.fillMaxSize(), content = drawerContent)
+            Box(Modifier.semantics {
+                paneTitle = navigationMenu
+                if (drawerState.isOpen) {
+                    dismiss {
+                        if (
+                            drawerState.swipeableState
+                                .confirmStateChange(DrawerValue.Closed)
+                        ) {
+                            scope.launch { drawerState.close() }
+                        }; true
+                    }
+                }
+            }) {
+                drawerContent()
             }
             Box {
                 content()
@@ -494,6 +447,27 @@
  *
  * @param drawerContent content inside this drawer
  * @param modifier the [Modifier] to be applied to this drawer
+ * @param content content of the rest of the UI
+ */
+@ExperimentalMaterial3Api
+@Composable
+fun PermanentNavigationDrawer(
+    drawerContent: @Composable () -> Unit,
+    modifier: Modifier = Modifier,
+    content: @Composable () -> Unit
+) {
+    Row(modifier.fillMaxSize()) {
+        drawerContent()
+        Box {
+            content()
+        }
+    }
+}
+
+/**
+ * Content inside of a modal navigation drawer.
+ *
+ * @param modifier the [Modifier] to be applied to this drawer's content
  * @param drawerShape defines the shape of this drawer's container
  * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
  * primary color overlay is applied on top of the container. A higher tonal elevation value will
@@ -503,38 +477,131 @@
  * @param drawerContentColor the preferred color for content inside this drawer. Defaults to either
  * the matching content color for [drawerContainerColor], or to the current [LocalContentColor] if
  * [drawerContainerColor] is not a color from the theme.
- * @param content content of the rest of the UI
+ * @param content content inside of a modal navigation drawer
  */
 @ExperimentalMaterial3Api
 @Composable
-fun PermanentNavigationDrawer(
-    drawerContent: @Composable ColumnScope.() -> Unit,
+fun ModalDrawerSheet(
+    modifier: Modifier = Modifier,
+    drawerShape: Shape = DrawerDefaults.shape,
+    drawerTonalElevation: Dp = DrawerDefaults.ModalDrawerElevation,
+    drawerContainerColor: Color = MaterialTheme.colorScheme.surface,
+    drawerContentColor: Color = contentColorFor(drawerContainerColor),
+    content: @Composable ColumnScope.() -> Unit
+) {
+    DrawerSheet(
+        modifier,
+        drawerShape,
+        drawerTonalElevation,
+        drawerContainerColor,
+        drawerContentColor,
+        content
+    )
+}
+
+/**
+ * Content inside of a dismissible navigation drawer.
+ *
+ * @param modifier the [Modifier] to be applied to this drawer's content
+ * @param drawerShape defines the shape of this drawer's container
+ * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
+ * primary color overlay is applied on top of the container. A higher tonal elevation value will
+ * result in a darker color in light theme and lighter color in dark theme. See also: [Surface].
+ * @param drawerContainerColor the color used for the background of this drawer. Use
+ * [Color.Transparent] to have no color.
+ * @param drawerContentColor the preferred color for content inside this drawer. Defaults to either
+ * the matching content color for [drawerContainerColor], or to the current [LocalContentColor] if
+ * [drawerContainerColor] is not a color from the theme.
+ * @param content content inside of a dismissible navigation drawer
+ */
+@ExperimentalMaterial3Api
+@Composable
+fun DismissibleDrawerSheet(
+    modifier: Modifier = Modifier,
+    drawerShape: Shape = RectangleShape,
+    drawerTonalElevation: Dp = DrawerDefaults.DismissibleDrawerElevation,
+    drawerContainerColor: Color = MaterialTheme.colorScheme.surface,
+    drawerContentColor: Color = contentColorFor(drawerContainerColor),
+    content: @Composable ColumnScope.() -> Unit
+) {
+    DrawerSheet(
+        modifier,
+        drawerShape,
+        drawerTonalElevation,
+        drawerContainerColor,
+        drawerContentColor,
+        content
+    )
+}
+
+/**
+ * Content inside of a permanent navigation drawer.
+ *
+ * @param modifier the [Modifier] to be applied to this drawer's content
+ * @param drawerShape defines the shape of this drawer's container
+ * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
+ * primary color overlay is applied on top of the container. A higher tonal elevation value will
+ * result in a darker color in light theme and lighter color in dark theme. See also: [Surface].
+ * @param drawerContainerColor the color used for the background of this drawer. Use
+ * [Color.Transparent] to have no color.
+ * @param drawerContentColor the preferred color for content inside this drawer. Defaults to either
+ * the matching content color for [drawerContainerColor], or to the current [LocalContentColor] if
+ * [drawerContainerColor] is not a color from the theme.
+ * @param content content inside a permanent navigation drawer
+ */
+@ExperimentalMaterial3Api
+@Composable
+fun PermanentDrawerSheet(
     modifier: Modifier = Modifier,
     drawerShape: Shape = RectangleShape,
     drawerTonalElevation: Dp = DrawerDefaults.PermanentDrawerElevation,
     drawerContainerColor: Color = MaterialTheme.colorScheme.surface,
     drawerContentColor: Color = contentColorFor(drawerContainerColor),
-    content: @Composable () -> Unit
+    content: @Composable ColumnScope.() -> Unit
 ) {
-    val drawerWidth = NavigationDrawerTokens.ContainerWidth
-    Row(modifier.fillMaxSize()) {
-        val navigationMenu = getString(Strings.NavigationMenu)
-        Surface(
-            modifier = Modifier
-                .sizeIn(maxWidth = drawerWidth)
-                .semantics {
-                    paneTitle = navigationMenu
-                },
-            shape = drawerShape,
-            color = drawerContainerColor,
-            contentColor = drawerContentColor,
-            tonalElevation = drawerTonalElevation
-        ) {
-            Column(Modifier.fillMaxSize(), content = drawerContent)
-        }
-        Box {
-            content()
-        }
+    val navigationMenu = getString(Strings.NavigationMenu)
+    DrawerSheet(
+        modifier.semantics {
+            paneTitle = navigationMenu
+        },
+        drawerShape,
+        drawerTonalElevation,
+        drawerContainerColor,
+        drawerContentColor,
+        content
+    )
+}
+
+@ExperimentalMaterial3Api
+@Composable
+private fun DrawerSheet(
+    modifier: Modifier = Modifier,
+    drawerShape: Shape = RectangleShape,
+    drawerTonalElevation: Dp = DrawerDefaults.PermanentDrawerElevation,
+    drawerContainerColor: Color = MaterialTheme.colorScheme.surface,
+    drawerContentColor: Color = contentColorFor(drawerContainerColor),
+    content: @Composable ColumnScope.() -> Unit
+) {
+    Surface(
+        modifier = modifier
+            .sizeIn(
+                minWidth = MinimumDrawerWidth,
+                maxWidth = DrawerDefaults.MaximumDrawerWidth
+            )
+            .fillMaxHeight(),
+        shape = drawerShape,
+        color = drawerContainerColor,
+        contentColor = drawerContentColor,
+        tonalElevation = drawerTonalElevation
+    ) {
+        Column(
+            Modifier
+                .sizeIn(
+                    minWidth = MinimumDrawerWidth,
+                    maxWidth = DrawerDefaults.MaximumDrawerWidth
+                ),
+            content = content
+        )
     }
 }
 
@@ -570,6 +637,9 @@
 
     /** Default container color for a navigation drawer */
     val containerColor: Color @Composable get() = NavigationDrawerTokens.ContainerColor.toColor()
+
+    /** Default and maximum width of a navigation drawer **/
+    val MaximumDrawerWidth = NavigationDrawerTokens.ContainerWidth
 }
 
 /**
@@ -817,6 +887,7 @@
 }
 
 private val DrawerVelocityThreshold = 400.dp
+private val MinimumDrawerWidth = 240.dp
 
 // TODO: b/177571613 this should be a proper decay settling
 // this is taken from the DrawerLayout's DragViewHelper as a min duration.
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt
index 74cb1fc..fcfc11a 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt
@@ -55,6 +55,7 @@
 import androidx.compose.ui.layout.layoutId
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.constrainWidth
@@ -163,7 +164,11 @@
 ) {
     val styledIcon = @Composable {
         val iconColor by colors.iconColor(selected = selected)
-        CompositionLocalProvider(LocalContentColor provides iconColor, content = icon)
+        // If there's a label, don't have a11y services repeat the icon description.
+        val clearSemantics = alwaysShowLabel || selected
+        Box(modifier = if (clearSemantics) Modifier.clearAndSetSemantics {} else Modifier) {
+            CompositionLocalProvider(LocalContentColor provides iconColor, content = icon)
+        }
     }
 
     val styledLabel: @Composable (() -> Unit)? = label?.let {
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
index 66afa3a..d55c461 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
@@ -134,7 +134,7 @@
         )
     },
     divider: @Composable () -> Unit = @Composable {
-        TabRowDefaults.Divider()
+        Divider()
     },
     tabs: @Composable () -> Unit
 ) {
@@ -223,7 +223,7 @@
         )
     },
     divider: @Composable () -> Unit = @Composable {
-        TabRowDefaults.Divider()
+        Divider()
     },
     tabs: @Composable () -> Unit
 ) {
@@ -351,25 +351,6 @@
         PrimaryNavigationTabTokens.ActiveLabelTextColor.toColor()
 
     /**
-     * Default [Divider], which will be positioned at the bottom of the [TabRow], underneath the
-     * indicator.
-     *
-     * @param modifier modifier for the divider's layout
-     * @param thickness thickness of the divider
-     * @param color color of the divider
-     */
-    @Composable
-    fun Divider(
-        modifier: Modifier = Modifier,
-        thickness: Dp = PrimaryNavigationTabTokens.DividerHeight,
-        color: Color =
-            MaterialTheme.colorScheme.fromToken(PrimaryNavigationTabTokens.DividerColor)
-    ) {
-        androidx.compose.material3.Divider(
-            modifier = modifier, thickness = thickness, color = color)
-    }
-
-    /**
      * Default indicator, which will be positioned at the bottom of the [TabRow], on top of the
      * divider.
      *
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/DividerTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/DividerTokens.kt
index 42a4b2b..508aa4e 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/DividerTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/DividerTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2021 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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// VERSION: v0_92
+// VERSION: v0_103
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -21,6 +21,6 @@
 import androidx.compose.ui.unit.dp
 
 internal object DividerTokens {
-    val Color = ColorSchemeKeyTokens.OnSurfaceVariant
+    val Color = ColorSchemeKeyTokens.SurfaceVariant
     val Thickness = 1.0.dp
-}
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
index a5719c8..d7d4ddd 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
@@ -2953,7 +2953,14 @@
                         }
                     }
                 } else {
-                    val nodesToInsert = from.slotTable.collectNodesFrom(from.anchor)
+                    // If the state was already removed from the from table then it will have a
+                    // state recorded in the recomposer, retrieve that now if we can. If not the
+                    // state is still in its original location, recompose over it there.
+                    val resolvedState = parentContext.movableContentStateResolve(from)
+                    val fromTable = resolvedState?.slotTable ?: from.slotTable
+                    val fromAnchor = resolvedState?.slotTable?.anchor(0) ?: from.anchor
+                    val nodesToInsert = fromTable.collectNodesFrom(fromAnchor)
+
                     // Insert nodes if necessary
                     if (nodesToInsert.isNotEmpty()) {
                         record { applier, _, _ ->
@@ -2965,24 +2972,30 @@
                                 applier.insertTopDown(base + i, node)
                             }
                         }
-                        val group = slotTable.anchorIndex(anchor)
-                        updateNodeCount(
-                            group,
-                            updatedNodeCount(group) + nodesToInsert.size
-                        )
+                        if (to.slotTable == slotTable) {
+                            // Inserting the content into the current slot table then we need to
+                            // update the virtual node counts. Otherwise, we are inserting into
+                            // a new slot table which is being created, not updated, so the virtual
+                            // node counts do not need to be updated.
+                            val group = slotTable.anchorIndex(anchor)
+                            updateNodeCount(
+                                group,
+                                updatedNodeCount(group) + nodesToInsert.size
+                            )
+                        }
                     }
 
                     // Copy the slot table into the anchor location
                     record { _, slots, _ ->
-                        val state = parentContext.movableContentStateResolve(from)
+                        val state = resolvedState ?: parentContext.movableContentStateResolve(from)
                             ?: composeRuntimeError("Could not resolve state for movable content")
 
                         // The slot table contains the movable content group plus the group
                         // containing the movable content's table which then contains the actual
                         // state to be inserted. The state is at index 2 in the table (for the
-                        // to groups) and is inserted into the provider group at offset 1 from the
+                        // two groups) and is inserted into the provider group at offset 1 from the
                         // current location.
-                        val anchors = slots.moveIntoGroupFrom(1, state.slotTable, 1)
+                        val anchors = slots.moveIntoGroupFrom(1, state.slotTable, 2)
 
                         // For all the anchors that moved, if the anchor is tracking a recompose
                         // scope, update it to reference its new composer.
@@ -2997,12 +3010,9 @@
                         }
                     }
 
-                    // Recompose over the moved content.
-                    val fromTable = from.slotTable
-
                     fromTable.read { reader ->
                         withReader(reader) {
-                            val newLocation = fromTable.anchorIndex(from.anchor)
+                            val newLocation = fromTable.anchorIndex(fromAnchor)
                             reader.reposition(newLocation)
                             writersReaderDelta = newLocation
                             val offsetChanges = mutableListOf<Change>()
@@ -3457,7 +3467,7 @@
             // another insert. If the nested movable content ends up being removed this is reported
             // during that recomposition so there is no need to look at child movable content here.
             return if (reader.hasMark(group)) {
-                @Suppress("UNCHECKED_CAST")
+                @Suppress("UNCHECKED_CAST") // The mark is only used when this cast is valid.
                 val value = reader.groupObjectKey(group) as MovableContent<Any?>
                 val parameter = reader.groupGet(group, 0)
                 val anchor = reader.anchor(group)
@@ -3479,9 +3489,28 @@
                 record { _, slots, _ ->
                     val slotTable = SlotTable()
 
+                    // Write a table that as if it was written by a calling
+                    // invokeMovableContentLambda because this might be removed from the
+                    // composition before the new composition can be composed to receive it. When
+                    // the new composition receives the state it must recompose over the state by
+                    // calling invokeMovableContentLambda.
                     slotTable.write { writer ->
                         writer.beginInsert()
+
+                        // This is the prefix created by invokeMovableContentLambda
+                        writer.startGroup(movableContentKey, value)
+                        writer.markGroup()
+                        writer.update(parameter)
+
+                        // Move the content into current location
                         slots.moveTo(anchor, 1, writer)
+
+                        // skip the group that was just inserted.
+                        writer.skipGroup()
+
+                        // End the group that represents the call to invokeMovableContentLambda
+                        writer.endGroup()
+
                         writer.endInsert()
                     }
                     val state = MovableContentState(slotTable)
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
index accff5a..7ded476 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
@@ -1027,6 +1027,104 @@
         expectUnused()
     }
 
+    @Test // Regression test for 230830644
+    fun deferredSubcompose_conditional() = compositionTest {
+        var subcompose by mutableStateOf(false)
+        var lastPrivateState: State<Int> = mutableStateOf(0)
+
+        val content = movableContentOf {
+            lastPrivateState = remember { mutableStateOf(0) }
+            Text("Movable content")
+        }
+
+        compose {
+            Text("Main content start")
+            if (!subcompose) {
+                content()
+            }
+            Text("Main content end")
+            if (subcompose) {
+                DeferredSubcompose {
+                    Text("Sub-composed content start")
+                    content()
+                    Text("Sub-composed content end")
+                }
+            }
+        }
+
+        validate {
+            Text("Main content start")
+            if (!subcompose) {
+                Text("Movable content")
+            }
+            Text("Main content end")
+            if (subcompose) {
+                DeferredSubcompose {
+                    Text("Sub-composed content start")
+                    Text("Movable content")
+                    Text("Sub-composed content end")
+                }
+            }
+        }
+
+        val expectedState = lastPrivateState
+        subcompose = true
+        expectChanges()
+        revalidate()
+
+        assertEquals(expectedState, lastPrivateState)
+    }
+
+    @Test // Regression test for 230830644
+    fun deferredSubcompose_conditional_and_invalid() = compositionTest {
+        var subcompose by mutableStateOf(false)
+        var lastPrivateState: State<Int> = mutableStateOf(0)
+        var state by mutableStateOf("one")
+
+        val content = movableContentOf {
+            lastPrivateState = remember { mutableStateOf(0) }
+            Text("Movable content state: $state")
+        }
+
+        compose {
+            Text("Main content start")
+            if (!subcompose) {
+                content()
+            }
+            Text("Main content end")
+            if (subcompose) {
+                DeferredSubcompose {
+                    Text("Sub-composed content start")
+                    content()
+                    Text("Sub-composed content end")
+                }
+            }
+        }
+
+        validate {
+            Text("Main content start")
+            if (!subcompose) {
+                Text("Movable content state: $state")
+            }
+            Text("Main content end")
+            if (subcompose) {
+                DeferredSubcompose {
+                    Text("Sub-composed content start")
+                    Text("Movable content state: $state")
+                    Text("Sub-composed content end")
+                }
+            }
+        }
+
+        val expectedState = lastPrivateState
+        subcompose = true
+        state = "two"
+        expectChanges()
+        revalidate()
+
+        assertEquals(expectedState, lastPrivateState)
+    }
+
     @Test
     fun movableContentParameters_One() = compositionTest {
         val data = mutableStateOf(0)
@@ -1421,10 +1519,28 @@
     }
 }
 
+@Composable
+private fun DeferredSubcompose(content: @Composable () -> Unit) {
+    val host = View().also { it.name = "DeferredSubcompose" }
+    ComposeNode<View, ViewApplier>(factory = { host }, update = { })
+    val parent = rememberCompositionContext()
+    val composition = remember { Composition(ViewApplier(host), parent) }
+    SideEffect {
+        composition.setContent(content)
+    }
+    DisposableEffect(Unit) {
+        onDispose { composition.dispose() }
+    }
+}
+
 private fun MockViewValidator.Subcompose(content: MockViewValidator.() -> Unit) {
     view("SubcomposeHost", content)
 }
 
+private fun MockViewValidator.DeferredSubcompose(content: MockViewValidator.() -> Unit) {
+    view("DeferredSubcompose", content)
+}
+
 class RememberedObject : RememberObserver {
     var count: Int = 0
     val isLive: Boolean get() = count > 0
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt
index 3aabba5..4b63669 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt
@@ -45,7 +45,9 @@
 
     fun addAt(index: Int, view: View) {
         if (view.parent != null) {
-            error("View named $name already has a parent")
+            error(
+                "Inserting a view named ${view.name} already has a parent into a view named $name"
+            )
         }
         view.parent = this
         children.add(index, view)
diff --git a/compose/ui/ui-android-stubs/src/main/java/android/view/RenderNode.java b/compose/ui/ui-android-stubs/src/main/java/android/view/RenderNode.java
index 30a3cc5..13c2ab3 100644
--- a/compose/ui/ui-android-stubs/src/main/java/android/view/RenderNode.java
+++ b/compose/ui/ui-android-stubs/src/main/java/android/view/RenderNode.java
@@ -29,7 +29,7 @@
  * Stubs for RenderNode on M-P devices.
  */
 public class RenderNode {
-    @SuppressWarnings("UnusedVariable")
+    @SuppressWarnings("unused")
     private RenderNode(String name, View owningView) {
     }
 
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
index 7e84739..d565d31 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
@@ -49,6 +49,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCompositionContext
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.tooling.CompositionData
 import androidx.compose.runtime.tooling.LocalInspectionTables
@@ -60,6 +61,7 @@
 import androidx.compose.ui.inspection.compose.flatten
 import androidx.compose.ui.inspection.testdata.TestActivity
 import androidx.compose.ui.layout.GraphicLayerInfo
+import androidx.compose.ui.node.Ref
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalInspectionMode
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
@@ -890,6 +892,30 @@
         assertThat(inlineParameters.parameters).hasSize(2)
     }
 
+    @Test
+    fun testRemember() {
+        val slotTableRecord = CompositionDataRecord.create()
+
+        // Regression test for: b/235526153
+        // The method: SubCompositionRoots.remember had code like this:
+        //   group.data.filterIsInstance<Ref<ViewRootForInspector>>().singleOrNull()?.value
+        // which would cause a ClassCastException if the data contained a Ref to something
+        // else than a ViewRootForInspector instance. This would crash the app.
+        show {
+            Inspectable(slotTableRecord) {
+                rememberCompositionContext()
+                val stringReference = remember { Ref<String>() }
+                stringReference.value = "Hello"
+            }
+        }
+        val androidComposeView = findAndroidComposeView()
+        androidComposeView.setTag(R.id.inspection_slot_table_set, slotTableRecord.store)
+        val builder = LayoutInspectorTree()
+        builder.hideSystemNodes = false
+        builder.includeAllParameters = false
+        builder.convert(androidComposeView)
+    }
+
     @Suppress("SameParameterValue")
     private fun validate(
         result: List<InspectorNode>,
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
index 43fea44..702a57a 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
@@ -746,9 +746,7 @@
             if (!capturing) {
                 return node
             }
-            val root = group.data.filterIsInstance<ViewRootForInspector>().singleOrNull()
-                ?: group.data.filterIsInstance<Ref<ViewRootForInspector>>().singleOrNull()?.value
-                ?: return node
+            val root = findSingleRootInGroupData(group) ?: return node
 
             val view = root.subCompositionView
             if (view != null) {
@@ -764,6 +762,12 @@
             return node
         }
 
+        private fun findSingleRootInGroupData(group: CompositionGroup): ViewRootForInspector? {
+            group.data.filterIsInstance<ViewRootForInspector>().singleOrNull()?.let { return it }
+            val refs = group.data.filterIsInstance<Ref<*>>().map { it.value }
+            return refs.filterIsInstance<ViewRootForInspector>().singleOrNull()
+        }
+
         /**
          * Capture the top node of the sub-composition root until a non empty node is found.
          */
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 32bc836..0f28225 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2203,6 +2203,7 @@
 
   public abstract static class Placeable.PlacementScope {
     ctor public Placeable.PlacementScope();
+    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.layout.LayoutCoordinates? getCoordinates();
     method protected abstract androidx.compose.ui.unit.LayoutDirection getParentLayoutDirection();
     method protected abstract int getParentWidth();
     method public final void place(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
@@ -2213,6 +2214,7 @@
     method public final void placeRelativeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
     method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
     method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, long position, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
+    property @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.layout.LayoutCoordinates? coordinates;
     property protected abstract androidx.compose.ui.unit.LayoutDirection parentLayoutDirection;
     property protected abstract int parentWidth;
   }
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PlacementScopeCoordinatesSample.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PlacementScopeCoordinatesSample.kt
new file mode 100644
index 0000000..a6f0137
--- /dev/null
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PlacementScopeCoordinatesSample.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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.compose.ui.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.round
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Sampled
+@Composable
+fun PlacementScopeCoordinatesSample() {
+    // Layout so that the first item consumes to half of the width of the screen, if possible.
+    // The remainder of the layouts are positioned horizontally in the remaining space.
+    @Composable
+    fun FirstItemHalf(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
+        val view = LocalView.current
+
+        Layout(content = content, modifier = modifier, measurePolicy = { measurables, constraints ->
+            var width = constraints.minWidth
+            var height = constraints.minHeight
+            // If this doesn't have a fixed size, just layout horizontally
+            var placeables: List<Placeable>? = null
+            if (measurables.isNotEmpty()) {
+                if (constraints.hasBoundedWidth && constraints.hasBoundedHeight) {
+                    width = constraints.maxWidth
+                    height = constraints.maxHeight
+                } else {
+                    placeables = measurables.map { it.measure(constraints) }
+                    width = placeables.sumOf { it.width }
+                    height = placeables.maxOf { it.height }
+                }
+            }
+            layout(width, height) {
+                if (placeables != null) {
+                    var x = 0
+                    placeables.forEach {
+                        it.placeRelative(x, 0)
+                        x += it.width
+                    }
+                } else if (measurables.isNotEmpty() && coordinates != null) {
+                    val coordinates = coordinates!!
+                    val positionInWindow = IntArray(2)
+                    view.getLocationOnScreen(positionInWindow)
+                    val topLeft = coordinates.localToRoot(Offset.Zero).round() +
+                        IntOffset(positionInWindow[0], positionInWindow[1])
+                    val displayWidth = view.resources.displayMetrics.widthPixels
+                    val halfWay = displayWidth / 2
+
+                    val c0 = if (topLeft.x < halfWay) {
+                        // The first measurable should fit to half way across
+                        Constraints.fixed(
+                            halfWay - topLeft.x,
+                            height
+                        )
+                    } else {
+                        // The first is already past the half way, so just divide it evenly
+                        val measureWidth = width / measurables.size
+                        Constraints.fixed(measureWidth, height)
+                    }
+                    val p0 = measurables[0].measure(c0)
+                    p0.place(0, 0)
+
+                    // The rest just fit in the remainder of the space
+                    var x = p0.width
+                    for (i in 1..measurables.lastIndex) {
+                        val measureWidth = (width - x) / (measurables.size - i)
+                        val p = measurables[i].measure(Constraints.fixed(measureWidth, height))
+                        p.place(x, 0)
+                        x += p.width
+                    }
+                }
+            }
+        })
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
index 442f34c..9509a43 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
@@ -3241,7 +3241,7 @@
                 measurables: List<Measurable>,
                 constraints: Constraints
             ): MeasureResult =
-                measureScope.layout(x2 - x, y2 - y) {
+                innerLayoutNodeWrapper.layout(x2 - x, y2 - y) {
                     measurables.forEach { it.measure(constraints).place(0, 0) }
                 }
         }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadLayoutTest.kt
index 4deac3c..fd6b69e 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadLayoutTest.kt
@@ -1009,6 +1009,57 @@
         assertEquals(Offset.Zero, posInChild)
     }
 
+    @Test
+    fun multiMeasureLayoutInLookahead() {
+        var horizontal by mutableStateOf(true)
+        rule.setContent {
+            MyLookaheadLayout {
+                @Suppress("DEPRECATION")
+                MultiMeasureLayout(
+                    content = {
+                        if (horizontal) {
+                            Row {
+                                repeat(3) {
+                                    Box(
+                                        Modifier
+                                            .weight(1f)
+                                            .background(Color.Red)
+                                    )
+                                }
+                            }
+                        } else {
+                            Column {
+                                repeat(3) {
+                                    Box(
+                                        Modifier
+                                            .weight(1f)
+                                            .background(Color.Red)
+                                    )
+                                }
+                            }
+                        }
+                    },
+                    modifier = Modifier.fillMaxSize(0.6f),
+                    measurePolicy = MeasurePolicy { measurables, constraints ->
+                        // Intentionally measure twice here to ensure multi-measure is supported.
+                        measurables.map { it.measure(Constraints.fixed(200, 300)) }
+                        val placeables = measurables.map { it.measure(constraints) }
+                        val maxWidth: Int = placeables.maxOf { it.width }
+                        val maxHeight = placeables.maxOf { it.height }
+                        // Position the children.
+                        layout(maxWidth, maxHeight) {
+                            placeables.forEach {
+                                it.place(0, 0)
+                            }
+                        }
+                    })
+            }
+        }
+        rule.runOnIdle { horizontal = !horizontal }
+        rule.runOnIdle { horizontal = !horizontal }
+        rule.waitForIdle()
+    }
+
     private fun assertSameLayoutWithAndWithoutLookahead(
         content: @Composable (
             modifier: Modifier
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/PlacementLayoutCoordinatesTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/PlacementLayoutCoordinatesTest.kt
new file mode 100644
index 0000000..7586484
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/PlacementLayoutCoordinatesTest.kt
@@ -0,0 +1,685 @@
+/*
+ * Copyright 2020 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.layout
+
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentOf
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.AbsoluteAlignment
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.round
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalComposeUiApi::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class PlacementLayoutCoordinatesTest {
+    @get:Rule
+    val rule = createAndroidComposeRule<ComponentActivity>()
+
+    /**
+     * The [Placeable.PlacementScope.coordinates] should not be `null` during normal placement
+     * and should have the position of the parent that is placing.
+     */
+    @Test
+    fun coordinatesWhilePlacing() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        var locationAtPlace: IntOffset? by mutableStateOf(null)
+        var boxSize by mutableStateOf(IntSize.Zero)
+        var alignment by mutableStateOf(Alignment.Center)
+        rule.setContent {
+            Box(Modifier.fillMaxSize()) {
+                Box(
+                    Modifier
+                        .align(alignment)
+                        .layout { measurable, constraints ->
+                            val p = measurable.measure(constraints)
+                            layout(p.width, p.height) {
+                                locations += coordinates
+                                locationAtPlace = coordinates
+                                    ?.positionInRoot()
+                                    ?.round()
+                                boxSize = IntSize(p.width, p.height)
+                                p.place(0, 0)
+                            }
+                        }
+                        .size(10.dp, 10.dp)
+                )
+            }
+        }
+        rule.waitForIdle()
+        assertNotNull(locationAtPlace)
+        assertNotEquals(IntOffset.Zero, locationAtPlace)
+        assertEquals(1, locations.size)
+
+        locationAtPlace = null
+        locations.clear()
+        alignment = AbsoluteAlignment.TopLeft
+        rule.waitForIdle()
+        assertNotNull(locationAtPlace)
+        assertEquals(IntOffset.Zero, locationAtPlace)
+        assertEquals(1, locations.size)
+
+        locationAtPlace = null
+        locations.clear()
+        alignment = AbsoluteAlignment.BottomRight
+        rule.waitForIdle()
+        assertNotNull(locationAtPlace)
+        assertEquals(1, locations.size)
+        val content = rule.activity.findViewById<View>(android.R.id.content)
+        val bottomRight = IntOffset(content.width - boxSize.width, content.height - boxSize.height)
+        assertEquals(bottomRight, locationAtPlace)
+    }
+
+    /**
+     * The [Placeable.PlacementScope.coordinates] should not be `null` during normal placement
+     * and should have the position of the parent that is placing.
+     */
+    @OptIn(ExperimentalComposeUiApi::class)
+    @Test
+    fun coordinatesWhilePlacingWithLookaheadLayout() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        var locationAtPlace: IntOffset? by mutableStateOf(null)
+        var boxSize by mutableStateOf(IntSize.Zero)
+        var alignment by mutableStateOf(Alignment.Center)
+        rule.setContent {
+            SimpleLookaheadLayout {
+                Box(Modifier.fillMaxSize()) {
+                    Box(
+                        Modifier
+                            .align(alignment)
+                            .layout { measurable, constraints ->
+                                val p = measurable.measure(constraints)
+                                layout(p.width, p.height) {
+                                    locations += coordinates
+                                    locationAtPlace = coordinates
+                                        ?.positionInRoot()
+                                        ?.round()
+                                    boxSize = IntSize(p.width, p.height)
+                                    p.place(0, 0)
+                                }
+                            }
+                            .size(10.dp, 10.dp)
+                    )
+                }
+            }
+        }
+        rule.waitForIdle()
+        locationAtPlace = null
+        locations.clear()
+        alignment = AbsoluteAlignment.TopLeft
+        rule.waitForIdle()
+        assertNotNull(locationAtPlace)
+        assertEquals(IntOffset.Zero, locationAtPlace)
+        assertEquals(2, locations.size)
+
+        locationAtPlace = null
+        locations.clear()
+        alignment = AbsoluteAlignment.BottomRight
+        rule.waitForIdle()
+        assertNotNull(locationAtPlace)
+        assertEquals(2, locations.size)
+        val content = rule.activity.findViewById<View>(android.R.id.content)
+        val bottomRight = IntOffset(content.width - boxSize.width, content.height - boxSize.height)
+        assertEquals(bottomRight, locationAtPlace)
+    }
+
+    /**
+     * The [Placeable.PlacementScope.coordinates] should be `null` while calculating the alignment,
+     * but should be non-null after the alignment has been calculated.
+     */
+    @Test
+    fun coordinatesWhileAligning() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        rule.setContent {
+            Row(Modifier.fillMaxSize()) {
+                Box(Modifier.alignByBaseline()) {
+                    Text("Hello")
+                }
+                Box(
+                    Modifier
+                        .alignByBaseline()
+                        .layout { measurable, constraints ->
+                            val p = measurable.measure(constraints)
+                            layout(p.width, p.height) {
+                                locations += coordinates
+                                p.place(0, 0)
+                            }
+                        }) {
+                    Text("World")
+                }
+            }
+        }
+        rule.waitForIdle()
+        assertTrue(locations.size > 1)
+        assertNull(locations[0])
+        assertNotNull(locations.last())
+    }
+
+    /**
+     * The [Placeable.PlacementScope.coordinates] should be `null` while calculating the alignment,
+     * but should be non-null after the alignment has been calculated.
+     */
+    @OptIn(ExperimentalComposeUiApi::class)
+    @Test
+    fun coordinatesWhileAligningWithLookaheadLayout() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        rule.setContent {
+            SimpleLookaheadLayout {
+                Row(Modifier.fillMaxSize()) {
+                    Box(Modifier.alignByBaseline()) {
+                        Text("Hello")
+                    }
+                    Box(
+                        Modifier
+                            .alignByBaseline()
+                            .layout { measurable, constraints ->
+                                val p = measurable.measure(constraints)
+                                layout(p.width, p.height) {
+                                    locations += coordinates
+                                    p.place(0, 0)
+                                }
+                            }) {
+                        Text("World")
+                    }
+                }
+            }
+        }
+        rule.waitForIdle()
+        // There may be a way to make this only 2 invocations for the first pass rather than 3.
+        assertEquals(4, locations.size)
+        assertNull(locations[0]) // Lookahead pass
+        assertNull(locations[1]) // Lookahead pass - second look at alignment line
+        assertNotNull(locations[2]) // Lookahead pass placement
+        assertNotNull(locations[3]) // Measure pass
+    }
+
+    /**
+     * The [Placeable.PlacementScope.coordinates] should be `null` while calculating the alignment,
+     * but should be non-null after the alignment has been calculated.
+     */
+    @Test
+    fun coordinatesWhileAligningInLayout() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        rule.setContent {
+            Row(Modifier.fillMaxSize()) {
+                Box(Modifier.alignByBaseline()) {
+                    Text("Hello")
+                }
+                val content = @Composable { Text("World") }
+                Layout(content, Modifier.alignByBaseline()) { measurables, constraints ->
+                    val p = measurables[0].measure(constraints)
+                    layout(p.width, p.height) {
+                        locations += coordinates
+                        p.place(0, 0)
+                    }
+                }
+            }
+        }
+        rule.waitForIdle()
+        assertEquals(2, locations.size)
+        assertNull(locations[0])
+        assertNotNull(locations[1])
+    }
+
+    /**
+     * The [Placeable.PlacementScope.coordinates] should be `null` while calculating the alignment,
+     * but should be non-null after the alignment has been calculated.
+     */
+    @OptIn(ExperimentalComposeUiApi::class)
+    @Test
+    fun coordinatesWhileAligningInLookaheadLayout() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        rule.setContent {
+            SimpleLookaheadLayout {
+                Row(Modifier.fillMaxSize()) {
+                    Box(Modifier.alignByBaseline()) {
+                        Text("Hello")
+                    }
+                    val content = @Composable { Text("World") }
+                    Layout(content, Modifier.alignByBaseline()) { measurables, constraints ->
+                        val p = measurables[0].measure(constraints)
+                        layout(p.width, p.height) {
+                            locations += coordinates
+                            p.place(0, 0)
+                        }
+                    }
+                }
+            }
+        }
+        rule.waitForIdle()
+        assertEquals(3, locations.size)
+        assertNull(locations[0]) // Lookahead pass
+        assertNotNull(locations[1]) // Lookahead pass
+        assertNotNull(locations[2]) // Measure pass
+    }
+
+    @Test
+    fun coordinatesInNestedAlignmentLookup() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        var textLayoutInvocations = 0
+        rule.setContent {
+            Row(Modifier.fillMaxSize()) {
+                Box(Modifier.alignByBaseline()) {
+                    Text("Hello", modifier = Modifier.layout { measurable, constraints ->
+                        val p = measurable.measure(constraints)
+                        layout(p.width, p.height) {
+                            textLayoutInvocations++
+                            p.place(0, 0)
+                        }
+                    })
+                }
+                val content = @Composable { Text("World") }
+                Layout(content,
+                    Modifier
+                        .alignByBaseline()
+                        .layout { measurable, constraints ->
+                            val p = measurable.measure(constraints)
+                            layout(p.width, p.height + 10) {
+                                p[LastBaseline] // invoke alignment
+                                p.place(0, 10)
+                            }
+                        }) { measurables, constraints ->
+                    val p = measurables[0].measure(constraints)
+                    layout(p.width, p.height) {
+                        locations += coordinates
+                        p.place(0, 0)
+                    }
+                }
+            }
+        }
+        rule.waitForIdle()
+        assertEquals(1, textLayoutInvocations)
+        assertTrue(locations.size > 1)
+        assertNull(locations[0])
+        assertNotNull(locations.last())
+    }
+
+    @Test
+    fun parentCoordateChangeCausesRelayout() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        var offset by mutableStateOf(DpOffset(0.dp, 0.dp))
+        rule.setContent {
+            Box(Modifier.fillMaxSize()) {
+                Box(Modifier.offset(offset.x, offset.y)) {
+                    Box(
+                        Modifier
+                            .layout { measurable, constraints ->
+                                val p = measurable.measure(constraints)
+                                layout(p.width, p.height) {
+                                    locations += coordinates
+                                    p.place(0, 0)
+                                }
+                            }
+                            .size(10.dp, 10.dp)
+                    )
+                }
+            }
+        }
+        rule.waitForIdle()
+        assertEquals(1, locations.size)
+
+        locations.clear()
+        offset = DpOffset(1.dp, 2.dp)
+        rule.waitForIdle()
+
+        assertEquals(1, locations.size)
+    }
+
+    @Test
+    fun grandParentCoordateChangeCausesRelayout() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        var offset by mutableStateOf(DpOffset(0.dp, 0.dp))
+        rule.setContent {
+            Box(Modifier.fillMaxSize()) {
+                Box(Modifier.offset(offset.x, offset.y)) {
+                    Box {
+                        Box(
+                            Modifier
+                                .layout { measurable, constraints ->
+                                    val p = measurable.measure(constraints)
+                                    layout(p.width, p.height) {
+                                        locations += coordinates
+                                        p.place(0, 0)
+                                    }
+                                }
+                                .size(10.dp, 10.dp)
+                        )
+                    }
+                }
+            }
+        }
+        rule.waitForIdle()
+        assertEquals(1, locations.size)
+
+        locations.clear()
+        offset = DpOffset(1.dp, 2.dp)
+        rule.waitForIdle()
+
+        assertEquals(1, locations.size)
+    }
+
+    @Test
+    fun newlyAddedStillUpdated() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        var offset by mutableStateOf(DpOffset(0.dp, 0.dp))
+        var showContent2 by mutableStateOf(false)
+        rule.setContent {
+            Box(Modifier.fillMaxSize()) {
+                Box(Modifier.offset(offset.x, offset.y)) {
+                    Box {
+                        Box(Modifier.fillMaxSize())
+                        if (showContent2) {
+                            Box(
+                                Modifier
+                                    .layout { measurable, constraints ->
+                                        val p = measurable.measure(constraints)
+                                        layout(p.width, p.height) {
+                                            locations += coordinates
+                                            p.place(0, 0)
+                                        }
+                                    }
+                                    .size(10.dp, 10.dp)
+                            )
+                        }
+                    }
+                }
+            }
+        }
+        rule.waitForIdle()
+        showContent2 = true
+        rule.waitForIdle()
+        assertEquals(1, locations.size)
+
+        locations.clear()
+        offset = DpOffset(1.dp, 2.dp)
+        rule.waitForIdle()
+
+        assertEquals(1, locations.size)
+    }
+
+    @Test
+    fun removedStopsUpdating() {
+        var readCoordinates by mutableStateOf(true)
+        val layoutCalls = mutableStateListOf<LayoutCoordinates?>()
+        var offset by mutableStateOf(DpOffset.Zero)
+        rule.setContent {
+            Box(Modifier.fillMaxSize()) {
+                Box(Modifier.offset(offset.x, offset.y)) {
+                    Box {
+                        Box(
+                            Modifier
+                                .layout { measurable, constraints ->
+                                    val p = measurable.measure(constraints)
+                                    layout(p.width, p.height) {
+                                        layoutCalls += if (readCoordinates) coordinates else null
+                                        p.place(0, 0)
+                                    }
+                                }
+                                .size(10.dp, 10.dp)
+                        )
+                    }
+                }
+            }
+        }
+        rule.waitForIdle()
+        assertEquals(1, layoutCalls.size)
+
+        layoutCalls.clear()
+        offset = DpOffset(10.dp, 5.dp)
+        rule.waitForIdle()
+
+        assertEquals(1, layoutCalls.size)
+
+        layoutCalls.clear()
+        readCoordinates = false
+        rule.waitForIdle()
+        assertEquals(1, layoutCalls.size)
+
+        layoutCalls.clear()
+        offset = DpOffset.Zero
+        rule.waitForIdle()
+
+        assertEquals(0, layoutCalls.size)
+    }
+
+    /**
+     * When a LayoutNode is moved, its usage of coordinates should follow.
+     */
+    @Test
+    fun movedContentNotifies() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        var offset1 by mutableStateOf(DpOffset.Zero)
+        var offset2 by mutableStateOf(DpOffset.Zero)
+        var showInOne by mutableStateOf(true)
+        rule.setContent {
+            val usingCoordinates = remember {
+                movableContentOf {
+                    Box(
+                        Modifier
+                            .layout { measurable, constraints ->
+                                val p = measurable.measure(constraints)
+                                layout(p.width, p.height) {
+                                    locations += coordinates
+                                    p.place(0, 0)
+                                }
+                            }
+                            .size(10.dp, 10.dp)
+                    )
+                }
+            }
+            Box(Modifier.fillMaxSize()) {
+                Box(
+                    Modifier
+                        .size(50.dp)
+                        .offset(offset1.x, offset1.y)
+                ) {
+                    if (showInOne) {
+                        usingCoordinates()
+                    }
+                }
+                Box(
+                    Modifier
+                        .size(50.dp)
+                        .offset(offset2.x, offset2.y)
+                ) {
+                    if (!showInOne) {
+                        usingCoordinates()
+                    }
+                }
+            }
+        }
+        rule.waitForIdle()
+        assertEquals(1, locations.size)
+        offset1 = DpOffset(1.dp, 1.dp)
+        rule.waitForIdle()
+        assertEquals(2, locations.size)
+        showInOne = false
+        rule.waitForIdle()
+        assertEquals(3, locations.size)
+        offset2 = DpOffset(1.dp, 1.dp)
+        rule.waitForIdle()
+        assertEquals(4, locations.size)
+    }
+
+    /**
+     * When [Placeable.PlacementScope.coordinates] is accessed during placement then changing the
+     * layer properties on an ancestor should cause relayout.
+     */
+    @Test
+    fun ancestorLayerChangesCausesPlacement() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        var offset by mutableStateOf(Offset.Zero)
+        rule.setContent {
+            Box(Modifier.fillMaxSize()) {
+                Box(Modifier.graphicsLayer {
+                    translationX = offset.x
+                    translationY = offset.y
+                }) {
+                    Box(
+                        Modifier
+                            .layout { measurable, constraints ->
+                                val p = measurable.measure(constraints)
+                                layout(p.width, p.height) {
+                                    locations += coordinates
+                                    p.place(0, 0)
+                                }
+                            }
+                            .size(10.dp, 10.dp)
+                    )
+                }
+            }
+        }
+        rule.waitForIdle()
+        assertEquals(1, locations.size)
+
+        offset = Offset(1f, 2f)
+        rule.waitForIdle()
+        assertEquals(2, locations.size)
+    }
+
+    /**
+     * When [Placeable.PlacementScope.coordinates] is accessed during placement then changing the
+     * layer properties of the LayoutNode should cause relayout.
+     */
+    @Test
+    fun layerChangesCausesPlacement() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        var offset by mutableStateOf(Offset.Zero)
+        rule.setContent {
+            Box(Modifier.fillMaxSize()) {
+                Box(
+                    Modifier
+                        .graphicsLayer {
+                            translationX = offset.x
+                            translationY = offset.y
+                        }
+                        .layout { measurable, constraints ->
+                            val p = measurable.measure(constraints)
+                            layout(p.width, p.height) {
+                                locations += coordinates
+                                p.place(0, 0)
+                            }
+                        }
+                        .size(10.dp, 10.dp)
+                )
+            }
+        }
+        rule.waitForIdle()
+        assertEquals(1, locations.size)
+
+        offset = Offset(1f, 2f)
+        rule.waitForIdle()
+        assertEquals(2, locations.size)
+    }
+
+    @Test
+    fun viewPositionChangeCausesPlacement() {
+        val locations = mutableStateListOf<LayoutCoordinates?>()
+        lateinit var composeView: ComposeView
+        rule.runOnUiThread {
+            val container = FrameLayout(rule.activity)
+
+            composeView = ComposeView(rule.activity).apply {
+                setContent {
+                    Box(
+                        Modifier
+                            .layout { measurable, constraints ->
+                                val p = measurable.measure(constraints)
+                                layout(p.width, p.height) {
+                                    locations += coordinates
+                                    p.place(0, 0)
+                                }
+                            }
+                            .size(10.dp)
+                    )
+                }
+            }
+            container.addView(
+                composeView,
+                FrameLayout.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT,
+                    Gravity.TOP or Gravity.LEFT
+                )
+            )
+            container.layoutParams = ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT
+            )
+            rule.activity.setContentView(container)
+        }
+
+        rule.waitForIdle()
+        locations.clear()
+
+        rule.runOnUiThread {
+            val lp = composeView.layoutParams as FrameLayout.LayoutParams
+            lp.gravity = Gravity.CENTER
+            composeView.layoutParams = lp
+        }
+        rule.waitForIdle()
+        assertEquals(1, locations.size)
+    }
+}
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+private fun SimpleLookaheadLayout(content: @Composable LookaheadLayoutScope.() -> Unit) {
+    LookaheadLayout(
+        content = content, measurePolicy = { measurables, constraints ->
+            val p = measurables[0].measure(constraints)
+            layout(p.width, p.height) {
+                p.place(0, 0)
+            }
+        }
+    )
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
index 532f0d5..aec4d3c 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
@@ -323,7 +323,7 @@
     override val hasPendingMeasureOrLayout
         get() = measureAndLayoutDelegate.hasPendingMeasureOrLayout
 
-    private var globalPosition: IntOffset = IntOffset.Zero
+    private var globalPosition: IntOffset = IntOffset(Int.MAX_VALUE, Int.MAX_VALUE)
 
     private val tmpPositionArray = intArrayOf(0, 0)
     private val viewToWindowMatrix = Matrix()
@@ -884,9 +884,13 @@
     private fun updatePositionCacheAndDispatch() {
         var positionChanged = false
         getLocationOnScreen(tmpPositionArray)
-        if (globalPosition.x != tmpPositionArray[0] || globalPosition.y != tmpPositionArray[1]) {
+        val (globalX, globalY) = globalPosition
+        if (globalX != tmpPositionArray[0] || globalY != tmpPositionArray[1]) {
             globalPosition = IntOffset(tmpPositionArray[0], tmpPositionArray[1])
-            positionChanged = true
+            if (globalX != Int.MAX_VALUE && globalY != Int.MAX_VALUE) {
+                positionChanged = true
+                root.layoutDelegate.measurePassDelegate.notifyChildrenUsingCoordinatesWhilePlacing()
+            }
         }
         measureAndLayoutDelegate.dispatchOnPositionedCallbacks(forceDispatch = positionChanged)
     }
diff --git a/compose/ui/ui/src/androidMain/res/values-af/strings.xml b/compose/ui/ui/src/androidMain/res/values-af/strings.xml
index 6e8b2c52..bec1e20 100644
--- a/compose/ui/ui/src/androidMain/res/values-af/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-af/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nie gemerk of ontmerk nie"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Aan"</string>
     <string name="off" msgid="875452955155264703">"Af"</string>
     <string name="selected" msgid="6043586758067023">"Gekies"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-am/strings.xml b/compose/ui/ui/src/androidMain/res/values-am/strings.xml
index 81b5d43..d4e13c5 100644
--- a/compose/ui/ui/src/androidMain/res/values-am/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-am/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"ምልክት የተደረገበትም ያልተደረገበትም"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"በርቷል"</string>
     <string name="off" msgid="875452955155264703">"ጠፍቷል"</string>
     <string name="selected" msgid="6043586758067023">"ተመርጧል"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ar/strings.xml b/compose/ui/ui/src/androidMain/res/values-ar/strings.xml
index d460e1f..e8e446d 100644
--- a/compose/ui/ui/src/androidMain/res/values-ar/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ar/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"لم يتم وضع علامة أو إزالتها"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"مفعّل"</string>
     <string name="off" msgid="875452955155264703">"غير مفعّل"</string>
     <string name="selected" msgid="6043586758067023">"محدّد"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-as/strings.xml b/compose/ui/ui/src/androidMain/res/values-as/strings.xml
index e59d517..e7bdb96 100644
--- a/compose/ui/ui/src/androidMain/res/values-as/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-as/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"টিক চিহ্ন দিয়াও নাই আঁতৰোৱাও নাই"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"অন কৰা আছে"</string>
     <string name="off" msgid="875452955155264703">"অফ আছে"</string>
     <string name="selected" msgid="6043586758067023">"বাছনি কৰা হৈছে"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-az/strings.xml b/compose/ui/ui/src/androidMain/res/values-az/strings.xml
index ee8dda4..5acabb9 100644
--- a/compose/ui/ui/src/androidMain/res/values-az/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-az/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nə seçilməyib, nə də seçim ləğv edilməyib"</string>
+    <string name="indeterminate" msgid="7933458017204019916">"Qismən yoxlanıb"</string>
     <string name="on" msgid="8655164131929253426">"Aktiv"</string>
     <string name="off" msgid="875452955155264703">"Deaktiv"</string>
     <string name="selected" msgid="6043586758067023">"Seçilib"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-b+sr+Latn/strings.xml b/compose/ui/ui/src/androidMain/res/values-b+sr+Latn/strings.xml
index 667cbcc..24ce253 100644
--- a/compose/ui/ui/src/androidMain/res/values-b+sr+Latn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-b+sr+Latn/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nije označeno niti poništeno"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Uključeno"</string>
     <string name="off" msgid="875452955155264703">"Isključeno"</string>
     <string name="selected" msgid="6043586758067023">"Izabrano"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-be/strings.xml b/compose/ui/ui/src/androidMain/res/values-be/strings.xml
index 0cc8894..d8ba140 100644
--- a/compose/ui/ui/src/androidMain/res/values-be/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-be/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Птушка не пастаўлена і не знята"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Уключана"</string>
     <string name="off" msgid="875452955155264703">"Выключана"</string>
     <string name="selected" msgid="6043586758067023">"Выбрана"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-bg/strings.xml b/compose/ui/ui/src/androidMain/res/values-bg/strings.xml
index ef623c7..4d8841d 100644
--- a/compose/ui/ui/src/androidMain/res/values-bg/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-bg/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Няма поставена или премахната отметка"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Вкл."</string>
     <string name="off" msgid="875452955155264703">"Изкл."</string>
     <string name="selected" msgid="6043586758067023">"Избрано"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-bn/strings.xml b/compose/ui/ui/src/androidMain/res/values-bn/strings.xml
index 307b248..1ba969f 100644
--- a/compose/ui/ui/src/androidMain/res/values-bn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-bn/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"টিক চিহ্ন দেওয়া বা টিকচিহ্ন সরান হয়নি"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"চালু আছে"</string>
     <string name="off" msgid="875452955155264703">"বন্ধ আছে"</string>
     <string name="selected" msgid="6043586758067023">"বেছে নেওয়া হয়েছে"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-bs/strings.xml b/compose/ui/ui/src/androidMain/res/values-bs/strings.xml
index b5c2913..f940559 100644
--- a/compose/ui/ui/src/androidMain/res/values-bs/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-bs/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nije odabrano ni poništeno"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Uključeno"</string>
     <string name="off" msgid="875452955155264703">"Isključeno"</string>
     <string name="selected" msgid="6043586758067023">"Odabrano"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ca/strings.xml b/compose/ui/ui/src/androidMain/res/values-ca/strings.xml
index e115375..4d001db 100644
--- a/compose/ui/ui/src/androidMain/res/values-ca/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ca/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ni marcada ni desmarcada"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Activat"</string>
     <string name="off" msgid="875452955155264703">"Desactivat"</string>
     <string name="selected" msgid="6043586758067023">"Seleccionat"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-cs/strings.xml b/compose/ui/ui/src/androidMain/res/values-cs/strings.xml
index 41b93fc..d02f4f8 100644
--- a/compose/ui/ui/src/androidMain/res/values-cs/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-cs/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ani zaškrtnuto, ani nezaškrtnuto"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Zapnuto"</string>
     <string name="off" msgid="875452955155264703">"Vypnuto"</string>
     <string name="selected" msgid="6043586758067023">"Vybráno"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-da/strings.xml b/compose/ui/ui/src/androidMain/res/values-da/strings.xml
index a58393a..f9d0a98 100644
--- a/compose/ui/ui/src/androidMain/res/values-da/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-da/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Hverken tilvalgt eller fravalgt"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Til"</string>
     <string name="off" msgid="875452955155264703">"Fra"</string>
     <string name="selected" msgid="6043586758067023">"Valgt"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-de/strings.xml b/compose/ui/ui/src/androidMain/res/values-de/strings.xml
index 9a1b88d..7a0e339 100644
--- a/compose/ui/ui/src/androidMain/res/values-de/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-de/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Häkchen weder gesetzt noch entfernt"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"An"</string>
     <string name="off" msgid="875452955155264703">"Aus"</string>
     <string name="selected" msgid="6043586758067023">"Ausgewählt"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-el/strings.xml b/compose/ui/ui/src/androidMain/res/values-el/strings.xml
index dd1f8c5..4f9e9b7 100644
--- a/compose/ui/ui/src/androidMain/res/values-el/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-el/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ούτε επιλεγμένο ούτε μη επιλεγμένο"</string>
+    <string name="indeterminate" msgid="7933458017204019916">"Έγινε μερικός έλεγχος"</string>
     <string name="on" msgid="8655164131929253426">"Ενεργό"</string>
     <string name="off" msgid="875452955155264703">"Ανενεργό"</string>
     <string name="selected" msgid="6043586758067023">"Επιλεγμένο"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-en-rAU/strings.xml b/compose/ui/ui/src/androidMain/res/values-en-rAU/strings.xml
index 9d4dfc6..3ba0ede 100644
--- a/compose/ui/ui/src/androidMain/res/values-en-rAU/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-en-rAU/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Neither ticked nor unticked"</string>
+    <string name="indeterminate" msgid="7933458017204019916">"Partially ticked"</string>
     <string name="on" msgid="8655164131929253426">"On"</string>
     <string name="off" msgid="875452955155264703">"Off"</string>
     <string name="selected" msgid="6043586758067023">"Selected"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-en-rCA/strings.xml b/compose/ui/ui/src/androidMain/res/values-en-rCA/strings.xml
index 9d4dfc6..3ba0ede 100644
--- a/compose/ui/ui/src/androidMain/res/values-en-rCA/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-en-rCA/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Neither ticked nor unticked"</string>
+    <string name="indeterminate" msgid="7933458017204019916">"Partially ticked"</string>
     <string name="on" msgid="8655164131929253426">"On"</string>
     <string name="off" msgid="875452955155264703">"Off"</string>
     <string name="selected" msgid="6043586758067023">"Selected"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-en-rGB/strings.xml b/compose/ui/ui/src/androidMain/res/values-en-rGB/strings.xml
index 9d4dfc6..3ba0ede 100644
--- a/compose/ui/ui/src/androidMain/res/values-en-rGB/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-en-rGB/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Neither ticked nor unticked"</string>
+    <string name="indeterminate" msgid="7933458017204019916">"Partially ticked"</string>
     <string name="on" msgid="8655164131929253426">"On"</string>
     <string name="off" msgid="875452955155264703">"Off"</string>
     <string name="selected" msgid="6043586758067023">"Selected"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-en-rIN/strings.xml b/compose/ui/ui/src/androidMain/res/values-en-rIN/strings.xml
index 9d4dfc6..3ba0ede 100644
--- a/compose/ui/ui/src/androidMain/res/values-en-rIN/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-en-rIN/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Neither ticked nor unticked"</string>
+    <string name="indeterminate" msgid="7933458017204019916">"Partially ticked"</string>
     <string name="on" msgid="8655164131929253426">"On"</string>
     <string name="off" msgid="875452955155264703">"Off"</string>
     <string name="selected" msgid="6043586758067023">"Selected"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-en-rXC/strings.xml b/compose/ui/ui/src/androidMain/res/values-en-rXC/strings.xml
index fc769fe..cdf7e28 100644
--- a/compose/ui/ui/src/androidMain/res/values-en-rXC/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-en-rXC/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎Neither checked nor unchecked‎‏‎‎‏‎"</string>
+    <string name="indeterminate" msgid="7933458017204019916">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‎‎Partially checked‎‏‎‎‏‎"</string>
     <string name="on" msgid="8655164131929253426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎On‎‏‎‎‏‎"</string>
     <string name="off" msgid="875452955155264703">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎Off‎‏‎‎‏‎"</string>
     <string name="selected" msgid="6043586758067023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎Selected‎‏‎‎‏‎"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-es-rUS/strings.xml b/compose/ui/ui/src/androidMain/res/values-es-rUS/strings.xml
index 105601f..2d63515 100644
--- a/compose/ui/ui/src/androidMain/res/values-es-rUS/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-es-rUS/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ni marcada ni desmarcada"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Sí"</string>
     <string name="off" msgid="875452955155264703">"No"</string>
     <string name="selected" msgid="6043586758067023">"Seleccionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-es/strings.xml b/compose/ui/ui/src/androidMain/res/values-es/strings.xml
index 4e2e0d6..5dfa6df 100644
--- a/compose/ui/ui/src/androidMain/res/values-es/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-es/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ni marcada ni desmarcada"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Activado"</string>
     <string name="off" msgid="875452955155264703">"Desactivado"</string>
     <string name="selected" msgid="6043586758067023">"Seleccionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-et/strings.xml b/compose/ui/ui/src/androidMain/res/values-et/strings.xml
index 2b32c19..6855b96 100644
--- a/compose/ui/ui/src/androidMain/res/values-et/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-et/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ei märgitud ega märkimata"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Sees"</string>
     <string name="off" msgid="875452955155264703">"Väljas"</string>
     <string name="selected" msgid="6043586758067023">"Valitud"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-eu/strings.xml b/compose/ui/ui/src/androidMain/res/values-eu/strings.xml
index 4750873..6858d16 100644
--- a/compose/ui/ui/src/androidMain/res/values-eu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-eu/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ez markatuta eta ez markatu gabe"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Aktibatuta"</string>
     <string name="off" msgid="875452955155264703">"Desaktibatuta"</string>
     <string name="selected" msgid="6043586758067023">"Hautatuta"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-fa/strings.xml b/compose/ui/ui/src/androidMain/res/values-fa/strings.xml
index 63bd852..a670069 100644
--- a/compose/ui/ui/src/androidMain/res/values-fa/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fa/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"نه علامت‌گذاری شده و نه علامت‌گذاری آن لغو شده"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"روشن"</string>
     <string name="off" msgid="875452955155264703">"خاموش"</string>
     <string name="selected" msgid="6043586758067023">"انتخاب شد"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-fi/strings.xml b/compose/ui/ui/src/androidMain/res/values-fi/strings.xml
index 5dac04c..a2946a5 100644
--- a/compose/ui/ui/src/androidMain/res/values-fi/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fi/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ei valittu eikä valitsematta"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Päällä"</string>
     <string name="off" msgid="875452955155264703">"Pois"</string>
     <string name="selected" msgid="6043586758067023">"Valittu"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-fr-rCA/strings.xml b/compose/ui/ui/src/androidMain/res/values-fr-rCA/strings.xml
index c22615d..c52e9d5 100644
--- a/compose/ui/ui/src/androidMain/res/values-fr-rCA/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fr-rCA/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ni cochée ni décochée"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Activé"</string>
     <string name="off" msgid="875452955155264703">"Désactivé"</string>
     <string name="selected" msgid="6043586758067023">"Sélectionné"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-fr/strings.xml b/compose/ui/ui/src/androidMain/res/values-fr/strings.xml
index 98c1df9..de53dd7 100644
--- a/compose/ui/ui/src/androidMain/res/values-fr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fr/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ni cochée, ni décochée"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Activé"</string>
     <string name="off" msgid="875452955155264703">"Désactivé"</string>
     <string name="selected" msgid="6043586758067023">"Sélectionné"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-gl/strings.xml b/compose/ui/ui/src/androidMain/res/values-gl/strings.xml
index a38f25e..795a358 100644
--- a/compose/ui/ui/src/androidMain/res/values-gl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-gl/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Non marcada nin desmarcada"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Activado"</string>
     <string name="off" msgid="875452955155264703">"Desactivado"</string>
     <string name="selected" msgid="6043586758067023">"Seleccionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-gu/strings.xml b/compose/ui/ui/src/androidMain/res/values-gu/strings.xml
index 5ad7da5..c9bb052 100644
--- a/compose/ui/ui/src/androidMain/res/values-gu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-gu/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"ન ચેક કરેલું કે ન અનચેક કરેલું"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"ચાલુ છે"</string>
     <string name="off" msgid="875452955155264703">"બંધ છે"</string>
     <string name="selected" msgid="6043586758067023">"પસંદગી કરી"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-hi/strings.xml b/compose/ui/ui/src/androidMain/res/values-hi/strings.xml
index 5fb581f..5d7d0b2 100644
--- a/compose/ui/ui/src/androidMain/res/values-hi/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hi/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"चेकबॉक्स पर न तो सही का निशान लगाया गया, न ही पहले से लगा सही का निशान हटाया गया"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"चालू है"</string>
     <string name="off" msgid="875452955155264703">"बंद है"</string>
     <string name="selected" msgid="6043586758067023">"चुना गया"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-hr/strings.xml b/compose/ui/ui/src/androidMain/res/values-hr/strings.xml
index fec0a12..82e083c 100644
--- a/compose/ui/ui/src/androidMain/res/values-hr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hr/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nije označeno ni poništeno"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Uključeno"</string>
     <string name="off" msgid="875452955155264703">"Isključeno"</string>
     <string name="selected" msgid="6043586758067023">"Odabrano"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-hu/strings.xml b/compose/ui/ui/src/androidMain/res/values-hu/strings.xml
index 5ce0e68..bc13636 100644
--- a/compose/ui/ui/src/androidMain/res/values-hu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hu/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Sem bejelöléssel, sem bejelölés nélkül"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Be"</string>
     <string name="off" msgid="875452955155264703">"Ki"</string>
     <string name="selected" msgid="6043586758067023">"Kijelölve"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-hy/strings.xml b/compose/ui/ui/src/androidMain/res/values-hy/strings.xml
index 815516f..cdc836c 100644
--- a/compose/ui/ui/src/androidMain/res/values-hy/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hy/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Նշումը ո՛չ դրված է, ո՛չ էլ հանված"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Միացված է"</string>
     <string name="off" msgid="875452955155264703">"Անջատված է"</string>
     <string name="selected" msgid="6043586758067023">"Ընտրված է"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-in/strings.xml b/compose/ui/ui/src/androidMain/res/values-in/strings.xml
index 0d28a55..a47444c 100644
--- a/compose/ui/ui/src/androidMain/res/values-in/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-in/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Tidak dicentang atau dihapus centangnya"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Aktif"</string>
     <string name="off" msgid="875452955155264703">"Nonaktif"</string>
     <string name="selected" msgid="6043586758067023">"Dipilih"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-is/strings.xml b/compose/ui/ui/src/androidMain/res/values-is/strings.xml
index e505dd6..b7f7b05 100644
--- a/compose/ui/ui/src/androidMain/res/values-is/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-is/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Hvorki merkt né afmerkt"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Kveikt"</string>
     <string name="off" msgid="875452955155264703">"Slökkt"</string>
     <string name="selected" msgid="6043586758067023">"Valið"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-it/strings.xml b/compose/ui/ui/src/androidMain/res/values-it/strings.xml
index 23a93d8..414acd8 100644
--- a/compose/ui/ui/src/androidMain/res/values-it/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-it/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Casella di controllo né selezionata né deselezionata"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"On"</string>
     <string name="off" msgid="875452955155264703">"Off"</string>
     <string name="selected" msgid="6043586758067023">"Elemento selezionato"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-iw/strings.xml b/compose/ui/ui/src/androidMain/res/values-iw/strings.xml
index 887ec62..2cdab54 100644
--- a/compose/ui/ui/src/androidMain/res/values-iw/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-iw/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"עם סימון ובלי סימון"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"פועל"</string>
     <string name="off" msgid="875452955155264703">"כבוי"</string>
     <string name="selected" msgid="6043586758067023">"נבחר"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ja/strings.xml b/compose/ui/ui/src/androidMain/res/values-ja/strings.xml
index aaedf66..24c7609 100644
--- a/compose/ui/ui/src/androidMain/res/values-ja/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ja/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"オンでもオフでもありません"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"オン"</string>
     <string name="off" msgid="875452955155264703">"オフ"</string>
     <string name="selected" msgid="6043586758067023">"選択済み"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ka/strings.xml b/compose/ui/ui/src/androidMain/res/values-ka/strings.xml
index fd9c8fc..7875bad 100644
--- a/compose/ui/ui/src/androidMain/res/values-ka/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ka/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"არც მონიშნულია და არც მოუნიშნავი"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"ჩართული"</string>
     <string name="off" msgid="875452955155264703">"გამორთული"</string>
     <string name="selected" msgid="6043586758067023">"არჩეული"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-kk/strings.xml b/compose/ui/ui/src/androidMain/res/values-kk/strings.xml
index 3654be7..f30b035 100644
--- a/compose/ui/ui/src/androidMain/res/values-kk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-kk/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Белгіленбеді немесе белгісі алынбады"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Қосулы"</string>
     <string name="off" msgid="875452955155264703">"Өшірулі"</string>
     <string name="selected" msgid="6043586758067023">"Таңдалды"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-km/strings.xml b/compose/ui/ui/src/androidMain/res/values-km/strings.xml
index 00ee74c..58b175d 100644
--- a/compose/ui/ui/src/androidMain/res/values-km/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-km/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"មិនបានធីក ហើយក៏មិន​បានដោះធីកដែរ"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"បើក"</string>
     <string name="off" msgid="875452955155264703">"បិទ"</string>
     <string name="selected" msgid="6043586758067023">"បានជ្រើសរើស"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-kn/strings.xml b/compose/ui/ui/src/androidMain/res/values-kn/strings.xml
index b45702b..8850e24 100644
--- a/compose/ui/ui/src/androidMain/res/values-kn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-kn/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"ಗುರುತಿಸಲಾಗಿಲ್ಲ ಅಥವಾ ಗುರುತಿಸಿರುವುದನ್ನು ತೆಗೆಯಲಾಗಿಲ್ಲ"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"ಆನ್ ಆಗಿದೆ"</string>
     <string name="off" msgid="875452955155264703">"ಆಫ್ ಆಗಿದೆ"</string>
     <string name="selected" msgid="6043586758067023">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ko/strings.xml b/compose/ui/ui/src/androidMain/res/values-ko/strings.xml
index 1cb7b71..875fec1 100644
--- a/compose/ui/ui/src/androidMain/res/values-ko/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ko/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"선택 또는 선택 해제되지 않음"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"켜짐"</string>
     <string name="off" msgid="875452955155264703">"꺼짐"</string>
     <string name="selected" msgid="6043586758067023">"선택됨"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ky/strings.xml b/compose/ui/ui/src/androidMain/res/values-ky/strings.xml
index ff805e15..739a6b3 100644
--- a/compose/ui/ui/src/androidMain/res/values-ky/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ky/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Белгиленген же белгиленбеген эмес"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Күйүк"</string>
     <string name="off" msgid="875452955155264703">"Өчүк"</string>
     <string name="selected" msgid="6043586758067023">"Тандалды"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-lo/strings.xml b/compose/ui/ui/src/androidMain/res/values-lo/strings.xml
index 4bae605..0935bf6 100644
--- a/compose/ui/ui/src/androidMain/res/values-lo/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-lo/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"ບໍ່ໄດ້ເລືອກ ຫຼື ຍົກເລີກການເລືອກແລ້ວ"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"ເປີດ"</string>
     <string name="off" msgid="875452955155264703">"ປິດ"</string>
     <string name="selected" msgid="6043586758067023">"ເລືອກແລ້ວ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-lt/strings.xml b/compose/ui/ui/src/androidMain/res/values-lt/strings.xml
index 6f3ebd5..1e0f248 100644
--- a/compose/ui/ui/src/androidMain/res/values-lt/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-lt/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nei pažymėta, nei nepažymėta"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Įjungta"</string>
     <string name="off" msgid="875452955155264703">"Išjungta"</string>
     <string name="selected" msgid="6043586758067023">"Pasirinkta"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-lv/strings.xml b/compose/ui/ui/src/androidMain/res/values-lv/strings.xml
index 72971d7..8c533528 100644
--- a/compose/ui/ui/src/androidMain/res/values-lv/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-lv/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nav ne atzīmēta, ne neatzīmēta"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Ieslēgts"</string>
     <string name="off" msgid="875452955155264703">"Izslēgts"</string>
     <string name="selected" msgid="6043586758067023">"Atlasīts"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-mk/strings.xml b/compose/ui/ui/src/androidMain/res/values-mk/strings.xml
index dd4a1c8..0c5ed6e 100644
--- a/compose/ui/ui/src/androidMain/res/values-mk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-mk/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ниту штиклирано, ниту отштиклирано"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Вклучено"</string>
     <string name="off" msgid="875452955155264703">"Исклучено"</string>
     <string name="selected" msgid="6043586758067023">"Избрано"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ml/strings.xml b/compose/ui/ui/src/androidMain/res/values-ml/strings.xml
index 81c9d0f..f4d88e8 100644
--- a/compose/ui/ui/src/androidMain/res/values-ml/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ml/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"ചെക്ക് മാർക്കിടുകയോ അൺചെക്ക് ചെയ്യുകയോ ചെയ്തിട്ടില്ല"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"ഓണാണ്"</string>
     <string name="off" msgid="875452955155264703">"ഓഫാണ്"</string>
     <string name="selected" msgid="6043586758067023">"തിരഞ്ഞെടുത്തു"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-mn/strings.xml b/compose/ui/ui/src/androidMain/res/values-mn/strings.xml
index bee6723..ca6f341 100644
--- a/compose/ui/ui/src/androidMain/res/values-mn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-mn/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Тэмдэглэсэн, сонголтыг болиулаагүйн аль нь ч биш"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Асаалттай"</string>
     <string name="off" msgid="875452955155264703">"Унтраалттай"</string>
     <string name="selected" msgid="6043586758067023">"Сонгосон"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-mr/strings.xml b/compose/ui/ui/src/androidMain/res/values-mr/strings.xml
index d040eed..73cf75d 100644
--- a/compose/ui/ui/src/androidMain/res/values-mr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-mr/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"ना खूण केलेला ना खून काढून टाकलेला"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"सुरू आहे"</string>
     <string name="off" msgid="875452955155264703">"बंद आहे"</string>
     <string name="selected" msgid="6043586758067023">"निवडला"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ms/strings.xml b/compose/ui/ui/src/androidMain/res/values-ms/strings.xml
index 4a1b34e8..7a99bb2 100644
--- a/compose/ui/ui/src/androidMain/res/values-ms/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ms/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Berkecuali"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Hidup"</string>
     <string name="off" msgid="875452955155264703">"Mati"</string>
     <string name="selected" msgid="6043586758067023">"Dipilih"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-my/strings.xml b/compose/ui/ui/src/androidMain/res/values-my/strings.xml
index 441a625..ba757fb 100644
--- a/compose/ui/ui/src/androidMain/res/values-my/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-my/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"အမှတ်ခြစ်မထားပါ (သို့) အမှတ်ခြစ်ပြီး"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"ဖွင့်"</string>
     <string name="off" msgid="875452955155264703">"ပိတ်"</string>
     <string name="selected" msgid="6043586758067023">"ရွေးထားသည်"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-nb/strings.xml b/compose/ui/ui/src/androidMain/res/values-nb/strings.xml
index de3de7e..6c5bc69 100644
--- a/compose/ui/ui/src/androidMain/res/values-nb/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-nb/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Verken avmerket eller ikke avmerket"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"På"</string>
     <string name="off" msgid="875452955155264703">"Av"</string>
     <string name="selected" msgid="6043586758067023">"Valgt"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ne/strings.xml b/compose/ui/ui/src/androidMain/res/values-ne/strings.xml
index 73c7bb5..cb332e7 100644
--- a/compose/ui/ui/src/androidMain/res/values-ne/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ne/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"न त चिन्ह लगाइएको छ न नै चिन्ह हटाइएको छ"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"अन छ"</string>
     <string name="off" msgid="875452955155264703">"अफ छ"</string>
     <string name="selected" msgid="6043586758067023">"चयन गरिएको छ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-nl/strings.xml b/compose/ui/ui/src/androidMain/res/values-nl/strings.xml
index aa9dd5c..d1428af 100644
--- a/compose/ui/ui/src/androidMain/res/values-nl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-nl/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Niet aangevinkt of uitgevinkt"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Aan"</string>
     <string name="off" msgid="875452955155264703">"Uit"</string>
     <string name="selected" msgid="6043586758067023">"Geselecteerd"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-or/strings.xml b/compose/ui/ui/src/androidMain/res/values-or/strings.xml
index 9a3fc13..0d82f58 100644
--- a/compose/ui/ui/src/androidMain/res/values-or/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-or/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"ଚେକ୍ କରାଯାଇନାହିଁ କି ଅନଚେକ୍ କରାଯାଇନାହିଁ"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"ଚାଲୁ ଅଛି"</string>
     <string name="off" msgid="875452955155264703">"ବନ୍ଦ ଅଛି"</string>
     <string name="selected" msgid="6043586758067023">"ଚୟନିତ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-pa/strings.xml b/compose/ui/ui/src/androidMain/res/values-pa/strings.xml
index c58a426..c1e3b7d 100644
--- a/compose/ui/ui/src/androidMain/res/values-pa/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pa/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"ਨਾ ਨਿਸ਼ਾਨ ਲਗਾਇਆ ਗਿਆ ਨਾ ਹੀ ਨਿਸ਼ਾਨ ਹਟਾਇਆ ਗਿਆ"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"ਚਾਲੂ ਹੈ"</string>
     <string name="off" msgid="875452955155264703">"ਬੰਦ ਹੈ"</string>
     <string name="selected" msgid="6043586758067023">"ਚੁਣੀ ਗਈ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-pl/strings.xml b/compose/ui/ui/src/androidMain/res/values-pl/strings.xml
index 4c08550..75aa49e 100644
--- a/compose/ui/ui/src/androidMain/res/values-pl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pl/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ani nie zaznaczono, ani nie odznaczono"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Włączono"</string>
     <string name="off" msgid="875452955155264703">"Wyłączono"</string>
     <string name="selected" msgid="6043586758067023">"Wybrano"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-pt-rBR/strings.xml b/compose/ui/ui/src/androidMain/res/values-pt-rBR/strings.xml
index 737a848..d0607fd 100644
--- a/compose/ui/ui/src/androidMain/res/values-pt-rBR/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pt-rBR/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nem marcada nem desmarcada"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Ativado"</string>
     <string name="off" msgid="875452955155264703">"Desativado"</string>
     <string name="selected" msgid="6043586758067023">"Selecionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-pt-rPT/strings.xml b/compose/ui/ui/src/androidMain/res/values-pt-rPT/strings.xml
index 48cd28d..14c7c04 100644
--- a/compose/ui/ui/src/androidMain/res/values-pt-rPT/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pt-rPT/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nem selecionada nem desselecionada"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Ativado"</string>
     <string name="off" msgid="875452955155264703">"Desativado"</string>
     <string name="selected" msgid="6043586758067023">"Selecionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-pt/strings.xml b/compose/ui/ui/src/androidMain/res/values-pt/strings.xml
index 737a848..d0607fd 100644
--- a/compose/ui/ui/src/androidMain/res/values-pt/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pt/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nem marcada nem desmarcada"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Ativado"</string>
     <string name="off" msgid="875452955155264703">"Desativado"</string>
     <string name="selected" msgid="6043586758067023">"Selecionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ro/strings.xml b/compose/ui/ui/src/androidMain/res/values-ro/strings.xml
index e7d00c8..38108f8 100644
--- a/compose/ui/ui/src/androidMain/res/values-ro/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ro/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Nici bifată, nici debifată"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Activat"</string>
     <string name="off" msgid="875452955155264703">"Dezactivat"</string>
     <string name="selected" msgid="6043586758067023">"Selectat"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ru/strings.xml b/compose/ui/ui/src/androidMain/res/values-ru/strings.xml
index a5ef474..1928e3f 100644
--- a/compose/ui/ui/src/androidMain/res/values-ru/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ru/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Флажок не установлен и не снят"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Включено"</string>
     <string name="off" msgid="875452955155264703">"Отключено"</string>
     <string name="selected" msgid="6043586758067023">"Выбрано"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-si/strings.xml b/compose/ui/ui/src/androidMain/res/values-si/strings.xml
index 5ad02b8..13ffe02 100644
--- a/compose/ui/ui/src/androidMain/res/values-si/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-si/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"හරි ලකුණු යොදා හෝ හරි ලකුණු යෙදීම ඉවත් කර නැත"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"ක්‍රියාත්මකයි"</string>
     <string name="off" msgid="875452955155264703">"ක්‍රියාවිරහිතයි"</string>
     <string name="selected" msgid="6043586758067023">"තෝරන ලදි"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sk/strings.xml b/compose/ui/ui/src/androidMain/res/values-sk/strings.xml
index 6a65be8..c068e63 100644
--- a/compose/ui/ui/src/androidMain/res/values-sk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sk/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ani začiarknuté ani nezačiarknuté"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Zapnuté"</string>
     <string name="off" msgid="875452955155264703">"Vypnuté"</string>
     <string name="selected" msgid="6043586758067023">"Vybrané"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sl/strings.xml b/compose/ui/ui/src/androidMain/res/values-sl/strings.xml
index f4489d9..067183a 100644
--- a/compose/ui/ui/src/androidMain/res/values-sl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sl/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Niti potrjeno niti nepotrjeno"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Vklopljeno"</string>
     <string name="off" msgid="875452955155264703">"Izklopljeno"</string>
     <string name="selected" msgid="6043586758067023">"Izbrano"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sq/strings.xml b/compose/ui/ui/src/androidMain/res/values-sq/strings.xml
index a01f398..0f3cf23 100644
--- a/compose/ui/ui/src/androidMain/res/values-sq/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sq/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"As e zgjedhur as e pazgjedhur"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Aktiv"</string>
     <string name="off" msgid="875452955155264703">"Joaktiv"</string>
     <string name="selected" msgid="6043586758067023">"Zgjedhur"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sr/strings.xml b/compose/ui/ui/src/androidMain/res/values-sr/strings.xml
index b288103..f761f7e 100644
--- a/compose/ui/ui/src/androidMain/res/values-sr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sr/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Није означено нити поништено"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Укључено"</string>
     <string name="off" msgid="875452955155264703">"Искључено"</string>
     <string name="selected" msgid="6043586758067023">"Изабрано"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sv/strings.xml b/compose/ui/ui/src/androidMain/res/values-sv/strings.xml
index a95b01d..8843eba 100644
--- a/compose/ui/ui/src/androidMain/res/values-sv/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sv/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Varken markerad eller avmarkerad"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"På"</string>
     <string name="off" msgid="875452955155264703">"Av"</string>
     <string name="selected" msgid="6043586758067023">"Valt"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sw/strings.xml b/compose/ui/ui/src/androidMain/res/values-sw/strings.xml
index b2eac80..9a960fe 100644
--- a/compose/ui/ui/src/androidMain/res/values-sw/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sw/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Hujateua wala kubatilisha uteuzi"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Imewashwa"</string>
     <string name="off" msgid="875452955155264703">"Imezimwa"</string>
     <string name="selected" msgid="6043586758067023">"Umechagua"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ta/strings.xml b/compose/ui/ui/src/androidMain/res/values-ta/strings.xml
index 246c216..b8e6df2 100644
--- a/compose/ui/ui/src/androidMain/res/values-ta/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ta/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"தேர்வுசெய்யப்பட்டது மற்றும் தேர்வுசெய்யப்படாதது"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"இயக்கப்பட்டுள்ளது"</string>
     <string name="off" msgid="875452955155264703">"முடக்கப்பட்டுள்ளது"</string>
     <string name="selected" msgid="6043586758067023">"தேர்ந்தெடுக்கப்பட்டுள்ளது"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-te/strings.xml b/compose/ui/ui/src/androidMain/res/values-te/strings.xml
index fce8a54..7f1588e 100644
--- a/compose/ui/ui/src/androidMain/res/values-te/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-te/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"చెక్ చేయబడలేదు అలాగే చెక్ చేయబడదు"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"ఆన్‌లో ఉంది"</string>
     <string name="off" msgid="875452955155264703">"ఆఫ్‌లో ఉంది"</string>
     <string name="selected" msgid="6043586758067023">"ఎంచుకోబడింది"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-th/strings.xml b/compose/ui/ui/src/androidMain/res/values-th/strings.xml
index be43403..3d9e8cb 100644
--- a/compose/ui/ui/src/androidMain/res/values-th/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-th/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"ไม่ได้เลือกหรือยกเลิกการเลือก"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"เปิด"</string>
     <string name="off" msgid="875452955155264703">"ปิด"</string>
     <string name="selected" msgid="6043586758067023">"เลือกไว้"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-tl/strings.xml b/compose/ui/ui/src/androidMain/res/values-tl/strings.xml
index 073a722..489df2b 100644
--- a/compose/ui/ui/src/androidMain/res/values-tl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-tl/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Hindi may check o walang check"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Naka-on"</string>
     <string name="off" msgid="875452955155264703">"Naka-off"</string>
     <string name="selected" msgid="6043586758067023">"Napili"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-tr/strings.xml b/compose/ui/ui/src/androidMain/res/values-tr/strings.xml
index 738e1a1..d51a068 100644
--- a/compose/ui/ui/src/androidMain/res/values-tr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-tr/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Ne işaretli ne de işaretli değil"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Açık"</string>
     <string name="off" msgid="875452955155264703">"Kapalı"</string>
     <string name="selected" msgid="6043586758067023">"Seçili"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-uk/strings.xml b/compose/ui/ui/src/androidMain/res/values-uk/strings.xml
index eb77ff6..d7656ee 100644
--- a/compose/ui/ui/src/androidMain/res/values-uk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-uk/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Прапорець ані поставлено, ані знято"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Увімкнено"</string>
     <string name="off" msgid="875452955155264703">"Вимкнено"</string>
     <string name="selected" msgid="6043586758067023">"Вибрано"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ur/strings.xml b/compose/ui/ui/src/androidMain/res/values-ur/strings.xml
index 0406005..fcb05a7 100644
--- a/compose/ui/ui/src/androidMain/res/values-ur/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ur/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"نہ تو نشان زد کیا گیا نہ ہی نشان ہٹایا گیا"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"آن ہے"</string>
     <string name="off" msgid="875452955155264703">"آف ہے"</string>
     <string name="selected" msgid="6043586758067023">"منتخب کردہ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-uz/strings.xml b/compose/ui/ui/src/androidMain/res/values-uz/strings.xml
index 68df94a..2ec8ad47 100644
--- a/compose/ui/ui/src/androidMain/res/values-uz/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-uz/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Belgilanmagan va belgi olib tashlanmagan"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Yoniq"</string>
     <string name="off" msgid="875452955155264703">"Oʻchiq"</string>
     <string name="selected" msgid="6043586758067023">"Tanlangan"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-vi/strings.xml b/compose/ui/ui/src/androidMain/res/values-vi/strings.xml
index 99bd579..a95de5d 100644
--- a/compose/ui/ui/src/androidMain/res/values-vi/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-vi/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Không đánh dấu cũng không bỏ đánh dấu"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Đang bật"</string>
     <string name="off" msgid="875452955155264703">"Đang tắt"</string>
     <string name="selected" msgid="6043586758067023">"Đã chọn"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-zh-rCN/strings.xml b/compose/ui/ui/src/androidMain/res/values-zh-rCN/strings.xml
index e6f5a19..64da085 100644
--- a/compose/ui/ui/src/androidMain/res/values-zh-rCN/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-zh-rCN/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"既没有勾选也没有取消选中"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"已开启"</string>
     <string name="off" msgid="875452955155264703">"已关闭"</string>
     <string name="selected" msgid="6043586758067023">"已选择"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-zh-rHK/strings.xml b/compose/ui/ui/src/androidMain/res/values-zh-rHK/strings.xml
index 6291f69..f3153ed 100644
--- a/compose/ui/ui/src/androidMain/res/values-zh-rHK/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-zh-rHK/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"未勾選或者未取消勾選"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"開"</string>
     <string name="off" msgid="875452955155264703">"閂"</string>
     <string name="selected" msgid="6043586758067023">"揀咗"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-zh-rTW/strings.xml b/compose/ui/ui/src/androidMain/res/values-zh-rTW/strings.xml
index 22304ae..4b3e173 100644
--- a/compose/ui/ui/src/androidMain/res/values-zh-rTW/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-zh-rTW/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"沒有勾選也沒有取消勾選"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"已開啟"</string>
     <string name="off" msgid="875452955155264703">"已關閉"</string>
     <string name="selected" msgid="6043586758067023">"已選取"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-zu/strings.xml b/compose/ui/ui/src/androidMain/res/values-zu/strings.xml
index e887688..0394f97 100644
--- a/compose/ui/ui/src/androidMain/res/values-zu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-zu/strings.xml
@@ -17,7 +17,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="indeterminate" msgid="2486394087603402002">"Lutho oluhloliwe nolungahlolwanga"</string>
+    <!-- no translation found for indeterminate (7933458017204019916) -->
+    <skip />
     <string name="on" msgid="8655164131929253426">"Vuliwe"</string>
     <string name="off" msgid="875452955155264703">"Valiwe"</string>
     <string name="selected" msgid="6043586758067023">"Okukhethiwe"</string>
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/MeasureScope.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/MeasureScope.kt
index ea86547..74188cd 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/MeasureScope.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/MeasureScope.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.layout
 
+import androidx.compose.ui.node.LookaheadCapablePlaceable
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.internal.JvmDefaultWithCompatibility
 
@@ -52,6 +53,7 @@
             Placeable.PlacementScope.executeWithRtlMirroringValues(
                 width,
                 layoutDirection,
+                this@MeasureScope as? LookaheadCapablePlaceable,
                 placementBlock
             )
         }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
index 59b613b..f6d24c3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
@@ -16,7 +16,11 @@
 
 package androidx.compose.ui.layout
 
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.graphics.GraphicsLayerScope
+import androidx.compose.ui.node.LayoutNodeLayoutDelegate
+import androidx.compose.ui.node.LayoutNodeWrapper
+import androidx.compose.ui.node.LookaheadCapablePlaceable
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
@@ -148,6 +152,22 @@
         protected abstract val parentLayoutDirection: LayoutDirection
 
         /**
+         * The [LayoutCoordinates] of this layout, if known or `null` if the layout hasn't been
+         * placed yet. [coordinates] will be `null` when determining alignment lines, preventing
+         * alignment lines from depending on absolute coordinates.
+         *
+         * When [coordinates] is `null`, there will always be a follow-up placement call in which
+         * [coordinates] is not-`null`.
+         *
+         * @sample androidx.compose.ui.samples.PlacementScopeCoordinatesSample
+         */
+        @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+        @ExperimentalComposeUiApi
+        @get:ExperimentalComposeUiApi
+        open val coordinates: LayoutCoordinates?
+            get() = null
+
+        /**
          * Place a [Placeable] at [position] in its parent's coordinate system.
          * If the layout direction is right-to-left, the given [position] will be horizontally
          * mirrored so that the position of the [Placeable] implicitly reacts to RTL layout
@@ -320,19 +340,74 @@
                 private set
             override var parentWidth = 0
                 private set
+            private var _coordinates: LayoutCoordinates? = null
+
+            @ExperimentalComposeUiApi
+            override val coordinates: LayoutCoordinates?
+                get() {
+                    layoutDelegate?.coordinatesAccessedDuringPlacement = true
+                    return _coordinates
+                }
+
+            private var layoutDelegate: LayoutNodeLayoutDelegate? = null
 
             inline fun executeWithRtlMirroringValues(
                 parentWidth: Int,
                 parentLayoutDirection: LayoutDirection,
+                lookaheadCapablePlaceable: LookaheadCapablePlaceable?,
                 crossinline block: PlacementScope.() -> Unit
             ) {
+                val previousLayoutCoordinates = _coordinates
                 val previousParentWidth = Companion.parentWidth
                 val previousParentLayoutDirection = Companion.parentLayoutDirection
+                val previousLayoutDelegate = layoutDelegate
                 Companion.parentWidth = parentWidth
                 Companion.parentLayoutDirection = parentLayoutDirection
+                val wasPlacingForAlignment =
+                    configureForPlacingForAlignment(lookaheadCapablePlaceable)
                 this.block()
+                lookaheadCapablePlaceable?.isPlacingForAlignment = wasPlacingForAlignment
                 Companion.parentWidth = previousParentWidth
                 Companion.parentLayoutDirection = previousParentLayoutDirection
+                _coordinates = previousLayoutCoordinates
+                layoutDelegate = previousLayoutDelegate
+            }
+
+            /**
+             * Configures [_coordinates] and [layoutDelegate] based on the [scope].
+             * When it is [LayoutNodeWrapper.isPlacingForAlignment], then [_coordinates] should
+             * be `null`, and when [coordinates] is accessed, it indicates that the placement
+             * should not be finalized. When [LayoutNodeWrapper.isShallowPlacing], then
+             * [_coordinates] should be `null`, but we don't have to do anything else
+             * to trigger relayout because shallow placing will replace again anyway.
+             *
+             * [LayoutNodeWrapper.isPlacingForAlignment] will be set to true if its parent's
+             * value is `true`.
+             *
+             * @return the value for [LayoutNodeWrapper.isPlacingForAlignment] that should
+             * be set after completing the lambda.
+             */
+            private fun configureForPlacingForAlignment(
+                scope: LookaheadCapablePlaceable?
+            ): Boolean {
+                val wasPlacingForAlignment: Boolean
+                if (scope == null) {
+                    _coordinates = null
+                    layoutDelegate = null
+                    wasPlacingForAlignment = false
+                } else {
+                    wasPlacingForAlignment = scope.isPlacingForAlignment
+                    if (scope.parent?.isPlacingForAlignment == true) {
+                        scope.isPlacingForAlignment = true
+                    }
+                    layoutDelegate = scope.layoutNode.layoutDelegate
+                    if (scope.isPlacingForAlignment || scope.isShallowPlacing) {
+                        _coordinates = null
+                    } else {
+                        _coordinates = scope.coordinates
+                    }
+                }
+                return wasPlacingForAlignment
             }
         }
     }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
index 98aa13f..64d6c57 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
@@ -24,17 +24,14 @@
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.PaintingStyle
 import androidx.compose.ui.layout.AlignmentLine
-import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.LookaheadScope
+import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
 
 internal class InnerPlaceable(
     layoutNode: LayoutNode
-) : LayoutNodeWrapper(layoutNode), Density by layoutNode.measureScope {
-
-    override val measureScope get() = layoutNode.measureScope
+) : LayoutNodeWrapper(layoutNode) {
 
     private inner class LookaheadDelegateImpl(
         scope: LookaheadScope
@@ -48,7 +45,7 @@
                     it.measuredByParentInLookahead = LayoutNode.UsageByParent.NotUsed
                 }
                 val measureResult = with(layoutNode.measurePolicy) {
-                    layoutNode.measureScope.measure(
+                    measure(
                         layoutNode.childLookaheadMeasurables,
                         constraints
                     )
@@ -92,7 +89,7 @@
         }
 
         measureResult = with(layoutNode.measurePolicy) {
-            layoutNode.measureScope.measure(layoutNode.childMeasurables, constraints)
+            measure(layoutNode.childMeasurables, constraints)
         }
         onMeasured()
         return this
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/IntrinsicsPolicy.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/IntrinsicsPolicy.kt
index fd57154..7fa410c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/IntrinsicsPolicy.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/IntrinsicsPolicy.kt
@@ -34,35 +34,47 @@
     }
 
     fun minIntrinsicWidth(height: Int) = with(measurePolicyFromState()) {
-        layoutNode.measureScope.minIntrinsicWidth(layoutNode.childMeasurables, height)
+        layoutNode.outerLayoutNodeWrapper.minIntrinsicWidth(layoutNode.childMeasurables, height)
     }
 
     fun minIntrinsicHeight(width: Int) = with(measurePolicyFromState()) {
-        layoutNode.measureScope.minIntrinsicHeight(layoutNode.childMeasurables, width)
+        layoutNode.outerLayoutNodeWrapper.minIntrinsicHeight(layoutNode.childMeasurables, width)
     }
 
     fun maxIntrinsicWidth(height: Int) = with(measurePolicyFromState()) {
-        layoutNode.measureScope.maxIntrinsicWidth(layoutNode.childMeasurables, height)
+        layoutNode.outerLayoutNodeWrapper.maxIntrinsicWidth(layoutNode.childMeasurables, height)
     }
 
     fun maxIntrinsicHeight(width: Int) = with(measurePolicyFromState()) {
-        layoutNode.measureScope.maxIntrinsicHeight(layoutNode.childMeasurables, width)
+        layoutNode.outerLayoutNodeWrapper.maxIntrinsicHeight(layoutNode.childMeasurables, width)
     }
 
     fun minLookaheadIntrinsicWidth(height: Int) = with(measurePolicyFromState()) {
-        layoutNode.measureScope.minIntrinsicWidth(layoutNode.childLookaheadMeasurables, height)
+        layoutNode.outerLayoutNodeWrapper.minIntrinsicWidth(
+            layoutNode.childLookaheadMeasurables,
+            height
+        )
     }
 
     fun minLookaheadIntrinsicHeight(width: Int) = with(measurePolicyFromState()) {
-        layoutNode.measureScope.minIntrinsicHeight(layoutNode.childLookaheadMeasurables, width)
+        layoutNode.outerLayoutNodeWrapper.minIntrinsicHeight(
+            layoutNode.childLookaheadMeasurables,
+            width
+        )
     }
 
     fun maxLookaheadIntrinsicWidth(height: Int) = with(measurePolicyFromState()) {
-        layoutNode.measureScope.maxIntrinsicWidth(layoutNode.childLookaheadMeasurables, height)
+        layoutNode.outerLayoutNodeWrapper.maxIntrinsicWidth(
+            layoutNode.childLookaheadMeasurables,
+            height
+        )
     }
 
     fun maxLookaheadIntrinsicHeight(width: Int) = with(measurePolicyFromState()) {
-        layoutNode.measureScope.maxIntrinsicHeight(layoutNode.childLookaheadMeasurables, width)
+        layoutNode.outerLayoutNodeWrapper.maxIntrinsicHeight(
+            layoutNode.childLookaheadMeasurables,
+            width
+        )
     }
 
     private fun measurePolicyFromState(): MeasurePolicy {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
index a5b4070..e97628a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
@@ -276,6 +276,10 @@
         if (owner != null) {
             instance.attach(owner)
         }
+
+        if (instance.layoutDelegate.childrenAccessingCoordinatesDuringPlacement > 0) {
+            layoutDelegate.childrenAccessingCoordinatesDuringPlacement++
+        }
     }
 
     internal fun onZSortedChildrenInvalidated() {
@@ -313,6 +317,9 @@
     }
 
     private fun onChildRemoved(child: LayoutNode) {
+        if (child.layoutDelegate.childrenAccessingCoordinatesDuringPlacement > 0) {
+            layoutDelegate.childrenAccessingCoordinatesDuringPlacement--
+        }
         if (owner != null) {
             child.detach()
         }
@@ -545,15 +552,6 @@
             }
         }
 
-    /**
-     * The scope used to [measure][MeasurePolicy.measure] children.
-     */
-    internal val measureScope: MeasureScope = object : MeasureScope, Density {
-        override val density: Float get() = this@LayoutNode.density.density
-        override val fontScale: Float get() = this@LayoutNode.density.fontScale
-        override val layoutDirection: LayoutDirection get() = this@LayoutNode.layoutDirection
-    }
-
     internal var mLookaheadScope: LookaheadScope? = null
         private set(newScope) {
             if (newScope != field) {
@@ -673,7 +671,7 @@
             }
         }
 
-    internal val innerLayoutNodeWrapper: LayoutNodeWrapper = InnerPlaceable(this)
+    internal val innerLayoutNodeWrapper = InnerPlaceable(this)
     internal val layoutDelegate = LayoutNodeLayoutDelegate(this, innerLayoutNodeWrapper)
     internal val outerLayoutNodeWrapper: LayoutNodeWrapper
         get() = layoutDelegate.outerWrapper
@@ -777,7 +775,8 @@
 
             // Create a new chain of LayoutNodeWrappers, reusing existing ones from wrappers
             // when possible.
-            val outerWrapper = modifier.foldOut(innerLayoutNodeWrapper) { mod, toWrap ->
+            val innerPlaceable: LayoutNodeWrapper = innerLayoutNodeWrapper
+            val outerWrapper = modifier.foldOut(innerPlaceable) { mod, toWrap ->
                 if (mod is RemeasurementModifier) {
                     mod.onRemeasurementAvailable(this)
                 }
@@ -893,7 +892,8 @@
         with(measurePassDelegate) {
             Placeable.PlacementScope.executeWithRtlMirroringValues(
                 measuredWidth,
-                layoutDirection
+                layoutDirection,
+                parent?.innerLayoutNodeWrapper
             ) {
                 placeRelative(x, y)
             }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
index de1893f..155a08e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
@@ -60,8 +60,8 @@
         val previousDrawEntity = this.drawEntity
         this.drawEntity = drawEntity
         canvasDrawScope.draw(
-            layoutNodeWrapper.measureScope,
-            layoutNodeWrapper.measureScope.layoutDirection,
+            layoutNodeWrapper,
+            layoutNodeWrapper.layoutDirection,
             canvas,
             size,
             block
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
index e2bc7b3..d948d5b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
@@ -74,6 +74,15 @@
         private set
 
     /**
+     * Tracks whether another layout pass is needed for the LayoutNodeLayoutDelegate
+     * for the purposes of calculating alignment lines. After calculating alignment lines, if
+     * the [Placeable.PlacementScope.coordinates] have been accessed, there is no need to
+     * rerun layout for further alignment lines checks, but [layoutPending] will indicate
+     * that the normal placement still needs to be run.
+     */
+    private var layoutPendingForAlignment = false
+
+    /**
      * Tracks whether another lookahead measure pass is needed for the LayoutNodeLayoutDelegate.
      * Mutation to [lookaheadMeasurePending] is confined to LayoutNodeLayoutDelegate. It can only
      * be set true from outside of this class via [markLookaheadMeasurePending]. It is cleared
@@ -92,10 +101,20 @@
         private set
 
     /**
+     * Tracks whether another lookahead layout pass is needed for the LayoutNodeLayoutDelegate
+     * for the purposes of calculating alignment lines. After calculating alignment lines, if
+     * the [Placeable.PlacementScope.coordinates] have been accessed, there is no need to
+     * rerun layout for further alignment lines checks, but [lookaheadLayoutPending] will indicate
+     * that the normal placement still needs to be run.
+     */
+    private var lookaheadLayoutPendingForAlignment = false
+
+    /**
      * Marks the layoutNode dirty for another layout pass.
      */
     internal fun markLayoutPending() {
         layoutPending = true
+        layoutPendingForAlignment = true
     }
 
     /**
@@ -110,6 +129,7 @@
      */
     internal fun markLookaheadLayoutPending() {
         lookaheadLayoutPending = true
+        lookaheadLayoutPendingForAlignment = true
     }
 
     /**
@@ -125,6 +145,52 @@
         get() = lookaheadPassDelegate
 
     /**
+     * This is used to track when the [Placeable.PlacementScope.coordinates] have been
+     * accessed while placement is run. When the coordinates are accessed during an alignment
+     * line query, it indicates that the placement is not final and must be run again so that
+     * the correct positioning is done. If the coordinates are not accessed during an alignment
+     * lines query (and it isn't just a [LookaheadCapablePlaceable.isShallowPlacing]),
+     * then the placement can be considered final and doesn't have to be run again.
+     *
+     * Also, if coordinates are accessed during placement, then a change in parent coordinates
+     * requires placement to be run again.
+     */
+    var coordinatesAccessedDuringPlacement = false
+        set(value) {
+            val oldValue = field
+            if (oldValue != value) {
+                field = value
+                if (value) {
+                    childrenAccessingCoordinatesDuringPlacement++
+                } else {
+                    childrenAccessingCoordinatesDuringPlacement--
+                }
+            }
+        }
+
+    /**
+     * The number of children with [coordinatesAccessedDuringPlacement] or have
+     * descendants with [coordinatesAccessedDuringPlacement]. This also includes
+     * this, if [coordinatesAccessedDuringPlacement] is `true`.
+     */
+    var childrenAccessingCoordinatesDuringPlacement = 0
+        set(value) {
+            val oldValue = field
+            field = value
+            if ((oldValue == 0) != (value == 0)) {
+                // A child is either newly listening for coordinates or stopped listening
+                val parentLayoutDelegate = layoutNode.parent?.layoutDelegate
+                if (parentLayoutDelegate != null) {
+                    if (value == 0) {
+                        parentLayoutDelegate.childrenAccessingCoordinatesDuringPlacement--
+                    } else {
+                        parentLayoutDelegate.childrenAccessingCoordinatesDuringPlacement++
+                    }
+                }
+            }
+        }
+
+    /**
      * measurePassDelegate manages the measure/layout and alignmentLine related queries for the
      * actual measure/layout pass.
      */
@@ -189,7 +255,9 @@
             }
             // as a result of the previous operation we can figure out a child has been resized
             // and we need to be remeasured, not relaid out
-            if (layoutPending) {
+            if (layoutPendingForAlignment ||
+                (!duringAlignmentLinesQuery && !innerLayoutNodeWrapper.isPlacingForAlignment &&
+                    layoutPending)) {
                 layoutPending = false
                 layoutState = LayoutState.LayingOut
                 with(layoutNode) {
@@ -212,6 +280,13 @@
                     }
                 }
                 layoutState = LayoutState.Idle
+
+                if (innerLayoutNodeWrapper.isPlacingForAlignment &&
+                    coordinatesAccessedDuringPlacement
+                ) {
+                    requestLayout()
+                }
+                layoutPendingForAlignment = false
             }
 
             if (alignmentLines.usedDuringParentLayout) {
@@ -331,6 +406,9 @@
             zIndex: Float,
             layerBlock: (GraphicsLayerScope.() -> Unit)?
         ) {
+            if (position != lastPosition) {
+                notifyChildrenUsingCoordinatesWhilePlacing()
+            }
             // This can actually be called as soon as LookaheadMeasure is done, but devs may expect
             // certain placement results (e.g. LayoutCoordinates) to be valid when lookahead placement
             // takes place. If that's not the case, it will make sense to move this right after
@@ -357,6 +435,7 @@
 
             placedOnce = true
             alignmentLines.usedByModifierLayout = false
+            coordinatesAccessedDuringPlacement = false
             val owner = layoutNode.requireOwner()
             owner.snapshotObserver.observeLayoutModifierSnapshotReads(
                 layoutNode,
@@ -446,7 +525,9 @@
                     alignmentLines.usedByModifierLayout = true
                 }
             }
+            innerLayoutNodeWrapper.isPlacingForAlignment = true
             layoutChildren()
+            innerLayoutNodeWrapper.isPlacingForAlignment = false
             return alignmentLines.getLastCalculation()
         }
 
@@ -468,6 +549,31 @@
         }
 
         /**
+         * This is called any time a placement has done that changes the position during the
+         * layout pass. If any child is looking at their own coordinates to know how to
+         * place children, it will be invalided.
+         *
+         * Note that this is called for every changed position. While not many layouts
+         * look at their coordinates, if there is one, it will cause all position changes
+         * from an ancestor to call down the hierarchy. If this becomes expensive (e.g. many
+         * parents change their position on the same frame), it might be worth using a flag
+         * so that this call becomes cheap after the first one.
+         */
+        fun notifyChildrenUsingCoordinatesWhilePlacing() {
+            if (childrenAccessingCoordinatesDuringPlacement > 0) {
+                layoutNode.children.fastForEach { child ->
+                    val childLayoutDelegate = child.layoutDelegate
+                    if (childLayoutDelegate.coordinatesAccessedDuringPlacement &&
+                        !childLayoutDelegate.layoutPending) {
+                        child.requestRelayout()
+                    }
+                    childLayoutDelegate.measurePassDelegate
+                        .notifyChildrenUsingCoordinatesWhilePlacing()
+                }
+            }
+        }
+
+        /**
          * The callback to be executed before running layoutChildren.
          *
          * There are possible cases when we run layoutChildren() on the parent node, but some of its
@@ -576,9 +682,13 @@
             if (lookaheadLayoutPending) {
                 onBeforeLayoutChildren()
             }
+            val lookaheadDelegate = innerLayoutNodeWrapper.lookaheadDelegate!!
             // as a result of the previous operation we can figure out a child has been resized
             // and we need to be remeasured, not relaid out
-            if (lookaheadLayoutPending) {
+            if (lookaheadLayoutPendingForAlignment ||
+                (!duringAlignmentLinesQuery && !lookaheadDelegate.isPlacingForAlignment &&
+                lookaheadLayoutPending)
+            ) {
                 lookaheadLayoutPending = false
                 layoutState = LayoutState.LookaheadLayingOut
                 val owner = layoutNode.requireOwner()
@@ -600,8 +710,7 @@
                     forEachChildAlignmentLinesOwner { child ->
                         child.alignmentLines.usedDuringParentLayout = false
                     }
-                    layoutNode.innerLayoutNodeWrapper.lookaheadDelegate!!.measureResult
-                        .placeChildren()
+                    lookaheadDelegate.measureResult.placeChildren()
                     forEachChildAlignmentLinesOwner { child ->
                         child.alignmentLines.previousUsedDuringParentLayout =
                             child.alignmentLines.usedDuringParentLayout
@@ -613,6 +722,11 @@
                     }
                 }
                 layoutState = LayoutState.Idle
+                if (coordinatesAccessedDuringPlacement &&
+                    lookaheadDelegate.isPlacingForAlignment) {
+                    requestLayout()
+                }
+                lookaheadLayoutPendingForAlignment = false
             }
             if (alignmentLines.usedDuringParentLayout) {
                 alignmentLines.previousUsedDuringParentLayout = true
@@ -638,7 +752,9 @@
                     alignmentLines.usedByModifierLayout = true
                 }
             }
+            innerLayoutNodeWrapper.lookaheadDelegate?.isPlacingForAlignment = true
             layoutChildren()
+            innerLayoutNodeWrapper.lookaheadDelegate?.isPlacingForAlignment = false
             return alignmentLines.getLastCalculation()
         }
 
@@ -659,6 +775,31 @@
             layoutNode.requestLookaheadRemeasure()
         }
 
+        /**
+         * This is called any time a placement has done that changes the position during the
+         * lookahead layout pass. If any child is looking at their own coordinates to know how to
+         * place children, it will be invalided.
+         *
+         * Note that this is called for every changed position. While not many layouts
+         * look at their coordinates, if there is one, it will cause all position changes
+         * from an ancestor to call down the hierarchy. If this becomes expensive (e.g. many
+         * parents change their position on the same frame), it might be worth using a flag
+         * so that this call becomes cheap after the first one.
+         */
+        fun notifyChildrenUsingCoordinatesWhilePlacing() {
+            if (childrenAccessingCoordinatesDuringPlacement > 0) {
+                layoutNode.children.fastForEach { child ->
+                    val childLayoutDelegate = child.layoutDelegate
+                    if (childLayoutDelegate.coordinatesAccessedDuringPlacement &&
+                        !childLayoutDelegate.layoutPending) {
+                        child.requestLookaheadRelayout()
+                    }
+                    childLayoutDelegate.lookaheadPassDelegate
+                        ?.notifyChildrenUsingCoordinatesWhilePlacing()
+                }
+            }
+        }
+
         override fun measure(constraints: Constraints): Placeable {
             layoutNode.trackLookaheadMeasurementByParent()
             if (layoutNode.intrinsicsUsageByParent == LayoutNode.UsageByParent.NotUsed) {
@@ -706,6 +847,10 @@
 
         // Lookahead remeasurement with the given constraints.
         fun remeasure(constraints: Constraints): Boolean {
+            val parent = layoutNode.parent
+            @Suppress("Deprecation")
+            layoutNode.canMultiMeasure = layoutNode.canMultiMeasure ||
+                (parent != null && parent.canMultiMeasure)
             if (layoutNode.lookaheadMeasurePending || lookaheadConstraints != constraints) {
                 lookaheadConstraints = constraints
                 alignmentLines.usedByModifierMeasurement = false
@@ -735,8 +880,12 @@
             layerBlock: (GraphicsLayerScope.() -> Unit)?
         ) {
             placedOnce = true
+            if (position != lastPosition) {
+                notifyChildrenUsingCoordinatesWhilePlacing()
+            }
             alignmentLines.usedByModifierLayout = false
             val owner = layoutNode.requireOwner()
+            coordinatesAccessedDuringPlacement = false
             owner.snapshotObserver.observeLayoutModifierSnapshotReads(layoutNode) {
                 with(PlacementScope) {
                     outerWrapper.lookaheadDelegate!!.place(position)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
index 34fef50..41650e23 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
@@ -40,12 +40,11 @@
 import androidx.compose.ui.layout.AlignmentLine
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.LookaheadLayoutCoordinatesImpl
+import androidx.compose.ui.layout.LookaheadScope
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
-import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.layout.ParentDataModifier
 import androidx.compose.ui.layout.Placeable
-import androidx.compose.ui.layout.LookaheadScope
 import androidx.compose.ui.layout.findRoot
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.semantics.SemanticsEntity
@@ -63,19 +62,27 @@
  * Measurable and Placeable type that has a position.
  */
 internal abstract class LayoutNodeWrapper(
-    internal val layoutNode: LayoutNode
+    override val layoutNode: LayoutNode
 ) : LookaheadCapablePlaceable(), Measurable, LayoutCoordinates, OwnerScope,
         (Canvas) -> Unit {
 
     internal open val wrapped: LayoutNodeWrapper? get() = null
     internal var wrappedBy: LayoutNodeWrapper? = null
 
-    /**
-     * The scope used to measure the wrapped. InnerPlaceables are using the MeasureScope
-     * of the LayoutNode. For fewer allocations, everything else is reusing the measure scope of
-     * their wrapped.
-     */
-    abstract val measureScope: MeasureScope
+    override val layoutDirection: LayoutDirection
+        get() = layoutNode.layoutDirection
+
+    override val density: Float
+        get() = layoutNode.density.density
+
+    override val fontScale: Float
+        get() = layoutNode.density.fontScale
+
+    override val parent: LookaheadCapablePlaceable?
+        get() = wrappedBy
+
+    override val coordinates: LayoutCoordinates
+        get() = this
 
     // Size exposed to LayoutCoordinates.
     final override val size: IntSize get() = measuredSize
@@ -217,7 +224,7 @@
                  * ParentData provided through the parentData node will override the data provided
                  * through a modifier.
                  */
-                measureScope.modifyParentData(next.parentData)
+                modifyParentData(next.parentData)
             }
         }
 
@@ -291,6 +298,8 @@
         onLayerBlockUpdated(layerBlock)
         if (this.position != position) {
             this.position = position
+            layoutNode.layoutDelegate.measurePassDelegate
+                .notifyChildrenUsingCoordinatesWhilePlacing()
             val layer = layer
             if (layer != null) {
                 layer.move(position)
@@ -1166,6 +1175,14 @@
                     wrapper.updateLayerParameters()
                     if (!tmpLayerPositionalProperties.hasSameValuesAs(layerPositionalProperties)) {
                         val layoutNode = wrapper.layoutNode
+                        val layoutDelegate = layoutNode.layoutDelegate
+                        if (layoutDelegate.childrenAccessingCoordinatesDuringPlacement > 0) {
+                            if (layoutDelegate.coordinatesAccessedDuringPlacement) {
+                                layoutNode.requestRelayout()
+                            }
+                            layoutDelegate.measurePassDelegate
+                                .notifyChildrenUsingCoordinatesWhilePlacing()
+                        }
                         layoutNode.owner?.requestOnPositionedCallback(layoutNode)
                     }
                 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt
index 815a9dc..b1cbed0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt
@@ -18,24 +18,30 @@
 
 import androidx.compose.ui.graphics.GraphicsLayerScope
 import androidx.compose.ui.layout.AlignmentLine
+import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.LookaheadLayoutCoordinatesImpl
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.LookaheadScope
+import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.layout.VerticalAlignmentLine
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
 
 /**
  * This is the base class for LayoutNodeWrapper and LookaheadDelegate. The common
  * functionalities between the two are extracted here.
  */
-internal abstract class LookaheadCapablePlaceable : Placeable() {
+internal abstract class LookaheadCapablePlaceable : Placeable(), MeasureScope {
     abstract val position: IntOffset
     abstract val child: LookaheadCapablePlaceable?
+    abstract val parent: LookaheadCapablePlaceable?
     abstract val hasMeasureResult: Boolean
+    abstract val layoutNode: LayoutNode
+    abstract val coordinates: LayoutCoordinates
     final override fun get(alignmentLine: AlignmentLine): Int {
         if (!hasMeasureResult) return AlignmentLine.Unspecified
         val measuredPosition = calculateAlignmentLine(alignmentLine)
@@ -56,6 +62,15 @@
     internal abstract fun replace()
     abstract val alignmentLinesOwner: AlignmentLinesOwner
 
+    /**
+     * Used to indicate that this placement pass is for the purposes of calculating an
+     * alignment line. If it is, then
+     * [LayoutNodeLayoutDelegate.coordinatesAccessedDuringPlacement] will be changed
+     * when [Placeable.PlacementScope.coordinates] is accessed to indicate that the placement
+     * is not finalized and must be run again.
+     */
+    internal var isPlacingForAlignment = false
+
     protected fun LayoutNodeWrapper.invalidateAlignmentLinesFromPositionChange() {
         if (wrapped?.layoutNode != layoutNode) {
             alignmentLinesOwner.alignmentLines.onAlignmentsChanged()
@@ -79,6 +94,18 @@
         get() = _measureResult ?: error(
             "LookaheadDelegate has not been measured yet when measureResult is requested."
         )
+    override val layoutDirection: LayoutDirection
+        get() = wrapper.layoutDirection
+    override val density: Float
+        get() = wrapper.density
+    override val fontScale: Float
+        get() = wrapper.fontScale
+    override val parent: LookaheadCapablePlaceable?
+        get() = wrapper.wrappedBy?.lookaheadDelegate
+    override val layoutNode: LayoutNode
+        get() = wrapper.layoutNode
+    override val coordinates: LayoutCoordinates
+        get() = lookaheadLayoutCoordinates
 
     val lookaheadLayoutCoordinates = LookaheadLayoutCoordinatesImpl(this)
     override val alignmentLinesOwner: AlignmentLinesOwner
@@ -122,6 +149,8 @@
     ) {
         if (this.position != position) {
             this.position = position
+            layoutNode.layoutDelegate.lookaheadPassDelegate
+                ?.notifyChildrenUsingCoordinatesWhilePlacing()
             wrapper.invalidateAlignmentLinesFromPositionChange()
         }
         if (isShallowPlacing) return
@@ -131,7 +160,8 @@
     protected open fun placeChildren() {
         PlacementScope.executeWithRtlMirroringValues(
             measureResult.width,
-            wrapper.measureScope.layoutDirection
+            wrapper.layoutDirection,
+            this
         ) {
             measureResult.placeChildren()
         }
@@ -174,4 +204,4 @@
         }
         return aggregatedOffset
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
index ec8d0e68..d9d13d07 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
@@ -252,7 +252,7 @@
                 false
             }
             Idle -> {
-                if ((layoutNode.measurePending || layoutNode.layoutPending) && !forced) {
+                if (!forced && (layoutNode.measurePending || layoutNode.layoutPending)) {
                     // don't need to do anything else since the parent is already scheduled
                     // for a relayout (measure will trigger relayout), or is laying out right now
                     consistencyChecker?.assertConsistent()
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt
index 53d9b43..fb16f9a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt
@@ -27,10 +27,9 @@
 import androidx.compose.ui.layout.HorizontalAlignmentLine
 import androidx.compose.ui.layout.IntermediateLayoutModifier
 import androidx.compose.ui.layout.LayoutModifier
-import androidx.compose.ui.layout.MeasureResult
-import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.LookaheadScope
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
@@ -40,9 +39,6 @@
     override var wrapped: LayoutNodeWrapper,
     var modifier: LayoutModifier
 ) : LayoutNodeWrapper(wrapped.layoutNode) {
-    override val measureScope: MeasureScope
-        get() = wrapped.measureScope
-
     private var lookAheadTransientLayoutModifier: IntermediateLayoutModifier? =
         modifier as? IntermediateLayoutModifier
 
@@ -66,7 +62,7 @@
         override fun measure(constraints: Constraints): Placeable =
             performingMeasure(constraints) {
                 with(modifier) {
-                    measureScope.measure(
+                    measure(
                         // This allows `measure` calls in the modifier to be redirected to
                         // calling lookaheadMeasure in wrapped.
                         wrapped.lookaheadDelegate!!, constraints
@@ -82,22 +78,22 @@
 
         override fun minIntrinsicWidth(height: Int): Int =
             with(modifierFromState()) {
-                measureScope.minIntrinsicWidth(wrapped.lookaheadDelegate!!, height)
+                minIntrinsicWidth(wrapped.lookaheadDelegate!!, height)
             }
 
         override fun maxIntrinsicWidth(height: Int): Int =
             with(modifierFromState()) {
-                measureScope.maxIntrinsicWidth(wrapped.lookaheadDelegate!!, height)
+                maxIntrinsicWidth(wrapped.lookaheadDelegate!!, height)
             }
 
         override fun minIntrinsicHeight(width: Int): Int =
             with(modifierFromState()) {
-                measureScope.minIntrinsicHeight(wrapped.lookaheadDelegate!!, width)
+                minIntrinsicHeight(wrapped.lookaheadDelegate!!, width)
             }
 
         override fun maxIntrinsicHeight(width: Int): Int =
             with(modifierFromState()) {
-                measureScope.maxIntrinsicHeight(wrapped.lookaheadDelegate!!, width)
+                maxIntrinsicHeight(wrapped.lookaheadDelegate!!, width)
             }
     }
 
@@ -154,7 +150,7 @@
     override fun measure(constraints: Constraints): Placeable {
         performingMeasure(constraints) {
             with(modifier) {
-                measureResult = measureScope.measure(wrapped, constraints)
+                measureResult = measure(wrapped, constraints)
                 this@ModifiedLayoutNode
             }
         }
@@ -164,23 +160,23 @@
 
     override fun minIntrinsicWidth(height: Int): Int {
         return with(modifierFromState()) {
-            measureScope.minIntrinsicWidth(wrapped, height)
+            minIntrinsicWidth(wrapped, height)
         }
     }
 
     override fun maxIntrinsicWidth(height: Int): Int =
         with(modifierFromState()) {
-            measureScope.maxIntrinsicWidth(wrapped, height)
+            maxIntrinsicWidth(wrapped, height)
         }
 
     override fun minIntrinsicHeight(width: Int): Int =
         with(modifierFromState()) {
-            measureScope.minIntrinsicHeight(wrapped, width)
+            minIntrinsicHeight(wrapped, width)
         }
 
     override fun maxIntrinsicHeight(width: Int): Int =
         with(modifierFromState()) {
-            measureScope.maxIntrinsicHeight(wrapped, width)
+            maxIntrinsicHeight(wrapped, width)
         }
 
     override fun placeAt(
@@ -198,7 +194,8 @@
         onPlaced()
         PlacementScope.executeWithRtlMirroringValues(
             measuredSize.width,
-            measureScope.layoutDirection
+            layoutDirection,
+            this
         ) {
             measureResult.placeChildren()
         }
@@ -273,11 +270,13 @@
     }
     // Place our wrapped to obtain their position inside ourselves.
     child.isShallowPlacing = true
+    isPlacingForAlignment = true
     replace()
     child.isShallowPlacing = false
+    isPlacingForAlignment = false
     return if (alignmentLine is HorizontalAlignmentLine) {
         positionInWrapped + child.position.y
     } else {
         positionInWrapped + child.position.x
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index cd84292..bf026fd 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -855,10 +855,10 @@
             translationX = 5f
             translationY = 2f
         }
-        parent.outerLayoutNodeWrapper.measureScope
+        parent.outerLayoutNodeWrapper
             .measure(listOf(parent.outerLayoutNodeWrapper), Constraints())
         child.outerLayoutNodeWrapper
-            .measureScope.measure(listOf(child.outerLayoutNodeWrapper), Constraints())
+            .measure(listOf(child.outerLayoutNodeWrapper), Constraints())
         parent.place(0, 0)
         child.place(0, 0)
 
@@ -883,10 +883,10 @@
         child.modifier = Modifier.graphicsLayer {
             rotationZ = 90f
         }
-        parent.outerLayoutNodeWrapper.measureScope
+        parent.outerLayoutNodeWrapper
             .measure(listOf(parent.outerLayoutNodeWrapper), Constraints())
         child.outerLayoutNodeWrapper
-            .measureScope.measure(listOf(child.outerLayoutNodeWrapper), Constraints())
+            .measure(listOf(child.outerLayoutNodeWrapper), Constraints())
         parent.place(0, 0)
         child.place(0, 0)
 
@@ -911,10 +911,10 @@
         child.modifier = Modifier.graphicsLayer {
             scaleX = 0f
         }
-        parent.outerLayoutNodeWrapper.measureScope
+        parent.outerLayoutNodeWrapper
             .measure(listOf(parent.outerLayoutNodeWrapper), Constraints())
         child.outerLayoutNodeWrapper
-            .measureScope.measure(listOf(child.outerLayoutNodeWrapper), Constraints())
+            .measure(listOf(child.outerLayoutNodeWrapper), Constraints())
         parent.place(0, 0)
         child.place(0, 0)
 
@@ -966,12 +966,12 @@
             scaleY = 2f
             transformOrigin = TransformOrigin(0f, 0f)
         }
-        parent.outerLayoutNodeWrapper.measureScope
+        parent.outerLayoutNodeWrapper
             .measure(listOf(parent.outerLayoutNodeWrapper), Constraints())
         child1.outerLayoutNodeWrapper
-            .measureScope.measure(listOf(child1.outerLayoutNodeWrapper), Constraints())
+            .measure(listOf(child1.outerLayoutNodeWrapper), Constraints())
         child2.outerLayoutNodeWrapper
-            .measureScope.measure(listOf(child2.outerLayoutNodeWrapper), Constraints())
+            .measure(listOf(child2.outerLayoutNodeWrapper), Constraints())
         parent.place(0, 0)
         child1.place(100, 200)
         child2.place(5, 11)
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 23b1d5e..ccc9f55 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -4097,7 +4097,7 @@
     method public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
     method public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
     method public void setAutoSizeTextTypeWithDefaults(@androidx.core.widget.TextViewCompat.AutoSizeTextType int);
-    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final boolean PLATFORM_SUPPORTS_AUTOSIZE;
+    field @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final boolean PLATFORM_SUPPORTS_AUTOSIZE;
   }
 
   public final class CheckedTextViewCompat {
diff --git a/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationControllerCompat.java b/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationControllerCompat.java
index 2260711..964a06f 100644
--- a/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationControllerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationControllerCompat.java
@@ -267,10 +267,6 @@
         void finish(boolean shown) {
         }
 
-        public boolean isReady() {
-            return false;
-        }
-
         boolean isFinished() {
             return false;
         }
@@ -337,11 +333,6 @@
         }
 
         @Override
-        public boolean isReady() {
-            return mController.isReady();
-        }
-
-        @Override
         boolean isFinished() {
             return mController.isFinished();
         }
diff --git a/core/core/src/main/java/androidx/core/widget/AutoSizeableTextView.java b/core/core/src/main/java/androidx/core/widget/AutoSizeableTextView.java
index eaf80fc5..034a660 100644
--- a/core/core/src/main/java/androidx/core/widget/AutoSizeableTextView.java
+++ b/core/core/src/main/java/androidx/core/widget/AutoSizeableTextView.java
@@ -39,7 +39,9 @@
 public interface AutoSizeableTextView {
     /**
      * @hide
+     * @deprecated do not use, overlarge scope and missing annotations
      */
+    @Deprecated
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     boolean PLATFORM_SUPPORTS_AUTOSIZE = Build.VERSION.SDK_INT >= 27;
 
diff --git a/datastore/datastore-core-okio/build.gradle b/datastore/datastore-core-okio/build.gradle
index a6755ca..43441a2 100644
--- a/datastore/datastore-core-okio/build.gradle
+++ b/datastore/datastore-core-okio/build.gradle
@@ -35,6 +35,8 @@
     }
     mac()
     linux()
+    ios()
+
     sourceSets {
         commonMain {
             dependencies {
diff --git a/datastore/datastore-core/build.gradle b/datastore/datastore-core/build.gradle
index d6f826c..07be1ab 100644
--- a/datastore/datastore-core/build.gradle
+++ b/datastore/datastore-core/build.gradle
@@ -33,6 +33,8 @@
     }
     mac()
     linux()
+    ios()
+
     sourceSets {
         commonMain {
             dependencies {
diff --git a/datastore/datastore-core/src/nativeMain/kotlin/androidx/datastore/core/DataStoreFactory.native.kt b/datastore/datastore-core/src/nativeMain/kotlin/androidx/datastore/core/DataStoreFactory.native.kt
index b22b4c2..7c067d9 100644
--- a/datastore/datastore-core/src/nativeMain/kotlin/androidx/datastore/core/DataStoreFactory.native.kt
+++ b/datastore/datastore-core/src/nativeMain/kotlin/androidx/datastore/core/DataStoreFactory.native.kt
@@ -44,9 +44,6 @@
      * @param migrations Migrations are run before any access to data can occur. Migrations must
      * be idempotent.
      * @param scope The scope in which IO operations and transform functions will execute.
-     * @param produceFile Function which returns the file that the new DataStore will act on. The
-     * function must return the same path every time. No two instances of DataStore should act on
-     * the same file at the same time.
      *
      * @return a new DataStore instance with the provided configuration
      */
diff --git a/datastore/datastore-preferences-core/api/current.txt b/datastore/datastore-preferences-core/api/current.txt
index bb156b0..9e2de9f 100644
--- a/datastore/datastore-preferences-core/api/current.txt
+++ b/datastore/datastore-preferences-core/api/current.txt
@@ -19,6 +19,7 @@
     method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(optional androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.core.Preferences>? corruptionHandler, optional java.util.List<? extends androidx.datastore.core.DataMigration<androidx.datastore.preferences.core.Preferences>> migrations, kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
     method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(optional androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.core.Preferences>? corruptionHandler, kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
     method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
+    method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(androidx.datastore.core.Storage<androidx.datastore.preferences.core.Preferences> storage, androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.core.Preferences>? corruptionHandler, java.util.List<? extends androidx.datastore.core.DataMigration<androidx.datastore.preferences.core.Preferences>> migrations, kotlinx.coroutines.CoroutineScope scope);
     field public static final androidx.datastore.preferences.core.PreferenceDataStoreFactory INSTANCE;
   }
 
diff --git a/datastore/datastore-preferences-core/api/public_plus_experimental_current.txt b/datastore/datastore-preferences-core/api/public_plus_experimental_current.txt
index bb156b0..9e2de9f 100644
--- a/datastore/datastore-preferences-core/api/public_plus_experimental_current.txt
+++ b/datastore/datastore-preferences-core/api/public_plus_experimental_current.txt
@@ -19,6 +19,7 @@
     method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(optional androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.core.Preferences>? corruptionHandler, optional java.util.List<? extends androidx.datastore.core.DataMigration<androidx.datastore.preferences.core.Preferences>> migrations, kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
     method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(optional androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.core.Preferences>? corruptionHandler, kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
     method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
+    method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(androidx.datastore.core.Storage<androidx.datastore.preferences.core.Preferences> storage, androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.core.Preferences>? corruptionHandler, java.util.List<? extends androidx.datastore.core.DataMigration<androidx.datastore.preferences.core.Preferences>> migrations, kotlinx.coroutines.CoroutineScope scope);
     field public static final androidx.datastore.preferences.core.PreferenceDataStoreFactory INSTANCE;
   }
 
diff --git a/datastore/datastore-preferences-core/api/restricted_current.txt b/datastore/datastore-preferences-core/api/restricted_current.txt
index bb156b0..9e2de9f 100644
--- a/datastore/datastore-preferences-core/api/restricted_current.txt
+++ b/datastore/datastore-preferences-core/api/restricted_current.txt
@@ -19,6 +19,7 @@
     method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(optional androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.core.Preferences>? corruptionHandler, optional java.util.List<? extends androidx.datastore.core.DataMigration<androidx.datastore.preferences.core.Preferences>> migrations, kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
     method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(optional androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.core.Preferences>? corruptionHandler, kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
     method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
+    method public androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> create(androidx.datastore.core.Storage<androidx.datastore.preferences.core.Preferences> storage, androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.core.Preferences>? corruptionHandler, java.util.List<? extends androidx.datastore.core.DataMigration<androidx.datastore.preferences.core.Preferences>> migrations, kotlinx.coroutines.CoroutineScope scope);
     field public static final androidx.datastore.preferences.core.PreferenceDataStoreFactory INSTANCE;
   }
 
diff --git a/datastore/datastore-preferences-core/build.gradle b/datastore/datastore-preferences-core/build.gradle
index 983bc21..0ccd76c 100644
--- a/datastore/datastore-preferences-core/build.gradle
+++ b/datastore/datastore-preferences-core/build.gradle
@@ -15,17 +15,102 @@
  */
 
 import androidx.build.BundleInsideHelper
+import androidx.build.KmpPlatformsKt
+import androidx.build.LibraryType
 import androidx.build.Publish
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
 
 plugins {
     id("AndroidXPlugin")
-    id("kotlin")
+    alias(libs.plugins.kotlinSerialization)
 }
 
-BundleInsideHelper.forInsideJar(
-    project,
-    /* from = */ "com.google.protobuf",
-    /* to =   */ "androidx.datastore.preferences.protobuf"
+def enableNative = KmpPlatformsKt.enableNative(project)
+
+androidXMultiplatform {
+    jvm {
+        withJava()
+    }
+    mac()
+    linux()
+    ios()
+
+    sourceSets {
+        commonMain {
+            dependencies {
+                api(libs.kotlinStdlib)
+                api(libs.okio)
+                api(project(":datastore:datastore-core"))
+                api(project(":datastore:datastore-core-okio"))
+            }
+        }
+        commonTest {
+            dependencies {
+                implementation(libs.kotlinTestCommon)
+                implementation(libs.kotlinTestAnnotationsCommon)
+                implementation(libs.kotlinCoroutinesTest)
+                implementation(project(":datastore:datastore-core"))
+                implementation(project(":internal-testutils-kmp"))
+                implementation(project(":internal-testutils-datastore"))
+            }
+        }
+        jvmMain {
+            dependsOn(commonMain)
+        }
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+                implementation(libs.junit)
+                implementation(libs.kotlinTest)
+                implementation(project(":internal-testutils-datastore"))
+                implementation(project(":internal-testutils-kmp"))
+            }
+        }
+        if (enableNative) {
+            nativeMain {
+                dependsOn(commonMain)
+                dependencies {
+                    implementation(libs.kotlinSerializationCore)
+                    implementation(libs.kotlinSerializationProtobuf)
+                }
+            }
+
+            nativeTest {
+                dependsOn(commonTest)
+                dependencies {
+                    implementation(libs.kotlinTest)
+                    implementation(project(":internal-testutils-datastore"))
+                    implementation(project(":internal-testutils-kmp"))
+                }
+            }
+        }
+
+        all {
+            languageSettings.optIn("kotlin.RequiresOptIn")
+        }
+        targets.withType(KotlinNativeTarget).configureEach {
+            binaries.all {
+                binaryOptions["memoryModel"] = "experimental"
+            }
+        }
+        targets.forEach {target ->
+            if (target.platformType == KotlinPlatformType.native) {
+                target.compilations["main"].defaultSourceSet {
+                    dependsOn(sourceSets["nativeMain"])
+                }
+                target.compilations["test"].defaultSourceSet {
+                    dependsOn(sourceSets["nativeTest"])
+                }
+            }
+        }
+    }
+}
+
+BundleInsideHelper.forInsideJarKmp(
+        project,
+        /* from = */ "com.google.protobuf",
+        /* to =   */ "androidx.datastore.preferences.protobuf"
 )
 
 dependencies {
@@ -33,17 +118,11 @@
             path: ":datastore:datastore-preferences-proto",
             configuration: "export"
     ))
-    api(libs.kotlinStdlib)
-    api(project(":datastore:datastore-core"))
-
-    testImplementation(libs.junit)
-    testImplementation(libs.kotlinCoroutinesTest)
-    testImplementation(libs.kotlinTest)
 }
 
 androidx {
     name = "Android Preferences DataStore Core"
-    publish = Publish.SNAPSHOT_AND_RELEASE
+    type = LibraryType.KMP_LIBRARY
     mavenGroup = LibraryGroups.DATASTORE
     inceptionYear = "2020"
     description = "Android Preferences DataStore without the Android Dependencies"
diff --git a/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/Expect.kt b/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/Expect.kt
new file mode 100644
index 0000000..6c4ab296
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/Expect.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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.datastore.preferences.core
+
+import androidx.datastore.core.okio.OkioSerializer
+import kotlinx.coroutines.CoroutineDispatcher
+
+internal expect fun <K, V> immutableMap(map: Map<K, V>): Map<K, V>
+internal expect fun <T> immutableCopyOfSet(set: Set<T>): Set<T>
+
+internal expect fun ioDispatcher(): CoroutineDispatcher
+
+internal expect class AtomicBoolean {
+    constructor(initialValue: Boolean)
+    fun set(value: Boolean)
+    fun get(): Boolean
+}
+
+internal expect fun getPreferencesSerializer(): OkioSerializer<Preferences>
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactory.kt b/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactory.kt
new file mode 100644
index 0000000..5c5c269
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactory.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 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.datastore.preferences.core
+
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.DataMigration
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.Storage
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlin.jvm.JvmOverloads
+
+expect object PreferenceDataStoreFactory {
+
+    /**
+     * Create an instance of SingleProcessDataStore. Never create more than one instance of
+     * DataStore for a given file; doing so can break all DataStore functionality. You should
+     * consider managing your DataStore instance as a singleton.
+     *
+     * @param storage The storage object defines where and how the preferences will be stored.
+     * @param corruptionHandler The corruptionHandler is invoked if DataStore encounters a
+     * [CorruptionException] when attempting to read data. CorruptionExceptions are thrown by
+     * serializers when data cannot be de-serialized.
+     * @param migrations are run before any access to data can occur. Each producer and migration
+     * may be run more than once whether or not it already succeeded (potentially because another
+     * migration failed or a write to disk failed.)
+     * @param scope The scope in which IO operations and transform functions will execute.
+     * The function must return the same path every time. No two instances of PreferenceDataStore
+     * should act on the same file at the same time. The file must have the extension
+     * preferences_pb.
+     *
+     * @return a new DataStore instance with the provided configuration
+     */
+    @JvmOverloads
+    public fun create(
+        storage: Storage<Preferences>,
+        corruptionHandler: ReplaceFileCorruptionHandler<Preferences>? = null,
+        migrations: List<DataMigration<Preferences>> = listOf(),
+        scope: CoroutineScope = CoroutineScope(ioDispatcher() + SupervisorJob()),
+    ): DataStore<Preferences>
+}
+
+internal class PreferenceDataStore(private val delegate: DataStore<Preferences>) :
+    DataStore<Preferences> by delegate {
+    override suspend fun updateData(transform: suspend (t: Preferences) -> Preferences):
+        Preferences {
+        return delegate.updateData {
+            val transformed = transform(it)
+            // Freeze the preferences since any future mutations will break DataStore. If a user
+            // tunnels the value out of DataStore and mutates it, this could be problematic.
+            // This is a safe cast, since MutablePreferences is the only implementation of
+            // Preferences.
+            (transformed as MutablePreferences).freeze()
+            transformed
+        }
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/Preferences.kt b/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/Preferences.kt
similarity index 97%
rename from datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/Preferences.kt
rename to datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/Preferences.kt
index 606e60d..4ebd196 100644
--- a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/Preferences.kt
+++ b/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/Preferences.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 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.
@@ -16,8 +16,6 @@
 package androidx.datastore.preferences.core
 
 import androidx.datastore.core.DataStore
-import java.util.Collections
-import java.util.concurrent.atomic.AtomicBoolean
 
 /**
  * Preferences and MutablePreferences are a lot like a generic Map and MutableMap keyed by the
@@ -157,7 +155,7 @@
     }
 
     override fun asMap(): Map<Key<*>, Any> {
-        return Collections.unmodifiableMap(preferencesMap.entries.associate { entry ->
+        return immutableMap(preferencesMap.entries.associate { entry ->
             when (val value = entry.value) {
                 is ByteArray -> Pair(entry.key, value.copyOf())
                 else -> Pair(entry.key, entry.value)
@@ -195,7 +193,7 @@
             null -> remove(key)
             // Copy set so changes to input don't change Preferences. Wrap in unmodifiableSet so
             // returned instances can't be changed.
-            is Set<*> -> preferencesMap[key] = Collections.unmodifiableSet(value.toSet())
+            is Set<*> -> preferencesMap[key] = immutableCopyOfSet(value)
             is ByteArray -> preferencesMap[key] = value.copyOf()
             else -> preferencesMap[key] = value
         }
@@ -358,4 +356,4 @@
         // PreferencesDataStore.updateData()
         it.toMutablePreferences().apply { transform(this) }
     }
-}
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesFactory.kt b/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/PreferencesFactory.kt
similarity index 95%
rename from datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesFactory.kt
rename to datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/PreferencesFactory.kt
index 2605861..d10b2b4 100644
--- a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesFactory.kt
+++ b/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/PreferencesFactory.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 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.
@@ -18,6 +18,8 @@
 
 package androidx.datastore.preferences.core
 
+import kotlin.jvm.JvmName
+
 /**
  * Get a new empty Preferences.
  *
@@ -53,4 +55,4 @@
  */
 @JvmName("createMutable")
 public fun mutablePreferencesOf(vararg pairs: Preferences.Pair<*>): MutablePreferences =
-    MutablePreferences(startFrozen = false).apply { putAll(*pairs) }
+    MutablePreferences(startFrozen = false).apply { putAll(*pairs) }
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesKeys.kt b/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/PreferencesKeys.kt
similarity index 99%
rename from datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesKeys.kt
rename to datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/PreferencesKeys.kt
index 3e17ac2..c6ee440 100644
--- a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesKeys.kt
+++ b/datastore/datastore-preferences-core/src/commonMain/kotlin/androidx/datastore/preferences/core/PreferencesKeys.kt
@@ -18,6 +18,8 @@
 
 package androidx.datastore.preferences.core
 
+import kotlin.jvm.JvmName
+
 /**
  * Get a key for an Int preference. You should not have multiple keys with the same name (for use
  * with the same Preferences). Using overlapping keys with different types can result in
@@ -110,4 +112,4 @@
  * @return the Preferences.Key<ByteArray> for [name]
  */
 @JvmName("byteArrayKey")
-public fun byteArrayPreferencesKey(name: String): Preferences.Key<ByteArray> = Preferences.Key(name)
+public fun byteArrayPreferencesKey(name: String): Preferences.Key<ByteArray> = Preferences.Key(name)
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/commonTest/kotlin/androidx/datastore/preferences/core/PreferencesCompatibilityTest.kt b/datastore/datastore-preferences-core/src/commonTest/kotlin/androidx/datastore/preferences/core/PreferencesCompatibilityTest.kt
new file mode 100644
index 0000000..6c696a0
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/commonTest/kotlin/androidx/datastore/preferences/core/PreferencesCompatibilityTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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.datastore.preferences.core
+
+import androidx.datastore.core.okio.OkioSerializer
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlinx.coroutines.test.runTest
+import okio.Buffer
+import okio.ByteString.Companion.decodeBase64
+
+@OptIn(
+    kotlinx.coroutines.ExperimentalCoroutinesApi::class,
+)
+class PreferencesCompatibilityTest {
+
+    @Test
+    fun testWireCompatibility() = runTest(dispatchTimeoutMs = 10000) {
+
+        // base64 output of serializing "expectedProto"
+        val protoBase64 = "ChAKB215RmxvYXQSBRXNzIw/ChUKCG15RG91YmxlEgk5mpmZmZmZ8T8KCwoFbXlJbnQSAh" +
+            "gBCgwKBm15TG9uZxICIAEKGQoIbXlTdHJpbmcSDSoLc3RyaW5nVmFsdWUKDwoJbXlCb29sZWFuEgIIAQo" +
+            "bCgtteVN0cmluZ1NldBIMMgoKA29uZQoDdHdvChMKC215Qnl0ZUFycmF5EgRCAgEC"
+        val byteString = protoBase64.decodeBase64() ?: throw Exception("Unable to decode")
+        val preferencesSerializer: OkioSerializer<Preferences> = getPreferencesSerializer()
+        val expectedProto = preferencesOf(
+            Preferences.Pair(floatPreferencesKey("myFloat"), 1.1f),
+            Preferences.Pair(doublePreferencesKey("myDouble"), 1.1),
+            Preferences.Pair(intPreferencesKey("myInt"), 1),
+            Preferences.Pair(longPreferencesKey("myLong"), 1L),
+            Preferences.Pair(stringPreferencesKey("myString"), "stringValue"),
+            Preferences.Pair(booleanPreferencesKey("myBoolean"), true),
+            Preferences.Pair(stringSetPreferencesKey("myStringSet"), setOf("one", "two")),
+            Preferences.Pair(byteArrayPreferencesKey("myByteArray"), byteArrayOf(1, 2)),
+        )
+
+        val protoBuffer = Buffer()
+        protoBuffer.write(byteString)
+        val protoPrefsFromBytes = preferencesSerializer.readFrom(protoBuffer)
+        assertEquals(expectedProto, protoPrefsFromBytes)
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/commonTest/kotlin/androidx/datastore/preferences/core/PreferencesSerializerTest.kt b/datastore/datastore-preferences-core/src/commonTest/kotlin/androidx/datastore/preferences/core/PreferencesSerializerTest.kt
new file mode 100644
index 0000000..185d14f
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/commonTest/kotlin/androidx/datastore/preferences/core/PreferencesSerializerTest.kt
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2020 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.datastore.preferences.core
+
+import androidx.datastore.OkioPath
+import androidx.datastore.OkioTestIO
+import androidx.datastore.core.okio.OkioSerializer
+import kotlinx.coroutines.test.runTest
+import kotlin.test.assertEquals
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+import kotlinx.coroutines.test.TestScope
+import okio.FileSystem
+
+@OptIn(
+    kotlinx.coroutines.ExperimentalCoroutinesApi::class,
+    kotlinx.coroutines.ObsoleteCoroutinesApi::class,
+    kotlinx.coroutines.FlowPreview::class,
+)
+class PreferencesSerializerTest {
+
+    private val testIO = OkioTestIO()
+
+    private lateinit var testFile: OkioPath
+    private val preferencesSerializer: OkioSerializer<Preferences> = getPreferencesSerializer()
+    private val fileSystem: FileSystem = FileSystem.SYSTEM
+
+    @BeforeTest
+    fun setUp() {
+        testFile = testIO.newTempFile()
+        fileSystem.createDirectories(testFile.path.parent!!)
+    }
+    fun doTest(test: suspend TestScope.() -> Unit) {
+        runTest(dispatchTimeoutMs = 10000) {
+            test(this)
+        }
+    }
+
+    @Test
+    fun testWriteAndReadString() = doTest {
+        val stringKey = stringPreferencesKey("string_key")
+
+        val prefs = preferencesOf(
+            stringKey to "string1"
+        )
+
+        fileSystem.write(testFile.path) {
+            preferencesSerializer.writeTo(prefs, this)
+        }
+
+        val readPrefs = fileSystem.read(testFile.path) {
+            preferencesSerializer.readFrom(this)
+        }
+
+        assertEquals(prefs, readPrefs)
+    }
+
+    @Test
+    fun testWriteAndReadStringSet() = doTest {
+        val stringSetKey =
+            stringSetPreferencesKey("string_set_key")
+
+        val prefs = preferencesOf(
+            stringSetKey to setOf("string1", "string2", "string3")
+        )
+
+        fileSystem.write(testFile.path) {
+            preferencesSerializer.writeTo(prefs, this)
+        }
+
+        val readPrefs = fileSystem.read(testFile.path) {
+            preferencesSerializer.readFrom(this)
+        }
+
+        assertEquals(prefs, readPrefs)
+    }
+
+    @Test
+    fun testWriteAndReadLong() = doTest {
+        val longKey = longPreferencesKey("long_key")
+
+        val prefs = preferencesOf(
+            longKey to (1L shr 50)
+        )
+
+        fileSystem.write(testFile.path) {
+            preferencesSerializer.writeTo(prefs, this)
+        }
+
+        val readPrefs = fileSystem.read(testFile.path) {
+            preferencesSerializer.readFrom(this)
+        }
+
+        assertEquals(prefs, readPrefs)
+    }
+
+    @Test
+    fun testWriteAndReadInt() = doTest {
+        val intKey = intPreferencesKey("int_key")
+
+        val prefs = preferencesOf(
+            intKey to 3
+        )
+
+        fileSystem.write(testFile.path) {
+            preferencesSerializer.writeTo(prefs, this)
+        }
+
+        val readPrefs = fileSystem.read(testFile.path) {
+            preferencesSerializer.readFrom(this)
+        }
+
+        assertEquals(prefs, readPrefs)
+    }
+
+    @Test
+    fun testWriteAndReadBoolean() = doTest {
+        val booleanKey = booleanPreferencesKey("boolean_key")
+
+        val prefs = preferencesOf(
+            booleanKey to true
+        )
+
+        fileSystem.write(testFile.path) {
+            preferencesSerializer.writeTo(prefs, this)
+        }
+
+        val readPrefs = fileSystem.read(testFile.path) {
+            preferencesSerializer.readFrom(this)
+        }
+
+        assertEquals(prefs, readPrefs)
+    }
+
+    @Test
+    fun testWriteAndReadFloat() = doTest {
+        val floatKey = floatPreferencesKey("float_key")
+
+        val prefs = preferencesOf(
+            floatKey to 3.0f
+        )
+
+        fileSystem.write(testFile.path) {
+            preferencesSerializer.writeTo(prefs, this)
+        }
+
+        val readPrefs = fileSystem.read(testFile.path) {
+            preferencesSerializer.readFrom(this)
+        }
+
+        assertEquals(prefs, readPrefs)
+    }
+
+    @Test
+    fun testWriteAndReadDouble() = doTest {
+        val maxDouble = doublePreferencesKey("max_double_key")
+        val minDouble = doublePreferencesKey("min_double_key")
+
+        val prefs = preferencesOf(
+            maxDouble to Double.MAX_VALUE,
+            minDouble to Double.MIN_VALUE
+        )
+
+        fileSystem.write(testFile.path) {
+            preferencesSerializer.writeTo(prefs, this)
+        }
+
+        val readPrefs = fileSystem.read(testFile.path) {
+            preferencesSerializer.readFrom(this)
+        }
+
+        assertEquals(prefs, readPrefs)
+    }
+
+    @Test
+    fun testWriteAndReadByteArray() = doTest {
+        val byteArrayKey = byteArrayPreferencesKey("byteArray")
+
+        val prefs = preferencesOf(
+            byteArrayKey to byteArrayOf(1, 2, 3, 4)
+        )
+
+        fileSystem.write(testFile.path) {
+            preferencesSerializer.writeTo(prefs, this)
+        }
+
+        val readPrefs = fileSystem.read(testFile.path) {
+            preferencesSerializer.readFrom(this)
+        }
+
+        assertEquals(prefs, readPrefs)
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesTest.kt b/datastore/datastore-preferences-core/src/commonTest/kotlin/androidx/datastore/preferences/core/PreferencesTest.kt
similarity index 81%
rename from datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesTest.kt
rename to datastore/datastore-preferences-core/src/commonTest/kotlin/androidx/datastore/preferences/core/PreferencesTest.kt
index 22db185..7f2dc36 100644
--- a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesTest.kt
+++ b/datastore/datastore-preferences-core/src/commonTest/kotlin/androidx/datastore/preferences/core/PreferencesTest.kt
@@ -16,17 +16,12 @@
 
 package androidx.datastore.preferences.core
 
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import java.lang.UnsupportedOperationException
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-import kotlin.test.assertNotEquals
+import kotlin.test.Test
 import kotlin.test.assertNull
 import kotlin.test.assertTrue
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
 
-@RunWith(JUnit4::class)
 class PreferencesTest {
 
     @Test
@@ -98,7 +93,7 @@
     fun testLong() {
         val longKey = longPreferencesKey("long_key")
 
-        val bigLong = 1L shr 50; // 2^50 > Int.MAX_VALUE
+        val bigLong = 1L shr 50 // 2^50 > Int.MAX_VALUE
 
         val prefs = preferencesOf(longKey to bigLong)
 
@@ -158,31 +153,6 @@
     }
 
     @Test
-    fun testModifyingStringSetDoesntModifyInternalState() {
-        val stringSetKey =
-            stringSetPreferencesKey("string_set_key")
-
-        val stringSet = mutableSetOf("1", "2", "3")
-
-        val prefs = preferencesOf(stringSetKey to stringSet)
-
-        stringSet.add("4") // modify the set passed into preferences
-
-        // modify the returned set.
-        val returnedSet: Set<String> = prefs[stringSetKey]!!
-        val mutableReturnedSet: MutableSet<String> = returnedSet as MutableSet<String>
-
-        assertFailsWith<UnsupportedOperationException> {
-            mutableReturnedSet.clear()
-        }
-        assertFailsWith<UnsupportedOperationException> {
-            mutableReturnedSet.add("Original set does not contain this string")
-        }
-
-        assertEquals(setOf("1", "2", "3"), prefs[stringSetKey])
-    }
-
-    @Test
     fun testByteArray() {
         val byteArrayKey = byteArrayPreferencesKey("byte_array_key")
         val byteArray = byteArrayOf(1, 2, 3, 4)
@@ -225,32 +195,6 @@
     }
 
     @Test
-    @Suppress("UNUSED_VARIABLE")
-    fun testWrongTypeThrowsClassCastException() {
-        val stringKey = stringPreferencesKey("string_key")
-        val intKey =
-            intPreferencesKey("string_key") // long key of the same name as stringKey!
-        val longKey = longPreferencesKey("string_key")
-
-        val prefs = preferencesOf(intKey to 123456)
-
-        assertTrue { prefs.contains(intKey) }
-        assertTrue { prefs.contains(stringKey) } // TODO: I don't think we can prevent this
-
-        // Trying to get a long where there is an Int value throws a ClassCastException.
-        assertFailsWith<ClassCastException> {
-            var unused = prefs[stringKey] // This only throws if it's assigned to a
-            // variable
-        }
-
-        // Trying to get a Long where there is an Int value throws a ClassCastException.
-        assertFailsWith<ClassCastException> {
-            var unused = prefs[longKey] // This only throws if it's assigned to a
-            // variable
-        }
-    }
-
-    @Test
     fun testGetAll() {
         val intKey = intPreferencesKey("int_key")
         val stringSetKey =
@@ -269,30 +213,6 @@
     }
 
     @Test
-    @Suppress("UNCHECKED_CAST")
-    fun testGetAllCantMutateInternalState() {
-        val intKey = intPreferencesKey("int_key")
-        val stringSetKey =
-            stringSetPreferencesKey("string_set_key")
-
-        val prefs = preferencesOf(
-            intKey to 123,
-            stringSetKey to setOf("1", "2", "3")
-        )
-
-        val mutableAllPreferences = prefs.asMap() as MutableMap
-        assertFailsWith<UnsupportedOperationException> {
-            mutableAllPreferences[intKey] = 99999
-        }
-        assertFailsWith<UnsupportedOperationException> {
-            (mutableAllPreferences[stringSetKey] as MutableSet<String>).clear()
-        }
-
-        assertEquals(123, prefs[intKey])
-        assertEquals(setOf("1", "2", "3"), prefs[stringSetKey])
-    }
-
-    @Test
     fun testMutablePreferencesClear() {
         val intKey = intPreferencesKey("int_key")
 
diff --git a/datastore/datastore-preferences-core/src/jvmMain/kotlin/androidx/datastore/preferences/core/Actual.jvm.kt b/datastore/datastore-preferences-core/src/jvmMain/kotlin/androidx/datastore/preferences/core/Actual.jvm.kt
new file mode 100644
index 0000000..89dd3e5
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/jvmMain/kotlin/androidx/datastore/preferences/core/Actual.jvm.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+
+package androidx.datastore.preferences.core
+
+import androidx.annotation.RestrictTo
+
+import androidx.datastore.core.okio.OkioSerializer
+import java.util.Collections
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+
+internal actual fun <K, V> immutableMap(map: Map<K, V>): Map<K, V> {
+    return Collections.unmodifiableMap(map)
+}
+
+internal actual fun <T> immutableCopyOfSet(set: Set<T>): Set<T> =
+    Collections.unmodifiableSet(set.toSet())
+
+internal actual fun ioDispatcher(): CoroutineDispatcher = Dispatchers.IO
+
+internal actual class AtomicBoolean actual constructor(initialValue: Boolean) {
+    private val delegate: java.util.concurrent.atomic.AtomicBoolean
+
+    actual fun set(value: Boolean) = delegate.set(value)
+
+    actual fun get(): Boolean = delegate.get()
+
+    init {
+        delegate = AtomicBoolean(initialValue)
+    }
+}
+
+internal actual fun getPreferencesSerializer(): OkioSerializer<Preferences> {
+    return PreferencesSerializer
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/jvmMain/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactory.jvm.kt b/datastore/datastore-preferences-core/src/jvmMain/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactory.jvm.kt
new file mode 100644
index 0000000..f54a2e4
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/jvmMain/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactory.jvm.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 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.datastore.preferences.core
+
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.DataMigration
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.core.Storage
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
+import androidx.datastore.core.okio.OkioStorage
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import java.io.File
+import okio.FileSystem
+import okio.Path.Companion.toOkioPath
+
+/**
+ * Public factory for creating PreferenceDataStore instances.
+ */
+public actual object PreferenceDataStoreFactory {
+    /**
+     * Create an instance of SingleProcessDataStore. Never create more than one instance of
+     * DataStore for a given file; doing so can break all DataStore functionality. You should
+     * consider managing your DataStore instance as a singleton.
+     *
+     * @param corruptionHandler The corruptionHandler is invoked if DataStore encounters a
+     * [CorruptionException] when attempting to read data. CorruptionExceptions are thrown by
+     * serializers when data cannot be de-serialized.
+     * @param migrations are run before any access to data can occur. Each producer and migration
+     * may be run more than once whether or not it already succeeded (potentially because another
+     * migration failed or a write to disk failed.)
+     * @param scope The scope in which IO operations and transform functions will execute.
+     * @param produceFile Function which returns the file that the new DataStore will act on.
+     * The function must return the same path every time. No two instances of PreferenceDataStore
+     * should act on the same file at the same time. The file must have the extension
+     * preferences_pb.
+     *
+     * @return a new DataStore instance with the provided configuration
+     */
+    @JvmOverloads
+    public fun create(
+        corruptionHandler: ReplaceFileCorruptionHandler<Preferences>? = null,
+        migrations: List<DataMigration<Preferences>> = listOf(),
+        scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
+        produceFile: () -> File
+    ): DataStore<Preferences> {
+        val delegate = create(
+            storage = OkioStorage(FileSystem.SYSTEM, PreferencesSerializer) {
+                val file = produceFile()
+                check(file.extension == PreferencesSerializer.fileExtension) {
+                    "File extension for file: $file does not match required extension for" +
+                        " Preferences file: ${PreferencesSerializer.fileExtension}"
+                }
+                file.absoluteFile.toOkioPath()
+            },
+            corruptionHandler = corruptionHandler,
+            migrations = migrations,
+            scope = scope
+        )
+        return PreferenceDataStore(delegate)
+    }
+
+    /**
+     * Create an instance of SingleProcessDataStore. Never create more than one instance of
+     * DataStore for a given file; doing so can break all DataStore functionality. You should
+     * consider managing your DataStore instance as a singleton.
+     *
+     * @param storage The storage object defines where and how the preferences will be stored.
+     * @param corruptionHandler The corruptionHandler is invoked if DataStore encounters a
+     * [CorruptionException] when attempting to read data. CorruptionExceptions are thrown by
+     * serializers when data cannot be de-serialized.
+     * @param migrations are run before any access to data can occur. Each producer and migration
+     * may be run more than once whether or not it already succeeded (potentially because another
+     * migration failed or a write to disk failed.)
+     * @param scope The scope in which IO operations and transform functions will execute.
+     *
+     * @return a new DataStore instance with the provided configuration
+     */
+    @JvmOverloads
+    public actual fun create(
+        storage: Storage<Preferences>,
+        corruptionHandler: ReplaceFileCorruptionHandler<Preferences>?,
+        migrations: List<DataMigration<Preferences>>,
+        scope: CoroutineScope,
+    ): DataStore<Preferences> {
+        return PreferenceDataStore(DataStoreFactory.create(
+            storage = storage,
+            corruptionHandler = corruptionHandler,
+            migrations = migrations,
+            scope = scope
+        ))
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesSerializer.kt b/datastore/datastore-preferences-core/src/jvmMain/kotlin/androidx/datastore/preferences/core/PreferencesSerializer.kt
similarity index 89%
rename from datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesSerializer.kt
rename to datastore/datastore-preferences-core/src/jvmMain/kotlin/androidx/datastore/preferences/core/PreferencesSerializer.kt
index 9c2fc52..2418e46 100644
--- a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesSerializer.kt
+++ b/datastore/datastore-preferences-core/src/jvmMain/kotlin/androidx/datastore/preferences/core/PreferencesSerializer.kt
@@ -20,13 +20,13 @@
 import androidx.datastore.preferences.PreferencesProto.PreferenceMap
 import androidx.datastore.preferences.PreferencesProto.Value
 import androidx.datastore.preferences.PreferencesProto.StringSet
-import androidx.datastore.core.Serializer
+import androidx.datastore.core.okio.OkioSerializer
 import androidx.datastore.preferences.PreferencesMapCompat
 import androidx.datastore.preferences.protobuf.ByteString
-import java.io.IOException
-import java.io.InputStream
-import java.io.OutputStream
 import kotlin.jvm.Throws
+import okio.BufferedSink
+import okio.BufferedSource
+import okio.IOException
 
 /**
  * Proto based serializer for Preferences.
@@ -34,7 +34,7 @@
  * TODO(b/156533452): this is a temporary implementation to allow for development. This will be
  * replaced before launching.
  */
-internal object PreferencesSerializer : Serializer<Preferences> {
+internal object PreferencesSerializer : OkioSerializer<Preferences> {
     val fileExtension = "preferences_pb"
 
     override val defaultValue: Preferences
@@ -43,8 +43,8 @@
         }
 
     @Throws(IOException::class, CorruptionException::class)
-    override suspend fun readFrom(input: InputStream): Preferences {
-        val preferencesProto = PreferencesMapCompat.readFrom(input)
+    override suspend fun readFrom(source: BufferedSource): Preferences {
+        val preferencesProto = PreferencesMapCompat.readFrom(source.inputStream())
 
         val mutablePreferences = mutablePreferencesOf()
 
@@ -56,7 +56,7 @@
     }
 
     @Throws(IOException::class, CorruptionException::class)
-    override suspend fun writeTo(t: Preferences, output: OutputStream) {
+    override suspend fun writeTo(t: Preferences, sink: BufferedSink) {
         val preferences = t.asMap()
         val protoBuilder = PreferenceMap.newBuilder()
 
@@ -64,7 +64,7 @@
             protoBuilder.putPreferences(key.name, getValueProto(value))
         }
 
-        protoBuilder.build().writeTo(output)
+        protoBuilder.build().writeTo(sink.outputStream())
     }
 
     private fun getValueProto(value: Any): Value {
diff --git a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferenceDataStoreFactoryTest.kt b/datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactoryTest.kt
similarity index 100%
rename from datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferenceDataStoreFactoryTest.kt
rename to datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactoryTest.kt
diff --git a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesFromJavaTest.java b/datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferencesFromJavaTest.java
similarity index 100%
rename from datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesFromJavaTest.java
rename to datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferencesFromJavaTest.java
diff --git a/datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferencesSerializerJavaTest.kt b/datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferencesSerializerJavaTest.kt
new file mode 100644
index 0000000..fa08b90
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferencesSerializerJavaTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 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.datastore.preferences.core
+
+import androidx.datastore.OkioPath
+import androidx.datastore.OkioTestIO
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.okio.OkioSerializer
+import kotlinx.coroutines.test.runTest
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+import kotlin.test.assertTrue
+import kotlinx.coroutines.test.TestScope
+import okio.FileSystem
+
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+@kotlinx.coroutines.ObsoleteCoroutinesApi
+@kotlinx.coroutines.FlowPreview
+class PreferencesSerializerJavaTest {
+
+    private val testIO = OkioTestIO()
+
+    private lateinit var testFile: OkioPath
+    private val preferencesSerializer: OkioSerializer<Preferences> = getPreferencesSerializer()
+    private val fileSystem: FileSystem = FileSystem.SYSTEM
+
+    @BeforeTest
+    fun setUp() {
+        testFile = testIO.newTempFile()
+        fileSystem.createDirectories(testFile.path.parent!!)
+    }
+    fun doTest(test: suspend TestScope.() -> Unit) {
+        runTest(dispatchTimeoutMs = 10000) {
+            test(this)
+        }
+    }
+
+    @Test
+    fun testThrowsCorruptionException() = doTest {
+        // Not a valid proto - protos cannot start with a 0 byte.
+        fileSystem.write(testFile.path) {
+            this.write(byteArrayOf(0, 1, 2, 3, 4))
+        }
+
+        assertFailsWith<CorruptionException> {
+            fileSystem.read(testFile.path) {
+                preferencesSerializer.readFrom(this)
+            }
+        }
+    }
+
+    @Test
+    @Suppress("UNCHECKED_CAST")
+    fun testGetAllCantMutateInternalState() {
+        val intKey = intPreferencesKey("int_key")
+        val stringSetKey =
+            stringSetPreferencesKey("string_set_key")
+
+        val prefs = preferencesOf(
+            intKey to 123,
+            stringSetKey to setOf("1", "2", "3")
+        )
+
+        val mutableAllPreferences = prefs.asMap() as MutableMap
+        assertFailsWith<UnsupportedOperationException> {
+            mutableAllPreferences[intKey] = 99999
+        }
+        assertFailsWith<UnsupportedOperationException> {
+            (mutableAllPreferences[stringSetKey] as MutableSet<String>).clear()
+        }
+
+        assertEquals(123, prefs[intKey])
+        assertEquals(setOf("1", "2", "3"), prefs[stringSetKey])
+    }
+
+    @Test
+    fun testModifyingStringSetDoesntModifyInternalState() {
+        val stringSetKey =
+            stringSetPreferencesKey("string_set_key")
+
+        val stringSet = mutableSetOf("1", "2", "3")
+
+        val prefs = preferencesOf(stringSetKey to stringSet)
+
+        stringSet.add("4") // modify the set passed into preferences
+
+        // modify the returned set.
+        val returnedSet: Set<String> = prefs[stringSetKey]!!
+        val mutableReturnedSet: MutableSet<String> = returnedSet as MutableSet<String>
+
+        assertFailsWith<UnsupportedOperationException> {
+            mutableReturnedSet.clear()
+        }
+        assertFailsWith<UnsupportedOperationException> {
+            mutableReturnedSet.add("Original set does not contain this string")
+        }
+
+        assertEquals(setOf("1", "2", "3"), prefs[stringSetKey])
+    }
+
+    // TODO: This doesn't pass on native: https://youtrack.jetbrains.com/issue/KT-42903
+    @Test
+    @Suppress("UNUSED_VARIABLE")
+    fun testWrongTypeThrowsClassCastException() {
+        val stringKey = stringPreferencesKey("string_key")
+        val intKey =
+            intPreferencesKey("string_key") // long key of the same name as stringKey!
+        val longKey = longPreferencesKey("string_key")
+
+        val prefs = preferencesOf(intKey to 123456)
+
+        assertTrue { prefs.contains(intKey) }
+        assertTrue { prefs.contains(stringKey) } // TODO: I don't think we can prevent this
+
+        // Trying to get a long where there is an Int value throws a ClassCastException.
+        assertFailsWith<ClassCastException> {
+            var unused = prefs[stringKey] // This only throws if it's assigned to a
+            // variable
+        }
+
+        // Trying to get a Long where there is an Int value throws a ClassCastException.
+        assertFailsWith<ClassCastException> {
+            var unused = prefs[longKey] // This only throws if it's assigned to a
+            // variable
+        }
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferenceDataStoreFactory.kt b/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferenceDataStoreFactory.kt
deleted file mode 100644
index ed8110d..0000000
--- a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferenceDataStoreFactory.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2020 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.datastore.preferences.core
-
-import androidx.datastore.core.CorruptionException
-import androidx.datastore.core.DataMigration
-import androidx.datastore.core.DataStore
-import androidx.datastore.core.DataStoreFactory
-import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.SupervisorJob
-import java.io.File
-
-/**
- * Public factory for creating PreferenceDataStore instances.
- */
-public object PreferenceDataStoreFactory {
-    /**
-     * Create an instance of SingleProcessDataStore. Never create more than one instance of
-     * DataStore for a given file; doing so can break all DataStore functionality. You should
-     * consider managing your DataStore instance as a singleton.
-     *
-     * @param produceFile Function which returns the file that the new DataStore will act on.
-     * The function must return the same path every time. No two instances of PreferenceDataStore
-     * should act on the same file at the same time. The file must have the extension
-     * preferences_pb.
-     * @param corruptionHandler The corruptionHandler is invoked if DataStore encounters a
-     * [CorruptionException] when attempting to read data. CorruptionExceptions are thrown by
-     * serializers when data cannot be de-serialized.
-     * @param migrations are run before any access to data can occur. Each producer and migration
-     * may be run more than once whether or not it already succeeded (potentially because another
-     * migration failed or a write to disk failed.)
-     * @param scope The scope in which IO operations and transform functions will execute.
-     * @param produceFile Function which returns the file that the new DataStore will act on.
-     * The function must return the same path every time. No two instances of PreferenceDataStore
-     * should act on the same file at the same time. The file must have the extension
-     * preferences_pb.
-     *
-     * @return a new DataStore instance with the provided configuration
-     */
-    @JvmOverloads
-    public fun create(
-        corruptionHandler: ReplaceFileCorruptionHandler<Preferences>? = null,
-        migrations: List<DataMigration<Preferences>> = listOf(),
-        scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
-        produceFile: () -> File
-    ): DataStore<Preferences> {
-        val delegate = DataStoreFactory.create(
-            serializer = PreferencesSerializer,
-            corruptionHandler = corruptionHandler,
-            migrations = migrations,
-            scope = scope
-        ) {
-            val file = produceFile()
-            check(file.extension == PreferencesSerializer.fileExtension) {
-                "File extension for file: $file does not match required extension for" +
-                    " Preferences file: ${PreferencesSerializer.fileExtension}"
-            }
-            file
-        }
-        return PreferenceDataStore(delegate)
-    }
-}
-
-internal class PreferenceDataStore(private val delegate: DataStore<Preferences>) :
-    DataStore<Preferences> by delegate {
-    override suspend fun updateData(transform: suspend (t: Preferences) -> Preferences):
-        Preferences {
-            return delegate.updateData {
-                val transformed = transform(it)
-                // Freeze the preferences since any future mutations will break DataStore. If a user
-                // tunnels the value out of DataStore and mutates it, this could be problematic.
-                // This is a safe cast, since MutablePreferences is the only implementation of
-                // Preferences.
-                (transformed as MutablePreferences).freeze()
-                transformed
-            }
-        }
-}
diff --git a/datastore/datastore-preferences-core/src/nativeMain/kotlin/androidx/datastore/preferences/core/Actual.native.kt b/datastore/datastore-preferences-core/src/nativeMain/kotlin/androidx/datastore/preferences/core/Actual.native.kt
new file mode 100644
index 0000000..ed2de2f
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/nativeMain/kotlin/androidx/datastore/preferences/core/Actual.native.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 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.datastore.preferences.core
+
+import androidx.datastore.core.okio.OkioSerializer
+import kotlinx.atomicfu.atomic
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+
+internal actual fun <K, V> immutableMap(map: Map<K, V>): Map<K, V> {
+    // TODO:(b/239829063) Find a replacement for java's unmodifyable map.  For now just make a copy.
+    return map.toMap()
+}
+
+// TODO:(b/239829063) Find a replacement for java's unmodifyable set.  For now just make a copy.
+internal actual fun <T> immutableCopyOfSet(set: Set<T>): Set<T> = set.toSet()
+
+internal actual class AtomicBoolean actual constructor(initialValue: Boolean) {
+    private var delegate: kotlinx.atomicfu.AtomicBoolean = atomic(initialValue)
+    private var property by delegate
+
+    actual fun get(): Boolean = property
+
+    actual fun set(value: Boolean) {
+        property = value
+    }
+}
+
+// TODO(b/234049307): Pick a better dispatcher for IO
+internal actual fun ioDispatcher(): CoroutineDispatcher = Dispatchers.Default
+
+internal actual fun getPreferencesSerializer(): OkioSerializer<Preferences> {
+    return PreferencesSerializationSerializer
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/nativeMain/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactory.native.kt b/datastore/datastore-preferences-core/src/nativeMain/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactory.native.kt
new file mode 100644
index 0000000..c3ee75d
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/nativeMain/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactory.native.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 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.datastore.preferences.core
+
+import androidx.datastore.core.DataMigration
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.core.Storage
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
+import androidx.datastore.core.okio.OkioStorage
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import okio.FileSystem
+import okio.Path
+
+actual object PreferenceDataStoreFactory {
+    /**
+     * Create an instance of SingleProcessDataStore. Never create more than one instance of
+     * DataStore for a given file; doing so can break all DataStore functionality. You should
+     * consider managing your DataStore instance as a singleton.
+     *
+     * @param corruptionHandler The corruptionHandler is invoked if DataStore encounters a
+     * [CorruptionException] when attempting to read data. CorruptionExceptions are thrown by
+     * serializers when data cannot be de-serialized.
+     * @param migrations are run before any access to data can occur. Each producer and migration
+     * may be run more than once whether or not it already succeeded (potentially because another
+     * migration failed or a write to disk failed.)
+     * @param scope The scope in which IO operations and transform functions will execute.
+     * @param produceFile Function which returns the file that the new DataStore will act on.
+     * The function must return the same path every time. No two instances of PreferenceDataStore
+     * should act on the same file at the same time. The file must have the extension
+     * preferences_pb.
+     *
+     * @return a new DataStore instance with the provided configuration
+     */
+    public fun create(
+        corruptionHandler: ReplaceFileCorruptionHandler<Preferences>? = null,
+        migrations: List<DataMigration<Preferences>> = listOf(),
+        scope: CoroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob()),
+        produceFile: () -> Path
+    ): DataStore<Preferences> {
+        val delegate = create(
+            storage = OkioStorage(FileSystem.SYSTEM, PreferencesSerializationSerializer) {
+                val file = produceFile()
+                check(file.name.endsWith(".${PreferencesSerializationSerializer.fileExtension}")) {
+                    "File extension for file: $file does not match required extension for" +
+                        " Preferences file: ${PreferencesSerializationSerializer.fileExtension}"
+                }
+                file
+            },
+            corruptionHandler = corruptionHandler,
+            migrations = migrations,
+            scope = scope
+        )
+        return PreferenceDataStore(delegate)
+    }
+
+    /**
+     * Create an instance of SingleProcessDataStore. Never create more than one instance of
+     * DataStore for a given file; doing so can break all DataStore functionality. You should
+     * consider managing your DataStore instance as a singleton.
+     *
+     * @param storage The storage object defines where and how the preferences will be stored.
+     * @param corruptionHandler The corruptionHandler is invoked if DataStore encounters a
+     * [CorruptionException] when attempting to read data. CorruptionExceptions are thrown by
+     * serializers when data cannot be de-serialized.
+     * @param migrations are run before any access to data can occur. Each producer and migration
+     * may be run more than once whether or not it already succeeded (potentially because another
+     * migration failed or a write to disk failed.)
+     * @param scope The scope in which IO operations and transform functions will execute.
+     *
+     * @return a new DataStore instance with the provided configuration
+     */
+    public actual fun create(
+        storage: Storage<Preferences>,
+        corruptionHandler: ReplaceFileCorruptionHandler<Preferences>?,
+        migrations: List<DataMigration<Preferences>>,
+        scope: CoroutineScope,
+    ): DataStore<Preferences> {
+        return PreferenceDataStore(
+            DataStoreFactory.create(
+            storage = storage,
+            corruptionHandler = corruptionHandler,
+            migrations = migrations,
+            scope = scope
+        ))
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/nativeMain/kotlin/androidx/datastore/preferences/core/PreferencesSerializationSerializer.kt b/datastore/datastore-preferences-core/src/nativeMain/kotlin/androidx/datastore/preferences/core/PreferencesSerializationSerializer.kt
new file mode 100644
index 0000000..b27e26f
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/nativeMain/kotlin/androidx/datastore/preferences/core/PreferencesSerializationSerializer.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright 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.datastore.preferences.core
+
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.okio.OkioSerializer
+import okio.BufferedSink
+import okio.BufferedSource
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.decodeFromByteArray
+import kotlinx.serialization.encodeToByteArray
+import kotlinx.serialization.protobuf.ProtoBuf
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.SerializationException
+
+@OptIn(ExperimentalSerializationApi::class)
+internal object PreferencesSerializationSerializer : OkioSerializer<Preferences> {
+    val fileExtension = "preferences_pb"
+
+    override val defaultValue: Preferences
+        get() = emptyPreferences()
+
+    override suspend fun readFrom(source: BufferedSource): Preferences {
+        val prefMap: PreferencesMap = try {
+            ProtoBuf.decodeFromByteArray<PreferencesMap>(
+                source.readByteArray())
+        } catch (e: SerializationException) {
+            throw CorruptionException("Unable to parse preferences proto.", e)
+        }
+
+        val mutablePreferences = mutablePreferencesOf()
+
+        prefMap.preferences.forEach { (name, value) ->
+            addProtoEntryToPreferences(name, value, mutablePreferences)
+        }
+
+        return mutablePreferences.toPreferences()
+    }
+
+    override suspend fun writeTo(t: Preferences, sink: BufferedSink) {
+        val preferences = t.asMap()
+        val prefMap = mutableMapOf<String, Value>()
+        for ((key, value) in preferences) {
+            prefMap[key.name] = getValueProto(value)
+        }
+        val byteArray: ByteArray = ProtoBuf.encodeToByteArray(PreferencesMap(prefMap))
+        sink.write(byteArray)
+    }
+
+    private fun addProtoEntryToPreferences(
+        name: String,
+        value: Value,
+        mutablePreferences: MutablePreferences
+    ) {
+        if (value.boolean != null) {
+            mutablePreferences[booleanPreferencesKey(name)] = value.boolean
+        } else if (value.float != null) {
+            mutablePreferences[floatPreferencesKey(name)] = value.float
+        } else if (value.double != null) {
+            mutablePreferences[doublePreferencesKey(name)] = value.double
+        } else if (value.integer != null) {
+            mutablePreferences[intPreferencesKey(name)] = value.integer
+        } else if (value.long != null) {
+            mutablePreferences[longPreferencesKey(name)] = value.long
+        } else if (value.string != null) {
+            mutablePreferences[stringPreferencesKey(name)] = value.string
+        } else if (value.stringSet != null) {
+            mutablePreferences[stringSetPreferencesKey(name)] =
+                value.stringSet.strings.toSet()
+        } else if (value.bytes != null) {
+            mutablePreferences[byteArrayPreferencesKey(name)] = value.bytes
+        } else {
+            throw CorruptionException("Value case is null.")
+        }
+    }
+
+    private fun getValueProto(value: Any): Value {
+        return when (value) {
+            is Boolean -> Value(boolean = value)
+            is Float -> Value(float = value)
+            is Double -> Value(double = value)
+            is Int -> Value(integer = value)
+            is Long -> Value(long = value)
+            is String -> Value(string = value)
+            is Set<*> ->
+                @Suppress("UNCHECKED_CAST")
+                Value(stringSet = StringSet(strings = value.map { it.toString() }))
+            is ByteArray -> Value(bytes = value.copyOf())
+            else -> throw IllegalStateException(
+                "PreferencesSerializer does not support type: ${value::class}"
+            )
+        }
+    }
+}
+
+// These data classes below map directly to
+// datastore/datastore-preferences-proto/src/main/proto/preferences.proto
+@Serializable
+internal data class PreferencesMap(
+    val preferences: Map<String, Value> = emptyMap()
+)
+
+@Serializable
+internal data class Value(
+    val boolean: Boolean? = null,
+    val float: Float? = null,
+    val integer: Int? = null,
+    val long: Long? = null,
+    val string: String? = null,
+    val stringSet: StringSet? = null,
+    val double: Double? = null,
+    val bytes: ByteArray? = null,
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Value) return false
+
+        if (boolean != other.boolean) return false
+        if (float != other.float) return false
+        if (integer != other.integer) return false
+        if (long != other.long) return false
+        if (string != other.string) return false
+        if (stringSet != other.stringSet) return false
+        if (double != other.double) return false
+        if (bytes != null) {
+            if (other.bytes == null) return false
+            if (!bytes.contentEquals(other.bytes)) return false
+        } else if (other.bytes != null) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = boolean?.hashCode() ?: 0
+        result = 31 * result + (float?.hashCode() ?: 0)
+        result = 31 * result + (integer ?: 0)
+        result = 31 * result + (long?.hashCode() ?: 0)
+        result = 31 * result + (string?.hashCode() ?: 0)
+        result = 31 * result + stringSet.hashCode()
+        result = 31 * result + (double?.hashCode() ?: 0)
+        result = 31 * result + (bytes?.contentHashCode() ?: 0)
+        return result
+    }
+}
+
+@Serializable
+internal data class StringSet(
+    val strings: List<String> = emptyList(),
+)
diff --git a/datastore/datastore-preferences-core/src/nativeTest/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactoryNativeTest.kt b/datastore/datastore-preferences-core/src/nativeTest/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactoryNativeTest.kt
new file mode 100644
index 0000000..7d49acc
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/nativeTest/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactoryNativeTest.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2020 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.datastore.preferences.core
+
+import androidx.datastore.OkioTestIO
+import androidx.datastore.core.DataMigration
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import okio.FileSystem
+import okio.Path
+import okio.Path.Companion.toPath
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class PreferenceDataStoreFactoryNativeTest {
+
+    private lateinit var testIO: OkioTestIO
+    private lateinit var testFile: Path
+    private lateinit var dataStoreScope: TestScope
+
+    val stringKey = stringPreferencesKey("key")
+    val booleanKey = booleanPreferencesKey("key")
+
+    @BeforeTest
+    fun setUp() {
+        testIO = OkioTestIO()
+        testFile = testIO.tempDir().path / "test.preferences_pb".toPath()
+        dataStoreScope = TestScope(UnconfinedTestDispatcher())
+    }
+
+    @Test
+    fun testNewInstance() = runTest {
+        val store = PreferenceDataStoreFactory.create(
+            scope = dataStoreScope
+        ) { testFile }
+
+        val expectedPreferences =
+            preferencesOf(stringKey to "value")
+
+        assertEquals(
+            store.edit { prefs ->
+                prefs[stringKey] = "value"
+            },
+            expectedPreferences
+        )
+        assertEquals(expectedPreferences, store.data.first())
+    }
+
+    @Test
+    fun testCorruptionHandlerInstalled() = runTest {
+        FileSystem.SYSTEM.createDirectories(testFile.parent!!, false)
+        FileSystem.SYSTEM.write(testFile, false) {
+            write("BadData".encodeToByteArray())
+        }
+
+        val valueToReplace = preferencesOf(booleanKey to true)
+
+        val store = PreferenceDataStoreFactory.create(
+            corruptionHandler = ReplaceFileCorruptionHandler<Preferences> {
+                valueToReplace
+            },
+            scope = dataStoreScope
+        ) { testFile }
+        assertEquals(valueToReplace, store.data.first())
+    }
+
+    @Test
+    fun testMigrationsInstalled() = runTest {
+
+        val expectedPreferences = preferencesOf(
+            stringKey to "value",
+            booleanKey to true
+        )
+
+        val migrateTo5 = object : DataMigration<Preferences> {
+            override suspend fun shouldMigrate(currentData: Preferences) = true
+
+            override suspend fun migrate(currentData: Preferences) =
+                currentData.toMutablePreferences().apply { set(stringKey, "value") }.toPreferences()
+
+            override suspend fun cleanUp() {}
+        }
+
+        val migratePlus1 = object : DataMigration<Preferences> {
+            override suspend fun shouldMigrate(currentData: Preferences) = true
+
+            override suspend fun migrate(currentData: Preferences) =
+                currentData.toMutablePreferences().apply { set(booleanKey, true) }.toPreferences()
+
+            override suspend fun cleanUp() {}
+        }
+
+        val store = PreferenceDataStoreFactory.create(
+            migrations = listOf(migrateTo5, migratePlus1),
+            scope = dataStoreScope
+        ) { testFile }
+
+        assertEquals(expectedPreferences, store.data.first())
+    }
+
+    @Test
+    fun testCantMutateInternalState() = runTest {
+        val store =
+            PreferenceDataStoreFactory.create(scope = dataStoreScope) { testFile }
+
+        var mutableReference: MutablePreferences? = null
+        store.edit {
+            mutableReference = it
+            it[stringKey] = "ABCDEF"
+        }
+
+        assertEquals(
+            store.data.first(),
+            preferencesOf(stringKey to "ABCDEF")
+        )
+
+        assertFailsWith<IllegalStateException> {
+            mutableReference!!.clear()
+        }
+
+        assertFailsWith<IllegalStateException> {
+            mutableReference!! += preferencesOf(stringKey to "abc")
+        }
+
+        assertFailsWith<IllegalStateException> {
+            mutableReference!! += stringKey to "abc"
+        }
+
+        assertFailsWith<IllegalStateException> {
+            mutableReference!! -= stringKey
+        }
+
+        assertFailsWith<IllegalStateException> {
+            mutableReference!!.remove(stringKey)
+        }
+
+        assertFailsWith<IllegalStateException> {
+            mutableReference!!.putAll(stringKey to "abc")
+        }
+
+        assertFailsWith<IllegalStateException> {
+            mutableReference!![stringKey] = "asdjkfajksdhljkasdhf"
+        }
+        assertEquals(
+            store.data.first(),
+            preferencesOf(stringKey to "ABCDEF")
+        )
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesSerializerTest.kt b/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesSerializerTest.kt
deleted file mode 100644
index c7b6f4c80..0000000
--- a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesSerializerTest.kt
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright 2020 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.datastore.preferences.core
-
-import androidx.datastore.core.CorruptionException
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.TemporaryFolder
-import java.io.File
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-
-@kotlinx.coroutines.ExperimentalCoroutinesApi
-@kotlinx.coroutines.ObsoleteCoroutinesApi
-@kotlinx.coroutines.FlowPreview
-class PreferencesSerializerTest {
-
-    @get:Rule
-    val tmp = TemporaryFolder()
-
-    private lateinit var testFile: File
-    private val preferencesSerializer = PreferencesSerializer
-
-    @Before
-    fun setUp() {
-        testFile = tmp.newFile()
-    }
-
-    @Test
-    fun testWriteAndReadString() = runTest {
-        val stringKey = stringPreferencesKey("string_key")
-
-        val prefs = preferencesOf(
-            stringKey to "string1"
-        )
-
-        testFile.outputStream().use {
-            preferencesSerializer.writeTo(prefs, it)
-        }
-
-        val readPrefs = testFile.inputStream().use {
-            preferencesSerializer.readFrom(it)
-        }
-
-        assertEquals(prefs, readPrefs)
-    }
-
-    @Test
-    fun testWriteAndReadStringSet() = runTest {
-        val stringSetKey =
-            stringSetPreferencesKey("string_set_key")
-
-        val prefs = preferencesOf(
-            stringSetKey to setOf("string1", "string2", "string3")
-        )
-
-        testFile.outputStream().use {
-            preferencesSerializer.writeTo(prefs, it)
-        }
-
-        val readPrefs = testFile.inputStream().use {
-            preferencesSerializer.readFrom(it)
-        }
-
-        assertEquals(prefs, readPrefs)
-    }
-
-    @Test
-    fun testWriteAndReadLong() = runTest {
-        val longKey = longPreferencesKey("long_key")
-
-        val prefs = preferencesOf(
-            longKey to (1L shr 50)
-        )
-
-        testFile.outputStream().use {
-            preferencesSerializer.writeTo(prefs, it)
-        }
-
-        val readPrefs = testFile.inputStream().use {
-            preferencesSerializer.readFrom(it)
-        }
-
-        assertEquals(prefs, readPrefs)
-    }
-
-    @Test
-    fun testWriteAndReadInt() = runTest {
-        val intKey = intPreferencesKey("int_key")
-
-        val prefs = preferencesOf(
-            intKey to 3
-        )
-
-        testFile.outputStream().use {
-            preferencesSerializer.writeTo(prefs, it)
-        }
-
-        val readPrefs = testFile.inputStream().use {
-            preferencesSerializer.readFrom(it)
-        }
-
-        assertEquals(prefs, readPrefs)
-    }
-
-    @Test
-    fun testWriteAndReadBoolean() = runTest {
-        val booleanKey = booleanPreferencesKey("boolean_key")
-
-        val prefs = preferencesOf(
-            booleanKey to true
-        )
-
-        testFile.outputStream().use {
-            preferencesSerializer.writeTo(prefs, it)
-        }
-
-        val readPrefs = testFile.inputStream().use {
-            preferencesSerializer.readFrom(it)
-        }
-
-        assertEquals(prefs, readPrefs)
-    }
-
-    @Test
-    fun testWriteAndReadFloat() = runTest {
-        val floatKey = floatPreferencesKey("float_key")
-
-        val prefs = preferencesOf(
-            floatKey to 3.0f
-        )
-
-        testFile.outputStream().use {
-            preferencesSerializer.writeTo(prefs, it)
-        }
-
-        val readPrefs = testFile.inputStream().use {
-            preferencesSerializer.readFrom(it)
-        }
-
-        assertEquals(prefs, readPrefs)
-    }
-
-    @Test
-    fun testWriteAndReadDouble() = runTest {
-        val maxDouble = doublePreferencesKey("max_double_key")
-        val minDouble = doublePreferencesKey("min_double_key")
-
-        val prefs = preferencesOf(
-            maxDouble to Double.MAX_VALUE,
-            minDouble to Double.MIN_VALUE
-        )
-
-        testFile.outputStream().use {
-            preferencesSerializer.writeTo(prefs, it)
-        }
-
-        val readPrefs = testFile.inputStream().use {
-            preferencesSerializer.readFrom(it)
-        }
-
-        assertEquals(prefs, readPrefs)
-    }
-
-    @Test
-    fun testThrowsCorruptionException() = runTest {
-        // Not a valid proto - protos cannot start with a 0 byte.
-        testFile.writeBytes(byteArrayOf(0, 1, 2, 3, 4))
-
-        assertFailsWith<CorruptionException> {
-            testFile.inputStream().use {
-                preferencesSerializer.readFrom(it)
-            }
-        }
-    }
-
-    @Test
-    fun testWriteAndReadByteArray() = runTest {
-        val byteArrayKey = byteArrayPreferencesKey("byteArray")
-
-        val prefs = preferencesOf(
-            byteArrayKey to byteArrayOf(1, 2, 3, 4)
-        )
-
-        testFile.outputStream().use {
-            preferencesSerializer.writeTo(prefs, it)
-        }
-
-        val readPrefs = testFile.inputStream().use {
-            preferencesSerializer.readFrom(it)
-        }
-
-        assertEquals(prefs, readPrefs)
-    }
-}
\ No newline at end of file
diff --git a/development/auto-version-updater/test_update_versions_for_release.py b/development/auto-version-updater/test_update_versions_for_release.py
index 11040b0..720033e 100755
--- a/development/auto-version-updater/test_update_versions_for_release.py
+++ b/development/auto-version-updater/test_update_versions_for_release.py
@@ -93,35 +93,63 @@
         higher_version = get_higher_version("1.4.0", "1.4.2")
         self.assertEqual("1.4.2", higher_version)
 
-    def test_should_update_version_in_library_versions_kt(self):
-        self.assertTrue(should_update_version_in_library_versions_toml(
+    def test_should_update_group_version_in_library_versions_toml(self):
+        self.assertTrue(should_update_group_version_in_library_versions_toml(
             "1.1.0-alpha01", "1.1.0-alpha02", "androidx.core"))
-        self.assertTrue(should_update_version_in_library_versions_toml(
+        self.assertTrue(should_update_group_version_in_library_versions_toml(
             "1.1.0-alpha01", "1.3.0-alpha01", "androidx.appcompat"))
-        self.assertFalse(should_update_version_in_library_versions_toml(
+        self.assertFalse(should_update_group_version_in_library_versions_toml(
             "1.1.0-alpha01", "1.0.0-alpha01", "androidx.work"))
 
-        self.assertTrue(should_update_version_in_library_versions_toml(
+        self.assertTrue(should_update_group_version_in_library_versions_toml(
             "1.0.0-beta04", "1.1.0-alpha02", "androidx.wear"))
-        self.assertTrue(should_update_version_in_library_versions_toml(
+        self.assertTrue(should_update_group_version_in_library_versions_toml(
             "1.0.0-beta04", "1.3.0-alpha01", "androidx.viewpager"))
-        self.assertFalse(should_update_version_in_library_versions_toml(
+        self.assertFalse(should_update_group_version_in_library_versions_toml(
             "1.0.0-beta04", "1.0.0-alpha01", "androidx.compose.foundation"))
 
-        self.assertFalse(should_update_version_in_library_versions_toml(
+        self.assertTrue(should_update_group_version_in_library_versions_toml(
+            "1.0.0-beta04", "1.1.0-alpha02", "androidx.tracing"))
+        self.assertTrue(should_update_group_version_in_library_versions_toml(
+            "1.0.0-beta04", "1.3.0-alpha01", "androidx.tracing"))
+        self.assertTrue(should_update_group_version_in_library_versions_toml(
+            "1.2.0", "1.3.0-alpha01", "androidx.tracing"))
+
+        self.assertFalse(should_update_group_version_in_library_versions_toml(
             "1.0.0-beta04", "1.1.0-alpha02", "androidx.car"))
-        self.assertFalse(should_update_version_in_library_versions_toml(
+        self.assertFalse(should_update_group_version_in_library_versions_toml(
             "1.0.0-beta04", "1.3.0-alpha01", "androidx.car"))
-        self.assertFalse(should_update_version_in_library_versions_toml(
+        self.assertFalse(should_update_group_version_in_library_versions_toml(
             "1.0.0-beta04", "1.0.0-alpha01", "androidx.car"))
 
-        self.assertFalse(should_update_version_in_library_versions_toml(
+        self.assertFalse(should_update_group_version_in_library_versions_toml(
             "1.0.0-beta04", "1.1.0-alpha02", "androidx.compose.compiler"))
-        self.assertFalse(should_update_version_in_library_versions_toml(
+        self.assertFalse(should_update_group_version_in_library_versions_toml(
             "1.0.0-beta04", "1.3.0-alpha01", "androidx.compose.compiler"))
-        self.assertFalse(should_update_version_in_library_versions_toml(
+        self.assertFalse(should_update_group_version_in_library_versions_toml(
             "1.2.0", "1.3.0-alpha01", "androidx.compose.compiler"))
 
+    def test_should_update_artifact_version_in_library_versions_toml(self):
+        self.assertTrue(should_update_artifact_version_in_library_versions_toml(
+            "1.1.0-alpha01", "1.1.0-alpha02", "core"))
+        self.assertTrue(should_update_artifact_version_in_library_versions_toml(
+            "1.1.0-alpha01", "1.3.0-alpha01", "appcompat"))
+        self.assertFalse(should_update_artifact_version_in_library_versions_toml(
+            "1.1.0-alpha01", "1.0.0-alpha01", "work-runtime"))
+
+        self.assertFalse(should_update_artifact_version_in_library_versions_toml(
+            "1.0.0-beta04", "1.1.0-alpha02", "tracing-perfetto"))
+        self.assertFalse(should_update_artifact_version_in_library_versions_toml(
+            "1.0.0-beta04", "1.3.0-alpha01", "tracing-perfetto"))
+        self.assertFalse(should_update_artifact_version_in_library_versions_toml(
+            "1.0.0-beta04", "1.0.0-alpha01", "tracing-perfetto"))
+        self.assertFalse(should_update_artifact_version_in_library_versions_toml(
+            "1.1.0-alpha02", "1.1.0-alpha03", "tracing-perfetto"))
+        self.assertFalse(should_update_artifact_version_in_library_versions_toml(
+            "1.1.0-alpha02", "1.1.0-alpha03", "tracing-perfetto-binary"))
+        self.assertFalse(should_update_artifact_version_in_library_versions_toml(
+            "1.1.0-alpha02", "1.1.0-alpha03", "tracing-perfetto-common"))
+
 
 class TestFileParsing(unittest.TestCase):
 
diff --git a/development/auto-version-updater/update_versions_for_release.py b/development/auto-version-updater/update_versions_for_release.py
index 8ce954a..95b2665 100755
--- a/development/auto-version-updater/update_versions_for_release.py
+++ b/development/auto-version-updater/update_versions_for_release.py
@@ -157,7 +157,7 @@
     return version_a
 
 
-def should_update_version_in_library_versions_toml(old_version, new_version, group_id):
+def should_update_group_version_in_library_versions_toml(old_version, new_version, group_id):
     """Whether or not this specific group ID and version should be updated.
 
     Returns true if the new_version is greater than the version in line
@@ -177,6 +177,26 @@
     return new_version == get_higher_version(old_version, new_version)
 
 
+def should_update_artifact_version_in_library_versions_toml(old_version, new_version, artifact_id):
+    """Whether or not this specific artifact ID and version should be updated.
+
+    Returns true if the new_version is greater than the version in line
+    and the artifact ID is not the set of artifact_ids_to_not_update.
+
+    Args:
+        old_version: the old version from libraryversions.toml file.
+        new_version: the version to check again.
+        artifact_id: artifact id of the version being considered
+
+    Returns:
+        True if should update version, false otherwise.
+    """
+    # If we hit a artifact ID we should not update, just return.
+    artifact_ids_to_not_update = ["tracing-perfetto", "tracing-perfetto-binary", "tracing-perfetto-common"]
+    if artifact_id in artifact_ids_to_not_update: return False
+    return new_version == get_higher_version(old_version, new_version)
+
+
 def increment_version(version):
     """Increments an androidx SemVer version.
 
@@ -249,7 +269,7 @@
     # First check any artifact ids with unique versions.
     if artifact_id_variable_name in library_versions["versions"]:
         old_version = library_versions["versions"][artifact_id_variable_name]
-        if should_update_version_in_library_versions_toml(old_version, new_version, group_id):
+        if should_update_artifact_version_in_library_versions_toml(old_version, new_version, artifact_id):
             library_versions["versions"][artifact_id_variable_name] = new_version
             updated_version = True
 
@@ -257,7 +277,7 @@
         # Then check any group ids.
         if group_id_variable_name in library_versions["versions"]:
             old_version = library_versions["versions"][group_id_variable_name]
-            if should_update_version_in_library_versions_toml(old_version, new_version, group_id):
+            if should_update_group_version_in_library_versions_toml(old_version, new_version, group_id):
                 library_versions["versions"][group_id_variable_name] = new_version
                 updated_version = True
 
diff --git a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
index e661776..b91b710 100644
--- a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
+++ b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
@@ -3167,10 +3167,6 @@
             return new ExifAttribute(IFD_FORMAT_SLONG, values.length, buffer.array());
         }
 
-        public static ExifAttribute createSLong(int value, ByteOrder byteOrder) {
-            return createSLong(new int[] {value}, byteOrder);
-        }
-
         public static ExifAttribute createByte(String value) {
             // Exception for GPSAltitudeRef tag
             if (value.length() == 1 && value.charAt(0) >= '0' && value.charAt(0) <= '1') {
@@ -3212,10 +3208,6 @@
             return new ExifAttribute(IFD_FORMAT_SRATIONAL, values.length, buffer.array());
         }
 
-        public static ExifAttribute createSRational(Rational value, ByteOrder byteOrder) {
-            return createSRational(new Rational[] {value}, byteOrder);
-        }
-
         public static ExifAttribute createDouble(double[] values, ByteOrder byteOrder) {
             final ByteBuffer buffer = ByteBuffer.wrap(
                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_DOUBLE] * values.length]);
@@ -3226,10 +3218,6 @@
             return new ExifAttribute(IFD_FORMAT_DOUBLE, values.length, buffer.array());
         }
 
-        public static ExifAttribute createDouble(double value, ByteOrder byteOrder) {
-            return createDouble(new double[] {value}, byteOrder);
-        }
-
         @NonNull
         @Override
         public String toString() {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index dc99e9d..3cef7aa 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -34,6 +34,7 @@
 kotlinNative = "1.7.10"
 kotlinCompileTesting = "1.4.1"
 kotlinCoroutines = "1.6.1"
+kotlinSerialization = "1.3.3"
 ksp = "1.7.10-1.0.6"
 ktlint = "0.46.0-20220520.192227-74"
 leakcanary = "2.8.1"
@@ -93,8 +94,8 @@
 hiltCompiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
 hiltCore = { module = "com.google.dagger:hilt-core", version.ref = "hilt" }
 intellijAnnotations = { module = "com.intellij:annotations", version = "12.0" }
-japicmpPluginz = { module = "me.champeau.gradle:japicmp-gradle-plugin", version = "0.2.9" }
 javapoet = { module = "com.squareup:javapoet", version = "1.13.0" }
+jsonSimple = { module = "com.googlecode.json-simple:json-simple", version = "1.1" }
 jsqlparser = { module = "com.github.jsqlparser:jsqlparser", version = "3.1" }
 jsr250 = { module = "javax.annotation:javax.annotation-api", version = "1.2" }
 junit = { module = "junit:junit", version = "4.13.2" }
@@ -122,6 +123,8 @@
 kotlinDaemonEmbeddable = { module = "org.jetbrains.kotlin:kotlin-daemon-embeddable", version.ref = "kotlin" }
 kotlinKlibCommonizer = { module = "org.jetbrains.kotlin:kotlin-klib-commonizer", version.ref = "kotlin" }
 kotlinMetadataJvm = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version = "0.5.0" }
+kotlinSerializationCore = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinSerialization" }
+kotlinSerializationProtobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "kotlinSerialization" }
 kotlinStdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
 kotlinStdlibCommon = { module = "org.jetbrains.kotlin:kotlin-stdlib-common", version.ref = "kotlin" }
 kotlinStdlibJdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index f923e74..6a1d261 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -468,22 +468,6 @@
       </trusted-keys>
    </configuration>
    <components>
-      <component group="backport-util-concurrent" name="backport-util-concurrent" version="3.1" androidx:reason="Unsigned">
-         <artifact name="backport-util-concurrent-3.1.jar">
-            <sha256 value="f5759b7fcdfc83a525a036deedcbd32e5b536b625ebc282426f16ca137eb5902" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="backport-util-concurrent-3.1.pom">
-            <sha256 value="770471090ca40a17b9e436ee2ec00819be42042da6f4085ece1d37916dc08ff9" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="classworlds" name="classworlds" version="1.1-alpha-2" androidx:reason="Unsigned">
-         <artifact name="classworlds-1.1-alpha-2.jar">
-            <sha256 value="2bf4e59f3acd106fea6145a9a88fe8956509f8b9c0fdd11eb96fee757269e3f3" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="classworlds-1.1-alpha-2.pom">
-            <sha256 value="0cc647963b74ad1d7a37c9868e9e5a8f474e49297e1863582253a08a4c719cb1" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
       <component group="com.github.gundy" name="semver4j" version="0.16.4" androidx:reason="Unsigned https://github.com/gundy/semver4j/issues/6">
          <artifact name="semver4j-0.16.4-nodeps.jar">
             <sha256 value="3f59eca516374ccd4fd3551625bf50f8a4b191f700508f7ce4866460a6128af0" origin="Generated by Gradle"/>
@@ -493,7 +477,7 @@
             <sha256 value="32001db2443b339dd21f5b79ff29d1ade722d1ba080c214bde819f0f72d1604d" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="com.google" name="google" version="1" androidx:reason="Unsigned">
+      <component group="com.google" name="google" version="1" androidx:reason="Unsigned. Used by protoc https://github.com/protocolbuffers/protobuf/pull/10290">
          <artifact name="google-1.pom">
             <sha256 value="cd6db17a11a31ede794ccbd1df0e4d9750f640234731f21cff885a9997277e81" origin="Generated by Gradle"/>
          </artifact>
@@ -562,7 +546,7 @@
             <sha256 value="02c12c3c2ae12dd475219ff691c82a4d9ea21f44bc594a181295bf6d43dcfbb0" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="com.google.prefab" name="cli" version="2.0.0" androidx:reason="Unsigned">
+      <component group="com.google.prefab" name="cli" version="2.0.0" androidx:reason="Unsigned. https://github.com/google/prefab/issues/157">
          <artifact name="cli-2.0.0-all.jar">
             <sha256 value="d9bd89f68446b82be038aae774771ad85922d0b375209b17625a2734b5317e29" origin="Generated by Gradle"/>
          </artifact>
@@ -570,7 +554,7 @@
             <sha256 value="4856401a263b39c5394b36a16e0d99628cf05c68008a0cda9691c72bb101e1df" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="com.googlecode.json-simple" name="json-simple" version="1.1" androidx:reason="Unsigned">
+      <component group="com.googlecode.json-simple" name="json-simple" version="1.1" androidx:reason="Unsigned. Used by AGP. b/239967984">
          <artifact name="json-simple-1.1.jar">
             <sha256 value="2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439" origin="Generated by Gradle"/>
          </artifact>
@@ -588,48 +572,6 @@
             <sha256 value="57603c9a75a9ef86ce30b1cb2db728d3cd9caf1be967343f1fc2316c85df5653" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="com.squareup.okio" name="okio" version="2.8.0" androidx:reason="Unsigned">
-         <artifact name="okio-2.8.0.module">
-            <sha256 value="17baab7270389a5fa63ab12811864d0a00f381611bc4eb042fa1bd5918ed0965" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="okio-jvm-2.8.0.jar">
-            <sha256 value="4496b06e73982fcdd8a5393f46e5df2ce2fa4465df5895454cac68a32f09bbc8" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.squareup.okio" name="okio" version="2.10.0" androidx:reason="Unsigned">
-         <artifact name="okio-jvm-2.10.0.jar">
-            <sha256 value="a27f091d34aa452e37227e2cfa85809f29012a8ef2501a9b5a125a978e4fcbc1" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.squareup.sqldelight" name="coroutines-extensions-jvm" version="1.3.0" androidx:reason="Unsigned">
-         <artifact name="sqldelight-coroutines-extensions-jvm-1.3.0.jar">
-            <sha256 value="47305eab44f8b2aef533d8ce76cec9eb5175715cac26b538b6bff5b106ed0ba1" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.squareup.wire" name="wire-grpc-client" version="3.6.0" androidx:reason="Unsigned">
-         <artifact name="wire-grpc-client-3.6.0.module">
-            <sha256 value="f4d91b43e5ce4603d63842652f063f16c0827abda1922dfb9551a4ac23ba4462" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="wire-grpc-client-jvm-3.6.0.jar">
-            <sha256 value="96904172b35af353e4459786a7d02f1550698cd03b249799ecb563cea3b4c277" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.squareup.wire" name="wire-runtime" version="3.6.0" androidx:reason="Unsigned">
-         <artifact name="wire-runtime-3.6.0.module">
-            <sha256 value="3b99891842fdec80e7b24ae7f7c485ae41ca35b47c902ca2043cc948aaf58010" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="wire-runtime-jvm-3.6.0.jar">
-            <sha256 value="ac41d3f9b8a88046788c6827b0519bf0c53dcc271f598f48aa666c6f5a9523d0" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.squareup.wire" name="wire-schema" version="3.6.0" androidx:reason="Unsigned">
-         <artifact name="wire-schema-3.6.0.module">
-            <sha256 value="85abd765f2efca0545889c935d8c240e31736a22221231a59bcc4510358b6aaa" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="wire-schema-jvm-3.6.0.jar">
-            <sha256 value="108bc4bafe7024a41460a1a60e72b6a95b69e5afd29c9f11ba7d8e0de2207976" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
       <component group="de.undercouch" name="gradle-download-task" version="4.1.1" androidx:reason="Invalid signature https://github.com/michel-kraemer/gradle-download-task/issues/187">
          <artifact name="gradle-download-task-4.1.1.jar">
             <ignored-keys>
@@ -664,14 +606,6 @@
             <sha256 value="d8c46016037cda6360561b9c6a21a6c2a4847cad15c3c63903e15328fbcccc45" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="javax.activation" name="activation" version="1.1" androidx:reason="Unsigned">
-         <artifact name="activation-1.1.jar">
-            <sha256 value="2881c79c9d6ef01c58e62beea13e9d1ac8b8baa16f2fc198ad6e6776defdcdd3" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="activation-1.1.pom">
-            <sha256 value="d490e540a11504b9d71718b1c85fef7b3de6802361290824539b076d58faa8a0" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
       <component group="javax.annotation" name="jsr250-api" version="1.0" androidx:reason="Unsigned">
          <artifact name="jsr250-api-1.0.jar">
             <sha256 value="a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f" origin="Generated by Gradle"/>
@@ -696,30 +630,6 @@
             <sha256 value="2864f19da84fd52763d75a197a71779b2decbccaac3eb4e4760ffc884c5af4a2" origin="Generated by Gradle because artifact wasn't signed"/>
          </artifact>
       </component>
-      <component group="me.champeau.gradle" name="japicmp-gradle-plugin" version="0.2.9" androidx:reason="Unsigned">
-         <artifact name="japicmp-gradle-plugin-0.2.9.jar">
-            <sha256 value="320944e8f3a42a38a5e0f08c6e1e8ae11a63fc82e1f7bf0429a6b7d89d26fac3" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="japicmp-gradle-plugin-0.2.9.pom">
-            <sha256 value="41fc0c243907c241cffa24a06a8cb542747c848ebad5feb6b0413d61b4a0ebc2" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="nekohtml" name="nekohtml" version="1.9.6.2" androidx:reason="Unsigned">
-         <artifact name="nekohtml-1.9.6.2.jar">
-            <sha256 value="fdff6cfa9ed9cc911c842a5d2395f209ec621ef1239d46810e9e495809d3ae09" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="nekohtml-1.9.6.2.pom">
-            <sha256 value="f5655d331af6afcd4dbaedaa739b889380c771a7e83f7aea5c8544a05074cf0b" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="nekohtml" name="xercesMinimal" version="1.9.6.2" androidx:reason="Unsigned">
-         <artifact name="xercesMinimal-1.9.6.2.jar">
-            <sha256 value="95b8b357d19f63797dd7d67622fd3f18374d64acbc6584faba1c7759a31e8438" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="xercesMinimal-1.9.6.2.pom">
-            <sha256 value="c219d697fa9c8f243d8f6e347499b6d4e8af1d0cac4bbc7b3907d338a2024c13" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
       <component group="net.java" name="jvnet-parent" version="1" androidx:reason="Unsigned">
          <artifact name="jvnet-parent-1.pom">
             <sha256 value="281440811268e65d9e266b3cc898297e214e04f09740d0386ceeb4a8923d63bf" origin="Generated by Gradle"/>
@@ -751,7 +661,7 @@
             <sha256 value="31ce606f4e9518936299bb0d27c978fa61e185fd1de7c9874fe959a53e34a685" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="org.ccil.cowan.tagsoup" name="tagsoup" version="1.2" androidx:reason="Unsigned">
+      <component group="org.ccil.cowan.tagsoup" name="tagsoup" version="1.2" androidx:reason="Unsigned. Used by espresso-web. cr/160019907">
          <artifact name="tagsoup-1.2.jar">
             <sha256 value="10d12b82c9a58a7842765a1152a56fbbd11eac9122a621f5a86a087503297266" origin="Generated by Gradle"/>
          </artifact>
@@ -759,50 +669,6 @@
             <sha256 value="186fd460ee13150e31188703a2c871bf86e20332636f3ede4ab959cd5568da78" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="org.codehaus.plexus" name="plexus-utils" version="1.5.15" androidx:reason="Unsigned">
-         <artifact name="plexus-utils-1.5.15.jar">
-            <sha256 value="2ca121831e597b4d8f2cb22d17c5c041fc23a7777ceb6bfbdd4dfb34bbe7d997" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="plexus-utils-1.5.15.pom">
-            <sha256 value="12a3c9a32b82fdc95223cab1f9d344e14ef3e396da14c4d0013451646f3280e7" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="org.codehaus.plexus" name="plexus" version="1.0.4" androidx:reason="Unsigned">
-         <artifact name="plexus-1.0.4.pom">
-            <sha256 value="2242fd02d12b1ca73267fb3d89863025517200e7a4ee426cba4667d0172c74c3" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="org.codehaus.plexus" name="plexus" version="2.0.2" androidx:reason="Unsigned">
-         <artifact name="plexus-2.0.2.pom">
-            <sha256 value="e246e2a062b5d989fdefc521c9c56431ba5554ff8d2344edee9218a34a546a33" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="org.codehaus.plexus" name="plexus-components" version="1.1.14" androidx:reason="Unsigned">
-         <artifact name="plexus-components-1.1.14.pom">
-            <sha256 value="381d72c526be217b770f9f8c3f749a86d3b1548ac5c1fcb48d267530ec60d43f" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="org.codehaus.plexus" name="plexus-container-default" version="1.0-alpha-9-stable-1" androidx:reason="Unsigned">
-         <artifact name="plexus-container-default-1.0-alpha-9-stable-1.jar">
-            <sha256 value="7c758612888782ccfe376823aee7cdcc7e0cdafb097f7ef50295a0b0c3a16edf" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="plexus-container-default-1.0-alpha-9-stable-1.pom">
-            <sha256 value="ef71d45a49edfe76be0f520312a76bc2aae73ec0743a5ffffe10d30122c6e2b2" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="org.codehaus.plexus" name="plexus-containers" version="1.0.3" androidx:reason="Unsigned">
-         <artifact name="plexus-containers-1.0.3.pom">
-            <sha256 value="7c75075badcb014443ee94c8c4cad2f4a9905be3ce9430fe7b220afc7fa3a80f" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="org.codehaus.plexus" name="plexus-interpolation" version="1.11" androidx:reason="Unsigned">
-         <artifact name="plexus-interpolation-1.11.jar">
-            <sha256 value="fd9507feb858fa620d1b4aa4b7039fdea1a77e09d3fd28cfbddfff468d9d8c28" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="plexus-interpolation-1.11.pom">
-            <sha256 value="b84d281f59b9da528139e0752a0e1cab0bd98d52c58442b00e45c9748e1d9eee" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
       <component group="org.jetbrains.dokka" name="dokka-android-gradle-plugin" version="0.9.17-g014" androidx:reason="Unsigned">
          <artifact name="dokka-android-gradle-plugin-0.9.17-g014.jar">
             <sha256 value="64b2e96fd20762351c74f08d598d49c25a490a3b685b8a09446e81d6db36fe81" origin="Generated by Gradle"/>
@@ -827,12 +693,12 @@
             <sha256 value="b217e9a1f1503c9f0c744b736fc2afed6fa6c3c2e86e5276a3c549f5bd48baef" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="org.ow2" name="ow2" version="1.5">
+      <component group="org.ow2" name="ow2" version="1.5" androidx:reason="https://gitlab.ow2.org/asm/asm/-/merge_requests/354">
          <artifact name="ow2-1.5.pom">
             <sha256 value="0f8a1b116e760b8fe6389c51b84e4b07a70fc11082d4f936e453b583dd50b43b" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="org.ow2.asm" name="asm" version="7.0" androidx:reason="Unsigned">
+      <component group="org.ow2.asm" name="asm" version="7.0" androidx:reason="Unsigned. Used by AGP 7.0 dependency of androidx.benchmark library">
          <artifact name="asm-7.0.jar">
             <sha256 value="b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf" origin="Generated by Gradle"/>
          </artifact>
@@ -840,7 +706,7 @@
             <sha256 value="83f65b1083d5ce4f8ba7f9545cfe9ff17824589c9a7cc82c3a4695801e4f5f68" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="org.ow2.asm" name="asm-analysis" version="7.0" androidx:reason="Unsigned">
+      <component group="org.ow2.asm" name="asm-analysis" version="7.0" androidx:reason="Unsigned. Used by AGP 7.0 dependency of androidx.benchmark library">
          <artifact name="asm-analysis-7.0.jar">
             <sha256 value="e981f8f650c4d900bb033650b18e122fa6b161eadd5f88978d08751f72ee8474" origin="Generated by Gradle"/>
          </artifact>
@@ -848,7 +714,7 @@
             <sha256 value="c6b54477e9d5bae1e7addff2e24cbf92aaff2ff08fd6bc0596c3933c3fadc2cb" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="org.ow2.asm" name="asm-commons" version="7.0" androidx:reason="Unsigned">
+      <component group="org.ow2.asm" name="asm-commons" version="7.0" androidx:reason="Unsigned. Used by AGP 7.0 dependency of androidx.benchmark library">
          <artifact name="asm-commons-7.0.jar">
             <sha256 value="fed348ef05958e3e846a3ac074a12af5f7936ef3d21ce44a62c4fa08a771927d" origin="Generated by Gradle"/>
          </artifact>
@@ -856,7 +722,7 @@
             <sha256 value="f4c697886cdb4a5b2472054a0b5e34371e9b48e620be40c3ed48e1f4b6d51eb4" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="org.ow2.asm" name="asm-tree" version="7.0" androidx:reason="Unsigned">
+      <component group="org.ow2.asm" name="asm-tree" version="7.0" androidx:reason="Unsigned. Used by AGP 7.0 dependency of androidx.benchmark library">
          <artifact name="asm-tree-7.0.jar">
             <sha256 value="cfd7a0874f9de36a999c127feeadfbfe6e04d4a71ee954d7af3d853f0be48a6c" origin="Generated by Gradle"/>
          </artifact>
@@ -864,7 +730,7 @@
             <sha256 value="d39e7dd12f4ff535a0839d1949c39c7644355a4470220c94b76a5c168c57a068" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="org.ow2.asm" name="asm-util" version="7.0" androidx:reason="Unsigned">
+      <component group="org.ow2.asm" name="asm-util" version="7.0" androidx:reason="Unsigned. Used by AGP 7.0 dependency of androidx.benchmark library">
          <artifact name="asm-util-7.0.jar">
             <sha256 value="75fbbca440ef463f41c2b0ab1a80abe67e910ac486da60a7863cbcb5bae7e145" origin="Generated by Gradle"/>
          </artifact>
diff --git a/health/health-connect-client/samples/src/main/java/androidx/health/connect/client/samples/AggregationSamples.kt b/health/health-connect-client/samples/src/main/java/androidx/health/connect/client/samples/AggregationSamples.kt
new file mode 100644
index 0000000..829941f5
--- /dev/null
+++ b/health/health-connect-client/samples/src/main/java/androidx/health/connect/client/samples/AggregationSamples.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 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.
+ */
+
+@file:Suppress("UNUSED_VARIABLE")
+
+package androidx.health.connect.client.samples
+
+import androidx.annotation.Sampled
+import androidx.health.connect.client.HealthConnectClient
+import androidx.health.connect.client.records.DistanceRecord
+import androidx.health.connect.client.records.HeartRateRecord
+import androidx.health.connect.client.records.StepsRecord
+import androidx.health.connect.client.request.AggregateRequest
+import androidx.health.connect.client.request.AggregateGroupByDurationRequest
+import androidx.health.connect.client.request.AggregateGroupByPeriodRequest
+import androidx.health.connect.client.time.TimeRangeFilter
+import java.time.Duration
+import java.time.Instant
+import java.time.Period
+
+@Sampled
+suspend fun AggregateDistance(
+    healthConnectClient: HealthConnectClient,
+    startTime: Instant,
+    endTime: Instant
+) {
+    val response =
+        healthConnectClient.aggregate(
+            AggregateRequest(
+                metrics = setOf(DistanceRecord.DISTANCE_TOTAL),
+                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
+            )
+        )
+    // The result may be null if no data is available in the time range.
+    val distanceTotalInMeters = response[DistanceRecord.DISTANCE_TOTAL]?.inMeters
+}
+
+@Sampled
+suspend fun AggregateHeartRate(
+    healthConnectClient: HealthConnectClient,
+    startTime: Instant,
+    endTime: Instant
+) {
+    val response =
+        healthConnectClient.aggregate(
+            AggregateRequest(
+                setOf(HeartRateRecord.BPM_MAX, HeartRateRecord.BPM_MIN),
+                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
+            )
+        )
+    // The result may be null if no data is available in the time range.
+    val minimumHeartRate = response[HeartRateRecord.BPM_MIN]
+    val maximumHeartRate = response[HeartRateRecord.BPM_MAX]
+}
+
+@Sampled
+suspend fun AggregateIntoMinutes(
+    healthConnectClient: HealthConnectClient,
+    startTime: Instant,
+    endTime: Instant
+) {
+    val response =
+        healthConnectClient.aggregateGroupByDuration(
+            AggregateGroupByDurationRequest(
+                metrics = setOf(StepsRecord.COUNT_TOTAL),
+                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
+                timeRangeSlicer = Duration.ofMinutes(1)
+            )
+        )
+    for (monthlyResult in response) {
+        // The result may be null if no data is available in the time range.
+        val totalSteps = monthlyResult.result[StepsRecord.COUNT_TOTAL]
+    }
+}
+
+@Sampled
+suspend fun AggregateIntoMonths(
+    healthConnectClient: HealthConnectClient,
+    startTime: Instant,
+    endTime: Instant
+) {
+    val response =
+        healthConnectClient.aggregateGroupByPeriod(
+            AggregateGroupByPeriodRequest(
+                metrics = setOf(StepsRecord.COUNT_TOTAL),
+                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
+                timeRangeSlicer = Period.ofMonths(1)
+            )
+        )
+    for (monthlyResult in response) {
+        // The result may be null if no data is available in the time range.
+        val totalSteps = monthlyResult.result[StepsRecord.COUNT_TOTAL]
+    }
+}
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
index c3dff53..d81a5a3a 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
@@ -160,6 +160,12 @@
      * Reads [AggregateMetric]s according to requested read criteria: [Record]s from
      * [AggregateRequest.dataOriginFilter] and within [AggregateRequest.timeRangeFilter].
      *
+     * Example code to aggregate cumulative data like distance:
+     * @sample androidx.health.connect.client.samples.AggregateDistance
+     *
+     * Example code to retrieve statistical aggregates like maximum or minimum heart rate:
+     * @sample androidx.health.connect.client.samples.AggregateHeartRate
+     *
      * @param request [AggregateRequest] object specifying [AggregateMetric]s to aggregate and other
      * filters.
      *
@@ -182,6 +188,9 @@
      * An [AggregationResultGroupedByDuration] is returned only if there are [Record] to aggregate
      * within start and end time of the row.
      *
+     * Example code to retrieve cumulative step count for each minute within provided time range:
+     * @sample androidx.health.connect.client.samples.AggregateIntoMinutes
+     *
      * @param request [AggregateGroupByDurationRequest] object specifying [AggregateMetric]s to
      * aggregate and other filters.
      *
@@ -207,6 +216,9 @@
      * An [AggregationResultGroupedByPeriod] is returned only if there are [Record] to aggregate
      * within start and end time of the row.
      *
+     * Example code to retrieve cumulative step count for each month within provided time range:
+     * @sample androidx.health.connect.client.samples.AggregateIntoMonths
+     *
      * @param request [AggregateGroupByPeriodRequest] object specifying [AggregateMetric]s to
      * aggregate and other filters.
      *
diff --git a/health/health-services-client/api/1.0.0-beta01.txt b/health/health-services-client/api/1.0.0-beta01.txt
index 931baf5..56bfe2d 100644
--- a/health/health-services-client/api/1.0.0-beta01.txt
+++ b/health/health-services-client/api/1.0.0-beta01.txt
@@ -2,7 +2,7 @@
 package androidx.health.services.client {
 
   public interface ExerciseClient {
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> clearUpdateCallbackAsync(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> endExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> flushExerciseAsync();
@@ -12,7 +12,7 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> overrideAutoPauseAndResumeForActiveExerciseAsync(boolean enabled);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> pauseExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> prepareExerciseAsync(androidx.health.services.client.data.WarmUpConfig configuration);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> resumeExerciseAsync();
     method public void setUpdateCallback(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public void setUpdateCallback(java.util.concurrent.Executor executor, androidx.health.services.client.ExerciseUpdateCallback callback);
@@ -20,7 +20,7 @@
   }
 
   public interface ExerciseUpdateCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
     method public void onLapSummaryReceived(androidx.health.services.client.data.ExerciseLapSummary lapSummary);
     method public void onRegistered();
@@ -42,32 +42,37 @@
   }
 
   public interface MeasureCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
-    method public void onDataReceived(java.util.List<androidx.health.services.client.data.DataPoint> data);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
+    method public void onDataReceived(androidx.health.services.client.data.DataPointContainer data);
     method public default void onRegistered();
     method public default void onRegistrationFailed(Throwable throwable);
   }
 
   public interface MeasureClient {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.health.services.client.data.MeasureCapabilities> getCapabilitiesAsync();
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
-    method public default void onNewDataPointsReceived(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints);
+    method public default void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
     method public default void onPermissionLost();
-    method public void onRegistered();
-    method public void onRegistrationFailed(Throwable throwable);
+    method public default void onRegistered();
+    method public default void onRegistrationFailed(Throwable throwable);
     method public default void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
   }
 
-  public abstract class PassiveListenerService extends android.app.Service implements androidx.health.services.client.PassiveListenerCallback {
+  public abstract class PassiveListenerService extends android.app.Service {
     ctor public PassiveListenerService();
     method public final android.os.IBinder? onBind(android.content.Intent intent);
+    method public final void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
+    method public final void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
+    method public final void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
+    method public final void onPermissionLost();
+    method public final void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
     field public static final String TAG = "PassiveListenerService";
   }
 
@@ -90,34 +95,8 @@
 
 package androidx.health.services.client.data {
 
-  public final class AchievedExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AchievedExerciseGoal> {
-    ctor public AchievedExerciseGoal(androidx.health.services.client.data.ExerciseGoal goal);
-    method public androidx.health.services.client.data.ExerciseGoal getGoal();
-    method public androidx.health.services.client.proto.DataProto.AchievedExerciseGoal getProto();
-    property public final androidx.health.services.client.data.ExerciseGoal goal;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.AchievedExerciseGoal> CREATOR;
-    field public static final androidx.health.services.client.data.AchievedExerciseGoal.Companion Companion;
-  }
-
-  public static final class AchievedExerciseGoal.Companion {
-  }
-
-  public abstract class AggregateDataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AggregateDataPoint> {
-    ctor public AggregateDataPoint();
-  }
-
-  @Keep public final class AggregateDataPoints {
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateAbsoluteElevation(double minAbsElevationMeters, double maxAbsElevationMeters, double avgAbsElevationMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateCalories(double kcalories, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateDistance(double meters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateElevationGain(double gainMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateHeartRate(double minBpm, double maxBpm, double avgBpm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregatePace(java.time.Duration minMillisPerKm, java.time.Duration maxMillisPerKm, java.time.Duration avgMillisPerKm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateSpeed(double minMetersPerSecond, double maxMetersPerSecond, double avgMetersPerSecond, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSteps(long steps, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateStepsPerMinute(long minStepsPerMinute, long maxStepsPerMinute, long avgStepsPerMinute, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSwimmingStrokes(long swimmingStrokes, java.time.Instant startTime, java.time.Instant endTime);
-    field public static final androidx.health.services.client.data.AggregateDataPoints INSTANCE;
+  public final class AggregateDataType<T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public AggregateDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public interface Availability {
@@ -145,146 +124,104 @@
   public static final class ComparisonType.Companion {
   }
 
-  public final class CumulativeDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public CumulativeDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value total);
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    method public androidx.health.services.client.data.Value getTotal();
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final java.time.Instant startTime;
-    property public final androidx.health.services.client.data.Value total;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.CumulativeDataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.CumulativeDataPoint.Companion Companion;
+  public final class CumulativeDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public CumulativeDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.CumulativeDataPoint<T>> dataType, T total, java.time.Instant start, java.time.Instant end);
+    method public java.time.Instant getEnd();
+    method public java.time.Instant getStart();
+    method public T getTotal();
+    property public final java.time.Instant end;
+    property public final java.time.Instant start;
+    property public final T total;
   }
 
-  public static final class CumulativeDataPoint.Companion {
-  }
-
-  public final class DataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPoint> {
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
-    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Duration getEndDurationFromBoot();
-    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
-    method public android.os.Bundle getMetadata();
-    method public androidx.health.services.client.proto.DataProto.DataPoint getProto();
-    method public java.time.Duration getStartDurationFromBoot();
-    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
-    method public androidx.health.services.client.data.Value getValue();
-    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Duration endDurationFromBoot;
-    property public final android.os.Bundle metadata;
-    property public final java.time.Duration startDurationFromBoot;
-    property public final androidx.health.services.client.data.Value value;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.DataPoint.Companion Companion;
-  }
-
-  public static final class DataPoint.Companion {
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
+  public abstract class DataPoint<T> {
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> getDataType();
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> dataType;
   }
 
   public abstract class DataPointAccuracy extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPointAccuracy> {
     ctor public DataPointAccuracy();
   }
 
-  @Keep public final class DataPoints {
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyCalories(double calories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyDistance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyFloors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailySteps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.HeartRateAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.LocationAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint pace(java.time.Duration millisPerKm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint stepsPerMinute(long stepsPerMinute, java.time.Duration startDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint swimmingStrokes(long strokes, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    field public static final androidx.health.services.client.data.DataPoints INSTANCE;
-    field public static final int LOCATION_DATA_POINT_ALTITUDE_INDEX = 2; // 0x2
-    field public static final int LOCATION_DATA_POINT_BEARING_INDEX = 3; // 0x3
-    field public static final int LOCATION_DATA_POINT_LATITUDE_INDEX = 0; // 0x0
-    field public static final int LOCATION_DATA_POINT_LONGITUDE_INDEX = 1; // 0x1
+  public final class DataPointContainer {
+    ctor public DataPointContainer(java.util.Map<androidx.health.services.client.data.DataType<?,?>,? extends java.util.List<? extends androidx.health.services.client.data.DataPoint<?>>> dataPoints);
+    ctor public DataPointContainer(java.util.List<? extends androidx.health.services.client.data.DataPoint<?>> dataPointList);
+    method public java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> getCumulativeDataPoints();
+    method public <T, D extends androidx.health.services.client.data.DataPoint<T>> java.util.List<D> getData(androidx.health.services.client.data.DeltaDataType<T,D> type);
+    method public <T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> D? getData(androidx.health.services.client.data.AggregateDataType<T,D> type);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> getIntervalDataPoints();
+    method public java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> getSampleDataPoints();
+    method public java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> getStatisticalDataPoints();
+    property public final java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> cumulativeDataPoints;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> intervalDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> sampleDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> statisticalDataPoints;
   }
 
-  public final class DataType extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataType> {
-    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, int format);
-    method public int getFormat();
-    method public String getName();
-    method public androidx.health.services.client.proto.DataProto.DataType getProto();
-    method public androidx.health.services.client.data.DataType.TimeType getTimeType();
-    property public final int format;
+  public abstract class DataType<T, D extends androidx.health.services.client.data.DataPoint<T>> {
+    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass, boolean isAggregate);
+    method public final String getName();
+    method public final kotlin.reflect.KClass<T> getValueClass();
     property public final String name;
-    property public final androidx.health.services.client.data.DataType.TimeType timeType;
-    field public static final androidx.health.services.client.data.DataType ABSOLUTE_ELEVATION;
-    field public static final androidx.health.services.client.data.DataType ACTIVE_EXERCISE_DURATION;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataType> CREATOR;
+    property public final kotlin.reflect.KClass<T> valueClass;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> ACTIVE_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> CALORIES_TOTAL;
     field public static final androidx.health.services.client.data.DataType.Companion Companion;
-    field public static final androidx.health.services.client.data.DataType DAILY_CALORIES;
-    field public static final androidx.health.services.client.data.DataType DAILY_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DAILY_FLOORS;
-    field public static final androidx.health.services.client.data.DataType DAILY_STEPS;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType DISTANCE;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_GAIN;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_LOSS;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DURATION;
-    field public static final androidx.health.services.client.data.DataType FLOORS;
-    field public static final androidx.health.services.client.data.DataType GOLF_SHOT_COUNT;
-    field public static final androidx.health.services.client.data.DataType HEART_RATE_BPM;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType LOCATION;
-    field public static final androidx.health.services.client.data.DataType PACE;
-    field public static final androidx.health.services.client.data.DataType REP_COUNT;
-    field public static final androidx.health.services.client.data.DataType RESTING_EXERCISE_DURATION;
-    field public static final androidx.health.services.client.data.DataType RUNNING_STEPS;
-    field public static final androidx.health.services.client.data.DataType SPEED;
-    field public static final androidx.health.services.client.data.DataType SPO2;
-    field public static final androidx.health.services.client.data.DataType STEPS;
-    field public static final androidx.health.services.client.data.DataType STEPS_PER_MINUTE;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_LAP_COUNT;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_STROKES;
-    field public static final androidx.health.services.client.data.DataType TOTAL_CALORIES;
-    field public static final androidx.health.services.client.data.DataType VO2;
-    field public static final androidx.health.services.client.data.DataType VO2_MAX;
-    field public static final androidx.health.services.client.data.DataType WALKING_STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DECLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DECLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> DECLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> DECLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_GAIN;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_GAIN_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_LOSS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_LOSS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> FLAT_GROUND_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> FLAT_GROUND_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLOORS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> GOLF_SHOT_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> GOLF_SHOT_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> HEART_RATE_BPM;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> HEART_RATE_BPM_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> INCLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> INCLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> INCLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> INCLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<androidx.health.services.client.data.LocationData,androidx.health.services.client.data.SampleDataPoint<androidx.health.services.client.data.LocationData>> LOCATION;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> PACE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> PACE_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> REP_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> REP_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS_DAILY;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.SampleDataPoint<java.lang.Long>> STEPS_PER_MINUTE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Long>> STEPS_PER_MINUTE_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_LAP_COUNT;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_STROKES;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> SWIMMING_STROKES_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> VO2_MAX;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> VO2_MAX_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> WALKING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> WALKING_STEPS_TOTAL;
   }
 
   public static final class DataType.Companion {
@@ -298,6 +235,7 @@
     field public static final androidx.health.services.client.data.DataType.TimeType.Companion Companion;
     field public static final androidx.health.services.client.data.DataType.TimeType INTERVAL;
     field public static final androidx.health.services.client.data.DataType.TimeType SAMPLE;
+    field public static final androidx.health.services.client.data.DataType.TimeType UNKNOWN;
   }
 
   public static final class DataType.TimeType.Companion {
@@ -321,28 +259,18 @@
     method public androidx.health.services.client.data.DataTypeAvailability? fromId(int id);
   }
 
-  public final class DataTypeCondition extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataTypeCondition> {
-    ctor public DataTypeCondition(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value threshold, androidx.health.services.client.data.ComparisonType comparisonType);
+  public final class DataTypeCondition<T extends java.lang.Number, D extends androidx.health.services.client.data.DataType<T, ? extends androidx.health.services.client.data.DataPoint<T>>> {
+    ctor public DataTypeCondition(D dataType, T threshold, androidx.health.services.client.data.ComparisonType comparisonType);
     method public androidx.health.services.client.data.ComparisonType getComparisonType();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public androidx.health.services.client.proto.DataProto.DataTypeCondition getProto();
-    method public androidx.health.services.client.data.Value getThreshold();
-    method public boolean isSatisfied(androidx.health.services.client.data.DataPoint dataPoint);
-    method public boolean isThresholdSatisfied(androidx.health.services.client.data.Value value);
+    method public D getDataType();
+    method public T getThreshold();
     property public final androidx.health.services.client.data.ComparisonType comparisonType;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final androidx.health.services.client.data.Value threshold;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataTypeCondition> CREATOR;
-    field public static final androidx.health.services.client.data.DataTypeCondition.Companion Companion;
+    property public final D dataType;
+    property public final T threshold;
   }
 
-  public static final class DataTypeCondition.Companion {
-  }
-
-  public final class DataTypes {
-    method public static boolean isCumulativeDataType(androidx.health.services.client.data.DataType dataType);
-    method public static boolean isStatisticalDataType(androidx.health.services.client.data.DataType dataType);
-    field public static final androidx.health.services.client.data.DataTypes INSTANCE;
+  public final class DeltaDataType<T, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public DeltaDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public final class ExerciseCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseCapabilities> {
@@ -362,63 +290,57 @@
   public static final class ExerciseCapabilities.Companion {
   }
 
-  public final class ExerciseConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseConfig> {
-    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes, java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals, android.os.Bundle exerciseParams);
-    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getAggregateDataTypes();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
-    method public java.util.List<androidx.health.services.client.data.ExerciseGoal> getExerciseGoals();
+  public final class ExerciseConfig {
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams);
+    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> getExerciseGoals();
     method public android.os.Bundle getExerciseParams();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
-    method public androidx.health.services.client.proto.DataProto.ExerciseConfig getProto();
     method public boolean isAutoPauseAndResumeEnabled();
     method public boolean isGpsEnabled();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
-    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals;
     property public final android.os.Bundle exerciseParams;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     property public final boolean isAutoPauseAndResumeEnabled;
     property public final boolean isGpsEnabled;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseConfig> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseConfig.Companion Companion;
   }
 
   public static final class ExerciseConfig.Builder {
-    ctor public ExerciseConfig.Builder();
+    ctor public ExerciseConfig.Builder(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig build();
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setAggregateDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseParams(android.os.Bundle exerciseParams);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseType(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsAutoPauseAndResumeEnabled(boolean isAutoPauseAndResumeEnabled);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsGpsEnabled(boolean isGpsEnabled);
   }
 
   public static final class ExerciseConfig.Companion {
-    method public androidx.health.services.client.data.ExerciseConfig.Builder builder();
+    method public androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
   }
 
-  public final class ExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public static androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
+  public final class ExerciseGoal<T extends java.lang.Number> extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
+    method public androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> getDataTypeCondition();
     method public androidx.health.services.client.data.ExerciseGoalType getExerciseGoalType();
-    method public androidx.health.services.client.data.Value? getPeriod();
+    method public T? getPeriod();
     method public androidx.health.services.client.proto.DataProto.ExerciseGoal getProto();
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> dataTypeCondition;
     property public final androidx.health.services.client.data.ExerciseGoalType exerciseGoalType;
-    property public final androidx.health.services.client.data.Value? period;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal> CREATOR;
+    property public final T? period;
+    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal<?>> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseGoal.Companion Companion;
   }
 
   public static final class ExerciseGoal.Companion {
-    method public androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
   }
 
   public final class ExerciseGoalType {
@@ -450,24 +372,18 @@
   public static final class ExerciseInfo.Companion {
   }
 
-  public final class ExerciseLapSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseLapSummary> {
-    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> lapMetrics);
+  public final class ExerciseLapSummary {
+    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.DataPointContainer lapMetrics);
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public int getLapCount();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLapMetrics();
-    method public androidx.health.services.client.proto.DataProto.ExerciseLapSummary getProto();
+    method public androidx.health.services.client.data.DataPointContainer getLapMetrics();
     method public java.time.Instant getStartTime();
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final int lapCount;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> lapMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer lapMetrics;
     property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseLapSummary> CREATOR;
-    field public static final androidx.health.services.client.data.ExerciseLapSummary.Companion Companion;
-  }
-
-  public static final class ExerciseLapSummary.Companion {
   }
 
   public final class ExerciseState {
@@ -628,18 +544,16 @@
   }
 
   public final class ExerciseTypeCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities> {
-    ctor public ExerciseTypeCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume, boolean supportsLaps);
+    ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume);
     method public androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypes();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypes();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
     method public boolean getSupportsAutoPauseAndResume();
-    method public boolean getSupportsLaps();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypes;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
     property public final boolean supportsAutoPauseAndResume;
-    property public final boolean supportsLaps;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseTypeCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseTypeCapabilities.Companion Companion;
   }
@@ -648,15 +562,15 @@
   }
 
   public final class ExerciseUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseUpdate> {
-    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics, java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
+    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, androidx.health.services.client.data.DataPointContainer latestMetrics, java.util.Set<? extends androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
     method public java.time.Duration getActiveDuration();
-    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.DataPoint dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.IntervalDataPoint<?> dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.SampleDataPoint<?> dataPoint);
     method public androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? getActiveDurationCheckpoint();
     method public androidx.health.services.client.data.ExerciseConfig? getExerciseConfig();
     method public androidx.health.services.client.data.ExerciseStateInfo getExerciseStateInfo();
-    method public java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> getLatestAchievedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLatestAggregateMetrics();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getLatestMetrics();
+    method public java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> getLatestAchievedGoals();
+    method public androidx.health.services.client.data.DataPointContainer getLatestMetrics();
     method public java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> getLatestMilestoneMarkerSummaries();
     method public androidx.health.services.client.proto.DataProto.ExerciseUpdate getProto();
     method public java.time.Instant? getStartTime();
@@ -665,9 +579,8 @@
     property public final androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint;
     property public final androidx.health.services.client.data.ExerciseConfig? exerciseConfig;
     property public final androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo;
-    property public final java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics;
+    property public final java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals;
+    property public final androidx.health.services.client.data.DataPointContainer latestMetrics;
     property public final java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries;
     property public final java.time.Instant? startTime;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseUpdate> CREATOR;
@@ -686,12 +599,12 @@
   }
 
   public final class HealthEvent {
-    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> metrics);
+    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, androidx.health.services.client.data.DataPointContainer metrics);
     method public java.time.Instant getEventTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getMetrics();
+    method public androidx.health.services.client.data.DataPointContainer getMetrics();
     method public androidx.health.services.client.data.HealthEvent.Type getType();
     property public final java.time.Instant eventTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> metrics;
+    property public final androidx.health.services.client.data.DataPointContainer metrics;
     property public final androidx.health.services.client.data.HealthEvent.Type type;
   }
 
@@ -737,6 +650,24 @@
   public static final class HeartRateAccuracy.SensorStatus.Companion {
   }
 
+  public final class IntervalDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public IntervalDataPoint(androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType, T value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> getDataType();
+    method public java.time.Duration getEndDurationFromBoot();
+    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getStartDurationFromBoot();
+    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType;
+    property public final java.time.Duration endDurationFromBoot;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration startDurationFromBoot;
+    property public final T value;
+  }
+
   public final class LocationAccuracy extends androidx.health.services.client.data.DataPointAccuracy {
     ctor public LocationAccuracy(double horizontalPositionErrorMeters, Double? verticalPositionErrorMeters);
     method public double getHorizontalPositionErrorMeters();
@@ -770,11 +701,23 @@
     method public androidx.health.services.client.data.LocationAvailability? fromId(int id);
   }
 
+  public final class LocationData {
+    ctor public LocationData(double latitude, double longitude, optional Double? altitude, optional Double? bearing);
+    method public Double? getAltitude();
+    method public Double? getBearing();
+    method public double getLatitude();
+    method public double getLongitude();
+    property public final Double? altitude;
+    property public final Double? bearing;
+    property public final double latitude;
+    property public final double longitude;
+  }
+
   public final class MeasureCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MeasureCapabilities> {
-    ctor public MeasureCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure);
+    ctor public MeasureCapabilities(java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure);
     method public androidx.health.services.client.proto.DataProto.MeasureCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesMeasure();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure;
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getSupportedDataTypesMeasure();
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MeasureCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.MeasureCapabilities.Companion Companion;
   }
@@ -783,18 +726,18 @@
   }
 
   public final class MilestoneMarkerSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary> {
-    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.AchievedExerciseGoal achievedGoal, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> summaryMetrics);
-    method public androidx.health.services.client.data.AchievedExerciseGoal getAchievedGoal();
+    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal, androidx.health.services.client.data.DataPointContainer summaryMetrics);
+    method public androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> getAchievedGoal();
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary getProto();
     method public java.time.Instant getStartTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getSummaryMetrics();
-    property public final androidx.health.services.client.data.AchievedExerciseGoal achievedGoal;
+    method public androidx.health.services.client.data.DataPointContainer getSummaryMetrics();
+    property public final androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal;
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final java.time.Instant startTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> summaryMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer summaryMetrics;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MilestoneMarkerSummary> CREATOR;
     field public static final androidx.health.services.client.data.MilestoneMarkerSummary.Companion Companion;
   }
@@ -802,34 +745,23 @@
   public static final class MilestoneMarkerSummary.Companion {
   }
 
-  public final class PassiveGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveGoal> {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition dataTypeCondition, int triggerFrequency);
-    method public static androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
-    method public androidx.health.services.client.proto.DataProto.PassiveGoal getProto();
+  public final class PassiveGoal {
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
     method public int getTriggerFrequency();
-    method public boolean isTriggered(androidx.health.services.client.data.DataPoint dataPoint);
-    method public void putToIntent(android.content.Intent intent);
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
     property public final int triggerFrequency;
-    field public static final String ACTION_GOAL = "hs.passivemonitoring.GOAL";
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveGoal> CREATOR;
-    field public static final androidx.health.services.client.data.PassiveGoal.Companion Companion;
-  }
-
-  public static final class PassiveGoal.Companion {
-    method public androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
-    ctor public PassiveListenerConfig(java.util.Set<androidx.health.services.client.data.DataType> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
+    ctor public PassiveListenerConfig(java.util.Set<? extends androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public static androidx.health.services.client.data.PassiveListenerConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    method public java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> getDataTypes();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.PassiveGoal> getPassiveGoals();
     method public androidx.health.services.client.proto.DataProto.PassiveListenerConfig getProto();
     method public boolean getShouldRequestUserActivityState();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals;
     property public final boolean shouldRequestUserActivityState;
@@ -840,7 +772,7 @@
   public static final class PassiveListenerConfig.Builder {
     ctor public PassiveListenerConfig.Builder();
     method public androidx.health.services.client.data.PassiveListenerConfig build();
-    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
+    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setHealthEventTypes(java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setPassiveGoals(java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setShouldRequestUserActivityState(boolean requestUserActivityState);
@@ -851,14 +783,14 @@
   }
 
   public final class PassiveMonitoringCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities> {
-    ctor public PassiveMonitoringCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring, java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
+    ctor public PassiveMonitoringCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveGoals();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveMonitoring();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveGoals();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveMonitoring();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getSupportedHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.UserActivityState> getSupportedUserActivityStates();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringCapabilities> CREATOR;
@@ -869,13 +801,13 @@
   }
 
   public final class PassiveMonitoringUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate> {
-    ctor public PassiveMonitoringUpdate(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
+    ctor public PassiveMonitoringUpdate(androidx.health.services.client.data.DataPointContainer dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
     method public static androidx.health.services.client.data.PassiveMonitoringUpdate? fromIntent(android.content.Intent intent);
-    method public java.util.List<androidx.health.services.client.data.DataPoint> getDataPoints();
+    method public androidx.health.services.client.data.DataPointContainer getDataPoints();
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate getProto();
     method public java.util.List<androidx.health.services.client.data.UserActivityInfo> getUserActivityInfoUpdates();
     method public void putToIntent(android.content.Intent intent);
-    property public final java.util.List<androidx.health.services.client.data.DataPoint> dataPoints;
+    property public final androidx.health.services.client.data.DataPointContainer dataPoints;
     property public final java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates;
     field public static final String ACTION_DATA = "hs.passivemonitoring.DATA";
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringUpdate> CREATOR;
@@ -900,22 +832,33 @@
   public static final class ProtoParcelable.Companion {
   }
 
-  public final class StatisticalDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public StatisticalDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value min, androidx.health.services.client.data.Value max, androidx.health.services.client.data.Value average);
-    method public androidx.health.services.client.data.Value getAverage();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.data.Value getMax();
-    method public androidx.health.services.client.data.Value getMin();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    property public final androidx.health.services.client.data.Value average;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final androidx.health.services.client.data.Value max;
-    property public final androidx.health.services.client.data.Value min;
-    property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.StatisticalDataPoint> CREATOR;
+  public final class SampleDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public SampleDataPoint(androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType, T value, java.time.Duration timeDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> getDataType();
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getTimeDurationFromBoot();
+    method public java.time.Instant getTimeInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration timeDurationFromBoot;
+    property public final T value;
+  }
+
+  public final class StatisticalDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public StatisticalDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.StatisticalDataPoint<T>> dataType, T min, T max, T average, java.time.Instant start, java.time.Instant end);
+    method public T getAverage();
+    method public java.time.Instant getEnd();
+    method public T getMax();
+    method public T getMin();
+    method public java.time.Instant getStart();
+    property public final T average;
+    property public final java.time.Instant end;
+    property public final T max;
+    property public final T min;
+    property public final java.time.Instant start;
     field public static final androidx.health.services.client.data.StatisticalDataPoint.Companion Companion;
   }
 
@@ -962,57 +905,12 @@
   public static final class UserActivityState.Companion {
   }
 
-  public final class Value extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.Value> {
-    method public boolean asBoolean();
-    method public byte[] asByteArray();
-    method public double asDouble();
-    method public double[] asDoubleArray();
-    method public long asLong();
-    method public static int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public int getFormat();
-    method public androidx.health.services.client.proto.DataProto.Value getProto();
-    method public boolean isBoolean();
-    method public boolean isByteArray();
-    method public boolean isDouble();
-    method public boolean isDoubleArray();
-    method public boolean isLong();
-    method public static androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public static androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public static androidx.health.services.client.data.Value ofDouble(double value);
-    method public static androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public static androidx.health.services.client.data.Value ofLong(long value);
-    method public static androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    property public final int format;
-    property public final boolean isBoolean;
-    property public final boolean isByteArray;
-    property public final boolean isDouble;
-    property public final boolean isDoubleArray;
-    property public final boolean isLong;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.Value> CREATOR;
-    field public static final androidx.health.services.client.data.Value.Companion Companion;
-    field public static final int FORMAT_BOOLEAN = 4; // 0x4
-    field public static final int FORMAT_BYTE_ARRAY = 5; // 0x5
-    field public static final int FORMAT_DOUBLE = 1; // 0x1
-    field public static final int FORMAT_DOUBLE_ARRAY = 3; // 0x3
-    field public static final int FORMAT_LONG = 2; // 0x2
-  }
-
-  public static final class Value.Companion {
-    method public int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public androidx.health.services.client.data.Value ofDouble(double value);
-    method public androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public androidx.health.services.client.data.Value ofLong(long value);
-    method public androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-  }
-
   public final class WarmUpConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.WarmUpConfig> {
-    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes);
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getDataTypes();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
     method public androidx.health.services.client.proto.DataProto.WarmUpConfig getProto();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.WarmUpConfig> CREATOR;
     field public static final androidx.health.services.client.data.WarmUpConfig.Companion Companion;
diff --git a/health/health-services-client/api/api_lint.ignore b/health/health-services-client/api/api_lint.ignore
index 6f43c4b..692c66a 100644
--- a/health/health-services-client/api/api_lint.ignore
+++ b/health/health-services-client/api/api_lint.ignore
@@ -3,6 +3,14 @@
     Must avoid boxed primitives (`java.lang.Double`)
 AutoBoxing: androidx.health.services.client.data.LocationAccuracy#getVerticalPositionErrorMeters():
     Must avoid boxed primitives (`java.lang.Double`)
+AutoBoxing: androidx.health.services.client.data.LocationData#LocationData(double, double, Double, Double) parameter #2:
+    Must avoid boxed primitives (`java.lang.Double`)
+AutoBoxing: androidx.health.services.client.data.LocationData#LocationData(double, double, Double, Double) parameter #3:
+    Must avoid boxed primitives (`java.lang.Double`)
+AutoBoxing: androidx.health.services.client.data.LocationData#getAltitude():
+    Must avoid boxed primitives (`java.lang.Double`)
+AutoBoxing: androidx.health.services.client.data.LocationData#getBearing():
+    Must avoid boxed primitives (`java.lang.Double`)
 
 
 ExecutorRegistration: androidx.health.services.client.ExerciseClient#clearUpdateCallbackAsync(androidx.health.services.client.ExerciseUpdateCallback):
@@ -19,7 +27,11 @@
     Invalid nullability on parameter `dest` in method `writeToParcel`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
 
 
-PairedRegistration: androidx.health.services.client.MeasureClient#registerMeasureCallback(androidx.health.services.client.data.DataType, androidx.health.services.client.MeasureCallback):
+PairedRegistration: androidx.health.services.client.MeasureClient#registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?>, androidx.health.services.client.MeasureCallback):
     Found registerMeasureCallback but not unregisterMeasureCallback in androidx.health.services.client.MeasureClient
-PairedRegistration: androidx.health.services.client.MeasureClient#registerMeasureCallback(androidx.health.services.client.data.DataType, java.util.concurrent.Executor, androidx.health.services.client.MeasureCallback):
+PairedRegistration: androidx.health.services.client.MeasureClient#registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?>, java.util.concurrent.Executor, androidx.health.services.client.MeasureCallback):
     Found registerMeasureCallback but not unregisterMeasureCallback in androidx.health.services.client.MeasureClient
+
+
+ParcelCreator: androidx.health.services.client.data.ExerciseGoal:
+    Parcelable requires `void writeToParcel(Parcel, int)`; missing in androidx.health.services.client.data.ExerciseGoal
diff --git a/health/health-services-client/api/current.txt b/health/health-services-client/api/current.txt
index 931baf5..56bfe2d 100644
--- a/health/health-services-client/api/current.txt
+++ b/health/health-services-client/api/current.txt
@@ -2,7 +2,7 @@
 package androidx.health.services.client {
 
   public interface ExerciseClient {
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> clearUpdateCallbackAsync(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> endExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> flushExerciseAsync();
@@ -12,7 +12,7 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> overrideAutoPauseAndResumeForActiveExerciseAsync(boolean enabled);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> pauseExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> prepareExerciseAsync(androidx.health.services.client.data.WarmUpConfig configuration);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> resumeExerciseAsync();
     method public void setUpdateCallback(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public void setUpdateCallback(java.util.concurrent.Executor executor, androidx.health.services.client.ExerciseUpdateCallback callback);
@@ -20,7 +20,7 @@
   }
 
   public interface ExerciseUpdateCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
     method public void onLapSummaryReceived(androidx.health.services.client.data.ExerciseLapSummary lapSummary);
     method public void onRegistered();
@@ -42,32 +42,37 @@
   }
 
   public interface MeasureCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
-    method public void onDataReceived(java.util.List<androidx.health.services.client.data.DataPoint> data);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
+    method public void onDataReceived(androidx.health.services.client.data.DataPointContainer data);
     method public default void onRegistered();
     method public default void onRegistrationFailed(Throwable throwable);
   }
 
   public interface MeasureClient {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.health.services.client.data.MeasureCapabilities> getCapabilitiesAsync();
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
-    method public default void onNewDataPointsReceived(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints);
+    method public default void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
     method public default void onPermissionLost();
-    method public void onRegistered();
-    method public void onRegistrationFailed(Throwable throwable);
+    method public default void onRegistered();
+    method public default void onRegistrationFailed(Throwable throwable);
     method public default void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
   }
 
-  public abstract class PassiveListenerService extends android.app.Service implements androidx.health.services.client.PassiveListenerCallback {
+  public abstract class PassiveListenerService extends android.app.Service {
     ctor public PassiveListenerService();
     method public final android.os.IBinder? onBind(android.content.Intent intent);
+    method public final void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
+    method public final void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
+    method public final void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
+    method public final void onPermissionLost();
+    method public final void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
     field public static final String TAG = "PassiveListenerService";
   }
 
@@ -90,34 +95,8 @@
 
 package androidx.health.services.client.data {
 
-  public final class AchievedExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AchievedExerciseGoal> {
-    ctor public AchievedExerciseGoal(androidx.health.services.client.data.ExerciseGoal goal);
-    method public androidx.health.services.client.data.ExerciseGoal getGoal();
-    method public androidx.health.services.client.proto.DataProto.AchievedExerciseGoal getProto();
-    property public final androidx.health.services.client.data.ExerciseGoal goal;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.AchievedExerciseGoal> CREATOR;
-    field public static final androidx.health.services.client.data.AchievedExerciseGoal.Companion Companion;
-  }
-
-  public static final class AchievedExerciseGoal.Companion {
-  }
-
-  public abstract class AggregateDataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AggregateDataPoint> {
-    ctor public AggregateDataPoint();
-  }
-
-  @Keep public final class AggregateDataPoints {
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateAbsoluteElevation(double minAbsElevationMeters, double maxAbsElevationMeters, double avgAbsElevationMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateCalories(double kcalories, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateDistance(double meters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateElevationGain(double gainMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateHeartRate(double minBpm, double maxBpm, double avgBpm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregatePace(java.time.Duration minMillisPerKm, java.time.Duration maxMillisPerKm, java.time.Duration avgMillisPerKm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateSpeed(double minMetersPerSecond, double maxMetersPerSecond, double avgMetersPerSecond, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSteps(long steps, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateStepsPerMinute(long minStepsPerMinute, long maxStepsPerMinute, long avgStepsPerMinute, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSwimmingStrokes(long swimmingStrokes, java.time.Instant startTime, java.time.Instant endTime);
-    field public static final androidx.health.services.client.data.AggregateDataPoints INSTANCE;
+  public final class AggregateDataType<T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public AggregateDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public interface Availability {
@@ -145,146 +124,104 @@
   public static final class ComparisonType.Companion {
   }
 
-  public final class CumulativeDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public CumulativeDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value total);
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    method public androidx.health.services.client.data.Value getTotal();
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final java.time.Instant startTime;
-    property public final androidx.health.services.client.data.Value total;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.CumulativeDataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.CumulativeDataPoint.Companion Companion;
+  public final class CumulativeDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public CumulativeDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.CumulativeDataPoint<T>> dataType, T total, java.time.Instant start, java.time.Instant end);
+    method public java.time.Instant getEnd();
+    method public java.time.Instant getStart();
+    method public T getTotal();
+    property public final java.time.Instant end;
+    property public final java.time.Instant start;
+    property public final T total;
   }
 
-  public static final class CumulativeDataPoint.Companion {
-  }
-
-  public final class DataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPoint> {
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
-    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Duration getEndDurationFromBoot();
-    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
-    method public android.os.Bundle getMetadata();
-    method public androidx.health.services.client.proto.DataProto.DataPoint getProto();
-    method public java.time.Duration getStartDurationFromBoot();
-    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
-    method public androidx.health.services.client.data.Value getValue();
-    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Duration endDurationFromBoot;
-    property public final android.os.Bundle metadata;
-    property public final java.time.Duration startDurationFromBoot;
-    property public final androidx.health.services.client.data.Value value;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.DataPoint.Companion Companion;
-  }
-
-  public static final class DataPoint.Companion {
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
+  public abstract class DataPoint<T> {
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> getDataType();
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> dataType;
   }
 
   public abstract class DataPointAccuracy extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPointAccuracy> {
     ctor public DataPointAccuracy();
   }
 
-  @Keep public final class DataPoints {
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyCalories(double calories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyDistance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyFloors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailySteps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.HeartRateAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.LocationAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint pace(java.time.Duration millisPerKm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint stepsPerMinute(long stepsPerMinute, java.time.Duration startDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint swimmingStrokes(long strokes, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    field public static final androidx.health.services.client.data.DataPoints INSTANCE;
-    field public static final int LOCATION_DATA_POINT_ALTITUDE_INDEX = 2; // 0x2
-    field public static final int LOCATION_DATA_POINT_BEARING_INDEX = 3; // 0x3
-    field public static final int LOCATION_DATA_POINT_LATITUDE_INDEX = 0; // 0x0
-    field public static final int LOCATION_DATA_POINT_LONGITUDE_INDEX = 1; // 0x1
+  public final class DataPointContainer {
+    ctor public DataPointContainer(java.util.Map<androidx.health.services.client.data.DataType<?,?>,? extends java.util.List<? extends androidx.health.services.client.data.DataPoint<?>>> dataPoints);
+    ctor public DataPointContainer(java.util.List<? extends androidx.health.services.client.data.DataPoint<?>> dataPointList);
+    method public java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> getCumulativeDataPoints();
+    method public <T, D extends androidx.health.services.client.data.DataPoint<T>> java.util.List<D> getData(androidx.health.services.client.data.DeltaDataType<T,D> type);
+    method public <T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> D? getData(androidx.health.services.client.data.AggregateDataType<T,D> type);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> getIntervalDataPoints();
+    method public java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> getSampleDataPoints();
+    method public java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> getStatisticalDataPoints();
+    property public final java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> cumulativeDataPoints;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> intervalDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> sampleDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> statisticalDataPoints;
   }
 
-  public final class DataType extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataType> {
-    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, int format);
-    method public int getFormat();
-    method public String getName();
-    method public androidx.health.services.client.proto.DataProto.DataType getProto();
-    method public androidx.health.services.client.data.DataType.TimeType getTimeType();
-    property public final int format;
+  public abstract class DataType<T, D extends androidx.health.services.client.data.DataPoint<T>> {
+    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass, boolean isAggregate);
+    method public final String getName();
+    method public final kotlin.reflect.KClass<T> getValueClass();
     property public final String name;
-    property public final androidx.health.services.client.data.DataType.TimeType timeType;
-    field public static final androidx.health.services.client.data.DataType ABSOLUTE_ELEVATION;
-    field public static final androidx.health.services.client.data.DataType ACTIVE_EXERCISE_DURATION;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataType> CREATOR;
+    property public final kotlin.reflect.KClass<T> valueClass;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> ACTIVE_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> CALORIES_TOTAL;
     field public static final androidx.health.services.client.data.DataType.Companion Companion;
-    field public static final androidx.health.services.client.data.DataType DAILY_CALORIES;
-    field public static final androidx.health.services.client.data.DataType DAILY_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DAILY_FLOORS;
-    field public static final androidx.health.services.client.data.DataType DAILY_STEPS;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType DISTANCE;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_GAIN;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_LOSS;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DURATION;
-    field public static final androidx.health.services.client.data.DataType FLOORS;
-    field public static final androidx.health.services.client.data.DataType GOLF_SHOT_COUNT;
-    field public static final androidx.health.services.client.data.DataType HEART_RATE_BPM;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType LOCATION;
-    field public static final androidx.health.services.client.data.DataType PACE;
-    field public static final androidx.health.services.client.data.DataType REP_COUNT;
-    field public static final androidx.health.services.client.data.DataType RESTING_EXERCISE_DURATION;
-    field public static final androidx.health.services.client.data.DataType RUNNING_STEPS;
-    field public static final androidx.health.services.client.data.DataType SPEED;
-    field public static final androidx.health.services.client.data.DataType SPO2;
-    field public static final androidx.health.services.client.data.DataType STEPS;
-    field public static final androidx.health.services.client.data.DataType STEPS_PER_MINUTE;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_LAP_COUNT;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_STROKES;
-    field public static final androidx.health.services.client.data.DataType TOTAL_CALORIES;
-    field public static final androidx.health.services.client.data.DataType VO2;
-    field public static final androidx.health.services.client.data.DataType VO2_MAX;
-    field public static final androidx.health.services.client.data.DataType WALKING_STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DECLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DECLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> DECLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> DECLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_GAIN;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_GAIN_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_LOSS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_LOSS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> FLAT_GROUND_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> FLAT_GROUND_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLOORS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> GOLF_SHOT_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> GOLF_SHOT_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> HEART_RATE_BPM;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> HEART_RATE_BPM_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> INCLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> INCLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> INCLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> INCLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<androidx.health.services.client.data.LocationData,androidx.health.services.client.data.SampleDataPoint<androidx.health.services.client.data.LocationData>> LOCATION;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> PACE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> PACE_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> REP_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> REP_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS_DAILY;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.SampleDataPoint<java.lang.Long>> STEPS_PER_MINUTE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Long>> STEPS_PER_MINUTE_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_LAP_COUNT;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_STROKES;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> SWIMMING_STROKES_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> VO2_MAX;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> VO2_MAX_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> WALKING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> WALKING_STEPS_TOTAL;
   }
 
   public static final class DataType.Companion {
@@ -298,6 +235,7 @@
     field public static final androidx.health.services.client.data.DataType.TimeType.Companion Companion;
     field public static final androidx.health.services.client.data.DataType.TimeType INTERVAL;
     field public static final androidx.health.services.client.data.DataType.TimeType SAMPLE;
+    field public static final androidx.health.services.client.data.DataType.TimeType UNKNOWN;
   }
 
   public static final class DataType.TimeType.Companion {
@@ -321,28 +259,18 @@
     method public androidx.health.services.client.data.DataTypeAvailability? fromId(int id);
   }
 
-  public final class DataTypeCondition extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataTypeCondition> {
-    ctor public DataTypeCondition(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value threshold, androidx.health.services.client.data.ComparisonType comparisonType);
+  public final class DataTypeCondition<T extends java.lang.Number, D extends androidx.health.services.client.data.DataType<T, ? extends androidx.health.services.client.data.DataPoint<T>>> {
+    ctor public DataTypeCondition(D dataType, T threshold, androidx.health.services.client.data.ComparisonType comparisonType);
     method public androidx.health.services.client.data.ComparisonType getComparisonType();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public androidx.health.services.client.proto.DataProto.DataTypeCondition getProto();
-    method public androidx.health.services.client.data.Value getThreshold();
-    method public boolean isSatisfied(androidx.health.services.client.data.DataPoint dataPoint);
-    method public boolean isThresholdSatisfied(androidx.health.services.client.data.Value value);
+    method public D getDataType();
+    method public T getThreshold();
     property public final androidx.health.services.client.data.ComparisonType comparisonType;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final androidx.health.services.client.data.Value threshold;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataTypeCondition> CREATOR;
-    field public static final androidx.health.services.client.data.DataTypeCondition.Companion Companion;
+    property public final D dataType;
+    property public final T threshold;
   }
 
-  public static final class DataTypeCondition.Companion {
-  }
-
-  public final class DataTypes {
-    method public static boolean isCumulativeDataType(androidx.health.services.client.data.DataType dataType);
-    method public static boolean isStatisticalDataType(androidx.health.services.client.data.DataType dataType);
-    field public static final androidx.health.services.client.data.DataTypes INSTANCE;
+  public final class DeltaDataType<T, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public DeltaDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public final class ExerciseCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseCapabilities> {
@@ -362,63 +290,57 @@
   public static final class ExerciseCapabilities.Companion {
   }
 
-  public final class ExerciseConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseConfig> {
-    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes, java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals, android.os.Bundle exerciseParams);
-    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getAggregateDataTypes();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
-    method public java.util.List<androidx.health.services.client.data.ExerciseGoal> getExerciseGoals();
+  public final class ExerciseConfig {
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams);
+    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> getExerciseGoals();
     method public android.os.Bundle getExerciseParams();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
-    method public androidx.health.services.client.proto.DataProto.ExerciseConfig getProto();
     method public boolean isAutoPauseAndResumeEnabled();
     method public boolean isGpsEnabled();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
-    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals;
     property public final android.os.Bundle exerciseParams;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     property public final boolean isAutoPauseAndResumeEnabled;
     property public final boolean isGpsEnabled;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseConfig> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseConfig.Companion Companion;
   }
 
   public static final class ExerciseConfig.Builder {
-    ctor public ExerciseConfig.Builder();
+    ctor public ExerciseConfig.Builder(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig build();
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setAggregateDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseParams(android.os.Bundle exerciseParams);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseType(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsAutoPauseAndResumeEnabled(boolean isAutoPauseAndResumeEnabled);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsGpsEnabled(boolean isGpsEnabled);
   }
 
   public static final class ExerciseConfig.Companion {
-    method public androidx.health.services.client.data.ExerciseConfig.Builder builder();
+    method public androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
   }
 
-  public final class ExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public static androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
+  public final class ExerciseGoal<T extends java.lang.Number> extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
+    method public androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> getDataTypeCondition();
     method public androidx.health.services.client.data.ExerciseGoalType getExerciseGoalType();
-    method public androidx.health.services.client.data.Value? getPeriod();
+    method public T? getPeriod();
     method public androidx.health.services.client.proto.DataProto.ExerciseGoal getProto();
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> dataTypeCondition;
     property public final androidx.health.services.client.data.ExerciseGoalType exerciseGoalType;
-    property public final androidx.health.services.client.data.Value? period;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal> CREATOR;
+    property public final T? period;
+    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal<?>> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseGoal.Companion Companion;
   }
 
   public static final class ExerciseGoal.Companion {
-    method public androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
   }
 
   public final class ExerciseGoalType {
@@ -450,24 +372,18 @@
   public static final class ExerciseInfo.Companion {
   }
 
-  public final class ExerciseLapSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseLapSummary> {
-    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> lapMetrics);
+  public final class ExerciseLapSummary {
+    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.DataPointContainer lapMetrics);
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public int getLapCount();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLapMetrics();
-    method public androidx.health.services.client.proto.DataProto.ExerciseLapSummary getProto();
+    method public androidx.health.services.client.data.DataPointContainer getLapMetrics();
     method public java.time.Instant getStartTime();
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final int lapCount;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> lapMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer lapMetrics;
     property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseLapSummary> CREATOR;
-    field public static final androidx.health.services.client.data.ExerciseLapSummary.Companion Companion;
-  }
-
-  public static final class ExerciseLapSummary.Companion {
   }
 
   public final class ExerciseState {
@@ -628,18 +544,16 @@
   }
 
   public final class ExerciseTypeCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities> {
-    ctor public ExerciseTypeCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume, boolean supportsLaps);
+    ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume);
     method public androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypes();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypes();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
     method public boolean getSupportsAutoPauseAndResume();
-    method public boolean getSupportsLaps();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypes;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
     property public final boolean supportsAutoPauseAndResume;
-    property public final boolean supportsLaps;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseTypeCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseTypeCapabilities.Companion Companion;
   }
@@ -648,15 +562,15 @@
   }
 
   public final class ExerciseUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseUpdate> {
-    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics, java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
+    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, androidx.health.services.client.data.DataPointContainer latestMetrics, java.util.Set<? extends androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
     method public java.time.Duration getActiveDuration();
-    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.DataPoint dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.IntervalDataPoint<?> dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.SampleDataPoint<?> dataPoint);
     method public androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? getActiveDurationCheckpoint();
     method public androidx.health.services.client.data.ExerciseConfig? getExerciseConfig();
     method public androidx.health.services.client.data.ExerciseStateInfo getExerciseStateInfo();
-    method public java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> getLatestAchievedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLatestAggregateMetrics();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getLatestMetrics();
+    method public java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> getLatestAchievedGoals();
+    method public androidx.health.services.client.data.DataPointContainer getLatestMetrics();
     method public java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> getLatestMilestoneMarkerSummaries();
     method public androidx.health.services.client.proto.DataProto.ExerciseUpdate getProto();
     method public java.time.Instant? getStartTime();
@@ -665,9 +579,8 @@
     property public final androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint;
     property public final androidx.health.services.client.data.ExerciseConfig? exerciseConfig;
     property public final androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo;
-    property public final java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics;
+    property public final java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals;
+    property public final androidx.health.services.client.data.DataPointContainer latestMetrics;
     property public final java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries;
     property public final java.time.Instant? startTime;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseUpdate> CREATOR;
@@ -686,12 +599,12 @@
   }
 
   public final class HealthEvent {
-    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> metrics);
+    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, androidx.health.services.client.data.DataPointContainer metrics);
     method public java.time.Instant getEventTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getMetrics();
+    method public androidx.health.services.client.data.DataPointContainer getMetrics();
     method public androidx.health.services.client.data.HealthEvent.Type getType();
     property public final java.time.Instant eventTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> metrics;
+    property public final androidx.health.services.client.data.DataPointContainer metrics;
     property public final androidx.health.services.client.data.HealthEvent.Type type;
   }
 
@@ -737,6 +650,24 @@
   public static final class HeartRateAccuracy.SensorStatus.Companion {
   }
 
+  public final class IntervalDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public IntervalDataPoint(androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType, T value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> getDataType();
+    method public java.time.Duration getEndDurationFromBoot();
+    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getStartDurationFromBoot();
+    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType;
+    property public final java.time.Duration endDurationFromBoot;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration startDurationFromBoot;
+    property public final T value;
+  }
+
   public final class LocationAccuracy extends androidx.health.services.client.data.DataPointAccuracy {
     ctor public LocationAccuracy(double horizontalPositionErrorMeters, Double? verticalPositionErrorMeters);
     method public double getHorizontalPositionErrorMeters();
@@ -770,11 +701,23 @@
     method public androidx.health.services.client.data.LocationAvailability? fromId(int id);
   }
 
+  public final class LocationData {
+    ctor public LocationData(double latitude, double longitude, optional Double? altitude, optional Double? bearing);
+    method public Double? getAltitude();
+    method public Double? getBearing();
+    method public double getLatitude();
+    method public double getLongitude();
+    property public final Double? altitude;
+    property public final Double? bearing;
+    property public final double latitude;
+    property public final double longitude;
+  }
+
   public final class MeasureCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MeasureCapabilities> {
-    ctor public MeasureCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure);
+    ctor public MeasureCapabilities(java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure);
     method public androidx.health.services.client.proto.DataProto.MeasureCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesMeasure();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure;
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getSupportedDataTypesMeasure();
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MeasureCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.MeasureCapabilities.Companion Companion;
   }
@@ -783,18 +726,18 @@
   }
 
   public final class MilestoneMarkerSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary> {
-    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.AchievedExerciseGoal achievedGoal, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> summaryMetrics);
-    method public androidx.health.services.client.data.AchievedExerciseGoal getAchievedGoal();
+    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal, androidx.health.services.client.data.DataPointContainer summaryMetrics);
+    method public androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> getAchievedGoal();
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary getProto();
     method public java.time.Instant getStartTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getSummaryMetrics();
-    property public final androidx.health.services.client.data.AchievedExerciseGoal achievedGoal;
+    method public androidx.health.services.client.data.DataPointContainer getSummaryMetrics();
+    property public final androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal;
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final java.time.Instant startTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> summaryMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer summaryMetrics;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MilestoneMarkerSummary> CREATOR;
     field public static final androidx.health.services.client.data.MilestoneMarkerSummary.Companion Companion;
   }
@@ -802,34 +745,23 @@
   public static final class MilestoneMarkerSummary.Companion {
   }
 
-  public final class PassiveGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveGoal> {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition dataTypeCondition, int triggerFrequency);
-    method public static androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
-    method public androidx.health.services.client.proto.DataProto.PassiveGoal getProto();
+  public final class PassiveGoal {
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
     method public int getTriggerFrequency();
-    method public boolean isTriggered(androidx.health.services.client.data.DataPoint dataPoint);
-    method public void putToIntent(android.content.Intent intent);
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
     property public final int triggerFrequency;
-    field public static final String ACTION_GOAL = "hs.passivemonitoring.GOAL";
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveGoal> CREATOR;
-    field public static final androidx.health.services.client.data.PassiveGoal.Companion Companion;
-  }
-
-  public static final class PassiveGoal.Companion {
-    method public androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
-    ctor public PassiveListenerConfig(java.util.Set<androidx.health.services.client.data.DataType> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
+    ctor public PassiveListenerConfig(java.util.Set<? extends androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public static androidx.health.services.client.data.PassiveListenerConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    method public java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> getDataTypes();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.PassiveGoal> getPassiveGoals();
     method public androidx.health.services.client.proto.DataProto.PassiveListenerConfig getProto();
     method public boolean getShouldRequestUserActivityState();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals;
     property public final boolean shouldRequestUserActivityState;
@@ -840,7 +772,7 @@
   public static final class PassiveListenerConfig.Builder {
     ctor public PassiveListenerConfig.Builder();
     method public androidx.health.services.client.data.PassiveListenerConfig build();
-    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
+    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setHealthEventTypes(java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setPassiveGoals(java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setShouldRequestUserActivityState(boolean requestUserActivityState);
@@ -851,14 +783,14 @@
   }
 
   public final class PassiveMonitoringCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities> {
-    ctor public PassiveMonitoringCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring, java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
+    ctor public PassiveMonitoringCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveGoals();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveMonitoring();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveGoals();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveMonitoring();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getSupportedHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.UserActivityState> getSupportedUserActivityStates();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringCapabilities> CREATOR;
@@ -869,13 +801,13 @@
   }
 
   public final class PassiveMonitoringUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate> {
-    ctor public PassiveMonitoringUpdate(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
+    ctor public PassiveMonitoringUpdate(androidx.health.services.client.data.DataPointContainer dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
     method public static androidx.health.services.client.data.PassiveMonitoringUpdate? fromIntent(android.content.Intent intent);
-    method public java.util.List<androidx.health.services.client.data.DataPoint> getDataPoints();
+    method public androidx.health.services.client.data.DataPointContainer getDataPoints();
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate getProto();
     method public java.util.List<androidx.health.services.client.data.UserActivityInfo> getUserActivityInfoUpdates();
     method public void putToIntent(android.content.Intent intent);
-    property public final java.util.List<androidx.health.services.client.data.DataPoint> dataPoints;
+    property public final androidx.health.services.client.data.DataPointContainer dataPoints;
     property public final java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates;
     field public static final String ACTION_DATA = "hs.passivemonitoring.DATA";
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringUpdate> CREATOR;
@@ -900,22 +832,33 @@
   public static final class ProtoParcelable.Companion {
   }
 
-  public final class StatisticalDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public StatisticalDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value min, androidx.health.services.client.data.Value max, androidx.health.services.client.data.Value average);
-    method public androidx.health.services.client.data.Value getAverage();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.data.Value getMax();
-    method public androidx.health.services.client.data.Value getMin();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    property public final androidx.health.services.client.data.Value average;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final androidx.health.services.client.data.Value max;
-    property public final androidx.health.services.client.data.Value min;
-    property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.StatisticalDataPoint> CREATOR;
+  public final class SampleDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public SampleDataPoint(androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType, T value, java.time.Duration timeDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> getDataType();
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getTimeDurationFromBoot();
+    method public java.time.Instant getTimeInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration timeDurationFromBoot;
+    property public final T value;
+  }
+
+  public final class StatisticalDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public StatisticalDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.StatisticalDataPoint<T>> dataType, T min, T max, T average, java.time.Instant start, java.time.Instant end);
+    method public T getAverage();
+    method public java.time.Instant getEnd();
+    method public T getMax();
+    method public T getMin();
+    method public java.time.Instant getStart();
+    property public final T average;
+    property public final java.time.Instant end;
+    property public final T max;
+    property public final T min;
+    property public final java.time.Instant start;
     field public static final androidx.health.services.client.data.StatisticalDataPoint.Companion Companion;
   }
 
@@ -962,57 +905,12 @@
   public static final class UserActivityState.Companion {
   }
 
-  public final class Value extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.Value> {
-    method public boolean asBoolean();
-    method public byte[] asByteArray();
-    method public double asDouble();
-    method public double[] asDoubleArray();
-    method public long asLong();
-    method public static int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public int getFormat();
-    method public androidx.health.services.client.proto.DataProto.Value getProto();
-    method public boolean isBoolean();
-    method public boolean isByteArray();
-    method public boolean isDouble();
-    method public boolean isDoubleArray();
-    method public boolean isLong();
-    method public static androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public static androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public static androidx.health.services.client.data.Value ofDouble(double value);
-    method public static androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public static androidx.health.services.client.data.Value ofLong(long value);
-    method public static androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    property public final int format;
-    property public final boolean isBoolean;
-    property public final boolean isByteArray;
-    property public final boolean isDouble;
-    property public final boolean isDoubleArray;
-    property public final boolean isLong;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.Value> CREATOR;
-    field public static final androidx.health.services.client.data.Value.Companion Companion;
-    field public static final int FORMAT_BOOLEAN = 4; // 0x4
-    field public static final int FORMAT_BYTE_ARRAY = 5; // 0x5
-    field public static final int FORMAT_DOUBLE = 1; // 0x1
-    field public static final int FORMAT_DOUBLE_ARRAY = 3; // 0x3
-    field public static final int FORMAT_LONG = 2; // 0x2
-  }
-
-  public static final class Value.Companion {
-    method public int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public androidx.health.services.client.data.Value ofDouble(double value);
-    method public androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public androidx.health.services.client.data.Value ofLong(long value);
-    method public androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-  }
-
   public final class WarmUpConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.WarmUpConfig> {
-    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes);
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getDataTypes();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
     method public androidx.health.services.client.proto.DataProto.WarmUpConfig getProto();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.WarmUpConfig> CREATOR;
     field public static final androidx.health.services.client.data.WarmUpConfig.Companion Companion;
diff --git a/health/health-services-client/api/public_plus_experimental_1.0.0-beta01.txt b/health/health-services-client/api/public_plus_experimental_1.0.0-beta01.txt
index 931baf5..56bfe2d 100644
--- a/health/health-services-client/api/public_plus_experimental_1.0.0-beta01.txt
+++ b/health/health-services-client/api/public_plus_experimental_1.0.0-beta01.txt
@@ -2,7 +2,7 @@
 package androidx.health.services.client {
 
   public interface ExerciseClient {
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> clearUpdateCallbackAsync(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> endExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> flushExerciseAsync();
@@ -12,7 +12,7 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> overrideAutoPauseAndResumeForActiveExerciseAsync(boolean enabled);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> pauseExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> prepareExerciseAsync(androidx.health.services.client.data.WarmUpConfig configuration);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> resumeExerciseAsync();
     method public void setUpdateCallback(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public void setUpdateCallback(java.util.concurrent.Executor executor, androidx.health.services.client.ExerciseUpdateCallback callback);
@@ -20,7 +20,7 @@
   }
 
   public interface ExerciseUpdateCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
     method public void onLapSummaryReceived(androidx.health.services.client.data.ExerciseLapSummary lapSummary);
     method public void onRegistered();
@@ -42,32 +42,37 @@
   }
 
   public interface MeasureCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
-    method public void onDataReceived(java.util.List<androidx.health.services.client.data.DataPoint> data);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
+    method public void onDataReceived(androidx.health.services.client.data.DataPointContainer data);
     method public default void onRegistered();
     method public default void onRegistrationFailed(Throwable throwable);
   }
 
   public interface MeasureClient {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.health.services.client.data.MeasureCapabilities> getCapabilitiesAsync();
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
-    method public default void onNewDataPointsReceived(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints);
+    method public default void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
     method public default void onPermissionLost();
-    method public void onRegistered();
-    method public void onRegistrationFailed(Throwable throwable);
+    method public default void onRegistered();
+    method public default void onRegistrationFailed(Throwable throwable);
     method public default void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
   }
 
-  public abstract class PassiveListenerService extends android.app.Service implements androidx.health.services.client.PassiveListenerCallback {
+  public abstract class PassiveListenerService extends android.app.Service {
     ctor public PassiveListenerService();
     method public final android.os.IBinder? onBind(android.content.Intent intent);
+    method public final void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
+    method public final void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
+    method public final void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
+    method public final void onPermissionLost();
+    method public final void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
     field public static final String TAG = "PassiveListenerService";
   }
 
@@ -90,34 +95,8 @@
 
 package androidx.health.services.client.data {
 
-  public final class AchievedExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AchievedExerciseGoal> {
-    ctor public AchievedExerciseGoal(androidx.health.services.client.data.ExerciseGoal goal);
-    method public androidx.health.services.client.data.ExerciseGoal getGoal();
-    method public androidx.health.services.client.proto.DataProto.AchievedExerciseGoal getProto();
-    property public final androidx.health.services.client.data.ExerciseGoal goal;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.AchievedExerciseGoal> CREATOR;
-    field public static final androidx.health.services.client.data.AchievedExerciseGoal.Companion Companion;
-  }
-
-  public static final class AchievedExerciseGoal.Companion {
-  }
-
-  public abstract class AggregateDataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AggregateDataPoint> {
-    ctor public AggregateDataPoint();
-  }
-
-  @Keep public final class AggregateDataPoints {
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateAbsoluteElevation(double minAbsElevationMeters, double maxAbsElevationMeters, double avgAbsElevationMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateCalories(double kcalories, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateDistance(double meters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateElevationGain(double gainMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateHeartRate(double minBpm, double maxBpm, double avgBpm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregatePace(java.time.Duration minMillisPerKm, java.time.Duration maxMillisPerKm, java.time.Duration avgMillisPerKm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateSpeed(double minMetersPerSecond, double maxMetersPerSecond, double avgMetersPerSecond, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSteps(long steps, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateStepsPerMinute(long minStepsPerMinute, long maxStepsPerMinute, long avgStepsPerMinute, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSwimmingStrokes(long swimmingStrokes, java.time.Instant startTime, java.time.Instant endTime);
-    field public static final androidx.health.services.client.data.AggregateDataPoints INSTANCE;
+  public final class AggregateDataType<T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public AggregateDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public interface Availability {
@@ -145,146 +124,104 @@
   public static final class ComparisonType.Companion {
   }
 
-  public final class CumulativeDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public CumulativeDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value total);
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    method public androidx.health.services.client.data.Value getTotal();
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final java.time.Instant startTime;
-    property public final androidx.health.services.client.data.Value total;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.CumulativeDataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.CumulativeDataPoint.Companion Companion;
+  public final class CumulativeDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public CumulativeDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.CumulativeDataPoint<T>> dataType, T total, java.time.Instant start, java.time.Instant end);
+    method public java.time.Instant getEnd();
+    method public java.time.Instant getStart();
+    method public T getTotal();
+    property public final java.time.Instant end;
+    property public final java.time.Instant start;
+    property public final T total;
   }
 
-  public static final class CumulativeDataPoint.Companion {
-  }
-
-  public final class DataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPoint> {
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
-    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Duration getEndDurationFromBoot();
-    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
-    method public android.os.Bundle getMetadata();
-    method public androidx.health.services.client.proto.DataProto.DataPoint getProto();
-    method public java.time.Duration getStartDurationFromBoot();
-    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
-    method public androidx.health.services.client.data.Value getValue();
-    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Duration endDurationFromBoot;
-    property public final android.os.Bundle metadata;
-    property public final java.time.Duration startDurationFromBoot;
-    property public final androidx.health.services.client.data.Value value;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.DataPoint.Companion Companion;
-  }
-
-  public static final class DataPoint.Companion {
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
+  public abstract class DataPoint<T> {
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> getDataType();
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> dataType;
   }
 
   public abstract class DataPointAccuracy extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPointAccuracy> {
     ctor public DataPointAccuracy();
   }
 
-  @Keep public final class DataPoints {
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyCalories(double calories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyDistance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyFloors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailySteps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.HeartRateAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.LocationAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint pace(java.time.Duration millisPerKm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint stepsPerMinute(long stepsPerMinute, java.time.Duration startDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint swimmingStrokes(long strokes, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    field public static final androidx.health.services.client.data.DataPoints INSTANCE;
-    field public static final int LOCATION_DATA_POINT_ALTITUDE_INDEX = 2; // 0x2
-    field public static final int LOCATION_DATA_POINT_BEARING_INDEX = 3; // 0x3
-    field public static final int LOCATION_DATA_POINT_LATITUDE_INDEX = 0; // 0x0
-    field public static final int LOCATION_DATA_POINT_LONGITUDE_INDEX = 1; // 0x1
+  public final class DataPointContainer {
+    ctor public DataPointContainer(java.util.Map<androidx.health.services.client.data.DataType<?,?>,? extends java.util.List<? extends androidx.health.services.client.data.DataPoint<?>>> dataPoints);
+    ctor public DataPointContainer(java.util.List<? extends androidx.health.services.client.data.DataPoint<?>> dataPointList);
+    method public java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> getCumulativeDataPoints();
+    method public <T, D extends androidx.health.services.client.data.DataPoint<T>> java.util.List<D> getData(androidx.health.services.client.data.DeltaDataType<T,D> type);
+    method public <T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> D? getData(androidx.health.services.client.data.AggregateDataType<T,D> type);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> getIntervalDataPoints();
+    method public java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> getSampleDataPoints();
+    method public java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> getStatisticalDataPoints();
+    property public final java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> cumulativeDataPoints;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> intervalDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> sampleDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> statisticalDataPoints;
   }
 
-  public final class DataType extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataType> {
-    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, int format);
-    method public int getFormat();
-    method public String getName();
-    method public androidx.health.services.client.proto.DataProto.DataType getProto();
-    method public androidx.health.services.client.data.DataType.TimeType getTimeType();
-    property public final int format;
+  public abstract class DataType<T, D extends androidx.health.services.client.data.DataPoint<T>> {
+    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass, boolean isAggregate);
+    method public final String getName();
+    method public final kotlin.reflect.KClass<T> getValueClass();
     property public final String name;
-    property public final androidx.health.services.client.data.DataType.TimeType timeType;
-    field public static final androidx.health.services.client.data.DataType ABSOLUTE_ELEVATION;
-    field public static final androidx.health.services.client.data.DataType ACTIVE_EXERCISE_DURATION;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataType> CREATOR;
+    property public final kotlin.reflect.KClass<T> valueClass;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> ACTIVE_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> CALORIES_TOTAL;
     field public static final androidx.health.services.client.data.DataType.Companion Companion;
-    field public static final androidx.health.services.client.data.DataType DAILY_CALORIES;
-    field public static final androidx.health.services.client.data.DataType DAILY_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DAILY_FLOORS;
-    field public static final androidx.health.services.client.data.DataType DAILY_STEPS;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType DISTANCE;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_GAIN;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_LOSS;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DURATION;
-    field public static final androidx.health.services.client.data.DataType FLOORS;
-    field public static final androidx.health.services.client.data.DataType GOLF_SHOT_COUNT;
-    field public static final androidx.health.services.client.data.DataType HEART_RATE_BPM;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType LOCATION;
-    field public static final androidx.health.services.client.data.DataType PACE;
-    field public static final androidx.health.services.client.data.DataType REP_COUNT;
-    field public static final androidx.health.services.client.data.DataType RESTING_EXERCISE_DURATION;
-    field public static final androidx.health.services.client.data.DataType RUNNING_STEPS;
-    field public static final androidx.health.services.client.data.DataType SPEED;
-    field public static final androidx.health.services.client.data.DataType SPO2;
-    field public static final androidx.health.services.client.data.DataType STEPS;
-    field public static final androidx.health.services.client.data.DataType STEPS_PER_MINUTE;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_LAP_COUNT;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_STROKES;
-    field public static final androidx.health.services.client.data.DataType TOTAL_CALORIES;
-    field public static final androidx.health.services.client.data.DataType VO2;
-    field public static final androidx.health.services.client.data.DataType VO2_MAX;
-    field public static final androidx.health.services.client.data.DataType WALKING_STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DECLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DECLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> DECLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> DECLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_GAIN;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_GAIN_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_LOSS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_LOSS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> FLAT_GROUND_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> FLAT_GROUND_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLOORS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> GOLF_SHOT_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> GOLF_SHOT_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> HEART_RATE_BPM;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> HEART_RATE_BPM_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> INCLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> INCLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> INCLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> INCLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<androidx.health.services.client.data.LocationData,androidx.health.services.client.data.SampleDataPoint<androidx.health.services.client.data.LocationData>> LOCATION;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> PACE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> PACE_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> REP_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> REP_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS_DAILY;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.SampleDataPoint<java.lang.Long>> STEPS_PER_MINUTE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Long>> STEPS_PER_MINUTE_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_LAP_COUNT;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_STROKES;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> SWIMMING_STROKES_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> VO2_MAX;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> VO2_MAX_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> WALKING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> WALKING_STEPS_TOTAL;
   }
 
   public static final class DataType.Companion {
@@ -298,6 +235,7 @@
     field public static final androidx.health.services.client.data.DataType.TimeType.Companion Companion;
     field public static final androidx.health.services.client.data.DataType.TimeType INTERVAL;
     field public static final androidx.health.services.client.data.DataType.TimeType SAMPLE;
+    field public static final androidx.health.services.client.data.DataType.TimeType UNKNOWN;
   }
 
   public static final class DataType.TimeType.Companion {
@@ -321,28 +259,18 @@
     method public androidx.health.services.client.data.DataTypeAvailability? fromId(int id);
   }
 
-  public final class DataTypeCondition extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataTypeCondition> {
-    ctor public DataTypeCondition(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value threshold, androidx.health.services.client.data.ComparisonType comparisonType);
+  public final class DataTypeCondition<T extends java.lang.Number, D extends androidx.health.services.client.data.DataType<T, ? extends androidx.health.services.client.data.DataPoint<T>>> {
+    ctor public DataTypeCondition(D dataType, T threshold, androidx.health.services.client.data.ComparisonType comparisonType);
     method public androidx.health.services.client.data.ComparisonType getComparisonType();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public androidx.health.services.client.proto.DataProto.DataTypeCondition getProto();
-    method public androidx.health.services.client.data.Value getThreshold();
-    method public boolean isSatisfied(androidx.health.services.client.data.DataPoint dataPoint);
-    method public boolean isThresholdSatisfied(androidx.health.services.client.data.Value value);
+    method public D getDataType();
+    method public T getThreshold();
     property public final androidx.health.services.client.data.ComparisonType comparisonType;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final androidx.health.services.client.data.Value threshold;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataTypeCondition> CREATOR;
-    field public static final androidx.health.services.client.data.DataTypeCondition.Companion Companion;
+    property public final D dataType;
+    property public final T threshold;
   }
 
-  public static final class DataTypeCondition.Companion {
-  }
-
-  public final class DataTypes {
-    method public static boolean isCumulativeDataType(androidx.health.services.client.data.DataType dataType);
-    method public static boolean isStatisticalDataType(androidx.health.services.client.data.DataType dataType);
-    field public static final androidx.health.services.client.data.DataTypes INSTANCE;
+  public final class DeltaDataType<T, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public DeltaDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public final class ExerciseCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseCapabilities> {
@@ -362,63 +290,57 @@
   public static final class ExerciseCapabilities.Companion {
   }
 
-  public final class ExerciseConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseConfig> {
-    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes, java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals, android.os.Bundle exerciseParams);
-    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getAggregateDataTypes();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
-    method public java.util.List<androidx.health.services.client.data.ExerciseGoal> getExerciseGoals();
+  public final class ExerciseConfig {
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams);
+    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> getExerciseGoals();
     method public android.os.Bundle getExerciseParams();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
-    method public androidx.health.services.client.proto.DataProto.ExerciseConfig getProto();
     method public boolean isAutoPauseAndResumeEnabled();
     method public boolean isGpsEnabled();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
-    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals;
     property public final android.os.Bundle exerciseParams;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     property public final boolean isAutoPauseAndResumeEnabled;
     property public final boolean isGpsEnabled;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseConfig> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseConfig.Companion Companion;
   }
 
   public static final class ExerciseConfig.Builder {
-    ctor public ExerciseConfig.Builder();
+    ctor public ExerciseConfig.Builder(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig build();
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setAggregateDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseParams(android.os.Bundle exerciseParams);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseType(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsAutoPauseAndResumeEnabled(boolean isAutoPauseAndResumeEnabled);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsGpsEnabled(boolean isGpsEnabled);
   }
 
   public static final class ExerciseConfig.Companion {
-    method public androidx.health.services.client.data.ExerciseConfig.Builder builder();
+    method public androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
   }
 
-  public final class ExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public static androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
+  public final class ExerciseGoal<T extends java.lang.Number> extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
+    method public androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> getDataTypeCondition();
     method public androidx.health.services.client.data.ExerciseGoalType getExerciseGoalType();
-    method public androidx.health.services.client.data.Value? getPeriod();
+    method public T? getPeriod();
     method public androidx.health.services.client.proto.DataProto.ExerciseGoal getProto();
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> dataTypeCondition;
     property public final androidx.health.services.client.data.ExerciseGoalType exerciseGoalType;
-    property public final androidx.health.services.client.data.Value? period;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal> CREATOR;
+    property public final T? period;
+    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal<?>> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseGoal.Companion Companion;
   }
 
   public static final class ExerciseGoal.Companion {
-    method public androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
   }
 
   public final class ExerciseGoalType {
@@ -450,24 +372,18 @@
   public static final class ExerciseInfo.Companion {
   }
 
-  public final class ExerciseLapSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseLapSummary> {
-    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> lapMetrics);
+  public final class ExerciseLapSummary {
+    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.DataPointContainer lapMetrics);
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public int getLapCount();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLapMetrics();
-    method public androidx.health.services.client.proto.DataProto.ExerciseLapSummary getProto();
+    method public androidx.health.services.client.data.DataPointContainer getLapMetrics();
     method public java.time.Instant getStartTime();
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final int lapCount;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> lapMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer lapMetrics;
     property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseLapSummary> CREATOR;
-    field public static final androidx.health.services.client.data.ExerciseLapSummary.Companion Companion;
-  }
-
-  public static final class ExerciseLapSummary.Companion {
   }
 
   public final class ExerciseState {
@@ -628,18 +544,16 @@
   }
 
   public final class ExerciseTypeCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities> {
-    ctor public ExerciseTypeCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume, boolean supportsLaps);
+    ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume);
     method public androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypes();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypes();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
     method public boolean getSupportsAutoPauseAndResume();
-    method public boolean getSupportsLaps();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypes;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
     property public final boolean supportsAutoPauseAndResume;
-    property public final boolean supportsLaps;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseTypeCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseTypeCapabilities.Companion Companion;
   }
@@ -648,15 +562,15 @@
   }
 
   public final class ExerciseUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseUpdate> {
-    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics, java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
+    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, androidx.health.services.client.data.DataPointContainer latestMetrics, java.util.Set<? extends androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
     method public java.time.Duration getActiveDuration();
-    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.DataPoint dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.IntervalDataPoint<?> dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.SampleDataPoint<?> dataPoint);
     method public androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? getActiveDurationCheckpoint();
     method public androidx.health.services.client.data.ExerciseConfig? getExerciseConfig();
     method public androidx.health.services.client.data.ExerciseStateInfo getExerciseStateInfo();
-    method public java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> getLatestAchievedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLatestAggregateMetrics();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getLatestMetrics();
+    method public java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> getLatestAchievedGoals();
+    method public androidx.health.services.client.data.DataPointContainer getLatestMetrics();
     method public java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> getLatestMilestoneMarkerSummaries();
     method public androidx.health.services.client.proto.DataProto.ExerciseUpdate getProto();
     method public java.time.Instant? getStartTime();
@@ -665,9 +579,8 @@
     property public final androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint;
     property public final androidx.health.services.client.data.ExerciseConfig? exerciseConfig;
     property public final androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo;
-    property public final java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics;
+    property public final java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals;
+    property public final androidx.health.services.client.data.DataPointContainer latestMetrics;
     property public final java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries;
     property public final java.time.Instant? startTime;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseUpdate> CREATOR;
@@ -686,12 +599,12 @@
   }
 
   public final class HealthEvent {
-    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> metrics);
+    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, androidx.health.services.client.data.DataPointContainer metrics);
     method public java.time.Instant getEventTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getMetrics();
+    method public androidx.health.services.client.data.DataPointContainer getMetrics();
     method public androidx.health.services.client.data.HealthEvent.Type getType();
     property public final java.time.Instant eventTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> metrics;
+    property public final androidx.health.services.client.data.DataPointContainer metrics;
     property public final androidx.health.services.client.data.HealthEvent.Type type;
   }
 
@@ -737,6 +650,24 @@
   public static final class HeartRateAccuracy.SensorStatus.Companion {
   }
 
+  public final class IntervalDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public IntervalDataPoint(androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType, T value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> getDataType();
+    method public java.time.Duration getEndDurationFromBoot();
+    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getStartDurationFromBoot();
+    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType;
+    property public final java.time.Duration endDurationFromBoot;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration startDurationFromBoot;
+    property public final T value;
+  }
+
   public final class LocationAccuracy extends androidx.health.services.client.data.DataPointAccuracy {
     ctor public LocationAccuracy(double horizontalPositionErrorMeters, Double? verticalPositionErrorMeters);
     method public double getHorizontalPositionErrorMeters();
@@ -770,11 +701,23 @@
     method public androidx.health.services.client.data.LocationAvailability? fromId(int id);
   }
 
+  public final class LocationData {
+    ctor public LocationData(double latitude, double longitude, optional Double? altitude, optional Double? bearing);
+    method public Double? getAltitude();
+    method public Double? getBearing();
+    method public double getLatitude();
+    method public double getLongitude();
+    property public final Double? altitude;
+    property public final Double? bearing;
+    property public final double latitude;
+    property public final double longitude;
+  }
+
   public final class MeasureCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MeasureCapabilities> {
-    ctor public MeasureCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure);
+    ctor public MeasureCapabilities(java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure);
     method public androidx.health.services.client.proto.DataProto.MeasureCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesMeasure();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure;
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getSupportedDataTypesMeasure();
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MeasureCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.MeasureCapabilities.Companion Companion;
   }
@@ -783,18 +726,18 @@
   }
 
   public final class MilestoneMarkerSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary> {
-    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.AchievedExerciseGoal achievedGoal, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> summaryMetrics);
-    method public androidx.health.services.client.data.AchievedExerciseGoal getAchievedGoal();
+    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal, androidx.health.services.client.data.DataPointContainer summaryMetrics);
+    method public androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> getAchievedGoal();
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary getProto();
     method public java.time.Instant getStartTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getSummaryMetrics();
-    property public final androidx.health.services.client.data.AchievedExerciseGoal achievedGoal;
+    method public androidx.health.services.client.data.DataPointContainer getSummaryMetrics();
+    property public final androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal;
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final java.time.Instant startTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> summaryMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer summaryMetrics;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MilestoneMarkerSummary> CREATOR;
     field public static final androidx.health.services.client.data.MilestoneMarkerSummary.Companion Companion;
   }
@@ -802,34 +745,23 @@
   public static final class MilestoneMarkerSummary.Companion {
   }
 
-  public final class PassiveGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveGoal> {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition dataTypeCondition, int triggerFrequency);
-    method public static androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
-    method public androidx.health.services.client.proto.DataProto.PassiveGoal getProto();
+  public final class PassiveGoal {
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
     method public int getTriggerFrequency();
-    method public boolean isTriggered(androidx.health.services.client.data.DataPoint dataPoint);
-    method public void putToIntent(android.content.Intent intent);
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
     property public final int triggerFrequency;
-    field public static final String ACTION_GOAL = "hs.passivemonitoring.GOAL";
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveGoal> CREATOR;
-    field public static final androidx.health.services.client.data.PassiveGoal.Companion Companion;
-  }
-
-  public static final class PassiveGoal.Companion {
-    method public androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
-    ctor public PassiveListenerConfig(java.util.Set<androidx.health.services.client.data.DataType> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
+    ctor public PassiveListenerConfig(java.util.Set<? extends androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public static androidx.health.services.client.data.PassiveListenerConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    method public java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> getDataTypes();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.PassiveGoal> getPassiveGoals();
     method public androidx.health.services.client.proto.DataProto.PassiveListenerConfig getProto();
     method public boolean getShouldRequestUserActivityState();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals;
     property public final boolean shouldRequestUserActivityState;
@@ -840,7 +772,7 @@
   public static final class PassiveListenerConfig.Builder {
     ctor public PassiveListenerConfig.Builder();
     method public androidx.health.services.client.data.PassiveListenerConfig build();
-    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
+    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setHealthEventTypes(java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setPassiveGoals(java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setShouldRequestUserActivityState(boolean requestUserActivityState);
@@ -851,14 +783,14 @@
   }
 
   public final class PassiveMonitoringCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities> {
-    ctor public PassiveMonitoringCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring, java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
+    ctor public PassiveMonitoringCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveGoals();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveMonitoring();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveGoals();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveMonitoring();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getSupportedHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.UserActivityState> getSupportedUserActivityStates();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringCapabilities> CREATOR;
@@ -869,13 +801,13 @@
   }
 
   public final class PassiveMonitoringUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate> {
-    ctor public PassiveMonitoringUpdate(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
+    ctor public PassiveMonitoringUpdate(androidx.health.services.client.data.DataPointContainer dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
     method public static androidx.health.services.client.data.PassiveMonitoringUpdate? fromIntent(android.content.Intent intent);
-    method public java.util.List<androidx.health.services.client.data.DataPoint> getDataPoints();
+    method public androidx.health.services.client.data.DataPointContainer getDataPoints();
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate getProto();
     method public java.util.List<androidx.health.services.client.data.UserActivityInfo> getUserActivityInfoUpdates();
     method public void putToIntent(android.content.Intent intent);
-    property public final java.util.List<androidx.health.services.client.data.DataPoint> dataPoints;
+    property public final androidx.health.services.client.data.DataPointContainer dataPoints;
     property public final java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates;
     field public static final String ACTION_DATA = "hs.passivemonitoring.DATA";
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringUpdate> CREATOR;
@@ -900,22 +832,33 @@
   public static final class ProtoParcelable.Companion {
   }
 
-  public final class StatisticalDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public StatisticalDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value min, androidx.health.services.client.data.Value max, androidx.health.services.client.data.Value average);
-    method public androidx.health.services.client.data.Value getAverage();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.data.Value getMax();
-    method public androidx.health.services.client.data.Value getMin();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    property public final androidx.health.services.client.data.Value average;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final androidx.health.services.client.data.Value max;
-    property public final androidx.health.services.client.data.Value min;
-    property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.StatisticalDataPoint> CREATOR;
+  public final class SampleDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public SampleDataPoint(androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType, T value, java.time.Duration timeDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> getDataType();
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getTimeDurationFromBoot();
+    method public java.time.Instant getTimeInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration timeDurationFromBoot;
+    property public final T value;
+  }
+
+  public final class StatisticalDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public StatisticalDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.StatisticalDataPoint<T>> dataType, T min, T max, T average, java.time.Instant start, java.time.Instant end);
+    method public T getAverage();
+    method public java.time.Instant getEnd();
+    method public T getMax();
+    method public T getMin();
+    method public java.time.Instant getStart();
+    property public final T average;
+    property public final java.time.Instant end;
+    property public final T max;
+    property public final T min;
+    property public final java.time.Instant start;
     field public static final androidx.health.services.client.data.StatisticalDataPoint.Companion Companion;
   }
 
@@ -962,57 +905,12 @@
   public static final class UserActivityState.Companion {
   }
 
-  public final class Value extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.Value> {
-    method public boolean asBoolean();
-    method public byte[] asByteArray();
-    method public double asDouble();
-    method public double[] asDoubleArray();
-    method public long asLong();
-    method public static int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public int getFormat();
-    method public androidx.health.services.client.proto.DataProto.Value getProto();
-    method public boolean isBoolean();
-    method public boolean isByteArray();
-    method public boolean isDouble();
-    method public boolean isDoubleArray();
-    method public boolean isLong();
-    method public static androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public static androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public static androidx.health.services.client.data.Value ofDouble(double value);
-    method public static androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public static androidx.health.services.client.data.Value ofLong(long value);
-    method public static androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    property public final int format;
-    property public final boolean isBoolean;
-    property public final boolean isByteArray;
-    property public final boolean isDouble;
-    property public final boolean isDoubleArray;
-    property public final boolean isLong;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.Value> CREATOR;
-    field public static final androidx.health.services.client.data.Value.Companion Companion;
-    field public static final int FORMAT_BOOLEAN = 4; // 0x4
-    field public static final int FORMAT_BYTE_ARRAY = 5; // 0x5
-    field public static final int FORMAT_DOUBLE = 1; // 0x1
-    field public static final int FORMAT_DOUBLE_ARRAY = 3; // 0x3
-    field public static final int FORMAT_LONG = 2; // 0x2
-  }
-
-  public static final class Value.Companion {
-    method public int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public androidx.health.services.client.data.Value ofDouble(double value);
-    method public androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public androidx.health.services.client.data.Value ofLong(long value);
-    method public androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-  }
-
   public final class WarmUpConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.WarmUpConfig> {
-    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes);
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getDataTypes();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
     method public androidx.health.services.client.proto.DataProto.WarmUpConfig getProto();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.WarmUpConfig> CREATOR;
     field public static final androidx.health.services.client.data.WarmUpConfig.Companion Companion;
diff --git a/health/health-services-client/api/public_plus_experimental_current.txt b/health/health-services-client/api/public_plus_experimental_current.txt
index 931baf5..56bfe2d 100644
--- a/health/health-services-client/api/public_plus_experimental_current.txt
+++ b/health/health-services-client/api/public_plus_experimental_current.txt
@@ -2,7 +2,7 @@
 package androidx.health.services.client {
 
   public interface ExerciseClient {
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> clearUpdateCallbackAsync(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> endExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> flushExerciseAsync();
@@ -12,7 +12,7 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> overrideAutoPauseAndResumeForActiveExerciseAsync(boolean enabled);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> pauseExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> prepareExerciseAsync(androidx.health.services.client.data.WarmUpConfig configuration);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> resumeExerciseAsync();
     method public void setUpdateCallback(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public void setUpdateCallback(java.util.concurrent.Executor executor, androidx.health.services.client.ExerciseUpdateCallback callback);
@@ -20,7 +20,7 @@
   }
 
   public interface ExerciseUpdateCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
     method public void onLapSummaryReceived(androidx.health.services.client.data.ExerciseLapSummary lapSummary);
     method public void onRegistered();
@@ -42,32 +42,37 @@
   }
 
   public interface MeasureCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
-    method public void onDataReceived(java.util.List<androidx.health.services.client.data.DataPoint> data);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
+    method public void onDataReceived(androidx.health.services.client.data.DataPointContainer data);
     method public default void onRegistered();
     method public default void onRegistrationFailed(Throwable throwable);
   }
 
   public interface MeasureClient {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.health.services.client.data.MeasureCapabilities> getCapabilitiesAsync();
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
-    method public default void onNewDataPointsReceived(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints);
+    method public default void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
     method public default void onPermissionLost();
-    method public void onRegistered();
-    method public void onRegistrationFailed(Throwable throwable);
+    method public default void onRegistered();
+    method public default void onRegistrationFailed(Throwable throwable);
     method public default void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
   }
 
-  public abstract class PassiveListenerService extends android.app.Service implements androidx.health.services.client.PassiveListenerCallback {
+  public abstract class PassiveListenerService extends android.app.Service {
     ctor public PassiveListenerService();
     method public final android.os.IBinder? onBind(android.content.Intent intent);
+    method public final void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
+    method public final void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
+    method public final void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
+    method public final void onPermissionLost();
+    method public final void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
     field public static final String TAG = "PassiveListenerService";
   }
 
@@ -90,34 +95,8 @@
 
 package androidx.health.services.client.data {
 
-  public final class AchievedExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AchievedExerciseGoal> {
-    ctor public AchievedExerciseGoal(androidx.health.services.client.data.ExerciseGoal goal);
-    method public androidx.health.services.client.data.ExerciseGoal getGoal();
-    method public androidx.health.services.client.proto.DataProto.AchievedExerciseGoal getProto();
-    property public final androidx.health.services.client.data.ExerciseGoal goal;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.AchievedExerciseGoal> CREATOR;
-    field public static final androidx.health.services.client.data.AchievedExerciseGoal.Companion Companion;
-  }
-
-  public static final class AchievedExerciseGoal.Companion {
-  }
-
-  public abstract class AggregateDataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AggregateDataPoint> {
-    ctor public AggregateDataPoint();
-  }
-
-  @Keep public final class AggregateDataPoints {
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateAbsoluteElevation(double minAbsElevationMeters, double maxAbsElevationMeters, double avgAbsElevationMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateCalories(double kcalories, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateDistance(double meters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateElevationGain(double gainMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateHeartRate(double minBpm, double maxBpm, double avgBpm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregatePace(java.time.Duration minMillisPerKm, java.time.Duration maxMillisPerKm, java.time.Duration avgMillisPerKm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateSpeed(double minMetersPerSecond, double maxMetersPerSecond, double avgMetersPerSecond, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSteps(long steps, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateStepsPerMinute(long minStepsPerMinute, long maxStepsPerMinute, long avgStepsPerMinute, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSwimmingStrokes(long swimmingStrokes, java.time.Instant startTime, java.time.Instant endTime);
-    field public static final androidx.health.services.client.data.AggregateDataPoints INSTANCE;
+  public final class AggregateDataType<T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public AggregateDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public interface Availability {
@@ -145,146 +124,104 @@
   public static final class ComparisonType.Companion {
   }
 
-  public final class CumulativeDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public CumulativeDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value total);
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    method public androidx.health.services.client.data.Value getTotal();
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final java.time.Instant startTime;
-    property public final androidx.health.services.client.data.Value total;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.CumulativeDataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.CumulativeDataPoint.Companion Companion;
+  public final class CumulativeDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public CumulativeDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.CumulativeDataPoint<T>> dataType, T total, java.time.Instant start, java.time.Instant end);
+    method public java.time.Instant getEnd();
+    method public java.time.Instant getStart();
+    method public T getTotal();
+    property public final java.time.Instant end;
+    property public final java.time.Instant start;
+    property public final T total;
   }
 
-  public static final class CumulativeDataPoint.Companion {
-  }
-
-  public final class DataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPoint> {
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
-    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Duration getEndDurationFromBoot();
-    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
-    method public android.os.Bundle getMetadata();
-    method public androidx.health.services.client.proto.DataProto.DataPoint getProto();
-    method public java.time.Duration getStartDurationFromBoot();
-    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
-    method public androidx.health.services.client.data.Value getValue();
-    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Duration endDurationFromBoot;
-    property public final android.os.Bundle metadata;
-    property public final java.time.Duration startDurationFromBoot;
-    property public final androidx.health.services.client.data.Value value;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.DataPoint.Companion Companion;
-  }
-
-  public static final class DataPoint.Companion {
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
+  public abstract class DataPoint<T> {
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> getDataType();
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> dataType;
   }
 
   public abstract class DataPointAccuracy extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPointAccuracy> {
     ctor public DataPointAccuracy();
   }
 
-  @Keep public final class DataPoints {
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyCalories(double calories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyDistance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyFloors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailySteps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.HeartRateAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.LocationAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint pace(java.time.Duration millisPerKm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint stepsPerMinute(long stepsPerMinute, java.time.Duration startDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint swimmingStrokes(long strokes, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    field public static final androidx.health.services.client.data.DataPoints INSTANCE;
-    field public static final int LOCATION_DATA_POINT_ALTITUDE_INDEX = 2; // 0x2
-    field public static final int LOCATION_DATA_POINT_BEARING_INDEX = 3; // 0x3
-    field public static final int LOCATION_DATA_POINT_LATITUDE_INDEX = 0; // 0x0
-    field public static final int LOCATION_DATA_POINT_LONGITUDE_INDEX = 1; // 0x1
+  public final class DataPointContainer {
+    ctor public DataPointContainer(java.util.Map<androidx.health.services.client.data.DataType<?,?>,? extends java.util.List<? extends androidx.health.services.client.data.DataPoint<?>>> dataPoints);
+    ctor public DataPointContainer(java.util.List<? extends androidx.health.services.client.data.DataPoint<?>> dataPointList);
+    method public java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> getCumulativeDataPoints();
+    method public <T, D extends androidx.health.services.client.data.DataPoint<T>> java.util.List<D> getData(androidx.health.services.client.data.DeltaDataType<T,D> type);
+    method public <T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> D? getData(androidx.health.services.client.data.AggregateDataType<T,D> type);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> getIntervalDataPoints();
+    method public java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> getSampleDataPoints();
+    method public java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> getStatisticalDataPoints();
+    property public final java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> cumulativeDataPoints;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> intervalDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> sampleDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> statisticalDataPoints;
   }
 
-  public final class DataType extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataType> {
-    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, int format);
-    method public int getFormat();
-    method public String getName();
-    method public androidx.health.services.client.proto.DataProto.DataType getProto();
-    method public androidx.health.services.client.data.DataType.TimeType getTimeType();
-    property public final int format;
+  public abstract class DataType<T, D extends androidx.health.services.client.data.DataPoint<T>> {
+    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass, boolean isAggregate);
+    method public final String getName();
+    method public final kotlin.reflect.KClass<T> getValueClass();
     property public final String name;
-    property public final androidx.health.services.client.data.DataType.TimeType timeType;
-    field public static final androidx.health.services.client.data.DataType ABSOLUTE_ELEVATION;
-    field public static final androidx.health.services.client.data.DataType ACTIVE_EXERCISE_DURATION;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataType> CREATOR;
+    property public final kotlin.reflect.KClass<T> valueClass;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> ACTIVE_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> CALORIES_TOTAL;
     field public static final androidx.health.services.client.data.DataType.Companion Companion;
-    field public static final androidx.health.services.client.data.DataType DAILY_CALORIES;
-    field public static final androidx.health.services.client.data.DataType DAILY_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DAILY_FLOORS;
-    field public static final androidx.health.services.client.data.DataType DAILY_STEPS;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType DISTANCE;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_GAIN;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_LOSS;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DURATION;
-    field public static final androidx.health.services.client.data.DataType FLOORS;
-    field public static final androidx.health.services.client.data.DataType GOLF_SHOT_COUNT;
-    field public static final androidx.health.services.client.data.DataType HEART_RATE_BPM;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType LOCATION;
-    field public static final androidx.health.services.client.data.DataType PACE;
-    field public static final androidx.health.services.client.data.DataType REP_COUNT;
-    field public static final androidx.health.services.client.data.DataType RESTING_EXERCISE_DURATION;
-    field public static final androidx.health.services.client.data.DataType RUNNING_STEPS;
-    field public static final androidx.health.services.client.data.DataType SPEED;
-    field public static final androidx.health.services.client.data.DataType SPO2;
-    field public static final androidx.health.services.client.data.DataType STEPS;
-    field public static final androidx.health.services.client.data.DataType STEPS_PER_MINUTE;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_LAP_COUNT;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_STROKES;
-    field public static final androidx.health.services.client.data.DataType TOTAL_CALORIES;
-    field public static final androidx.health.services.client.data.DataType VO2;
-    field public static final androidx.health.services.client.data.DataType VO2_MAX;
-    field public static final androidx.health.services.client.data.DataType WALKING_STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DECLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DECLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> DECLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> DECLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_GAIN;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_GAIN_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_LOSS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_LOSS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> FLAT_GROUND_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> FLAT_GROUND_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLOORS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> GOLF_SHOT_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> GOLF_SHOT_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> HEART_RATE_BPM;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> HEART_RATE_BPM_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> INCLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> INCLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> INCLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> INCLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<androidx.health.services.client.data.LocationData,androidx.health.services.client.data.SampleDataPoint<androidx.health.services.client.data.LocationData>> LOCATION;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> PACE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> PACE_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> REP_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> REP_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS_DAILY;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.SampleDataPoint<java.lang.Long>> STEPS_PER_MINUTE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Long>> STEPS_PER_MINUTE_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_LAP_COUNT;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_STROKES;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> SWIMMING_STROKES_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> VO2_MAX;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> VO2_MAX_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> WALKING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> WALKING_STEPS_TOTAL;
   }
 
   public static final class DataType.Companion {
@@ -298,6 +235,7 @@
     field public static final androidx.health.services.client.data.DataType.TimeType.Companion Companion;
     field public static final androidx.health.services.client.data.DataType.TimeType INTERVAL;
     field public static final androidx.health.services.client.data.DataType.TimeType SAMPLE;
+    field public static final androidx.health.services.client.data.DataType.TimeType UNKNOWN;
   }
 
   public static final class DataType.TimeType.Companion {
@@ -321,28 +259,18 @@
     method public androidx.health.services.client.data.DataTypeAvailability? fromId(int id);
   }
 
-  public final class DataTypeCondition extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataTypeCondition> {
-    ctor public DataTypeCondition(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value threshold, androidx.health.services.client.data.ComparisonType comparisonType);
+  public final class DataTypeCondition<T extends java.lang.Number, D extends androidx.health.services.client.data.DataType<T, ? extends androidx.health.services.client.data.DataPoint<T>>> {
+    ctor public DataTypeCondition(D dataType, T threshold, androidx.health.services.client.data.ComparisonType comparisonType);
     method public androidx.health.services.client.data.ComparisonType getComparisonType();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public androidx.health.services.client.proto.DataProto.DataTypeCondition getProto();
-    method public androidx.health.services.client.data.Value getThreshold();
-    method public boolean isSatisfied(androidx.health.services.client.data.DataPoint dataPoint);
-    method public boolean isThresholdSatisfied(androidx.health.services.client.data.Value value);
+    method public D getDataType();
+    method public T getThreshold();
     property public final androidx.health.services.client.data.ComparisonType comparisonType;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final androidx.health.services.client.data.Value threshold;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataTypeCondition> CREATOR;
-    field public static final androidx.health.services.client.data.DataTypeCondition.Companion Companion;
+    property public final D dataType;
+    property public final T threshold;
   }
 
-  public static final class DataTypeCondition.Companion {
-  }
-
-  public final class DataTypes {
-    method public static boolean isCumulativeDataType(androidx.health.services.client.data.DataType dataType);
-    method public static boolean isStatisticalDataType(androidx.health.services.client.data.DataType dataType);
-    field public static final androidx.health.services.client.data.DataTypes INSTANCE;
+  public final class DeltaDataType<T, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public DeltaDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public final class ExerciseCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseCapabilities> {
@@ -362,63 +290,57 @@
   public static final class ExerciseCapabilities.Companion {
   }
 
-  public final class ExerciseConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseConfig> {
-    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes, java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals, android.os.Bundle exerciseParams);
-    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getAggregateDataTypes();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
-    method public java.util.List<androidx.health.services.client.data.ExerciseGoal> getExerciseGoals();
+  public final class ExerciseConfig {
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams);
+    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> getExerciseGoals();
     method public android.os.Bundle getExerciseParams();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
-    method public androidx.health.services.client.proto.DataProto.ExerciseConfig getProto();
     method public boolean isAutoPauseAndResumeEnabled();
     method public boolean isGpsEnabled();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
-    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals;
     property public final android.os.Bundle exerciseParams;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     property public final boolean isAutoPauseAndResumeEnabled;
     property public final boolean isGpsEnabled;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseConfig> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseConfig.Companion Companion;
   }
 
   public static final class ExerciseConfig.Builder {
-    ctor public ExerciseConfig.Builder();
+    ctor public ExerciseConfig.Builder(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig build();
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setAggregateDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseParams(android.os.Bundle exerciseParams);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseType(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsAutoPauseAndResumeEnabled(boolean isAutoPauseAndResumeEnabled);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsGpsEnabled(boolean isGpsEnabled);
   }
 
   public static final class ExerciseConfig.Companion {
-    method public androidx.health.services.client.data.ExerciseConfig.Builder builder();
+    method public androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
   }
 
-  public final class ExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public static androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
+  public final class ExerciseGoal<T extends java.lang.Number> extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
+    method public androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> getDataTypeCondition();
     method public androidx.health.services.client.data.ExerciseGoalType getExerciseGoalType();
-    method public androidx.health.services.client.data.Value? getPeriod();
+    method public T? getPeriod();
     method public androidx.health.services.client.proto.DataProto.ExerciseGoal getProto();
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> dataTypeCondition;
     property public final androidx.health.services.client.data.ExerciseGoalType exerciseGoalType;
-    property public final androidx.health.services.client.data.Value? period;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal> CREATOR;
+    property public final T? period;
+    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal<?>> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseGoal.Companion Companion;
   }
 
   public static final class ExerciseGoal.Companion {
-    method public androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
   }
 
   public final class ExerciseGoalType {
@@ -450,24 +372,18 @@
   public static final class ExerciseInfo.Companion {
   }
 
-  public final class ExerciseLapSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseLapSummary> {
-    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> lapMetrics);
+  public final class ExerciseLapSummary {
+    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.DataPointContainer lapMetrics);
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public int getLapCount();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLapMetrics();
-    method public androidx.health.services.client.proto.DataProto.ExerciseLapSummary getProto();
+    method public androidx.health.services.client.data.DataPointContainer getLapMetrics();
     method public java.time.Instant getStartTime();
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final int lapCount;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> lapMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer lapMetrics;
     property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseLapSummary> CREATOR;
-    field public static final androidx.health.services.client.data.ExerciseLapSummary.Companion Companion;
-  }
-
-  public static final class ExerciseLapSummary.Companion {
   }
 
   public final class ExerciseState {
@@ -628,18 +544,16 @@
   }
 
   public final class ExerciseTypeCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities> {
-    ctor public ExerciseTypeCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume, boolean supportsLaps);
+    ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume);
     method public androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypes();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypes();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
     method public boolean getSupportsAutoPauseAndResume();
-    method public boolean getSupportsLaps();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypes;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
     property public final boolean supportsAutoPauseAndResume;
-    property public final boolean supportsLaps;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseTypeCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseTypeCapabilities.Companion Companion;
   }
@@ -648,15 +562,15 @@
   }
 
   public final class ExerciseUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseUpdate> {
-    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics, java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
+    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, androidx.health.services.client.data.DataPointContainer latestMetrics, java.util.Set<? extends androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
     method public java.time.Duration getActiveDuration();
-    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.DataPoint dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.IntervalDataPoint<?> dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.SampleDataPoint<?> dataPoint);
     method public androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? getActiveDurationCheckpoint();
     method public androidx.health.services.client.data.ExerciseConfig? getExerciseConfig();
     method public androidx.health.services.client.data.ExerciseStateInfo getExerciseStateInfo();
-    method public java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> getLatestAchievedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLatestAggregateMetrics();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getLatestMetrics();
+    method public java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> getLatestAchievedGoals();
+    method public androidx.health.services.client.data.DataPointContainer getLatestMetrics();
     method public java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> getLatestMilestoneMarkerSummaries();
     method public androidx.health.services.client.proto.DataProto.ExerciseUpdate getProto();
     method public java.time.Instant? getStartTime();
@@ -665,9 +579,8 @@
     property public final androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint;
     property public final androidx.health.services.client.data.ExerciseConfig? exerciseConfig;
     property public final androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo;
-    property public final java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics;
+    property public final java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals;
+    property public final androidx.health.services.client.data.DataPointContainer latestMetrics;
     property public final java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries;
     property public final java.time.Instant? startTime;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseUpdate> CREATOR;
@@ -686,12 +599,12 @@
   }
 
   public final class HealthEvent {
-    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> metrics);
+    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, androidx.health.services.client.data.DataPointContainer metrics);
     method public java.time.Instant getEventTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getMetrics();
+    method public androidx.health.services.client.data.DataPointContainer getMetrics();
     method public androidx.health.services.client.data.HealthEvent.Type getType();
     property public final java.time.Instant eventTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> metrics;
+    property public final androidx.health.services.client.data.DataPointContainer metrics;
     property public final androidx.health.services.client.data.HealthEvent.Type type;
   }
 
@@ -737,6 +650,24 @@
   public static final class HeartRateAccuracy.SensorStatus.Companion {
   }
 
+  public final class IntervalDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public IntervalDataPoint(androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType, T value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> getDataType();
+    method public java.time.Duration getEndDurationFromBoot();
+    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getStartDurationFromBoot();
+    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType;
+    property public final java.time.Duration endDurationFromBoot;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration startDurationFromBoot;
+    property public final T value;
+  }
+
   public final class LocationAccuracy extends androidx.health.services.client.data.DataPointAccuracy {
     ctor public LocationAccuracy(double horizontalPositionErrorMeters, Double? verticalPositionErrorMeters);
     method public double getHorizontalPositionErrorMeters();
@@ -770,11 +701,23 @@
     method public androidx.health.services.client.data.LocationAvailability? fromId(int id);
   }
 
+  public final class LocationData {
+    ctor public LocationData(double latitude, double longitude, optional Double? altitude, optional Double? bearing);
+    method public Double? getAltitude();
+    method public Double? getBearing();
+    method public double getLatitude();
+    method public double getLongitude();
+    property public final Double? altitude;
+    property public final Double? bearing;
+    property public final double latitude;
+    property public final double longitude;
+  }
+
   public final class MeasureCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MeasureCapabilities> {
-    ctor public MeasureCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure);
+    ctor public MeasureCapabilities(java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure);
     method public androidx.health.services.client.proto.DataProto.MeasureCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesMeasure();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure;
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getSupportedDataTypesMeasure();
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MeasureCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.MeasureCapabilities.Companion Companion;
   }
@@ -783,18 +726,18 @@
   }
 
   public final class MilestoneMarkerSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary> {
-    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.AchievedExerciseGoal achievedGoal, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> summaryMetrics);
-    method public androidx.health.services.client.data.AchievedExerciseGoal getAchievedGoal();
+    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal, androidx.health.services.client.data.DataPointContainer summaryMetrics);
+    method public androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> getAchievedGoal();
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary getProto();
     method public java.time.Instant getStartTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getSummaryMetrics();
-    property public final androidx.health.services.client.data.AchievedExerciseGoal achievedGoal;
+    method public androidx.health.services.client.data.DataPointContainer getSummaryMetrics();
+    property public final androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal;
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final java.time.Instant startTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> summaryMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer summaryMetrics;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MilestoneMarkerSummary> CREATOR;
     field public static final androidx.health.services.client.data.MilestoneMarkerSummary.Companion Companion;
   }
@@ -802,34 +745,23 @@
   public static final class MilestoneMarkerSummary.Companion {
   }
 
-  public final class PassiveGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveGoal> {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition dataTypeCondition, int triggerFrequency);
-    method public static androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
-    method public androidx.health.services.client.proto.DataProto.PassiveGoal getProto();
+  public final class PassiveGoal {
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
     method public int getTriggerFrequency();
-    method public boolean isTriggered(androidx.health.services.client.data.DataPoint dataPoint);
-    method public void putToIntent(android.content.Intent intent);
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
     property public final int triggerFrequency;
-    field public static final String ACTION_GOAL = "hs.passivemonitoring.GOAL";
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveGoal> CREATOR;
-    field public static final androidx.health.services.client.data.PassiveGoal.Companion Companion;
-  }
-
-  public static final class PassiveGoal.Companion {
-    method public androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
-    ctor public PassiveListenerConfig(java.util.Set<androidx.health.services.client.data.DataType> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
+    ctor public PassiveListenerConfig(java.util.Set<? extends androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public static androidx.health.services.client.data.PassiveListenerConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    method public java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> getDataTypes();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.PassiveGoal> getPassiveGoals();
     method public androidx.health.services.client.proto.DataProto.PassiveListenerConfig getProto();
     method public boolean getShouldRequestUserActivityState();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals;
     property public final boolean shouldRequestUserActivityState;
@@ -840,7 +772,7 @@
   public static final class PassiveListenerConfig.Builder {
     ctor public PassiveListenerConfig.Builder();
     method public androidx.health.services.client.data.PassiveListenerConfig build();
-    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
+    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setHealthEventTypes(java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setPassiveGoals(java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setShouldRequestUserActivityState(boolean requestUserActivityState);
@@ -851,14 +783,14 @@
   }
 
   public final class PassiveMonitoringCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities> {
-    ctor public PassiveMonitoringCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring, java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
+    ctor public PassiveMonitoringCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveGoals();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveMonitoring();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveGoals();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveMonitoring();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getSupportedHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.UserActivityState> getSupportedUserActivityStates();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringCapabilities> CREATOR;
@@ -869,13 +801,13 @@
   }
 
   public final class PassiveMonitoringUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate> {
-    ctor public PassiveMonitoringUpdate(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
+    ctor public PassiveMonitoringUpdate(androidx.health.services.client.data.DataPointContainer dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
     method public static androidx.health.services.client.data.PassiveMonitoringUpdate? fromIntent(android.content.Intent intent);
-    method public java.util.List<androidx.health.services.client.data.DataPoint> getDataPoints();
+    method public androidx.health.services.client.data.DataPointContainer getDataPoints();
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate getProto();
     method public java.util.List<androidx.health.services.client.data.UserActivityInfo> getUserActivityInfoUpdates();
     method public void putToIntent(android.content.Intent intent);
-    property public final java.util.List<androidx.health.services.client.data.DataPoint> dataPoints;
+    property public final androidx.health.services.client.data.DataPointContainer dataPoints;
     property public final java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates;
     field public static final String ACTION_DATA = "hs.passivemonitoring.DATA";
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringUpdate> CREATOR;
@@ -900,22 +832,33 @@
   public static final class ProtoParcelable.Companion {
   }
 
-  public final class StatisticalDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public StatisticalDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value min, androidx.health.services.client.data.Value max, androidx.health.services.client.data.Value average);
-    method public androidx.health.services.client.data.Value getAverage();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.data.Value getMax();
-    method public androidx.health.services.client.data.Value getMin();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    property public final androidx.health.services.client.data.Value average;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final androidx.health.services.client.data.Value max;
-    property public final androidx.health.services.client.data.Value min;
-    property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.StatisticalDataPoint> CREATOR;
+  public final class SampleDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public SampleDataPoint(androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType, T value, java.time.Duration timeDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> getDataType();
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getTimeDurationFromBoot();
+    method public java.time.Instant getTimeInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration timeDurationFromBoot;
+    property public final T value;
+  }
+
+  public final class StatisticalDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public StatisticalDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.StatisticalDataPoint<T>> dataType, T min, T max, T average, java.time.Instant start, java.time.Instant end);
+    method public T getAverage();
+    method public java.time.Instant getEnd();
+    method public T getMax();
+    method public T getMin();
+    method public java.time.Instant getStart();
+    property public final T average;
+    property public final java.time.Instant end;
+    property public final T max;
+    property public final T min;
+    property public final java.time.Instant start;
     field public static final androidx.health.services.client.data.StatisticalDataPoint.Companion Companion;
   }
 
@@ -962,57 +905,12 @@
   public static final class UserActivityState.Companion {
   }
 
-  public final class Value extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.Value> {
-    method public boolean asBoolean();
-    method public byte[] asByteArray();
-    method public double asDouble();
-    method public double[] asDoubleArray();
-    method public long asLong();
-    method public static int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public int getFormat();
-    method public androidx.health.services.client.proto.DataProto.Value getProto();
-    method public boolean isBoolean();
-    method public boolean isByteArray();
-    method public boolean isDouble();
-    method public boolean isDoubleArray();
-    method public boolean isLong();
-    method public static androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public static androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public static androidx.health.services.client.data.Value ofDouble(double value);
-    method public static androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public static androidx.health.services.client.data.Value ofLong(long value);
-    method public static androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    property public final int format;
-    property public final boolean isBoolean;
-    property public final boolean isByteArray;
-    property public final boolean isDouble;
-    property public final boolean isDoubleArray;
-    property public final boolean isLong;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.Value> CREATOR;
-    field public static final androidx.health.services.client.data.Value.Companion Companion;
-    field public static final int FORMAT_BOOLEAN = 4; // 0x4
-    field public static final int FORMAT_BYTE_ARRAY = 5; // 0x5
-    field public static final int FORMAT_DOUBLE = 1; // 0x1
-    field public static final int FORMAT_DOUBLE_ARRAY = 3; // 0x3
-    field public static final int FORMAT_LONG = 2; // 0x2
-  }
-
-  public static final class Value.Companion {
-    method public int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public androidx.health.services.client.data.Value ofDouble(double value);
-    method public androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public androidx.health.services.client.data.Value ofLong(long value);
-    method public androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-  }
-
   public final class WarmUpConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.WarmUpConfig> {
-    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes);
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getDataTypes();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
     method public androidx.health.services.client.proto.DataProto.WarmUpConfig getProto();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.WarmUpConfig> CREATOR;
     field public static final androidx.health.services.client.data.WarmUpConfig.Companion Companion;
diff --git a/health/health-services-client/api/restricted_1.0.0-beta01.txt b/health/health-services-client/api/restricted_1.0.0-beta01.txt
index 931baf5..56bfe2d 100644
--- a/health/health-services-client/api/restricted_1.0.0-beta01.txt
+++ b/health/health-services-client/api/restricted_1.0.0-beta01.txt
@@ -2,7 +2,7 @@
 package androidx.health.services.client {
 
   public interface ExerciseClient {
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> clearUpdateCallbackAsync(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> endExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> flushExerciseAsync();
@@ -12,7 +12,7 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> overrideAutoPauseAndResumeForActiveExerciseAsync(boolean enabled);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> pauseExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> prepareExerciseAsync(androidx.health.services.client.data.WarmUpConfig configuration);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> resumeExerciseAsync();
     method public void setUpdateCallback(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public void setUpdateCallback(java.util.concurrent.Executor executor, androidx.health.services.client.ExerciseUpdateCallback callback);
@@ -20,7 +20,7 @@
   }
 
   public interface ExerciseUpdateCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
     method public void onLapSummaryReceived(androidx.health.services.client.data.ExerciseLapSummary lapSummary);
     method public void onRegistered();
@@ -42,32 +42,37 @@
   }
 
   public interface MeasureCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
-    method public void onDataReceived(java.util.List<androidx.health.services.client.data.DataPoint> data);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
+    method public void onDataReceived(androidx.health.services.client.data.DataPointContainer data);
     method public default void onRegistered();
     method public default void onRegistrationFailed(Throwable throwable);
   }
 
   public interface MeasureClient {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.health.services.client.data.MeasureCapabilities> getCapabilitiesAsync();
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
-    method public default void onNewDataPointsReceived(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints);
+    method public default void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
     method public default void onPermissionLost();
-    method public void onRegistered();
-    method public void onRegistrationFailed(Throwable throwable);
+    method public default void onRegistered();
+    method public default void onRegistrationFailed(Throwable throwable);
     method public default void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
   }
 
-  public abstract class PassiveListenerService extends android.app.Service implements androidx.health.services.client.PassiveListenerCallback {
+  public abstract class PassiveListenerService extends android.app.Service {
     ctor public PassiveListenerService();
     method public final android.os.IBinder? onBind(android.content.Intent intent);
+    method public final void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
+    method public final void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
+    method public final void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
+    method public final void onPermissionLost();
+    method public final void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
     field public static final String TAG = "PassiveListenerService";
   }
 
@@ -90,34 +95,8 @@
 
 package androidx.health.services.client.data {
 
-  public final class AchievedExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AchievedExerciseGoal> {
-    ctor public AchievedExerciseGoal(androidx.health.services.client.data.ExerciseGoal goal);
-    method public androidx.health.services.client.data.ExerciseGoal getGoal();
-    method public androidx.health.services.client.proto.DataProto.AchievedExerciseGoal getProto();
-    property public final androidx.health.services.client.data.ExerciseGoal goal;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.AchievedExerciseGoal> CREATOR;
-    field public static final androidx.health.services.client.data.AchievedExerciseGoal.Companion Companion;
-  }
-
-  public static final class AchievedExerciseGoal.Companion {
-  }
-
-  public abstract class AggregateDataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AggregateDataPoint> {
-    ctor public AggregateDataPoint();
-  }
-
-  @Keep public final class AggregateDataPoints {
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateAbsoluteElevation(double minAbsElevationMeters, double maxAbsElevationMeters, double avgAbsElevationMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateCalories(double kcalories, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateDistance(double meters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateElevationGain(double gainMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateHeartRate(double minBpm, double maxBpm, double avgBpm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregatePace(java.time.Duration minMillisPerKm, java.time.Duration maxMillisPerKm, java.time.Duration avgMillisPerKm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateSpeed(double minMetersPerSecond, double maxMetersPerSecond, double avgMetersPerSecond, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSteps(long steps, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateStepsPerMinute(long minStepsPerMinute, long maxStepsPerMinute, long avgStepsPerMinute, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSwimmingStrokes(long swimmingStrokes, java.time.Instant startTime, java.time.Instant endTime);
-    field public static final androidx.health.services.client.data.AggregateDataPoints INSTANCE;
+  public final class AggregateDataType<T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public AggregateDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public interface Availability {
@@ -145,146 +124,104 @@
   public static final class ComparisonType.Companion {
   }
 
-  public final class CumulativeDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public CumulativeDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value total);
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    method public androidx.health.services.client.data.Value getTotal();
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final java.time.Instant startTime;
-    property public final androidx.health.services.client.data.Value total;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.CumulativeDataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.CumulativeDataPoint.Companion Companion;
+  public final class CumulativeDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public CumulativeDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.CumulativeDataPoint<T>> dataType, T total, java.time.Instant start, java.time.Instant end);
+    method public java.time.Instant getEnd();
+    method public java.time.Instant getStart();
+    method public T getTotal();
+    property public final java.time.Instant end;
+    property public final java.time.Instant start;
+    property public final T total;
   }
 
-  public static final class CumulativeDataPoint.Companion {
-  }
-
-  public final class DataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPoint> {
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
-    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Duration getEndDurationFromBoot();
-    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
-    method public android.os.Bundle getMetadata();
-    method public androidx.health.services.client.proto.DataProto.DataPoint getProto();
-    method public java.time.Duration getStartDurationFromBoot();
-    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
-    method public androidx.health.services.client.data.Value getValue();
-    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Duration endDurationFromBoot;
-    property public final android.os.Bundle metadata;
-    property public final java.time.Duration startDurationFromBoot;
-    property public final androidx.health.services.client.data.Value value;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.DataPoint.Companion Companion;
-  }
-
-  public static final class DataPoint.Companion {
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
+  public abstract class DataPoint<T> {
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> getDataType();
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> dataType;
   }
 
   public abstract class DataPointAccuracy extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPointAccuracy> {
     ctor public DataPointAccuracy();
   }
 
-  @Keep public final class DataPoints {
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyCalories(double calories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyDistance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyFloors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailySteps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.HeartRateAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.LocationAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint pace(java.time.Duration millisPerKm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint stepsPerMinute(long stepsPerMinute, java.time.Duration startDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint swimmingStrokes(long strokes, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    field public static final androidx.health.services.client.data.DataPoints INSTANCE;
-    field public static final int LOCATION_DATA_POINT_ALTITUDE_INDEX = 2; // 0x2
-    field public static final int LOCATION_DATA_POINT_BEARING_INDEX = 3; // 0x3
-    field public static final int LOCATION_DATA_POINT_LATITUDE_INDEX = 0; // 0x0
-    field public static final int LOCATION_DATA_POINT_LONGITUDE_INDEX = 1; // 0x1
+  public final class DataPointContainer {
+    ctor public DataPointContainer(java.util.Map<androidx.health.services.client.data.DataType<?,?>,? extends java.util.List<? extends androidx.health.services.client.data.DataPoint<?>>> dataPoints);
+    ctor public DataPointContainer(java.util.List<? extends androidx.health.services.client.data.DataPoint<?>> dataPointList);
+    method public java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> getCumulativeDataPoints();
+    method public <T, D extends androidx.health.services.client.data.DataPoint<T>> java.util.List<D> getData(androidx.health.services.client.data.DeltaDataType<T,D> type);
+    method public <T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> D? getData(androidx.health.services.client.data.AggregateDataType<T,D> type);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> getIntervalDataPoints();
+    method public java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> getSampleDataPoints();
+    method public java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> getStatisticalDataPoints();
+    property public final java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> cumulativeDataPoints;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> intervalDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> sampleDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> statisticalDataPoints;
   }
 
-  public final class DataType extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataType> {
-    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, int format);
-    method public int getFormat();
-    method public String getName();
-    method public androidx.health.services.client.proto.DataProto.DataType getProto();
-    method public androidx.health.services.client.data.DataType.TimeType getTimeType();
-    property public final int format;
+  public abstract class DataType<T, D extends androidx.health.services.client.data.DataPoint<T>> {
+    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass, boolean isAggregate);
+    method public final String getName();
+    method public final kotlin.reflect.KClass<T> getValueClass();
     property public final String name;
-    property public final androidx.health.services.client.data.DataType.TimeType timeType;
-    field public static final androidx.health.services.client.data.DataType ABSOLUTE_ELEVATION;
-    field public static final androidx.health.services.client.data.DataType ACTIVE_EXERCISE_DURATION;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataType> CREATOR;
+    property public final kotlin.reflect.KClass<T> valueClass;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> ACTIVE_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> CALORIES_TOTAL;
     field public static final androidx.health.services.client.data.DataType.Companion Companion;
-    field public static final androidx.health.services.client.data.DataType DAILY_CALORIES;
-    field public static final androidx.health.services.client.data.DataType DAILY_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DAILY_FLOORS;
-    field public static final androidx.health.services.client.data.DataType DAILY_STEPS;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType DISTANCE;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_GAIN;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_LOSS;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DURATION;
-    field public static final androidx.health.services.client.data.DataType FLOORS;
-    field public static final androidx.health.services.client.data.DataType GOLF_SHOT_COUNT;
-    field public static final androidx.health.services.client.data.DataType HEART_RATE_BPM;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType LOCATION;
-    field public static final androidx.health.services.client.data.DataType PACE;
-    field public static final androidx.health.services.client.data.DataType REP_COUNT;
-    field public static final androidx.health.services.client.data.DataType RESTING_EXERCISE_DURATION;
-    field public static final androidx.health.services.client.data.DataType RUNNING_STEPS;
-    field public static final androidx.health.services.client.data.DataType SPEED;
-    field public static final androidx.health.services.client.data.DataType SPO2;
-    field public static final androidx.health.services.client.data.DataType STEPS;
-    field public static final androidx.health.services.client.data.DataType STEPS_PER_MINUTE;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_LAP_COUNT;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_STROKES;
-    field public static final androidx.health.services.client.data.DataType TOTAL_CALORIES;
-    field public static final androidx.health.services.client.data.DataType VO2;
-    field public static final androidx.health.services.client.data.DataType VO2_MAX;
-    field public static final androidx.health.services.client.data.DataType WALKING_STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DECLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DECLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> DECLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> DECLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_GAIN;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_GAIN_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_LOSS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_LOSS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> FLAT_GROUND_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> FLAT_GROUND_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLOORS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> GOLF_SHOT_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> GOLF_SHOT_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> HEART_RATE_BPM;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> HEART_RATE_BPM_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> INCLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> INCLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> INCLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> INCLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<androidx.health.services.client.data.LocationData,androidx.health.services.client.data.SampleDataPoint<androidx.health.services.client.data.LocationData>> LOCATION;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> PACE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> PACE_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> REP_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> REP_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS_DAILY;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.SampleDataPoint<java.lang.Long>> STEPS_PER_MINUTE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Long>> STEPS_PER_MINUTE_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_LAP_COUNT;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_STROKES;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> SWIMMING_STROKES_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> VO2_MAX;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> VO2_MAX_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> WALKING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> WALKING_STEPS_TOTAL;
   }
 
   public static final class DataType.Companion {
@@ -298,6 +235,7 @@
     field public static final androidx.health.services.client.data.DataType.TimeType.Companion Companion;
     field public static final androidx.health.services.client.data.DataType.TimeType INTERVAL;
     field public static final androidx.health.services.client.data.DataType.TimeType SAMPLE;
+    field public static final androidx.health.services.client.data.DataType.TimeType UNKNOWN;
   }
 
   public static final class DataType.TimeType.Companion {
@@ -321,28 +259,18 @@
     method public androidx.health.services.client.data.DataTypeAvailability? fromId(int id);
   }
 
-  public final class DataTypeCondition extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataTypeCondition> {
-    ctor public DataTypeCondition(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value threshold, androidx.health.services.client.data.ComparisonType comparisonType);
+  public final class DataTypeCondition<T extends java.lang.Number, D extends androidx.health.services.client.data.DataType<T, ? extends androidx.health.services.client.data.DataPoint<T>>> {
+    ctor public DataTypeCondition(D dataType, T threshold, androidx.health.services.client.data.ComparisonType comparisonType);
     method public androidx.health.services.client.data.ComparisonType getComparisonType();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public androidx.health.services.client.proto.DataProto.DataTypeCondition getProto();
-    method public androidx.health.services.client.data.Value getThreshold();
-    method public boolean isSatisfied(androidx.health.services.client.data.DataPoint dataPoint);
-    method public boolean isThresholdSatisfied(androidx.health.services.client.data.Value value);
+    method public D getDataType();
+    method public T getThreshold();
     property public final androidx.health.services.client.data.ComparisonType comparisonType;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final androidx.health.services.client.data.Value threshold;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataTypeCondition> CREATOR;
-    field public static final androidx.health.services.client.data.DataTypeCondition.Companion Companion;
+    property public final D dataType;
+    property public final T threshold;
   }
 
-  public static final class DataTypeCondition.Companion {
-  }
-
-  public final class DataTypes {
-    method public static boolean isCumulativeDataType(androidx.health.services.client.data.DataType dataType);
-    method public static boolean isStatisticalDataType(androidx.health.services.client.data.DataType dataType);
-    field public static final androidx.health.services.client.data.DataTypes INSTANCE;
+  public final class DeltaDataType<T, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public DeltaDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public final class ExerciseCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseCapabilities> {
@@ -362,63 +290,57 @@
   public static final class ExerciseCapabilities.Companion {
   }
 
-  public final class ExerciseConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseConfig> {
-    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes, java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals, android.os.Bundle exerciseParams);
-    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getAggregateDataTypes();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
-    method public java.util.List<androidx.health.services.client.data.ExerciseGoal> getExerciseGoals();
+  public final class ExerciseConfig {
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams);
+    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> getExerciseGoals();
     method public android.os.Bundle getExerciseParams();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
-    method public androidx.health.services.client.proto.DataProto.ExerciseConfig getProto();
     method public boolean isAutoPauseAndResumeEnabled();
     method public boolean isGpsEnabled();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
-    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals;
     property public final android.os.Bundle exerciseParams;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     property public final boolean isAutoPauseAndResumeEnabled;
     property public final boolean isGpsEnabled;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseConfig> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseConfig.Companion Companion;
   }
 
   public static final class ExerciseConfig.Builder {
-    ctor public ExerciseConfig.Builder();
+    ctor public ExerciseConfig.Builder(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig build();
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setAggregateDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseParams(android.os.Bundle exerciseParams);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseType(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsAutoPauseAndResumeEnabled(boolean isAutoPauseAndResumeEnabled);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsGpsEnabled(boolean isGpsEnabled);
   }
 
   public static final class ExerciseConfig.Companion {
-    method public androidx.health.services.client.data.ExerciseConfig.Builder builder();
+    method public androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
   }
 
-  public final class ExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public static androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
+  public final class ExerciseGoal<T extends java.lang.Number> extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
+    method public androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> getDataTypeCondition();
     method public androidx.health.services.client.data.ExerciseGoalType getExerciseGoalType();
-    method public androidx.health.services.client.data.Value? getPeriod();
+    method public T? getPeriod();
     method public androidx.health.services.client.proto.DataProto.ExerciseGoal getProto();
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> dataTypeCondition;
     property public final androidx.health.services.client.data.ExerciseGoalType exerciseGoalType;
-    property public final androidx.health.services.client.data.Value? period;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal> CREATOR;
+    property public final T? period;
+    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal<?>> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseGoal.Companion Companion;
   }
 
   public static final class ExerciseGoal.Companion {
-    method public androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
   }
 
   public final class ExerciseGoalType {
@@ -450,24 +372,18 @@
   public static final class ExerciseInfo.Companion {
   }
 
-  public final class ExerciseLapSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseLapSummary> {
-    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> lapMetrics);
+  public final class ExerciseLapSummary {
+    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.DataPointContainer lapMetrics);
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public int getLapCount();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLapMetrics();
-    method public androidx.health.services.client.proto.DataProto.ExerciseLapSummary getProto();
+    method public androidx.health.services.client.data.DataPointContainer getLapMetrics();
     method public java.time.Instant getStartTime();
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final int lapCount;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> lapMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer lapMetrics;
     property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseLapSummary> CREATOR;
-    field public static final androidx.health.services.client.data.ExerciseLapSummary.Companion Companion;
-  }
-
-  public static final class ExerciseLapSummary.Companion {
   }
 
   public final class ExerciseState {
@@ -628,18 +544,16 @@
   }
 
   public final class ExerciseTypeCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities> {
-    ctor public ExerciseTypeCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume, boolean supportsLaps);
+    ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume);
     method public androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypes();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypes();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
     method public boolean getSupportsAutoPauseAndResume();
-    method public boolean getSupportsLaps();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypes;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
     property public final boolean supportsAutoPauseAndResume;
-    property public final boolean supportsLaps;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseTypeCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseTypeCapabilities.Companion Companion;
   }
@@ -648,15 +562,15 @@
   }
 
   public final class ExerciseUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseUpdate> {
-    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics, java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
+    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, androidx.health.services.client.data.DataPointContainer latestMetrics, java.util.Set<? extends androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
     method public java.time.Duration getActiveDuration();
-    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.DataPoint dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.IntervalDataPoint<?> dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.SampleDataPoint<?> dataPoint);
     method public androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? getActiveDurationCheckpoint();
     method public androidx.health.services.client.data.ExerciseConfig? getExerciseConfig();
     method public androidx.health.services.client.data.ExerciseStateInfo getExerciseStateInfo();
-    method public java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> getLatestAchievedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLatestAggregateMetrics();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getLatestMetrics();
+    method public java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> getLatestAchievedGoals();
+    method public androidx.health.services.client.data.DataPointContainer getLatestMetrics();
     method public java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> getLatestMilestoneMarkerSummaries();
     method public androidx.health.services.client.proto.DataProto.ExerciseUpdate getProto();
     method public java.time.Instant? getStartTime();
@@ -665,9 +579,8 @@
     property public final androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint;
     property public final androidx.health.services.client.data.ExerciseConfig? exerciseConfig;
     property public final androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo;
-    property public final java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics;
+    property public final java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals;
+    property public final androidx.health.services.client.data.DataPointContainer latestMetrics;
     property public final java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries;
     property public final java.time.Instant? startTime;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseUpdate> CREATOR;
@@ -686,12 +599,12 @@
   }
 
   public final class HealthEvent {
-    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> metrics);
+    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, androidx.health.services.client.data.DataPointContainer metrics);
     method public java.time.Instant getEventTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getMetrics();
+    method public androidx.health.services.client.data.DataPointContainer getMetrics();
     method public androidx.health.services.client.data.HealthEvent.Type getType();
     property public final java.time.Instant eventTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> metrics;
+    property public final androidx.health.services.client.data.DataPointContainer metrics;
     property public final androidx.health.services.client.data.HealthEvent.Type type;
   }
 
@@ -737,6 +650,24 @@
   public static final class HeartRateAccuracy.SensorStatus.Companion {
   }
 
+  public final class IntervalDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public IntervalDataPoint(androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType, T value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> getDataType();
+    method public java.time.Duration getEndDurationFromBoot();
+    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getStartDurationFromBoot();
+    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType;
+    property public final java.time.Duration endDurationFromBoot;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration startDurationFromBoot;
+    property public final T value;
+  }
+
   public final class LocationAccuracy extends androidx.health.services.client.data.DataPointAccuracy {
     ctor public LocationAccuracy(double horizontalPositionErrorMeters, Double? verticalPositionErrorMeters);
     method public double getHorizontalPositionErrorMeters();
@@ -770,11 +701,23 @@
     method public androidx.health.services.client.data.LocationAvailability? fromId(int id);
   }
 
+  public final class LocationData {
+    ctor public LocationData(double latitude, double longitude, optional Double? altitude, optional Double? bearing);
+    method public Double? getAltitude();
+    method public Double? getBearing();
+    method public double getLatitude();
+    method public double getLongitude();
+    property public final Double? altitude;
+    property public final Double? bearing;
+    property public final double latitude;
+    property public final double longitude;
+  }
+
   public final class MeasureCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MeasureCapabilities> {
-    ctor public MeasureCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure);
+    ctor public MeasureCapabilities(java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure);
     method public androidx.health.services.client.proto.DataProto.MeasureCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesMeasure();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure;
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getSupportedDataTypesMeasure();
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MeasureCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.MeasureCapabilities.Companion Companion;
   }
@@ -783,18 +726,18 @@
   }
 
   public final class MilestoneMarkerSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary> {
-    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.AchievedExerciseGoal achievedGoal, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> summaryMetrics);
-    method public androidx.health.services.client.data.AchievedExerciseGoal getAchievedGoal();
+    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal, androidx.health.services.client.data.DataPointContainer summaryMetrics);
+    method public androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> getAchievedGoal();
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary getProto();
     method public java.time.Instant getStartTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getSummaryMetrics();
-    property public final androidx.health.services.client.data.AchievedExerciseGoal achievedGoal;
+    method public androidx.health.services.client.data.DataPointContainer getSummaryMetrics();
+    property public final androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal;
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final java.time.Instant startTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> summaryMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer summaryMetrics;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MilestoneMarkerSummary> CREATOR;
     field public static final androidx.health.services.client.data.MilestoneMarkerSummary.Companion Companion;
   }
@@ -802,34 +745,23 @@
   public static final class MilestoneMarkerSummary.Companion {
   }
 
-  public final class PassiveGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveGoal> {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition dataTypeCondition, int triggerFrequency);
-    method public static androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
-    method public androidx.health.services.client.proto.DataProto.PassiveGoal getProto();
+  public final class PassiveGoal {
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
     method public int getTriggerFrequency();
-    method public boolean isTriggered(androidx.health.services.client.data.DataPoint dataPoint);
-    method public void putToIntent(android.content.Intent intent);
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
     property public final int triggerFrequency;
-    field public static final String ACTION_GOAL = "hs.passivemonitoring.GOAL";
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveGoal> CREATOR;
-    field public static final androidx.health.services.client.data.PassiveGoal.Companion Companion;
-  }
-
-  public static final class PassiveGoal.Companion {
-    method public androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
-    ctor public PassiveListenerConfig(java.util.Set<androidx.health.services.client.data.DataType> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
+    ctor public PassiveListenerConfig(java.util.Set<? extends androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public static androidx.health.services.client.data.PassiveListenerConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    method public java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> getDataTypes();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.PassiveGoal> getPassiveGoals();
     method public androidx.health.services.client.proto.DataProto.PassiveListenerConfig getProto();
     method public boolean getShouldRequestUserActivityState();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals;
     property public final boolean shouldRequestUserActivityState;
@@ -840,7 +772,7 @@
   public static final class PassiveListenerConfig.Builder {
     ctor public PassiveListenerConfig.Builder();
     method public androidx.health.services.client.data.PassiveListenerConfig build();
-    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
+    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setHealthEventTypes(java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setPassiveGoals(java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setShouldRequestUserActivityState(boolean requestUserActivityState);
@@ -851,14 +783,14 @@
   }
 
   public final class PassiveMonitoringCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities> {
-    ctor public PassiveMonitoringCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring, java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
+    ctor public PassiveMonitoringCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveGoals();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveMonitoring();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveGoals();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveMonitoring();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getSupportedHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.UserActivityState> getSupportedUserActivityStates();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringCapabilities> CREATOR;
@@ -869,13 +801,13 @@
   }
 
   public final class PassiveMonitoringUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate> {
-    ctor public PassiveMonitoringUpdate(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
+    ctor public PassiveMonitoringUpdate(androidx.health.services.client.data.DataPointContainer dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
     method public static androidx.health.services.client.data.PassiveMonitoringUpdate? fromIntent(android.content.Intent intent);
-    method public java.util.List<androidx.health.services.client.data.DataPoint> getDataPoints();
+    method public androidx.health.services.client.data.DataPointContainer getDataPoints();
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate getProto();
     method public java.util.List<androidx.health.services.client.data.UserActivityInfo> getUserActivityInfoUpdates();
     method public void putToIntent(android.content.Intent intent);
-    property public final java.util.List<androidx.health.services.client.data.DataPoint> dataPoints;
+    property public final androidx.health.services.client.data.DataPointContainer dataPoints;
     property public final java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates;
     field public static final String ACTION_DATA = "hs.passivemonitoring.DATA";
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringUpdate> CREATOR;
@@ -900,22 +832,33 @@
   public static final class ProtoParcelable.Companion {
   }
 
-  public final class StatisticalDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public StatisticalDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value min, androidx.health.services.client.data.Value max, androidx.health.services.client.data.Value average);
-    method public androidx.health.services.client.data.Value getAverage();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.data.Value getMax();
-    method public androidx.health.services.client.data.Value getMin();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    property public final androidx.health.services.client.data.Value average;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final androidx.health.services.client.data.Value max;
-    property public final androidx.health.services.client.data.Value min;
-    property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.StatisticalDataPoint> CREATOR;
+  public final class SampleDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public SampleDataPoint(androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType, T value, java.time.Duration timeDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> getDataType();
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getTimeDurationFromBoot();
+    method public java.time.Instant getTimeInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration timeDurationFromBoot;
+    property public final T value;
+  }
+
+  public final class StatisticalDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public StatisticalDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.StatisticalDataPoint<T>> dataType, T min, T max, T average, java.time.Instant start, java.time.Instant end);
+    method public T getAverage();
+    method public java.time.Instant getEnd();
+    method public T getMax();
+    method public T getMin();
+    method public java.time.Instant getStart();
+    property public final T average;
+    property public final java.time.Instant end;
+    property public final T max;
+    property public final T min;
+    property public final java.time.Instant start;
     field public static final androidx.health.services.client.data.StatisticalDataPoint.Companion Companion;
   }
 
@@ -962,57 +905,12 @@
   public static final class UserActivityState.Companion {
   }
 
-  public final class Value extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.Value> {
-    method public boolean asBoolean();
-    method public byte[] asByteArray();
-    method public double asDouble();
-    method public double[] asDoubleArray();
-    method public long asLong();
-    method public static int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public int getFormat();
-    method public androidx.health.services.client.proto.DataProto.Value getProto();
-    method public boolean isBoolean();
-    method public boolean isByteArray();
-    method public boolean isDouble();
-    method public boolean isDoubleArray();
-    method public boolean isLong();
-    method public static androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public static androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public static androidx.health.services.client.data.Value ofDouble(double value);
-    method public static androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public static androidx.health.services.client.data.Value ofLong(long value);
-    method public static androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    property public final int format;
-    property public final boolean isBoolean;
-    property public final boolean isByteArray;
-    property public final boolean isDouble;
-    property public final boolean isDoubleArray;
-    property public final boolean isLong;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.Value> CREATOR;
-    field public static final androidx.health.services.client.data.Value.Companion Companion;
-    field public static final int FORMAT_BOOLEAN = 4; // 0x4
-    field public static final int FORMAT_BYTE_ARRAY = 5; // 0x5
-    field public static final int FORMAT_DOUBLE = 1; // 0x1
-    field public static final int FORMAT_DOUBLE_ARRAY = 3; // 0x3
-    field public static final int FORMAT_LONG = 2; // 0x2
-  }
-
-  public static final class Value.Companion {
-    method public int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public androidx.health.services.client.data.Value ofDouble(double value);
-    method public androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public androidx.health.services.client.data.Value ofLong(long value);
-    method public androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-  }
-
   public final class WarmUpConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.WarmUpConfig> {
-    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes);
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getDataTypes();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
     method public androidx.health.services.client.proto.DataProto.WarmUpConfig getProto();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.WarmUpConfig> CREATOR;
     field public static final androidx.health.services.client.data.WarmUpConfig.Companion Companion;
diff --git a/health/health-services-client/api/restricted_current.txt b/health/health-services-client/api/restricted_current.txt
index 931baf5..56bfe2d 100644
--- a/health/health-services-client/api/restricted_current.txt
+++ b/health/health-services-client/api/restricted_current.txt
@@ -2,7 +2,7 @@
 package androidx.health.services.client {
 
   public interface ExerciseClient {
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> addGoalToActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> clearUpdateCallbackAsync(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> endExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> flushExerciseAsync();
@@ -12,7 +12,7 @@
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> overrideAutoPauseAndResumeForActiveExerciseAsync(boolean enabled);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> pauseExerciseAsync();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> prepareExerciseAsync(androidx.health.services.client.data.WarmUpConfig configuration);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal exerciseGoal);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> removeGoalFromActiveExerciseAsync(androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> resumeExerciseAsync();
     method public void setUpdateCallback(androidx.health.services.client.ExerciseUpdateCallback callback);
     method public void setUpdateCallback(java.util.concurrent.Executor executor, androidx.health.services.client.ExerciseUpdateCallback callback);
@@ -20,7 +20,7 @@
   }
 
   public interface ExerciseUpdateCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
     method public void onLapSummaryReceived(androidx.health.services.client.data.ExerciseLapSummary lapSummary);
     method public void onRegistered();
@@ -42,32 +42,37 @@
   }
 
   public interface MeasureCallback {
-    method public void onAvailabilityChanged(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Availability availability);
-    method public void onDataReceived(java.util.List<androidx.health.services.client.data.DataPoint> data);
+    method public void onAvailabilityChanged(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
+    method public void onDataReceived(androidx.health.services.client.data.DataPointContainer data);
     method public default void onRegistered();
     method public default void onRegistrationFailed(Throwable throwable);
   }
 
   public interface MeasureClient {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.health.services.client.data.MeasureCapabilities> getCapabilitiesAsync();
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
-    method public void registerMeasureCallback(androidx.health.services.client.data.DataType dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
+    method public void registerMeasureCallback(androidx.health.services.client.data.DeltaDataType<?,?> dataType, java.util.concurrent.Executor executor, androidx.health.services.client.MeasureCallback callback);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> unregisterMeasureCallbackAsync(androidx.health.services.client.data.DeltaDataType<?,?> dataType, androidx.health.services.client.MeasureCallback callback);
   }
 
   public interface PassiveListenerCallback {
     method public default void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
     method public default void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
-    method public default void onNewDataPointsReceived(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints);
+    method public default void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
     method public default void onPermissionLost();
-    method public void onRegistered();
-    method public void onRegistrationFailed(Throwable throwable);
+    method public default void onRegistered();
+    method public default void onRegistrationFailed(Throwable throwable);
     method public default void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
   }
 
-  public abstract class PassiveListenerService extends android.app.Service implements androidx.health.services.client.PassiveListenerCallback {
+  public abstract class PassiveListenerService extends android.app.Service {
     ctor public PassiveListenerService();
     method public final android.os.IBinder? onBind(android.content.Intent intent);
+    method public final void onGoalCompleted(androidx.health.services.client.data.PassiveGoal goal);
+    method public final void onHealthEventReceived(androidx.health.services.client.data.HealthEvent event);
+    method public final void onNewDataPointsReceived(androidx.health.services.client.data.DataPointContainer dataPoints);
+    method public final void onPermissionLost();
+    method public final void onUserActivityInfoReceived(androidx.health.services.client.data.UserActivityInfo info);
     field public static final String TAG = "PassiveListenerService";
   }
 
@@ -90,34 +95,8 @@
 
 package androidx.health.services.client.data {
 
-  public final class AchievedExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AchievedExerciseGoal> {
-    ctor public AchievedExerciseGoal(androidx.health.services.client.data.ExerciseGoal goal);
-    method public androidx.health.services.client.data.ExerciseGoal getGoal();
-    method public androidx.health.services.client.proto.DataProto.AchievedExerciseGoal getProto();
-    property public final androidx.health.services.client.data.ExerciseGoal goal;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.AchievedExerciseGoal> CREATOR;
-    field public static final androidx.health.services.client.data.AchievedExerciseGoal.Companion Companion;
-  }
-
-  public static final class AchievedExerciseGoal.Companion {
-  }
-
-  public abstract class AggregateDataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.AggregateDataPoint> {
-    ctor public AggregateDataPoint();
-  }
-
-  @Keep public final class AggregateDataPoints {
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateAbsoluteElevation(double minAbsElevationMeters, double maxAbsElevationMeters, double avgAbsElevationMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateCalories(double kcalories, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateDistance(double meters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateElevationGain(double gainMeters, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateHeartRate(double minBpm, double maxBpm, double avgBpm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregatePace(java.time.Duration minMillisPerKm, java.time.Duration maxMillisPerKm, java.time.Duration avgMillisPerKm, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.StatisticalDataPoint aggregateSpeed(double minMetersPerSecond, double maxMetersPerSecond, double avgMetersPerSecond, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSteps(long steps, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateStepsPerMinute(long minStepsPerMinute, long maxStepsPerMinute, long avgStepsPerMinute, java.time.Instant startTime, java.time.Instant endTime);
-    method public static androidx.health.services.client.data.AggregateDataPoint aggregateSwimmingStrokes(long swimmingStrokes, java.time.Instant startTime, java.time.Instant endTime);
-    field public static final androidx.health.services.client.data.AggregateDataPoints INSTANCE;
+  public final class AggregateDataType<T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public AggregateDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public interface Availability {
@@ -145,146 +124,104 @@
   public static final class ComparisonType.Companion {
   }
 
-  public final class CumulativeDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public CumulativeDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value total);
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    method public androidx.health.services.client.data.Value getTotal();
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final java.time.Instant startTime;
-    property public final androidx.health.services.client.data.Value total;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.CumulativeDataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.CumulativeDataPoint.Companion Companion;
+  public final class CumulativeDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public CumulativeDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.CumulativeDataPoint<T>> dataType, T total, java.time.Instant start, java.time.Instant end);
+    method public java.time.Instant getEnd();
+    method public java.time.Instant getStart();
+    method public T getTotal();
+    property public final java.time.Instant end;
+    property public final java.time.Instant start;
+    property public final T total;
   }
 
-  public static final class CumulativeDataPoint.Companion {
-  }
-
-  public final class DataPoint extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPoint> {
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public static androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
-    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Duration getEndDurationFromBoot();
-    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
-    method public android.os.Bundle getMetadata();
-    method public androidx.health.services.client.proto.DataProto.DataPoint getProto();
-    method public java.time.Duration getStartDurationFromBoot();
-    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
-    method public androidx.health.services.client.data.Value getValue();
-    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Duration endDurationFromBoot;
-    property public final android.os.Bundle metadata;
-    property public final java.time.Duration startDurationFromBoot;
-    property public final androidx.health.services.client.data.Value value;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataPoint> CREATOR;
-    field public static final androidx.health.services.client.data.DataPoint.Companion Companion;
-  }
-
-  public static final class DataPoint.Companion {
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createInterval(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot, optional android.os.Bundle metadata);
-    method public androidx.health.services.client.data.DataPoint createSample(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value value, java.time.Duration durationFromBoot);
+  public abstract class DataPoint<T> {
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> getDataType();
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.DataPoint<T>> dataType;
   }
 
   public abstract class DataPointAccuracy extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataPointAccuracy> {
     ctor public DataPointAccuracy();
   }
 
-  @Keep public final class DataPoints {
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint absoluteElevation(double meters, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint calories(double kcalories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyCalories(double calories, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyDistance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailyFloors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint dailySteps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint distance(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationGain(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint elevationLoss(double meters, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint floors(double floors, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint golfShotCount(long shots, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.HeartRateAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint heartRate(double bpm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot, optional androidx.health.services.client.data.LocationAccuracy? accuracy);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, optional double bearing, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, optional double altitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint location(double latitude, double longitude, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint pace(java.time.Duration millisPerKm, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint speed(double metersPerSecond, java.time.Duration durationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle? metadata);
-    method public static androidx.health.services.client.data.DataPoint steps(long steps, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint stepsPerMinute(long stepsPerMinute, java.time.Duration startDurationFromBoot);
-    method public static androidx.health.services.client.data.DataPoint swimmingStrokes(long strokes, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot);
-    field public static final androidx.health.services.client.data.DataPoints INSTANCE;
-    field public static final int LOCATION_DATA_POINT_ALTITUDE_INDEX = 2; // 0x2
-    field public static final int LOCATION_DATA_POINT_BEARING_INDEX = 3; // 0x3
-    field public static final int LOCATION_DATA_POINT_LATITUDE_INDEX = 0; // 0x0
-    field public static final int LOCATION_DATA_POINT_LONGITUDE_INDEX = 1; // 0x1
+  public final class DataPointContainer {
+    ctor public DataPointContainer(java.util.Map<androidx.health.services.client.data.DataType<?,?>,? extends java.util.List<? extends androidx.health.services.client.data.DataPoint<?>>> dataPoints);
+    ctor public DataPointContainer(java.util.List<? extends androidx.health.services.client.data.DataPoint<?>> dataPointList);
+    method public java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> getCumulativeDataPoints();
+    method public <T, D extends androidx.health.services.client.data.DataPoint<T>> java.util.List<D> getData(androidx.health.services.client.data.DeltaDataType<T,D> type);
+    method public <T extends java.lang.Number, D extends androidx.health.services.client.data.DataPoint<T>> D? getData(androidx.health.services.client.data.AggregateDataType<T,D> type);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> getIntervalDataPoints();
+    method public java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> getSampleDataPoints();
+    method public java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> getStatisticalDataPoints();
+    property public final java.util.List<androidx.health.services.client.data.CumulativeDataPoint<?>> cumulativeDataPoints;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.IntervalDataPoint<?>> intervalDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.SampleDataPoint<?>> sampleDataPoints;
+    property public final java.util.List<androidx.health.services.client.data.StatisticalDataPoint<?>> statisticalDataPoints;
   }
 
-  public final class DataType extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataType> {
-    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, int format);
-    method public int getFormat();
-    method public String getName();
-    method public androidx.health.services.client.proto.DataProto.DataType getProto();
-    method public androidx.health.services.client.data.DataType.TimeType getTimeType();
-    property public final int format;
+  public abstract class DataType<T, D extends androidx.health.services.client.data.DataPoint<T>> {
+    ctor public DataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass, boolean isAggregate);
+    method public final String getName();
+    method public final kotlin.reflect.KClass<T> getValueClass();
     property public final String name;
-    property public final androidx.health.services.client.data.DataType.TimeType timeType;
-    field public static final androidx.health.services.client.data.DataType ABSOLUTE_ELEVATION;
-    field public static final androidx.health.services.client.data.DataType ACTIVE_EXERCISE_DURATION;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataType> CREATOR;
+    property public final kotlin.reflect.KClass<T> valueClass;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> ABSOLUTE_ELEVATION_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> ACTIVE_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> CALORIES_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> CALORIES_TOTAL;
     field public static final androidx.health.services.client.data.DataType.Companion Companion;
-    field public static final androidx.health.services.client.data.DataType DAILY_CALORIES;
-    field public static final androidx.health.services.client.data.DataType DAILY_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DAILY_FLOORS;
-    field public static final androidx.health.services.client.data.DataType DAILY_STEPS;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType DECLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType DISTANCE;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_GAIN;
-    field public static final androidx.health.services.client.data.DataType ELEVATION_LOSS;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType FLAT_GROUND_DURATION;
-    field public static final androidx.health.services.client.data.DataType FLOORS;
-    field public static final androidx.health.services.client.data.DataType GOLF_SHOT_COUNT;
-    field public static final androidx.health.services.client.data.DataType HEART_RATE_BPM;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DISTANCE;
-    field public static final androidx.health.services.client.data.DataType INCLINE_DURATION;
-    field public static final androidx.health.services.client.data.DataType LOCATION;
-    field public static final androidx.health.services.client.data.DataType PACE;
-    field public static final androidx.health.services.client.data.DataType REP_COUNT;
-    field public static final androidx.health.services.client.data.DataType RESTING_EXERCISE_DURATION;
-    field public static final androidx.health.services.client.data.DataType RUNNING_STEPS;
-    field public static final androidx.health.services.client.data.DataType SPEED;
-    field public static final androidx.health.services.client.data.DataType SPO2;
-    field public static final androidx.health.services.client.data.DataType STEPS;
-    field public static final androidx.health.services.client.data.DataType STEPS_PER_MINUTE;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_LAP_COUNT;
-    field public static final androidx.health.services.client.data.DataType SWIMMING_STROKES;
-    field public static final androidx.health.services.client.data.DataType TOTAL_CALORIES;
-    field public static final androidx.health.services.client.data.DataType VO2;
-    field public static final androidx.health.services.client.data.DataType VO2_MAX;
-    field public static final androidx.health.services.client.data.DataType WALKING_STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DECLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DECLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> DECLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> DECLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> DISTANCE_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_GAIN;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_GAIN_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> ELEVATION_LOSS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> ELEVATION_LOSS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLAT_GROUND_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> FLAT_GROUND_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> FLAT_GROUND_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> FLOORS_DAILY;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> FLOORS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> GOLF_SHOT_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> GOLF_SHOT_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> HEART_RATE_BPM;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> HEART_RATE_BPM_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.IntervalDataPoint<java.lang.Double>> INCLINE_DISTANCE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Double>> INCLINE_DISTANCE_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> INCLINE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> INCLINE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<androidx.health.services.client.data.LocationData,androidx.health.services.client.data.SampleDataPoint<androidx.health.services.client.data.LocationData>> LOCATION;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> PACE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> PACE_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> REP_COUNT;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> REP_COUNT_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS_DAILY;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.SampleDataPoint<java.lang.Long>> STEPS_PER_MINUTE;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Long>> STEPS_PER_MINUTE_STATS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> STEPS_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_LAP_COUNT;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_STROKES;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> SWIMMING_STROKES_TOTAL;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> VO2_MAX;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> VO2_MAX_STATS;
+    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> WALKING_STEPS;
+    field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> WALKING_STEPS_TOTAL;
   }
 
   public static final class DataType.Companion {
@@ -298,6 +235,7 @@
     field public static final androidx.health.services.client.data.DataType.TimeType.Companion Companion;
     field public static final androidx.health.services.client.data.DataType.TimeType INTERVAL;
     field public static final androidx.health.services.client.data.DataType.TimeType SAMPLE;
+    field public static final androidx.health.services.client.data.DataType.TimeType UNKNOWN;
   }
 
   public static final class DataType.TimeType.Companion {
@@ -321,28 +259,18 @@
     method public androidx.health.services.client.data.DataTypeAvailability? fromId(int id);
   }
 
-  public final class DataTypeCondition extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.DataTypeCondition> {
-    ctor public DataTypeCondition(androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value threshold, androidx.health.services.client.data.ComparisonType comparisonType);
+  public final class DataTypeCondition<T extends java.lang.Number, D extends androidx.health.services.client.data.DataType<T, ? extends androidx.health.services.client.data.DataPoint<T>>> {
+    ctor public DataTypeCondition(D dataType, T threshold, androidx.health.services.client.data.ComparisonType comparisonType);
     method public androidx.health.services.client.data.ComparisonType getComparisonType();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public androidx.health.services.client.proto.DataProto.DataTypeCondition getProto();
-    method public androidx.health.services.client.data.Value getThreshold();
-    method public boolean isSatisfied(androidx.health.services.client.data.DataPoint dataPoint);
-    method public boolean isThresholdSatisfied(androidx.health.services.client.data.Value value);
+    method public D getDataType();
+    method public T getThreshold();
     property public final androidx.health.services.client.data.ComparisonType comparisonType;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final androidx.health.services.client.data.Value threshold;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.DataTypeCondition> CREATOR;
-    field public static final androidx.health.services.client.data.DataTypeCondition.Companion Companion;
+    property public final D dataType;
+    property public final T threshold;
   }
 
-  public static final class DataTypeCondition.Companion {
-  }
-
-  public final class DataTypes {
-    method public static boolean isCumulativeDataType(androidx.health.services.client.data.DataType dataType);
-    method public static boolean isStatisticalDataType(androidx.health.services.client.data.DataType dataType);
-    field public static final androidx.health.services.client.data.DataTypes INSTANCE;
+  public final class DeltaDataType<T, D extends androidx.health.services.client.data.DataPoint<T>> extends androidx.health.services.client.data.DataType<T,D> {
+    ctor public DeltaDataType(String name, androidx.health.services.client.data.DataType.TimeType timeType, kotlin.reflect.KClass<T> valueClass);
   }
 
   public final class ExerciseCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseCapabilities> {
@@ -362,63 +290,57 @@
   public static final class ExerciseCapabilities.Companion {
   }
 
-  public final class ExerciseConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseConfig> {
-    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes, java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals, android.os.Bundle exerciseParams);
-    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getAggregateDataTypes();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
-    method public java.util.List<androidx.health.services.client.data.ExerciseGoal> getExerciseGoals();
+  public final class ExerciseConfig {
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams);
+    method public static androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> getExerciseGoals();
     method public android.os.Bundle getExerciseParams();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
-    method public androidx.health.services.client.proto.DataProto.ExerciseConfig getProto();
     method public boolean isAutoPauseAndResumeEnabled();
     method public boolean isGpsEnabled();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> aggregateDataTypes;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
-    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals;
     property public final android.os.Bundle exerciseParams;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     property public final boolean isAutoPauseAndResumeEnabled;
     property public final boolean isGpsEnabled;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseConfig> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseConfig.Companion Companion;
   }
 
   public static final class ExerciseConfig.Builder {
-    ctor public ExerciseConfig.Builder();
+    ctor public ExerciseConfig.Builder(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig build();
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setAggregateDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<androidx.health.services.client.data.ExerciseGoal> exerciseGoals);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseParams(android.os.Bundle exerciseParams);
-    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseType(androidx.health.services.client.data.ExerciseType exerciseType);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsAutoPauseAndResumeEnabled(boolean isAutoPauseAndResumeEnabled);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setIsGpsEnabled(boolean isGpsEnabled);
   }
 
   public static final class ExerciseConfig.Companion {
-    method public androidx.health.services.client.data.ExerciseConfig.Builder builder();
+    method public androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
   }
 
-  public final class ExerciseGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public static androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public static androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
+  public final class ExerciseGoal<T extends java.lang.Number> extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseGoal> {
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
+    method public androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> getDataTypeCondition();
     method public androidx.health.services.client.data.ExerciseGoalType getExerciseGoalType();
-    method public androidx.health.services.client.data.Value? getPeriod();
+    method public T? getPeriod();
     method public androidx.health.services.client.proto.DataProto.ExerciseGoal getProto();
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> dataTypeCondition;
     property public final androidx.health.services.client.data.ExerciseGoalType exerciseGoalType;
-    property public final androidx.health.services.client.data.Value? period;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal> CREATOR;
+    property public final T? period;
+    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseGoal<?>> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseGoal.Companion Companion;
   }
 
   public static final class ExerciseGoal.Companion {
-    method public androidx.health.services.client.data.ExerciseGoal createMilestone(androidx.health.services.client.data.DataTypeCondition condition, androidx.health.services.client.data.Value period);
-    method public androidx.health.services.client.data.ExerciseGoal createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal goal, androidx.health.services.client.data.Value newThreshold);
-    method public androidx.health.services.client.data.ExerciseGoal createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition condition);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
+    method public <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createOneTimeGoal(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition);
   }
 
   public final class ExerciseGoalType {
@@ -450,24 +372,18 @@
   public static final class ExerciseInfo.Companion {
   }
 
-  public final class ExerciseLapSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseLapSummary> {
-    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> lapMetrics);
+  public final class ExerciseLapSummary {
+    ctor public ExerciseLapSummary(int lapCount, java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.DataPointContainer lapMetrics);
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public int getLapCount();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLapMetrics();
-    method public androidx.health.services.client.proto.DataProto.ExerciseLapSummary getProto();
+    method public androidx.health.services.client.data.DataPointContainer getLapMetrics();
     method public java.time.Instant getStartTime();
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final int lapCount;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> lapMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer lapMetrics;
     property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseLapSummary> CREATOR;
-    field public static final androidx.health.services.client.data.ExerciseLapSummary.Companion Companion;
-  }
-
-  public static final class ExerciseLapSummary.Companion {
   }
 
   public final class ExerciseState {
@@ -628,18 +544,16 @@
   }
 
   public final class ExerciseTypeCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities> {
-    ctor public ExerciseTypeCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume, boolean supportsLaps);
+    ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume);
     method public androidx.health.services.client.proto.DataProto.ExerciseTypeCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypes();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypes();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
+    method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
     method public boolean getSupportsAutoPauseAndResume();
-    method public boolean getSupportsLaps();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypes;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypes;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
+    property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
     property public final boolean supportsAutoPauseAndResume;
-    property public final boolean supportsLaps;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseTypeCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.ExerciseTypeCapabilities.Companion Companion;
   }
@@ -648,15 +562,15 @@
   }
 
   public final class ExerciseUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.ExerciseUpdate> {
-    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics, java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
+    ctor public ExerciseUpdate(java.time.Instant? startTime, java.time.Duration activeDuration, java.time.Duration? updateDurationFromBoot, androidx.health.services.client.data.DataPointContainer latestMetrics, java.util.Set<? extends androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals, java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries, androidx.health.services.client.data.ExerciseConfig? exerciseConfig, androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint, androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo);
     method public java.time.Duration getActiveDuration();
-    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.DataPoint dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.IntervalDataPoint<?> dataPoint);
+    method public java.time.Duration getActiveDurationAtDataPoint(androidx.health.services.client.data.SampleDataPoint<?> dataPoint);
     method public androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? getActiveDurationCheckpoint();
     method public androidx.health.services.client.data.ExerciseConfig? getExerciseConfig();
     method public androidx.health.services.client.data.ExerciseStateInfo getExerciseStateInfo();
-    method public java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> getLatestAchievedGoals();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getLatestAggregateMetrics();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getLatestMetrics();
+    method public java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> getLatestAchievedGoals();
+    method public androidx.health.services.client.data.DataPointContainer getLatestMetrics();
     method public java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> getLatestMilestoneMarkerSummaries();
     method public androidx.health.services.client.proto.DataProto.ExerciseUpdate getProto();
     method public java.time.Instant? getStartTime();
@@ -665,9 +579,8 @@
     property public final androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint? activeDurationCheckpoint;
     property public final androidx.health.services.client.data.ExerciseConfig? exerciseConfig;
     property public final androidx.health.services.client.data.ExerciseStateInfo exerciseStateInfo;
-    property public final java.util.Set<androidx.health.services.client.data.AchievedExerciseGoal> latestAchievedGoals;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> latestAggregateMetrics;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> latestMetrics;
+    property public final java.util.Set<androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number>> latestAchievedGoals;
+    property public final androidx.health.services.client.data.DataPointContainer latestMetrics;
     property public final java.util.Set<androidx.health.services.client.data.MilestoneMarkerSummary> latestMilestoneMarkerSummaries;
     property public final java.time.Instant? startTime;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.ExerciseUpdate> CREATOR;
@@ -686,12 +599,12 @@
   }
 
   public final class HealthEvent {
-    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, java.util.Map<androidx.health.services.client.data.DataType,? extends java.util.List<androidx.health.services.client.data.DataPoint>> metrics);
+    ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, androidx.health.services.client.data.DataPointContainer metrics);
     method public java.time.Instant getEventTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> getMetrics();
+    method public androidx.health.services.client.data.DataPointContainer getMetrics();
     method public androidx.health.services.client.data.HealthEvent.Type getType();
     property public final java.time.Instant eventTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,java.util.List<androidx.health.services.client.data.DataPoint>> metrics;
+    property public final androidx.health.services.client.data.DataPointContainer metrics;
     property public final androidx.health.services.client.data.HealthEvent.Type type;
   }
 
@@ -737,6 +650,24 @@
   public static final class HeartRateAccuracy.SensorStatus.Companion {
   }
 
+  public final class IntervalDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public IntervalDataPoint(androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType, T value, java.time.Duration startDurationFromBoot, java.time.Duration endDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> getDataType();
+    method public java.time.Duration getEndDurationFromBoot();
+    method public java.time.Instant getEndInstant(java.time.Instant bootInstant);
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getStartDurationFromBoot();
+    method public java.time.Instant getStartInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,? extends androidx.health.services.client.data.IntervalDataPoint<T>> dataType;
+    property public final java.time.Duration endDurationFromBoot;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration startDurationFromBoot;
+    property public final T value;
+  }
+
   public final class LocationAccuracy extends androidx.health.services.client.data.DataPointAccuracy {
     ctor public LocationAccuracy(double horizontalPositionErrorMeters, Double? verticalPositionErrorMeters);
     method public double getHorizontalPositionErrorMeters();
@@ -770,11 +701,23 @@
     method public androidx.health.services.client.data.LocationAvailability? fromId(int id);
   }
 
+  public final class LocationData {
+    ctor public LocationData(double latitude, double longitude, optional Double? altitude, optional Double? bearing);
+    method public Double? getAltitude();
+    method public Double? getBearing();
+    method public double getLatitude();
+    method public double getLongitude();
+    property public final Double? altitude;
+    property public final Double? bearing;
+    property public final double latitude;
+    property public final double longitude;
+  }
+
   public final class MeasureCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MeasureCapabilities> {
-    ctor public MeasureCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure);
+    ctor public MeasureCapabilities(java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure);
     method public androidx.health.services.client.proto.DataProto.MeasureCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesMeasure();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesMeasure;
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getSupportedDataTypesMeasure();
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> supportedDataTypesMeasure;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MeasureCapabilities> CREATOR;
     field public static final androidx.health.services.client.data.MeasureCapabilities.Companion Companion;
   }
@@ -783,18 +726,18 @@
   }
 
   public final class MilestoneMarkerSummary extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary> {
-    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.AchievedExerciseGoal achievedGoal, java.util.Map<androidx.health.services.client.data.DataType,? extends androidx.health.services.client.data.AggregateDataPoint> summaryMetrics);
-    method public androidx.health.services.client.data.AchievedExerciseGoal getAchievedGoal();
+    ctor public MilestoneMarkerSummary(java.time.Instant startTime, java.time.Instant endTime, java.time.Duration activeDuration, androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal, androidx.health.services.client.data.DataPointContainer summaryMetrics);
+    method public androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> getAchievedGoal();
     method public java.time.Duration getActiveDuration();
     method public java.time.Instant getEndTime();
     method public androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary getProto();
     method public java.time.Instant getStartTime();
-    method public java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> getSummaryMetrics();
-    property public final androidx.health.services.client.data.AchievedExerciseGoal achievedGoal;
+    method public androidx.health.services.client.data.DataPointContainer getSummaryMetrics();
+    property public final androidx.health.services.client.data.ExerciseGoal<? extends java.lang.Number> achievedGoal;
     property public final java.time.Duration activeDuration;
     property public final java.time.Instant endTime;
     property public final java.time.Instant startTime;
-    property public final java.util.Map<androidx.health.services.client.data.DataType,androidx.health.services.client.data.AggregateDataPoint> summaryMetrics;
+    property public final androidx.health.services.client.data.DataPointContainer summaryMetrics;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.MilestoneMarkerSummary> CREATOR;
     field public static final androidx.health.services.client.data.MilestoneMarkerSummary.Companion Companion;
   }
@@ -802,34 +745,23 @@
   public static final class MilestoneMarkerSummary.Companion {
   }
 
-  public final class PassiveGoal extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveGoal> {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition dataTypeCondition, int triggerFrequency);
-    method public static androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
-    method public androidx.health.services.client.data.DataTypeCondition getDataTypeCondition();
-    method public androidx.health.services.client.proto.DataProto.PassiveGoal getProto();
+  public final class PassiveGoal {
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
     method public int getTriggerFrequency();
-    method public boolean isTriggered(androidx.health.services.client.data.DataPoint dataPoint);
-    method public void putToIntent(android.content.Intent intent);
-    property public final androidx.health.services.client.data.DataTypeCondition dataTypeCondition;
+    property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
     property public final int triggerFrequency;
-    field public static final String ACTION_GOAL = "hs.passivemonitoring.GOAL";
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveGoal> CREATOR;
-    field public static final androidx.health.services.client.data.PassiveGoal.Companion Companion;
-  }
-
-  public static final class PassiveGoal.Companion {
-    method public androidx.health.services.client.data.PassiveGoal? fromIntent(android.content.Intent intent);
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
-    ctor public PassiveListenerConfig(java.util.Set<androidx.health.services.client.data.DataType> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
+    ctor public PassiveListenerConfig(java.util.Set<? extends androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes, boolean shouldRequestUserActivityState, java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public static androidx.health.services.client.data.PassiveListenerConfig.Builder builder();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    method public java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> getDataTypes();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.PassiveGoal> getPassiveGoals();
     method public androidx.health.services.client.proto.DataProto.PassiveListenerConfig getProto();
     method public boolean getShouldRequestUserActivityState();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<? extends java.lang.Object,? extends androidx.health.services.client.data.DataPoint<?>>> dataTypes;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals;
     property public final boolean shouldRequestUserActivityState;
@@ -840,7 +772,7 @@
   public static final class PassiveListenerConfig.Builder {
     ctor public PassiveListenerConfig.Builder();
     method public androidx.health.services.client.data.PassiveListenerConfig build();
-    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
+    method public androidx.health.services.client.data.PassiveListenerConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setHealthEventTypes(java.util.Set<androidx.health.services.client.data.HealthEvent.Type> healthEventTypes);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setPassiveGoals(java.util.Set<androidx.health.services.client.data.PassiveGoal> passiveGoals);
     method public androidx.health.services.client.data.PassiveListenerConfig.Builder setShouldRequestUserActivityState(boolean requestUserActivityState);
@@ -851,14 +783,14 @@
   }
 
   public final class PassiveMonitoringCapabilities extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities> {
-    ctor public PassiveMonitoringCapabilities(java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring, java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
+    ctor public PassiveMonitoringCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals, java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes, java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates);
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringCapabilities getProto();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveGoals();
-    method public java.util.Set<androidx.health.services.client.data.DataType> getSupportedDataTypesPassiveMonitoring();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveGoals();
+    method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypesPassiveMonitoring();
     method public java.util.Set<androidx.health.services.client.data.HealthEvent.Type> getSupportedHealthEventTypes();
     method public java.util.Set<androidx.health.services.client.data.UserActivityState> getSupportedUserActivityStates();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveGoals;
-    property public final java.util.Set<androidx.health.services.client.data.DataType> supportedDataTypesPassiveMonitoring;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveGoals;
+    property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypesPassiveMonitoring;
     property public final java.util.Set<androidx.health.services.client.data.HealthEvent.Type> supportedHealthEventTypes;
     property public final java.util.Set<androidx.health.services.client.data.UserActivityState> supportedUserActivityStates;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringCapabilities> CREATOR;
@@ -869,13 +801,13 @@
   }
 
   public final class PassiveMonitoringUpdate extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate> {
-    ctor public PassiveMonitoringUpdate(java.util.List<androidx.health.services.client.data.DataPoint> dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
+    ctor public PassiveMonitoringUpdate(androidx.health.services.client.data.DataPointContainer dataPoints, java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates);
     method public static androidx.health.services.client.data.PassiveMonitoringUpdate? fromIntent(android.content.Intent intent);
-    method public java.util.List<androidx.health.services.client.data.DataPoint> getDataPoints();
+    method public androidx.health.services.client.data.DataPointContainer getDataPoints();
     method public androidx.health.services.client.proto.DataProto.PassiveMonitoringUpdate getProto();
     method public java.util.List<androidx.health.services.client.data.UserActivityInfo> getUserActivityInfoUpdates();
     method public void putToIntent(android.content.Intent intent);
-    property public final java.util.List<androidx.health.services.client.data.DataPoint> dataPoints;
+    property public final androidx.health.services.client.data.DataPointContainer dataPoints;
     property public final java.util.List<androidx.health.services.client.data.UserActivityInfo> userActivityInfoUpdates;
     field public static final String ACTION_DATA = "hs.passivemonitoring.DATA";
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.PassiveMonitoringUpdate> CREATOR;
@@ -900,22 +832,33 @@
   public static final class ProtoParcelable.Companion {
   }
 
-  public final class StatisticalDataPoint extends androidx.health.services.client.data.AggregateDataPoint {
-    ctor public StatisticalDataPoint(java.time.Instant startTime, java.time.Instant endTime, androidx.health.services.client.data.DataType dataType, androidx.health.services.client.data.Value min, androidx.health.services.client.data.Value max, androidx.health.services.client.data.Value average);
-    method public androidx.health.services.client.data.Value getAverage();
-    method public androidx.health.services.client.data.DataType getDataType();
-    method public java.time.Instant getEndTime();
-    method public androidx.health.services.client.data.Value getMax();
-    method public androidx.health.services.client.data.Value getMin();
-    method public androidx.health.services.client.proto.DataProto.AggregateDataPoint getProto();
-    method public java.time.Instant getStartTime();
-    property public final androidx.health.services.client.data.Value average;
-    property public final androidx.health.services.client.data.DataType dataType;
-    property public final java.time.Instant endTime;
-    property public final androidx.health.services.client.data.Value max;
-    property public final androidx.health.services.client.data.Value min;
-    property public final java.time.Instant startTime;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.StatisticalDataPoint> CREATOR;
+  public final class SampleDataPoint<T> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public SampleDataPoint(androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType, T value, java.time.Duration timeDurationFromBoot, optional android.os.Bundle metadata, optional androidx.health.services.client.data.DataPointAccuracy? accuracy);
+    method public androidx.health.services.client.data.DataPointAccuracy? getAccuracy();
+    method public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> getDataType();
+    method public android.os.Bundle getMetadata();
+    method public java.time.Duration getTimeDurationFromBoot();
+    method public java.time.Instant getTimeInstant(java.time.Instant bootInstant);
+    method public T getValue();
+    property public final androidx.health.services.client.data.DataPointAccuracy? accuracy;
+    property public androidx.health.services.client.data.DataType<T,androidx.health.services.client.data.SampleDataPoint<T>> dataType;
+    property public final android.os.Bundle metadata;
+    property public final java.time.Duration timeDurationFromBoot;
+    property public final T value;
+  }
+
+  public final class StatisticalDataPoint<T extends java.lang.Number> extends androidx.health.services.client.data.DataPoint<T> {
+    ctor public StatisticalDataPoint(androidx.health.services.client.data.AggregateDataType<T,androidx.health.services.client.data.StatisticalDataPoint<T>> dataType, T min, T max, T average, java.time.Instant start, java.time.Instant end);
+    method public T getAverage();
+    method public java.time.Instant getEnd();
+    method public T getMax();
+    method public T getMin();
+    method public java.time.Instant getStart();
+    property public final T average;
+    property public final java.time.Instant end;
+    property public final T max;
+    property public final T min;
+    property public final java.time.Instant start;
     field public static final androidx.health.services.client.data.StatisticalDataPoint.Companion Companion;
   }
 
@@ -962,57 +905,12 @@
   public static final class UserActivityState.Companion {
   }
 
-  public final class Value extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.Value> {
-    method public boolean asBoolean();
-    method public byte[] asByteArray();
-    method public double asDouble();
-    method public double[] asDoubleArray();
-    method public long asLong();
-    method public static int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public int getFormat();
-    method public androidx.health.services.client.proto.DataProto.Value getProto();
-    method public boolean isBoolean();
-    method public boolean isByteArray();
-    method public boolean isDouble();
-    method public boolean isDoubleArray();
-    method public boolean isLong();
-    method public static androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public static androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public static androidx.health.services.client.data.Value ofDouble(double value);
-    method public static androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public static androidx.health.services.client.data.Value ofLong(long value);
-    method public static androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    property public final int format;
-    property public final boolean isBoolean;
-    property public final boolean isByteArray;
-    property public final boolean isDouble;
-    property public final boolean isDoubleArray;
-    property public final boolean isLong;
-    field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.Value> CREATOR;
-    field public static final androidx.health.services.client.data.Value.Companion Companion;
-    field public static final int FORMAT_BOOLEAN = 4; // 0x4
-    field public static final int FORMAT_BYTE_ARRAY = 5; // 0x5
-    field public static final int FORMAT_DOUBLE = 1; // 0x1
-    field public static final int FORMAT_DOUBLE_ARRAY = 3; // 0x3
-    field public static final int FORMAT_LONG = 2; // 0x2
-  }
-
-  public static final class Value.Companion {
-    method public int compare(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-    method public androidx.health.services.client.data.Value ofBoolean(boolean value);
-    method public androidx.health.services.client.data.Value ofByteArray(byte[] value);
-    method public androidx.health.services.client.data.Value ofDouble(double value);
-    method public androidx.health.services.client.data.Value ofDoubleArray(double... doubleArray);
-    method public androidx.health.services.client.data.Value ofLong(long value);
-    method public androidx.health.services.client.data.Value sum(androidx.health.services.client.data.Value first, androidx.health.services.client.data.Value second);
-  }
-
   public final class WarmUpConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.WarmUpConfig> {
-    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<androidx.health.services.client.data.DataType> dataTypes);
-    method public java.util.Set<androidx.health.services.client.data.DataType> getDataTypes();
+    ctor public WarmUpConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes);
+    method public java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> getDataTypes();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
     method public androidx.health.services.client.proto.DataProto.WarmUpConfig getProto();
-    property public final java.util.Set<androidx.health.services.client.data.DataType> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.DeltaDataType<?,?>> dataTypes;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
     field public static final android.os.Parcelable.Creator<androidx.health.services.client.data.WarmUpConfig> CREATOR;
     field public static final androidx.health.services.client.data.WarmUpConfig.Companion Companion;
diff --git a/health/health-services-client/build.gradle b/health/health-services-client/build.gradle
index b135b8b..138d521 100644
--- a/health/health-services-client/build.gradle
+++ b/health/health-services-client/build.gradle
@@ -15,7 +15,6 @@
  */
 
 import androidx.build.LibraryType
-import androidx.build.dependencies.DependenciesKt
 
 plugins {
     id("AndroidXPlugin")
@@ -31,6 +30,12 @@
     implementation(libs.guavaAndroid)
     implementation("androidx.core:core-ktx:1.7.0")
     implementation(libs.protobufLite)
+
+    testImplementation(libs.junit)
+    testImplementation(libs.kotlinReflect)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.testCore)
+    testImplementation(libs.truth)
 }
 
 android {
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClient.kt b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClient.kt
index b7a7d9e..5b303bd 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClient.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClient.kt
@@ -23,6 +23,7 @@
 import androidx.health.services.client.data.ExerciseGoal
 import androidx.health.services.client.data.ExerciseInfo
 import androidx.health.services.client.data.ExerciseState
+import androidx.health.services.client.data.ExerciseEndReason
 import androidx.health.services.client.data.ExerciseType
 import androidx.health.services.client.data.ExerciseUpdate
 import androidx.health.services.client.data.WarmUpConfig
@@ -236,7 +237,7 @@
      * @return a [ListenableFuture] that completes once the exercise goal has been added. This
      * returned [ListenableFuture] fails if the calling app does not own the active exercise.
      */
-    public fun addGoalToActiveExerciseAsync(exerciseGoal: ExerciseGoal): ListenableFuture<Void>
+    public fun addGoalToActiveExerciseAsync(exerciseGoal: ExerciseGoal<*>): ListenableFuture<Void>
 
     /**
      * Removes an exercise goal for an active exercise.
@@ -250,7 +251,9 @@
      * returned [ListenableFuture] fails if the exercise is not active, and will be a no-op if
      * [exerciseGoal] has not been added in the past.
      */
-    public fun removeGoalFromActiveExerciseAsync(exerciseGoal: ExerciseGoal): ListenableFuture<Void>
+    public fun removeGoalFromActiveExerciseAsync(
+        exerciseGoal: ExerciseGoal<*>
+    ): ListenableFuture<Void>
 
     /**
      * Enables or disables auto pause/resume for the current exercise.
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseUpdateCallback.kt b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseUpdateCallback.kt
index 66ae32a..f2bc4c34 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseUpdateCallback.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseUpdateCallback.kt
@@ -58,5 +58,5 @@
      * @param dataType the [DataType] which experienced a change in availability
      * @param availability the new [Availability] state
      */
-    public fun onAvailabilityChanged(dataType: DataType, availability: Availability)
+    public fun onAvailabilityChanged(dataType: DataType<*, *>, availability: Availability)
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/MeasureCallback.kt b/health/health-services-client/src/main/java/androidx/health/services/client/MeasureCallback.kt
index 01f1123..e648778 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/MeasureCallback.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/MeasureCallback.kt
@@ -18,9 +18,11 @@
 
 import androidx.health.services.client.data.Availability
 import androidx.health.services.client.data.DataPoint
+import androidx.health.services.client.data.DataPointContainer
 import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.DeltaDataType
 
-/** Callback for [MeasureClient.registerCallback]. */
+/** Callback for [MeasureClient.registerMeasureCallback]. */
 public interface MeasureCallback {
 
     /** Called when this callback has been successfully registered with Health Services. */
@@ -38,15 +40,16 @@
     /**
      * Called when the availability of a [DataType] changes.
      *
-     * @param dataType the [DataType] that experienced a change in availability
+     * @param dataType the [DeltaDataType] that experienced a change in availability
      * @param availability the new [Availability] status for this [dataType]
      */
-    public fun onAvailabilityChanged(dataType: DataType, availability: Availability)
+    public fun onAvailabilityChanged(dataType: DeltaDataType<*, *>, availability: Availability)
 
     /**
-     * Called when new data is available. Data can be batched in a list of [DataPoint].
+     * Called when new data is available. Data may be batched.
      *
-     * @param data the (potentially batched) set of measured [DataPoint]s
+     * @param data the (potentially batched) set of measured [DataPoint]s corresponding to one or
+     * more of the requested [DeltaDataType]s
      */
-    public fun onDataReceived(data: List<@JvmSuppressWildcards DataPoint>)
+    public fun onDataReceived(data: DataPointContainer)
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/MeasureClient.kt b/health/health-services-client/src/main/java/androidx/health/services/client/MeasureClient.kt
index 1eb9a27..7a0fb47 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/MeasureClient.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/MeasureClient.kt
@@ -16,7 +16,7 @@
 
 package androidx.health.services.client
 
-import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.DeltaDataType
 import androidx.health.services.client.data.MeasureCapabilities
 import com.google.common.util.concurrent.ListenableFuture
 import java.util.concurrent.Executor
@@ -33,7 +33,7 @@
  */
 public interface MeasureClient {
     /**
-     * Registers the app for live measurement of the specified [DataType].
+     * Registers the app for live measurement of the specified [DeltaDataType].
      *
      * The callback will be called on the main application thread. To move calls to an alternative
      * thread use [registerMeasureCallback].
@@ -41,56 +41,57 @@
      * Even if data is registered for live capture, it can still be sent out in batches depending on
      * the application processor state.
      *
-     * Registering a [DataType] for live measurement capture is expected to increase the sample rate
-     * on the associated sensor(s); this is typically used for one-off measurements. Do not use this
-     * method for background capture or workout tracking. The client is responsible for ensuring
-     * that their requested [DataType] is supported on this device by checking the
+     * Registering a [DeltaDataType] for live measurement capture is expected to increase the sample
+     * rate on the associated sensor(s); this is typically used for one-off measurements. Do not use
+     * this method for background capture or workout tracking. The client is responsible for
+     * ensuring that their requested [DeltaDataType] is supported on this device by checking the
      * [MeasureCapabilities]. The returned future will fail if the request is not supported on a
      * given device.
      *
      * The callback will continue to be called until the app is killed or
      * [unregisterMeasureCallbackAsync] is called.
      *
-     * If the same [callback] is already registered for the given [DataType], this operation is a
-     * no-op.
+     * If the same [callback] is already registered for the given [DeltaDataType], this operation is
+     * a no-op.
      *
-     * @param dataType the [DataType] that needs to be measured
+     * @param dataType the [DeltaDataType] that needs to be measured
      * @param callback the [MeasureCallback] to receive updates from Health Services
      */
-    public fun registerMeasureCallback(dataType: DataType, callback: MeasureCallback)
+    public fun registerMeasureCallback(dataType: DeltaDataType<*, *>, callback: MeasureCallback)
 
     /**
      * Same as [registerMeasureCallback], except the [callback] is called on the given [Executor].
      *
-     * @param dataType the [DataType] that needs to be measured
+     * @param dataType the [DeltaDataType] that needs to be measured
      * @param executor the [Executor] on which [callback] will be invoked
      * @param callback the [MeasureCallback] to receive updates from Health Services
      */
     public fun registerMeasureCallback(
-        dataType: DataType,
+        dataType: DeltaDataType<*, *>,
         executor: Executor,
         callback: MeasureCallback
     )
 
     /**
-     * Unregisters the given [MeasureCallback] for updates of the given [DataType].
+     * Unregisters the given [MeasureCallback] for updates of the given [DeltaDataType].
      *
-     * @param dataType the [DataType] that needs to be unregistered
+     * @param dataType the [DeltaDataType] that needs to be unregistered
      * @param callback the [MeasureCallback] which was used in registration
      * @return a [ListenableFuture] that completes when the un-registration succeeds in Health
      * Services. This is a no-op if the callback has already been unregistered.
      */
     public fun unregisterMeasureCallbackAsync(
-        dataType: DataType,
+        dataType: DeltaDataType<*, *>,
         callback: MeasureCallback
     ): ListenableFuture<Void>
 
     /**
      * Returns the [MeasureCapabilities] of this client for the device.
      *
-     * This can be used to determine what [DataType]s this device supports for live measurement.
-     * Clients should use the capabilities to inform their requests since Health Services will
-     * typically reject requests made for [DataType]s which are not enabled for measurement.
+     * This can be used to determine what [DeltaDataType]s this device supports for live
+     * measurement. Clients should use the capabilities to inform their requests since Health
+     * Services will typically reject requests made for [DeltaDataType]s which are not enabled for
+     * measurement.
      *
      * @return a [ListenableFuture] containing the [MeasureCapabilities] for this device
      */
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/PassiveListenerCallback.kt b/health/health-services-client/src/main/java/androidx/health/services/client/PassiveListenerCallback.kt
index 45ffd86..b202724 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/PassiveListenerCallback.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/PassiveListenerCallback.kt
@@ -17,6 +17,7 @@
 package androidx.health.services.client
 
 import androidx.health.services.client.data.DataPoint
+import androidx.health.services.client.data.DataPointContainer
 import androidx.health.services.client.data.HealthEvent
 import androidx.health.services.client.data.PassiveGoal
 import androidx.health.services.client.data.UserActivityInfo
@@ -25,21 +26,21 @@
 public interface PassiveListenerCallback {
 
     /** Called when this callback has been successfully registered with Health Services. */
-    public fun onRegistered()
+    public fun onRegistered() {}
 
     /**
      * Called when Health Services reports a failure with the registration of this callback.
      *
      * @param throwable a [Throwable] sent by Health Services with information about the failure
      */
-    public fun onRegistrationFailed(throwable: Throwable)
+    public fun onRegistrationFailed(throwable: Throwable) {}
 
     /**
      * Called when new [DataPoint]s are generated.
      *
      * @param dataPoints a list of new [DataPoint]s generated
      */
-    public fun onNewDataPointsReceived(dataPoints: List<DataPoint>) {}
+    public fun onNewDataPointsReceived(dataPoints: DataPointContainer) {}
 
     /**
      * Called when new [UserActivityInfo] is generated.
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/PassiveListenerService.kt b/health/health-services-client/src/main/java/androidx/health/services/client/PassiveListenerService.kt
index cae3646..7b3f66d33 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/PassiveListenerService.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/PassiveListenerService.kt
@@ -20,6 +20,11 @@
 import android.content.Intent
 import android.os.IBinder
 import android.util.Log
+import androidx.health.services.client.data.DataPoint
+import androidx.health.services.client.data.DataPointContainer
+import androidx.health.services.client.data.HealthEvent
+import androidx.health.services.client.data.PassiveGoal
+import androidx.health.services.client.data.UserActivityInfo
 import androidx.health.services.client.impl.IPassiveListenerService
 import androidx.health.services.client.impl.event.PassiveListenerEvent
 import androidx.health.services.client.impl.response.HealthEventResponse
@@ -40,7 +45,8 @@
  * the [PassiveListenerCallback] that they care about. They can then pass in their service to
  * [PassiveMonitoringClient.setPassiveListenerServiceAsync] to receive data updates.
  */
-public abstract class PassiveListenerService : PassiveListenerCallback, Service() {
+@Suppress("UNUSED_PARAMETER")
+public abstract class PassiveListenerService : Service() {
 
     private var wrapper: IPassiveListenerServiceWrapper? = null
 
@@ -49,6 +55,42 @@
         return wrapper
     }
 
+    /**
+     * Called when new [DataPoint]s are generated.
+     *
+     * @param dataPoints a list of new [DataPoint]s generated
+     */
+    public fun onNewDataPointsReceived(dataPoints: DataPointContainer) {}
+
+    /**
+     * Called when new [UserActivityInfo] is generated.
+     *
+     * @param info a new [UserActivityInfo] representing the current state
+     */
+    public fun onUserActivityInfoReceived(info: UserActivityInfo) {}
+
+    /**
+     * Called when a [PassiveGoal] has been completed.
+     *
+     * @param goal the [PassiveGoal] that has been completed
+     */
+    public fun onGoalCompleted(goal: PassiveGoal) {}
+
+    /**
+     * Called when a [HealthEvent] has been detected.
+     *
+     * @param event the [HealthEvent] that has been detected
+     */
+    public fun onHealthEventReceived(event: HealthEvent) {}
+
+    /**
+     * Called when the client has lost permission for the passive listener request. If this happens,
+     * WHS will automatically unregister the client request and stop the relevant sensors. The
+     * client can use this callback to detect the problem and either prompt the user to re-grant the
+     * permissions or re-register while requesting only that which the app does have permission for.
+     */
+    public fun onPermissionLost() {}
+
     private inner class IPassiveListenerServiceWrapper : IPassiveListenerService.Stub() {
 
         override fun onPassiveListenerEvent(event: PassiveListenerEvent) {
@@ -57,7 +99,7 @@
             when (proto.eventCase) {
                 PASSIVE_UPDATE_RESPONSE -> {
                     val response = PassiveMonitoringUpdateResponse(proto.passiveUpdateResponse)
-                    if (!response.passiveMonitoringUpdate.dataPoints.isEmpty()) {
+                    if (!response.passiveMonitoringUpdate.dataPoints.dataPoints.isEmpty()) {
                         this@PassiveListenerService.onNewDataPointsReceived(
                             response.passiveMonitoringUpdate.dataPoints
                         )
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/AchievedExerciseGoal.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/AchievedExerciseGoal.kt
deleted file mode 100644
index 59414b8..0000000
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/AchievedExerciseGoal.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2021 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.health.services.client.data
-
-import android.os.Parcelable
-import androidx.health.services.client.proto.DataProto
-
-/** Defines an achieved [ExerciseGoal]. */
-@Suppress("ParcelCreator")
-public class AchievedExerciseGoal(
-    /** [ExerciseGoal] that has been achieved. */
-    // TODO(b/181235444): do we need to deliver the DataPoint to the user again here, given
-    // that they will have already gotten it in the ExerciseState? And, what other data do we need
-    // to
-    // tag along an achieved ExerciseGoal?
-    public val goal: ExerciseGoal,
-) : ProtoParcelable<DataProto.AchievedExerciseGoal>() {
-
-    internal constructor(
-        proto: DataProto.AchievedExerciseGoal
-    ) : this(ExerciseGoal(proto.exerciseGoal))
-
-    /** @hide */
-    override val proto: DataProto.AchievedExerciseGoal by lazy {
-        DataProto.AchievedExerciseGoal.newBuilder().setExerciseGoal(goal.proto).build()
-    }
-
-    override fun toString(): String = "AchievedExerciseGoal(goal=$goal)"
-
-    public companion object {
-        @JvmField
-        public val CREATOR: Parcelable.Creator<AchievedExerciseGoal> = newCreator {
-            val proto = DataProto.AchievedExerciseGoal.parseFrom(it)
-            AchievedExerciseGoal(proto)
-        }
-    }
-}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/AggregateDataPoint.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/AggregateDataPoint.kt
deleted file mode 100644
index 2edd65a..0000000
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/AggregateDataPoint.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package androidx.health.services.client.data
-
-import androidx.health.services.client.proto.DataProto
-import androidx.health.services.client.proto.DataProto.AggregateDataPoint.AggregateCase.AGGREGATE_NOT_SET
-import androidx.health.services.client.proto.DataProto.AggregateDataPoint.AggregateCase.CUMULATIVE_DATA_POINT
-import androidx.health.services.client.proto.DataProto.AggregateDataPoint.AggregateCase.STATISTICAL_DATA_POINT
-
-/** Accuracy of a [DataPoint]. */
-@Suppress("ParcelCreator", "ParcelNotFinal")
-public abstract class AggregateDataPoint : ProtoParcelable<DataProto.AggregateDataPoint>() {
-
-    internal companion object {
-        internal fun fromProto(proto: DataProto.AggregateDataPoint): AggregateDataPoint =
-            when (proto.aggregateCase) {
-                CUMULATIVE_DATA_POINT -> CumulativeDataPoint(proto)
-                STATISTICAL_DATA_POINT -> StatisticalDataPoint(proto)
-                null, AGGREGATE_NOT_SET ->
-                    throw IllegalStateException("Aggregate not set on $proto")
-            }
-    }
-}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/AggregateDataPoints.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/AggregateDataPoints.kt
deleted file mode 100644
index 0dfcb72..0000000
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/AggregateDataPoints.kt
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2021 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.health.services.client.data
-
-import androidx.annotation.Keep
-import java.time.Duration
-import java.time.Instant
-
-/** Helper class to facilitate working with [DataPoint]s. */
-// TODO(b/177504986): Remove all @Keep annotations once we figure out why this class gets stripped
-// away by proguard.
-@Keep
-public object AggregateDataPoints {
-
-    /**
-     * Creates a new [StatisticalDataPoint] of type [DataType.ABSOLUTE_ELEVATION] with the given
-     * elevations (in meters).
-     */
-    @JvmStatic
-    public fun aggregateAbsoluteElevation(
-        minAbsElevationMeters: Double,
-        maxAbsElevationMeters: Double,
-        avgAbsElevationMeters: Double,
-        startTime: Instant,
-        endTime: Instant
-    ): StatisticalDataPoint =
-        StatisticalDataPoint(
-            startTime,
-            endTime,
-            DataType.ABSOLUTE_ELEVATION,
-            Value.ofDouble(minAbsElevationMeters),
-            Value.ofDouble(maxAbsElevationMeters),
-            Value.ofDouble(avgAbsElevationMeters)
-        )
-
-    /**
-     * Creates a new [AggregateDataPoint] of type [DataType.TOTAL_CALORIES] with the given
-     * `kcalories`.
-     */
-    @JvmStatic
-    public fun aggregateCalories(
-        kcalories: Double,
-        startTime: Instant,
-        endTime: Instant
-    ): AggregateDataPoint =
-        CumulativeDataPoint(startTime, endTime, DataType.TOTAL_CALORIES, Value.ofDouble(kcalories))
-
-    /** Creates a new [AggregateDataPoint] for the [DataType.DISTANCE] with the given `meters`. */
-    @JvmStatic
-    public fun aggregateDistance(
-        meters: Double,
-        startTime: Instant,
-        endTime: Instant
-    ): AggregateDataPoint =
-        CumulativeDataPoint(startTime, endTime, DataType.DISTANCE, Value.ofDouble(meters))
-
-    /**
-     * Creates a new [AggregateDataPoint] for the [DataType.ELEVATION_GAIN] with the given
-     * `gainMeters`.
-     */
-    @JvmStatic
-    public fun aggregateElevationGain(
-        gainMeters: Double,
-        startTime: Instant,
-        endTime: Instant
-    ): AggregateDataPoint =
-        CumulativeDataPoint(startTime, endTime, DataType.ELEVATION_GAIN, Value.ofDouble(gainMeters))
-
-    /**
-     * Creates a new [AggregateDataPoint] of type [DataType.HEART_RATE_BPM] with the given `bpm`s.
-     */
-    @JvmStatic
-    public fun aggregateHeartRate(
-        minBpm: Double,
-        maxBpm: Double,
-        avgBpm: Double,
-        startTime: Instant,
-        endTime: Instant
-    ): StatisticalDataPoint =
-        StatisticalDataPoint(
-            startTime,
-            endTime,
-            DataType.HEART_RATE_BPM,
-            Value.ofDouble(minBpm),
-            Value.ofDouble(maxBpm),
-            Value.ofDouble(avgBpm)
-        )
-
-    /** Creates a new [AggregateDataPoint] of type [DataType.PACE] with the given `millisPerKm`. */
-    @JvmStatic
-    public fun aggregatePace(
-        minMillisPerKm: Duration,
-        maxMillisPerKm: Duration,
-        avgMillisPerKm: Duration,
-        startTime: Instant,
-        endTime: Instant
-    ): AggregateDataPoint =
-        StatisticalDataPoint(
-            startTime,
-            endTime,
-            DataType.PACE,
-            Value.ofDouble((minMillisPerKm.toMillis()).toDouble()),
-            Value.ofDouble((maxMillisPerKm.toMillis()).toDouble()),
-            Value.ofDouble((avgMillisPerKm.toMillis()).toDouble())
-        )
-
-    /**
-     * Creates a new [StatisticalDataPoint] of type [DataType.SPEED] with the given
-     * `metersPerSecond`.
-     */
-    @JvmStatic
-    public fun aggregateSpeed(
-        minMetersPerSecond: Double,
-        maxMetersPerSecond: Double,
-        avgMetersPerSecond: Double,
-        startTime: Instant,
-        endTime: Instant
-    ): StatisticalDataPoint =
-        StatisticalDataPoint(
-            startTime,
-            endTime,
-            DataType.SPEED,
-            Value.ofDouble(minMetersPerSecond),
-            Value.ofDouble(maxMetersPerSecond),
-            Value.ofDouble(avgMetersPerSecond)
-        )
-
-    /** Creates a new [AggregateDataPoint] of type [DataType.STEPS] with the given `steps`. */
-    @JvmStatic
-    public fun aggregateSteps(
-        steps: Long,
-        startTime: Instant,
-        endTime: Instant
-    ): AggregateDataPoint =
-        CumulativeDataPoint(startTime, endTime, DataType.STEPS, Value.ofLong(steps))
-
-    /**
-     * Creates a new [AggregateDataPoint] of type [DataType.STEPS_PER_MINUTE] with the given
-     * `steps`.
-     *
-     * @param minStepsPerMinute minimum number of steps per minute between [startTime] and [endTime]
-     * @param maxStepsPerMinute maximum number of steps per minute between [startTime] and [endTime]
-     * @param avgStepsPerMinute average number of steps per minute between [startTime] and [endTime]
-     * @param startTime the point in time this data point begins
-     * @param endTime the point in time this data point ends
-     */
-    @JvmStatic
-    public fun aggregateStepsPerMinute(
-        minStepsPerMinute: Long,
-        maxStepsPerMinute: Long,
-        avgStepsPerMinute: Long,
-        startTime: Instant,
-        endTime: Instant
-    ): AggregateDataPoint =
-        StatisticalDataPoint(
-            startTime,
-            endTime,
-            DataType.STEPS_PER_MINUTE,
-            Value.ofLong(minStepsPerMinute),
-            Value.ofLong(maxStepsPerMinute),
-            Value.ofLong(avgStepsPerMinute)
-        )
-
-    /**
-     * Creates a new [DataPoint] of type [DataType.SWIMMING_STROKES] with the given
-     * `swimmingStrokes`.
-     */
-    @JvmStatic
-    public fun aggregateSwimmingStrokes(
-        swimmingStrokes: Long,
-        startTime: Instant,
-        endTime: Instant
-    ): AggregateDataPoint =
-        CumulativeDataPoint(
-            startTime,
-            endTime,
-            DataType.SWIMMING_STROKES,
-            Value.ofLong(swimmingStrokes)
-        )
-}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/BundlesUtil.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/BundlesUtil.kt
index 2659f03..1d81efb 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/BundlesUtil.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/BundlesUtil.kt
@@ -17,17 +17,11 @@
 package androidx.health.services.client.data
 
 import android.os.Bundle
-import androidx.annotation.RestrictTo
 import androidx.health.services.client.proto.DataProto
 import com.google.protobuf.ByteString
 
-/**
- * Utility methods for working with Bundles.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public object BundlesUtil {
+/** Utility methods for working with Bundles. */
+internal object BundlesUtil {
 
     @JvmStatic
     internal fun toProto(bundle: Bundle): DataProto.Bundle {
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/CumulativeDataPoint.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/CumulativeDataPoint.kt
index ab2a337..9f7ca1e 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/CumulativeDataPoint.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/CumulativeDataPoint.kt
@@ -1,58 +1,65 @@
+/*
+ * Copyright 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.health.services.client.data
 
-import android.os.Parcelable
 import androidx.health.services.client.proto.DataProto
-import androidx.health.services.client.proto.DataProto.AggregateDataPoint.CumulativeDataPoint as CumulativeDataPointProto
 import java.time.Instant
 
 /**
- * An [AggregateDataPoint] containing a cumulative [total] for the type [dataType] between
- * [startTime] and [endTime]. This should generally correspond with [DataPoint]s of type
- * [DataType.TimeType.INTERVAL].
+ * A [DataPoint] containing a cumulative [total] for the type [dataType] between [start] and [end].
+ * Unlike [IntervalDataPoint], this is guaranteed to increase over time (assuming the same [start]
+ * value.) For example, an [IntervalDataPoint] for [DataType.STEPS]
  */
-@Suppress("ParcelCreator")
-public class CumulativeDataPoint(
-    public val startTime: Instant,
-    public val endTime: Instant,
-    public val dataType: DataType,
-    public val total: Value,
-) : AggregateDataPoint() {
+class CumulativeDataPoint<T : Number>(
+    /** The [DataType] this [DataPoint] represents. */
+    dataType: AggregateDataType<T, CumulativeDataPoint<T>>,
+    /** The accumulated value between [start] and [end]. */
+    val total: T,
+    /** The beginning of the time period this [DataPoint] represents. */
+    val start: Instant,
+    /** The end of the time period this [DataPoint] represents. */
+    val end: Instant
+) : DataPoint<T>(dataType) {
 
-    internal constructor(
-        proto: DataProto.AggregateDataPoint
-    ) : this(
-        Instant.ofEpochMilli(proto.cumulativeDataPoint.startTimeEpochMs),
-        Instant.ofEpochMilli(proto.cumulativeDataPoint.endTimeEpochMs),
-        DataType(proto.cumulativeDataPoint.dataType),
-        Value(proto.cumulativeDataPoint.total)
-    )
-
-    /** @hide */
-    override val proto: DataProto.AggregateDataPoint by lazy {
+    internal val proto: DataProto.AggregateDataPoint by lazy {
         DataProto.AggregateDataPoint.newBuilder()
             .setCumulativeDataPoint(
-                CumulativeDataPointProto.newBuilder()
-                    .setStartTimeEpochMs(startTime.toEpochMilli())
-                    .setEndTimeEpochMs(endTime.toEpochMilli())
+                DataProto.AggregateDataPoint.CumulativeDataPoint.newBuilder()
                     .setDataType(dataType.proto)
-                    .setTotal(total.proto)
-                    .build()
-            )
-            .build()
+                    .setStartTimeEpochMs(start.toEpochMilli())
+                    .setEndTimeEpochMs(end.toEpochMilli())
+                    .setTotal(dataType.toProtoFromValue(total))
+            ).build()
     }
 
-    override fun toString(): String =
-        "CumulativeDataPoint(" +
-            "startTime=$startTime, " +
-            "endTime=$endTime, " +
-            "dataType=$dataType, " +
-            "total=$total)"
-
-    public companion object {
-        @JvmField
-        public val CREATOR: Parcelable.Creator<CumulativeDataPoint> = newCreator {
-            val proto = DataProto.AggregateDataPoint.parseFrom(it)
-            CumulativeDataPoint(proto)
+    internal companion object {
+        @Suppress("UNCHECKED_CAST")
+        internal fun fromProto(
+            proto: DataProto.AggregateDataPoint.CumulativeDataPoint
+        ): CumulativeDataPoint<*> {
+            val dataType =
+                DataType.aggregateFromProto(proto.dataType)
+                    as AggregateDataType<Number, CumulativeDataPoint<Number>>
+            return CumulativeDataPoint(
+                dataType,
+                dataType.toValueFromProto(proto.total),
+                start = Instant.ofEpochMilli(proto.startTimeEpochMs),
+                end = Instant.ofEpochMilli(proto.endTimeEpochMs)
+            )
         }
     }
-}
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPoint.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPoint.kt
index af0cc1e..d2e71ea 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPoint.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPoint.kt
@@ -16,188 +16,28 @@
 
 package androidx.health.services.client.data
 
-import android.os.Bundle
-import android.os.Parcelable
 import androidx.health.services.client.proto.DataProto
-import java.time.Duration
-import java.time.Instant
+import androidx.health.services.client.proto.DataProto.AggregateDataPoint
 
-/**
- * A data point containing a [value] of type [dataType] from either a single point in time:
- * [DataType.TimeType.SAMPLE], or a range in time: [DataType.TimeType.INTERVAL].
- */
-@Suppress("DataClassPrivateConstructor", "ParcelCreator")
-public class DataPoint
-internal constructor(
-    public val dataType: DataType,
-    public val value: Value,
-
-    /**
-     * Elapsed start time of this [DataPoint].
-     *
-     * This represents the time at which this [DataPoint] originated, as a [Duration] since boot
-     * time. This is not exposed as a timestamp as the clock may drift between when the data is
-     * generated and when it is read out. Use [getStartInstant] to get the start time of this
-     * [DataPoint] as an [Instant].
-     */
-    public val startDurationFromBoot: Duration,
-
-    /**
-     * Elapsed end time of this [DataPoint].
-     *
-     * This represents the time at which this [DataPoint] ends, as a [Duration] since boot time.
-     * This is not exposed as a timestamp as the clock may drift between when the data is generated
-     * and when it is read out. Use [getStartInstant] to get the start time of this [DataPoint] as
-     * an [Instant].
-     *
-     * For instantaneous data points, this is equal to [startDurationFromBoot].
-     */
-    public val endDurationFromBoot: Duration = startDurationFromBoot,
-
-    /** Returns any provided metadata of this [DataPoint]. */
-    public val metadata: Bundle = Bundle(),
-
-    /**
-     * Returns the accuracy of this [DataPoint].
-     *
-     * The specific [DataPointAccuracy] implementation this refers to depends on the [DataType] of
-     * the data point. For example, accuracy of [DataType.LOCATION] data points is represented by
-     * [LocationAccuracy]. If there is no associated [DataPointAccuracy] for the [DataType], this
-     * will return `null`.
-     */
-    public val accuracy: DataPointAccuracy? = null,
-) : ProtoParcelable<DataProto.DataPoint>() {
-
-    internal constructor(
-        proto: DataProto.DataPoint
-    ) : this(
-        DataType(proto.dataType),
-        Value(proto.value),
-        Duration.ofMillis(proto.startDurationFromBootMs),
-        Duration.ofMillis(proto.endDurationFromBootMs),
-        BundlesUtil.fromProto(proto.metaData),
-        if (proto.hasAccuracy()) DataPointAccuracy.fromProto(proto.accuracy) else null
-    )
-
-    /** @hide */
-    override val proto: DataProto.DataPoint by lazy {
-        val builder =
-            DataProto.DataPoint.newBuilder()
-                .setDataType(dataType.proto)
-                .setValue(value.proto)
-                .setStartDurationFromBootMs(startDurationFromBoot.toMillis())
-                .setEndDurationFromBootMs(endDurationFromBoot.toMillis())
-                .setMetaData(BundlesUtil.toProto(metadata))
-
-        accuracy?.let { builder.setAccuracy(it.proto) }
-
-        builder.build()
-    }
-
-    init {
-        require(dataType.format == value.format) {
-            "DataType and Value format must match, but got ${dataType.format} and ${value.format}"
-        }
-    }
-
-    /**
-     * Returns the start [Instant] of this [DataPoint], knowing the time at which the system booted.
-     *
-     * @param bootInstant the [Instant] at which the system booted, this can be computed by
-     * `Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())`
-     */
-    public fun getStartInstant(bootInstant: Instant): Instant {
-        return bootInstant.plus(startDurationFromBoot)
-    }
-
-    /**
-     * Returns the end [Instant] of this [DataPoint], knowing the time at which the system booted.
-     *
-     * @param bootInstant the [Instant] at which the system booted, this can be computed by
-     * `Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())`
-     */
-    public fun getEndInstant(bootInstant: Instant): Instant {
-        return bootInstant.plus(endDurationFromBoot)
-    }
-
-    override fun toString(): String =
-        "DataPoint(" +
-            "dataType=$dataType, " +
-            "value=$value, " +
-            "startDurationFromBoot=$startDurationFromBoot, " +
-            "endDurationFromBoot=$endDurationFromBoot, " +
-            "accuracy=$accuracy)"
-
-    public companion object {
-        @JvmField
-        public val CREATOR: Parcelable.Creator<DataPoint> = newCreator {
-            val proto = DataProto.DataPoint.parseFrom(it)
-            DataPoint(proto)
-        }
-
-        /**
-         * Returns a [DataPoint] representing the [value] of type [dataType] from
-         * [startDurationFromBoot] to [endDurationFromBoot].
-         *
-         * @throws IllegalArgumentException if the [DataType.TimeType] of the associated [DataType]
-         * is not [DataType.TimeType.INTERVAL], or if data is malformed
-         */
-        @JvmStatic
-        @JvmOverloads
-        public fun createInterval(
-            dataType: DataType,
-            value: Value,
-            startDurationFromBoot: Duration,
-            endDurationFromBoot: Duration,
-            metadata: Bundle = Bundle(),
-            accuracy: DataPointAccuracy? = null
-        ): DataPoint {
-            require(DataType.TimeType.INTERVAL == dataType.timeType) {
-                "DataType $dataType must be of interval type to be created with an interval"
+/** Base class to represent individual pieces of data of type [dataType]. */
+public abstract class DataPoint<T : Any> internal constructor(
+    /** Type of data contained within this [DataPoint]. */
+    public open val dataType: DataType<T, out DataPoint<T>>,
+) {
+    internal companion object {
+        internal fun fromProto(proto: AggregateDataPoint): DataPoint<*> {
+            return if (proto.hasCumulativeDataPoint()) {
+                CumulativeDataPoint.fromProto(proto.cumulativeDataPoint)
+            } else {
+                StatisticalDataPoint.fromProto(proto.statisticalDataPoint)
             }
-
-            require(endDurationFromBoot >= startDurationFromBoot) {
-                "End timestamp mustn't be earlier than start timestamp, but got " +
-                    "$startDurationFromBoot and $endDurationFromBoot"
-            }
-
-            return DataPoint(
-                dataType,
-                value,
-                startDurationFromBoot,
-                endDurationFromBoot,
-                metadata,
-                accuracy
-            )
         }
-
-        /**
-         * Returns a [DataPoint] representing the [value] of type [dataType] at [durationFromBoot].
-         *
-         * @throws IllegalArgumentException if the [DataType.TimeType] of the associated [DataType]
-         * is not [DataType.TimeType.SAMPLE], or if data is malformed
-         */
-        @JvmStatic
-        @JvmOverloads
-        public fun createSample(
-            dataType: DataType,
-            value: Value,
-            durationFromBoot: Duration,
-            metadata: Bundle = Bundle(),
-            accuracy: DataPointAccuracy? = null
-        ): DataPoint {
-            require(DataType.TimeType.SAMPLE == dataType.timeType) {
-                "DataType $dataType must be of sample type to be created with a single timestamp"
+        internal fun fromProto(proto: DataProto.DataPoint): DataPoint<*> {
+            return if (proto.dataType.timeType == DataProto.DataType.TimeType.TIME_TYPE_INTERVAL) {
+                IntervalDataPoint.fromProto(proto)
+            } else {
+                SampleDataPoint.fromProto(proto)
             }
-
-            return DataPoint(
-                dataType,
-                value,
-                durationFromBoot,
-                endDurationFromBoot = durationFromBoot,
-                metadata,
-                accuracy
-            )
         }
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPointContainer.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPointContainer.kt
new file mode 100644
index 0000000..b9b8717
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPointContainer.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 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.health.services.client.data
+
+/**
+ * Container that provides ease of use methods to access [DataPoint]s in a type safe way.
+ *
+ * Example:
+ * ```
+ * dataPointContainer.getData(DataType.LOCATION).forEach { location ->
+ *   Log.d(TAG, "location = ${location.latitude}, ${location.longitude}")
+ * }
+ * ```
+ */
+class DataPointContainer(
+    internal val dataPoints: Map<DataType<*, *>, List<DataPoint<*>>>
+) {
+
+    /** Constructs a [DataPointContainer] using a list of [DataPoint]s. */
+    constructor(dataPointList: List<DataPoint<*>>) : this(
+        dataPointList.groupBy { it.dataType }
+    )
+
+    /** Set of [DataType]s contained within this [DataPointContainer]. */
+    val dataTypes: Set<DataType<*, *>> = dataPoints.keys
+
+    /** Returns all [SampleDataPoint]s contained in this update. */
+    val sampleDataPoints: List<SampleDataPoint<*>> by lazy {
+        dataPoints.flatMap { it.value }.filterIsInstance(SampleDataPoint::class.java).toList()
+    }
+
+    /** Returns all [IntervalDataPoint]s contained in this update. */
+    val intervalDataPoints: List<IntervalDataPoint<*>> by lazy {
+        dataPoints.flatMap { it.value }.filterIsInstance(IntervalDataPoint::class.java).toList()
+    }
+
+    /** Returns all [CumulativeDataPoint]s contained in this update. */
+    val cumulativeDataPoints: List<CumulativeDataPoint<*>> by lazy {
+        dataPoints.flatMap { it.value }.filterIsInstance(CumulativeDataPoint::class.java).toList()
+    }
+
+    /** Returns all [StatisticalDataPoint]s contained in this update. */
+    val statisticalDataPoints: List<StatisticalDataPoint<*>> by lazy {
+        dataPoints.flatMap { it.value }.filterIsInstance(StatisticalDataPoint::class.java).toList()
+    }
+
+    /** Returns all [DataPoint] objects with a matching delta [type]. */
+    @Suppress("UNCHECKED_CAST")
+    fun <T : Any, D : DataPoint<T>> getData(type: DeltaDataType<T, D>): List<D> {
+        return dataPoints[type] as? List<D> ?: emptyList()
+    }
+
+    /**
+     * Returns the [DataPoint] object with a matching aggregate [type], otherwise `null` if exist in
+     * this [DataPointContainer].
+     */
+    @Suppress("UNCHECKED_CAST")
+    fun <T : Number, D : DataPoint<T>> getData(type: AggregateDataType<T, D>): D? {
+        return (dataPoints[type] as? List<D>)?.lastOrNull()
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPoints.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPoints.kt
index 7fdf585..87e558e 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPoints.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataPoints.kt
@@ -16,344 +16,479 @@
 
 package androidx.health.services.client.data
 
-import android.os.Bundle
-import androidx.annotation.Keep
 import java.time.Duration
+import java.time.Instant
 
-/** Helper class to facilitate working with [DataPoint]s. */
+/**
+ * Helper class to facilitate creating [DataPoint]s. In general, this should not be needed outside
+ * of tests.
+ */
 // TODO(b/177504986): Remove all @Keep annotations once we figure out why this class gets stripped
 // away by proguard.
-@Keep
-public object DataPoints {
-    /**
-     * When using [DataType.LOCATION], the value is represented as `double[]`. The `double` value at
-     * this index represents the latitude.
-     */
-    public const val LOCATION_DATA_POINT_LATITUDE_INDEX: Int = 0
+internal object DataPoints {
 
     /**
-     * When using [DataType.LOCATION], the value is represented as `double[]`. The `double` value at
-     * this index represents the longitude.
+     * Creates a new [IntervalDataPoint] of type [DataType.STEPS] with the given [steps].
+     *
+     * @param steps number of steps taken between [startDurationFromBoot] and [endDurationFromBoot]
+     * @param startDurationFromBoot the point in time this data point begins
+     * @param endDurationFromBoot the point in time this data point ends
      */
-    public const val LOCATION_DATA_POINT_LONGITUDE_INDEX: Int = 1
-
-    /**
-     * When using [DataType.LOCATION], the value is represented as `double[]`. The `double` value at
-     * this index represents the altitude. This value will default to [Double.MAX_VALUE] if it is
-     * not available.
-     */
-    public const val LOCATION_DATA_POINT_ALTITUDE_INDEX: Int = 2
-
-    /**
-     * When using [DataType.LOCATION], the value is represented as `double[]`. The `double` value at
-     * this index represents the bearing. This value will default to [Double.MAX_VALUE] if it is not
-     * available.
-     */
-    public const val LOCATION_DATA_POINT_BEARING_INDEX: Int = 3
-
-    /** Name of intent extra containing the data points set on pending intent. */
-    private const val EXTRA_DATA_POINTS: String = "hs.data_points_list"
-
-    /** Name of intent extra containing whether permissions are granted or not. */
-    private const val EXTRA_PERMISSIONS_GRANTED: String = "hs.data_points_has_permissions"
-
-    /** Creates a new [DataPoint] of type [DataType.STEPS] with the given `steps`. */
     @JvmStatic
-    @JvmOverloads
     public fun steps(
         steps: Long,
         startDurationFromBoot: Duration,
         endDurationFromBoot: Duration,
-        metadata: Bundle? = null
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.STEPS,
-            Value.ofLong(steps),
-            startDurationFromBoot,
-            endDurationFromBoot,
-            metadata ?: Bundle()
+    ): IntervalDataPoint<Long> =
+        IntervalDataPoint(
+            dataType = DataType.STEPS,
+            value = steps,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot,
         )
 
     /**
-     * Creates a new [DataPoint] of type [DataType.STEPS_PER_MINUTE] with the given
-     * `stepsPerMinute`.
+     * Creates a new [SampleDataPoint] of type [DataType.STEPS_PER_MINUTE] with the given
+     * [stepsPerMinute].
+     *
+     * @param stepsPerMinute step rate at [timeDurationFromBoot]
+     * @param timeDurationFromBoot the point in time [stepsPerMinute] is accurate
      */
     @JvmStatic
-    public fun stepsPerMinute(stepsPerMinute: Long, startDurationFromBoot: Duration): DataPoint =
-        DataPoint.createSample(
-            DataType.STEPS_PER_MINUTE,
-            Value.ofLong(stepsPerMinute),
-            startDurationFromBoot
+    public fun stepsPerMinute(
+        stepsPerMinute: Long,
+        timeDurationFromBoot: Duration
+    ): SampleDataPoint<Long> =
+        SampleDataPoint(
+            dataType = DataType.STEPS_PER_MINUTE,
+            value = stepsPerMinute,
+            timeDurationFromBoot = timeDurationFromBoot
         )
 
-    /** Creates a new [DataPoint] of type [DataType.DISTANCE] with the given `meters`. */
+    /**
+     * Creates a new [IntervalDataPoint] of type [DataType.DISTANCE] with the given [meters].
+     *
+     * @param meters distance traveled between [startDurationFromBoot] and [endDurationFromBoot]
+     * @param startDurationFromBoot the point in time this data point begins
+     * @param endDurationFromBoot the point in time this data point ends
+     */
     @JvmStatic
-    @JvmOverloads
     public fun distance(
         meters: Double,
         startDurationFromBoot: Duration,
         endDurationFromBoot: Duration,
-        metadata: Bundle? = null
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.DISTANCE,
-            Value.ofDouble(meters),
-            startDurationFromBoot,
-            endDurationFromBoot,
-            metadata ?: Bundle()
+    ): IntervalDataPoint<Double> =
+        IntervalDataPoint(
+            dataType = DataType.DISTANCE,
+            value = meters,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot,
         )
 
     /**
-     * Creates a new [DataPoint] of type [DataType.ELEVATION_GAIN] with the given `meters`.
+     * Creates a new [CumulativeDataPoint] for [DataType.DISTANCE_TOTAL] with the given [meters].
+     *
+     * @param meters distance accumulated between [startTime] and [endTime]
+     * @param startTime the point in time this data point begins
+     * @param endTime the point in time this data point ends
+     */
+    @JvmStatic
+    public fun distanceTotal(
+        meters: Double,
+        startTime: Instant,
+        endTime: Instant
+    ): CumulativeDataPoint<Double> =
+        CumulativeDataPoint(
+            dataType = DataType.DISTANCE_TOTAL,
+            total = meters,
+            start = startTime,
+            end = endTime
+        )
+
+    /**
+     * Creates a new [IntervalDataPoint] of type [DataType.ELEVATION_GAIN] with the given [meters].
      *
      * @param meters meters gained between [startDurationFromBoot] and [endDurationFromBoot]
      * @param startDurationFromBoot the point in time this data point begins
      * @param endDurationFromBoot the point in time this data point ends
-     * @param metadata optional OEM specific data, not intended for broad consumption
      */
     @JvmStatic
-    @JvmOverloads
     public fun elevationGain(
         meters: Double,
         startDurationFromBoot: Duration,
         endDurationFromBoot: Duration,
-        metadata: Bundle? = null
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.ELEVATION_GAIN,
-            Value.ofDouble(meters),
-            startDurationFromBoot,
-            endDurationFromBoot,
-            metadata ?: Bundle()
+    ): IntervalDataPoint<Double> =
+        IntervalDataPoint(
+            dataType = DataType.ELEVATION_GAIN,
+            value = meters,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot,
         )
 
     /**
-     * Create a new [DataPoint] of type [DataType.ELEVATION_LOSS] with the given `meters`.
+     * Create a new [IntervalDataPoint] of type [DataType.ELEVATION_LOSS] with the given [meters].
      *
      * @param meters meters lost between [startDurationFromBoot] and [endDurationFromBoot]
      * @param startDurationFromBoot the point in time this data point begins
      * @param endDurationFromBoot the point in time this data point ends
-     * @param metadata optional OEM specific data, not intended for broad consumption
      */
     @JvmStatic
-    @JvmOverloads
     public fun elevationLoss(
         meters: Double,
         startDurationFromBoot: Duration,
         endDurationFromBoot: Duration,
-        metadata: Bundle? = null
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.ELEVATION_LOSS,
-            Value.ofDouble(meters),
-            startDurationFromBoot,
-            endDurationFromBoot,
-            metadata ?: Bundle()
+    ): IntervalDataPoint<Double> =
+        IntervalDataPoint(
+            dataType = DataType.ELEVATION_LOSS,
+            value = meters,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot,
         )
 
-    /** Creates a new [DataPoint] of type [DataType.ABSOLUTE_ELEVATION] with the given `meters`. */
+    /**
+     * Creates a new [SampleDataPoint] of type [DataType.ABSOLUTE_ELEVATION] with the given
+     * [meters].
+     *
+     * @param meters absolute elevation in meters at [timeDurationFromBoot]
+     * @param timeDurationFromBoot the point in time [stepsPerMinute] is accurate
+     */
     @JvmStatic
-    @JvmOverloads
     public fun absoluteElevation(
         meters: Double,
-        durationFromBoot: Duration,
-        metadata: Bundle? = null
-    ): DataPoint =
-        DataPoint.createSample(
-            DataType.ABSOLUTE_ELEVATION,
-            Value.ofDouble(meters),
-            durationFromBoot,
-            metadata ?: Bundle()
+        timeDurationFromBoot: Duration,
+    ): SampleDataPoint<Double> =
+        SampleDataPoint(
+            dataType = DataType.ABSOLUTE_ELEVATION,
+            value = meters,
+            timeDurationFromBoot = timeDurationFromBoot,
         )
 
-    /** Creates a new [DataPoint] of type [DataType.FLOORS] with the given `floors`. */
+    /**
+     * Creates a new [StatisticalDataPoint] of type [DataType.ABSOLUTE_ELEVATION_STATS] with the
+     * given elevations (in meters).
+     *
+     * @param minAbsoluteElevationMeters lowest observed elevation in this interval
+     * @param maxAbsoluteElevationMeters highest observed elevation in this interval
+     * @param averageAbsoluteElevationMeters average observed elevation in this interval
+     * @param startTime the point in time this data point begins
+     * @param endTime the point in time this data point ends
+     */
     @JvmStatic
-    @JvmOverloads
+    public fun absoluteElevationStats(
+        minAbsoluteElevationMeters: Double,
+        maxAbsoluteElevationMeters: Double,
+        averageAbsoluteElevationMeters: Double,
+        startTime: Instant,
+        endTime: Instant
+    ): StatisticalDataPoint<Double> =
+        StatisticalDataPoint(
+            dataType = DataType.ABSOLUTE_ELEVATION_STATS,
+            min = minAbsoluteElevationMeters,
+            max = maxAbsoluteElevationMeters,
+            average = averageAbsoluteElevationMeters,
+            start = startTime,
+            end = endTime,
+        )
+
+    /**
+     * Creates a new [IntervalDataPoint] of type [DataType.FLOORS] with the given [floors].
+     *
+     * @param floors floors ascended between [startDurationFromBoot] and [endDurationFromBoot]
+     * @param startDurationFromBoot the point in time this data point begins
+     * @param endDurationFromBoot the point in time this data point ends
+     */
+    @JvmStatic
     public fun floors(
         floors: Double,
         startDurationFromBoot: Duration,
         endDurationFromBoot: Duration,
-        metadata: Bundle? = null
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.FLOORS,
-            Value.ofDouble(floors),
-            startDurationFromBoot,
-            endDurationFromBoot,
-            metadata ?: Bundle()
+    ): IntervalDataPoint<Double> =
+        IntervalDataPoint(
+            dataType = DataType.FLOORS,
+            value = floors,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot,
         )
 
-    /** Creates a new [DataPoint] of type [DataType.TOTAL_CALORIES] with the given `kcalories`. */
+    /**
+     * Creates a new [IntervalDataPoint] of type [DataType.CALORIES] with the given [kilocalories].
+     *
+     * @param kilocalories total calories burned (BMR + Active) between [startDurationFromBoot] and
+     * [endDurationFromBoot]
+     * @param startDurationFromBoot the point in time this data point begins
+     * @param endDurationFromBoot the point in time this data point ends
+     */
     @JvmStatic
-    @JvmOverloads
     public fun calories(
-        kcalories: Double,
+        kilocalories: Double,
         startDurationFromBoot: Duration,
         endDurationFromBoot: Duration,
-        metadata: Bundle? = null
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.TOTAL_CALORIES,
-            Value.ofDouble(kcalories),
-            startDurationFromBoot,
-            endDurationFromBoot,
-            metadata ?: Bundle()
+    ): IntervalDataPoint<Double> =
+        IntervalDataPoint(
+            dataType = DataType.CALORIES,
+            value = kilocalories,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot,
         )
 
-    /** Creates a new [DataPoint] of type [DataType.SWIMMING_STROKES] with the given `strokes`. */
+    /**
+     * Creates a new [CumulativeDataPoint] of type [DataType.CALORIES_TOTAL] with the given
+     * [kilocalories] that represents an accumulation over a longer period of time.
+     *
+     * @param kilocalories total calories burned (BMR + Active) between [startTime] and
+     * [endTime]
+     * @param startTime the point in time this data point begins
+     * @param endTime the point in time this data point ends
+     */
+    @JvmStatic
+    public fun caloriesTotal(
+        kilocalories: Double,
+        startTime: Instant,
+        endTime: Instant
+    ): CumulativeDataPoint<Double> =
+        CumulativeDataPoint(
+            dataType = DataType.CALORIES_TOTAL,
+            total = kilocalories,
+            start = startTime,
+            end = endTime
+        )
+
+    /**
+     * Creates a new [IntervalDataPoint] of type [DataType.SWIMMING_STROKES] with the given
+     * [strokes].
+     *
+     * @param strokes total swimming strokes between [startDurationFromBoot] and
+     * [endDurationFromBoot]
+     * @param startDurationFromBoot the point in time this data point begins
+     * @param endDurationFromBoot the point in time this data point ends
+     */
     @JvmStatic
     public fun swimmingStrokes(
         strokes: Long,
         startDurationFromBoot: Duration,
         endDurationFromBoot: Duration
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.SWIMMING_STROKES,
-            Value.ofLong(strokes),
-            startDurationFromBoot,
-            endDurationFromBoot
+    ): IntervalDataPoint<Long> =
+        IntervalDataPoint(
+            dataType = DataType.SWIMMING_STROKES,
+            value = strokes,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot
         )
 
     /**
-     * Creates a new [DataPoint] of type [DataType.GOLF_SHOT_COUNT] with the given [shots].
+     * Creates a new [IntervalDataPoint] of type [DataType.GOLF_SHOT_COUNT] with the given [shots].
      *
      * @param shots golf shots made between [startDurationFromBoot] and [endDurationFromBoot]
      * @param startDurationFromBoot the point in time this data point begins
      * @param endDurationFromBoot the point in time this data point ends
-     * @param metadata optional OEM specific data, not intended for broad consumption
      */
     @JvmStatic
-    @JvmOverloads
     public fun golfShotCount(
         shots: Long,
         startDurationFromBoot: Duration,
         endDurationFromBoot: Duration,
-        metadata: Bundle? = null
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.GOLF_SHOT_COUNT,
-            Value.ofLong(shots),
-            startDurationFromBoot,
-            endDurationFromBoot,
-            metadata ?: Bundle()
+    ): IntervalDataPoint<Long> =
+        IntervalDataPoint(
+            dataType = DataType.GOLF_SHOT_COUNT,
+            value = shots,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot,
         )
 
     /**
-     * Creates a new [DataPoint] of type [DataType.LOCATION] with the given `latitude`, `longitude`,
-     * `altitude`, `bearing`, and `accuracy`.
+     * Creates a new [SampleDataPoint] of type [DataType.LOCATION] with the given [latitude],
+     * [longitude], and optionally [altitude], [bearing], and [accuracy].
+     *
+     * @param latitude latitude at [timeDurationFromBoot]
+     * @param longitude longitude at [timeDurationFromBoot]
+     * @param timeDurationFromBoot the point in time this data was recorded
+     * @param altitude optional altitude or `null` at [timeDurationFromBoot]
+     * @param bearing optional bearing or `null` at [timeDurationFromBoot]
+     * @param accuracy optional [LocationAccuracy] describing this data or `null`
      */
     @JvmStatic
     @JvmOverloads
     public fun location(
         latitude: Double,
         longitude: Double,
-        altitude: Double = Double.MAX_VALUE,
-        bearing: Double = Double.MAX_VALUE,
-        durationFromBoot: Duration,
+        timeDurationFromBoot: Duration,
+        altitude: Double? = null,
+        bearing: Double? = null,
         accuracy: LocationAccuracy? = null
-    ): DataPoint =
-        DataPoint.createSample(
-            DataType.LOCATION,
-            Value.ofDoubleArray(latitude, longitude, altitude, bearing),
-            durationFromBoot,
+    ): SampleDataPoint<LocationData> =
+        SampleDataPoint(
+            dataType = DataType.LOCATION,
+            value = LocationData(latitude, longitude, altitude, bearing),
+            timeDurationFromBoot = timeDurationFromBoot,
             accuracy = accuracy
         )
 
-    /** Creates a new [DataPoint] of type [DataType.SPEED] with the given `metersPerSecond`. */
+    /**
+     * Creates a new [SampleDataPoint] of type [DataType.SPEED] with the given [metersPerSecond].
+     *
+     * @param metersPerSecond speed in meters per second at [timeDurationFromBoot]
+     * @param timeDurationFromBoot the point in time [metersPerSecond] was recorded
+     */
     @JvmStatic
-    @JvmOverloads
     public fun speed(
         metersPerSecond: Double,
-        durationFromBoot: Duration,
-        metadata: Bundle? = null
-    ): DataPoint =
-        DataPoint.createSample(
-            DataType.SPEED,
-            Value.ofDouble(metersPerSecond),
-            durationFromBoot,
-            metadata ?: Bundle()
-        )
-
-    /** Creates a new [DataPoint] of type [DataType.PACE] with the given `millisPerKm`. */
-    @JvmStatic
-    public fun pace(millisPerKm: Duration, durationFromBoot: Duration): DataPoint =
-        DataPoint.createSample(
-            DataType.PACE,
-            Value.ofDouble((millisPerKm.toMillis()).toDouble()),
-            durationFromBoot
+        timeDurationFromBoot: Duration,
+    ): SampleDataPoint<Double> =
+        SampleDataPoint(
+            dataType = DataType.SPEED,
+            value = metersPerSecond,
+            timeDurationFromBoot = timeDurationFromBoot,
         )
 
     /**
-     * Creates a new [DataPoint] of type [DataType.HEART_RATE_BPM] with the given `bpm` and
-     * `accuracy`.
+     * Creates a new [SampleDataPoint] of type [DataType.PACE] with the given
+     * [durationPerKilometer].
+     *
+     * @param durationPerKilometer pace in terms of time per kilometer at [timeDurationFromBoot]
+     * @param timeDurationFromBoot the point in time [durationPerKilometer] was recorded
+     */
+    @JvmStatic
+    public fun pace(
+        durationPerKilometer: Duration,
+        timeDurationFromBoot: Duration
+    ): SampleDataPoint<Double> =
+        SampleDataPoint(
+            dataType = DataType.PACE,
+            value = (durationPerKilometer.toMillis()).toDouble(),
+            timeDurationFromBoot = timeDurationFromBoot
+        )
+
+    /**
+     * Creates a new [SampleDataPoint] of type [DataType.HEART_RATE_BPM] with the given [bpm] and
+     * [accuracy].
+     *
+     * @param bpm heart rate given in beats per minute
+     * @param timeDurationFromBoot the point in time this data was recorded
+     * @param accuracy optional [HeartRateAccuracy] describing this data or `null`
      */
     @JvmStatic
     @JvmOverloads
     public fun heartRate(
         bpm: Double,
-        durationFromBoot: Duration,
+        timeDurationFromBoot: Duration,
         accuracy: HeartRateAccuracy? = null
-    ): DataPoint =
-        DataPoint.createSample(
-            DataType.HEART_RATE_BPM,
-            Value.ofDouble(bpm),
-            durationFromBoot,
+    ): SampleDataPoint<Double> =
+        SampleDataPoint(
+            dataType = DataType.HEART_RATE_BPM,
+            value = bpm,
+            timeDurationFromBoot = timeDurationFromBoot,
             accuracy = accuracy
         )
 
-    /** Creates a new [DataPoint] of type [DataType.DAILY_STEPS] with the given `steps`. */
+    /**
+     * Creates a new [StatisticalDataPoint] of type [DataType.HEART_RATE_BPM] with the given
+     * min/max/average beats per minute.
+     *
+     * @param minBpm lowest observed heart rate given in beats per minute in this interval
+     * @param maxBpm highest observed heart rate given in beats per minute in this interval
+     * @param averageBpm average observed heart rate given in beats per minute in this interval
+     * @param startTime the point in time this data point begins
+     * @param endTime the point in time this data point ends
+     */
     @JvmStatic
-    public fun dailySteps(
-        steps: Long,
-        startDurationFromBoot: Duration,
-        endDurationFromBoot: Duration
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.DAILY_STEPS,
-            Value.ofLong(steps),
-            startDurationFromBoot,
-            endDurationFromBoot
+    public fun heartRateStats(
+        minBpm: Double,
+        maxBpm: Double,
+        averageBpm: Double,
+        startTime: Instant,
+        endTime: Instant,
+    ): StatisticalDataPoint<Double> =
+        StatisticalDataPoint(
+            dataType = DataType.HEART_RATE_BPM_STATS,
+            min = minBpm,
+            max = maxBpm,
+            average = averageBpm,
+            start = startTime,
+            end = endTime
         )
 
-    /** Creates a new [DataPoint] of type [DataType.DAILY_FLOORS] with the given `floors`. */
+    /**
+     * Creates a new [IntervalDataPoint] of type [DataType.STEPS_DAILY] with the given [dailySteps].
+     *
+     * @param dailySteps number of steps taken today, between [startDurationFromBoot] and
+     * [endDurationFromBoot]
+     * @param startDurationFromBoot the point in time this data point begins
+     * @param endDurationFromBoot the point in time this data point ends
+     */
+    @JvmStatic
+    public fun dailySteps(
+        dailySteps: Long,
+        startDurationFromBoot: Duration,
+        endDurationFromBoot: Duration
+    ): IntervalDataPoint<Long> =
+        IntervalDataPoint(
+            dataType = DataType.STEPS_DAILY,
+            value = dailySteps,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot
+        )
+
+    /**
+     * Creates a new [IntervalDataPoint] of type [DataType.FLOORS_DAILY] with the given [floors].
+     *
+     * @param floors number of floors ascended today, between [startDurationFromBoot] and
+     * [endDurationFromBoot]
+     * @param startDurationFromBoot the point in time this data point begins
+     * @param endDurationFromBoot the point in time this data point ends
+     */
     @JvmStatic
     public fun dailyFloors(
         floors: Double,
         startDurationFromBoot: Duration,
         endDurationFromBoot: Duration
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.DAILY_FLOORS,
-            Value.ofDouble(floors),
-            startDurationFromBoot,
-            endDurationFromBoot
+    ): IntervalDataPoint<Double> =
+        IntervalDataPoint(
+            dataType = DataType.FLOORS_DAILY,
+            value = floors,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot
         )
 
-    /** Creates a new [DataPoint] of type [DataType.DAILY_CALORIES] with the given `calories`. */
+    /**
+     * Creates a new [IntervalDataPoint] of type [DataType.CALORIES_DAILY] with the given
+     * [calories].
+     *
+     * @param calories number of calories burned today including both active and passive / BMR,
+     * between [startDurationFromBoot] and [endDurationFromBoot]
+     * @param startDurationFromBoot the point in time this data point begins
+     * @param endDurationFromBoot the point in time this data point ends
+     */
     @JvmStatic
     public fun dailyCalories(
         calories: Double,
         startDurationFromBoot: Duration,
         endDurationFromBoot: Duration
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.DAILY_CALORIES,
-            Value.ofDouble(calories),
-            startDurationFromBoot,
-            endDurationFromBoot
+    ): IntervalDataPoint<Double> =
+        IntervalDataPoint(
+            dataType = DataType.CALORIES_DAILY,
+            value = calories,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot
         )
 
-    /** Creates a new [DataPoint] of type [DataType.DAILY_DISTANCE] with the given `meters`. */
+    /**
+     * Creates a new [IntervalDataPoint] of type [DataType.DISTANCE_DAILY] with the given [meters].
+     *
+     * @param meters number of meters traveled today through active/passive exercise between
+     * [startDurationFromBoot] and [endDurationFromBoot]
+     * @param startDurationFromBoot the point in time this data point begins
+     * @param endDurationFromBoot the point in time this data point ends
+     */
     @JvmStatic
     public fun dailyDistance(
         meters: Double,
         startDurationFromBoot: Duration,
-        endDurationFromBoot: Duration
-    ): DataPoint =
-        DataPoint.createInterval(
-            DataType.DAILY_DISTANCE,
-            Value.ofDouble(meters),
-            startDurationFromBoot,
-            endDurationFromBoot
+        endDurationFromBoot: Duration,
+    ): IntervalDataPoint<Double> =
+        IntervalDataPoint(
+            dataType = DataType.DISTANCE_DAILY,
+            value = meters,
+            startDurationFromBoot = startDurationFromBoot,
+            endDurationFromBoot = endDurationFromBoot
         )
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt
index ceb1666..88d80f0 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt
@@ -16,46 +16,71 @@
 
 package androidx.health.services.client.data
 
-import android.os.Parcelable
-import androidx.annotation.RestrictTo
+import android.util.Log
+import androidx.health.services.client.data.DataType.Companion.DISTANCE
 import androidx.health.services.client.data.DataType.TimeType
 import androidx.health.services.client.proto.DataProto
 import androidx.health.services.client.proto.DataProto.DataType.TimeType.TIME_TYPE_INTERVAL
 import androidx.health.services.client.proto.DataProto.DataType.TimeType.TIME_TYPE_SAMPLE
 import androidx.health.services.client.proto.DataProto.DataType.TimeType.TIME_TYPE_UNKNOWN
+import com.google.protobuf.ByteString
+import kotlin.reflect.KClass
+
+/**
+ * [DataType] that represents a granular, non-aggregated point in time. This will map to
+ * [IntervalDataPoint]s and [SampleDataPoint]s.
+ */
+class DeltaDataType<T : Any, D : DataPoint<T>>(
+    name: String,
+    timeType: TimeType,
+    valueClass: KClass<T>
+) : DataType<T, D>(name, timeType, valueClass, isAggregate = false)
+
+/**
+ * [DataType] that represents aggregated data. This will map to [CumulativeDataPoint]s and
+ * [StatisticalDataPoint]s.
+ */
+class AggregateDataType<T : Number, D : DataPoint<T>>(
+    name: String,
+    timeType: TimeType,
+    valueClass: KClass<T>,
+) : DataType<T, D>(name, timeType, valueClass, isAggregate = true)
 
 /**
  * A data type is a representation of health data managed by Health Services.
  *
- * A [DataType] specifies the format of the values inside a [DataPoint]. Health Services defines
- * data types for instantaneous observations [TimeType.SAMPLE] (e.g. heart rate) and data types for
- * change between readings [TimeType.INTERVAL] (e.g. distance).
+ * A [DataType] specifies the type of the values inside of a [DataPoint]. Health Services defines
+ * data types for instantaneous observations (Samples / [SampleDataPoint], e.g. heart rate) and data
+ * types for a change between readings (Intervals / [IntervalDataPoint], e.g. distance).
+ *
+ * Health services also allows specifying aggregated versions of many data types, which will allow
+ * the developer to get e.g. a running total of intervals ([CumulativeDataPoint]) or statistics like
+ * min/max/average on samples ([StatisticalDataPoint]).
  *
  * Note: the data type defines only the representation and format of the data, and not how it's
- * being collected, the sensor being used, or the parameters of the collection.
+ * being collected, the sensor being used, or the parameters of the collection. As an example,
+ * [DISTANCE] may come from GPS location if available, or steps if not available.
  */
 @Suppress("ParcelCreator")
-public class DataType(
+abstract class DataType<T : Any, D : DataPoint<T>>(
     /** Returns the name of this [DataType], e.g. `"Steps"`. */
-    public val name: String,
+    val name: String,
+
     /** Returns the [TimeType] of this [DataType]. */
-    public val timeType: TimeType,
-    /** Returns the expected format for a [Value] of this [DataType]. */
-    public val format: Int,
-) : ProtoParcelable<DataProto.DataType>() {
-    /** @hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public constructor(
-        proto: DataProto.DataType
-    ) : this(
-        proto.name,
-        TimeType.fromProto(proto.timeType)
-            ?: throw IllegalStateException("Invalid TimeType: ${proto.timeType}"),
-        proto.format
-    )
+    internal val timeType: TimeType,
+
+    /** Returns the underlying [KClass] of this [DataType]. */
+    val valueClass: KClass<T>,
 
     /**
-     * Whether the `DataType` corresponds to a measurement spanning an interval, or a sample at a
+     * Returns `true` if this will be represented by [StatisticalDataPoint] or
+     * [CumulativeDataPoint], otherwise `false`.
+     */
+    internal val isAggregate: Boolean,
+) {
+
+    /**
+     * Whether the [DataType] corresponds to a measurement spanning an interval, or a sample at a
      * single point in time.
      */
     public class TimeType private constructor(public val id: Int, public val name: String) {
@@ -72,278 +97,644 @@
 
         override fun toString(): String = name
 
-        /** @hide */
-        @RestrictTo(RestrictTo.Scope.LIBRARY)
-        internal fun toProto(): DataProto.DataType.TimeType =
-            when (this) {
-                INTERVAL -> TIME_TYPE_INTERVAL
-                SAMPLE -> TIME_TYPE_SAMPLE
-                else -> TIME_TYPE_UNKNOWN
-            }
+        internal fun toProto(): DataProto.DataType.TimeType = when (this) {
+            INTERVAL -> TIME_TYPE_INTERVAL
+            SAMPLE -> TIME_TYPE_SAMPLE
+            else -> TIME_TYPE_UNKNOWN
+        }
 
-        public companion object {
+        companion object {
+            /** The [TimeType] is unknown or this library is too old to know about it. */
+            @JvmField
+            val UNKNOWN: TimeType = TimeType(0, "UNKNOWN")
 
             /**
              * TimeType that indicates the DataType has a value that represents an interval of time
              * with a beginning and end. For example, number of steps taken over a span of time.
              */
             @JvmField
-            public val INTERVAL: TimeType = TimeType(1, "INTERVAL")
+            val INTERVAL: TimeType = TimeType(1, "INTERVAL")
 
             /**
              * TimeType that indicates the DataType has a value that represents a single point in
              * time. For example, heart rate reading at a specific time.
              */
             @JvmField
-            public val SAMPLE: TimeType = TimeType(2, "SAMPLE")
+            val SAMPLE: TimeType = TimeType(2, "SAMPLE")
 
-            /** @hide */
-            @RestrictTo(RestrictTo.Scope.LIBRARY)
-            internal fun fromProto(proto: DataProto.DataType.TimeType): TimeType? =
-                when (proto) {
-                    TIME_TYPE_INTERVAL -> INTERVAL
-                    TIME_TYPE_SAMPLE -> SAMPLE
-                    TIME_TYPE_UNKNOWN -> null
-                }
+            internal fun fromProto(proto: DataProto.DataType.TimeType): TimeType = when (proto) {
+                TIME_TYPE_INTERVAL -> INTERVAL
+                TIME_TYPE_SAMPLE -> SAMPLE
+                TIME_TYPE_UNKNOWN -> UNKNOWN
+            }
         }
     }
 
-    override fun toString(): String = "DataType(name=$name, timeType=$timeType, format=$format)"
+    override fun toString(): String =
+        "DataType(" +
+            "name=$name," +
+            " timeType=$timeType," +
+            " class=${valueClass.simpleName}," +
+            " isAggregate=$isAggregate" + ")"
 
-    /** @hide */
-    override val proto: DataProto.DataType by lazy {
-        DataProto.DataType.newBuilder()
-            .setName(name)
-            .setTimeType(timeType.toProto())
-            .setFormat(format)
-            .build()
+    internal val proto: DataProto.DataType by lazy {
+        DataProto.DataType.newBuilder().setName(name).setTimeType(timeType.toProto())
+            .setFormat(classToValueFormat()).build()
     }
 
-    public companion object {
-        @JvmField
-        public val CREATOR: Parcelable.Creator<DataType> = newCreator {
-            val proto = DataProto.DataType.parseFrom(it)
-            DataType(proto)
+    internal fun toProtoFromValue(
+        value: T,
+    ): DataProto.Value {
+        val builder = DataProto.Value.newBuilder()
+        when (valueClass) {
+            Long::class -> builder.longVal = value as Long
+            Double::class -> builder.doubleVal = value as Double
+            Boolean::class -> builder.boolVal = value as Boolean
+            ByteArray::class -> builder.byteArrayVal = ByteString.copyFrom(value as ByteArray)
+            DoubleArray::class -> builder.doubleArrayVal = DataProto.Value.DoubleArray.newBuilder()
+                .addAllDoubleArray((value as DoubleArray).toList()).build()
+            LocationData::class -> (value as LocationData).addToValueProtoBuilder(builder)
+            else -> Log.w(TAG, "Unexpected value class ${valueClass.simpleName}")
         }
 
+        return builder.build()
+    }
+
+    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
+    internal fun toValueFromProto(proto: DataProto.Value): T {
+        return when (valueClass) {
+            Long::class -> proto.longVal
+            Double::class -> proto.doubleVal
+            Boolean::class -> proto.boolVal
+            ByteArray::class -> proto.byteArrayVal?.toByteArray()
+            DoubleArray::class -> proto.doubleArrayVal
+            LocationData::class -> LocationData.fromDataProtoValue(proto)
+            else -> throw UnsupportedOperationException("Cannot retrieve value for $valueClass")
+        } as T
+    }
+
+    private fun classToValueFormat(): Int {
+        return when (valueClass) {
+            Double::class -> FORMAT_DOUBLE
+            Long::class -> FORMAT_LONG
+            Boolean::class -> FORMAT_BOOLEAN
+            DoubleArray::class -> FORMAT_DOUBLE_ARRAY
+            ByteArray::class -> FORMAT_BYTE_ARRAY
+            LocationData::class -> FORMAT_DOUBLE_ARRAY
+            else ->
+                throw UnsupportedOperationException("No IPC format available for class $valueClass")
+        }
+    }
+
+    companion object {
+        private const val TAG = "DataType"
+
+        private inline fun <reified T : Number> createIntervalDataType(
+            name: String
+        ): DeltaDataType<T, IntervalDataPoint<T>> =
+            DeltaDataType(name, TimeType.INTERVAL, T::class)
+
+        private inline fun <reified T : Number> createSampleDataType(
+            name: String
+        ): DeltaDataType<T, SampleDataPoint<T>> = DeltaDataType(
+            name, TimeType.SAMPLE, T::class
+        )
+
+        private inline fun <reified T : Number> createStatsDataType(
+            name: String
+        ): AggregateDataType<T, StatisticalDataPoint<T>> =
+            AggregateDataType(name, TimeType.SAMPLE, T::class)
+
+        private inline fun <reified T : Number> createCumulativeDataType(
+            name: String
+        ): AggregateDataType<T, CumulativeDataPoint<T>> =
+            AggregateDataType(name, TimeType.INTERVAL, T::class)
+
         /**
-         * A measure of the gain in elevation expressed in meters in `double` format. Elevation
+         * A measure of the gain in elevation since the last update expressed in meters. Elevation
          * losses are not counted in this metric (so it will only be positive or 0).
          */
         @JvmField
-        public val ELEVATION_GAIN: DataType =
-            DataType("Elevation Gain", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
+        val ELEVATION_GAIN: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Elevation Gain")
 
         /**
-         * A measure of the loss in elevation expressed in meters in `double` format. This is a
-         * positive value representing elevation loss (a value of 10 will indicate 10m of elevation
-         * loss).
+         * A measure of the total gain in elevation since the start of an active exercise expressed
+         * in meters. Elevation losses are not counted in this metric (so it will only be positive
+         * or 0).
          */
         @JvmField
-        public val ELEVATION_LOSS: DataType =
-            DataType("Elevation Loss", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
-
-        /** Absolute elevation between each reading expressed in meters in `double` format. */
-        @JvmField
-        public val ABSOLUTE_ELEVATION: DataType =
-            DataType("Absolute Elevation", TimeType.SAMPLE, Value.FORMAT_DOUBLE)
-
-        /** A distance delta between each reading expressed in meters in `double` format. */
-        @JvmField
-        public val DISTANCE: DataType = DataType("Distance", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
+        val ELEVATION_GAIN_TOTAL: AggregateDataType<Double, CumulativeDataPoint<Double>> =
+            createCumulativeDataType("Elevation Gain")
 
         /**
-         * A distance delta traveled over declining ground between each reading expressed in meters
-         * in `double` format.
+         * A measure of the loss in elevation since the last update expressed in meters. Elevation
+         * gains are not counted in this metric (so it will only be positive or 0).
          */
         @JvmField
-        public val DECLINE_DISTANCE: DataType =
-            DataType("Decline Distance", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
+        val ELEVATION_LOSS: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Elevation Loss")
 
         /**
-         * A duration delta representing the amount of time the user spent traveling over declining
-         * ground during the interval, expressed in seconds in `long` format.
+         * A measure of the total loss in elevation since the start of an active exercise expressed
+         * in meters. Elevation gains are not counted in this metric (so it will only be positive or
+         * 0).
          */
         @JvmField
-        public val DECLINE_DURATION: DataType =
-            DataType("Decline Duration", TimeType.INTERVAL, Value.FORMAT_LONG)
+        val ELEVATION_LOSS_TOTAL: AggregateDataType<Double, CumulativeDataPoint<Double>> =
+            createCumulativeDataType("Elevation Loss")
+
+        /** Absolute elevation at a specific point in time expressed in meters. */
+        @JvmField
+        val ABSOLUTE_ELEVATION: DeltaDataType<Double, SampleDataPoint<Double>> =
+            createSampleDataType("Absolute Elevation")
 
         /**
-         * A distance delta traveled over flat ground between each reading expressed in meters in
-         * `double` format.
+         * Statistical information about the absolute elevation over the course of the active
+         * exercise expressed in meters.
          */
         @JvmField
-        public val FLAT_GROUND_DISTANCE: DataType =
-            DataType("Flat Ground Distance", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
+        val ABSOLUTE_ELEVATION_STATS: AggregateDataType<Double, StatisticalDataPoint<Double>> =
+            createStatsDataType("Absolute Elevation")
+
+        /** A distance delta between each reading expressed in meters. */
+        @JvmField
+        val DISTANCE: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Distance")
+
+        /** Total distance since the start of the active exercise expressed in meters. */
+        @JvmField
+        val DISTANCE_TOTAL: AggregateDataType<Double, CumulativeDataPoint<Double>> =
+            createCumulativeDataType("Distance")
+
+        /** Distance traveled over declining ground between each reading expressed in meters. */
+        @JvmField
+        val DECLINE_DISTANCE: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Decline Distance")
 
         /**
-         * A duration delta representing the amount of time the user spent traveling over flat
-         * ground during the interval, expressed in seconds in `long` format.
+         * The total distance traveled over declining ground between each reading since the start of
+         * the active exercise expressed in meters.
          */
         @JvmField
-        public val FLAT_GROUND_DURATION: DataType =
-            DataType("Flat Ground Duration", TimeType.INTERVAL, Value.FORMAT_LONG)
+        val DECLINE_DISTANCE_TOTAL: AggregateDataType<Double, CumulativeDataPoint<Double>> =
+            createCumulativeDataType("Decline Distance")
 
         /**
-         * A distance delta traveled over inclining ground between each reading expressed in meters
-         * in `double` format.
+         * The amount of time the user spent traveling over declining ground since the last update,
+         * expressed in seconds.
          */
         @JvmField
-        public val INCLINE_DISTANCE: DataType =
-            DataType("Incline Distance", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
+        val DECLINE_DURATION: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Decline Duration")
 
         /**
-         * A duration delta representing the amount of time the user spent traveling over inclining
-         * ground during the interval, expressed in seconds in `long` format.
+         * Total duration the user spent traveling over declining ground since the start of the
+         * active exercise, expressed in seconds.
          */
         @JvmField
-        public val INCLINE_DURATION: DataType =
-            DataType("Incline Duration", TimeType.INTERVAL, Value.FORMAT_LONG)
+        val DECLINE_DURATION_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Decline Duration")
 
-        /** Number of floors climbed between each reading in `double` format */
+        /** The distance traveled over flat since the last update expressed in meters. */
         @JvmField
-        public val FLOORS: DataType = DataType("Floors", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
+        val FLAT_GROUND_DISTANCE: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Flat Ground Distance")
 
         /**
-         * Current heart rate, in beats per minute in `double` format.
+         * The total distance traveled over flat ground since the start of the active exercise
+         * expressed in meters.
+         */
+        @JvmField
+        val FLAT_GROUND_DISTANCE_TOTAL: AggregateDataType<Double, CumulativeDataPoint<Double>> =
+            createCumulativeDataType("Flat Ground Distance")
+
+        /**
+         * The amount of time the user spent traveling over flat ground since the last update,
+         * expressed in seconds.
+         */
+        @JvmField
+        val FLAT_GROUND_DURATION: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Flat Ground Duration")
+
+        /**
+         * The total duration the user spent traveling over flat ground since the start of the
+         * active exercise, expressed in seconds.
+         */
+        @JvmField
+        val FLAT_GROUND_DURATION_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Flat Ground Duration")
+
+        /**
+         * The number of golf shots taken since the last update, where a golf shot consists of
+         * swinging the club and hitting the ball. Expressed in seconds.
+         */
+        @JvmField
+        val GOLF_SHOT_COUNT: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Golf Shot Count")
+
+        /**
+         * The total number of golf shots taken since the start of the current active exercise,
+         * where a golf shot consists swinging the club and hitting the ball. Expressed in seconds.
+         */
+        @JvmField
+        val GOLF_SHOT_COUNT_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Golf Shot Count")
+
+        /**
+         * The distance traveled over inclining ground since the last update expressed in meters.
+         */
+        @JvmField
+        val INCLINE_DISTANCE: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Incline Distance")
+
+        /**
+         * The total distance traveled over inclining since the start of the active exercise
+         * expressed in meters.
+         */
+        @JvmField
+        val INCLINE_DISTANCE_TOTAL: AggregateDataType<Double, CumulativeDataPoint<Double>> =
+            createCumulativeDataType("Incline Distance")
+
+        /**
+         * The amount of time the user spent traveling over inclining ground since the last update,
+         * expressed in seconds.
+         */
+        @JvmField
+        val INCLINE_DURATION: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Incline Duration")
+
+        /**
+         * Total amount of time the user spent traveling over inclining ground since the start of
+         * the active exercise, expressed in seconds.
+         */
+        @JvmField
+        val INCLINE_DURATION_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Incline Duration")
+
+        /**
+         * Number of floors climbed since the last update. Note that partial floors are supported,
+         * so this is represented as a [Double].
+         */
+        @JvmField
+        val FLOORS: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Floors")
+
+        /**
+         * Total number of floors climbed since the start of the active exercise. Note that partial
+         * floors are supported, so this is represented as a [Double].
+         */
+        @JvmField
+        val FLOORS_TOTAL: AggregateDataType<Double, CumulativeDataPoint<Double>> =
+            createCumulativeDataType("Floors")
+
+        /**
+         * Current heart rate, in beats per minute.
          *
          * Accuracy for a [DataPoint] of type [DataType.HEART_RATE_BPM] is represented by
          * [HeartRateAccuracy].
          */
         @JvmField
-        public val HEART_RATE_BPM: DataType =
-            DataType("HeartRate", TimeType.SAMPLE, Value.FORMAT_DOUBLE)
+        val HEART_RATE_BPM: DeltaDataType<Double, SampleDataPoint<Double>> =
+            createSampleDataType("HeartRate")
 
         /**
-         * Current latitude, longitude and optionally, altitude in `double[]` format. Latitude at
-         * index [DataPoints.LOCATION_DATA_POINT_LATITUDE_INDEX], longitude at index
-         * [DataPoints.LOCATION_DATA_POINT_LONGITUDE_INDEX] and if available, altitude at index
-         * [DataPoints.LOCATION_DATA_POINT_ALTITUDE_INDEX]
+         * Statistics on heart rate since the start of the current exercise, expressed in beats per
+         * minute.
+         */
+        @JvmField
+        val HEART_RATE_BPM_STATS: AggregateDataType<Double, StatisticalDataPoint<Double>> =
+            createStatsDataType("HeartRate")
+
+        /**
+         * Latitude, longitude and optionally, altitude and bearing at a specific point in time.
          *
-         * Accuracy for a [DataPoint] of type [DataType.LOCATION] is represented by
-         * [LocationAccuracy].
+         * Accuracy for a [DataPoint] of type [LOCATION] is represented by [LocationAccuracy].
          */
         @JvmField
-        public val LOCATION: DataType =
-            DataType("Location", TimeType.SAMPLE, Value.FORMAT_DOUBLE_ARRAY)
+        val LOCATION: DeltaDataType<LocationData, SampleDataPoint<LocationData>> =
+            DeltaDataType("Location", TimeType.SAMPLE, LocationData::class)
 
-        /** Current speed over time. In meters/second in `double` format. */
+        /** Speed at a specific point in time, expressed as meters/second. */
         @JvmField
-        public val SPEED: DataType = DataType("Speed", TimeType.SAMPLE, Value.FORMAT_DOUBLE)
-
-        /** Percentage of oxygen in the blood in `double` format. Valid range `0f` - `100f`. */
-        @JvmField public val SPO2: DataType = DataType("SpO2", TimeType.SAMPLE, Value.FORMAT_DOUBLE)
-
-        /** Rate of oxygen consumption in `double` format. Valid range `0f` - `100f`. */
-        @JvmField public val VO2: DataType = DataType("VO2", TimeType.SAMPLE, Value.FORMAT_DOUBLE)
+        val SPEED: DeltaDataType<Double, SampleDataPoint<Double>> =
+            createSampleDataType("Speed")
 
         /**
-         * Maximum rate of oxygen consumption measured during incremental exercise in `double`
-         * format. Valid range `0f` - `100f`.
+         * Statistics on speed since the start of the active exercise, expressed in meters/second.
          */
         @JvmField
-        public val VO2_MAX: DataType = DataType("VO2 Max", TimeType.SAMPLE, Value.FORMAT_DOUBLE)
-
-        /** Delta of steps between each reading in `long` format. */
-        @JvmField
-        public val STEPS: DataType = DataType("Steps", TimeType.INTERVAL, Value.FORMAT_LONG)
-
-        /** Delta of walking steps between each reading in `long` format. */
-        @JvmField
-        public val WALKING_STEPS: DataType =
-            DataType("Walking Steps", TimeType.INTERVAL, Value.FORMAT_LONG)
-
-        /** Delta of running steps between each reading in `long` format. */
-        @JvmField
-        public val RUNNING_STEPS: DataType =
-            DataType("Running Steps", TimeType.INTERVAL, Value.FORMAT_LONG)
-
-        /** Current step rate in steps/minute in `long` format. */
-        @JvmField
-        public val STEPS_PER_MINUTE: DataType =
-            DataType("Step per minute", TimeType.SAMPLE, Value.FORMAT_LONG)
-
-        /** Delta of strokes between each reading of swimming strokes in `long` format. */
-        @JvmField
-        public val SWIMMING_STROKES: DataType =
-            DataType("Swimming Strokes", TimeType.INTERVAL, Value.FORMAT_LONG)
-
-        /** Count of golf shots in `long` format. */
-        @JvmField
-        public val GOLF_SHOT_COUNT: DataType =
-            DataType("Golf Shot Count", TimeType.INTERVAL, Value.FORMAT_LONG)
+        val SPEED_STATS: AggregateDataType<Double, StatisticalDataPoint<Double>> =
+            createStatsDataType("Speed")
 
         /**
-         * Delta of total calories (including basal rate and activity) between each reading in
-         * `double` format.
+         * Maximum rate of oxygen consumption measured at a specific point in time. Valid range
+         * `0f` - `100f`.
          */
         @JvmField
-        public val TOTAL_CALORIES: DataType =
-            DataType("Calories", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
-
-        /** Current pace. In millisec/km in `double` format. */
-        @JvmField public val PACE: DataType = DataType("Pace", TimeType.SAMPLE, Value.FORMAT_DOUBLE)
+        val VO2_MAX: DeltaDataType<Double, SampleDataPoint<Double>> =
+            createSampleDataType("VO2 Max")
 
         /**
-         * The duration during which the user was resting during an Exercise in seconds in `long`
-         * format.
+         * Statistics on maximum rate of oxygen consumption measured since the start of an exercise.
+         * Valid range `0f` - `100f`.
          */
         @JvmField
-        public val RESTING_EXERCISE_DURATION: DataType =
-            DataType("Resting Exercise Duration", TimeType.INTERVAL, Value.FORMAT_LONG)
+        val VO2_MAX_STATS: AggregateDataType<Double, StatisticalDataPoint<Double>> =
+            createStatsDataType("VO2 Max")
 
-        /** The duration of the time the Exercise was ACTIVE in seconds in `long` format. */
+        /** Number of steps taken since the last update. */
         @JvmField
-        public val ACTIVE_EXERCISE_DURATION: DataType =
-            DataType("Active Exercise Duration", TimeType.INTERVAL, Value.FORMAT_LONG)
+        val STEPS: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Steps")
 
-        /** Count of swimming laps ins `long` format. */
+        /** Total steps taken since the start of the active exercise. */
         @JvmField
-        public val SWIMMING_LAP_COUNT: DataType =
-            DataType("Swim Lap Count", TimeType.INTERVAL, Value.FORMAT_LONG)
+        val STEPS_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Steps")
 
-        /** The current rep count of the exercise in `long` format. */
+        /** Number of steps taken while walking since the last update. */
         @JvmField
-        public val REP_COUNT: DataType = DataType("Rep Count", TimeType.INTERVAL, Value.FORMAT_LONG)
+        val WALKING_STEPS: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Walking Steps")
 
         /**
-         * The total step count over a day in `long` format, where the previous day ends and a new
-         * day begins at 12:00 AM local time. Each DataPoint of this type will cover the interval
-         * from the start of day to now. In the event of time-zone shifts, the interval might be
-         * greater than 24hrs.
+         * Total number of steps taken while walking since the start of the current active exercise.
          */
         @JvmField
-        public val DAILY_STEPS: DataType =
-            DataType("Daily Steps", TimeType.INTERVAL, Value.FORMAT_LONG)
+        val WALKING_STEPS_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Walking Steps")
+
+        /** Number of steps taken while running since the last update. */
+        @JvmField
+        val RUNNING_STEPS: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Running Steps")
+
+        /** Number of steps taken while running since the start of the current active exercise. */
+        @JvmField
+        val RUNNING_STEPS_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Running Steps")
+
+        /** Step rate in steps/minute at a given point in time. */
+        @JvmField
+        val STEPS_PER_MINUTE: DeltaDataType<Long, SampleDataPoint<Long>> =
+            createSampleDataType("Step per minute")
 
         /**
-         * The total number floors climbed over a day in `double` format, where the previous day
-         * ends and a new day begins at 12:00 AM local time. Each DataPoint of this type will cover
-         * the interval from the start of day to now. In the event of time-zone shifts, the interval
-         * might be greater than 24hrs.
+         * Statistics on step rate in steps/minute since the beginning of the current active
+         * exercise.
          */
         @JvmField
-        public val DAILY_FLOORS: DataType =
-            DataType("Daily Floors", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
+        val STEPS_PER_MINUTE_STATS: AggregateDataType<Long, StatisticalDataPoint<Long>> =
+            createStatsDataType("Step per minute")
+
+        /** Number of swimming strokes taken since the last update. */
+        @JvmField
+        val SWIMMING_STROKES: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Swimming Strokes")
 
         /**
-         * The total number calories over a day in `double` format, where the previous day ends and
-         * a new day begins at 12:00 AM local time. Each DataPoint of this type will cover the
-         * interval from the start of day to now. In the event of time-zone shifts, the interval
-         * might be greater than 24hrs.
+         * Total number of swimming strokes taken since the start of the current active exercise.
          */
         @JvmField
-        public val DAILY_CALORIES: DataType =
-            DataType("Daily Calories", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
+        val SWIMMING_STROKES_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Swimming Strokes")
+
+        /** Number of calories burned (including basal rate and activity) since the last update. */
+        @JvmField
+        val CALORIES: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Calories")
 
         /**
-         * The total distance over a day in `double` format, where the previous day ends and a new
-         * day begins at 12:00 AM local time. Each DataPoint of this type will cover the interval
-         * from the start of day to now. In the event of time-zone shifts, the interval might be
-         * greater than 24hrs.
+         * Total number of calories burned (including basal rate and activity) since the start of
+         * the current active exercise.
          */
         @JvmField
-        public val DAILY_DISTANCE: DataType =
-            DataType("Daily Distance", TimeType.INTERVAL, Value.FORMAT_DOUBLE)
+        val CALORIES_TOTAL: AggregateDataType<Double, CumulativeDataPoint<Double>> =
+            createCumulativeDataType("Calories")
+
+        /**
+         * Pace at a specific point in time. Will be 0 if the user stops moving, otherwise the value
+         * will be in milliseconds/kilometer.
+         */
+        @JvmField
+        val PACE: DeltaDataType<Double, SampleDataPoint<Double>> =
+            createSampleDataType("Pace")
+
+        /**
+         * Statistics on pace since the start of the current exercise. A value of 0 indicates the
+         * user stopped moving, otherwise the value will be in milliseconds/kilometer.
+         */
+        @JvmField
+        val PACE_STATS: AggregateDataType<Double, StatisticalDataPoint<Double>> =
+            createStatsDataType("Pace")
+
+        /**
+         * The number of seconds the user has been resting during an exercise since the last update.
+         */
+        @JvmField
+        val RESTING_EXERCISE_DURATION: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Resting Exercise Duration")
+
+        /**
+         * The total number of seconds the user has been resting during the active exercise.
+         */
+        @JvmField
+        val RESTING_EXERCISE_DURATION_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Resting Exercise Duration")
+
+        /**
+         * The total time the Exercise was [ExerciseState.ACTIVE] in seconds.
+         *
+         * **_Note_: this [DataType] is only intended to be used in conjunction with exercise
+         * goals. [DataPoint]s will not be delivered for this [DataType]. If you want to query the
+         * active duration, you should use [ExerciseUpdate.activeDuration] which is available in
+         * every [ExerciseUpdate].**
+         */
+        @JvmField
+        val ACTIVE_EXERCISE_DURATION_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Active Exercise Duration")
+
+        /** Count of swimming laps since the start of the current active exercise. */
+        @JvmField
+        val SWIMMING_LAP_COUNT: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Swim Lap Count")
+
+        /** The number of repetitions of an exercise performed since the last update. */
+        @JvmField
+        val REP_COUNT: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Rep Count")
+
+        /**
+         * The number of repetitions of an exercise performed since the start of the current active
+         * exercise.
+         */
+        @JvmField
+        val REP_COUNT_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+            createCumulativeDataType("Rep Count")
+
+        /**
+         * The total step count over a day, where the previous day ends and a new day begins at
+         * 12:00 AM local time. Each [DataPoint] of this type will cover the interval from the start
+         * of day to now. In the event of time-zone shifts, the interval may be greater than 24hrs.
+         */
+        @JvmField
+        val STEPS_DAILY: DeltaDataType<Long, IntervalDataPoint<Long>> =
+            createIntervalDataType("Daily Steps")
+
+        /**
+         * The total number floors climbed over a day, where the previous day ends and a new day
+         * begins at 12:00 AM local time. Each DataPoint of this type will cover the interval from
+         * the start of day to now. In the event of time-zone shifts, the interval may be greater
+         * than 24hrs.
+         */
+        @JvmField
+        val FLOORS_DAILY: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Daily Floors")
+
+        /**
+         * The total number of calories over a day (including both BMR and active calories), where
+         * the previous day ends and a new day begins at 12:00 AM local time. Each [DataPoint] of
+         * this type will cover the interval from the start of day to now. In the event of time-zone
+         * shifts, the interval might be greater than 24hrs.
+         */
+        @JvmField
+        val CALORIES_DAILY: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Daily Calories")
+
+        /**
+         * The total distance over a day, where the previous day ends and a new day begins at
+         * 12:00 AM local time. Each DataPoint of this type will cover the interval from the start
+         * of day to now. In the event of time-zone shifts, the interval may be greater than 24hrs.
+         */
+        @JvmField
+        val DISTANCE_DAILY: DeltaDataType<Double, IntervalDataPoint<Double>> =
+            createIntervalDataType("Daily Distance")
+
+        internal val deltaDataTypes: Set<DeltaDataType<*, *>> = setOf(
+            ABSOLUTE_ELEVATION,
+            CALORIES,
+            CALORIES_DAILY,
+            DISTANCE_DAILY,
+            FLOORS_DAILY,
+            STEPS_DAILY,
+            DECLINE_DISTANCE,
+            DECLINE_DURATION,
+            DISTANCE,
+            ELEVATION_GAIN,
+            ELEVATION_LOSS,
+            FLAT_GROUND_DISTANCE,
+            FLAT_GROUND_DURATION,
+            FLOORS,
+            GOLF_SHOT_COUNT,
+            HEART_RATE_BPM,
+            INCLINE_DISTANCE,
+            INCLINE_DURATION,
+            LOCATION,
+            PACE,
+            REP_COUNT,
+            RESTING_EXERCISE_DURATION,
+            RUNNING_STEPS,
+            SPEED,
+            STEPS,
+            STEPS_PER_MINUTE,
+            SWIMMING_LAP_COUNT,
+            SWIMMING_STROKES,
+            VO2_MAX,
+            WALKING_STEPS,
+        )
+
+        internal val aggregateDataTypes: Set<AggregateDataType<*, *>> = setOf(
+            ABSOLUTE_ELEVATION_STATS,
+            ACTIVE_EXERCISE_DURATION_TOTAL,
+            CALORIES_TOTAL,
+            DECLINE_DISTANCE_TOTAL,
+            DECLINE_DURATION_TOTAL,
+            DISTANCE_TOTAL,
+            ELEVATION_GAIN_TOTAL,
+            ELEVATION_LOSS_TOTAL,
+            FLAT_GROUND_DISTANCE_TOTAL,
+            FLAT_GROUND_DURATION_TOTAL,
+            FLOORS_TOTAL,
+            GOLF_SHOT_COUNT_TOTAL,
+            HEART_RATE_BPM_STATS,
+            INCLINE_DISTANCE_TOTAL,
+            INCLINE_DURATION_TOTAL,
+            PACE_STATS,
+            REP_COUNT_TOTAL,
+            RESTING_EXERCISE_DURATION_TOTAL,
+            RUNNING_STEPS_TOTAL,
+            SPEED_STATS,
+            STEPS_PER_MINUTE_STATS,
+            STEPS_TOTAL,
+            SWIMMING_STROKES_TOTAL,
+            VO2_MAX_STATS,
+            WALKING_STEPS_TOTAL,
+        )
+
+        private val namesOfDeltasWithNoAggregate =
+            deltaDataTypes.map { it.name } subtract aggregateDataTypes.map { it.name }.toSet()
+
+        private val namesOfAggregatesWithNoDelta =
+            aggregateDataTypes.map { it.name } subtract deltaDataTypes.map { it.name }.toSet()
+
+        /** The format used for a [DataProto.Value] represented as a [Double]. */
+        internal const val FORMAT_DOUBLE: Int = 1
+
+        /** The format used for a [DataProto.Value] represented as an [Long]. */
+        internal const val FORMAT_LONG: Int = 2
+
+        /** The format used for a [DataProto.Value] represented as an [Boolean]. */
+        internal const val FORMAT_BOOLEAN: Int = 4
+
+        /** The format used for a [DataProto.Value] represented as a [DoubleArray]. */
+        internal const val FORMAT_DOUBLE_ARRAY: Int = 3
+
+        /** The format used for a [DataProto.Value] represented as a [ByteArray]. */
+        internal const val FORMAT_BYTE_ARRAY: Int = 5
+
+        @Suppress("UNCHECKED_CAST")
+        internal fun aggregateFromProto(
+            proto: DataProto.DataType
+        ): AggregateDataType<out Number, out DataPoint<out Number>> =
+            aggregateDataTypes.firstOrNull { it.name == proto.name } ?: AggregateDataType(
+                proto.name,
+                TimeType.fromProto(proto.timeType),
+                protoDataTypeToClass(proto) as KClass<Number>
+            )
+
+        internal fun deltaFromProto(
+            proto: DataProto.DataType
+        ): DeltaDataType<out Any, out DataPoint<out Any>> =
+            deltaDataTypes.firstOrNull { it.name == proto.name } ?: DeltaDataType(
+                proto.name, TimeType.fromProto(proto.timeType), protoDataTypeToClass(proto)
+            )
+
+        internal fun deltaAndAggregateFromProto(
+            proto: DataProto.DataType
+        ): List<DataType<out Any, out DataPoint<out Any>>> {
+            val list = mutableListOf<DataType<out Any, out DataPoint<out Any>>>()
+
+            if (!namesOfAggregatesWithNoDelta.contains(proto.name)) {
+                list += deltaFromProto(proto)
+            }
+            if (!namesOfDeltasWithNoAggregate.contains(proto.name)) {
+                list += aggregateFromProto(proto)
+            }
+
+            return list
+        }
+
+        private fun protoDataTypeToClass(proto: DataProto.DataType) = when (proto.format) {
+            FORMAT_DOUBLE -> Double::class
+            FORMAT_LONG -> Long::class
+            FORMAT_BOOLEAN -> Boolean::class
+            FORMAT_DOUBLE_ARRAY -> {
+                if (proto.name == LOCATION.name) LOCATION.valueClass
+                else DoubleArray::class
+            }
+            FORMAT_BYTE_ARRAY -> ByteArray::class
+            else -> Nothing::class
+        }
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataTypeCondition.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataTypeCondition.kt
index 76f5076..1705a83 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataTypeCondition.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataTypeCondition.kt
@@ -16,36 +16,25 @@
 
 package androidx.health.services.client.data
 
-import android.os.Parcelable
 import androidx.health.services.client.proto.DataProto
 
 /** A condition which is considered met when a data type value passes a defined threshold. */
 @Suppress("ParcelCreator")
-public class DataTypeCondition(
-    public val dataType: DataType,
-    public val threshold: Value,
+public class DataTypeCondition<T : Number, D : DataType<T, out DataPoint<T>>>(
+    /** [DataType] which this condition applies to. */
+    public val dataType: D,
+
+    /** The threshold at which point this condition should be met. */
+    public val threshold: T,
+
+    /** The comparison type to use when comparing the threshold against the current value. */
     public val comparisonType: ComparisonType,
-) : ProtoParcelable<DataProto.DataTypeCondition>() {
+) {
 
-    internal constructor(
-        proto: DataProto.DataTypeCondition
-    ) : this(
-        DataType(proto.dataType),
-        Value(proto.threshold),
-        ComparisonType.fromProto(proto.comparisonType)
-    )
-
-    init {
-        require(dataType.format == threshold.format) {
-            "provided data type and threshold must have the same formats."
-        }
-    }
-
-    /** @hide */
-    override val proto: DataProto.DataTypeCondition by lazy {
+    internal val proto: DataProto.DataTypeCondition by lazy {
         DataProto.DataTypeCondition.newBuilder()
             .setDataType(dataType.proto)
-            .setThreshold(threshold.proto)
+            .setThreshold(dataType.toProtoFromValue(threshold))
             .setComparisonType(comparisonType.toProto())
             .build()
     }
@@ -54,32 +43,48 @@
         "DataTypeCondition(" +
             "dataType=$dataType, threshold=$threshold, comparisonType=$comparisonType)"
 
-    /** Checks whether or not the condition is satisfied by a given [DataPoint]. */
-    public fun isSatisfied(dataPoint: DataPoint): Boolean {
-        require(dataType == dataPoint.dataType) {
-            "attempted to evaluate data type condition with incorrect data type. Expected " +
-                "${dataType.name} but was ${dataPoint.dataType.name}"
-        }
-        return isThresholdSatisfied(dataPoint.value)
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is DataTypeCondition<*, *>) return false
+        if (dataType != other.dataType) return false
+        if (threshold != other.threshold) return false
+        if (comparisonType != other.comparisonType) return false
+
+        return true
     }
 
-    /** Checks whether or not the value of the condition is satisfied by a given [Value]. */
-    public fun isThresholdSatisfied(value: Value): Boolean {
-        val comparison = Value.compare(value, threshold)
-        return when (comparisonType) {
-            ComparisonType.LESS_THAN -> comparison < 0
-            ComparisonType.GREATER_THAN -> comparison > 0
-            ComparisonType.LESS_THAN_OR_EQUAL -> comparison <= 0
-            ComparisonType.GREATER_THAN_OR_EQUAL -> comparison >= 0
-            else -> false
-        }
+    override fun hashCode(): Int {
+        var result = dataType.hashCode()
+        result = 31 * result + threshold.hashCode()
+        result = 31 * result + comparisonType.hashCode()
+        return result
     }
 
-    public companion object {
-        @JvmField
-        public val CREATOR: Parcelable.Creator<DataTypeCondition> = newCreator {
-            val proto = DataProto.DataTypeCondition.parseFrom(it)
-            DataTypeCondition(proto)
+    internal companion object {
+        @Suppress("UNCHECKED_CAST")
+        internal fun deltaFromProto(
+            proto: DataProto.DataTypeCondition
+        ): DataTypeCondition<out Number, out DeltaDataType<out Number, *>> {
+            val dataType =
+                DataType.deltaFromProto(proto.dataType) as DeltaDataType<Number, *>
+            return DataTypeCondition(
+                dataType,
+                dataType.toValueFromProto(proto.threshold),
+                ComparisonType.fromProto(proto.comparisonType)
+            )
+        }
+
+        @Suppress("UNCHECKED_CAST")
+        internal fun aggregateFromProto(
+            proto: DataProto.DataTypeCondition
+        ): DataTypeCondition<out Number, out AggregateDataType<out Number, *>> {
+            val dataType =
+                DataType.aggregateFromProto(proto.dataType) as AggregateDataType<Number, *>
+            return DataTypeCondition(
+                dataType,
+                dataType.toValueFromProto(proto.threshold),
+                ComparisonType.fromProto(proto.comparisonType)
+            )
         }
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataTypes.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataTypes.kt
deleted file mode 100644
index d16cfd9..0000000
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataTypes.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 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.health.services.client.data
-
-/** Helper class to facilitate working with [DataType]s. */
-public object DataTypes {
-
-    /** Check if a [DataType] will be aggregated as a statistical value. */
-    @JvmStatic
-    public fun isStatisticalDataType(dataType: DataType): Boolean =
-        dataType.timeType == DataType.TimeType.SAMPLE &&
-            dataType.format != Value.FORMAT_DOUBLE_ARRAY
-
-    /**
-     * Check if a [DataType] will be aggregated as a cumulative value.
-     *
-     * Note: [DataType.SWIMMING_LAP_COUNT] already represents the total lap count, so it is not
-     * considered a cumulative data type.
-     */
-    @JvmStatic
-    public fun isCumulativeDataType(dataType: DataType): Boolean =
-        dataType.timeType == DataType.TimeType.INTERVAL &&
-            dataType.format != Value.FORMAT_DOUBLE_ARRAY &&
-            dataType != DataType.SWIMMING_LAP_COUNT
-}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseConfig.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseConfig.kt
index 637cf68..761af40 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseConfig.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseConfig.kt
@@ -17,8 +17,6 @@
 package androidx.health.services.client.data
 
 import android.os.Bundle
-import android.os.Parcelable
-import androidx.annotation.RestrictTo
 import androidx.health.services.client.ExerciseClient
 import androidx.health.services.client.proto.DataProto
 
@@ -29,35 +27,34 @@
  *
  * @property exerciseType [ExerciseType] user is performing for this exercise
  * @property dataTypes [DataType] which will be tracked for this exercise
- * @property aggregateDataTypes [DataType]s which should be tracked as aggregates for this exercise
- * @property isAutoPauseAndResumeEnabled whether auto-pause/ resume is enabled for this exercise
- * @property isGpsEnabled whether GPS is enabled for this exercise
- * @property exerciseGoals [ExerciseGoal]s for this exercise
+ * @property isAutoPauseAndResumeEnabled whether auto-pause/resume is enabled for this exercise
+ * @property isGpsEnabled whether GPS is enabled for this exercise. Must be set to `true` when
+ * [DataType.LOCATION] is present in [dataTypes].
+ * @property exerciseGoals [ExerciseGoal]s for this exercise. [DataType]s in [ExerciseGoal]s must
+ * also be tracked (i.e. contained in [dataTypes]) in some form. For example, an [ExerciseGoal] for
+ * [DataType.STEPS_TOTAL] requires that [dataTypes] contains either or both of
+ * [DataType.STEPS_TOTAL] / [DataType.STEPS].
  * @property exerciseParams [Bundle] additional OEM specific params for this exercise
  */
 @Suppress("ParcelCreator")
-public class ExerciseConfig
-public constructor(
-    public val exerciseType: ExerciseType,
-    public val dataTypes: Set<DataType>,
-    public val aggregateDataTypes: Set<DataType>,
-    public val isAutoPauseAndResumeEnabled: Boolean,
-    public val isGpsEnabled: Boolean,
-    public val exerciseGoals: List<ExerciseGoal>,
-    public val exerciseParams: Bundle,
-) : ProtoParcelable<DataProto.ExerciseConfig>() {
+class ExerciseConfig(
+    val exerciseType: ExerciseType,
+    val dataTypes: Set<DataType<*, *>>,
+    val isAutoPauseAndResumeEnabled: Boolean,
+    val isGpsEnabled: Boolean,
+    val exerciseGoals: List<ExerciseGoal<*>> = listOf(),
+    val exerciseParams: Bundle = Bundle(),
+) {
 
-    /** @hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public constructor(
+    internal constructor(
         proto: DataProto.ExerciseConfig
     ) : this(
         ExerciseType.fromProto(proto.exerciseType),
-        proto.dataTypesList.map { DataType(it) }.toSet(),
-        proto.aggregateDataTypesList.map { DataType(it) }.toSet(),
+        proto.dataTypesList.map { DataType.deltaFromProto(it) }.toMutableSet() +
+            proto.aggregateDataTypesList.map { DataType.aggregateFromProto(it) },
         proto.isAutoPauseAndResumeEnabled,
         proto.isGpsUsageEnabled,
-        proto.exerciseGoalsList.map { ExerciseGoal(it) },
+        proto.exerciseGoalsList.map { ExerciseGoal.fromProto(it) },
         BundlesUtil.fromProto(proto.exerciseParams)
     )
 
@@ -69,62 +66,43 @@
     }
 
     /** Builder for [ExerciseConfig] instances. */
-    public class Builder {
-        private var exerciseType: ExerciseType? = null
-        private var dataTypes: Set<DataType> = emptySet()
-        private var aggregateDataTypes: Set<DataType> = emptySet()
-        private var isAutoPauseAndResumeEnabled: Boolean = false
-        private var isGpsEnabled: Boolean = false
-        private var exerciseGoals: List<ExerciseGoal> = emptyList()
-        private var exerciseParams: Bundle = Bundle.EMPTY
-
+    class Builder(
         /**
-         * Sets the active [ExerciseType] the user is performing for this exercise.
+         * The active [ExerciseType] the user is performing for this exercise.
          *
          * Provide this parameter when tracking a workout to provide more accurate data. This
          * information can be used to tune sensors, e.g. the calories estimate can take the MET
          * value into account.
-         *
-         * @param exerciseType the [ExerciseType] representing this exercise
          */
-        public fun setExerciseType(exerciseType: ExerciseType): Builder {
-            require(exerciseType != ExerciseType.UNKNOWN) { "Must specify a valid exercise type." }
-            this.exerciseType = exerciseType
-            return this
-        }
+        private val exerciseType: ExerciseType
+    ) {
+        private var dataTypes: Set<DataType<*, *>> = emptySet()
+        private var isAutoPauseAndResumeEnabled: Boolean = false
+        private var isGpsEnabled: Boolean = false
+        private var exerciseGoals: List<ExerciseGoal<*>> = emptyList()
+        private var exerciseParams: Bundle = Bundle.EMPTY
 
         /**
          * Sets the requested [DataType]s that should be tracked during this exercise. If not
-         * explicitly called, a default set of [DataType] will be chosen based on the
+         * explicitly called, a default set of [DataType]s will be chosen based on the
          * [ExerciseType].
          *
-         * @param dataTypes set of [DataType]s to track during this exercise
+         * @param dataTypes set of [DataType]s ([AggregateDataType] or [DeltaDataType]) to track
+         * during this exercise
          */
-        public fun setDataTypes(dataTypes: Set<DataType>): Builder {
+        fun setDataTypes(dataTypes: Set<DataType<*, *>>): Builder {
             this.dataTypes = dataTypes.toSet()
             return this
         }
 
         /**
-         * Sets the requested [DataType]s that should be tracked as aggregates (i.e. total steps or
-         * average heart rate) during this exercise. If not explicitly called, a default set of
-         * [DataType] will be chosen based on the [ExerciseType].
-         *
-         * @param dataTypes set of aggregate [DataType]s to track during this exercise
-         */
-        public fun setAggregateDataTypes(dataTypes: Set<DataType>): Builder {
-            this.aggregateDataTypes = dataTypes.toSet()
-            return this
-        }
-
-        /**
          * Sets whether auto pause and auto resume should be enabled for this exercise. If not set,
          * auto-pause is disabled by default.
          *
          * @param isAutoPauseAndResumeEnabled if true, exercise will automatically pause and resume
          */
         @Suppress("MissingGetterMatchingBuilder")
-        public fun setIsAutoPauseAndResumeEnabled(isAutoPauseAndResumeEnabled: Boolean): Builder {
+        fun setIsAutoPauseAndResumeEnabled(isAutoPauseAndResumeEnabled: Boolean): Builder {
             this.isAutoPauseAndResumeEnabled = isAutoPauseAndResumeEnabled
             return this
         }
@@ -144,7 +122,7 @@
          * @param isGpsEnabled if true, GPS will be enabled for this exercise
          */
         @Suppress("MissingGetterMatchingBuilder")
-        public fun setIsGpsEnabled(isGpsEnabled: Boolean): Builder {
+        fun setIsGpsEnabled(isGpsEnabled: Boolean): Builder {
             this.isGpsEnabled = isGpsEnabled
             return this
         }
@@ -152,12 +130,14 @@
         /**
          * Sets [ExerciseGoal]s specified for this exercise.
          *
-         * This is useful to have goals specified before the start of an exercise.
+         * [DataType]s in [ExerciseGoal]s must also be tracked (i.e. provided to [setDataTypes]) in
+         * some form. For example, an [ExerciseGoal] for [DataType.STEPS_TOTAL] requires that either
+         * or both of [DataType.STEPS_TOTAL] / [DataType.STEPS] be passed into [setDataTypes].
          *
          * @param exerciseGoals the list of [ExerciseGoal]s to begin the exercise with
          */
-        public fun setExerciseGoals(exerciseGoals: List<ExerciseGoal>): Builder {
-            this.exerciseGoals = exerciseGoals.toList()
+        fun setExerciseGoals(exerciseGoals: List<ExerciseGoal<*>>): Builder {
+            this.exerciseGoals = exerciseGoals
             return this
         }
 
@@ -167,17 +147,16 @@
          *
          * @param exerciseParams [Bundle] containing OEM specific parameters
          */
-        public fun setExerciseParams(exerciseParams: Bundle): Builder {
+        fun setExerciseParams(exerciseParams: Bundle): Builder {
             this.exerciseParams = exerciseParams
             return this
         }
 
         /** Returns the built [ExerciseConfig]. */
-        public fun build(): ExerciseConfig {
+        fun build(): ExerciseConfig {
             return ExerciseConfig(
-                checkNotNull(exerciseType) { "No exercise type specified" },
+                exerciseType,
                 dataTypes,
-                aggregateDataTypes,
                 isAutoPauseAndResumeEnabled,
                 isGpsEnabled,
                 exerciseGoals,
@@ -186,35 +165,32 @@
         }
     }
 
-    /** @hide */
-    override val proto: DataProto.ExerciseConfig by lazy {
+    override fun toString(): String =
+        "ExerciseConfig(" +
+            "exerciseType=$exerciseType, " +
+            "dataTypes=$dataTypes, " +
+            "isAutoPauseAndResumeEnabled=$isAutoPauseAndResumeEnabled, " +
+            "isGpsEnabled=$isGpsEnabled, " +
+            "exerciseGoals=$exerciseGoals)"
+
+    internal fun toProto(): DataProto.ExerciseConfig =
         DataProto.ExerciseConfig.newBuilder()
             .setExerciseType(exerciseType.toProto())
-            .addAllDataTypes(dataTypes.map { it.proto })
-            .addAllAggregateDataTypes(aggregateDataTypes.map { it.proto })
+            .addAllDataTypes(dataTypes.filter { !it.isAggregate }.map { it.proto })
+            .addAllAggregateDataTypes(dataTypes.filter { it.isAggregate }.map { it.proto })
             .setIsAutoPauseAndResumeEnabled(isAutoPauseAndResumeEnabled)
             .setIsGpsUsageEnabled(isGpsEnabled)
             .addAllExerciseGoals(exerciseGoals.map { it.proto })
             .setExerciseParams(BundlesUtil.toProto(exerciseParams))
             .build()
-    }
 
-    override fun toString(): String =
-        "ExerciseConfig(" +
-            "exerciseType=$exerciseType, " +
-            "dataTypes=$dataTypes, " +
-            "aggregateDataTypes=$aggregateDataTypes, " +
-            "isAutoPauseAndResumeEnabled=$isAutoPauseAndResumeEnabled, " +
-            "isGpsEnabled=$isGpsEnabled, " +
-            "exerciseGoals=$exerciseGoals)"
-
-    public companion object {
-        @JvmStatic public fun builder(): Builder = Builder()
-
-        @JvmField
-        public val CREATOR: Parcelable.Creator<ExerciseConfig> = newCreator { bytes ->
-            val proto = DataProto.ExerciseConfig.parseFrom(bytes)
-            ExerciseConfig(proto)
-        }
+    companion object {
+        /**
+         * Returns a fresh new [Builder].
+         *
+         * @param exerciseType the [ExerciseType] representing this exercise
+          */
+        @JvmStatic
+        fun builder(exerciseType: ExerciseType): Builder = Builder(exerciseType)
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseGoal.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseGoal.kt
index 1228e91..587cf65 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseGoal.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseGoal.kt
@@ -17,39 +17,28 @@
 package androidx.health.services.client.data
 
 import android.os.Parcelable
-import androidx.annotation.RestrictTo
 import androidx.health.services.client.proto.DataProto
 import java.util.Objects
 
-// TODO(yeabkal): as we support more types of goals, we may want to rename the class.
 /** Defines a goal for an exercise. */
-@Suppress("DataClassPrivateConstructor", "ParcelCreator")
-public class ExerciseGoal
-private constructor(
-    public val exerciseGoalType: ExerciseGoalType,
-    public val dataTypeCondition: DataTypeCondition,
-    // TODO(yeabkal): shall we rename to "getMilestonePeriod"? Currently "getPeriod" is used to be
-    // flexible in case we support other kinds of goals. Recheck when design is fully locked.
-    public val period: Value? = null,
+class ExerciseGoal<T : Number>
+internal constructor(
+    /**
+     * The type of this exercise goal ([ExerciseGoalType.ONE_TIME_GOAL] or
+     * [ExerciseGoalType.MILESTONE].)
+     */
+    val exerciseGoalType: ExerciseGoalType,
+    val dataTypeCondition: DataTypeCondition<T, AggregateDataType<T, *>>,
+    val period: T? = null,
 ) : ProtoParcelable<DataProto.ExerciseGoal>() {
 
-    internal constructor(
-        proto: DataProto.ExerciseGoal
-    ) : this(
-        ExerciseGoalType.fromProto(proto.exerciseGoalType)
-            ?: throw IllegalStateException("${proto.exerciseGoalType} not found"),
-        DataTypeCondition(proto.dataTypeCondition),
-        if (proto.hasPeriod()) Value(proto.period) else null
-    )
-
     /** @hide */
     override val proto: DataProto.ExerciseGoal by lazy {
         val builder =
-            DataProto.ExerciseGoal.newBuilder()
-                .setExerciseGoalType(exerciseGoalType.toProto())
+            DataProto.ExerciseGoal.newBuilder().setExerciseGoalType(exerciseGoalType.toProto())
                 .setDataTypeCondition(dataTypeCondition.proto)
         if (period != null) {
-            builder.period = period.proto
+            builder.period = dataTypeCondition.dataType.toProtoFromValue(period)
         }
         builder.build()
     }
@@ -60,7 +49,7 @@
         if (other === this) {
             return true
         }
-        if (other !is ExerciseGoal) {
+        if (other !is ExerciseGoal<*>) {
             return false
         }
 
@@ -84,52 +73,29 @@
         }
     }
 
-    override fun toString(): String =
-        "ExerciseGoal(" +
-            "exerciseGoalType=$exerciseGoalType, " +
-            "dataTypeCondition=$dataTypeCondition, " +
-            "period=$period)"
+    override fun toString(): String = "ExerciseGoal(" +
+        "exerciseGoalType=$exerciseGoalType, " +
+        "dataTypeCondition=$dataTypeCondition, " +
+        "period=$period" +
+        ")"
 
-    /**
-     * Checks if [other] is a possible representation of this goal. For one-time goals, this simply
-     * checks for equality. For milestones, this returns `true` if and only if:
-     * - [other] uses the same [ComparisonType], [DataType], and [period] as this goal, and
-     * - the difference between [other]'s threshold and the threshold of this goal is a multiple of
-     * of their common period.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public fun isEquivalentTo(other: ExerciseGoal): Boolean {
-        if (this.exerciseGoalType != other.exerciseGoalType) {
-            return false
-        }
-
-        return when (exerciseGoalType) {
-            ExerciseGoalType.ONE_TIME_GOAL -> equals(other)
-            ExerciseGoalType.MILESTONE ->
-                this.dataTypeCondition.dataType == other.dataTypeCondition.dataType &&
-                    this.dataTypeCondition.comparisonType ==
-                        other.dataTypeCondition.comparisonType &&
-                    this.period == other.period &&
-                    Value.isZero(
-                        Value.modulo(
-                            Value.difference(
-                                dataTypeCondition.threshold,
-                                other.dataTypeCondition.threshold
-                            ),
-                            period!!
-                        )
-                    )
-            else -> equals(other)
-        }
-    }
-
-    public companion object {
+    companion object {
         @JvmField
-        public val CREATOR: Parcelable.Creator<ExerciseGoal> = newCreator {
+        public val CREATOR: Parcelable.Creator<ExerciseGoal<*>> = newCreator {
             val proto = DataProto.ExerciseGoal.parseFrom(it)
-            ExerciseGoal(proto)
+            fromProto(proto)
+        }
+
+        @Suppress("UNCHECKED_CAST")
+        internal fun fromProto(proto: DataProto.ExerciseGoal): ExerciseGoal<Number> {
+            val condition = DataTypeCondition.aggregateFromProto(proto.dataTypeCondition)
+                as DataTypeCondition<Number, AggregateDataType<Number, *>>
+            return ExerciseGoal(
+                ExerciseGoalType.fromProto(proto.exerciseGoalType)
+                    ?: throw IllegalStateException("${proto.exerciseGoalType} not found"),
+                condition,
+                if (proto.hasPeriod()) condition.dataType.toValueFromProto(proto.period) else null
+            )
         }
 
         /**
@@ -137,7 +103,9 @@
          * satisfied.
          */
         @JvmStatic
-        public fun createOneTimeGoal(condition: DataTypeCondition): ExerciseGoal {
+        fun <T : Number> createOneTimeGoal(
+            condition: DataTypeCondition<T, AggregateDataType<T, *>>
+        ): ExerciseGoal<T> {
             return ExerciseGoal(ExerciseGoalType.ONE_TIME_GOAL, condition)
         }
 
@@ -147,29 +115,23 @@
          * one for every 2km. This goal will there be triggered at distances = 2km, 4km, 6km, ...
          */
         @JvmStatic
-        public fun createMilestone(condition: DataTypeCondition, period: Value): ExerciseGoal {
-            require(period.format == condition.threshold.format) {
-                "The condition's threshold and the period should have the same types of values."
-            }
-            return ExerciseGoal(ExerciseGoalType.MILESTONE, condition, period)
-        }
+        fun <T : Number> createMilestone(
+            condition: DataTypeCondition<T, AggregateDataType<T, *>>,
+            period: T
+        ): ExerciseGoal<T> = ExerciseGoal(ExerciseGoalType.MILESTONE, condition, period)
 
         /** Creates a new goal that is the same as a given goal but with a new threshold value. */
         @JvmStatic
-        public fun createMilestoneGoalWithUpdatedThreshold(
-            goal: ExerciseGoal,
-            newThreshold: Value
-        ): ExerciseGoal {
+        fun <T : Number> createMilestoneGoalWithUpdatedThreshold(
+            goal: ExerciseGoal<T>,
+            newThreshold: T
+        ): ExerciseGoal<T> {
             require(ExerciseGoalType.MILESTONE == goal.exerciseGoalType) {
                 "The goal to update should be of MILESTONE type."
             }
             require(goal.period != null) { "The milestone goal's period should not be null." }
             val dataType = goal.dataTypeCondition.dataType
-            val oldThreshold = goal.dataTypeCondition.threshold
             val comparisonType = goal.dataTypeCondition.comparisonType
-            require(oldThreshold.format == newThreshold.format) {
-                "The old and new thresholds should have the same types of values."
-            }
             return ExerciseGoal(
                 ExerciseGoalType.MILESTONE,
                 DataTypeCondition(dataType, newThreshold, comparisonType),
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseLapSummary.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseLapSummary.kt
index 3f80b3e..286f1465 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseLapSummary.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseLapSummary.kt
@@ -16,8 +16,6 @@
 
 package androidx.health.services.client.data
 
-import android.os.Parcelable
-import androidx.annotation.RestrictTo
 import androidx.health.services.client.proto.DataProto
 import androidx.health.services.client.proto.DataProto.ExerciseLapSummary.LapMetricsEntry
 import java.time.Duration
@@ -25,58 +23,64 @@
 
 /** Describes a completed exercise lap. */
 @Suppress("ParcelCreator")
-public class ExerciseLapSummary(
+class ExerciseLapSummary(
     /** Returns the lap count of this summary. Lap count starts at 1 for the first lap. */
-    public val lapCount: Int,
+    val lapCount: Int,
 
     /** Returns the time at which the lap has started. */
-    public val startTime: Instant,
+    val startTime: Instant,
 
     /** Returns the time at which the lap has ended. */
-    public val endTime: Instant,
+    val endTime: Instant,
 
     /**
      * Returns the total elapsed time for which the exercise has been active during this lap, i.e.
      * started but not paused.
      */
-    public val activeDuration: Duration,
+    val activeDuration: Duration,
 
     /**
      * Returns the [DataPoint]s for each metric keyed by [DataType] tracked between [startTime] and
-     * [endTime] i.e. during the duration of this lap. This will only contain aggregated [DataType]s
+     * [endTime] i.e. during the duration of this lap. This will only contain [AggregateDataType]s
      * calculated over the duration of the lap.
      */
-    public val lapMetrics: Map<DataType, AggregateDataPoint>,
-) : ProtoParcelable<DataProto.ExerciseLapSummary>() {
+    val lapMetrics: DataPointContainer,
+) {
 
-    /** @hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public constructor(
+    internal constructor(
         proto: DataProto.ExerciseLapSummary
     ) : this(
         proto.lapCount,
         Instant.ofEpochMilli(proto.startTimeEpochMs),
         Instant.ofEpochMilli(proto.endTimeEpochMs),
         Duration.ofMillis(proto.activeDurationMs),
-        proto
-            .lapMetricsList
-            .map { DataType(it.dataType) to AggregateDataPoint.fromProto(it.aggregateDataPoint) }
-            .toMap()
+        DataPointContainer(
+            proto.lapMetricsList.map { DataPoint.fromProto(it.aggregateDataPoint) }
+        )
     )
 
-    /** @hide */
-    override val proto: DataProto.ExerciseLapSummary by lazy {
+    internal val proto: DataProto.ExerciseLapSummary by lazy {
         DataProto.ExerciseLapSummary.newBuilder()
             .setLapCount(lapCount)
             .setStartTimeEpochMs(startTime.toEpochMilli())
             .setEndTimeEpochMs(endTime.toEpochMilli())
             .setActiveDurationMs(activeDuration.toMillis())
             .addAllLapMetrics(
-                lapMetrics
+                lapMetrics.statisticalDataPoints
                     .map {
                         LapMetricsEntry.newBuilder()
-                            .setDataType(it.key.proto)
-                            .setAggregateDataPoint(it.value.proto)
+                            .setDataType(it.dataType.proto)
+                            .setAggregateDataPoint(it.proto)
+                            .build()
+                    }
+                    .sortedBy { it.dataType.name } // Required to ensure equals() works
+            )
+            .addAllLapMetrics(
+                lapMetrics.cumulativeDataPoints
+                    .map {
+                        LapMetricsEntry.newBuilder()
+                            .setDataType(it.dataType.proto)
+                            .setAggregateDataPoint(it.proto)
                             .build()
                     }
                     .sortedBy { it.dataType.name } // Required to ensure equals() works
@@ -91,12 +95,4 @@
             "endTime=$endTime, " +
             "activeDuration=$activeDuration, " +
             "lapMetrics=$lapMetrics)"
-
-    public companion object {
-        @JvmField
-        public val CREATOR: Parcelable.Creator<ExerciseLapSummary> = newCreator { bytes ->
-            val proto = DataProto.ExerciseLapSummary.parseFrom(bytes)
-            ExerciseLapSummary(proto)
-        }
-    }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseTypeCapabilities.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseTypeCapabilities.kt
index 06622d4..1871f1d 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseTypeCapabilities.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseTypeCapabilities.kt
@@ -24,23 +24,26 @@
 /** Provides exercise specific capabilities data. */
 @Suppress("ParcelCreator")
 public class ExerciseTypeCapabilities(
-    public val supportedDataTypes: Set<DataType>,
-    /** Map from supported goals datatypes to a set of compatible [ComparisonType]s. */
-    public val supportedGoals: Map<DataType, Set<ComparisonType>>,
-    /** Map from supported milestone datatypes to a set of compatible [ComparisonType]s. */
-    public val supportedMilestones: Map<DataType, Set<ComparisonType>>,
+    /** Supported [DataType]s for a given exercise. */
+    public val supportedDataTypes: Set<DataType<*, *>>,
+    /** Map from supported goals [DataType]s to a set of compatible [ComparisonType]s. */
+    public val supportedGoals: Map<AggregateDataType<*, *>, Set<ComparisonType>>,
+    /** Map from supported milestone [DataType]s to a set of compatible [ComparisonType]s. */
+    public val supportedMilestones: Map<AggregateDataType<*, *>, Set<ComparisonType>>,
+    /** Returns `true` if the given exercise supports auto pause and resume. */
     public val supportsAutoPauseAndResume: Boolean,
-    public val supportsLaps: Boolean,
 ) : ProtoParcelable<DataProto.ExerciseTypeCapabilities>() {
 
     internal constructor(
         proto: DataProto.ExerciseTypeCapabilities
     ) : this(
-        proto.supportedDataTypesList.map { DataType(it) }.toSet(),
+        proto.supportedDataTypesList.map { DataType.deltaAndAggregateFromProto(it) }
+            .flatten()
+            .toSet(),
         proto
             .supportedGoalsList
             .map { entry ->
-                DataType(entry.dataType) to
+                DataType.aggregateFromProto(entry.dataType) to
                     entry
                         .comparisonTypesList
                         .map { ComparisonType.fromProto(it) }
@@ -51,7 +54,7 @@
         proto
             .supportedMilestonesList
             .map { entry ->
-                DataType(entry.dataType) to
+                DataType.aggregateFromProto(entry.dataType) to
                     entry
                         .comparisonTypesList
                         .map { ComparisonType.fromProto(it) }
@@ -60,7 +63,6 @@
             }
             .toMap(),
         supportsAutoPauseAndResume = proto.isAutoPauseAndResumeSupported,
-        supportsLaps = proto.isLapsSupported
     )
 
     /** @hide */
@@ -88,7 +90,6 @@
                     .sortedBy { it.dataType.name } // Sorting to ensure equals() works
             )
             .setIsAutoPauseAndResumeSupported(supportsAutoPauseAndResume)
-            .setIsLapsSupported(supportsLaps)
             .build()
     }
 
@@ -97,8 +98,7 @@
             "supportedDataTypes=$supportedDataTypes, " +
             "supportedGoals=$supportedGoals, " +
             "supportedMilestones=$supportedMilestones, " +
-            "supportsAutoPauseAndResume=$supportsAutoPauseAndResume, " +
-            "supportsLaps=$supportsLaps)"
+            "supportsAutoPauseAndResume=$supportsAutoPauseAndResume, "
 
     public companion object {
         @JvmField
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseUpdate.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseUpdate.kt
index 0f42a41..89f23bf 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseUpdate.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseUpdate.kt
@@ -16,15 +16,13 @@
 
 package androidx.health.services.client.data
 
+import androidx.health.services.client.proto.DataProto.ExerciseUpdate.LatestMetricsEntry as LatestMetricsEntryProto
 import android.os.Parcelable
 import androidx.annotation.RestrictTo
 import androidx.health.services.client.data.ExerciseEndReason.Companion.toProto
+import androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint
 import androidx.health.services.client.proto.DataProto
-import androidx.health.services.client.proto.DataProto.AggregateDataPoint.AggregateCase.AGGREGATE_NOT_SET
-import androidx.health.services.client.proto.DataProto.AggregateDataPoint.AggregateCase.CUMULATIVE_DATA_POINT
-import androidx.health.services.client.proto.DataProto.AggregateDataPoint.AggregateCase.STATISTICAL_DATA_POINT
-import androidx.health.services.client.proto.DataProto.ExerciseUpdate.LatestMetricsEntry as LatestMetricsEntryProto
-import java.lang.IllegalStateException
+import androidx.health.services.client.proto.DataProto.AchievedExerciseGoal
 import java.time.Duration
 import java.time.Instant
 
@@ -46,21 +44,15 @@
     /** The duration since boot when this ExerciseUpdate was created. */
     private val updateDurationFromBoot: Duration?,
 
-    /**
-     * Returns the list of latest [DataPoint] for each metric keyed by data type name. This allows a
-     * client to easily query for the "current" values of each metric since last call.
-     */
-    public val latestMetrics: Map<DataType, List<DataPoint>>,
-
-    /** Returns the latest aggregated values for each metric keyed by [DataType.name]. */
-    public val latestAggregateMetrics: Map<DataType, AggregateDataPoint>,
+    /** Returns the list of the latest [DataPoint]s. */
+    public val latestMetrics: DataPointContainer,
 
     /**
      * Returns the latest [ExerciseGoalType.ONE_TIME_GOAL] [ExerciseGoal]s that have been achieved.
      * [ExerciseGoalType.MILESTONE] [ExerciseGoal]s will be returned via
      * [latestMilestoneMarkerSummaries].
      */
-    public val latestAchievedGoals: Set<AchievedExerciseGoal>,
+    public val latestAchievedGoals: Set<ExerciseGoal<out Number>>,
 
     /** Returns the latest [MilestoneMarkerSummary]s. */
     public val latestMilestoneMarkerSummaries: Set<MilestoneMarkerSummary>,
@@ -97,23 +89,8 @@
         } else {
             null
         },
-        proto
-            .latestMetricsList
-            .map { metric ->
-                DataType(metric.dataType) to metric.dataPointsList.map { DataPoint(it) }
-            }
-            .toMap(),
-        proto.latestAggregateMetricsList
-            .map { metric ->
-                when (metric.aggregateCase) {
-                    CUMULATIVE_DATA_POINT -> DataType(metric.cumulativeDataPoint.dataType)
-                    STATISTICAL_DATA_POINT -> DataType(metric.statisticalDataPoint.dataType)
-                    null, AGGREGATE_NOT_SET ->
-                        throw IllegalStateException("Aggregate not set on $metric")
-                } to AggregateDataPoint.fromProto(metric)
-            }
-            .toMap(),
-        proto.latestAchievedGoalsList.map { AchievedExerciseGoal(it) }.toSet(),
+        exerciseUpdateProtoToDataPointContainer(proto),
+        proto.latestAchievedGoalsList.map { ExerciseGoal.fromProto(it.exerciseGoal) }.toSet(),
         proto.mileStoneMarkerSummariesList.map { MilestoneMarkerSummary(it) }.toSet(),
         if (proto.hasExerciseConfig()) ExerciseConfig(proto.exerciseConfig) else null,
         if (proto.hasActiveDurationCheckpoint()) {
@@ -184,38 +161,48 @@
                 .setState(exerciseStateInfo.state.toProto())
                 .setActiveDurationMs(activeDuration.toMillis())
                 .addAllLatestMetrics(
-                    latestMetrics
+                    latestMetrics.sampleDataPoints
+                        .groupBy { it.dataType }
                         .map {
                             LatestMetricsEntryProto.newBuilder()
                                 .setDataType(it.key.proto)
-                                .addAllDataPoints(it.value.map { dataPoint -> dataPoint.proto })
+                                .addAllDataPoints(it.value.map(SampleDataPoint<*>::proto))
                                 .build()
                         }
-                        .sortedBy { entry ->
-                            entry.dataType.name
-                        } // If we don't sort, equals() may not work.
+                        // If we don't sort, equals() may not work.
+                        .sortedBy { entry -> entry.dataType.name }
+                )
+                .addAllLatestMetrics(
+                    latestMetrics.intervalDataPoints
+                        .groupBy { it.dataType }
+                        .map {
+                            LatestMetricsEntryProto.newBuilder()
+                                .setDataType(it.key.proto)
+                                .addAllDataPoints(it.value.map(IntervalDataPoint<*>::proto))
+                                .build()
+                        }
+                        // If we don't sort, equals() may not work.
+                        .sortedBy { entry -> entry.dataType.name }
                 )
                 .addAllLatestAggregateMetrics(
-                    latestAggregateMetrics
-                        .map { it.value.proto }
-                        .sortedBy { entry ->
-                            when (entry.aggregateCase) {
-                                CUMULATIVE_DATA_POINT -> entry.cumulativeDataPoint.dataType.name
-                                STATISTICAL_DATA_POINT -> entry.statisticalDataPoint.dataType.name
-                                null, AGGREGATE_NOT_SET ->
-                                    throw IllegalStateException("Aggregate not set on $entry")
-                            }
-                        }
-                ) // If we don't sort, equals() may not work.
-                .addAllLatestAchievedGoals(latestAchievedGoals.map { it.proto })
-                .addAllMileStoneMarkerSummaries(latestMilestoneMarkerSummaries.map { it.proto })
+                    latestMetrics.statisticalDataPoints
+                        .map { it.proto }
+                        // If we don't sort, equals() may not work.
+                        .sortedBy { entry -> entry.statisticalDataPoint.dataType.name }
+                ).addAllLatestAggregateMetrics(latestMetrics.cumulativeDataPoints
+                    .map { it.proto }
+                    // If we don't sort, equals() may not work.
+                    .sortedBy { entry -> entry.cumulativeDataPoint.dataType.name })
+                .addAllLatestAchievedGoals(latestAchievedGoals.map {
+                    AchievedExerciseGoal.newBuilder().setExerciseGoal(it.proto).build()
+                }).addAllMileStoneMarkerSummaries(latestMilestoneMarkerSummaries.map { it.proto })
                 .setExerciseEndReason((exerciseStateInfo.endReason).toProto())
 
         startTime?.let { builder.setStartTimeEpochMs(startTime.toEpochMilli()) }
         updateDurationFromBoot?.let {
             builder.setUpdateDurationFromBootMs(updateDurationFromBoot.toMillis())
         }
-        exerciseConfig?.let { builder.setExerciseConfig(exerciseConfig.proto) }
+        exerciseConfig?.let { builder.setExerciseConfig(exerciseConfig.toProto()) }
         activeDurationCheckpoint?.let {
             builder.setActiveDurationCheckpoint(activeDurationCheckpoint.toProto())
         }
@@ -236,16 +223,33 @@
             )
 
     /**
-     * Returns the ActiveDuration of the exercise at the time of the provided [DataPoint]. The
-     * provided [DataPoint] should be present in this [ExerciseUpdate].
+     * Returns the ActiveDuration of the exercise at the time of the provided [IntervalDataPoint].
+     * The provided [IntervalDataPoint] should be present in this [ExerciseUpdate].
      *
      * @throws IllegalArgumentException if [dataPoint] is not present in this [ExerciseUpdate]
      * @throws IllegalStateException if this [ExerciseUpdate] does not contain a valid
      * `updateDurationFromBoot` which may happen if the Health Services app is out of date
      */
-    public fun getActiveDurationAtDataPoint(dataPoint: DataPoint): Duration {
-        val dataPointList = latestMetrics[dataPoint.dataType]
-        if (dataPointList == null || dataPointList.indexOf(dataPoint) == -1) {
+    public fun getActiveDurationAtDataPoint(dataPoint: IntervalDataPoint<*>): Duration =
+        getActiveDurationAtDataPoint(dataPoint, dataPoint.endDurationFromBoot)
+
+    /**
+     * Returns the ActiveDuration of the exercise at the time of the provided [SampleDataPoint].
+     * The provided [SampleDataPoint] should be present in this [ExerciseUpdate].
+     *
+     * @throws IllegalArgumentException if [dataPoint] is not present in this [ExerciseUpdate]
+     * @throws IllegalStateException if this [ExerciseUpdate] does not contain a valid
+     * `updateDurationFromBoot` which may happen if the Health Services app is out of date
+     */
+    public fun getActiveDurationAtDataPoint(dataPoint: SampleDataPoint<*>): Duration =
+        getActiveDurationAtDataPoint(dataPoint, dataPoint.timeDurationFromBoot)
+
+    private fun getActiveDurationAtDataPoint(
+        dataPoint: DataPoint<*>,
+        durationFromBoot: Duration
+    ): Duration {
+        val dataPointList = latestMetrics.dataPoints[dataPoint.dataType]
+        if (dataPointList?.indexOf(dataPoint) == -1) {
             throw IllegalArgumentException("dataPoint not found in ExerciseUpdate")
         }
 
@@ -255,11 +259,11 @@
         ) {
             return activeDuration
         }
+
         // Active duration applies to when this update was generated so calculate for the given time
         // by working backwards.
         // First find time since this point was generated.
-        val durationSinceProvidedTime =
-            getUpdateDurationFromBoot().minus(dataPoint.endDurationFromBoot)
+        val durationSinceProvidedTime = getUpdateDurationFromBoot().minus(durationFromBoot)
         return activeDuration.minus(durationSinceProvidedTime)
     }
 
@@ -270,7 +274,6 @@
             "activeDuration=$activeDuration, " +
             "updateDurationFromBoot=$updateDurationFromBoot, " +
             "latestMetrics=$latestMetrics, " +
-            "latestAggregateMetrics=$latestAggregateMetrics, " +
             "latestAchievedGoals=$latestAchievedGoals, " +
             "latestMilestoneMarkerSummaries=$latestMilestoneMarkerSummaries, " +
             "exerciseConfig=$exerciseConfig, " +
@@ -285,7 +288,6 @@
         if (startTime != other.startTime) return false
         if (activeDuration != other.activeDuration) return false
         if (latestMetrics != other.latestMetrics) return false
-        if (latestAggregateMetrics != other.latestAggregateMetrics) return false
         if (latestAchievedGoals != other.latestAchievedGoals) return false
         if (latestMilestoneMarkerSummaries != other.latestMilestoneMarkerSummaries) return false
         if (exerciseConfig != other.exerciseConfig) return false
@@ -300,7 +302,6 @@
         var result = startTime?.hashCode() ?: 0
         result = 31 * result + activeDuration.hashCode()
         result = 31 * result + latestMetrics.hashCode()
-        result = 31 * result + latestAggregateMetrics.hashCode()
         result = 31 * result + latestAchievedGoals.hashCode()
         result = 31 * result + latestMilestoneMarkerSummaries.hashCode()
         result = 31 * result + (exerciseConfig?.hashCode() ?: 0)
@@ -316,5 +317,23 @@
             val proto = DataProto.ExerciseUpdate.parseFrom(bytes)
             ExerciseUpdate(proto)
         }
+
+        internal fun exerciseUpdateProtoToDataPointContainer(
+            proto: DataProto.ExerciseUpdate
+        ): DataPointContainer {
+            val dataPoints = mutableListOf<DataPoint<*>>()
+
+            proto.latestMetricsList
+                .flatMap { it.dataPointsList }
+                .forEach {
+                    dataPoints += DataPoint.fromProto(it)
+                }
+            proto.latestAggregateMetricsList
+                .forEach {
+                    dataPoints += DataPoint.fromProto(it)
+                }
+
+            return DataPointContainer(dataPoints)
+        }
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/HealthEvent.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/HealthEvent.kt
index 5f73d62..5650cff 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/HealthEvent.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/HealthEvent.kt
@@ -17,6 +17,7 @@
 package androidx.health.services.client.data
 
 import androidx.health.services.client.proto.DataProto
+import androidx.health.services.client.proto.DataProto.HealthEvent.MetricsEntry
 import java.time.Instant
 
 /** Represents a user's health event. */
@@ -28,7 +29,7 @@
     public val eventTime: Instant,
 
     /** Gets metrics associated to the event. */
-    public val metrics: Map<DataType, List<DataPoint>>,
+    public val metrics: DataPointContainer,
 ) {
 
     /** Health event types. */
@@ -75,26 +76,77 @@
     ) : this(
         Type.fromProto(proto.type),
         Instant.ofEpochMilli(proto.eventTimeEpochMs),
-        proto
-            .metricsList
-            .map { entry -> DataType(entry.dataType) to entry.dataPointsList.map { DataPoint(it) } }
-            .toMap()
+        fromHealthEventProto(proto)
     )
 
     internal val proto: DataProto.HealthEvent by lazy {
         DataProto.HealthEvent.newBuilder()
             .setType(type.toProto())
             .setEventTimeEpochMs(eventTime.toEpochMilli())
-            .addAllMetrics(
-                metrics
-                    .map { entry ->
-                        DataProto.HealthEvent.MetricsEntry.newBuilder()
-                            .setDataType(entry.key.proto)
-                            .addAllDataPoints(entry.value.map { it.proto })
-                            .build()
-                    }
-                    .sortedBy { it.dataType.name } // Required to ensure equals() works
-            )
+            .addAllMetrics(toEventProtoList(metrics))
             .build()
     }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is HealthEvent) return false
+        if (type != other.type) return false
+        if (eventTime != other.eventTime) return false
+        if (metrics != other.metrics) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = type.hashCode()
+        result = 31 * result + eventTime.hashCode()
+        result = 31 * result + metrics.hashCode()
+        return result
+    }
+
+    internal companion object {
+        internal fun toEventProtoList(container: DataPointContainer): List<MetricsEntry> {
+            val list = mutableListOf<MetricsEntry>()
+
+            for (entry in container.dataPoints) {
+                if (entry.value.isEmpty()) {
+                    continue
+                }
+
+                when (entry.key.timeType) {
+                    DataType.TimeType.SAMPLE -> {
+                        list.add(
+                            MetricsEntry.newBuilder()
+                                .setDataType(entry.key.proto)
+                                .addAllDataPoints(entry.value.map { (it as SampleDataPoint).proto })
+                                .build()
+                        )
+                    }
+                    DataType.TimeType.INTERVAL -> {
+                        list.add(
+                            MetricsEntry.newBuilder()
+                                .setDataType(entry.key.proto)
+                                .addAllDataPoints(entry.value.map {
+                                    (it as IntervalDataPoint).proto
+                                })
+                                .build()
+                        )
+                    }
+                }
+            }
+            return list.sortedBy { it.dataType.name } // Required to ensure equals() works
+        }
+
+        internal fun fromHealthEventProto(
+            proto: DataProto.HealthEvent
+        ): DataPointContainer {
+            val dataTypeToDataPoints: Map<DataType<*, *>, List<DataPoint<*>>> =
+                proto.metricsList.associate { entry ->
+                    DataType.deltaFromProto(entry.dataType) to entry.dataPointsList.map {
+                        DataPoint.fromProto(it)
+                    }
+                }
+            return DataPointContainer(dataTypeToDataPoints)
+        }
+    }
 }
\ No newline at end of file
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/IntervalDataPoint.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/IntervalDataPoint.kt
new file mode 100644
index 0000000..9a36c52
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/IntervalDataPoint.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import android.os.Bundle
+import androidx.health.services.client.proto.DataProto
+import java.time.Duration
+import java.time.Instant
+
+/** Data point that includes just the delta from the previous data point for [dataType]. */
+class IntervalDataPoint<T : Any>(
+    /** The [DataType] this [DataPoint] represents. */
+    override val dataType: DataType<T, out IntervalDataPoint<T>>,
+    /** The value of this data point. */
+    val value: T,
+    /** The beginning of the time period this [DataPoint] represents. */
+    val startDurationFromBoot: Duration,
+    /** The end of the time period this [DataPoint] represents. */
+    val endDurationFromBoot: Duration,
+    /** OEM specific data. In general, this should not be relied upon by non-preloaded apps. */
+    val metadata: Bundle = Bundle(),
+    /** Accuracy of this DataPoint. */
+    val accuracy: DataPointAccuracy? = null,
+) : DataPoint<T>(dataType) {
+
+    internal val proto: DataProto.DataPoint by lazy {
+        val builder =
+            DataProto.DataPoint.newBuilder()
+                .setDataType(dataType.proto)
+                .setValue(dataType.toProtoFromValue(value))
+                .setStartDurationFromBootMs(startDurationFromBoot.toMillis())
+                .setEndDurationFromBootMs(endDurationFromBoot.toMillis())
+                .setMetaData(BundlesUtil.toProto(metadata))
+
+        accuracy?.let { builder.setAccuracy(it.proto) }
+
+        builder.build()
+    }
+
+    /**
+     * Returns the start [Instant] of this [DataPoint], knowing the time at which the system booted.
+     *
+     * @param bootInstant the [Instant] at which the system booted, this can be computed by
+     * `Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime()) `
+     */
+    fun getStartInstant(bootInstant: Instant): Instant {
+        return bootInstant.plus(startDurationFromBoot)
+    }
+
+    /**
+     * Returns the end [Instant] of this [DataPoint], knowing the time at which the system booted.
+     *
+     * @param bootInstant the [Instant] at which the system booted, this can be computed by
+     * `Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())`
+     */
+    fun getEndInstant(bootInstant: Instant): Instant {
+        return bootInstant.plus(endDurationFromBoot)
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is IntervalDataPoint<*>) return false
+
+        if (dataType != other.dataType) return false
+        if (value != other.value) return false
+        if (startDurationFromBoot != other.startDurationFromBoot) return false
+        if (endDurationFromBoot != other.endDurationFromBoot) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = value.hashCode()
+        result = 31 * result + dataType.hashCode()
+        result = (31 * result + startDurationFromBoot.toNanos()).toInt()
+        result = (31 * result + endDurationFromBoot.toNanos()).toInt()
+        return result
+    }
+
+    internal companion object {
+        @Suppress("UNCHECKED_CAST")
+        internal fun fromProto(proto: DataProto.DataPoint): IntervalDataPoint<*> {
+            val dataType =
+                DataType.deltaFromProto(proto.dataType) as DataType<Any, IntervalDataPoint<Any>>
+            return IntervalDataPoint(
+                dataType,
+                dataType.toValueFromProto(proto.value),
+                Duration.ofMillis(proto.startDurationFromBootMs),
+                Duration.ofMillis(proto.endDurationFromBootMs),
+                metadata = BundlesUtil.fromProto(proto.metaData),
+                accuracy = if (proto.hasAccuracy()) {
+                    DataPointAccuracy.fromProto(proto.accuracy)
+                } else {
+                    null
+                },
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/LocationData.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/LocationData.kt
new file mode 100644
index 0000000..0e44470
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/LocationData.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.proto.DataProto
+
+/** Data representing one location point with direction. */
+public class LocationData(
+    /** Latitude of location. */
+    public val latitude: Double,
+    /** Longitude of location. */
+    public val longitude: Double,
+    /** Altitude of location in meters or `null` if not available. */
+    public val altitude: Double? = null,
+    /** Bearing in degrees or `null` if not available. */
+    public val bearing: Double? = null,
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is LocationData) return false
+
+        if (latitude != other.latitude) return false
+        if (longitude != other.longitude) return false
+        if (altitude != other.altitude) return false
+        if (bearing != other.bearing) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = latitude.hashCode()
+        result = 31 * result + longitude.hashCode()
+        result = 31 * result + altitude.hashCode()
+        result = 31 * result + bearing.hashCode()
+        return result
+    }
+
+    override fun toString(): String =
+        "LocationData(" +
+            "latitude=$latitude," +
+            " longitude=$longitude," +
+            " altitude=$altitude," +
+            " bearing=$bearing" +
+            ")"
+
+    internal fun addToValueProtoBuilder(proto: DataProto.Value.Builder) {
+        val doubleArrayBuilder = DataProto.Value.DoubleArray.newBuilder().apply {
+            addDoubleArray(latitude)
+            addDoubleArray(longitude)
+            addDoubleArray(altitude ?: Double.MAX_VALUE)
+            addDoubleArray(bearing ?: Double.MAX_VALUE)
+        }
+
+        proto.setDoubleArrayVal(doubleArrayBuilder)
+    }
+
+    internal companion object {
+        /**
+         * When using [DataType.LOCATION], the value is represented as [DoubleArray]. The [Double]
+         * value at this index represents the latitude.
+         */
+        private const val LATITUDE_INDEX: Int = 0
+
+        /**
+         * When using [DataType.LOCATION], the value is represented as [DoubleArray]. The [Double]
+         * value at this index represents the longitude.
+         */
+        private const val LONGITUDE_INDEX: Int = 1
+
+        /**
+         * When using [DataType.LOCATION], the value is represented as [DoubleArray]. The [Double]
+         * value at this index represents the altitude. This value will default to
+         * [Double.MAX_VALUE] if it is not available.
+         */
+        private const val ALTITUDE_INDEX: Int = 2
+
+        /**
+         * When using [DataType.LOCATION], the value is represented as [DoubleArray]. The [Double]
+         * value at this index represents the bearing. This value will default to [Double.MAX_VALUE]
+         * if it is not available.
+         */
+        private const val BEARING_INDEX: Int = 3
+
+        internal fun fromDataProtoValue(proto: DataProto.Value): LocationData {
+            require(proto.hasDoubleArrayVal())
+
+            val latitude = proto.doubleArrayVal.getDoubleArray(LATITUDE_INDEX)
+            val longitude = proto.doubleArrayVal.getDoubleArray(LONGITUDE_INDEX)
+
+            // Altitude and bearing are optional. There are two indications we need to look for to
+            // determine their absence and set them to null: the array being too short, or they are
+            // set to Double.MAX_VALUE.
+            var altitude: Double? = null
+            if (proto.doubleArrayVal.doubleArrayCount > ALTITUDE_INDEX) {
+                altitude = proto.doubleArrayVal.getDoubleArray(ALTITUDE_INDEX)
+                if (altitude == Double.MAX_VALUE) {
+                    altitude = null
+                }
+            }
+
+            var bearing: Double? = proto.doubleArrayVal.getDoubleArray(BEARING_INDEX)
+            if (proto.doubleArrayVal.doubleArrayCount > BEARING_INDEX) {
+                bearing = proto.doubleArrayVal.getDoubleArray(BEARING_INDEX)
+                if (bearing == Double.MAX_VALUE) {
+                    bearing = null
+                }
+            }
+
+            return LocationData(
+                latitude = latitude,
+                longitude = longitude,
+                altitude = altitude,
+                bearing = bearing
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/MeasureCapabilities.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/MeasureCapabilities.kt
index c7b2f71..e300c18 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/MeasureCapabilities.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/MeasureCapabilities.kt
@@ -31,12 +31,12 @@
      * Some data types are not available for measurement; this is typically used to measure health
      * data (e.g. HR).
      */
-    public val supportedDataTypesMeasure: Set<DataType>,
+    public val supportedDataTypesMeasure: Set<DeltaDataType<*, *>>,
 ) : ProtoParcelable<DataProto.MeasureCapabilities>() {
 
     internal constructor(
         proto: DataProto.MeasureCapabilities
-    ) : this(proto.supportedDataTypesList.map { DataType(it) }.toSet())
+    ) : this(proto.supportedDataTypesList.map { DataType.deltaFromProto(it) }.toSet())
 
     /** @hide */
     override val proto: DataProto.MeasureCapabilities by lazy {
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/MilestoneMarkerSummary.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/MilestoneMarkerSummary.kt
index 4c61c20..c5f5673 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/MilestoneMarkerSummary.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/MilestoneMarkerSummary.kt
@@ -18,6 +18,7 @@
 
 import android.os.Parcelable
 import androidx.health.services.client.proto.DataProto
+import androidx.health.services.client.proto.DataProto.AchievedExerciseGoal
 import androidx.health.services.client.proto.DataProto.MilestoneMarkerSummary.SummaryMetricsEntry
 import java.time.Duration
 import java.time.Instant
@@ -39,14 +40,15 @@
      */
     public val activeDuration: Duration,
 
-    /** The [AchievedExerciseGoal] that triggered this milestone summary. */
-    public val achievedGoal: AchievedExerciseGoal,
+    /** The [ExerciseGoal] that triggered this milestone summary. */
+    public val achievedGoal: ExerciseGoal<out Number>,
 
     /**
-     * Returns the [DataPoint] for each aggregated metric keyed by [DataType] tracked between
-     * [startTime] and [endTime] i.e. during the duration of this milestone.
+     * Returns the [DataPointContainer] for aggregated metrics tracked between [startTime] and
+     * [endTime] i.e. during the duration of this milestone. This summary will only contain
+     * [DataPoint]s for [AggregateDataType]s.
      */
-    public val summaryMetrics: Map<DataType, AggregateDataPoint>,
+    public val summaryMetrics: DataPointContainer,
 ) : ProtoParcelable<DataProto.MilestoneMarkerSummary>() {
 
     internal constructor(
@@ -55,11 +57,10 @@
         Instant.ofEpochMilli(proto.startTimeEpochMs),
         Instant.ofEpochMilli(proto.endTimeEpochMs),
         Duration.ofMillis(proto.activeDurationMs),
-        AchievedExerciseGoal(proto.achievedGoal),
-        proto
-            .summaryMetricsList
-            .map { DataType(it.dataType) to AggregateDataPoint.fromProto(it.aggregateDataPoint) }
-            .toMap()
+        ExerciseGoal.fromProto(proto.achievedGoal.exerciseGoal),
+        DataPointContainer(proto.summaryMetricsList.map {
+                DataPoint.fromProto(it.aggregateDataPoint)
+        })
     )
 
     /** @hide */
@@ -68,18 +69,28 @@
             .setStartTimeEpochMs(startTime.toEpochMilli())
             .setEndTimeEpochMs(endTime.toEpochMilli())
             .setActiveDurationMs(activeDuration.toMillis())
-            .setAchievedGoal(achievedGoal.proto)
+            .setAchievedGoal(AchievedExerciseGoal.newBuilder().setExerciseGoal(achievedGoal.proto))
             .addAllSummaryMetrics(
-                summaryMetrics
+                summaryMetrics.cumulativeDataPoints
                     .map {
                         SummaryMetricsEntry.newBuilder()
-                            .setDataType(it.key.proto)
-                            .setAggregateDataPoint(it.value.proto)
+                            .setDataType(it.dataType.proto)
+                            .setAggregateDataPoint(it.proto)
                             .build()
                     }
-                    .sortedBy { entry ->
-                        entry.dataType.name
-                    } // Sorting to ensure equals() works correctly.
+                    // Sorting to ensure equals() works correctly.
+                    .sortedBy { it.dataType.name }
+            )
+            .addAllSummaryMetrics(
+                summaryMetrics.statisticalDataPoints
+                    .map {
+                        SummaryMetricsEntry.newBuilder()
+                            .setDataType(it.dataType.proto)
+                            .setAggregateDataPoint(it.proto)
+                            .build()
+                    }
+                    // Sorting to ensure equals() works correctly.
+                    .sortedBy { it.dataType.name }
             )
             .build()
     }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveGoal.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveGoal.kt
index 12bbc10..fdb9f65 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveGoal.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveGoal.kt
@@ -16,39 +16,45 @@
 
 package androidx.health.services.client.data
 
-import android.content.Intent
-import android.os.Parcelable
+import androidx.health.services.client.proto.DataProto.PassiveGoal as PassiveGoalProto
 import androidx.annotation.IntDef
 import androidx.annotation.RestrictTo
 import androidx.health.services.client.data.PassiveGoal.TriggerFrequency.Companion.toProto
-import androidx.health.services.client.proto.DataProto.PassiveGoal as PassiveGoalProto
 
 /** Defines an passive goal that will be triggered when the specified condition is met. */
 @Suppress("ParcelCreator")
-public class PassiveGoal(
+class PassiveGoal(
     /** [DataTypeCondition] which must be met for the passive goal to be triggered. */
-    public val dataTypeCondition: DataTypeCondition,
-    @TriggerFrequency public val triggerFrequency: Int,
-) : ProtoParcelable<PassiveGoalProto>() {
+    val dataTypeCondition: DataTypeCondition<out Number, out DeltaDataType<out Number, *>>,
+    /** Frequency this goal should trigger, which is expected to be a  */
+    @TriggerFrequency val triggerFrequency: Int,
+) {
 
     internal constructor(
         proto: PassiveGoalProto
-    ) : this(DataTypeCondition(proto.condition), TriggerFrequency.fromProto(proto.triggerFrequency))
+    ) : this(
+        DataTypeCondition.deltaFromProto(proto.condition),
+        TriggerFrequency.fromProto(proto.triggerFrequency)
+    )
 
-    /** @hide */
-    override val proto: PassiveGoalProto by lazy {
-        PassiveGoalProto.newBuilder()
-            .setCondition(dataTypeCondition.proto)
-            .setTriggerFrequency(triggerFrequency.toProto())
-            .build()
+    internal val proto: PassiveGoalProto by lazy {
+        PassiveGoalProto.newBuilder().setCondition(dataTypeCondition.proto)
+            .setTriggerFrequency(triggerFrequency.toProto()).build()
     }
 
-    /**
-     * Puts the goal as an extra into a given [Intent]. The state can then be obtained from the
-     * intent via [PassiveGoal.fromIntent].
-     */
-    public fun putToIntent(intent: Intent) {
-        intent.putExtra(EXTRA_KEY, this)
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is PassiveGoal) return false
+        if (dataTypeCondition != other.dataTypeCondition) return false
+        if (triggerFrequency != other.triggerFrequency) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = dataTypeCondition.hashCode()
+        result = 31 * result + triggerFrequency
+        return result
     }
 
     override fun toString(): String =
@@ -64,20 +70,20 @@
         TriggerFrequency.ONCE,
         TriggerFrequency.REPEATED,
     )
-    public annotation class TriggerFrequency {
+    annotation class TriggerFrequency {
 
-        public companion object {
+        companion object {
             /** TriggerFrequency is an unknown or unexpected value. */
-            public const val UNKNOWN: Int = 0
+            const val UNKNOWN: Int = 0
 
             /** The passive goal will trigger the first time the specified conditions are met. */
-            public const val ONCE: Int = 1
+            const val ONCE: Int = 1
 
             /**
              * The passive goal will trigger *each time* the specified conditions become met.
              * Repeated goals on daily metrics will trigger once per day.
              */
-            public const val REPEATED: Int = 2
+            const val REPEATED: Int = 2
 
             /** @hide */
             @RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -90,36 +96,7 @@
             @RestrictTo(RestrictTo.Scope.LIBRARY)
             @TriggerFrequency
             @Suppress("WrongConstant")
-            public fun fromProto(proto: PassiveGoalProto.TriggerFrequency): Int = proto.number
+            fun fromProto(proto: PassiveGoalProto.TriggerFrequency): Int = proto.number
         }
     }
-
-    /**
-     * Does the provided [DataPoint] satisfy the passive goal condition.
-     *
-     * @throws IllegalArgumentException if the provided data point is not of the same data type as
-     * the condition itself.
-     */
-    public fun isTriggered(dataPoint: DataPoint): Boolean {
-        return dataTypeCondition.isSatisfied(dataPoint)
-    }
-
-    public companion object {
-        private const val EXTRA_KEY = "hs.passive_goal"
-        @Suppress("ActionValue") public const val ACTION_GOAL: String = "hs.passivemonitoring.GOAL"
-
-        @JvmField
-        public val CREATOR: Parcelable.Creator<PassiveGoal> = newCreator { bytes ->
-            val proto = PassiveGoalProto.parseFrom(bytes)
-            PassiveGoal(proto)
-        }
-
-        /**
-         * Creates a [PassiveGoal] from an [Intent]. Returns `null` if no [PassiveGoal] is stored in
-         * the given intent.
-         */
-        @Suppress("DEPRECATION")
-        @JvmStatic
-        public fun fromIntent(intent: Intent): PassiveGoal? = intent.getParcelableExtra(EXTRA_KEY)
-    }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveListenerConfig.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveListenerConfig.kt
index 26fc206..8f86001 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveListenerConfig.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveListenerConfig.kt
@@ -29,17 +29,16 @@
  * @property dataTypes set of [DataType]s which should be tracked. Requested data will be returned
  * by [PassiveListenerCallback.onNewDataPointsReceived].
  * @property shouldRequestUserActivityState whether to request [UserActivityInfo] updates. Data will
- * be returned by [PassiveListenerCallback.onUserActivityInfoReceived]. If set to true, calling app must
- * have [android.Manifest.permission.ACTIVITY_RECOGNITION].
+ * be returned by [PassiveListenerCallback.onUserActivityInfoReceived]. If set to true, calling app
+ * must have [android.Manifest.permission.ACTIVITY_RECOGNITION].
  * @property passiveGoals set of [PassiveGoal]s which should be tracked. Achieved goals will be
  * returned by [PassiveListenerCallback.onGoalCompleted].
  * @property healthEventTypes set of [HealthEvent.Type] which should be tracked. Detected health
  * events will be returned by [PassiveListenerCallback.onHealthEventReceived].
  */
 @Suppress("ParcelCreator")
-public class PassiveListenerConfig
-public constructor(
-    public val dataTypes: Set<DataType>,
+public class PassiveListenerConfig(
+    public val dataTypes: Set<DataType<out Any, out DataPoint<out Any>>>,
     @get:JvmName("shouldRequestUserActivityState")
     public val shouldRequestUserActivityState: Boolean,
     public val passiveGoals: Set<PassiveGoal>,
@@ -49,7 +48,7 @@
     internal constructor(
         proto: DataProto.PassiveListenerConfig
     ) : this(
-        proto.dataTypesList.map { DataType(it) }.toSet(),
+        proto.dataTypesList.map { DataType.deltaFromProto(it) }.toSet(),
         proto.includeUserActivityState,
         proto.passiveGoalsList.map { PassiveGoal(it) }.toSet(),
         proto.healthEventTypesList
@@ -59,13 +58,13 @@
 
     /** Builder for [PassiveListenerConfig] instances. */
     public class Builder {
-        private var dataTypes: Set<DataType> = emptySet()
+        private var dataTypes: Set<DataType<*, *>> = emptySet()
         private var requestUserActivityState: Boolean = false
         private var passiveGoals: Set<PassiveGoal> = emptySet()
         private var healthEventTypes: Set<HealthEvent.Type> = emptySet()
 
         /** Sets the requested [DataType]s that should be passively tracked. */
-        public fun setDataTypes(dataTypes: Set<DataType>): Builder {
+        public fun setDataTypes(dataTypes: Set<DataType<*, *>>): Builder {
             this.dataTypes = dataTypes.toSet()
             return this
         }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveMonitoringCapabilities.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveMonitoringCapabilities.kt
index c9ad557..9642743 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveMonitoringCapabilities.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveMonitoringCapabilities.kt
@@ -32,10 +32,10 @@
      *
      * Some data types are only available during exercise (e.g. location) or for measurements.
      */
-    public val supportedDataTypesPassiveMonitoring: Set<DataType>,
+    public val supportedDataTypesPassiveMonitoring: Set<DataType<*, *>>,
 
     /** Set of supported [DataType]s for goal callbacks on this device. */
-    public val supportedDataTypesPassiveGoals: Set<DataType>,
+    public val supportedDataTypesPassiveGoals: Set<DataType<*, *>>,
 
     /** Set of supported [HealthEvent.Type]s on this device. */
     public val supportedHealthEventTypes: Set<HealthEvent.Type>,
@@ -46,8 +46,8 @@
     internal constructor(
         proto: DataProto.PassiveMonitoringCapabilities
     ) : this(
-        proto.supportedDataTypesPassiveMonitoringList.map { DataType(it) }.toSet(),
-        proto.supportedDataTypesPassiveGoalsList.map { DataType(it) }.toSet(),
+        proto.supportedDataTypesPassiveMonitoringList.map { DataType.deltaFromProto(it) }.toSet(),
+        proto.supportedDataTypesPassiveGoalsList.map { DataType.deltaFromProto(it) }.toSet(),
         proto.supportedHealthEventTypesList.mapNotNull { HealthEvent.Type.fromProto(it) }.toSet(),
         proto.supportedUserActivityStatesList.mapNotNull { UserActivityState.fromProto(it) }.toSet()
     )
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveMonitoringUpdate.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveMonitoringUpdate.kt
index 21a7295..76744aa 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveMonitoringUpdate.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveMonitoringUpdate.kt
@@ -29,8 +29,8 @@
  */
 @Suppress("ParcelCreator")
 public class PassiveMonitoringUpdate(
-    /** List of [DataPoint] s from Passive tracking. */
-    public val dataPoints: List<DataPoint>,
+    /** List of [DataPoint]s from Passive tracking. */
+    public val dataPoints: DataPointContainer,
 
     /** The [UserActivityInfo] of the user from Passive tracking. */
     public val userActivityInfoUpdates: List<UserActivityInfo>,
@@ -39,7 +39,7 @@
     internal constructor(
         proto: DataProto.PassiveMonitoringUpdate
     ) : this(
-        proto.dataPointsList.map { DataPoint(it) },
+        DataPointContainer(proto.dataPointsList.map { DataPoint.fromProto(it) }),
         proto.userActivityInfoUpdatesList.map { UserActivityInfo(it) }
     )
 
@@ -54,7 +54,8 @@
     /** @hide */
     override val proto: PassiveMonitoringUpdateProto by lazy {
         PassiveMonitoringUpdateProto.newBuilder()
-            .addAllDataPoints(dataPoints.map { it.proto })
+            .addAllDataPoints(dataPoints.sampleDataPoints.map { it.proto })
+            .addAllDataPoints(dataPoints.intervalDataPoints.map { it.proto })
             .addAllUserActivityInfoUpdates(userActivityInfoUpdates.map { it.proto })
             .build()
     }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/SampleDataPoint.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/SampleDataPoint.kt
new file mode 100644
index 0000000..1324448
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/SampleDataPoint.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import android.os.Bundle
+import androidx.health.services.client.proto.DataProto
+import java.time.Duration
+import java.time.Instant
+
+/**
+ * Data point that represents a piece of data that was valid at a single point in time, for example
+ * heart rate or speed.
+ */
+public class SampleDataPoint<T : Any>(
+    /** The [DataType] this [DataPoint] represents. */
+    public override val dataType: DataType<T, SampleDataPoint<T>>,
+    /** The value of this data point. */
+    public val value: T,
+    /** The time this [DataPoint] represents. */
+    public val timeDurationFromBoot: Duration,
+    /** OEM specific data. In general, this should not be relied upon by non-preloaded apps. */
+    public val metadata: Bundle = Bundle(),
+    /** Accuracy of this DataPoint. */
+    public val accuracy: DataPointAccuracy? = null,
+) : DataPoint<T>(dataType) {
+
+    internal val proto: DataProto.DataPoint by lazy {
+        val builder =
+            DataProto.DataPoint.newBuilder()
+                .setDataType(dataType.proto)
+                .setValue(dataType.toProtoFromValue(value))
+                .setStartDurationFromBootMs(timeDurationFromBoot.toMillis())
+                .setEndDurationFromBootMs(timeDurationFromBoot.toMillis())
+                .setMetaData(BundlesUtil.toProto(metadata))
+
+        accuracy?.let { builder.setAccuracy(it.proto) }
+
+        builder.build()
+    }
+
+    /**
+     * Returns the time [Instant] of this [DataPoint], knowing the time at which the system booted.
+     *
+     * @param bootInstant the [Instant] at which the system booted, this can be computed by
+     * `Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime()) `
+     */
+    public fun getTimeInstant(bootInstant: Instant): Instant {
+        return bootInstant.plus(timeDurationFromBoot)
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is SampleDataPoint<*>) return false
+
+        if (dataType != other.dataType) return false
+        if (value != other.value) return false
+        if (accuracy != other.accuracy) return false
+        if (timeDurationFromBoot != other.timeDurationFromBoot) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = value.hashCode()
+        result = 31 * result + timeDurationFromBoot.hashCode()
+        result = 31 * result + metadata.hashCode()
+        result = 31 * result + (accuracy?.hashCode() ?: 0)
+        return result
+    }
+
+    internal companion object {
+        @Suppress("UNCHECKED_CAST")
+        internal fun fromProto(proto: DataProto.DataPoint): SampleDataPoint<*> {
+            val dataType =
+                DataType.deltaFromProto(proto.dataType) as DataType<Any, SampleDataPoint<Any>>
+            return SampleDataPoint(
+                dataType,
+                dataType.toValueFromProto(proto.value),
+                Duration.ofMillis(proto.startDurationFromBootMs),
+                metadata = BundlesUtil.fromProto(proto.metaData),
+                accuracy = if (proto.hasAccuracy()) {
+                    DataPointAccuracy.fromProto(proto.accuracy)
+                } else {
+                    null
+                },
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/StatisticalDataPoint.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/StatisticalDataPoint.kt
index 177670b..5c7855e 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/StatisticalDataPoint.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/StatisticalDataPoint.kt
@@ -1,66 +1,72 @@
+/*
+ * Copyright 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.health.services.client.data
 
-import android.os.Parcelable
 import androidx.health.services.client.proto.DataProto
-import androidx.health.services.client.proto.DataProto.AggregateDataPoint.StatisticalDataPoint as StatisticalDataPointProto
 import java.time.Instant
 
 /**
- * An [AggregateDataPoint] containing the statistical aggregates [min], [max], and [average] for the
- * type [dataType] between [startTime] and [endTime]. This should generally correspond with
- * [DataPoint]s of type [DataType.TimeType.SAMPLE].
+ * Data point that represents statistics on [SampleDataPoint]s between [start] and [end], though it
+ * is not required to request samples separately.
  */
-@Suppress("ParcelCreator")
-public class StatisticalDataPoint(
-    public val startTime: Instant,
-    public val endTime: Instant,
-    public val dataType: DataType,
-    public val min: Value,
-    public val max: Value,
-    public val average: Value,
-) : AggregateDataPoint() {
+class StatisticalDataPoint<T : Number>(
+    /** The [DataType] this [DataPoint] represents. */
+    dataType: AggregateDataType<T, StatisticalDataPoint<T>>,
+    /** The minimum observed value between [start] and [end]. */
+    val min: T,
+    /** The maximum observed value between [start] and [end]. */
+    val max: T,
+    /** The average observed value between [start] and [end]. */
+    val average: T,
+    /** The beginning of time this point covers.  */
+    val start: Instant,
+    /** The end time this point covers.  */
+    val end: Instant
+) : DataPoint<T>(dataType) {
 
-    internal constructor(
-        proto: DataProto.AggregateDataPoint
-    ) : this(
-        Instant.ofEpochMilli(proto.statisticalDataPoint.startTimeEpochMs),
-        Instant.ofEpochMilli(proto.statisticalDataPoint.endTimeEpochMs),
-        DataType(proto.statisticalDataPoint.dataType),
-        Value(proto.statisticalDataPoint.minValue),
-        Value(proto.statisticalDataPoint.maxValue),
-        Value(proto.statisticalDataPoint.avgValue)
-    )
-
-    /** @hide */
-    override val proto: DataProto.AggregateDataPoint by lazy {
+    internal val proto: DataProto.AggregateDataPoint by lazy {
         DataProto.AggregateDataPoint.newBuilder()
             .setStatisticalDataPoint(
-                StatisticalDataPointProto.newBuilder()
-                    .setStartTimeEpochMs(startTime.toEpochMilli())
-                    .setEndTimeEpochMs(endTime.toEpochMilli())
+                DataProto.AggregateDataPoint.StatisticalDataPoint.newBuilder()
                     .setDataType(dataType.proto)
-                    .setMinValue(min.proto)
-                    .setMaxValue(max.proto)
-                    .setAvgValue(average.proto)
-                    .build()
-            )
-            .build()
+                    .setMinValue(dataType.toProtoFromValue(min))
+                    .setMaxValue(dataType.toProtoFromValue(max))
+                    .setAvgValue(dataType.toProtoFromValue(average))
+                    .setStartTimeEpochMs(start.toEpochMilli())
+                    .setEndTimeEpochMs(end.toEpochMilli())
+            ).build()
     }
 
-    override fun toString(): String =
-        "StatisticalDataPoint(" +
-            "startTime=$startTime, " +
-            "endTime=$endTime, " +
-            "dataType=$dataType, " +
-            "min=$min, " +
-            "max=$max, " +
-            "average=$average)"
-
-    public companion object {
-        @JvmField
-        public val CREATOR: Parcelable.Creator<StatisticalDataPoint> = newCreator {
-            val proto = DataProto.AggregateDataPoint.parseFrom(it)
-            StatisticalDataPoint(proto)
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        internal fun fromProto(
+            proto: DataProto.AggregateDataPoint.StatisticalDataPoint
+        ): StatisticalDataPoint<*> {
+            val dataType =
+                DataType.aggregateFromProto(proto.dataType)
+                    as AggregateDataType<Number, StatisticalDataPoint<Number>>
+            return StatisticalDataPoint(
+                dataType,
+                min = dataType.toValueFromProto(proto.minValue),
+                max = dataType.toValueFromProto(proto.maxValue),
+                average = dataType.toValueFromProto(proto.avgValue),
+                start = Instant.ofEpochMilli(proto.startTimeEpochMs),
+                end = Instant.ofEpochMilli(proto.endTimeEpochMs)
+            )
         }
     }
-}
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/Value.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/Value.kt
deleted file mode 100644
index cd206c7..0000000
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/Value.kt
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright (C) 2021 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.health.services.client.data
-
-import android.os.Parcelable
-import androidx.health.services.client.proto.DataProto
-import com.google.protobuf.ByteString
-
-/** A [Parcelable] wrapper that can hold a value of a specified type. */
-@Suppress("DataClassPrivateConstructor", "ParcelCreator")
-public class Value internal constructor(proto: DataProto.Value) :
-    ProtoParcelable<DataProto.Value>() {
-
-    /** @hide */
-    override val proto: DataProto.Value by lazy { proto }
-
-    public val format: Int =
-        when (proto.valueCase) {
-            DataProto.Value.ValueCase.BOOL_VAL -> FORMAT_BOOLEAN
-            DataProto.Value.ValueCase.DOUBLE_VAL -> FORMAT_DOUBLE
-            DataProto.Value.ValueCase.LONG_VAL -> FORMAT_LONG
-            DataProto.Value.ValueCase.DOUBLE_ARRAY_VAL -> FORMAT_DOUBLE_ARRAY
-            DataProto.Value.ValueCase.BYTE_ARRAY_VAL -> FORMAT_BYTE_ARRAY
-            else -> throw IllegalStateException("Unexpected format: ${proto.valueCase}")
-        }
-
-    /**
-     * Returns this [Value] as a `String`.
-     *
-     * @throws IllegalStateException if [format] is unknown
-     */
-    override fun toString(): String =
-        "Value(format=$format, " +
-            when (format) {
-                FORMAT_BOOLEAN -> "boolVal=${proto.boolVal})"
-                FORMAT_LONG -> "longVal=${proto.longVal})"
-                FORMAT_DOUBLE -> "doubleVal=${proto.doubleVal})"
-                FORMAT_DOUBLE_ARRAY -> "doubleArrayVal=${proto.doubleArrayVal.doubleArrayList})"
-                FORMAT_BYTE_ARRAY -> "byteArrayVal=${proto.byteArrayVal})"
-                else -> throw IllegalStateException("Unexpected format: ${proto.valueCase}")
-            }
-
-    /**
-     * Returns this [Value] represented as an `long`.
-     *
-     * @throws IllegalStateException if [isLong] is `false`
-     */
-    public fun asLong(): Long {
-        check(isLong) { "Attempted to read value as long, but value is not of type long" }
-        return proto.longVal
-    }
-
-    /**
-     * Returns this [Value] represented as an `boolean`.
-     *
-     * @throws IllegalStateException if [isBoolean] is `false`
-     */
-    public fun asBoolean(): Boolean {
-        check(isBoolean) { "Attempted to read value as boolean, but value is not of type boolean" }
-        return proto.boolVal
-    }
-
-    /**
-     * Returns this [Value] represented as a `double`.
-     *
-     * @throws IllegalStateException if [isDouble] is `false`
-     */
-    public fun asDouble(): Double {
-        check(isDouble) { "Attempted to read value as double, but value is not of type double" }
-        return proto.doubleVal
-    }
-
-    /**
-     * Returns this [Value] as a `ByteArray`.
-     *
-     * @throws IllegalStateException if [isByteArray] is `false`
-     */
-    public fun asByteArray(): ByteArray {
-        check(isByteArray) {
-            "Attempted to read value as ByteArray, but value is not of type ByteArray"
-        }
-        val byteArray = ByteArray(proto.byteArrayVal.size())
-        proto.byteArrayVal.copyTo(byteArray, 0)
-        return byteArray
-    }
-
-    /**
-     * Returns this [Value] represented as a `double[]`.
-     *
-     * @throws IllegalStateException if [isDoubleArray] is `false`
-     */
-    public fun asDoubleArray(): DoubleArray {
-        check(isDoubleArray) {
-            "Attempted to read value as double array, but value is not correct type"
-        }
-        return proto.doubleArrayVal.doubleArrayList.toDoubleArray()
-    }
-
-    /** Whether or not this [Value] can be represented as an `long`. */
-    public val isLong: Boolean
-        get() = format == FORMAT_LONG
-
-    /** Whether or not this [Value] can be represented as an `boolean`. */
-    public val isBoolean: Boolean
-        get() = format == FORMAT_BOOLEAN
-
-    /** Whether or not this [Value] can be represented as a `double`. */
-    public val isDouble: Boolean
-        get() = format == FORMAT_DOUBLE
-
-    /** Whether or not this [Value] can be represented as a `double[]`. */
-    public val isDoubleArray: Boolean
-        get() = format == FORMAT_DOUBLE_ARRAY
-
-    /** Whether or not this [Value] can be represented as a `byteArray`. */
-    public val isByteArray: Boolean
-        get() = format == FORMAT_BYTE_ARRAY
-
-    public companion object {
-        /** The format used for a [Value] represented as a `double`. */
-        public const val FORMAT_DOUBLE: Int = 1
-
-        /** The format used for a [Value] represented as an `long`. */
-        public const val FORMAT_LONG: Int = 2
-
-        /** The format used for a [Value] represented as an `boolean`. */
-        public const val FORMAT_BOOLEAN: Int = 4
-
-        /** The format used for a [Value] represented as a `double[]`. */
-        public const val FORMAT_DOUBLE_ARRAY: Int = 3
-
-        /** The format used for a [Value] represented as a `ByteArray`. */
-        public const val FORMAT_BYTE_ARRAY: Int = 5
-
-        @JvmField
-        public val CREATOR: Parcelable.Creator<Value> = newCreator {
-            Value(DataProto.Value.parseFrom(it))
-        }
-
-        /** Creates a [Value] that represents a `long`. */
-        @JvmStatic
-        public fun ofLong(value: Long): Value =
-            Value(DataProto.Value.newBuilder().setLongVal(value).build())
-
-        /** Creates a [Value] that represents an `boolean`. */
-        @JvmStatic
-        public fun ofBoolean(value: Boolean): Value =
-            Value(DataProto.Value.newBuilder().setBoolVal(value).build())
-
-        /** Creates a [Value] that represents a `double`. */
-        @JvmStatic
-        public fun ofDouble(value: Double): Value =
-            Value(DataProto.Value.newBuilder().setDoubleVal(value).build())
-
-        /** Creates a [Value] that represents a `double[]`. */
-        @JvmStatic
-        public fun ofDoubleArray(vararg doubleArray: Double): Value =
-            Value(
-                DataProto.Value.newBuilder()
-                    .setDoubleArrayVal(
-                        DataProto.Value.DoubleArray.newBuilder()
-                            .addAllDoubleArray(doubleArray.toList())
-                    )
-                    .build()
-            )
-
-        /**
-         * Creates a [Value] that represents a `ByteArray`.
-         *
-         * @param value byte array to represent in the returned [Value]
-         */
-        @JvmStatic
-        public fun ofByteArray(value: ByteArray): Value =
-            Value(DataProto.Value.newBuilder().setByteArrayVal(ByteString.copyFrom(value)).build())
-
-        /**
-         * Compares two [Value]s based on their representation.
-         *
-         * @throws IllegalStateException if `first` and `second` do not share the same format or are
-         * represented as a `double[]`
-         */
-        @JvmStatic
-        public fun compare(first: Value, second: Value): Int {
-            check(first.format == second.format) {
-                "Attempted to compare Values with different formats"
-            }
-            when (first.format) {
-                FORMAT_LONG -> return first.asLong().compareTo(second.asLong())
-                FORMAT_BOOLEAN -> return first.asBoolean().compareTo(second.asBoolean())
-                FORMAT_DOUBLE -> return first.asDouble().compareTo(second.asDouble())
-                FORMAT_DOUBLE_ARRAY ->
-                    throw IllegalStateException(
-                        "Attempted to compare Values with invalid format (double array)"
-                    )
-                FORMAT_BYTE_ARRAY ->
-                    throw IllegalStateException(
-                        "Attempted to compate values with invalid format (byte array)"
-                    )
-                else -> {}
-            }
-            throw IllegalStateException(String.format("Unexpected format: %s", first.format))
-        }
-
-        /**
-         * Adds two [Value]s based on their representation.
-         *
-         * @throws IllegalArgumentException if `first` and `second` do not share the same format or
-         * are represented as a `double[]` or `boolean`
-         */
-        @JvmStatic
-        public fun sum(first: Value, second: Value): Value {
-            require(first.format == second.format) {
-                "Attempted to add Values with different formats"
-            }
-            return when (first.format) {
-                FORMAT_LONG -> ofLong(first.asLong() + second.asLong())
-                FORMAT_DOUBLE -> ofDouble(first.asDouble() + second.asDouble())
-                FORMAT_BOOLEAN ->
-                    throw IllegalArgumentException(
-                        "Attempted to add Values with invalid format (boolean)"
-                    )
-                FORMAT_DOUBLE_ARRAY ->
-                    throw IllegalArgumentException(
-                        "Attempted to add Values with invalid format (double array)"
-                    )
-                FORMAT_BYTE_ARRAY ->
-                    throw IllegalArgumentException(
-                        "Attempted to add Values with invalid format (ByteArray)"
-                    )
-                else -> throw IllegalArgumentException("Unexpected format: ${first.format}")
-            }
-        }
-
-        /**
-         * Subtracts two [Value]s based on their representation (i.e. returns `first` - `second`).
-         *
-         * @throws IllegalArgumentException if `first` and `second` do not share the same format or
-         * are represented as a `double[]` or `boolean`
-         *
-         * @hide
-         */
-        @JvmStatic
-        public fun difference(first: Value, second: Value): Value {
-            require(first.format == second.format) {
-                "Attempted to subtract Values with different formats"
-            }
-            require(first.format == FORMAT_LONG || first.format == FORMAT_DOUBLE) {
-                "Provided values are not supported for difference"
-            }
-            return when (first.format) {
-                FORMAT_LONG -> ofLong(first.asLong() - second.asLong())
-                FORMAT_DOUBLE -> ofDouble(first.asDouble() - second.asDouble())
-                else -> throw IllegalArgumentException("Unexpected format: ${first.format}")
-            }
-        }
-
-        /**
-         * Gets the modulo of two [Value]s based on their representation. (i.e. returns `first` %
-         * `second`)
-         *
-         * @throws IllegalArgumentException if `first` and `second` do not share the same format or
-         * are represented as a `double[]` or `boolean`
-         *
-         * @hide
-         */
-        @JvmStatic
-        public fun modulo(first: Value, second: Value): Value {
-            require(first.format == second.format) {
-                "Attempted to modulo Values with different formats"
-            }
-            require(first.format == FORMAT_LONG || first.format == FORMAT_DOUBLE) {
-                "Provided values are not supported for modulo"
-            }
-
-            return when (first.format) {
-                FORMAT_LONG -> ofLong(first.asLong() % second.asLong())
-                FORMAT_DOUBLE -> ofDouble(first.asDouble() % second.asDouble())
-                else -> throw IllegalArgumentException("Unexpected format: ${first.format}")
-            }
-        }
-
-        /**
-         * Checks if a given [Value] is zero or not.
-         *
-         * @throws IllegalArgumentException if `value` is a `double[]` or `boolean`
-         *
-         * @hide
-         */
-        @JvmStatic
-        public fun isZero(value: Value): Boolean {
-            return when (value.format) {
-                FORMAT_LONG -> value.asLong() == 0L
-                FORMAT_DOUBLE -> value.asDouble() == 0.0
-                else -> throw IllegalArgumentException("Unexpected format: ${value.format}")
-            }
-        }
-    }
-}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/WarmUpConfig.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/WarmUpConfig.kt
index ecfcb56..f7a6a57 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/WarmUpConfig.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/WarmUpConfig.kt
@@ -25,19 +25,19 @@
  * @constructor Creates a new WarmUpConfig for an exercise tracked using Health Services
  *
  * @property exerciseType The active [ExerciseType] user is performing for this exercise
- * @property dataTypes [DataType]s which should be tracked during this exercise
+ * @property dataTypes [DeltaDataType]s which should be tracked during this exercise
  */
 @Suppress("ParcelCreator")
 public class WarmUpConfig(
     public val exerciseType: ExerciseType,
-    public val dataTypes: Set<DataType>,
+    public val dataTypes: Set<DeltaDataType<*, *>>,
 ) : ProtoParcelable<DataProto.WarmUpConfig>() {
 
     internal constructor(
         proto: DataProto.WarmUpConfig
     ) : this(
         ExerciseType.fromProto(proto.exerciseType),
-        proto.dataTypesList.map { DataType(it) }.toSet(),
+        proto.dataTypesList.map { DataType.deltaFromProto(it) }.toSet(),
     )
 
     init {
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/ExerciseUpdateListenerStub.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/ExerciseUpdateListenerStub.kt
index f691251..74d6913 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/ExerciseUpdateListenerStub.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/ExerciseUpdateListenerStub.kt
@@ -18,7 +18,6 @@
 
 import android.util.Log
 import androidx.annotation.GuardedBy
-import androidx.annotation.RestrictTo
 import androidx.health.services.client.ExerciseUpdateCallback
 import androidx.health.services.client.data.Availability
 import androidx.health.services.client.data.DataType
@@ -31,15 +30,12 @@
 import java.util.HashMap
 import java.util.concurrent.Executor
 
-/**
- * A stub implementation for IExerciseUpdateListener.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class ExerciseUpdateListenerStub
-private constructor(private val listener: ExerciseUpdateCallback, private val executor: Executor) :
-    IExerciseUpdateListener.Stub() {
+/** A stub implementation for IExerciseUpdateListener. */
+internal class ExerciseUpdateListenerStub internal constructor(
+    private val listener: ExerciseUpdateCallback,
+    private val executor: Executor,
+    private val requestedDataTypesProvider: () -> Set<DataType<*, *>>
+) : IExerciseUpdateListener.Stub() {
 
     public val listenerKey: ListenerKey = ListenerKey(listener)
 
@@ -57,11 +53,22 @@
                 listener.onLapSummaryReceived(
                     ExerciseLapSummary(proto.lapSummaryResponse.lapSummary)
                 )
-            EventCase.AVAILABILITY_RESPONSE ->
-                listener.onAvailabilityChanged(
-                    DataType(proto.availabilityResponse.dataType),
-                    Availability.fromProto(proto.availabilityResponse.availability)
-                )
+            EventCase.AVAILABILITY_RESPONSE -> {
+                val requestedDataTypes = requestedDataTypesProvider.invoke()
+                if (requestedDataTypes.isEmpty()) {
+                    Log.w(TAG, "Availability received without any requested DataTypes")
+                    return
+                }
+                // The DataType in the Availability Response could be a delta or aggregate; there's
+                // not enough information to distinguish. For example, the developer might have
+                // requested ether or both of HEART_RATE_BPM / HEART_RATE_BPM_STATS. We should
+                // trigger onAvailabilityChanged for all matching data types.
+                val matchingDataTypes = requestedDataTypes.filter {
+                    it.name == proto.availabilityResponse.dataType.name
+                }
+                val availability = Availability.fromProto(proto.availabilityResponse.availability)
+                matchingDataTypes.forEach { listener.onAvailabilityChanged(it, availability) }
+            }
             null, EventCase.EVENT_NOT_SET -> Log.w(TAG, "Received unknown event ${proto.eventCase}")
         }
     }
@@ -71,23 +78,30 @@
      * object is passed by framework to service side of the IPC.
      */
     public class ExerciseUpdateListenerCache private constructor() {
-        @GuardedBy("this")
+        private val listenerLock = Any()
+
+        @GuardedBy("listenerLock")
         private val listeners: MutableMap<ExerciseUpdateCallback, ExerciseUpdateListenerStub> =
             HashMap()
 
-        @Synchronized
         public fun getOrCreate(
             listener: ExerciseUpdateCallback,
-            executor: Executor
+            executor: Executor,
+            requestedDataTypesProvider: () -> Set<DataType<*, *>>
         ): ExerciseUpdateListenerStub {
-            return listeners.getOrPut(listener) { ExerciseUpdateListenerStub(listener, executor) }
+            synchronized(listenerLock) {
+                return listeners.getOrPut(listener) {
+                    ExerciseUpdateListenerStub(listener, executor, requestedDataTypesProvider)
+                }
+            }
         }
 
-        @Synchronized
         public fun remove(
             exerciseUpdateCallback: ExerciseUpdateCallback
         ): ExerciseUpdateListenerStub? {
-            return listeners.remove(exerciseUpdateCallback)
+            synchronized(listenerLock) {
+                return listeners.remove(exerciseUpdateCallback)
+            }
         }
 
         public companion object {
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/MeasureCallbackStub.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/MeasureCallbackStub.kt
index 2f91a4b..18f97db 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/MeasureCallbackStub.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/MeasureCallbackStub.kt
@@ -22,6 +22,7 @@
 import androidx.annotation.VisibleForTesting
 import androidx.health.services.client.MeasureCallback
 import androidx.health.services.client.data.Availability
+import androidx.health.services.client.data.DataPointContainer
 import androidx.health.services.client.data.DataType
 import androidx.health.services.client.impl.event.MeasureCallbackEvent
 import androidx.health.services.client.impl.ipc.internal.ListenerKey
@@ -56,11 +57,11 @@
         when (proto.eventCase) {
             EventCase.DATA_POINT_RESPONSE -> {
                 val dataPointsResponse = DataPointsResponse(proto.dataPointResponse)
-                callback.onDataReceived(dataPointsResponse.dataPoints)
+                callback.onDataReceived(DataPointContainer(dataPointsResponse.dataPoints))
             }
             EventCase.AVAILABILITY_RESPONSE ->
                 callback.onAvailabilityChanged(
-                    DataType(proto.availabilityResponse.dataType),
+                    DataType.deltaFromProto(proto.availabilityResponse.dataType),
                     Availability.fromProto(proto.availabilityResponse.availability)
                 )
             null, EventCase.EVENT_NOT_SET -> Log.w(TAG, "Received unknown event ${proto.eventCase}")
@@ -77,7 +78,7 @@
 
         @Synchronized
         public fun getOrCreate(
-            dataType: DataType,
+            dataType: DataType<*, *>,
             executor: Executor,
             measureCallback: MeasureCallback
         ): MeasureCallbackStub {
@@ -100,7 +101,7 @@
 
         @Synchronized
         public fun remove(
-            dataType: DataType,
+            dataType: DataType<*, *>,
             measureCallback: MeasureCallback
         ): MeasureCallbackStub? {
             val callbackKey = MeasureCallbackKey(dataType, measureCallback)
@@ -113,7 +114,7 @@
     }
 
     private data class MeasureCallbackKey(
-        private val dataType: DataType,
+        private val dataType: DataType<*, *>,
         private val measureCallback: MeasureCallback
     )
 
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/PassiveListenerCallbackStub.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/PassiveListenerCallbackStub.kt
index 9402919..6042029 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/PassiveListenerCallbackStub.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/PassiveListenerCallbackStub.kt
@@ -49,7 +49,7 @@
         when (proto.eventCase) {
             PASSIVE_UPDATE_RESPONSE -> {
                 val response = PassiveMonitoringUpdateResponse(proto.passiveUpdateResponse)
-                if (!response.passiveMonitoringUpdate.dataPoints.isEmpty()) {
+                if (response.passiveMonitoringUpdate.dataPoints.dataPoints.isNotEmpty()) {
                     callback.onNewDataPointsReceived(response.passiveMonitoringUpdate.dataPoints)
                 }
                 for (userActivityInfo in response.passiveMonitoringUpdate.userActivityInfoUpdates) {
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedExerciseClient.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedExerciseClient.kt
index 33fd728..94dd9c5 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedExerciseClient.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedExerciseClient.kt
@@ -17,10 +17,12 @@
 package androidx.health.services.client.impl
 
 import android.content.Context
+import androidx.annotation.GuardedBy
 import androidx.annotation.RestrictTo
 import androidx.core.content.ContextCompat
 import androidx.health.services.client.ExerciseClient
 import androidx.health.services.client.ExerciseUpdateCallback
+import androidx.health.services.client.data.DataType
 import androidx.health.services.client.data.ExerciseCapabilities
 import androidx.health.services.client.data.ExerciseConfig
 import androidx.health.services.client.data.ExerciseGoal
@@ -64,13 +66,24 @@
         { service -> service.apiVersion }
     ) {
 
+    private val requestedDataTypesLock = Any()
+    @GuardedBy("requestedDataTypesLock")
+    private val requestedDataTypes: MutableSet<DataType<*, *>> = mutableSetOf()
     private val packageName = context.packageName
 
     override fun prepareExerciseAsync(configuration: WarmUpConfig): ListenableFuture<Void> =
         execute { service, resultFuture ->
             service.prepareExercise(
                 PrepareExerciseRequest(packageName, configuration),
-                StatusCallback(resultFuture)
+                object : StatusCallback(resultFuture) {
+                    override fun onSuccess() {
+                        synchronized(requestedDataTypesLock) {
+                            requestedDataTypes.clear()
+                            requestedDataTypes.addAll(configuration.dataTypes)
+                        }
+                        super.onSuccess()
+                    }
+                }
             )
         }
 
@@ -78,7 +91,15 @@
         execute { service, resultFuture ->
             service.startExercise(
                 StartExerciseRequest(packageName, configuration),
-                StatusCallback(resultFuture)
+                object : StatusCallback(resultFuture) {
+                    override fun onSuccess() {
+                        synchronized(requestedDataTypesLock) {
+                            requestedDataTypes.clear()
+                            requestedDataTypes.addAll(configuration.dataTypes)
+                        }
+                        super.onSuccess()
+                    }
+                }
             )
         }
 
@@ -122,7 +143,12 @@
         val listenerStub =
             ExerciseUpdateListenerStub.ExerciseUpdateListenerCache.INSTANCE.getOrCreate(
                 callback,
-                executor
+                executor,
+                requestedDataTypesProvider = {
+                    synchronized(requestedDataTypesLock) {
+                        requestedDataTypes
+                    }
+                }
             )
         val future =
             registerListener(listenerStub.listenerKey) { service, result: SettableFuture<Void?> ->
@@ -155,7 +181,9 @@
         }
     }
 
-    override fun addGoalToActiveExerciseAsync(exerciseGoal: ExerciseGoal): ListenableFuture<Void> =
+    override fun addGoalToActiveExerciseAsync(
+        exerciseGoal: ExerciseGoal<*>
+    ): ListenableFuture<Void> =
         execute { service, resultFuture ->
             service.addGoalToActiveExercise(
                 ExerciseGoalRequest(packageName, exerciseGoal),
@@ -164,7 +192,7 @@
         }
 
     override fun removeGoalFromActiveExerciseAsync(
-        exerciseGoal: ExerciseGoal
+        exerciseGoal: ExerciseGoal<*>
     ): ListenableFuture<Void> = execute { service, resultFuture ->
         service.removeGoalFromActiveExercise(
             ExerciseGoalRequest(packageName, exerciseGoal),
@@ -189,8 +217,8 @@
         )
 
     internal companion object {
-        private const val CLIENT = "HealthServicesExerciseClient"
-        private val CLIENT_CONFIGURATION =
+        internal const val CLIENT = "HealthServicesExerciseClient"
+        internal val CLIENT_CONFIGURATION =
             ClientConfiguration(CLIENT, SERVICE_PACKAGE_NAME, EXERCISE_API_BIND_ACTION)
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedMeasureClient.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedMeasureClient.kt
index 2c5619a..f7f7f76 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedMeasureClient.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedMeasureClient.kt
@@ -22,7 +22,7 @@
 import androidx.core.content.ContextCompat
 import androidx.health.services.client.MeasureCallback
 import androidx.health.services.client.MeasureClient
-import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.DeltaDataType
 import androidx.health.services.client.data.MeasureCapabilities
 import androidx.health.services.client.impl.IpcConstants.MEASURE_API_BIND_ACTION
 import androidx.health.services.client.impl.IpcConstants.SERVICE_PACKAGE_NAME
@@ -60,12 +60,12 @@
         { service -> service.apiVersion }
     ) {
 
-    override fun registerMeasureCallback(dataType: DataType, callback: MeasureCallback) {
+    override fun registerMeasureCallback(dataType: DeltaDataType<*, *>, callback: MeasureCallback) {
         registerMeasureCallback(dataType, ContextCompat.getMainExecutor(context), callback)
     }
 
     override fun registerMeasureCallback(
-        dataType: DataType,
+        dataType: DeltaDataType<*, *>,
         executor: Executor,
         callback: MeasureCallback
     ) {
@@ -94,7 +94,7 @@
     }
 
     override fun unregisterMeasureCallbackAsync(
-        dataType: DataType,
+        dataType: DeltaDataType<*, *>,
         callback: MeasureCallback
     ): ListenableFuture<Void> {
         val callbackStub =
@@ -118,8 +118,8 @@
         )
 
     internal companion object {
-        const val CLIENT = "HealthServicesMeasureClient"
-        private val CLIENT_CONFIGURATION =
+        internal const val CLIENT = "HealthServicesMeasureClient"
+        internal val CLIENT_CONFIGURATION =
             ClientConfiguration(CLIENT, SERVICE_PACKAGE_NAME, MEASURE_API_BIND_ACTION)
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClient.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClient.kt
index f818b2b..4cf36a7 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClient.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClient.kt
@@ -159,9 +159,9 @@
             ContextCompat.getMainExecutor(applicationContext)
         )
 
-    private companion object {
-        const val CLIENT = "HealthServicesPassiveMonitoringClient"
-        private val CLIENT_CONFIGURATION =
+    internal companion object {
+        internal const val CLIENT = "HealthServicesPassiveMonitoringClient"
+        internal val CLIENT_CONFIGURATION =
             ClientConfiguration(CLIENT, SERVICE_PACKAGE_NAME, PASSIVE_API_BIND_ACTION)
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/ExerciseUpdateListenerEvent.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/ExerciseUpdateListenerEvent.kt
index 87e9046..7b4a169 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/ExerciseUpdateListenerEvent.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/ExerciseUpdateListenerEvent.kt
@@ -7,12 +7,8 @@
 import androidx.health.services.client.impl.response.ExerciseUpdateResponse
 import androidx.health.services.client.proto.EventsProto.ExerciseUpdateListenerEvent as ListenerProto
 
-/**
- * An event representing an `ExerciseUpdateListener` invocation.
- *
- * @hide
- */
-public class ExerciseUpdateListenerEvent(public override val proto: ListenerProto) :
+/** An event representing an [ExerciseUpdateListener] invocation. */
+internal class ExerciseUpdateListenerEvent(override val proto: ListenerProto) :
     ProtoParcelable<ListenerProto>() {
 
     public companion object {
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/MeasureCallbackEvent.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/MeasureCallbackEvent.kt
index 4170ff1..30c1849 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/MeasureCallbackEvent.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/MeasureCallbackEvent.kt
@@ -22,16 +22,14 @@
             MeasureCallbackEvent(ListenerProto.parseFrom(it))
         }
 
-        @JvmStatic
-        public fun createDataPointsUpdateEvent(
+        internal fun createDataPointsUpdateEvent(
             dataPointsResponse: DataPointsResponse
         ): MeasureCallbackEvent =
             MeasureCallbackEvent(
                 ListenerProto.newBuilder().setDataPointResponse(dataPointsResponse.proto).build()
             )
 
-        @JvmStatic
-        public fun createAvailabilityUpdateEvent(
+        internal fun createAvailabilityUpdateEvent(
             availability: AvailabilityResponse
         ): MeasureCallbackEvent =
             MeasureCallbackEvent(
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/StatusCallback.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/StatusCallback.kt
index 0618caf..874eec1 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/StatusCallback.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/internal/StatusCallback.kt
@@ -17,6 +17,7 @@
 package androidx.health.services.client.impl.internal
 
 import android.os.RemoteException
+import androidx.annotation.CallSuper
 import androidx.annotation.RestrictTo
 import com.google.common.util.concurrent.SettableFuture
 
@@ -26,15 +27,17 @@
  * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-public class StatusCallback(private val resultFuture: SettableFuture<Void?>) :
+internal open class StatusCallback(private val resultFuture: SettableFuture<Void?>) :
     IStatusCallback.Stub() {
 
     @Throws(RemoteException::class)
+    @CallSuper
     override fun onSuccess() {
         resultFuture.set(null)
     }
 
     @Throws(RemoteException::class)
+    @CallSuper
     override fun onFailure(msg: String) {
         resultFuture.setException(Exception(msg))
     }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/ExerciseGoalRequest.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/ExerciseGoalRequest.kt
index c1f22a7..4838af7 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/ExerciseGoalRequest.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/ExerciseGoalRequest.kt
@@ -27,7 +27,7 @@
  * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-public data class ExerciseGoalRequest(val packageName: String, val exerciseGoal: ExerciseGoal) :
+public data class ExerciseGoalRequest(val packageName: String, val exerciseGoal: ExerciseGoal<*>) :
     Parcelable {
     override fun describeContents(): Int = 0
 
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/MeasureRegistrationRequest.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/MeasureRegistrationRequest.kt
index 7f9f61f..dd15164 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/MeasureRegistrationRequest.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/MeasureRegistrationRequest.kt
@@ -30,7 +30,7 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 public class MeasureRegistrationRequest(
     public val packageName: String,
-    public val dataType: DataType,
+    public val dataType: DataType<*, *>,
 ) : ProtoParcelable<RequestsProto.MeasureRegistrationRequest>() {
 
     override val proto: RequestsProto.MeasureRegistrationRequest by lazy {
@@ -44,7 +44,7 @@
         @JvmField
         public val CREATOR: Parcelable.Creator<MeasureRegistrationRequest> = newCreator { bytes ->
             val proto = RequestsProto.MeasureRegistrationRequest.parseFrom(bytes)
-            MeasureRegistrationRequest(proto.packageName, DataType(proto.dataType))
+            MeasureRegistrationRequest(proto.packageName, DataType.deltaFromProto(proto.dataType))
         }
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/MeasureUnregistrationRequest.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/MeasureUnregistrationRequest.kt
index 02c9d8f..ee85ba4 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/MeasureUnregistrationRequest.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/MeasureUnregistrationRequest.kt
@@ -30,7 +30,7 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 public class MeasureUnregistrationRequest(
     public val packageName: String,
-    public val dataType: DataType,
+    public val dataType: DataType<*, *>,
 ) : ProtoParcelable<RequestsProto.MeasureUnregistrationRequest>() {
 
     override val proto: RequestsProto.MeasureUnregistrationRequest by lazy {
@@ -44,7 +44,7 @@
         @JvmField
         public val CREATOR: Parcelable.Creator<MeasureUnregistrationRequest> = newCreator { bytes ->
             val proto = RequestsProto.MeasureUnregistrationRequest.parseFrom(bytes)
-            MeasureUnregistrationRequest(proto.packageName, DataType(proto.dataType))
+            MeasureUnregistrationRequest(proto.packageName, DataType.deltaFromProto(proto.dataType))
         }
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/StartExerciseRequest.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/StartExerciseRequest.kt
index aab8d00..fab794b 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/StartExerciseRequest.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/request/StartExerciseRequest.kt
@@ -36,7 +36,7 @@
     override val proto: RequestsProto.StartExerciseRequest by lazy {
         RequestsProto.StartExerciseRequest.newBuilder()
             .setPackageName(packageName)
-            .setConfig(exerciseConfig.proto)
+            .setConfig(exerciseConfig.toProto())
             .build()
     }
 
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/AvailabilityResponse.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/AvailabilityResponse.kt
index d20636b..abd811f 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/AvailabilityResponse.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/AvailabilityResponse.kt
@@ -17,10 +17,10 @@
 package androidx.health.services.client.impl.response
 
 import android.os.Parcelable
-import androidx.annotation.RestrictTo
 import androidx.health.services.client.data.Availability
 import androidx.health.services.client.data.DataType
 import androidx.health.services.client.data.DataTypeAvailability
+import androidx.health.services.client.data.DeltaDataType
 import androidx.health.services.client.data.LocationAvailability
 import androidx.health.services.client.data.ProtoParcelable
 import androidx.health.services.client.proto.DataProto.Availability.AvailabilityCase.AVAILABILITY_NOT_SET
@@ -31,13 +31,10 @@
 /**
  * Response sent on MeasureCallback and ExerciseUpdateListener with a [DataType] and its associated
  * [Availability] status.
- *
- * @hide
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class AvailabilityResponse(
+internal class AvailabilityResponse(
     /** [DataType] of the [AvailabilityResponse]. */
-    public val dataType: DataType,
+    public val dataType: DeltaDataType<*, *>,
     /** [Availability] of the [AvailabilityResponse]. */
     public val availability: Availability,
 ) : ProtoParcelable<ResponsesProto.AvailabilityResponse>() {
@@ -61,7 +58,8 @@
                         LocationAvailability.fromProto(proto.availability.locationAvailability)
                     null, AVAILABILITY_NOT_SET -> DataTypeAvailability.UNKNOWN
                 }
-            AvailabilityResponse(DataType(proto.dataType), availability)
+
+            AvailabilityResponse(DataType.deltaFromProto(proto.dataType), availability)
         }
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/DataPointsResponse.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/DataPointsResponse.kt
index 60506e2..8e0aa07 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/DataPointsResponse.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/DataPointsResponse.kt
@@ -17,29 +17,33 @@
 package androidx.health.services.client.impl.response
 
 import android.os.Parcelable
-import androidx.annotation.RestrictTo
 import androidx.health.services.client.data.DataPoint
+import androidx.health.services.client.data.IntervalDataPoint
 import androidx.health.services.client.data.ProtoParcelable
+import androidx.health.services.client.data.SampleDataPoint
 import androidx.health.services.client.proto.ResponsesProto
 
-/**
- * Response sent on MeasureCallback when new [DataPoints] [DataPoint] are available.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class DataPointsResponse(public val dataPoints: List<DataPoint>) :
+/** Response sent on MeasureCallback when new (non-aggregate) [DataPoint]s are available. */
+internal class DataPointsResponse(public val dataPoints: List<DataPoint<*>>) :
     ProtoParcelable<ResponsesProto.DataPointsResponse>() {
 
-    /** @hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
     public constructor(
         proto: ResponsesProto.DataPointsResponse
-    ) : this(proto.dataPointsList.map { DataPoint(it) })
+    ) : this(proto.dataPointsList.map { DataPoint.fromProto(it) })
 
     override val proto: ResponsesProto.DataPointsResponse by lazy {
         ResponsesProto.DataPointsResponse.newBuilder()
-            .addAllDataPoints(dataPoints.map { it.proto })
+            .addAllDataPoints(
+                dataPoints
+                    .filter { it is IntervalDataPoint || it is SampleDataPoint }
+                    .map {
+                        when (it) {
+                            is IntervalDataPoint -> it.proto
+                            is SampleDataPoint -> it.proto
+                            else -> throw IllegalStateException("Invalid DataPoint type: $it")
+                        }
+                    }
+            )
             .build()
     }
 
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/CumulativeDataPointTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/CumulativeDataPointTest.kt
new file mode 100644
index 0000000..1c419fc
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/CumulativeDataPointTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import com.google.common.truth.Truth
+import java.time.Instant
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class CumulativeDataPointTest {
+    fun Int.instant() = Instant.ofEpochMilli(toLong())
+
+    @Test
+    fun protoRoundTrip() {
+        val proto = CumulativeDataPoint(
+            dataType = DataType.CALORIES_TOTAL,
+            total = 100.0,
+            start = 10.instant(),
+            end = 99.instant(),
+        ).proto
+
+        val dataPoint = CumulativeDataPoint.fromProto(proto.cumulativeDataPoint)
+
+        Truth.assertThat(dataPoint.dataType).isEqualTo(DataType.CALORIES_TOTAL)
+        Truth.assertThat(dataPoint.total).isEqualTo(100.0)
+        Truth.assertThat(dataPoint.start).isEqualTo(10.instant())
+        Truth.assertThat(dataPoint.end).isEqualTo(99.instant())
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/DataPointContainerTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataPointContainerTest.kt
new file mode 100644
index 0000000..b3a56d39
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataPointContainerTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.DataType.Companion.ABSOLUTE_ELEVATION_STATS
+import androidx.health.services.client.data.DataType.Companion.CALORIES_TOTAL
+import androidx.health.services.client.data.DataType.Companion.FLOORS
+import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM
+import androidx.health.services.client.data.DataType.Companion.STEPS
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import java.time.Instant
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class DataPointContainerTest {
+
+    private fun Int.duration() = Duration.ofSeconds(this.toLong())
+    private fun Int.instant() = Instant.ofEpochMilli(this.toLong())
+
+    private val elevationStatsDataPoint = DataPoints.absoluteElevationStats(
+        minAbsoluteElevationMeters = 10.0,
+        maxAbsoluteElevationMeters = 20.0,
+        averageAbsoluteElevationMeters = 15.0,
+        startTime = 10.instant(),
+        endTime = 20.instant()
+    )
+
+    @Test
+    fun getDataReturnsCorrectNumberSimpleInput() {
+        val step1 = DataPoints.steps(5, 1.duration(), 2.duration())
+        val step2 = DataPoints.steps(10, 2.duration(), 10.duration())
+        val container = DataPointContainer(listOf(step1, step2))
+
+        val list: List<IntervalDataPoint<Long>> = container.getData(STEPS)
+
+        assertThat(list).hasSize(2)
+        assertThat(list).containsExactly(step1, step2)
+    }
+
+    @Test
+    fun getDataReturnsCorrectNumberMultipleDataTypes() {
+        val container = DataPointContainer(
+            listOf(
+                DataPoints.steps(5, 1.duration(), 2.duration()),
+                DataPoints.heartRate(130.0, 1.duration()),
+                DataPoints.floors(5.0, 1.duration(), 10.duration()),
+                DataPoints.caloriesTotal(130.0, 10.instant(), 20.instant()),
+                elevationStatsDataPoint,
+                DataPoints.steps(10, 2.duration(), 10.duration()),
+            )
+        )
+
+        val steps: List<IntervalDataPoint<Long>> = container.getData(STEPS)
+        val floors: List<IntervalDataPoint<Double>> = container.getData(FLOORS)
+        val elevationStats: StatisticalDataPoint<Double>? =
+            container.getData(ABSOLUTE_ELEVATION_STATS)
+        val caloriesTotal: CumulativeDataPoint<Double>? =
+            container.getData(CALORIES_TOTAL)
+
+        assertThat(steps).hasSize(2)
+        assertThat(floors).hasSize(1)
+        assertThat(elevationStats).isNotNull()
+        assertThat(caloriesTotal).isNotNull()
+    }
+
+    @Test
+    fun getSampleDataPointsReturnsTheCorrectNumber() {
+        val container = DataPointContainer(
+            listOf(
+                DataPoints.steps(5, 1.duration(), 2.duration()),
+                DataPoints.heartRate(130.0, 1.duration()),
+                DataPoints.floors(5.0, 1.duration(), 10.duration()),
+                DataPoints.caloriesTotal(130.0, 10.instant(), 20.instant()),
+                elevationStatsDataPoint,
+                DataPoints.steps(10, 2.duration(), 10.duration()),
+            )
+        )
+
+        val dataPoints = container.sampleDataPoints
+
+        assertThat(dataPoints).hasSize(1)
+    }
+
+    @Test
+    fun getIntervalDataPointsReturnsTheCorrectNumber() {
+        val container = DataPointContainer(
+            listOf(
+                DataPoints.steps(5, 1.duration(), 2.duration()),
+                DataPoints.heartRate(130.0, 1.duration()),
+                DataPoints.floors(5.0, 1.duration(), 10.duration()),
+                DataPoints.caloriesTotal(130.0, 10.instant(), 20.instant()),
+                elevationStatsDataPoint,
+                DataPoints.steps(10, 2.duration(), 10.duration()),
+            )
+        )
+
+        val dataPoints = container.intervalDataPoints
+
+        assertThat(dataPoints).hasSize(3)
+    }
+
+    @Test
+    fun getCumulativeDataPointsReturnsTheCorrectNumber() {
+        val container = DataPointContainer(
+            listOf(
+                DataPoints.steps(5, 1.duration(), 2.duration()),
+                DataPoints.heartRate(130.0, 1.duration()),
+                DataPoints.floors(5.0, 1.duration(), 10.duration()),
+                DataPoints.caloriesTotal(130.0, 10.instant(), 20.instant()),
+                elevationStatsDataPoint,
+                DataPoints.steps(10, 2.duration(), 10.duration()),
+            )
+        )
+
+        val dataPoints = container.cumulativeDataPoints
+
+        assertThat(dataPoints).hasSize(1)
+    }
+
+    @Test
+    fun getStatisticalDataPointsReturnsTheCorrectNumber() {
+        val container = DataPointContainer(
+            listOf(
+                DataPoints.steps(5, 1.duration(), 2.duration()),
+                DataPoints.heartRate(130.0, 1.duration()),
+                DataPoints.floors(5.0, 1.duration(), 10.duration()),
+                DataPoints.caloriesTotal(130.0, 10.instant(), 20.instant()),
+                elevationStatsDataPoint,
+                DataPoints.steps(10, 2.duration(), 10.duration()),
+            )
+        )
+
+        val dataPoints = container.statisticalDataPoints
+
+        assertThat(dataPoints).hasSize(1)
+    }
+
+    @Test
+    fun dataTypePropertyContainsCorrectDataTypes() {
+        val container = DataPointContainer(
+            listOf(
+                DataPoints.steps(5, 1.duration(), 2.duration()),
+                DataPoints.heartRate(130.0, 1.duration()),
+                DataPoints.floors(5.0, 1.duration(), 10.duration()),
+                DataPoints.caloriesTotal(130.0, 10.instant(), 20.instant()),
+                elevationStatsDataPoint,
+                DataPoints.steps(10, 2.duration(), 10.duration()),
+            )
+        )
+
+        assertThat(container.dataTypes).containsExactly(
+            STEPS,
+            HEART_RATE_BPM,
+            FLOORS,
+            CALORIES_TOTAL,
+            ABSOLUTE_ELEVATION_STATS
+        )
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/DataPointTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataPointTest.kt
new file mode 100644
index 0000000..3da23f0
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataPointTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import android.os.Bundle
+import com.google.common.truth.Truth
+import java.time.Duration
+import java.time.Instant
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class DataPointTest {
+    fun Int.duration() = Duration.ofSeconds(toLong())
+    fun Int.instant() = Instant.ofEpochMilli(toLong())
+
+    @Test
+    fun intervalDataPointProtoRoundTrip() {
+        val proto = IntervalDataPoint(
+            DataType.CALORIES,
+            value = 130.0,
+            startDurationFromBoot = 10.duration(),
+            endDurationFromBoot = 20.duration(),
+            Bundle().apply {
+                putInt("int", 5)
+                putString("string", "value")
+            },
+            accuracy = null // No interval DataPoints have an accuracy component
+        ).proto
+
+        val dataPoint = DataPoint.fromProto(proto) as IntervalDataPoint
+
+        Truth.assertThat(dataPoint.dataType).isEqualTo(DataType.CALORIES)
+        Truth.assertThat(dataPoint.value).isEqualTo(130.0)
+        Truth.assertThat(dataPoint.startDurationFromBoot).isEqualTo(10.duration())
+        Truth.assertThat(dataPoint.endDurationFromBoot).isEqualTo(20.duration())
+        Truth.assertThat(dataPoint.metadata.getInt("int")).isEqualTo(5)
+        Truth.assertThat(dataPoint.metadata.getString("string")).isEqualTo("value")
+        Truth.assertThat(dataPoint.accuracy).isNull()
+    }
+
+    @Test
+    fun sampleDataPointProtoRoundTrip() {
+        val proto = SampleDataPoint(
+            DataType.HEART_RATE_BPM,
+            130.0,
+            20.duration(),
+            Bundle().apply {
+                putInt("int", 5)
+                putString("string", "value")
+            },
+            HeartRateAccuracy(HeartRateAccuracy.SensorStatus.ACCURACY_HIGH)
+        ).proto
+
+        val dataPoint = DataPoint.fromProto(proto) as SampleDataPoint
+
+        Truth.assertThat(dataPoint.dataType).isEqualTo(DataType.HEART_RATE_BPM)
+        Truth.assertThat(dataPoint.value).isEqualTo(130.0)
+        Truth.assertThat(dataPoint.timeDurationFromBoot).isEqualTo(20.duration())
+        Truth.assertThat(dataPoint.metadata.getInt("int")).isEqualTo(5)
+        Truth.assertThat(dataPoint.metadata.getString("string")).isEqualTo("value")
+        Truth.assertThat((dataPoint.accuracy as HeartRateAccuracy).sensorStatus)
+            .isEqualTo(HeartRateAccuracy.SensorStatus.ACCURACY_HIGH)
+    }
+
+    @Test
+    fun cumulativeDataPointProtoRoundTrip() {
+        val proto = CumulativeDataPoint(
+            dataType = DataType.CALORIES_TOTAL,
+            total = 100.0,
+            start = 10.instant(),
+            end = 99.instant(),
+        ).proto
+
+        val dataPoint = DataPoint.fromProto(proto) as CumulativeDataPoint
+
+        Truth.assertThat(dataPoint.dataType).isEqualTo(DataType.CALORIES_TOTAL)
+        Truth.assertThat(dataPoint.total).isEqualTo(100.0)
+        Truth.assertThat(dataPoint.start).isEqualTo(10.instant())
+        Truth.assertThat(dataPoint.end).isEqualTo(99.instant())
+    }
+
+    @Test
+    fun statisticalDataPointProtoRoundTrip() {
+        val proto = StatisticalDataPoint(
+            dataType = DataType.HEART_RATE_BPM_STATS,
+            min = 100.0,
+            max = 175.5,
+            average = 155.0,
+            start = 10.instant(),
+            end = 99.instant(),
+        ).proto
+
+        val dataPoint = DataPoint.fromProto(proto) as StatisticalDataPoint
+
+        Truth.assertThat(dataPoint.dataType).isEqualTo(DataType.HEART_RATE_BPM_STATS)
+        Truth.assertThat(dataPoint.min).isEqualTo(100.0)
+        Truth.assertThat(dataPoint.max).isEqualTo(175.5)
+        Truth.assertThat(dataPoint.average).isEqualTo(155.0)
+        Truth.assertThat(dataPoint.start).isEqualTo(10.instant())
+        Truth.assertThat(dataPoint.end).isEqualTo(99.instant())
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeConditionTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeConditionTest.kt
new file mode 100644
index 0000000..d2a8d71
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeConditionTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.ComparisonType.Companion.GREATER_THAN
+import androidx.health.services.client.data.DataType.Companion.CALORIES_TOTAL
+import androidx.health.services.client.data.DataType.Companion.INCLINE_DURATION
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class DataTypeConditionTest {
+
+    @Test
+    fun protoRoundTripDelta() {
+        val proto = DataTypeCondition(INCLINE_DURATION, 10, GREATER_THAN).proto
+
+        val observed = DataTypeCondition.deltaFromProto(proto)
+
+        assertThat(observed.dataType).isEqualTo(INCLINE_DURATION)
+        assertThat(observed.threshold).isEqualTo(10)
+        assertThat(observed.comparisonType).isEqualTo(GREATER_THAN)
+    }
+
+    @Test
+    fun protoRoundTripAggregate() {
+        val proto = DataTypeCondition(CALORIES_TOTAL, 10.5, GREATER_THAN).proto
+
+        val observed = DataTypeCondition.aggregateFromProto(proto)
+
+        assertThat(observed.dataType).isEqualTo(CALORIES_TOTAL)
+        assertThat(observed.threshold).isEqualTo(10.5)
+        assertThat(observed.comparisonType).isEqualTo(GREATER_THAN)
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt
new file mode 100644
index 0000000..c320002
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt
@@ -0,0 +1,214 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.DataType.Companion.ABSOLUTE_ELEVATION
+import androidx.health.services.client.data.DataType.Companion.ABSOLUTE_ELEVATION_STATS
+import androidx.health.services.client.data.DataType.Companion.ACTIVE_EXERCISE_DURATION_TOTAL
+import androidx.health.services.client.data.DataType.Companion.CALORIES
+import androidx.health.services.client.data.DataType.Companion.CALORIES_TOTAL
+import androidx.health.services.client.data.DataType.Companion.CALORIES_DAILY
+import androidx.health.services.client.data.DataType.Companion.DISTANCE_DAILY
+import androidx.health.services.client.data.DataType.Companion.FLOORS_DAILY
+import androidx.health.services.client.data.DataType.Companion.STEPS_DAILY
+import androidx.health.services.client.data.DataType.Companion.FORMAT_BYTE_ARRAY
+import androidx.health.services.client.data.DataType.Companion.LOCATION
+import androidx.health.services.client.data.DataType.Companion.STEPS
+import androidx.health.services.client.data.DataType.Companion.SWIMMING_LAP_COUNT
+import androidx.health.services.client.data.DataType.TimeType.Companion.UNKNOWN
+import androidx.health.services.client.proto.DataProto
+import androidx.health.services.client.proto.DataProto.DataType.TimeType.TIME_TYPE_UNKNOWN
+import com.google.common.truth.Truth.assertThat
+import kotlin.reflect.KVisibility
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import kotlin.reflect.full.declaredMemberProperties
+import kotlin.reflect.jvm.javaField
+
+@RunWith(RobolectricTestRunner::class)
+internal class DataTypeTest {
+
+    @Test
+    fun aggregateProtoRoundTrip() {
+        val proto = CALORIES_TOTAL.proto
+
+        val dataType = DataType.aggregateFromProto(proto)
+
+        assertThat(dataType).isEqualTo(CALORIES_TOTAL)
+    }
+
+    @Test
+    fun deltaProtoRoundTrip() {
+        val proto = CALORIES.proto
+
+        val dataType = DataType.deltaFromProto(proto)
+
+        assertThat(dataType).isEqualTo(CALORIES)
+    }
+
+    @Test
+    fun dailyProtoRoundTrip() {
+        val proto = CALORIES_DAILY.proto
+
+        val dataType = DataType.deltaFromProto(proto)
+
+        assertThat(dataType).isEqualTo(CALORIES_DAILY)
+    }
+
+    @Test
+    fun equalsDelta() {
+        assertThat(CALORIES).isEqualTo(CALORIES)
+        assertThat(CALORIES).isNotEqualTo(CALORIES_TOTAL)
+        assertThat(CALORIES).isNotEqualTo(STEPS)
+        assertThat(CALORIES).isNotEqualTo(CALORIES_DAILY)
+    }
+
+    @Test
+    fun equalsAggregate() {
+        assertThat(CALORIES_TOTAL).isEqualTo(CALORIES_TOTAL)
+        assertThat(CALORIES_TOTAL).isNotEqualTo(CALORIES)
+        assertThat(CALORIES_TOTAL).isNotEqualTo(CALORIES_DAILY)
+    }
+
+    @Test
+    fun equalsDaily() {
+        assertThat(CALORIES_DAILY).isEqualTo(CALORIES_DAILY)
+        assertThat(CALORIES_DAILY).isNotEqualTo(CALORIES)
+        assertThat(CALORIES_DAILY).isNotEqualTo(CALORIES_TOTAL)
+        assertThat(CALORIES_DAILY).isNotEqualTo(STEPS)
+    }
+
+    @Test
+    fun deltaFromProtoDoesNotThrowExceptionForNewDataType() {
+        val proto = DataProto.DataType.newBuilder()
+            .setName("some unknown type")
+            .setTimeType(TIME_TYPE_UNKNOWN)
+            .setFormat(FORMAT_BYTE_ARRAY)
+            .build()
+
+        val dataType = DataType.deltaFromProto(proto)
+
+        assertThat(dataType.name).isEqualTo("some unknown type")
+        assertThat(dataType.timeType).isEqualTo(UNKNOWN)
+    }
+
+    @Test
+    fun aggregateFromProtoDoesNotThrowExceptionForNewDataType() {
+        val proto = DataProto.DataType.newBuilder()
+            .setName("some unknown type")
+            .setTimeType(TIME_TYPE_UNKNOWN)
+            .setFormat(FORMAT_BYTE_ARRAY)
+            .build()
+
+        val dataType = DataType.aggregateFromProto(proto)
+
+        assertThat(dataType.name).isEqualTo("some unknown type")
+        assertThat(dataType.timeType).isEqualTo(UNKNOWN)
+    }
+
+    @Test
+    fun deltaAndAggregateShouldContainBothIfAvailable() {
+        // The proto representation of deltas and aggregates is identical.
+        val deltaProto = CALORIES_TOTAL.proto
+
+        val list = DataType.deltaAndAggregateFromProto(deltaProto)
+
+        assertThat(list).containsExactly(CALORIES, CALORIES_TOTAL)
+    }
+
+    @Test
+    fun deltaAndAggregateShouldOnlyContainDeltaIfNoAggregateIsAvailable() {
+        // The proto representation of deltas and aggregates is identical, but LOCATION does not
+        // have an aggregate version
+        val deltaProto = LOCATION.proto
+
+        val list = DataType.deltaAndAggregateFromProto(deltaProto)
+
+        assertThat(list).containsExactly(LOCATION)
+    }
+
+    @Test
+    fun deltaAndAggregateShouldContainBothForNewDataTypes() {
+        val proto = DataProto.DataType.newBuilder()
+            .setName("new")
+            .setTimeType(TIME_TYPE_UNKNOWN)
+            .setFormat(FORMAT_BYTE_ARRAY)
+            .build()
+
+        val list = DataType.deltaAndAggregateFromProto(proto)
+
+        val item1 = list[0]
+        val item2 = list[1]
+        assertThat(item1.name).isEqualTo("new")
+        assertThat(item1.timeType).isEqualTo(UNKNOWN)
+        assertThat(item1.valueClass).isEqualTo(ByteArray::class)
+        assertThat(item1::class).isEqualTo(DeltaDataType::class)
+        assertThat(item1.isAggregate).isFalse()
+        assertThat(item2.name).isEqualTo("new")
+        assertThat(item2.timeType).isEqualTo(UNKNOWN)
+        assertThat(item2.valueClass).isEqualTo(ByteArray::class)
+        assertThat(item2::class).isEqualTo(AggregateDataType::class)
+        assertThat(item2.isAggregate).isTrue()
+    }
+
+    @Test
+    fun aggregatesShouldContainAllExpectedDeltas() {
+        val aggregateNames = DataType.aggregateDataTypes.toMutableSet().apply {
+            // Active duration is special cased and does not have a delta form. Developers get the
+            // Active duration not from a DataPoint, but instead from from a property in the
+            // ExerciseUpdate directly. The DataType is only used to enable setting an ExerciseGoal,
+            // which only operate on aggregates. So, we do not have a delta datatype for this and
+            // instead only have an aggregate.
+            remove(ACTIVE_EXERCISE_DURATION_TOTAL)
+        }.map { it.name }
+        // Certain deltas are expected to not have aggregates
+        val deltaNames = DataType.deltaDataTypes.toMutableSet().apply {
+            // Swimming lap count is already aggregated
+            remove(SWIMMING_LAP_COUNT)
+            // Aggregate location doesn't make a lot of sense
+            remove(LOCATION)
+            // Dailies are used in passive and passive only deals with deltas
+            remove(CALORIES_DAILY)
+            remove(DISTANCE_DAILY)
+            remove(FLOORS_DAILY)
+            remove(STEPS_DAILY)
+        }.map { it.name }
+
+        assertThat(aggregateNames).containsExactlyElementsIn(deltaNames)
+    }
+
+    @Test
+    fun allDataTypesShouldBeInEitherDeltaOrAggregateDataTypeSets() {
+        // If this test fails, you haven't added a new DataType to one of the sets below:
+        val joinedSet = DataType.deltaDataTypes + DataType.aggregateDataTypes
+        // Use reflection get all public data types defined. Reflection is yuck, but not being able
+        // to return the same DataType object from a proto is worse.
+        val dataTypesThroughReflection = DataType.Companion::class
+            .declaredMemberProperties
+            .filter { it.visibility == KVisibility.PUBLIC }
+            .filter {
+                it.javaField != null && DataType::class.java.isAssignableFrom(it.javaField!!.type)
+            }
+            .map { it.get(DataType.Companion) }
+
+        assertThat(dataTypesThroughReflection).contains(LOCATION)
+        assertThat(dataTypesThroughReflection).contains(ABSOLUTE_ELEVATION)
+        assertThat(dataTypesThroughReflection).contains(ABSOLUTE_ELEVATION_STATS)
+        assertThat(joinedSet).containsExactlyElementsIn(dataTypesThroughReflection)
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseConfigTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseConfigTest.kt
new file mode 100644
index 0000000..b397c09
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseConfigTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.ComparisonType.Companion.GREATER_THAN
+import androidx.health.services.client.data.DataType.Companion.DISTANCE_TOTAL
+import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM
+import androidx.health.services.client.data.DataType.Companion.LOCATION
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class ExerciseConfigTest {
+    @Test
+    fun protoRoundTrip() {
+        val proto = ExerciseConfig(
+            ExerciseType.RUNNING,
+            setOf(LOCATION, DISTANCE_TOTAL, HEART_RATE_BPM),
+            isAutoPauseAndResumeEnabled = true,
+            isGpsEnabled = true,
+            exerciseGoals = listOf(
+                ExerciseGoal.createOneTimeGoal(
+                    DataTypeCondition(DISTANCE_TOTAL, 50.0, GREATER_THAN)
+                ),
+                ExerciseGoal.createOneTimeGoal(
+                    DataTypeCondition(DISTANCE_TOTAL, 150.0, GREATER_THAN)
+                ),
+            ),
+        ).toProto()
+
+        val config = ExerciseConfig(proto)
+
+        assertThat(config.exerciseType).isEqualTo(ExerciseType.RUNNING)
+        assertThat(config.dataTypes).containsExactly(LOCATION, HEART_RATE_BPM, DISTANCE_TOTAL)
+        assertThat(config.isAutoPauseAndResumeEnabled).isEqualTo(true)
+        assertThat(config.isGpsEnabled).isEqualTo(true)
+        assertThat(config.exerciseGoals[0].dataTypeCondition.dataType).isEqualTo(DISTANCE_TOTAL)
+        assertThat(config.exerciseGoals[0].dataTypeCondition.threshold).isEqualTo(50.0)
+        assertThat(config.exerciseGoals[0].dataTypeCondition.comparisonType).isEqualTo(GREATER_THAN)
+        assertThat(config.exerciseGoals[1].dataTypeCondition.dataType).isEqualTo(DISTANCE_TOTAL)
+        assertThat(config.exerciseGoals[1].dataTypeCondition.threshold).isEqualTo(150.0)
+        assertThat(config.exerciseGoals[1].dataTypeCondition.comparisonType).isEqualTo(GREATER_THAN)
+    }
+
+    @Test
+    fun throwsWhenLocationIsRequestedButGpsNotEnabled() {
+        assertThrows(IllegalArgumentException::class.java) {
+            ExerciseConfig(
+                ExerciseType.RUNNING,
+                setOf(LOCATION),
+                isAutoPauseAndResumeEnabled = true,
+                isGpsEnabled = false,
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseGoalTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseGoalTest.kt
new file mode 100644
index 0000000..08c73d9
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseGoalTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.ComparisonType.Companion.GREATER_THAN
+import androidx.health.services.client.data.DataType.Companion.DISTANCE_TOTAL
+import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM_STATS
+import androidx.health.services.client.data.ExerciseGoalType.Companion.MILESTONE
+import androidx.health.services.client.data.ExerciseGoalType.Companion.ONE_TIME_GOAL
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class ExerciseGoalTest {
+
+    @Test
+    fun protoRoundTrip_oneTime() {
+        val proto = ExerciseGoal.createOneTimeGoal(
+            DataTypeCondition(HEART_RATE_BPM_STATS, 145.0, GREATER_THAN)
+        ).proto
+
+        val goal = ExerciseGoal.fromProto(proto)
+
+        assertThat(goal.exerciseGoalType).isEqualTo(ONE_TIME_GOAL)
+        assertThat(goal.dataTypeCondition.dataType).isEqualTo(HEART_RATE_BPM_STATS)
+        assertThat(goal.dataTypeCondition.threshold).isEqualTo(145.0)
+        assertThat(goal.dataTypeCondition.comparisonType).isEqualTo(GREATER_THAN)
+        assertThat(goal.period).isEqualTo(null)
+    }
+
+    @Test
+    fun protoRoundTrip_milestone() {
+        val proto = ExerciseGoal.createMilestone(
+            DataTypeCondition(DISTANCE_TOTAL, 500.0, GREATER_THAN),
+            period = 1000.0
+        ).proto
+
+        val goal = ExerciseGoal.fromProto(proto)
+
+        assertThat(goal.exerciseGoalType).isEqualTo(MILESTONE)
+        assertThat(goal.dataTypeCondition.dataType).isEqualTo(DISTANCE_TOTAL)
+        assertThat(goal.dataTypeCondition.threshold).isEqualTo(500.0)
+        assertThat(goal.dataTypeCondition.comparisonType).isEqualTo(GREATER_THAN)
+        assertThat(goal.period).isEqualTo(1000.0)
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseLapSummaryTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseLapSummaryTest.kt
new file mode 100644
index 0000000..ae1dbef
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseLapSummaryTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.DataType.Companion.CALORIES_TOTAL
+import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM_STATS
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import java.time.Instant
+import java.time.temporal.ChronoUnit
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class ExerciseLapSummaryTest {
+
+    @Test
+    fun protoRoundTrip() {
+        val startTime = Instant.parse("2021-03-02T14:00:00.00Z")
+        val endTime = Instant.parse("2021-03-02T14:12:00.00Z").truncatedTo(ChronoUnit.SECONDS)
+        val proto = ExerciseLapSummary(
+            lapCount = 1,
+            startTime = startTime,
+            endTime = endTime,
+            activeDuration = Duration.ofMinutes(10),
+            lapMetrics = DataPointContainer(
+                listOf(
+                    DataPoints.caloriesTotal(
+                        kilocalories = 1000.0,
+                        startTime = startTime,
+                        endTime = endTime
+                    ),
+                    DataPoints.heartRateStats(
+                        minBpm = 50.0,
+                        maxBpm = 150.0,
+                        averageBpm = 100.0,
+                        startTime = startTime,
+                        endTime = endTime
+                    )
+                )
+            )
+        ).proto
+
+        val summary = ExerciseLapSummary(proto)
+
+        assertThat(summary.lapCount).isEqualTo(1)
+        assertThat(summary.startTime).isEqualTo(startTime)
+        assertThat(summary.endTime).isEqualTo(endTime)
+        assertThat(summary.activeDuration).isEqualTo(Duration.ofMinutes(10))
+        val calories = summary.lapMetrics.getData(CALORIES_TOTAL)!!
+        assertThat(calories.dataType).isEqualTo(CALORIES_TOTAL)
+        assertThat(calories.total).isEqualTo(1000.0)
+        assertThat(calories.start).isEqualTo(startTime)
+        assertThat(calories.end).isEqualTo(endTime)
+        val hrStats = summary.lapMetrics.getData(HEART_RATE_BPM_STATS)!!
+        assertThat(hrStats.dataType).isEqualTo(HEART_RATE_BPM_STATS)
+        assertThat(hrStats.min).isEqualTo(50.0)
+        assertThat(hrStats.max).isEqualTo(150.0)
+        assertThat(hrStats.average).isEqualTo(100.0)
+        assertThat(hrStats.start).isEqualTo(startTime)
+        assertThat(hrStats.end).isEqualTo(endTime)
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseUpdateTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseUpdateTest.kt
new file mode 100644
index 0000000..34767b03
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseUpdateTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.ComparisonType.Companion.GREATER_THAN_OR_EQUAL
+import androidx.health.services.client.data.DataType.Companion.CALORIES_TOTAL
+import androidx.health.services.client.data.ExerciseGoal.Companion.createOneTimeGoal
+import androidx.health.services.client.data.ExerciseType.Companion.WALKING
+import androidx.health.services.client.data.ExerciseUpdate.ActiveDurationCheckpoint
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import java.time.Instant
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class ExerciseUpdateTest {
+
+    fun Int.instant() = Instant.ofEpochMilli(toLong())
+    fun Int.duration() = Duration.ofSeconds(toLong())
+
+    @Test
+    public fun protoRoundTrip() {
+        val goal = createOneTimeGoal(
+            DataTypeCondition(CALORIES_TOTAL, 125.0, GREATER_THAN_OR_EQUAL)
+        )
+        val proto = ExerciseUpdate(
+            startTime = 10.instant(),
+            activeDuration = 60.duration(),
+            updateDurationFromBoot = 42.duration(),
+            latestMetrics = DataPointContainer(
+                listOf(DataPoints.calories(130.0, 15.duration(), 35.duration()))
+            ),
+            latestAchievedGoals = setOf(goal),
+            latestMilestoneMarkerSummaries = setOf(
+                MilestoneMarkerSummary(
+                    15.instant(),
+                    40.instant(),
+                    20.duration(),
+                    goal,
+                    DataPointContainer(
+                        listOf(DataPoints.calories(130.0, 15.duration(), 35.duration()))
+                    )
+                )
+            ),
+            exerciseConfig = ExerciseConfig(
+                WALKING,
+                setOf(CALORIES_TOTAL),
+                isAutoPauseAndResumeEnabled = true,
+                isGpsEnabled = false,
+                exerciseGoals = listOf(goal)
+            ),
+            activeDurationCheckpoint = ActiveDurationCheckpoint(42.instant(), 30.duration()),
+            exerciseStateInfo = ExerciseStateInfo(ExerciseState.ACTIVE, ExerciseEndReason.UNKNOWN)
+        ).proto
+
+        val update = ExerciseUpdate(proto)
+
+        val caloriesDataPoint = update.latestMetrics.getData(DataType.CALORIES).first()
+        val markerSummary = update.latestMilestoneMarkerSummaries.first()
+        assertThat(update.startTime).isEqualTo(10.instant())
+        assertThat(update.activeDuration).isEqualTo(60.duration())
+        assertThat(update.getUpdateDurationFromBoot()).isEqualTo(42.duration())
+        assertThat(caloriesDataPoint.value).isEqualTo(130.0)
+        assertThat(caloriesDataPoint.startDurationFromBoot).isEqualTo(15.duration())
+        assertThat(caloriesDataPoint.endDurationFromBoot).isEqualTo(35.duration())
+        assertThat(update.latestAchievedGoals.first().dataTypeCondition.dataType)
+            .isEqualTo(CALORIES_TOTAL)
+        assertThat(markerSummary.achievedGoal.dataTypeCondition.dataType).isEqualTo(CALORIES_TOTAL)
+        assertThat(update.exerciseConfig!!.exerciseType).isEqualTo(WALKING)
+        assertThat(update.activeDurationCheckpoint!!.activeDuration).isEqualTo(30.duration())
+        assertThat(update.exerciseStateInfo.state).isEqualTo(ExerciseState.ACTIVE)
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/HealthEventTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/HealthEventTest.kt
new file mode 100644
index 0000000..7b3e17e
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/HealthEventTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.DataType.Companion.DISTANCE
+import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM
+import androidx.health.services.client.data.HealthEvent.Type.Companion.FALL_DETECTED
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import java.time.Instant
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class HealthEventTest {
+
+    fun Int.instant() = Instant.ofEpochMilli(toLong())
+    fun Int.duration() = Duration.ofSeconds(toLong())
+
+    @Test
+    fun protoRoundTripWithDataPoint() {
+        val proto = HealthEvent(
+            FALL_DETECTED,
+            30.instant(),
+            DataPointContainer(listOf(
+                DataPoints.heartRate(42.0, 20.duration()),
+                DataPoints.heartRate(43.0, 10.duration()),
+                DataPoints.distance(180.0, 20.duration(), 40.duration()),
+            ))
+        ).proto
+
+        val event = HealthEvent(proto)
+
+        assertThat(event.type).isEqualTo(FALL_DETECTED)
+        assertThat(event.eventTime).isEqualTo(30.instant())
+        assertThat(event.metrics.getData(HEART_RATE_BPM)[0].value).isEqualTo(42.0)
+        assertThat(event.metrics.getData(HEART_RATE_BPM)[1].value).isEqualTo(43.0)
+        assertThat(event.metrics.getData(DISTANCE)[0].value).isEqualTo(180.0)
+    }
+
+    @Test
+    fun protoRoundTripEmptyDataPointContainer() {
+        val proto = HealthEvent(FALL_DETECTED, 30.instant(), DataPointContainer(listOf())).proto
+
+        val event = HealthEvent(proto)
+
+        assertThat(event.type).isEqualTo(FALL_DETECTED)
+        assertThat(event.eventTime).isEqualTo(30.instant())
+        assertThat(event.metrics.dataPoints).isEmpty()
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/IntervalDataPointTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/IntervalDataPointTest.kt
new file mode 100644
index 0000000..048f207
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/IntervalDataPointTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import android.os.Bundle
+import com.google.common.truth.Truth
+import java.time.Duration
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class IntervalDataPointTest {
+    fun Int.duration() = Duration.ofSeconds(toLong())
+
+    @Test
+    fun protoRoundTrip() {
+        val proto = IntervalDataPoint(
+            DataType.CALORIES,
+            value = 130.0,
+            startDurationFromBoot = 10.duration(),
+            endDurationFromBoot = 20.duration(),
+            Bundle().apply {
+                putInt("int", 5)
+                putString("string", "value")
+            },
+            accuracy = null // No interval DataPoints have an accuracy component
+        ).proto
+
+        val dataPoint = IntervalDataPoint.fromProto(proto)
+
+        Truth.assertThat(dataPoint.dataType).isEqualTo(DataType.CALORIES)
+        Truth.assertThat(dataPoint.value).isEqualTo(130.0)
+        Truth.assertThat(dataPoint.startDurationFromBoot).isEqualTo(10.duration())
+        Truth.assertThat(dataPoint.endDurationFromBoot).isEqualTo(20.duration())
+        Truth.assertThat(dataPoint.metadata.getInt("int")).isEqualTo(5)
+        Truth.assertThat(dataPoint.metadata.getString("string")).isEqualTo("value")
+        Truth.assertThat(dataPoint.accuracy).isNull()
+    }
+
+    @Test
+    fun protoRoundTrip_emptyBundle() {
+        val proto = IntervalDataPoint(
+            DataType.CALORIES,
+            value = 130.0,
+            startDurationFromBoot = 10.duration(),
+            endDurationFromBoot = 20.duration(),
+            metadata = Bundle(),
+            accuracy = null // No interval DataPoints have an accuracy component
+        ).proto
+
+        val dataPoint = IntervalDataPoint.fromProto(proto)
+
+        Truth.assertThat(dataPoint.dataType).isEqualTo(DataType.CALORIES)
+        Truth.assertThat(dataPoint.value).isEqualTo(130.0)
+        Truth.assertThat(dataPoint.startDurationFromBoot).isEqualTo(10.duration())
+        Truth.assertThat(dataPoint.endDurationFromBoot).isEqualTo(20.duration())
+        Truth.assertThat(dataPoint.metadata.keySet()).isEmpty()
+        Truth.assertThat(dataPoint.accuracy).isNull()
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/LocationDataTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/LocationDataTest.kt
new file mode 100644
index 0000000..be6e0aa
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/LocationDataTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.proto.DataProto
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class LocationDataTest {
+
+    @Test
+    fun protoRoundTrip() {
+        val valueProtoBuilder = DataProto.Value.newBuilder()
+        LocationData(
+            latitude = 1.4,
+            longitude = 2.3,
+            altitude = 3.2,
+            bearing = 4.1
+        ).addToValueProtoBuilder(valueProtoBuilder)
+
+        val location = LocationData.fromDataProtoValue(valueProtoBuilder.build())
+
+        assertThat(location.latitude).isEqualTo(1.4)
+        assertThat(location.longitude).isEqualTo(2.3)
+        assertThat(location.altitude).isEqualTo(3.2)
+        assertThat(location.bearing).isEqualTo(4.1)
+    }
+
+    @Test
+    fun protoRoundTripWithNulls() {
+        val valueProtoBuilder = DataProto.Value.newBuilder()
+        LocationData(
+            latitude = 1.4,
+            longitude = 2.3,
+            altitude = null,
+            bearing = null
+        ).addToValueProtoBuilder(valueProtoBuilder)
+
+        val location = LocationData.fromDataProtoValue(valueProtoBuilder.build())
+
+        assertThat(location.latitude).isEqualTo(1.4)
+        assertThat(location.longitude).isEqualTo(2.3)
+        assertThat(location.altitude).isNull()
+        assertThat(location.bearing).isNull()
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/MilestoneMarkerSummaryTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/MilestoneMarkerSummaryTest.kt
new file mode 100644
index 0000000..5aaad5b
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/MilestoneMarkerSummaryTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.DataType.Companion.CALORIES_TOTAL
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import java.time.Instant
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class MilestoneMarkerSummaryTest {
+    fun Int.instant() = Instant.ofEpochMilli(toLong())
+    fun Int.duration() = Duration.ofSeconds(toLong())
+
+    @Test
+    fun protoRoundTrip() {
+        val goal = ExerciseGoal.createOneTimeGoal(
+            DataTypeCondition(CALORIES_TOTAL, 125.0, ComparisonType.GREATER_THAN_OR_EQUAL)
+        )
+        val proto = MilestoneMarkerSummary(
+            startTime = 15.instant(),
+            endTime = 40.instant(),
+            activeDuration = 20.duration(),
+            achievedGoal = goal,
+            summaryMetrics = DataPointContainer(
+                listOf(DataPoints.caloriesTotal(130.0, 15.instant(), 35.instant()))
+            )
+        ).proto
+
+        val summary = MilestoneMarkerSummary(proto)
+
+        assertThat(summary.startTime).isEqualTo(15.instant())
+        assertThat(summary.endTime).isEqualTo(40.instant())
+        assertThat(summary.activeDuration).isEqualTo(20.duration())
+        assertThat(summary.achievedGoal.dataTypeCondition.dataType).isEqualTo(CALORIES_TOTAL)
+        assertThat(summary.summaryMetrics.getData(CALORIES_TOTAL)!!.total).isEqualTo(130.0)
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/PassiveGoalTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/PassiveGoalTest.kt
new file mode 100644
index 0000000..4675680
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/PassiveGoalTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.ComparisonType.Companion.GREATER_THAN
+import androidx.health.services.client.data.DataType.Companion.STEPS
+import androidx.health.services.client.data.DataType.Companion.STEPS_DAILY
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class PassiveGoalTest {
+    @Test
+    fun protoRoundTrip() {
+        val proto = PassiveGoal(
+            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
+            PassiveGoal.TriggerFrequency.ONCE
+        ).proto
+
+        val goal = PassiveGoal(proto)
+
+        assertThat(goal.dataTypeCondition.dataType).isEqualTo(STEPS_DAILY)
+        assertThat(goal.dataTypeCondition.threshold).isEqualTo(400)
+        assertThat(goal.dataTypeCondition.comparisonType).isEqualTo(GREATER_THAN)
+        assertThat(goal.triggerFrequency).isEqualTo(PassiveGoal.TriggerFrequency.ONCE)
+    }
+
+    @Test
+    fun shouldEqual() {
+        val goal1 = PassiveGoal(
+            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
+            PassiveGoal.TriggerFrequency.ONCE
+        )
+        val goal2 = PassiveGoal(
+            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
+            PassiveGoal.TriggerFrequency.ONCE
+        )
+
+        assertThat(goal1).isEqualTo(goal2)
+    }
+
+    @Test
+    fun shouldNotEqual_differentTriggerFrequency() {
+        val goal1 = PassiveGoal(
+            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
+            PassiveGoal.TriggerFrequency.ONCE
+        )
+        val goal2 = PassiveGoal(
+            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
+            PassiveGoal.TriggerFrequency.REPEATED
+        )
+
+        assertThat(goal1).isNotEqualTo(goal2)
+    }
+
+    @Test
+    fun shouldNotEqual_differentThreshold() {
+        val goal1 = PassiveGoal(
+            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
+            PassiveGoal.TriggerFrequency.ONCE
+        )
+        val goal2 = PassiveGoal(
+            DataTypeCondition(STEPS_DAILY, 800, GREATER_THAN),
+            PassiveGoal.TriggerFrequency.ONCE
+        )
+
+        assertThat(goal1).isNotEqualTo(goal2)
+    }
+
+    @Test
+    fun shouldNotEqual_differentDataType() {
+        val goal1 = PassiveGoal(
+            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
+            PassiveGoal.TriggerFrequency.ONCE
+        )
+        val goal2 = PassiveGoal(
+            DataTypeCondition(STEPS, 400, GREATER_THAN),
+            PassiveGoal.TriggerFrequency.ONCE
+        )
+
+        assertThat(goal1).isNotEqualTo(goal2)
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/SampleDataPointTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/SampleDataPointTest.kt
new file mode 100644
index 0000000..3dddc26
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/SampleDataPointTest.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import android.os.Bundle
+import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM
+import androidx.health.services.client.data.DataType.Companion.LOCATION
+import androidx.health.services.client.data.HeartRateAccuracy.SensorStatus.Companion.ACCURACY_HIGH
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class SampleDataPointTest {
+    fun Int.duration() = Duration.ofSeconds(toLong())
+
+    @Test
+    fun protoRoundTrip() {
+        val proto = SampleDataPoint(
+            HEART_RATE_BPM,
+            130.0,
+            20.duration(),
+            Bundle().apply {
+                putInt("int", 5)
+                putString("string", "value")
+            },
+            HeartRateAccuracy(ACCURACY_HIGH)
+        ).proto
+
+        val dataPoint = SampleDataPoint.fromProto(proto)
+
+        assertThat(dataPoint.dataType).isEqualTo(HEART_RATE_BPM)
+        assertThat(dataPoint.value).isEqualTo(130.0)
+        assertThat(dataPoint.timeDurationFromBoot).isEqualTo(20.duration())
+        assertThat(dataPoint.metadata.getInt("int")).isEqualTo(5)
+        assertThat(dataPoint.metadata.getString("string")).isEqualTo("value")
+        assertThat((dataPoint.accuracy as HeartRateAccuracy).sensorStatus).isEqualTo(ACCURACY_HIGH)
+    }
+
+    @Test
+    fun protoRoundTrip_emptyBundleAndAccuracy() {
+        val proto = SampleDataPoint(
+            HEART_RATE_BPM,
+            130.0,
+            20.duration(),
+            accuracy = null,
+            metadata = Bundle()
+        ).proto
+
+        val dataPoint = SampleDataPoint.fromProto(proto)
+
+        assertThat(dataPoint.dataType).isEqualTo(HEART_RATE_BPM)
+        assertThat(dataPoint.value).isEqualTo(130.0)
+        assertThat(dataPoint.timeDurationFromBoot).isEqualTo(20.duration())
+        assertThat(dataPoint.metadata.keySet()).isEmpty()
+        assertThat(dataPoint.accuracy).isNull()
+    }
+
+    @Test
+    fun protoRoundTripLocation() {
+        val proto = SampleDataPoint(
+            dataType = LOCATION,
+            value = LocationData(
+                latitude = 41.2,
+                longitude = 82.3,
+                altitude = 93.4,
+                bearing = 274.5
+            ),
+            timeDurationFromBoot = 20.duration(),
+            accuracy = LocationAccuracy(
+                horizontalPositionErrorMeters = 3.5,
+                verticalPositionErrorMeters = 4.7
+            )
+        ).proto
+
+        val dataPoint = SampleDataPoint.fromProto(proto)
+
+        assertThat(dataPoint.dataType).isEqualTo(LOCATION)
+        val data = dataPoint.value as LocationData
+        assertThat(data.latitude).isEqualTo(41.2)
+        assertThat(data.longitude).isEqualTo(82.3)
+        assertThat(data.altitude).isEqualTo(93.4)
+        assertThat(data.bearing).isEqualTo(274.5)
+        assertThat(dataPoint.timeDurationFromBoot).isEqualTo(20.duration())
+        val accuracy = dataPoint.accuracy as LocationAccuracy
+        assertThat(accuracy.horizontalPositionErrorMeters).isEqualTo(3.5)
+        assertThat(accuracy.verticalPositionErrorMeters).isEqualTo(4.7)
+    }
+
+    @Test
+    fun protoRoundTripLocation_altitudeAndBearingNull() {
+        val proto = SampleDataPoint(
+            dataType = LOCATION,
+            value = LocationData(
+                latitude = 41.2,
+                longitude = 82.3,
+            ),
+            timeDurationFromBoot = 20.duration(),
+            accuracy = LocationAccuracy(
+                horizontalPositionErrorMeters = 3.5,
+                verticalPositionErrorMeters = null
+            )
+        ).proto
+
+        val dataPoint = SampleDataPoint.fromProto(proto)
+
+        assertThat(dataPoint.dataType).isEqualTo(LOCATION)
+        val data = dataPoint.value as LocationData
+        assertThat(data.latitude).isEqualTo(41.2)
+        assertThat(data.longitude).isEqualTo(82.3)
+        assertThat(data.altitude).isNull()
+        assertThat(data.bearing).isNull()
+        assertThat(dataPoint.timeDurationFromBoot).isEqualTo(20.duration())
+        val accuracy = dataPoint.accuracy as LocationAccuracy
+        assertThat(accuracy.horizontalPositionErrorMeters).isEqualTo(3.5)
+        assertThat(accuracy.verticalPositionErrorMeters).isNull()
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/StatisticalDataPointTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/StatisticalDataPointTest.kt
new file mode 100644
index 0000000..6e69001
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/StatisticalDataPointTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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.health.services.client.data
+
+import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM_STATS
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+internal class StatisticalDataPointTest {
+    fun Int.instant() = Instant.ofEpochMilli(toLong())
+
+    @Test
+    fun protoRoundTrip() {
+        val proto = StatisticalDataPoint(
+            dataType = HEART_RATE_BPM_STATS,
+            min = 100.0,
+            max = 175.5,
+            average = 155.0,
+            start = 10.instant(),
+            end = 99.instant(),
+        ).proto
+
+        val dataPoint = StatisticalDataPoint.fromProto(proto.statisticalDataPoint)
+
+        assertThat(dataPoint.dataType).isEqualTo(HEART_RATE_BPM_STATS)
+        assertThat(dataPoint.min).isEqualTo(100.0)
+        assertThat(dataPoint.max).isEqualTo(175.5)
+        assertThat(dataPoint.average).isEqualTo(155.0)
+        assertThat(dataPoint.start).isEqualTo(10.instant())
+        assertThat(dataPoint.end).isEqualTo(99.instant())
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt
new file mode 100644
index 0000000..32e90cf
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt
@@ -0,0 +1,306 @@
+/*
+ * Copyright 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.health.services.client.impl
+
+import android.app.Application
+import android.content.ComponentName
+import android.content.Intent
+import android.os.Looper.getMainLooper
+import androidx.health.services.client.ExerciseUpdateCallback
+import androidx.health.services.client.data.Availability
+import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM
+import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM_STATS
+import androidx.health.services.client.data.DataTypeAvailability.Companion.ACQUIRING
+import androidx.health.services.client.data.ExerciseConfig
+import androidx.health.services.client.data.ExerciseLapSummary
+import androidx.health.services.client.data.ExerciseType
+import androidx.health.services.client.data.ExerciseUpdate
+import androidx.health.services.client.data.WarmUpConfig
+import androidx.health.services.client.impl.event.ExerciseUpdateListenerEvent
+import androidx.health.services.client.impl.internal.IExerciseInfoCallback
+import androidx.health.services.client.impl.internal.IStatusCallback
+import androidx.health.services.client.impl.ipc.internal.ConnectionManager
+import androidx.health.services.client.impl.request.AutoPauseAndResumeConfigRequest
+import androidx.health.services.client.impl.request.CapabilitiesRequest
+import androidx.health.services.client.impl.request.ExerciseGoalRequest
+import androidx.health.services.client.impl.request.FlushRequest
+import androidx.health.services.client.impl.request.PrepareExerciseRequest
+import androidx.health.services.client.impl.request.StartExerciseRequest
+import androidx.health.services.client.impl.response.AvailabilityResponse
+import androidx.health.services.client.impl.response.ExerciseCapabilitiesResponse
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.Shadows.shadowOf
+
+@RunWith(RobolectricTestRunner::class)
+class ServiceBackedExerciseClientTest {
+
+    private lateinit var client: ServiceBackedExerciseClient
+    private lateinit var fakeService: FakeServiceStub
+    private val callback = FakeExerciseUpdateCallback()
+
+    @Before
+    fun setUp() {
+        val context = ApplicationProvider.getApplicationContext<Application>()
+        client =
+            ServiceBackedExerciseClient(context, ConnectionManager(context, context.mainLooper))
+        fakeService = FakeServiceStub()
+
+        val packageName = ServiceBackedExerciseClient.CLIENT_CONFIGURATION.servicePackageName
+        val action = ServiceBackedExerciseClient.CLIENT_CONFIGURATION.bindAction
+        shadowOf(context).setComponentNameAndServiceForBindServiceForIntent(
+            Intent().setPackage(packageName).setAction(action),
+            ComponentName(packageName, ServiceBackedExerciseClient.CLIENT),
+            fakeService
+        )
+    }
+
+    @After
+    fun tearDown() {
+        client.clearUpdateCallbackAsync(callback)
+    }
+
+    @Test
+    fun registeredCallbackShouldBeInvoked() {
+        client.setUpdateCallback(callback)
+        shadowOf(getMainLooper()).idle()
+
+        assertThat(callback.onRegisteredCalls).isEqualTo(1)
+        assertThat(callback.onRegistrationFailedCalls).isEqualTo(0)
+    }
+
+    @Test
+    fun registrationFailedCallbackShouldBeInvoked() {
+        fakeService.statusCallbackAction = { it!!.onFailure("Terrible failure!") }
+
+        client.setUpdateCallback(callback)
+        shadowOf(getMainLooper()).idle()
+
+        assertThat(callback.onRegisteredCalls).isEqualTo(0)
+        assertThat(callback.onRegistrationFailedCalls).isEqualTo(1)
+        assertThat(callback.registrationFailureThrowables[0].message).isEqualTo("Terrible failure!")
+    }
+
+    @Test
+    fun dataTypeInAvailabilityCallbackShouldMatchRequested_justSampleType_startExercise() {
+        val exerciseConfig = ExerciseConfig(
+            ExerciseType.WALKING,
+            setOf(HEART_RATE_BPM),
+            isAutoPauseAndResumeEnabled = false,
+            isGpsEnabled = false
+        )
+        val availabilityEvent = ExerciseUpdateListenerEvent.createAvailabilityUpdateEvent(
+            AvailabilityResponse(HEART_RATE_BPM, ACQUIRING)
+        )
+        client.setUpdateCallback(callback)
+        client.startExerciseAsync(exerciseConfig)
+        shadowOf(getMainLooper()).idle()
+
+        fakeService.listener!!.onExerciseUpdateListenerEvent(availabilityEvent)
+        shadowOf(getMainLooper()).idle()
+
+        assertThat(callback.availabilities).containsEntry(HEART_RATE_BPM, ACQUIRING)
+    }
+
+    @Test
+    fun dataTypeInAvailabilityCallbackShouldMatchRequested_justStatsType_startExercise() {
+        val exerciseConfig = ExerciseConfig(
+            ExerciseType.WALKING,
+            setOf(HEART_RATE_BPM_STATS),
+            isAutoPauseAndResumeEnabled = false,
+            isGpsEnabled = false
+        )
+        val availabilityEvent = ExerciseUpdateListenerEvent.createAvailabilityUpdateEvent(
+            // Currently the proto form of HEART_RATE_BPM and HEART_RATE_BPM_STATS is identical. The
+            // APK doesn't know about _STATS, so pass the sample type to mimic that behavior.
+            AvailabilityResponse(HEART_RATE_BPM, ACQUIRING)
+        )
+        client.setUpdateCallback(callback)
+        client.startExerciseAsync(exerciseConfig)
+        shadowOf(getMainLooper()).idle()
+
+        fakeService.listener!!.onExerciseUpdateListenerEvent(availabilityEvent)
+        shadowOf(getMainLooper()).idle()
+
+        assertThat(callback.availabilities).containsEntry(HEART_RATE_BPM_STATS, ACQUIRING)
+    }
+
+    @Test
+    fun dataTypeInAvailabilityCallbackShouldMatchRequested_statsAndSample_startExercise() {
+        val exerciseConfig = ExerciseConfig(
+            ExerciseType.WALKING,
+            setOf(HEART_RATE_BPM, HEART_RATE_BPM_STATS),
+            isAutoPauseAndResumeEnabled = false,
+            isGpsEnabled = false
+        )
+        val availabilityEvent = ExerciseUpdateListenerEvent.createAvailabilityUpdateEvent(
+            // Currently the proto form of HEART_RATE_BPM and HEART_RATE_BPM_STATS is identical. The
+            // APK doesn't know about _STATS, so pass the sample type to mimic that behavior.
+            AvailabilityResponse(HEART_RATE_BPM, ACQUIRING)
+        )
+        client.setUpdateCallback(callback)
+        client.startExerciseAsync(exerciseConfig)
+        shadowOf(getMainLooper()).idle()
+
+        fakeService.listener!!.onExerciseUpdateListenerEvent(availabilityEvent)
+        shadowOf(getMainLooper()).idle()
+
+        // When both the sample type and stat type are requested, both should be notified
+        assertThat(callback.availabilities).containsEntry(HEART_RATE_BPM, ACQUIRING)
+        assertThat(callback.availabilities).containsEntry(HEART_RATE_BPM_STATS, ACQUIRING)
+    }
+
+    @Test
+    fun dataTypeInAvailabilityCallbackShouldMatchRequested_justSampleType_prepare() {
+        val warmUpConfig = WarmUpConfig(
+            ExerciseType.WALKING,
+            setOf(HEART_RATE_BPM),
+        )
+        val availabilityEvent = ExerciseUpdateListenerEvent.createAvailabilityUpdateEvent(
+            AvailabilityResponse(HEART_RATE_BPM, ACQUIRING)
+        )
+        client.setUpdateCallback(callback)
+        client.prepareExerciseAsync(warmUpConfig)
+        shadowOf(getMainLooper()).idle()
+
+        fakeService.listener!!.onExerciseUpdateListenerEvent(availabilityEvent)
+        shadowOf(getMainLooper()).idle()
+
+        assertThat(callback.availabilities).containsEntry(HEART_RATE_BPM, ACQUIRING)
+    }
+
+    class FakeExerciseUpdateCallback : ExerciseUpdateCallback {
+        val availabilities = mutableMapOf<DataType<*, *>, Availability>()
+        val registrationFailureThrowables = mutableListOf<Throwable>()
+        var onRegisteredCalls = 0
+        var onRegistrationFailedCalls = 0
+
+        override fun onRegistered() {
+            onRegisteredCalls++
+        }
+
+        override fun onRegistrationFailed(throwable: Throwable) {
+            onRegistrationFailedCalls++
+            registrationFailureThrowables.add(throwable)
+        }
+
+        override fun onExerciseUpdateReceived(update: ExerciseUpdate) {}
+
+        override fun onLapSummaryReceived(lapSummary: ExerciseLapSummary) {}
+
+        override fun onAvailabilityChanged(dataType: DataType<*, *>, availability: Availability) {
+            availabilities[dataType] = availability
+        }
+    }
+
+    class FakeServiceStub : IExerciseApiService.Stub() {
+
+        var listener: IExerciseUpdateListener? = null
+        var statusCallbackAction: (IStatusCallback?) -> Unit = { it!!.onSuccess() }
+
+        override fun getApiVersion(): Int = 12
+
+        override fun prepareExercise(
+            prepareExerciseRequest: PrepareExerciseRequest?,
+            statusCallback: IStatusCallback?
+        ) {
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun startExercise(
+            startExerciseRequest: StartExerciseRequest?,
+            statusCallback: IStatusCallback?
+        ) {
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun pauseExercise(packageName: String?, statusCallback: IStatusCallback?) {
+            throw NotImplementedError()
+        }
+
+        override fun resumeExercise(packageName: String?, statusCallback: IStatusCallback?) {
+            throw NotImplementedError()
+        }
+
+        override fun endExercise(packageName: String?, statusCallback: IStatusCallback?) {
+            throw NotImplementedError()
+        }
+
+        override fun markLap(packageName: String?, statusCallback: IStatusCallback?) {
+            throw NotImplementedError()
+        }
+
+        override fun getCurrentExerciseInfo(
+            packageName: String?,
+            exerciseInfoCallback: IExerciseInfoCallback?
+        ) {
+            throw NotImplementedError()
+        }
+
+        override fun setUpdateListener(
+            packageName: String?,
+            listener: IExerciseUpdateListener?,
+            statusCallback: IStatusCallback?
+        ) {
+            this.listener = listener
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun clearUpdateListener(
+            packageName: String?,
+            listener: IExerciseUpdateListener?,
+            statusCallback: IStatusCallback?
+        ) {
+            throw NotImplementedError()
+        }
+
+        override fun addGoalToActiveExercise(
+            request: ExerciseGoalRequest?,
+            statusCallback: IStatusCallback?
+        ) {
+            throw NotImplementedError()
+        }
+
+        override fun removeGoalFromActiveExercise(
+            request: ExerciseGoalRequest?,
+            statusCallback: IStatusCallback?
+        ) {
+            throw NotImplementedError()
+        }
+
+        override fun overrideAutoPauseAndResumeForActiveExercise(
+            request: AutoPauseAndResumeConfigRequest?,
+            statusCallback: IStatusCallback?
+        ) {
+            throw NotImplementedError()
+        }
+
+        override fun getCapabilities(request: CapabilitiesRequest?): ExerciseCapabilitiesResponse {
+            throw NotImplementedError()
+        }
+
+        override fun flushExercise(request: FlushRequest?, statusCallback: IStatusCallback?) {
+            throw NotImplementedError()
+        }
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedMeasureClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedMeasureClientTest.kt
new file mode 100644
index 0000000..a86c098
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedMeasureClientTest.kt
@@ -0,0 +1,250 @@
+/*
+ * Copyright 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.health.services.client.impl
+
+import android.app.Application
+import android.content.ComponentName
+import android.content.Intent
+import android.os.Looper
+import androidx.health.services.client.MeasureCallback
+import androidx.health.services.client.data.Availability
+import androidx.health.services.client.data.DataPointContainer
+import androidx.health.services.client.data.DataPoints
+import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM
+import androidx.health.services.client.data.DataTypeAvailability.Companion.AVAILABLE
+import androidx.health.services.client.data.DeltaDataType
+import androidx.health.services.client.data.HeartRateAccuracy
+import androidx.health.services.client.data.HeartRateAccuracy.SensorStatus.Companion.ACCURACY_HIGH
+import androidx.health.services.client.data.MeasureCapabilities
+import androidx.health.services.client.impl.event.MeasureCallbackEvent
+import androidx.health.services.client.impl.internal.IStatusCallback
+import androidx.health.services.client.impl.ipc.internal.ConnectionManager
+import androidx.health.services.client.impl.request.CapabilitiesRequest
+import androidx.health.services.client.impl.request.MeasureRegistrationRequest
+import androidx.health.services.client.impl.request.MeasureUnregistrationRequest
+import androidx.health.services.client.impl.response.AvailabilityResponse
+import androidx.health.services.client.impl.response.DataPointsResponse
+import androidx.health.services.client.impl.response.MeasureCapabilitiesResponse
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.Shadows.shadowOf
+
+@RunWith(RobolectricTestRunner::class)
+class ServiceBackedMeasureClientTest {
+
+    private val callback = FakeCallback()
+    private lateinit var client: ServiceBackedMeasureClient
+    private lateinit var fakeService: FakeServiceStub
+
+    @Before
+    fun setUp() {
+        val context = ApplicationProvider.getApplicationContext<Application>()
+        client =
+            ServiceBackedMeasureClient(context, ConnectionManager(context, context.mainLooper))
+        fakeService = FakeServiceStub()
+
+        val packageName = ServiceBackedMeasureClient.CLIENT_CONFIGURATION.servicePackageName
+        val action = ServiceBackedMeasureClient.CLIENT_CONFIGURATION.bindAction
+        shadowOf(context).setComponentNameAndServiceForBindServiceForIntent(
+            Intent().setPackage(packageName).setAction(action),
+            ComponentName(packageName, ServiceBackedMeasureClient.CLIENT),
+            fakeService
+        )
+    }
+
+    @After
+    fun tearDown() {
+        client.unregisterMeasureCallbackAsync(HEART_RATE_BPM, callback)
+    }
+
+    @Test
+    fun registerCallbackReachesService() {
+        client.registerMeasureCallback(HEART_RATE_BPM, callback)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        assertThat(callback.onRegisteredInvocationCount).isEqualTo(1)
+        assertThat(fakeService.registerEvents).hasSize(1)
+        assertThat(fakeService.registerEvents[0].request.dataType).isEqualTo(HEART_RATE_BPM)
+        assertThat(fakeService.registerEvents[0].request.packageName)
+            .isEqualTo("androidx.health.services.client.test")
+    }
+    @Test
+    fun registerCallbackFailureReachesClient() {
+        fakeService.statusCallbackAction = { it.onFailure("Measure twice, cut once.") }
+
+        client.registerMeasureCallback(HEART_RATE_BPM, callback)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        assertThat(callback.registrationFailureThrowables).hasSize(1)
+        assertThat(callback.registrationFailureThrowables[0].message)
+            .isEqualTo("Measure twice, cut once.")
+    }
+
+    @Test
+    fun unregisterCallbackReachesService() {
+        client.registerMeasureCallback(HEART_RATE_BPM, callback)
+        client.unregisterMeasureCallbackAsync(HEART_RATE_BPM, callback)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        assertThat(fakeService.unregisterEvents).hasSize(1)
+        assertThat(fakeService.unregisterEvents[0].request.dataType).isEqualTo(HEART_RATE_BPM)
+        assertThat(fakeService.unregisterEvents[0].request.packageName)
+            .isEqualTo("androidx.health.services.client.test")
+    }
+
+    @Test
+    fun dataPointsReachAppCallback() {
+        val event = MeasureCallbackEvent.createDataPointsUpdateEvent(
+            DataPointsResponse(
+                listOf(
+                    DataPoints.heartRate(
+                        50.0,
+                        Duration.ofSeconds(42),
+                        HeartRateAccuracy(ACCURACY_HIGH)
+                    )
+                )
+            )
+        )
+        client.registerMeasureCallback(HEART_RATE_BPM, callback)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        fakeService.registerEvents[0].callback.onMeasureCallbackEvent(event)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        assertThat(callback.dataReceivedEvents).hasSize(1)
+        val dataEvent = callback.dataReceivedEvents[0]
+        assertThat(dataEvent.data.getData(HEART_RATE_BPM)).hasSize(1)
+        val dataPoint = dataEvent.data.getData(HEART_RATE_BPM)[0]
+        assertThat(dataPoint.value).isEqualTo(50.0)
+        assertThat((dataPoint.accuracy as HeartRateAccuracy).sensorStatus).isEqualTo(ACCURACY_HIGH)
+        assertThat(dataPoint.timeDurationFromBoot.seconds).isEqualTo(42)
+    }
+
+    @Test
+    fun availabilityReachesAppCallback() {
+        val event = MeasureCallbackEvent.createAvailabilityUpdateEvent(
+            AvailabilityResponse(HEART_RATE_BPM, AVAILABLE)
+        )
+        client.registerMeasureCallback(HEART_RATE_BPM, callback)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        fakeService.registerEvents[0].callback.onMeasureCallbackEvent(event)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        assertThat(callback.availabilityChangeEvents).hasSize(1)
+        val availabilityEvent = callback.availabilityChangeEvents[0]
+        assertThat(availabilityEvent.dataType).isEqualTo(HEART_RATE_BPM)
+        assertThat(availabilityEvent.availability).isEqualTo(AVAILABLE)
+    }
+
+    @Test
+    fun capabilitiesReturnsCorrectValue() {
+        fakeService.supportedDataTypes = setOf(HEART_RATE_BPM)
+
+        val capabilitiesFuture = client.getCapabilitiesAsync()
+        shadowOf(Looper.getMainLooper()).idle()
+        val capabilities = capabilitiesFuture.get()
+
+        assertThat(capabilities.supportedDataTypesMeasure).hasSize(1)
+        assertThat(capabilities.supportedDataTypesMeasure).containsExactly(HEART_RATE_BPM)
+    }
+
+    class FakeCallback : MeasureCallback {
+        data class AvailabilityChangeEvent(
+            val dataType: DataType<*, *>,
+            val availability: Availability
+        )
+
+        data class DataReceivedEvent(val data: DataPointContainer)
+
+        val availabilityChangeEvents = mutableListOf<AvailabilityChangeEvent>()
+        val dataReceivedEvents = mutableListOf<DataReceivedEvent>()
+        var onRegisteredInvocationCount = 0
+        var registrationFailureThrowables = mutableListOf<Throwable>()
+
+        override fun onRegistered() {
+            onRegisteredInvocationCount++
+        }
+
+        override fun onRegistrationFailed(throwable: Throwable) {
+            registrationFailureThrowables += throwable
+        }
+
+        override fun onAvailabilityChanged(
+            dataType: DeltaDataType<*, *>,
+            availability: Availability
+        ) {
+            availabilityChangeEvents += AvailabilityChangeEvent(dataType, availability)
+        }
+
+        override fun onDataReceived(data: DataPointContainer) {
+            dataReceivedEvents += DataReceivedEvent(data)
+        }
+    }
+
+    class FakeServiceStub : IMeasureApiService.Stub() {
+
+        class RegisterEvent(
+            val request: MeasureRegistrationRequest,
+            val callback: IMeasureCallback,
+            val statusCallback: IStatusCallback
+        )
+
+        class UnregisterEvent(
+            val request: MeasureUnregistrationRequest,
+            val callback: IMeasureCallback,
+            val statusCallback: IStatusCallback
+        )
+
+        var statusCallbackAction: (IStatusCallback) -> Unit = { it.onSuccess() }
+        var supportedDataTypes = setOf(HEART_RATE_BPM)
+
+        val registerEvents = mutableListOf<RegisterEvent>()
+        val unregisterEvents = mutableListOf<UnregisterEvent>()
+
+        override fun getApiVersion() = 42
+
+        override fun registerCallback(
+            request: MeasureRegistrationRequest,
+            callback: IMeasureCallback,
+            statusCallback: IStatusCallback
+        ) {
+            registerEvents += RegisterEvent(request, callback, statusCallback)
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun unregisterCallback(
+            request: MeasureUnregistrationRequest,
+            callback: IMeasureCallback,
+            statusCallback: IStatusCallback
+        ) {
+            unregisterEvents += UnregisterEvent(request, callback, statusCallback)
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun getCapabilities(request: CapabilitiesRequest): MeasureCapabilitiesResponse {
+            return MeasureCapabilitiesResponse(MeasureCapabilities(supportedDataTypes))
+        }
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClientTest.kt
new file mode 100644
index 0000000..11dd637
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClientTest.kt
@@ -0,0 +1,375 @@
+/*
+ * Copyright 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.health.services.client.impl
+
+import android.app.Application
+import android.content.ComponentName
+import android.content.Intent
+import android.os.Looper
+import androidx.health.services.client.PassiveListenerCallback
+import androidx.health.services.client.PassiveListenerService
+import androidx.health.services.client.data.ComparisonType.Companion.GREATER_THAN
+import androidx.health.services.client.data.DataPointContainer
+import androidx.health.services.client.data.DataType.Companion.CALORIES_DAILY
+import androidx.health.services.client.data.DataType.Companion.STEPS_DAILY
+import androidx.health.services.client.data.DataTypeCondition
+import androidx.health.services.client.data.HealthEvent
+import androidx.health.services.client.data.HealthEvent.Type.Companion.FALL_DETECTED
+import androidx.health.services.client.data.PassiveGoal
+import androidx.health.services.client.data.PassiveGoal.TriggerFrequency.Companion.ONCE
+import androidx.health.services.client.data.PassiveListenerConfig
+import androidx.health.services.client.data.UserActivityInfo
+import androidx.health.services.client.data.UserActivityState
+import androidx.health.services.client.impl.event.PassiveListenerEvent
+import androidx.health.services.client.impl.internal.IStatusCallback
+import androidx.health.services.client.impl.ipc.internal.ConnectionManager
+import androidx.health.services.client.impl.request.CapabilitiesRequest
+import androidx.health.services.client.impl.request.FlushRequest
+import androidx.health.services.client.impl.request.PassiveListenerCallbackRegistrationRequest
+import androidx.health.services.client.impl.request.PassiveListenerServiceRegistrationRequest
+import androidx.health.services.client.impl.response.HealthEventResponse
+import androidx.health.services.client.impl.response.PassiveMonitoringCapabilitiesResponse
+import androidx.health.services.client.impl.response.PassiveMonitoringGoalResponse
+import androidx.health.services.client.impl.response.PassiveMonitoringUpdateResponse
+import androidx.health.services.client.proto.DataProto
+import androidx.health.services.client.proto.DataProto.ComparisonType.COMPARISON_TYPE_GREATER_THAN
+import androidx.health.services.client.proto.DataProto.HealthEvent.HealthEventType.HEALTH_EVENT_TYPE_FALL_DETECTED
+import androidx.health.services.client.proto.DataProto.PassiveGoal.TriggerFrequency.TRIGGER_FREQUENCY_ONCE
+import androidx.health.services.client.proto.DataProto.UserActivityState.USER_ACTIVITY_STATE_PASSIVE
+import androidx.health.services.client.proto.ResponsesProto
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.Shadows.shadowOf
+
+@RunWith(RobolectricTestRunner::class)
+class ServiceBackedPassiveMonitoringClientTest {
+
+    private lateinit var client: ServiceBackedPassiveMonitoringClient
+    private lateinit var fakeService: FakeServiceStub
+
+    @Before
+    fun setUp() {
+        val context = ApplicationProvider.getApplicationContext<Application>()
+        client = ServiceBackedPassiveMonitoringClient(
+            context, ConnectionManager(context, context.mainLooper)
+        )
+        fakeService = FakeServiceStub()
+
+        val packageName =
+            ServiceBackedPassiveMonitoringClient.CLIENT_CONFIGURATION.servicePackageName
+        val action = ServiceBackedPassiveMonitoringClient.CLIENT_CONFIGURATION.bindAction
+        shadowOf(context).setComponentNameAndServiceForBindServiceForIntent(
+            Intent().setPackage(packageName).setAction(action),
+            ComponentName(packageName, ServiceBackedPassiveMonitoringClient.CLIENT),
+            fakeService
+        )
+    }
+
+    @After
+    fun tearDown() {
+        client.clearPassiveListenerCallbackAsync()
+        client.clearPassiveListenerServiceAsync()
+        shadowOf(Looper.getMainLooper()).idle()
+    }
+
+    @Test
+    fun registersPassiveListenerService() {
+        val config = PassiveListenerConfig(
+            dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
+            shouldRequestUserActivityState = true,
+            passiveGoals = setOf(),
+            healthEventTypes = setOf()
+        )
+
+        val future = client.setPassiveListenerServiceAsync(FakeListenerService::class.java, config)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        // Return value of future.get() is not used, but verifying no exceptions are thrown.
+        future.get()
+        assertThat(fakeService.registerServiceRequests).hasSize(1)
+        val request = fakeService.registerServiceRequests[0]
+        assertThat(request.passiveListenerConfig.dataTypes).containsExactly(
+            STEPS_DAILY, CALORIES_DAILY
+        )
+        assertThat(request.passiveListenerConfig.shouldRequestUserActivityState).isTrue()
+        assertThat(request.packageName).isEqualTo("androidx.health.services.client.test")
+    }
+
+    @Test
+    fun registersPassiveListenerCallback() {
+        val config = PassiveListenerConfig(
+            dataTypes = setOf(STEPS_DAILY),
+            shouldRequestUserActivityState = true,
+            passiveGoals = setOf(),
+            healthEventTypes = setOf()
+        )
+        val callback = FakeCallback()
+
+        client.setPassiveListenerCallback(config, callback)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        assertThat(fakeService.registerCallbackRequests).hasSize(1)
+        assertThat(callback.onRegisteredCalls).isEqualTo(1)
+        val request = fakeService.registerCallbackRequests[0]
+        assertThat(request.passiveListenerConfig.dataTypes).containsExactly(STEPS_DAILY)
+        assertThat(request.passiveListenerConfig.shouldRequestUserActivityState).isTrue()
+        assertThat(request.packageName).isEqualTo("androidx.health.services.client.test")
+    }
+
+    @Test
+    fun callbackReceivesDataPointsAndUserActivityInfo() {
+        shadowOf(Looper.getMainLooper()).idle() // ?????
+        val config = PassiveListenerConfig(
+            dataTypes = setOf(STEPS_DAILY),
+            shouldRequestUserActivityState = true,
+            passiveGoals = setOf(),
+            healthEventTypes = setOf()
+        )
+        val callback = FakeCallback()
+        client.setPassiveListenerCallback(config, callback)
+        shadowOf(Looper.getMainLooper()).idle()
+        assertThat(fakeService.registerCallbackRequests).hasSize(1)
+        val callbackFromService = fakeService.registeredCallbacks[0]
+        val passiveDataPointEvent = PassiveListenerEvent.createPassiveUpdateResponse(
+            PassiveMonitoringUpdateResponse(
+                ResponsesProto.PassiveMonitoringUpdateResponse.newBuilder().setUpdate(
+                    DataProto.PassiveMonitoringUpdate.newBuilder().addDataPoints(
+                        DataProto.DataPoint.newBuilder().setDataType(STEPS_DAILY.proto)
+                            .setStartDurationFromBootMs(2)
+                            .setEndDurationFromBootMs(49)
+                            .setValue(DataProto.Value.newBuilder().setLongVal(89)
+                            )
+                    ).addUserActivityInfoUpdates(
+                        DataProto.UserActivityInfo.newBuilder()
+                            .setState(USER_ACTIVITY_STATE_PASSIVE)
+                    )
+                ).build()
+            )
+        )
+
+        callbackFromService.onPassiveListenerEvent(passiveDataPointEvent)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        assertThat(fakeService.registerCallbackRequests).hasSize(1)
+        assertThat(callback.dataPointsReceived).hasSize(1)
+        assertThat(callback.dataPointsReceived[0].dataPoints).hasSize(1)
+        val stepsDataPoint = callback.dataPointsReceived[0].getData(STEPS_DAILY)[0]
+        assertThat(stepsDataPoint.value).isEqualTo(89)
+        assertThat(stepsDataPoint.dataType).isEqualTo(STEPS_DAILY)
+        assertThat(callback.userActivityInfosReceived).hasSize(1)
+        assertThat(callback.userActivityInfosReceived[0].userActivityState).isEqualTo(
+            UserActivityState.USER_ACTIVITY_PASSIVE
+        )
+    }
+
+    @Test
+    fun callbackReceivesCompletedGoals() {
+        val config = PassiveListenerConfig(
+            dataTypes = setOf(STEPS_DAILY),
+            shouldRequestUserActivityState = false,
+            passiveGoals = setOf(
+                PassiveGoal(DataTypeCondition(STEPS_DAILY, 87, GREATER_THAN), ONCE)
+            ),
+            healthEventTypes = setOf()
+        )
+        val callback = FakeCallback()
+        client.setPassiveListenerCallback(config, callback)
+        shadowOf(Looper.getMainLooper()).idle()
+        val callbackFromService = fakeService.registeredCallbacks[0]
+        val passiveGoalEvent = PassiveListenerEvent.createPassiveGoalResponse(
+            PassiveMonitoringGoalResponse(
+                ResponsesProto.PassiveMonitoringGoalResponse.newBuilder()
+                    .setGoal(DataProto.PassiveGoal.newBuilder()
+                        .setTriggerFrequency(TRIGGER_FREQUENCY_ONCE)
+                        .setCondition(DataProto.DataTypeCondition.newBuilder()
+                            .setDataType(STEPS_DAILY.proto)
+                            .setComparisonType(COMPARISON_TYPE_GREATER_THAN)
+                            .setThreshold(DataProto.Value.newBuilder().setLongVal(87))
+                            .build())
+                        .build())
+                    .build()
+            )
+        )
+
+        callbackFromService.onPassiveListenerEvent(passiveGoalEvent)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        assertThat(fakeService.registerCallbackRequests).hasSize(1)
+        assertThat(callback.completedGoals).hasSize(1)
+        val goal = callback.completedGoals[0]
+        assertThat(goal.triggerFrequency).isEqualTo(ONCE)
+        assertThat(goal.dataTypeCondition.dataType).isEqualTo(STEPS_DAILY)
+        assertThat(goal.dataTypeCondition.comparisonType).isEqualTo(GREATER_THAN)
+        assertThat(goal.dataTypeCondition.threshold).isEqualTo(87)
+    }
+
+    @Test
+    fun callbackReceivesHealthEvents() {
+        val config = PassiveListenerConfig(
+            dataTypes = setOf(),
+            shouldRequestUserActivityState = false,
+            passiveGoals = setOf(),
+            healthEventTypes = setOf(FALL_DETECTED)
+        )
+        val callback = FakeCallback()
+        client.setPassiveListenerCallback(config, callback)
+        shadowOf(Looper.getMainLooper()).idle()
+        val callbackFromService = fakeService.registeredCallbacks[0]
+        val passiveHealthEvent = PassiveListenerEvent.createHealthEventResponse(
+            HealthEventResponse(
+                ResponsesProto.HealthEventResponse.newBuilder().setHealthEvent(
+                    DataProto.HealthEvent.newBuilder()
+                        .setType(HEALTH_EVENT_TYPE_FALL_DETECTED)
+                        .build()
+                ).build()
+            )
+        )
+
+        callbackFromService.onPassiveListenerEvent(passiveHealthEvent)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        assertThat(fakeService.registerCallbackRequests).hasSize(1)
+        assertThat(callback.healthEventsReceived).hasSize(1)
+        val healthEvent = callback.healthEventsReceived[0]
+        assertThat(healthEvent.type).isEqualTo(FALL_DETECTED)
+    }
+
+    @Test
+    fun callbackReceivesPermissionsLost() {
+        val config = PassiveListenerConfig(
+            dataTypes = setOf(STEPS_DAILY),
+            shouldRequestUserActivityState = false,
+            passiveGoals = setOf(
+                PassiveGoal(DataTypeCondition(STEPS_DAILY, 87, GREATER_THAN), ONCE)
+            ),
+            healthEventTypes = setOf()
+        )
+        val callback = FakeCallback()
+        client.setPassiveListenerCallback(config, callback)
+        shadowOf(Looper.getMainLooper()).idle()
+        val callbackFromService = fakeService.registeredCallbacks[0]
+        val passiveGoalEvent = PassiveListenerEvent.createPermissionLostResponse()
+
+        callbackFromService.onPassiveListenerEvent(passiveGoalEvent)
+        shadowOf(Looper.getMainLooper()).idle()
+
+        assertThat(callback.onPermissionLostCalls).isEqualTo(1)
+    }
+
+    class FakeListenerService : PassiveListenerService()
+
+    internal class FakeCallback : PassiveListenerCallback {
+        var onRegisteredCalls = 0
+        val onRegistrationFailedThrowables = mutableListOf<Throwable>()
+        val dataPointsReceived = mutableListOf<DataPointContainer>()
+        val userActivityInfosReceived = mutableListOf<UserActivityInfo>()
+        val completedGoals = mutableListOf<PassiveGoal>()
+        val healthEventsReceived = mutableListOf<HealthEvent>()
+        var onPermissionLostCalls = 0
+
+        override fun onRegistered() {
+            onRegisteredCalls++
+        }
+
+        override fun onRegistrationFailed(throwable: Throwable) {
+            onRegistrationFailedThrowables += throwable
+        }
+
+        override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
+            dataPointsReceived += dataPoints
+        }
+
+        override fun onUserActivityInfoReceived(info: UserActivityInfo) {
+            userActivityInfosReceived += info
+        }
+
+        override fun onGoalCompleted(goal: PassiveGoal) {
+            completedGoals += goal
+        }
+
+        override fun onHealthEventReceived(event: HealthEvent) {
+            healthEventsReceived += event
+        }
+
+        override fun onPermissionLost() {
+            onPermissionLostCalls++
+        }
+    }
+
+    internal class FakeServiceStub : IPassiveMonitoringApiService.Stub() {
+        @JvmField
+        var apiVersion = 42
+
+        var statusCallbackAction: (IStatusCallback?) -> Unit = { it!!.onSuccess() }
+        val registerServiceRequests = mutableListOf<PassiveListenerServiceRegistrationRequest>()
+        val registerCallbackRequests = mutableListOf<PassiveListenerCallbackRegistrationRequest>()
+        val registeredCallbacks = mutableListOf<IPassiveListenerCallback>()
+        val unregisterServicePackageNames = mutableListOf<String>()
+        val unregisterCallbackPackageNames = mutableListOf<String>()
+
+        override fun getApiVersion() = 42
+
+        override fun getCapabilities(
+            request: CapabilitiesRequest?
+        ): PassiveMonitoringCapabilitiesResponse {
+            throw NotImplementedError()
+        }
+
+        override fun flush(request: FlushRequest?, statusCallback: IStatusCallback?) {
+            throw NotImplementedError()
+        }
+
+        override fun registerPassiveListenerService(
+            request: PassiveListenerServiceRegistrationRequest,
+            statusCallback: IStatusCallback
+        ) {
+            registerServiceRequests += request
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun registerPassiveListenerCallback(
+            request: PassiveListenerCallbackRegistrationRequest,
+            callback: IPassiveListenerCallback,
+            statusCallback: IStatusCallback
+        ) {
+            registerCallbackRequests += request
+            registeredCallbacks += callback
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun unregisterPassiveListenerService(
+            packageName: String,
+            statusCallback: IStatusCallback
+        ) {
+            unregisterServicePackageNames += packageName
+            statusCallbackAction.invoke(statusCallback)
+        }
+
+        override fun unregisterPassiveListenerCallback(
+            packageName: String,
+            statusCallback: IStatusCallback
+        ) {
+            unregisterCallbackPackageNames += packageName
+            statusCallbackAction.invoke(statusCallback)
+        }
+    }
+}
\ No newline at end of file
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/impl/response/PassiveMonitoringGoalResponseTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/impl/response/PassiveMonitoringGoalResponseTest.kt
new file mode 100644
index 0000000..042f45d
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/impl/response/PassiveMonitoringGoalResponseTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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.health.services.client.impl.response
+
+import androidx.health.services.client.data.ComparisonType
+import androidx.health.services.client.data.DataType.Companion.STEPS_DAILY
+import androidx.health.services.client.data.DataTypeCondition
+import androidx.health.services.client.data.PassiveGoal
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class PassiveMonitoringGoalResponseTest {
+    @Test
+    fun protoRoundTrip() {
+        val proto = PassiveMonitoringGoalResponse(
+            PassiveGoal(
+                DataTypeCondition(STEPS_DAILY, 1000, ComparisonType.GREATER_THAN),
+                PassiveGoal.TriggerFrequency.REPEATED
+            )
+        ).proto
+
+        val response = PassiveMonitoringGoalResponse(proto)
+
+        assertThat(response.passiveGoal.dataTypeCondition.dataType).isEqualTo(STEPS_DAILY)
+    }
+}
\ No newline at end of file
diff --git a/leanback/leanback/src/main/res/values-te/strings.xml b/leanback/leanback/src/main/res/values-te/strings.xml
index 58a8e1c..1ffce04 100644
--- a/leanback/leanback/src/main/res/values-te/strings.xml
+++ b/leanback/leanback/src/main/res/values-te/strings.xml
@@ -34,9 +34,9 @@
     <string name="lb_playback_controls_skip_next" msgid="8117512422682146745">"తదుపరి దానికి దాటవేయి"</string>
     <string name="lb_playback_controls_skip_previous" msgid="3481218248309447059">"మునుపటి దానికి దాటవేయి"</string>
     <string name="lb_playback_controls_more_actions" msgid="8730341244454469032">"మరిన్ని చర్యలు"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="3458671378107738666">"బాగుంది సంకేతాన్ని తీసివేయి"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="3458671378107738666">"బాగుంది సంకేతాన్ని తీసివేయండి"</string>
     <string name="lb_playback_controls_thumb_up_outline" msgid="1385865732502550659">"బాగుంది సంకేతాన్ని ఎంపిక చేయి"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="3544533410444618518">"బాగాలేదు సంకేతాన్ని తీసివేయి"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="3544533410444618518">"బాగాలేదు సంకేతాన్ని తీసివేయండి"</string>
     <string name="lb_playback_controls_thumb_down_outline" msgid="8475278766138652105">"బాగాలేదు సంకేతాన్ని ఎంపిక చేయి"</string>
     <string name="lb_playback_controls_repeat_none" msgid="1614290959784265209">"ఏదీ రిపీట్‌ చేయవద్దు"</string>
     <string name="lb_playback_controls_repeat_all" msgid="8429099206716245199">"అన్నీ రిపీట్‌ చేయి"</string>
diff --git a/libraryversions.toml b/libraryversions.toml
index 5c0c365..553a607 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -111,7 +111,7 @@
 TEST_UIAUTOMATOR = "2.3.0-alpha01"
 TEXT = "1.0.0-alpha01"
 TRACING = "1.2.0-alpha01"
-TRACING_PERFETTO = "1.0.0-alpha01"
+TRACING_PERFETTO = "1.0.0-alpha02"
 TRANSITION = "1.5.0-alpha01"
 TV = "1.0.0-alpha01"
 TVPROVIDER = "1.1.0-alpha02"
@@ -130,7 +130,7 @@
 WEAR_REMOTE_INTERACTIONS = "1.1.0-alpha01"
 WEAR_TILES = "1.1.0-beta01"
 WEAR_WATCHFACE = "1.2.0-alpha01"
-WEBKIT = "1.5.0-rc01"
+WEBKIT = "1.6.0-alpha01"
 WINDOW = "1.1.0-alpha03"
 WINDOW_EXTENSIONS = "1.1.0-alpha01"
 WINDOW_SIDECAR = "1.0.0-rc01"
diff --git a/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java b/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
index 717e9c1..a3f33fe 100644
--- a/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -1758,11 +1758,6 @@
                 Callback.this.onSetRating(RatingCompat.fromRating(ratingFwk));
                 clearCurrentControllerInfo(sessionImpl);
             }
-
-            public void onSetRating(Rating ratingFwk, Bundle extras) {
-                // This method will not be called.
-            }
-
             @Override
             @SuppressWarnings("deprecation")
             public void onCustomAction(String action, Bundle extras) {
diff --git a/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSelectorActivity.java b/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSelectorActivity.java
index 0fe7e7e..f8c2542 100644
--- a/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSelectorActivity.java
+++ b/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSelectorActivity.java
@@ -219,21 +219,13 @@
      * framework to populate the list of videos to select.
      */
     private class VideoItemList extends ArrayAdapter<VideoItem> {
-        private final String mPath;
         private final boolean mIsRoot;
-
-        private VideoItemList(String path, boolean isRoot) {
+        private VideoItemList(boolean isRoot) {
             super(VideoSelectorActivity.this,
                   R.layout.video_list_item,
                   R.id.video_list_item);
-            mPath = path;
             mIsRoot = isRoot;
         }
-
-        public String getPath() {
-            return mPath;
-        }
-
         public boolean getIsRoot() {
             return mIsRoot;
         }
@@ -247,7 +239,7 @@
             return null;
         }
 
-        VideoItemList retVal = new VideoItemList(p, is_root);
+        VideoItemList retVal = new VideoItemList(is_root);
 
         // If this is not the root directory, go ahead and add the back link to
         // our parent.
diff --git a/media2/media2-widget/src/main/java/androidx/media2/widget/Cea608CCParser.java b/media2/media2-widget/src/main/java/androidx/media2/widget/Cea608CCParser.java
index e3eb245..9b24e74 100644
--- a/media2/media2-widget/src/main/java/androidx/media2/widget/Cea608CCParser.java
+++ b/media2/media2-widget/src/main/java/androidx/media2/widget/Cea608CCParser.java
@@ -280,12 +280,6 @@
 
     private static class StyleCode {
         static final int COLOR_WHITE = 0;
-        static final int COLOR_GREEN = 1;
-        static final int COLOR_BLUE = 2;
-        static final int COLOR_CYAN = 3;
-        static final int COLOR_RED = 4;
-        static final int COLOR_YELLOW = 5;
-        static final int COLOR_MAGENTA = 6;
         static final int COLOR_INVALID = 7;
 
         static final int STYLE_ITALICS   = 0x00000001;
@@ -327,11 +321,6 @@
         boolean isUnderline() {
             return (mStyle & STYLE_UNDERLINE) != 0;
         }
-
-        int getColor() {
-            return mColor;
-        }
-
         @Override
         public String toString() {
             StringBuilder str = new StringBuilder();
diff --git a/media2/media2-widget/src/main/java/androidx/media2/widget/Cea708CaptionRenderer.java b/media2/media2-widget/src/main/java/androidx/media2/widget/Cea708CaptionRenderer.java
index 1232d70..4fff8a8 100644
--- a/media2/media2-widget/src/main/java/androidx/media2/widget/Cea708CaptionRenderer.java
+++ b/media2/media2-widget/src/main/java/androidx/media2/widget/Cea708CaptionRenderer.java
@@ -1140,10 +1140,6 @@
                 }
             }
 
-            public void setText(String text) {
-                updateText(text, false);
-            }
-
             public void appendText(String text) {
                 updateText(text, true);
             }
diff --git a/paging/paging-common/build.gradle b/paging/paging-common/build.gradle
index 8f4aacb5..ec8b5f5 100644
--- a/paging/paging-common/build.gradle
+++ b/paging/paging-common/build.gradle
@@ -26,6 +26,10 @@
     // Atomic Group
     constraints {
         implementation(project(":paging:paging-runtime"))
+        // syntax is temp workaround to allow project dependency on cross-project-set
+        // i.e. COMPOSE + MAIN project sets
+        // update syntax when b/239979823 is fixed
+        implementation("androidx.paging:paging-compose:${androidx.LibraryVersions.PAGING_COMPOSE}")
     }
 
     api("androidx.annotation:annotation:1.3.0")
diff --git a/paging/paging-compose/build.gradle b/paging/paging-compose/build.gradle
index c9fb87e..d67110f 100644
--- a/paging/paging-compose/build.gradle
+++ b/paging/paging-compose/build.gradle
@@ -26,6 +26,13 @@
 }
 
 dependencies {
+    constraints {
+        // this syntax mirrors the temp workaround in paging-common dependencies constraint
+        // which allows paging-common to have project constraint on compose even though
+        // compose is not within the `MAIN` project-set.
+        // update syntax when b/239979823 is fixed
+        implementation("androidx.paging:paging-common:${androidx.LibraryVersions.PAGING}")
+    }
 
     implementation(libs.kotlinStdlib)
     api("androidx.compose.foundation:foundation:1.0.5")
diff --git a/preference/preference/src/main/java/androidx/preference/EditTextPreference.java b/preference/preference/src/main/java/androidx/preference/EditTextPreference.java
index 97dcadd..cd75a1e 100644
--- a/preference/preference/src/main/java/androidx/preference/EditTextPreference.java
+++ b/preference/preference/src/main/java/androidx/preference/EditTextPreference.java
@@ -242,7 +242,7 @@
         @Override
         public CharSequence provideSummary(@NonNull EditTextPreference preference) {
             if (TextUtils.isEmpty(preference.getText())) {
-                return (preference.getContext().getString(R.string.not_set));
+                return preference.getContext().getString(R.string.not_set);
             } else {
                 return preference.getText();
             }
diff --git a/preference/preference/src/main/java/androidx/preference/ListPreference.java b/preference/preference/src/main/java/androidx/preference/ListPreference.java
index 89086f1..ec78894 100644
--- a/preference/preference/src/main/java/androidx/preference/ListPreference.java
+++ b/preference/preference/src/main/java/androidx/preference/ListPreference.java
@@ -349,7 +349,7 @@
         @Override
         public CharSequence provideSummary(@NonNull ListPreference preference) {
             if (TextUtils.isEmpty(preference.getEntry())) {
-                return (preference.getContext().getString(R.string.not_set));
+                return preference.getContext().getString(R.string.not_set);
             } else {
                 return preference.getEntry();
             }
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java
index ebcc890..7e2739f 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java
@@ -622,14 +622,6 @@
         final RelativeCoordinate mX;
         final RelativeCoordinate mY;
 
-        RelativePoint(
-                @NonNull List<Limits> columnLimits,
-                @NonNull List<Limits> rowLimits, Point point) {
-
-            this.mX = new RelativeCoordinate(columnLimits, point.x);
-            this.mY = new RelativeCoordinate(rowLimits, point.y);
-        }
-
         RelativePoint(@NonNull RelativeCoordinate x, @NonNull RelativeCoordinate y) {
             this.mX = x;
             this.mY = y;
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/PetDao.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/PetDao.java
index 00043be..1f9c7a4 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/PetDao.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/PetDao.java
@@ -61,6 +61,9 @@
     Flowable<Pet> petWithIdFlowable(int id);
 
     @Query("SELECT * FROM Pet WHERE Pet.mPetId = :id")
+    Pet petWithId(int id);
+
+    @Query("SELECT * FROM Pet WHERE Pet.mPetId = :id")
     LiveData<Pet> petWithIdLiveData(int id);
 
     @Query("SELECT * FROM PetWithUser WHERE mPetId = :id")
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EntityUpsertionAdapterTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EntityUpsertionAdapterTest.java
new file mode 100644
index 0000000..5e6b2fa
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EntityUpsertionAdapterTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 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.room.integration.testapp.test;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.room.EntityDeletionOrUpdateAdapter;
+import androidx.room.EntityInsertionAdapter;
+import androidx.room.EntityUpsertionAdapter;
+import androidx.room.Room;
+import androidx.room.integration.testapp.TestDatabase;
+import androidx.room.integration.testapp.dao.PetDao;
+import androidx.room.integration.testapp.vo.Pet;
+import androidx.sqlite.db.SupportSQLiteStatement;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Date;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class EntityUpsertionAdapterTest{
+    private TestDatabase mTestDatabase;
+    private PetDao mPetDao;
+
+    private EntityInsertionAdapter<Pet> mInsertionAdapter;
+
+    private EntityDeletionOrUpdateAdapter<Pet> mUpdateAdapter;
+
+    private EntityUpsertionAdapter<Pet> mUpsertionAdapter;
+
+    @Before
+    public void setUp() {
+        mTestDatabase = Room.inMemoryDatabaseBuilder(
+                        ApplicationProvider.getApplicationContext(),
+                        TestDatabase.class).build();
+        mPetDao = mTestDatabase.getPetDao();
+        mInsertionAdapter =
+                new EntityInsertionAdapter<Pet>(mTestDatabase) {
+                    @Override
+                    protected void bind(@Nullable SupportSQLiteStatement statement, Pet entity) {
+                        statement.bindLong(1, entity.getPetId());
+                        statement.bindLong(2, entity.getUserId());
+                        statement.bindString(3, entity.getName());
+                        statement.bindString(4, entity.getAdoptionDate().toString());
+                    }
+
+                    @NonNull
+                    @Override
+                    protected String createQuery() {
+                        return "INSERT INTO `Pet` (`mPetId`, `mUserId`, `mPetName`,`mAdoptionDate`)"
+                                + " VALUES (?,?,?,?)";
+                    }
+                };
+        mUpdateAdapter =
+                new EntityDeletionOrUpdateAdapter<Pet>(mTestDatabase) {
+                    @NonNull
+                    @Override
+                    protected String createQuery() {
+                        return "UPDATE `Pet` SET `mPetName` = ?, `mAdoptionDate` = ? WHERE `mPetId`"
+                                + " = ?";
+                    }
+
+                    @Override
+                    protected void bind(@NonNull SupportSQLiteStatement statement, Pet entity) {
+                        statement.bindString(1, entity.getName());
+                        statement.bindString(2, entity.getAdoptionDate().toString());
+                        statement.bindLong(3, entity.getPetId());
+                    }
+                };
+        mUpsertionAdapter =
+                new EntityUpsertionAdapter<>(mInsertionAdapter, mUpdateAdapter);
+    }
+
+    @After
+    public void tearDown() {
+        mTestDatabase.close();
+    }
+
+    @Test
+    public void testUpsert() {
+        Pet newPet = new Pet();
+        Date newDate = new Date(123456);
+        Date testDate = new Date(123458);
+        newPet.setPetId(1);
+        newPet.setName("petname");
+        newPet.setAdoptionDate(newDate);
+
+        Pet testPet = new Pet();
+        testPet.setPetId(1);
+        testPet.setName("anotherName");
+        testPet.setAdoptionDate(testDate);
+
+        mInsertionAdapter.insert(newPet);
+        mUpsertionAdapter.upsert(testPet);
+
+        assertThat(testPet.getName()).isEqualTo(mPetDao.petWithId(1).getName());
+    }
+
+    @Test
+    public void testUpsertList() {
+        Pet[] testPets = TestUtil.createPetsForUser(0, 3, 9);
+        Pet[] petArray = TestUtil.createPetsForUser(0, 1, 10);
+        mInsertionAdapter.insert(petArray);
+        mUpsertionAdapter.upsert(testPets);
+        assertThat(mPetDao.petWithId(2).getName()).isEqualTo(petArray[1].getName());
+        assertThat(mPetDao.petWithId(7).getName()).isEqualTo(testPets[4].getName());
+        assertThat(mPetDao.count()).isEqualTo(11);
+    }
+
+    @Test
+    public void testUpsertReturnId() {
+        Pet testPet = TestUtil.createPet(343562);
+        Pet testPet2 = TestUtil.createPet(343562);
+        long resultId = mUpsertionAdapter.upsertAndReturnId(testPet);
+        long result2 = mUpsertionAdapter.upsertAndReturnId(testPet2);
+        assertThat(resultId).isEqualTo(343562);
+        assertThat(result2).isEqualTo(-1);
+        assertThat(mPetDao.petWithId(343562).getName()).isEqualTo(testPet2.getName());
+    }
+
+    @Test
+    public void testUpsertReturnIds() {
+        Pet[] testPets = TestUtil.createPetsForUser(0, 1, 10);
+        Pet[] testPets2 = TestUtil.createPetsForUser(0, 5, 9);
+        long[] result = mUpsertionAdapter.upsertAndReturnIdArray(testPets);
+        long[] check = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        assertThat(result[3]).isEqualTo(check[3]);
+        long[] testResult = mUpsertionAdapter.upsertAndReturnIdArray(testPets2);
+        assertThat(testResult[8]).isEqualTo(13);
+        assertThat(testResult[2]).isEqualTo(-1);
+    }
+
+    @Test
+    public void testUpsertWithoutTryCatch() {
+        Pet testPet = TestUtil.createPet(232);
+        Pet testPet2 = TestUtil.createPet(232);
+        Pet testPet3 = TestUtil.createPet(454);
+        EntityInsertionAdapter<Pet> insertionAdapter =
+                new EntityInsertionAdapter<Pet>(mTestDatabase) {
+            @Override
+            protected void bind(@Nullable SupportSQLiteStatement statement, Pet entity) {
+                statement.bindLong(1, entity.getPetId());
+                statement.bindLong(2, entity.getUserId());
+                statement.bindString(3, entity.getName());
+                statement.bindString(4, entity.getAdoptionDate().toString());
+            }
+
+            @NonNull
+            @Override
+            protected String createQuery() {
+                return "INSERT OR IGNORE INTO `Pet` (`mPetId`, `mUserId`, `mPetName`,"
+                        + "`mAdoptionDate`)"
+                        + " VALUES (?,?,?,?)";
+            }
+        };
+        insertionAdapter.insertAndReturnId(testPet);
+        long result = insertionAdapter.insertAndReturnId(testPet2);
+        if (result == -1) {
+            mUpdateAdapter.handle(testPet2);
+        }
+        insertionAdapter.insertAndReturnId(testPet3);
+        assertThat(mPetDao.count()).isEqualTo(2);
+        assertThat(mPetDao.petWithId(232).getName()).isEqualTo(testPet2.getName());
+    }
+
+    @Test
+    public void testUpsertWithMultipleEntity() {
+        Pet[] testPets = TestUtil.createPetsForUser(0, 1, 10);
+        Pet[] testPets2 = TestUtil.createPetsForUser(0, 5, 10);
+        long[] resultArray;
+        long[] resultArray2 = new long[10];
+        EntityInsertionAdapter<Pet> insertionAdapter =
+                new EntityInsertionAdapter<Pet>(mTestDatabase) {
+                    @Override
+                    protected void bind(@Nullable SupportSQLiteStatement statement, Pet entity) {
+                        statement.bindLong(1, entity.getPetId());
+                        statement.bindLong(2, entity.getUserId());
+                        statement.bindString(3, entity.getName());
+                        statement.bindString(4, entity.getAdoptionDate().toString());
+                    }
+
+                    @NonNull
+                    @Override
+                    protected String createQuery() {
+                        return "INSERT OR IGNORE INTO `Pet` (`mPetId`, `mUserId`, `mPetName`,"
+                                + "`mAdoptionDate`)"
+                                + " VALUES (?,?,?,?)";
+                    }
+                };
+        resultArray = insertionAdapter.insertAndReturnIdsArray(testPets);
+        assertThat(resultArray[4]).isEqualTo(5);
+        for (int i = 0; i < 10; i++) {
+            resultArray2[i] = insertionAdapter.insertAndReturnId(testPets2[i]);
+            if (resultArray2[i] == -1) {
+                mUpdateAdapter.handle(testPets2[i]);
+            }
+        }
+        assertThat(resultArray2[4]).isEqualTo(-1);
+        assertThat(resultArray2[7]).isEqualTo(12);
+        assertThat(mPetDao.petWithId(6).getName()).isEqualTo(testPets2[1].getName());
+    }
+}
diff --git a/room/room-runtime/api/restricted_current.txt b/room/room-runtime/api/restricted_current.txt
index d12ab60..ddee7ea 100644
--- a/room/room-runtime/api/restricted_current.txt
+++ b/room/room-runtime/api/restricted_current.txt
@@ -56,6 +56,19 @@
     method public final java.util.List<java.lang.Long> insertAndReturnIdsList(java.util.Collection<? extends T> entities);
   }
 
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class EntityUpsertionAdapter<T> {
+    ctor public EntityUpsertionAdapter(androidx.room.EntityInsertionAdapter<T> insertionAdapter, androidx.room.EntityDeletionOrUpdateAdapter<T> updateAdapter);
+    method public void upsert(T? entity);
+    method public void upsert(T![] entities);
+    method public void upsert(Iterable<? extends T> entities);
+    method public long upsertAndReturnId(T? entity);
+    method public long[] upsertAndReturnIdArray(T![] entities);
+    method public long[] upsertAndReturnIdArray(java.util.Collection<? extends T> entities);
+    method public Long![] upsertAndReturnIdArrayBox(T![] entities);
+    method public Long![] upsertAndReturnIdArrayBox(java.util.Collection<? extends T> entities);
+    method public java.util.List<java.lang.Long> upsertAndReturnIdList(T![] entities);
+  }
+
   public class InvalidationTracker {
     ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public InvalidationTracker(androidx.room.RoomDatabase database, java.util.Map<java.lang.String,java.lang.String> shadowTablesMap, java.util.Map<java.lang.String,java.util.Set<java.lang.String>> viewTables, java.lang.String... tableNames);
     ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public InvalidationTracker(androidx.room.RoomDatabase database, java.lang.String... tableNames);
diff --git a/room/room-runtime/src/main/java/androidx/room/EntityUpsertionAdapter.kt b/room/room-runtime/src/main/java/androidx/room/EntityUpsertionAdapter.kt
new file mode 100644
index 0000000..16674b7
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/EntityUpsertionAdapter.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright 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.room
+
+import android.database.sqlite.SQLiteConstraintException
+import androidx.annotation.RestrictTo
+
+/**
+ * This class knows how to insert an entity. When the insertion fails
+ * due to a unique constraint conflict (i.e. primary key conflict),
+ * it will perform an update.
+ *
+ * @constructor Creates an EntityUpsertionAdapter that can upsert entity of type T
+ * into the database using the given insertionAdapter to perform insertion and
+ * updateAdapter to perform update when the insertion fails
+ *
+ * @param T the type param of the entity to be upserted
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+class EntityUpsertionAdapter<T>(
+    private val insertionAdapter: EntityInsertionAdapter<T>,
+    private val updateAdapter: EntityDeletionOrUpdateAdapter<T>
+) {
+    /**
+     * Inserts the entity into the database. If a constraint exception is thrown
+     * i.e. a primary key conflict, update the existing entity.
+     *
+     * @param entity The entity to insert
+     */
+    fun upsert(entity: T) {
+        try {
+            insertionAdapter.insert(entity)
+        } catch (ex: SQLiteConstraintException) {
+            updateAdapter.handle(entity)
+        }
+    }
+
+    /**
+     * Upserts (insert or update) the given entities into the database.
+     * For each entity, insert if it is not already in the database
+     * update if there is a constraint conflict.
+     *
+     * @param entities array of entities to upsert
+     */
+    fun upsert(entities: Array<T>) {
+        entities.forEach { entity ->
+            try {
+                insertionAdapter.insert(entity)
+            } catch (ex: SQLiteConstraintException) {
+                updateAdapter.handle(entity)
+            }
+        }
+    }
+
+    fun upsert(entities: Iterable<T>) {
+        entities.forEach { entity ->
+            try {
+                insertionAdapter.insert(entity)
+            } catch (ex: SQLiteConstraintException) {
+                updateAdapter.handle(entity)
+            }
+        }
+    }
+
+    /**
+     * Upserts the given entity into the database and returns the row id.
+     * If the insertion failed, update the existing entity and return -1.
+     *
+     * @param entity The entity to upsert
+     * @return The SQLite row id or -1 if the insertion failed and update
+     * is performed
+     */
+    fun upsertAndReturnId(entity: T): Long {
+        return try {
+            insertionAdapter.insertAndReturnId(entity)
+        } catch (ex: SQLiteConstraintException) {
+            updateAdapter.handle(entity)
+            return -1
+        }
+    }
+
+    /**
+     * Upserts the given entities into the database and returns the row ids.
+     *
+     * @param entities Entities to upsert
+     * @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
+     */
+    fun upsertAndReturnIdArray(entities: Array<T>): LongArray {
+        return LongArray(entities.size) { index ->
+            try {
+                insertionAdapter.insertAndReturnId(entities[index])
+            } catch (ex: SQLiteConstraintException) {
+                updateAdapter.handle(entities[index])
+                -1
+            }
+        }
+    }
+
+    fun upsertAndReturnIdArray(entities: Collection<T>): LongArray {
+        val iterator = entities.iterator()
+        return LongArray(entities.size) {
+            val entity = iterator.next()
+            try {
+                    insertionAdapter.insertAndReturnId(entity)
+                } catch (ex: SQLiteConstraintException) {
+                    updateAdapter.handle(entity)
+                    -1
+                }
+            }
+    }
+
+    fun upsertAndReturnIdList(entities: Array<T>): List<Long> {
+        return buildList<Long> {
+            entities.forEach { entity ->
+                try {
+                    add(insertionAdapter.insertAndReturnId(entity))
+                } catch (ex: SQLiteConstraintException) {
+                    updateAdapter.handle(entity)
+                    add(-1)
+                }
+            }
+        }
+    }
+
+    fun upsertAndReturnIdArrayBox(entities: Array<T>): Array<Long?> {
+        return Array<Long?>(entities.size) { index ->
+            try {
+                insertionAdapter.insertAndReturnId(entities[index])
+            } catch (ex: SQLiteConstraintException) {
+                updateAdapter.handle(entities[index])
+                -1
+            }
+        }
+    }
+
+    fun upsertAndReturnIdArrayBox(entities: Collection<T>): Array<Long?> {
+        val iterator = entities.iterator()
+        return Array<Long?>(entities.size) {
+            val entity = iterator.next()
+            try {
+                insertionAdapter.insertAndReturnId(entity)
+            } catch (ex: SQLiteConstraintException) {
+                updateAdapter.handle(entity)
+                -1
+            }
+        }
+    }
+}
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/PackageValidator.java b/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/PackageValidator.java
index 368cd2f..9e1fe11 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/PackageValidator.java
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/PackageValidator.java
@@ -67,7 +67,7 @@
                     boolean isRelease = parser.getAttributeBooleanValue(null, "release", false);
                     String certificate = parser.nextText().replaceAll("\\s|\\n", "");
 
-                    CallerInfo info = new CallerInfo(name, packageName, isRelease, certificate);
+                    CallerInfo info = new CallerInfo(name, packageName, isRelease);
 
                     ArrayList<CallerInfo> infos = validCertificates.get(certificate);
                     if (infos == null) {
@@ -147,14 +147,11 @@
         final String name;
         final String packageName;
         final boolean release;
-        final String signingCertificate;
 
-        public CallerInfo(String name, String packageName, boolean release,
-                          String signingCertificate) {
+        CallerInfo(String name, String packageName, boolean release) {
             this.name = name;
             this.packageName = packageName;
             this.release = release;
-            this.signingCertificate = signingCertificate;
         }
     }
 }
diff --git a/samples/SupportLeanbackDemos/src/main/java/com/example/android/leanback/PagedRowPresenter.java b/samples/SupportLeanbackDemos/src/main/java/com/example/android/leanback/PagedRowPresenter.java
index 02b75d9..a48b5e3 100644
--- a/samples/SupportLeanbackDemos/src/main/java/com/example/android/leanback/PagedRowPresenter.java
+++ b/samples/SupportLeanbackDemos/src/main/java/com/example/android/leanback/PagedRowPresenter.java
@@ -26,9 +26,7 @@
 import androidx.leanback.widget.ListRowPresenter;
 import androidx.leanback.widget.RowPresenter;
 import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LiveData;
 import androidx.lifecycle.ViewModelProvider;
-import androidx.paging.PagingData;
 
 /**
  * PagedRowPresenter
@@ -63,16 +61,9 @@
                 pagingData -> adapter.submitData(mLifecycleOwner.getLifecycle(), pagingData));
     }
     private static class LiveDataRowPresenterViewHolder extends ListRowPresenter.ViewHolder {
-        private LiveData<PagingData<PhotoItem>> mLiveData;
         LiveDataRowPresenterViewHolder(View rootView, HorizontalGridView gridView,
                 ListRowPresenter p) {
             super(rootView, gridView, p);
         }
-        public void setLiveData(LiveData<PagingData<PhotoItem>> liveData) {
-            mLiveData = liveData;
-        }
-        public final LiveData<PagingData<PhotoItem>> getLiveData() {
-            return mLiveData;
-        }
     }
 }
diff --git a/slice/slice-builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java b/slice/slice-builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java
index 56257a0..184cd4e 100644
--- a/slice/slice-builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java
+++ b/slice/slice-builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java
@@ -27,7 +27,6 @@
 import static androidx.slice.core.SliceHints.HINT_OVERLAY;
 
 import android.app.PendingIntent;
-import android.net.Uri;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -175,12 +174,6 @@
 
         /**
          */
-        private CellBuilderImpl(@NonNull Uri uri) {
-            super(new Slice.Builder(uri), null);
-        }
-
-        /**
-         */
         private void addText(@Nullable CharSequence text, boolean isLoading) {
             @Slice.SliceHint String[] hints = isLoading
                     ? new String[] {HINT_PARTIAL}
diff --git a/slice/slice-builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java b/slice/slice-builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java
index 3fcdd9c..1c34c39 100644
--- a/slice/slice-builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java
+++ b/slice/slice-builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java
@@ -50,7 +50,6 @@
 import static androidx.slice.core.SliceHints.SUBTYPE_SELECTION;
 
 import android.app.PendingIntent;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.PersistableBundle;
 
@@ -605,18 +604,6 @@
 
         /**
          */
-        private RowBuilderImpl(@NonNull ListBuilderImpl parent) {
-            super(parent.createChildBuilder(), null);
-        }
-
-        /**
-         */
-        private RowBuilderImpl(@NonNull Uri uri) {
-            super(new Slice.Builder(uri), null);
-        }
-
-        /**
-         */
         RowBuilderImpl(Slice.Builder builder) {
             super(builder, null);
         }
@@ -821,13 +808,6 @@
         HeaderBuilderImpl(@NonNull ListBuilderImpl parent) {
             super(parent.createChildBuilder(), null);
         }
-
-        /**
-         */
-        private HeaderBuilderImpl(@NonNull Uri uri) {
-            super(new Slice.Builder(uri), null);
-        }
-
         void fillFrom(HeaderBuilder builder) {
             if (builder.getUri() != null) {
                 setBuilder(new Slice.Builder(builder.getUri()));
diff --git a/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java b/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
index 9de4b03..8da3c43 100644
--- a/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
+++ b/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
@@ -193,8 +193,6 @@
 
     private final Rect mTmpRect = new Rect();
 
-    final ArrayList<DisableLayerRunnable> mPostedRunnables = new ArrayList<>();
-
     @LockMode
     private int mLockMode;
 
@@ -594,11 +592,6 @@
         if (mFoldingFeatureObserver != null) {
             mFoldingFeatureObserver.unregisterLayoutStateChangeCallback();
         }
-        for (int i = 0, count = mPostedRunnables.size(); i < count; i++) {
-            final DisableLayerRunnable dlr = mPostedRunnables.get(i);
-            dlr.run();
-        }
-        mPostedRunnables.clear();
     }
 
     @Override
@@ -1831,23 +1824,6 @@
         }
     }
 
-    private class DisableLayerRunnable implements Runnable {
-        final View mChildView;
-
-        DisableLayerRunnable(View childView) {
-            mChildView = childView;
-        }
-
-        @Override
-        public void run() {
-            if (mChildView.getParent() == SlidingPaneLayout.this) {
-                mChildView.setLayerType(View.LAYER_TYPE_NONE, null);
-                invalidateChildRegion(mChildView);
-            }
-            mPostedRunnables.remove(this);
-        }
-    }
-
     boolean isLayoutRtlSupport() {
         return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
     }
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiWindowTests.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiWindowTests.java
index aa2ef86..ff9a7bd 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiWindowTests.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiWindowTests.java
@@ -16,12 +16,14 @@
 
 package androidx.test.uiautomator.testapp;
 
+import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
 import static androidx.test.uiautomator.testapp.SplitScreenTestActivity.WINDOW_ID;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -31,6 +33,7 @@
 
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiObject2;
@@ -38,48 +41,50 @@
 
 import org.junit.Test;
 
+import java.util.Arrays;
+import java.util.List;
+
 /** Integration tests for multi-window support. */
 @LargeTest
 public class MultiWindowTests extends BaseTest {
 
-    private static final long TIMEOUT_MS = 10_000;
+    private static final long TIMEOUT_MS = 30_000;
     private static final long DELAY_MS = 5_000;
 
     @Test
     @SdkSuppress(minSdkVersion = 21)
-    public void testHasStatusBar() {
+    public void testMultiWindow_statusBar() {
+        // Can locate objects outside of current context.
         assertTrue(mDevice.hasObject(By.res("com.android.systemui", "status_bar")));
     }
 
     @Test
     @SdkSuppress(minSdkVersion = 24)
-    public void testPictureInPicture() {
+    public void testMultiWindow_pictureInPicture() {
         BySelector defaultMode = By.res(TEST_APP, "pip_mode").text("Default Mode");
         BySelector pipMode = By.res(TEST_APP, "pip_mode").text("PiP Mode");
 
         // Create window in PiP mode and verify its location (bounds correctly calculated).
         launchTestActivity(PictureInPictureTestActivity.class);
-        mDevice.findObject(By.res(TEST_APP, "pip_button")).click();
+        assertTrue(mDevice.hasObject(defaultMode));
+        mDevice.pressHome();
         SystemClock.sleep(DELAY_MS); // Wait for the PiP window to settle.
         int width = mDevice.getDisplayWidth();
         int height = mDevice.getDisplayHeight();
         Rect bottomHalf = new Rect(0, height / 2, width, height);
         UiObject2 pipWindow = mDevice.wait(Until.findObject(pipMode), TIMEOUT_MS);
+        assertNotNull("Timed out waiting for PiP window", pipWindow);
         assertTrue(bottomHalf.contains(pipWindow.getVisibleBounds()));
-
-        // Create window in default mode and verify that both windows are present and searchable.
-        launchTestActivity(PictureInPictureTestActivity.class,
-                new Intent().setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK));
-        SystemClock.sleep(DELAY_MS); // Wait for the PiP window to settle.
-        assertTrue(mDevice.wait(Until.hasObject(pipMode), TIMEOUT_MS));
-        assertTrue(mDevice.hasObject(defaultMode));
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 32)
-    public void testSplitScreen() {
+    @SdkSuppress(minSdkVersion = 31)
+    public void testMultiWindow_splitScreen() {
+        // Launch two split-screen activities with different IDs.
         launchTestActivity(SplitScreenTestActivity.class,
                 new Intent().setFlags(DEFAULT_FLAGS).putExtra(WINDOW_ID, "first"));
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .performGlobalAction(GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
         launchTestActivity(SplitScreenTestActivity.class,
                 new Intent().setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_ADJACENT
                         | FLAG_ACTIVITY_MULTIPLE_TASK).putExtra(WINDOW_ID, "second"));
@@ -91,10 +96,38 @@
         UiObject2 secondWindow = mDevice.findObject(By.res(TEST_APP, "window_id").text("second"));
         assertNotNull(secondWindow);
 
-        // Window IDs are centered in each window (bounds correctly calculated).
+        // Window IDs are centered in each window (bounds correctly calculated; order independent).
         int width = mDevice.getDisplayWidth();
         int height = mDevice.getDisplayHeight();
-        assertTrue(firstWindow.getVisibleBounds().contains(width / 2, height / 4));
-        assertTrue(secondWindow.getVisibleBounds().contains(width / 2, 3 * height / 4));
+        List<UiObject2> windows = Arrays.asList(firstWindow, secondWindow);
+        assertTrue(windows.stream().anyMatch(
+                w -> w.getVisibleBounds().contains(width / 2, height / 4)));
+        assertTrue(windows.stream().anyMatch(
+                w -> w.getVisibleBounds().contains(width / 2, 3 * height / 4)));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 31)
+    public void testMultiWindow_click() {
+        // Launch two split-screen activities with buttons.
+        launchTestActivity(UiDeviceTestClickActivity.class);
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .performGlobalAction(GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
+        launchTestActivity(UiDeviceTestClickActivity.class,
+                new Intent().setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_ADJACENT
+                        | FLAG_ACTIVITY_MULTIPLE_TASK));
+        SystemClock.sleep(DELAY_MS); // Wait for the windows to settle.
+
+        // Click a button in the middle of each activity.
+        int width = mDevice.getDisplayWidth();
+        int height = mDevice.getDisplayHeight();
+        mDevice.click(width / 2, height / 4);
+        mDevice.click(width / 2, 3 * height / 4);
+
+        // Verify that both buttons were clicked.
+        List<UiObject2> buttons = mDevice.findObjects(By.res(TEST_APP, "button"));
+        assertEquals(2, buttons.size());
+        assertEquals("I've been clicked!", buttons.get(0).getText());
+        assertEquals("I've been clicked!", buttons.get(1).getText());
     }
 }
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
index e1a2e0b..795e746 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
@@ -16,18 +16,10 @@
 
 package androidx.test.uiautomator.testapp;
 
-import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import android.content.Intent;
-import android.os.SystemClock;
-
 import androidx.test.filters.LargeTest;
-import androidx.test.filters.SdkSuppress;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiObject2;
 
@@ -38,7 +30,6 @@
 import org.w3c.dom.Element;
 
 import java.io.File;
-import java.util.List;
 
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.xpath.XPathConstants;
@@ -48,8 +39,6 @@
 @LargeTest
 public class UiDeviceTest extends BaseTest {
 
-    private static final long DELAY_MS = 5_000;
-
     @Rule
     public TemporaryFolder mTmpDir = new TemporaryFolder();
 
@@ -69,29 +58,6 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 32)
-    public void testClick_multiWindow() {
-        // Launch two split-screen activities with buttons.
-        launchTestActivity(UiDeviceTestClickActivity.class);
-        launchTestActivity(UiDeviceTestClickActivity.class,
-                new Intent().setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_ADJACENT
-                        | FLAG_ACTIVITY_MULTIPLE_TASK));
-        SystemClock.sleep(DELAY_MS); // Wait for the windows to settle.
-
-        // Click a button in the middle of each activity.
-        int width = mDevice.getDisplayWidth();
-        int height = mDevice.getDisplayHeight();
-        mDevice.click(width / 2, height / 4);
-        mDevice.click(width / 2, 3 * height / 4);
-
-        // Verify that both buttons were clicked.
-        List<UiObject2> buttons = mDevice.findObjects(By.res(TEST_APP, "button"));
-        assertEquals(2, buttons.size());
-        assertEquals("I've been clicked!", buttons.get(0).getText());
-        assertEquals("I've been clicked!", buttons.get(1).getText());
-    }
-
-    @Test
     public void testDumpWindowHierarchy() throws Exception {
         launchTestActivity(MainActivity.class);
         File outFile = mTmpDir.newFile();
@@ -114,7 +80,7 @@
         assertEquals("true", element.getAttribute("clickable"));
         assertEquals("true", element.getAttribute("enabled"));
         assertEquals("true", element.getAttribute("focusable"));
-        assertEquals("false", element.getAttribute("focused"));
+        assertNotNull(element.getAttribute("focused"));
         assertEquals("false", element.getAttribute("scrollable"));
         assertEquals("false", element.getAttribute("long-clickable"));
         assertEquals("false", element.getAttribute("password"));
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java
index 1989ab4..47b3acd 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java
@@ -28,6 +28,7 @@
 import android.graphics.Rect;
 import android.view.ViewConfiguration;
 
+import androidx.test.filters.SdkSuppress;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.Direction;
 import androidx.test.uiautomator.UiObject2;
@@ -130,7 +131,7 @@
     }
 
     @Test
-    public void testClickAndWait() {
+    public void testClickAndWait_conditionAndTimeout() {
         launchTestActivity(UiObject2TestClickAndWaitActivity.class);
 
         // Click the button and wait for a new window
@@ -139,6 +140,62 @@
     }
 
     @Test
+    public void testClickAndWait_pointAndConditionAndTimeout() {
+        launchTestActivity(UiObject2TestClickAndWaitActivity.class);
+
+        // Click point inside the button.
+        UiObject2 button1 = mDevice.findObject(By.res(TEST_APP, "new_window_button"));
+        assertTrue(button1.clickAndWait(getPointInsideBounds(button1), Until.newWindow(),
+                TIMEOUT_MS));
+
+        // Click point outside the button.
+        UiObject2 button2 = mDevice.findObject(By.res(TEST_APP, "new_window_button"));
+        assertTrue(button2.clickAndWait(getPointOutsideBounds(button2), Until.newWindow(),
+                TIMEOUT_MS));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 24)
+    public void testDrag_dest() {
+        launchTestActivity(UiObject2TestDragActivity.class);
+
+        UiObject2 dragButton = mDevice.findObject(By.res(TEST_APP, "drag_button"));
+        UiObject2 dragDestination = mDevice.findObject(By.res(TEST_APP, "drag_destination"));
+        Point dest = dragDestination.getVisibleCenter();
+        assertEquals("no_drag_yet", dragDestination.getText());
+        dragButton.drag(dest);
+        dragDestination.wait(Until.textEquals("drag_received"), TIMEOUT_MS);
+        assertEquals("drag_received", dragDestination.getText());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 24)
+    public void testDrag_destAndSpeed() {
+        launchTestActivity(UiObject2TestDragActivity.class);
+
+        UiObject2 dragButton = mDevice.findObject(By.res(TEST_APP, "drag_button"));
+        UiObject2 dragDestination = mDevice.findObject(By.res(TEST_APP, "drag_destination"));
+        Point dest = dragDestination.getVisibleCenter();
+        assertEquals("no_drag_yet", dragDestination.getText());
+        dragButton.drag(dest, 1000);
+        dragDestination.wait(Until.textEquals("drag_received"), TIMEOUT_MS);
+        assertEquals("drag_received", dragDestination.getText());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 24)
+    public void testDrag_destAndSpeed_throwsIllegalArgumentException() {
+        launchTestActivity(UiObject2TestDragActivity.class);
+
+        UiObject2 dragButton = mDevice.findObject(By.res(TEST_APP, "drag_button"));
+        UiObject2 dragDestination = mDevice.findObject(By.res(TEST_APP, "drag_destination"));
+        Point dest = dragDestination.getVisibleCenter();
+        assertEquals("no_drag_yet", dragDestination.getText());
+        assertThrows("Speed cannot be negative", IllegalArgumentException.class,
+                () -> dragButton.drag(dest, -1000));
+    }
+
+    @Test
     public void testEquals() {
         launchTestActivity(MainActivity.class);
 
@@ -432,7 +489,7 @@
     }
 
     @Test
-    public void testLongClickButton() {
+    public void testLongClick() {
         launchTestActivity(UiObject2TestLongClickActivity.class);
 
         // Find the button and verify its initial state
@@ -619,8 +676,6 @@
     }
 
     /* TODO(b/235841473): Implement these tests
-    public void testDrag() {}
-
     public void testFling() {}
 
     public void testSwipe() {}
@@ -630,7 +685,7 @@
     public void testWaitForGone() {}
     */
 
-    /* Helper method for `testClick*()`. Get a point inside the object. */
+    /* Helper method to get a point inside the object. */
     private Point getPointInsideBounds(UiObject2 obj) {
         Rect objBounds = obj.getVisibleBounds();
         int pointX = objBounds.left + objBounds.width() / 3;
@@ -638,7 +693,7 @@
         return new Point(pointX, pointY);
     }
 
-    /* Helper method for `testClick*()`. Get a point outside the object. */
+    /* Helper method to get a point outside the object. */
     private Point getPointOutsideBounds(UiObject2 obj) {
         Rect objBounds = obj.getVisibleBounds();
         int pointX = objBounds.right + objBounds.width() / 3;
diff --git a/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml b/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
index 8de1258..b3a03eb 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -94,7 +94,7 @@
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
         </activity>
-        <activity android:name=".UiObject2TestClickAndWaitConfirmActivity"
+        <activity android:name=".UiObject2TestDragActivity"
             android:exported="true"
             android:theme="@android:style/Theme.Holo.NoActionBar">
             <intent-filter>
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/PictureInPictureTestActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/PictureInPictureTestActivity.java
index a93774b..be37fd2 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/PictureInPictureTestActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/PictureInPictureTestActivity.java
@@ -18,8 +18,6 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
@@ -29,24 +27,20 @@
 @RequiresApi(24)
 public class PictureInPictureTestActivity extends Activity {
 
-    private TextView mMode;
-    private Button mButton;
-
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.pip_test_activity);
+    }
 
-        mMode = (TextView) findViewById(R.id.pip_mode);
-        mButton = (Button) findViewById(R.id.pip_button);
-        mButton.setOnClickListener(v -> enterPictureInPictureMode());
+    @Override
+    public void onUserLeaveHint() {
+        enterPictureInPictureMode();
     }
 
     @Override
     public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        if (isInPictureInPictureMode) {
-            mMode.setText("PiP Mode");
-            mButton.setVisibility(View.GONE);
-        }
+        TextView mode = findViewById(R.id.pip_mode);
+        mode.setText(isInPictureInPictureMode ? "PiP Mode" : "Default Mode");
     }
 }
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitActivity.java
index 79e46dd..96a6adb 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitActivity.java
@@ -19,7 +19,9 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.Handler;
 import android.view.View;
+import android.widget.Button;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -34,7 +36,11 @@
     }
 
     public void launchNewWindow(@NonNull View v) {
-        Intent intent = new Intent(this, UiObject2TestClickAndWaitConfirmActivity.class);
-        startActivity(intent);
+        ((Button) v).append("_clicked");
+        new Handler().postDelayed(() -> {
+            Intent intent = new Intent(getApplicationContext(),
+                    UiObject2TestClickAndWaitActivity.class);
+            startActivity(intent);
+        }, 2_000);
     }
 }
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitConfirmActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitConfirmActivity.java
deleted file mode 100644
index 72fd28a..0000000
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitConfirmActivity.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2014 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.test.uiautomator.testapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import androidx.annotation.Nullable;
-
-public class UiObject2TestClickAndWaitConfirmActivity extends Activity {
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.uiobject2_testclickandwaitconfirm_activity);
-    }
-}
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestDragActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestDragActivity.java
new file mode 100644
index 0000000..be56bfd
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestDragActivity.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package androidx.test.uiautomator.testapp;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.DragEvent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+@RequiresApi(24)
+public class UiObject2TestDragActivity extends Activity {
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.uiobject2_testdrag_activity);
+
+        Button dragButton = (Button) findViewById(R.id.drag_button);
+        TextView dragDestination = (TextView) findViewById(R.id.drag_destination);
+
+        dragButton.setOnLongClickListener(v -> {
+            v.startDragAndDrop(ClipData.newPlainText(null, null),
+                    new View.DragShadowBuilder(dragButton),
+                    null,
+                    0);
+            return true;
+        });
+
+        dragButton.setOnDragListener((v, e) -> {
+            if (e.getAction() == DragEvent.ACTION_DRAG_ENDED) {
+                Rect destRegion = new Rect();
+                dragDestination.getGlobalVisibleRect(destRegion);
+                if (destRegion.contains((int) e.getX(), (int) e.getY())) {
+                    dragDestination.setText("drag_received");
+                }
+            }
+            return true;
+        });
+    }
+}
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/pip_test_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/pip_test_activity.xml
index 3b121dd..de7d8d9 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/res/layout/pip_test_activity.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/pip_test_activity.xml
@@ -27,10 +27,4 @@
         android:layout_height="wrap_content"
         android:text="Default Mode" />
 
-    <Button
-        android:id="@+id/pip_button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="Enter PiP" />
-
 </LinearLayout>
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/split_screen_test_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/split_screen_test_activity.xml
index bc27d87..c1e936b 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/res/layout/split_screen_test_activity.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/split_screen_test_activity.xml
@@ -25,6 +25,7 @@
         android:id="@+id/window_id"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:minHeight="200px"/>
+        android:minHeight="200px"
+        android:gravity="center"/>
 
 </LinearLayout>
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testclickandwait_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testclickandwait_activity.xml
index d9c8163..36150b9 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testclickandwait_activity.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testclickandwait_activity.xml
@@ -24,6 +24,7 @@
         android:id="@+id/new_window_button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:onClick="launchNewWindow" />
+        android:onClick="launchNewWindow"
+        android:text="new_window_button" />
 
 </LinearLayout>
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testclickandwaitconfirm_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testdrag_activity.xml
similarity index 76%
rename from test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testclickandwaitconfirm_activity.xml
rename to test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testdrag_activity.xml
index 1e7d3e0..aa9e570 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testclickandwaitconfirm_activity.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testdrag_activity.xml
@@ -18,12 +18,18 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    tools:context=".UiObject2TestClickAndWaitConfirmActivity">
+    tools:context=".UiObject2TestDragActivity">
 
-    <TextView
-        android:id="@+id/confirm_text"
+    <Button
+        android:id="@+id/drag_button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="confirm" />
+        android:text="drag_button" />
+
+    <TextView
+        android:id="@+id/drag_destination"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="no_drag_yet" />
 
 </LinearLayout>
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
index 3177ad2..6883eb3 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
@@ -501,7 +501,7 @@
         if (drag)
             SystemClock.sleep(REGULAR_CLICK_LENGTH);
         ret &= touchUp(upX, upY);
-        return(ret);
+        return ret;
     }
 
     /**
@@ -546,7 +546,7 @@
             }
         }
         ret &= touchUp(segments[segments.length - 1].x, segments[segments.length -1].y);
-        return(ret);
+        return ret;
     }
 
 
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
index ab8466c1..3a9700e 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
@@ -560,7 +560,7 @@
     public boolean click(int x, int y) {
         Tracer.trace(x, y);
         if (x >= getDisplayWidth() || y >= getDisplayHeight()) {
-            return (false);
+            return false;
         }
         return getInteractionController().clickNoSync(x, y);
     }
@@ -980,7 +980,8 @@
             @Override
             public boolean accept(AccessibilityEvent t) {
                 if (t.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
-                    return packageName == null || packageName.contentEquals(t.getPackageName());
+                    return packageName == null || (t.getPackageName() != null
+                            && packageName.contentEquals(t.getPackageName()));
                 }
                 return false;
             }
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
index a4332c6..2712266 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
@@ -700,7 +700,7 @@
         }
 
         CharSequence currentText = node.getText();
-        if (!text.contentEquals(currentText)) {
+        if (currentText == null || !text.contentEquals(currentText)) {
             InteractionController ic = getDevice().getInteractionController();
 
             // Long click left + center
@@ -739,7 +739,7 @@
             }
         } else {
             CharSequence currentText = node.getText();
-            if (!text.contentEquals(currentText)) {
+            if (currentText == null || !text.contentEquals(currentText)) {
                 // Give focus to the object. Expect this to fail if the object already has focus.
                 if (!node.performAction(AccessibilityNodeInfo.ACTION_FOCUS) && !node.isFocused()) {
                     // TODO: Decide if we should throw here
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java
index 8983175..7a463ac 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java
@@ -275,12 +275,12 @@
         // if we happen to be on top of the text we want then return here
         UiSelector childSelector = getSelector().childSelector(selector);
         if (exists(childSelector)) {
-            return (true);
+            return true;
         } else {
             // we will need to reset the search from the beginning to start search
             scrollToBeginning(mMaxSearchSwipes);
             if (exists(childSelector)) {
-                return (true);
+                return true;
             }
             for (int x = 0; x < mMaxSearchSwipes; x++) {
                 boolean scrolled = scrollForward();
diff --git a/testutils/testutils-datastore/build.gradle b/testutils/testutils-datastore/build.gradle
index 0888962..0ab9be5 100644
--- a/testutils/testutils-datastore/build.gradle
+++ b/testutils/testutils-datastore/build.gradle
@@ -21,9 +21,9 @@
 }
 androidXMultiplatform {
     jvm {}
-    macosX64()
-    linuxX64()
-    macosArm64()
+    mac()
+    linux()
+    ios()
 
     sourceSets {
         commonMain {
diff --git a/testutils/testutils-kmp/build.gradle b/testutils/testutils-kmp/build.gradle
index da0db456..8a85a37 100644
--- a/testutils/testutils-kmp/build.gradle
+++ b/testutils/testutils-kmp/build.gradle
@@ -22,9 +22,9 @@
 
 androidXMultiplatform {
     jvm {}
-    macosX64()
-    linuxX64()
-    macosArm64()
+    mac()
+    linux()
+    ios()
 
     sourceSets {
         commonMain {
diff --git a/tracing/tracing-perfetto-binary/src/main/cpp/tracing_perfetto.cc b/tracing/tracing-perfetto-binary/src/main/cpp/tracing_perfetto.cc
index 662c2ff..0465a06 100644
--- a/tracing/tracing-perfetto-binary/src/main/cpp/tracing_perfetto.cc
+++ b/tracing/tracing-perfetto-binary/src/main/cpp/tracing_perfetto.cc
@@ -25,7 +25,7 @@
 // Concept of version useful e.g. for human-readable error messages, and stable once released.
 // Does not replace the need for a binary verification mechanism (e.g. checksum check).
 // TODO: populate using CMake
-#define VERSION "1.0.0-alpha01"
+#define VERSION "1.0.0-alpha02"
 
 namespace tracing_perfetto {
     void RegisterWithPerfetto() {
diff --git a/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/jni/test/PerfettoNativeTest.kt b/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/jni/test/PerfettoNativeTest.kt
index 1d1b403..0231115 100644
--- a/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/jni/test/PerfettoNativeTest.kt
+++ b/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/jni/test/PerfettoNativeTest.kt
@@ -30,7 +30,7 @@
         init {
             PerfettoNative.loadLib()
         }
-        const val libraryVersion = "1.0.0-alpha01" // TODO: get using reflection
+        const val libraryVersion = "1.0.0-alpha02" // TODO: get using reflection
     }
 
     @Test
diff --git a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/jni/PerfettoNative.kt b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/jni/PerfettoNative.kt
index dad0523..120b352 100644
--- a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/jni/PerfettoNative.kt
+++ b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/jni/PerfettoNative.kt
@@ -23,12 +23,12 @@
 
     // TODO(224510255): load from a file produced at build time
     object Metadata {
-        const val version = "1.0.0-alpha01"
+        const val version = "1.0.0-alpha02"
         val checksums = mapOf(
-            "arm64-v8a" to "3e42560024c7311c55ad09ffbb9228806303c4e42be4bc6fa8f66ea77d48999a",
-            "armeabi-v7a" to "46d84ff8765f87e5673ba1fc93ba84c79f9d01509770b99f5f68210498bdd8dc",
-            "x86" to "18d4aeca911080983bf05fd69bef153ed707aee431d5ccf1f55e7d1ae62904e5",
-            "x86_64" to "f54fb5fdeb75ef414116d6169d1fc1ba12c034056b0b98c58690661090312d10",
+            "arm64-v8a" to "d6187ec5bd682c48b50416c0486893cb721a816e3f28cee63bdf5e8af4e2cb36",
+            "armeabi-v7a" to "ccae81fba02dcb139bd567ffd804de444d02b2435eddbd2ecfe597ce824cf3af",
+            "x86" to "a796d80749ce7defc11ad9c0ad89ec783fd13fa52ce42b75ea562d0bfbb51714",
+            "x86_64" to "e9064001c7267bb6a77c5f521a00aef396bb8f64e8fd78b62df23a1bba7b18ce",
         )
     }
 
diff --git a/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java b/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java
index 2069f89..cf38e41 100644
--- a/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java
+++ b/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java
@@ -163,18 +163,6 @@
         PathDataEvaluator() {
         }
 
-        /**
-         * Create a PathDataEvaluator that reuses <code>nodeArray</code> for every evaluate() call.
-         * Caution must be taken to ensure that the value returned from
-         * {@link android.animation.ValueAnimator#getAnimatedValue()} is not cached, modified, or
-         * used across threads. The value will be modified on each <code>evaluate()</code> call.
-         *
-         * @param nodeArray The array to modify and return from <code>evaluate</code>.
-         */
-        PathDataEvaluator(PathParser.PathDataNode[] nodeArray) {
-            mNodeArray = nodeArray;
-        }
-
         @Override
         public PathParser.PathDataNode[] evaluate(float fraction,
                 PathParser.PathDataNode[] startPathData,
diff --git a/wear/compose/compose-material/api/current.txt b/wear/compose/compose-material/api/current.txt
index ec70a69..24edf96 100644
--- a/wear/compose/compose-material/api/current.txt
+++ b/wear/compose/compose-material/api/current.txt
@@ -35,8 +35,10 @@
   }
 
   public final class ButtonKt {
-    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void CompactButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional float backgroundPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<? extends kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,? extends kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void CompactButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional float backgroundPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void CompactButton(kotlin.jvm.functions.Function0<? extends kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional float backgroundPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,? extends kotlin.Unit> content);
   }
 
   public final class CardDefaults {
@@ -593,7 +595,8 @@
   }
 
   public final class ToggleButtonKt {
-    method @androidx.compose.runtime.Composable public static void ToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void ToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,? extends kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,? extends kotlin.Unit> content);
   }
 
   @androidx.compose.runtime.Stable public interface ToggleChipColors {
diff --git a/wear/compose/compose-material/api/public_plus_experimental_current.txt b/wear/compose/compose-material/api/public_plus_experimental_current.txt
index 648c654..998bb35 100644
--- a/wear/compose/compose-material/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-material/api/public_plus_experimental_current.txt
@@ -35,8 +35,10 @@
   }
 
   public final class ButtonKt {
-    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void CompactButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional float backgroundPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<? extends kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,? extends kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void CompactButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional float backgroundPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void CompactButton(kotlin.jvm.functions.Function0<? extends kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional float backgroundPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,? extends kotlin.Unit> content);
   }
 
   public final class CardDefaults {
@@ -672,7 +674,8 @@
   }
 
   public final class ToggleButtonKt {
-    method @androidx.compose.runtime.Composable public static void ToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void ToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,? extends kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,? extends kotlin.Unit> content);
   }
 
   @androidx.compose.runtime.Stable public interface ToggleChipColors {
diff --git a/wear/compose/compose-material/api/restricted_current.txt b/wear/compose/compose-material/api/restricted_current.txt
index ec70a69..24edf96 100644
--- a/wear/compose/compose-material/api/restricted_current.txt
+++ b/wear/compose/compose-material/api/restricted_current.txt
@@ -35,8 +35,10 @@
   }
 
   public final class ButtonKt {
-    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void CompactButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional float backgroundPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<? extends kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,? extends kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void CompactButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional float backgroundPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void CompactButton(kotlin.jvm.functions.Function0<? extends kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ButtonColors colors, optional float backgroundPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,? extends kotlin.Unit> content);
   }
 
   public final class CardDefaults {
@@ -593,7 +595,8 @@
   }
 
   public final class ToggleButtonKt {
-    method @androidx.compose.runtime.Composable public static void ToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable public static void ToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,? extends kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,? extends kotlin.Unit> content);
   }
 
   @androidx.compose.runtime.Stable public interface ToggleChipColors {
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ButtonTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ButtonTest.kt
index dede24f..5fe12c5 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ButtonTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ButtonTest.kt
@@ -22,11 +22,13 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.CutCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.testTag
@@ -459,6 +461,71 @@
     }
 }
 
+public class ButtonShapeTest {
+    @get:Rule
+    public val rule = createComposeRule()
+
+    @Test
+    public fun default_button_shape_is_circle() {
+        rule.isShape(CircleShape) { modifier ->
+            Button(
+                onClick = {},
+                enabled = true,
+                colors = ButtonDefaults.primaryButtonColors(),
+                modifier = modifier
+            ) {
+            }
+        }
+    }
+
+    @Test
+    public fun allows_custom_button_shape_override() {
+        val shape = CutCornerShape(4.dp)
+
+        rule.isShape(shape) { modifier ->
+            Button(
+                onClick = {},
+                enabled = true,
+                colors = ButtonDefaults.primaryButtonColors(),
+                modifier = modifier,
+                shape = shape
+            ) {
+            }
+        }
+    }
+
+    @Test
+    public fun default_compact_button_shape_is_circle() {
+        rule.isShape(CircleShape) { modifier ->
+            CompactButton(
+                onClick = {},
+                enabled = true,
+                colors = ButtonDefaults.primaryButtonColors(),
+                backgroundPadding = 0.dp,
+                modifier = modifier
+            ) {
+            }
+        }
+    }
+
+    @Test
+    public fun allows_custom_compact_button_shape_override() {
+        val shape = CutCornerShape(4.dp)
+
+        rule.isShape(shape) { modifier ->
+            CompactButton(
+                onClick = {},
+                enabled = true,
+                colors = ButtonDefaults.primaryButtonColors(),
+                backgroundPadding = 0.dp,
+                modifier = modifier,
+                shape = shape
+            ) {
+            }
+        }
+    }
+}
+
 public class ButtonColorTest {
     @get:Rule
     public val rule = createComposeRule()
@@ -903,6 +970,38 @@
         )
 }
 
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+private fun ComposeContentTestRule.isShape(
+    expectedShape: Shape,
+    content: @Composable (Modifier) -> Unit
+) {
+    var background = Color.Transparent
+    var buttonColor = Color.Transparent
+    val padding = 0.dp
+
+    setContentWithTheme {
+        background = MaterialTheme.colors.surface
+        buttonColor = MaterialTheme.colors.primary
+        content(
+            Modifier
+                .testTag(TEST_TAG)
+                .padding(padding)
+                .background(background))
+    }
+
+    onNodeWithTag(TEST_TAG)
+        .captureToImage()
+        .assertShape(
+            density = density,
+            horizontalPadding = 0.dp,
+            verticalPadding = 0.dp,
+            shapeColor = buttonColor,
+            backgroundColor = background,
+            shapeOverlapPixelCount = 2.0f,
+            shape = expectedShape,
+        )
+}
+
 internal enum class TapSize(val size: Dp) {
     Small(48.dp),
     Default(52.dp),
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
index b6b7a7e..4ec2eb0 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.CutCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.mutableStateOf
@@ -30,6 +31,7 @@
 import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.testTag
@@ -286,6 +288,35 @@
         }
 
     @Test
+    fun default_toggle_button_shape_is_circle() {
+        rule.isShape(CircleShape) { modifier ->
+            ToggleButton(
+                checked = true,
+                onCheckedChange = {},
+                enabled = true,
+                colors = ToggleButtonDefaults.toggleButtonColors(),
+                modifier = modifier
+            ) {}
+        }
+    }
+
+    @Test
+    fun allows_custom_toggle_button_shape_override() {
+        val shape = CutCornerShape(4.dp)
+
+        rule.isShape(shape) { modifier ->
+            ToggleButton(
+                checked = true,
+                onCheckedChange = {},
+                enabled = true,
+                colors = ToggleButtonDefaults.toggleButtonColors(),
+                shape = shape,
+                modifier = modifier
+            ) {}
+        }
+    }
+
+    @Test
     fun displays_text_content() {
         val textContent = "abc"
 
@@ -668,3 +699,34 @@
             shapeColor = background
         )
 }
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+private fun ComposeContentTestRule.isShape(
+    expectedShape: Shape,
+    content: @Composable (Modifier) -> Unit
+) {
+    var background = Color.Transparent
+    var buttonColor = Color.Transparent
+    val padding = 0.dp
+
+    setContentWithTheme {
+        background = MaterialTheme.colors.surface
+        buttonColor = MaterialTheme.colors.primary
+        content(
+            Modifier
+                .testTag(TEST_TAG)
+                .padding(padding)
+                .background(background))
+    }
+
+    onNodeWithTag(TEST_TAG)
+        .captureToImage()
+        .assertShape(
+            density = density,
+            horizontalPadding = 0.dp,
+            verticalPadding = 0.dp,
+            shapeColor = buttonColor,
+            backgroundColor = background,
+            shape = expectedShape
+        )
+}
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt
index dc5ea2b..6d6b1df 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt
@@ -37,6 +37,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
@@ -81,6 +82,9 @@
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Button in different [Interaction]s.
  */
+@Deprecated("This overload is provided for backwards compatibility with Compose for Wear OS 1.0." +
+    "A newer overload is available with an additional shape parameter.",
+    level = DeprecationLevel.HIDDEN)
 @Composable
 public fun Button(
     onClick: () -> Unit,
@@ -89,6 +93,59 @@
     colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
     content: @Composable BoxScope.() -> Unit,
+) = Button(onClick, modifier, enabled, colors, interactionSource, CircleShape, content)
+
+/**
+ * Wear Material [Button] that offers a single slot to take any content (text, icon or image).
+ *
+ * The recommended [Button] sizes can be obtained
+ * from [ButtonDefaults] - see [ButtonDefaults.DefaultButtonSize], [ButtonDefaults.LargeButtonSize],
+ * [ButtonDefaults.SmallButtonSize].
+ * Icon content should be of size [ButtonDefaults.DefaultIconSize],
+ * [ButtonDefaults.LargeIconSize] or [ButtonDefaults.SmallIconSize] respectively.
+ *
+ * The recommended set of [ButtonColors] styles can be obtained from [ButtonDefaults], e.g.
+ * [ButtonDefaults.primaryButtonColors] to get a color scheme for a primary [Button] which by
+ * default will have a solid background of [Colors.primary] and content color of
+ * [Colors.onPrimary].
+ *
+ * [Button]s can be enabled or disabled. A disabled button will not respond to click events.
+ *
+ * Example of a [Button] displaying an icon:
+ * @sample androidx.wear.compose.material.samples.ButtonWithIcon
+ *
+ * Example of a large [Button] displaying an icon:
+ * @sample androidx.wear.compose.material.samples.LargeButtonWithIcon
+ *
+ * Example of a [Button] with text content and size modified to LargeButtonSize:
+ * @sample androidx.wear.compose.material.samples.ButtonWithText
+ *
+ * For more information, see the
+ * [Buttons](https://developer.android.com/training/wearables/components/buttons)
+ * guide.
+ *
+ * @param onClick Will be called when the user clicks the button.
+ * @param modifier Modifier to be applied to the button.
+ * @param enabled Controls the enabled state of the button. When `false`, this button will not
+ * be clickable.
+ * @param colors [ButtonColors] that will be used to resolve the background and content color for
+ * this button in different states. See [ButtonDefaults.buttonColors].
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this Button. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this Button in different [Interaction]s.
+ * @param shape Defines the button's shape. It is strongly recommended to use the default as this
+ * shape is a key characteristic of the Wear Material Theme.
+ */
+@Composable
+public fun Button(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    shape: Shape = CircleShape,
+    content: @Composable BoxScope.() -> Unit,
 ) {
     Box(
         contentAlignment = Alignment.Center,
@@ -97,7 +154,7 @@
                 minWidth = ButtonDefaults.DefaultButtonSize,
                 minHeight = ButtonDefaults.DefaultButtonSize
             )
-            .clip(CircleShape)
+            .clip(shape)
             .clickable(
                 onClick = onClick,
                 enabled = enabled,
@@ -107,7 +164,7 @@
             )
             .background(
                 color = colors.backgroundColor(enabled = enabled).value,
-                shape = CircleShape
+                shape = shape
             )
     ) {
         val contentColor = colors.contentColor(enabled = enabled).value
@@ -157,6 +214,9 @@
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Button in different [Interaction]s.
  */
+@Deprecated("This overload is provided for backwards compatibility with Compose for Wear OS 1.0." +
+    "A newer overload is available with an additional shape parameter.",
+    level = DeprecationLevel.HIDDEN)
 @Composable
 public fun CompactButton(
     onClick: () -> Unit,
@@ -166,11 +226,69 @@
     backgroundPadding: Dp = ButtonDefaults.CompactButtonBackgroundPadding,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
     content: @Composable BoxScope.() -> Unit,
+) = CompactButton(
+    onClick,
+    modifier,
+    enabled,
+    colors,
+    backgroundPadding,
+    interactionSource,
+    CircleShape,
+    content)
+
+/**
+ * Wear Material [CompactButton] that offers a single slot to take any content
+ * (text, icon or image).
+ *
+ * The [CompactButton] has background size [ButtonDefaults.ExtraSmallButtonSize].
+ * There is an optional transparent padding around
+ * the background, defaulted to [ButtonDefaults.CompactButtonBackgroundPadding],
+ * which increases the clickable area. Icon content should have size [ButtonDefaults.SmallIconSize].
+ *
+ * The recommended set of [ButtonColors] styles can be obtained from [ButtonDefaults], e.g.
+ * [ButtonDefaults.primaryButtonColors] to get a color scheme for a primary [Button] which by
+ * default will have a solid background of [Colors.primary] and content color of
+ * [Colors.onPrimary].
+ *
+ * [CompactButton]s can be enabled or disabled. A disabled button will not respond to click events.
+ *
+ * Example usage:
+ * @sample androidx.wear.compose.material.samples.CompactButtonWithIcon
+ *
+ * For more information, see the
+ * [Buttons](https://developer.android.com/training/wearables/components/buttons)
+ * guide.
+ *
+ * @param onClick Will be called when the user clicks the button.
+ * @param modifier Modifier to be applied to the button.
+ * @param enabled Controls the enabled state of the button. When `false`, this button will not
+ * be clickable.
+ * @param colors [ButtonColors] that will be used to resolve the background and content color for
+ * this button in different states. See [ButtonDefaults.buttonColors].
+ * @param backgroundPadding Increases the transparent clickable area around the background,
+ * defaults to [ButtonDefaults.CompactButtonBackgroundPadding]
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this Button. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this Button in different [Interaction]s.
+ * @param shape Defines the button's shape. It is strongly recommended to use the default as this
+ * shape is a key characteristic of the Wear Material Theme.
+ */
+@Composable
+public fun CompactButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
+    backgroundPadding: Dp = ButtonDefaults.CompactButtonBackgroundPadding,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    shape: Shape = CircleShape,
+    content: @Composable BoxScope.() -> Unit,
 ) {
     Box(
         contentAlignment = Alignment.Center,
         modifier = modifier
-            .clip(CircleShape)
+            .clip(shape)
             .clickable(
                 onClick = onClick,
                 enabled = enabled,
@@ -182,7 +300,7 @@
             .requiredSize(ButtonDefaults.ExtraSmallButtonSize)
             .background(
                 color = colors.backgroundColor(enabled = enabled).value,
-                shape = CircleShape
+                shape = shape
             )
     ) {
         val contentColor = colors.contentColor(enabled = enabled).value
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ToggleButton.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ToggleButton.kt
index eedd6ce..6caba46 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ToggleButton.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ToggleButton.kt
@@ -35,6 +35,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.unit.dp
 
@@ -77,6 +78,9 @@
  * appearance / behavior of this ToggleButton in different [Interaction]s.
  * @param content The icon, image or text to be drawn inside the toggle button.
  */
+@Deprecated("This overload is provided for backwards compatibility with Compose for Wear OS 1.0." +
+    "A newer overload is available with an additional shape parameter.",
+    level = DeprecationLevel.HIDDEN)
 @Composable
 public fun ToggleButton(
     checked: Boolean,
@@ -86,6 +90,66 @@
     colors: ToggleButtonColors = ToggleButtonDefaults.toggleButtonColors(),
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
     content: @Composable BoxScope.() -> Unit,
+) = ToggleButton(
+    checked,
+    onCheckedChange,
+    modifier,
+    enabled,
+    colors,
+    interactionSource,
+    CircleShape,
+    content)
+
+/**
+ * Wear Material [ToggleButton] that offers a single slot to take any content
+ * (text, icon or image).
+ *
+ * The [ToggleButton] defaults to size [ToggleButtonDefaults.DefaultToggleButtonSize] or [ToggleButtonDefaults.SmallToggleButtonSize].
+ * Icon content should be of size [ToggleButtonDefaults.DefaultIconSize] or
+ * [ToggleButtonDefaults.SmallIconSize] respectively.
+ *
+ * The recommended set of checked and unchecked [ToggleButtonColors] can be obtained
+ * from [ToggleButtonDefaults.toggleButtonColors], which defaults to
+ * checked colors being
+ * a solid background of [Colors.primary] with content color of [Colors.onPrimary]
+ * and unchecked colors being
+ * a solid background of [Colors.surface] with content color of [Colors.onSurface].
+ *
+ * [ToggleButton]s can be enabled or disabled. A disabled toggle button will not respond to click
+ * events.
+ *
+ * Example of a [ToggleButton] with an icon:
+ * @sample androidx.wear.compose.material.samples.ToggleButtonWithIcon
+ *
+ * For more information, see the
+ * [Buttons](https://developer.android.com/training/wearables/components/buttons#toggle-button)
+ * guide.
+ *
+ * @param checked Boolean flag indicating whether this toggle button is currently checked.
+ * @param onCheckedChange Callback to be invoked when this toggle button is clicked.
+ * @param modifier Modifier to be applied to the toggle button.
+ * @param enabled Controls the enabled state of the toggle button. When `false`,
+ * this toggle button will not be clickable.
+ * @param colors [ToggleButtonColors] that will be used to resolve the background and
+ * content color for this toggle button. See [ToggleButtonDefaults.toggleButtonColors].
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this toggle button. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this ToggleButton in different [Interaction]s.
+ * @param shape Defines the shape for this toggle button. It is strongly recommended to use the
+ * default as this shape is a key characteristic of the Wear Material Theme.
+ * @param content The icon, image or text to be drawn inside the toggle button.
+ */
+@Composable
+public fun ToggleButton(
+    checked: Boolean,
+    onCheckedChange: (Boolean) -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    colors: ToggleButtonColors = ToggleButtonDefaults.toggleButtonColors(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    shape: Shape = CircleShape,
+    content: @Composable BoxScope.() -> Unit,
 ) {
     Box(
         contentAlignment = Alignment.Center,
@@ -94,7 +158,7 @@
                 minWidth = ToggleButtonDefaults.DefaultToggleButtonSize,
                 minHeight = ToggleButtonDefaults.DefaultToggleButtonSize
             )
-            .clip(CircleShape)
+            .clip(shape)
             .toggleable(
                 value = checked,
                 onValueChange = onCheckedChange,
@@ -105,7 +169,7 @@
             )
             .background(
                 color = colors.backgroundColor(enabled = enabled, checked = checked).value,
-                shape = CircleShape
+                shape = shape
             )
     ) {
         val contentColor = colors.contentColor(enabled = enabled, checked = checked).value
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ButtonDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ButtonDemo.kt
index 1cd4b6d..16bd733 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ButtonDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ButtonDemo.kt
@@ -24,6 +24,7 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CutCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -199,6 +200,39 @@
             horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally),
             verticalAlignment = Alignment.CenterVertically
         ) {
+        Button(
+                onClick = {
+                    Toast.makeText(
+                        context,
+                        "Button: Custom Shape", Toast.LENGTH_LONG
+                    ).show()
+                },
+                colors = ButtonDefaults.secondaryButtonColors(),
+                modifier = Modifier,
+                enabled = enabled,
+                shape = CutCornerShape(4.dp)
+            ) {
+                DemoIcon(R.drawable.ic_accessibility_24px)
+            }
+            CompactButton(
+                onClick = {
+                    Toast.makeText(
+                        context,
+                        "Button: Compact Button with Custom Shape", Toast.LENGTH_LONG
+                    ).show()
+                },
+                colors = ButtonDefaults.secondaryButtonColors(),
+                modifier = Modifier,
+                enabled = enabled,
+                shape = CutCornerShape(4.dp)
+            ) {
+                DemoIcon(R.drawable.ic_accessibility_24px)
+            }
+        }
+        Row(
+            horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally),
+            verticalAlignment = Alignment.CenterVertically
+        ) {
             Text(
                 text = "Buttons Enabled",
                 style = MaterialTheme.typography.caption2,
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ToggleButtonDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ToggleButtonDemo.kt
index 3a963c1..0802699 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ToggleButtonDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ToggleButtonDemo.kt
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CutCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -42,6 +43,7 @@
     var toggleButtonsEnabled by remember { mutableStateOf(true) }
     var singularButton1Enabled by remember { mutableStateOf(true) }
     var singularButton2Enabled by remember { mutableStateOf(true) }
+    var singularButton3Enabled by remember { mutableStateOf(true) }
 
     Column(
         modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center,
@@ -88,6 +90,21 @@
             ) {
                 DemoIcon(R.drawable.ic_airplanemode_active_24px)
             }
+            Spacer(modifier = Modifier.size(4.dp))
+            ToggleButton(
+                checked = singularButton3Enabled,
+                onCheckedChange = {
+                    singularButton3Enabled = it
+                },
+                enabled = toggleButtonsEnabled,
+                colors = ToggleButtonDefaults.toggleButtonColors(
+                    checkedBackgroundColor = AlternatePrimaryColor3
+                ),
+                modifier = Modifier,
+                shape = CutCornerShape(4.dp)
+            ) {
+                DemoIcon(R.drawable.ic_airplanemode_active_24px)
+            }
         }
         Spacer(modifier = Modifier.size(4.dp))
         Row(
diff --git a/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/internal/TileRendererInternal.java b/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/internal/TileRendererInternal.java
index 9770536..c071ee6 100644
--- a/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/internal/TileRendererInternal.java
+++ b/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/internal/TileRendererInternal.java
@@ -1900,10 +1900,6 @@
     private static class FixedImageSpan extends ImageSpan {
         private WeakReference<Drawable> mDrawableRef;
 
-        FixedImageSpan(@NonNull Drawable drawable) {
-            super(drawable);
-        }
-
         FixedImageSpan(@NonNull Drawable drawable, int verticalAlignment) {
             super(drawable, verticalAlignment);
         }
diff --git a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleFlavors.kt b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleFlavors.kt
index f9a26a5..cacfd70 100644
--- a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleFlavors.kt
+++ b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleFlavors.kt
@@ -21,7 +21,6 @@
 import androidx.annotation.RestrictTo
 import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
 import androidx.wear.watchface.complications.IllegalNodeException
-import androidx.wear.watchface.complications.NAMESPACE_APP
 import androidx.wear.watchface.complications.hasValue
 import androidx.wear.watchface.complications.iterate
 import androidx.wear.watchface.style.data.UserStyleFlavorWireFormat
@@ -129,15 +128,9 @@
                         require(setting != null) { "no setting found for id $id" }
                         when (setting) {
                             is UserStyleSetting.BooleanUserStyleSetting -> {
-                                val booleanValue = parser.getAttributeBooleanValue(
-                                    NAMESPACE_APP,
-                                    "value",
-                                    true
-                                )
-
                                 userStyle[setting] =
                                     UserStyleSetting.BooleanUserStyleSetting
-                                        .BooleanOption.from(booleanValue)
+                                        .BooleanOption.from(value!!.toBoolean())
                             }
                             is UserStyleSetting.DoubleRangeUserStyleSetting -> {
                                 userStyle[setting] =
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt
index 85ac2af..140be48 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt
@@ -245,7 +245,7 @@
 
             assertThat(
                 watchFaceImpl.complicationSlotsManager.complicationSlots.size
-            ).isEqualTo(3)
+            ).isEqualTo(4)
 
             val slotA = watchFaceImpl.complicationSlotsManager.complicationSlots[10]!!
             assertThat(slotA.boundsType).isEqualTo(ComplicationSlotBoundsType.ROUND_RECT)
@@ -328,7 +328,7 @@
             assertThat(style[UserStyleSetting.Id("TimeStyle")]!!.id)
                 .isEqualTo(UserStyleSetting.Option.Id("minimal"))
             assertThat((style[UserStyleSetting.Id("BooleanId")]!! as BooleanOption).value)
-                .isEqualTo(true)
+                .isEqualTo(false)
             assertThat((style[UserStyleSetting.Id("DoubleId")]!! as DoubleRangeOption).value)
                 .isEqualTo(1.0)
             assertThat((style[UserStyleSetting.Id("LongId")]!! as LongRangeOption).value)
@@ -367,6 +367,12 @@
                     "primary(ComponentInfo{com.package/com.app}, SHORT_TEXT), " +
                     "secondary(null, null), " +
                     "system(16, SHORT_TEXT)]}]")
+            val slotD = watchFaceImpl.complicationSlotsManager.complicationSlots[40]!!
+            assertThat(slotD.supportedTypes).containsExactly(
+                ComplicationType.SHORT_TEXT,
+                ComplicationType.RANGED_VALUE,
+                ComplicationType.SMALL_IMAGE
+            ).inOrder()
         }
     }
 }
diff --git a/wear/watchface/watchface/src/androidTest/res/values/integers.xml b/wear/watchface/watchface/src/androidTest/res/values/integers.xml
index efcf809..3c247b6 100644
--- a/wear/watchface/watchface/src/androidTest/res/values/integers.xml
+++ b/wear/watchface/watchface/src/androidTest/res/values/integers.xml
@@ -18,4 +18,5 @@
     <integer name="complication_slot_10">10</integer>
     <integer name="complication_slot_20">20</integer>
     <integer name="complication_slot_30">30</integer>
+    <integer name="complication_slot_40">40</integer>
 </resources>
\ No newline at end of file
diff --git a/wear/watchface/watchface/src/androidTest/res/values/strings.xml b/wear/watchface/watchface/src/androidTest/res/values/strings.xml
index 06fd5c2..946f7e2 100644
--- a/wear/watchface/watchface/src/androidTest/res/values/strings.xml
+++ b/wear/watchface/watchface/src/androidTest/res/values/strings.xml
@@ -21,8 +21,10 @@
     <string name="complication_name_two" translatable="false">Two</string>
     <string name="complication_screen_reader_name_two"
         translatable="false">Complication two.</string>
+    <string name="complication_name_three" translatable="false">Three</string>
     <string name="flavor_id_0" translatable="false">flavor1</string>
     <string name="setting_time_style" translatable="false">TimeStyle</string>
     <string name="option_time_style_minimal" translatable="false">minimal</string>
     <string name="option_time_style_seconds" translatable="false">seconds</string>
+    <string name="supported_type_three" translatable="false">SHORT_TEXT|RANGED_VALUE|SMALL_IMAGE</string>
 </resources>
diff --git a/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface.xml b/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface.xml
index 64fdca2..cb508e7 100644
--- a/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface.xml
+++ b/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface.xml
@@ -74,11 +74,20 @@
         app:systemDataSourceFallbackDefaultType="NOT_CONFIGURED">
         <ComplicationSlotBounds app:left="1" app:top="20" app:right="3" app:bottom="40"/>
     </ComplicationSlot>
+    <ComplicationSlot
+        app:slotId="@integer/complication_slot_40"
+        app:name="@string/complication_name_three"
+        app:boundsType="ROUND_RECT"
+        app:supportedTypes="@string/supported_type_three"
+        app:systemDataSourceFallback="DATA_SOURCE_WATCH_BATTERY"
+        app:systemDataSourceFallbackDefaultType="RANGED_VALUE">
+        <ComplicationSlotBounds app:left="3" app:top="70" app:right="7" app:bottom="90"/>
+    </ComplicationSlot>
     <UserStyleFlavors>
         <UserStyleFlavor app:id="@string/flavor_id_0">
             <StyleOption app:id="@string/setting_time_style"
                 app:value="@string/option_time_style_minimal"/>
-            <StyleOption app:id="BooleanId" app:value="true"/>
+            <StyleOption app:id="BooleanId" app:value="false"/>
             <StyleOption app:id="DoubleId" app:value="1.0"/>
             <StyleOption app:id="LongId" app:value="2"/>
             <ComplicationPolicy
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/XmlSchemaAndComplicationSlotsDefinition.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/XmlSchemaAndComplicationSlotsDefinition.kt
index c7740d6..a8e1279 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/XmlSchemaAndComplicationSlotsDefinition.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/XmlSchemaAndComplicationSlotsDefinition.kt
@@ -31,6 +31,7 @@
 import androidx.wear.watchface.style.UserStyleFlavors
 import androidx.wear.watchface.style.UserStyleSchema
 import androidx.wear.watchface.style.getIntRefAttribute
+import androidx.wear.watchface.style.getStringRefAttribute
 import androidx.wear.watchface.style.moveToStart
 import org.xmlpull.v1.XmlPullParser
 import kotlin.jvm.Throws
@@ -173,7 +174,10 @@
                     "A ComplicationSlot must have a supportedTypes attribute"
                 }
                 val supportedTypes =
-                    parser.getAttributeValue(NAMESPACE_APP, "supportedTypes").split('|')
+                    getStringRefAttribute(resources, parser, "supportedTypes")
+                        ?.split('|') ?: throw IllegalArgumentException(
+                        "Unable to extract the supported type(s) for ComplicationSlot $slotId"
+                    )
                 val supportedTypesList = supportedTypes.map {
                     typesMap[it] ?: throw IllegalArgumentException(
                         "Unrecognised type $it for ComplicationSlot $slotId"
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
index f470cf3..7d4780d 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
@@ -112,34 +112,34 @@
         fun getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
             value: PendingWallpaperInteractiveWatchFaceInstance
         ): IInteractiveWatchFace? {
-            synchronized(pendingWallpaperInteractiveWatchFaceInstanceLock) {
+            val impl = synchronized(pendingWallpaperInteractiveWatchFaceInstanceLock) {
                 val instance = instances[value.params.instanceId]
-                return if (instance != null) {
-                    // The system on reboot will use this to connect to an existing watch face, we
-                    // need to ensure there isn't a skew between the style the watch face actually
-                    // has and what the system thinks we should have. Note runBlocking is safe here
-                    // because we never await.
-                    val engine = instance.impl.engine!!
-                    runBlocking {
-                        withContext(engine.uiThreadCoroutineScope.coroutineContext) {
-                            if (engine.deferredWatchFaceImpl.isCompleted) {
-                                // setUserStyle awaits deferredWatchFaceImpl but it's completed.
-                                engine.setUserStyle(value.params.userStyle)
-                            } else {
-                                // Defer the UI update until deferredWatchFaceImpl is about to
-                                // complete.
-                                engine.pendingUserStyle = value.params.userStyle
-                            }
-                        }
-                    }
-                    instance.impl
-                } else {
+                if (instance == null) {
                     TraceEvent("Set pendingWallpaperInteractiveWatchFaceInstance").use {
                         pendingWallpaperInteractiveWatchFaceInstance = value
                     }
-                    null
+                    return null
+                }
+                instance.impl
+            }
+
+            // The system on reboot will use this to connect to an existing watch face, we need to
+            // ensure there isn't a skew between the style the watch face actually has and what the
+            // system thinks we should have. Note runBlocking is safe here because we never await.
+            val engine = impl.engine!!
+            runBlocking {
+                withContext(engine.uiThreadCoroutineScope.coroutineContext) {
+                    if (engine.deferredWatchFaceImpl.isCompleted) {
+                        // setUserStyle awaits deferredWatchFaceImpl but it's completed.
+                        engine.setUserStyle(value.params.userStyle)
+                    } else {
+                        // Defer the UI update until deferredWatchFaceImpl is about to
+                        // complete.
+                        engine.pendingUserStyle = value.params.userStyle
+                    }
                 }
             }
+            return impl
         }
 
         /** Can be called on any thread. */
diff --git a/wear/watchface/watchface/src/main/res/values/config.xml b/wear/watchface/watchface/src/main/res/values/config.xml
index 5fcaed1..a313773 100644
--- a/wear/watchface/watchface/src/main/res/values/config.xml
+++ b/wear/watchface/watchface/src/main/res/values/config.xml
@@ -17,5 +17,5 @@
 
 <resources>
     <bool name="watch_face_instance_service_enabled">false</bool>
-    <integer name="watch_face_xml_version">4</integer>
+    <integer name="watch_face_xml_version">6</integer>
 </resources>
diff --git a/wear/wear/src/main/java/androidx/wear/widget/drawer/WearableActionDrawerView.java b/wear/wear/src/main/java/androidx/wear/widget/drawer/WearableActionDrawerView.java
index 191d502..bded2fb 100644
--- a/wear/wear/src/main/java/androidx/wear/widget/drawer/WearableActionDrawerView.java
+++ b/wear/wear/src/main/java/androidx/wear/widget/drawer/WearableActionDrawerView.java
@@ -374,13 +374,10 @@
     }
 
     private static final class TitleViewHolder extends RecyclerView.ViewHolder {
-
-        public final View view;
         public final TextView textView;
 
         TitleViewHolder(View view) {
             super(view);
-            this.view = view;
             textView = (TextView) view.findViewById(R.id.ws_action_drawer_title);
         }
     }
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/RendererTerminationActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/RendererTerminationActivity.java
index 8c628f6..c59a22a 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/RendererTerminationActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/RendererTerminationActivity.java
@@ -167,6 +167,7 @@
             }
         }
 
+        @SuppressWarnings("unused") // used from javascript
         @JavascriptInterface
         public void block() throws Exception {
             synchronized (mLock) {
diff --git a/window/extensions/extensions/api/current.txt b/window/extensions/extensions/api/current.txt
index 3054dd7..22679ee 100644
--- a/window/extensions/extensions/api/current.txt
+++ b/window/extensions/extensions/api/current.txt
@@ -87,7 +87,8 @@
   }
 
   public class SplitPlaceholderRule extends androidx.window.extensions.embedding.SplitRule {
-    method public int getFinishPrimaryWithSecondary();
+    method public int getFinishPrimaryWithPlaceholder();
+    method @Deprecated public int getFinishPrimaryWithSecondary();
     method public android.content.Intent getPlaceholderIntent();
     method public boolean isSticky();
     method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivity(android.app.Activity);
@@ -97,7 +98,8 @@
   public static final class SplitPlaceholderRule.Builder {
     ctor public SplitPlaceholderRule.Builder(android.content.Intent, java.util.function.Predicate<android.app.Activity!>, java.util.function.Predicate<android.content.Intent!>, java.util.function.Predicate<android.view.WindowMetrics!>);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule build();
-    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithSecondary(int);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithSecondary(int);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setLayoutDirection(int);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSplitRatio(float);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSticky(boolean);
diff --git a/window/extensions/extensions/api/public_plus_experimental_current.txt b/window/extensions/extensions/api/public_plus_experimental_current.txt
index 3054dd7..22679ee 100644
--- a/window/extensions/extensions/api/public_plus_experimental_current.txt
+++ b/window/extensions/extensions/api/public_plus_experimental_current.txt
@@ -87,7 +87,8 @@
   }
 
   public class SplitPlaceholderRule extends androidx.window.extensions.embedding.SplitRule {
-    method public int getFinishPrimaryWithSecondary();
+    method public int getFinishPrimaryWithPlaceholder();
+    method @Deprecated public int getFinishPrimaryWithSecondary();
     method public android.content.Intent getPlaceholderIntent();
     method public boolean isSticky();
     method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivity(android.app.Activity);
@@ -97,7 +98,8 @@
   public static final class SplitPlaceholderRule.Builder {
     ctor public SplitPlaceholderRule.Builder(android.content.Intent, java.util.function.Predicate<android.app.Activity!>, java.util.function.Predicate<android.content.Intent!>, java.util.function.Predicate<android.view.WindowMetrics!>);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule build();
-    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithSecondary(int);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithSecondary(int);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setLayoutDirection(int);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSplitRatio(float);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSticky(boolean);
diff --git a/window/extensions/extensions/api/restricted_current.txt b/window/extensions/extensions/api/restricted_current.txt
index 3054dd7..22679ee 100644
--- a/window/extensions/extensions/api/restricted_current.txt
+++ b/window/extensions/extensions/api/restricted_current.txt
@@ -87,7 +87,8 @@
   }
 
   public class SplitPlaceholderRule extends androidx.window.extensions.embedding.SplitRule {
-    method public int getFinishPrimaryWithSecondary();
+    method public int getFinishPrimaryWithPlaceholder();
+    method @Deprecated public int getFinishPrimaryWithSecondary();
     method public android.content.Intent getPlaceholderIntent();
     method public boolean isSticky();
     method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivity(android.app.Activity);
@@ -97,7 +98,8 @@
   public static final class SplitPlaceholderRule.Builder {
     ctor public SplitPlaceholderRule.Builder(android.content.Intent, java.util.function.Predicate<android.app.Activity!>, java.util.function.Predicate<android.content.Intent!>, java.util.function.Predicate<android.view.WindowMetrics!>);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule build();
-    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithSecondary(int);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithSecondary(int);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setLayoutDirection(int);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSplitRatio(float);
     method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSticky(boolean);
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPlaceholderRule.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPlaceholderRule.java
index 1f8a8fe..1f3aea3 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPlaceholderRule.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPlaceholderRule.java
@@ -22,9 +22,12 @@
 import android.os.Build;
 import android.view.WindowMetrics;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.function.Predicate;
 
 /**
@@ -39,18 +42,30 @@
     @NonNull
     private final Intent mPlaceholderIntent;
     private final boolean mIsSticky;
-    @SplitFinishBehavior
-    private final int mFinishPrimaryWithSecondary;
+
+    /**
+     * Determines what happens with the primary container when the placeholder activity is
+     * finished in one of the containers in a split.
+     */
+    @IntDef({
+            FINISH_ALWAYS,
+            FINISH_ADJACENT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface SplitPlaceholderFinishBehavior{}
+
+    @SplitPlaceholderFinishBehavior
+    private final int mFinishPrimaryWithPlaceholder;
 
     SplitPlaceholderRule(@NonNull Intent placeholderIntent,
             float splitRatio, @LayoutDir int layoutDirection, boolean isSticky,
-            @SplitFinishBehavior int finishPrimaryWithSecondary,
+            @SplitPlaceholderFinishBehavior int finishPrimaryWithPlaceholder,
             @NonNull Predicate<Activity> activityPredicate,
             @NonNull Predicate<Intent> intentPredicate,
             @NonNull Predicate<WindowMetrics> parentWindowMetricsPredicate) {
         super(parentWindowMetricsPredicate, splitRatio, layoutDirection);
         mIsSticky = isSticky;
-        mFinishPrimaryWithSecondary = finishPrimaryWithSecondary;
+        mFinishPrimaryWithPlaceholder = finishPrimaryWithPlaceholder;
         mActivityPredicate = activityPredicate;
         mIntentPredicate = intentPredicate;
         mPlaceholderIntent = placeholderIntent;
@@ -91,12 +106,22 @@
     }
 
     /**
+     * @deprecated Use {@link #getFinishPrimaryWithPlaceholder()} instead.
+     */
+    @Deprecated
+    @SplitPlaceholderFinishBehavior
+    public int getFinishPrimaryWithSecondary() {
+        return getFinishPrimaryWithPlaceholder();
+    }
+
+    /**
      * Determines what happens with the primary container when all activities are finished in the
      * associated secondary/placeholder container.
+     * TODO(b/238905747): Add api guard for extensions.
      */
-    @SplitFinishBehavior
-    public int getFinishPrimaryWithSecondary() {
-        return mFinishPrimaryWithSecondary;
+    @SplitPlaceholderFinishBehavior
+    public int getFinishPrimaryWithPlaceholder() {
+        return mFinishPrimaryWithPlaceholder;
     }
 
     /**
@@ -115,8 +140,8 @@
         @LayoutDir
         private int mLayoutDirection;
         private boolean mIsSticky = false;
-        @SplitFinishBehavior
-        private int mFinishPrimaryWithSecondary = FINISH_ALWAYS;
+        @SplitPlaceholderFinishBehavior
+        private int mFinishPrimaryWithPlaceholder = FINISH_ALWAYS;
 
         public Builder(@NonNull Intent placeholderIntent,
                 @NonNull Predicate<Activity> activityPredicate,
@@ -149,10 +174,27 @@
             return this;
         }
 
-        /** @see SplitPlaceholderRule#getFinishPrimaryWithSecondary() */
+        /**
+         * @deprecated Use SplitPlaceholderRule#setFinishPrimaryWithPlaceholder(int)} instead.
+         */
+        @Deprecated
         @NonNull
-        public Builder setFinishPrimaryWithSecondary(@SplitFinishBehavior int finishBehavior) {
-            mFinishPrimaryWithSecondary = finishBehavior;
+        public Builder setFinishPrimaryWithSecondary(
+                @SplitPlaceholderFinishBehavior int finishBehavior) {
+            if (finishBehavior == FINISH_NEVER) {
+                finishBehavior = FINISH_ALWAYS;
+            }
+            return setFinishPrimaryWithPlaceholder(finishBehavior);
+        }
+
+        /**
+         * @see SplitPlaceholderRule#getFinishPrimaryWithPlaceholder()
+         * TODO(b/238905747): Add api guard for extensions.
+         */
+        @NonNull
+        public Builder setFinishPrimaryWithPlaceholder(
+                @SplitPlaceholderFinishBehavior int finishBehavior) {
+            mFinishPrimaryWithPlaceholder = finishBehavior;
             return this;
         }
 
@@ -160,7 +202,7 @@
         @NonNull
         public SplitPlaceholderRule build() {
             return new SplitPlaceholderRule(mPlaceholderIntent, mSplitRatio,
-                    mLayoutDirection, mIsSticky, mFinishPrimaryWithSecondary, mActivityPredicate,
+                    mLayoutDirection, mIsSticky, mFinishPrimaryWithPlaceholder, mActivityPredicate,
                     mIntentPredicate, mParentWindowMetricsPredicate);
         }
     }
@@ -174,7 +216,7 @@
         SplitPlaceholderRule that = (SplitPlaceholderRule) o;
 
         if (mIsSticky != that.mIsSticky) return false;
-        if (mFinishPrimaryWithSecondary != that.mFinishPrimaryWithSecondary) return false;
+        if (mFinishPrimaryWithPlaceholder != that.mFinishPrimaryWithPlaceholder) return false;
         if (!mActivityPredicate.equals(that.mActivityPredicate)) return false;
         if (!mIntentPredicate.equals(that.mIntentPredicate)) return false;
         return mPlaceholderIntent.equals(that.mPlaceholderIntent);
@@ -187,7 +229,7 @@
         result = 31 * result + mIntentPredicate.hashCode();
         result = 31 * result + mPlaceholderIntent.hashCode();
         result = 31 * result + (mIsSticky ? 1 : 0);
-        result = 31 * result + mFinishPrimaryWithSecondary;
+        result = 31 * result + mFinishPrimaryWithPlaceholder;
         return result;
     }
 
@@ -197,7 +239,7 @@
         return "SplitPlaceholderRule{"
                 + "mActivityPredicate=" + mActivityPredicate
                 + ", mIsSticky=" + mIsSticky
-                + ", mFinishPrimaryWithPlaceholder=" + mFinishPrimaryWithSecondary
+                + ", mFinishPrimaryWithPlaceholder=" + mFinishPrimaryWithPlaceholder
                 + '}';
     }
 }
diff --git a/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitActivityBase.java b/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitActivityBase.java
index f30b09a..bd46884 100644
--- a/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitActivityBase.java
+++ b/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitActivityBase.java
@@ -296,7 +296,7 @@
                 0 /* minSmallestWidth */
         )
                 .setSticky(mViewBinding.useStickyPlaceholderCheckBox.isChecked())
-                .setFinishPrimaryWithSecondary(SplitRule.FINISH_ADJACENT)
+                .setFinishPrimaryWithPlaceholder(SplitRule.FINISH_ADJACENT)
                 .setSplitRatio(SPLIT_RATIO)
                 .build();
         if (mViewBinding.usePlaceholderCheckBox.isChecked()) {
diff --git a/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitActivityTrampoline.kt b/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitActivityTrampoline.kt
index 3cb2c03..67680f1 100644
--- a/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitActivityTrampoline.kt
+++ b/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitActivityTrampoline.kt
@@ -42,7 +42,7 @@
             placeholderIntent,
             minWidth = minSplitWidth(),
             minSmallestWidth = 0)
-            .setFinishPrimaryWithSecondary(FINISH_ADJACENT)
+            .setFinishPrimaryWithPlaceholder(FINISH_ADJACENT)
             .setSplitRatio(SPLIT_RATIO)
             .build()
         SplitController.getInstance().registerRule(placeholderRule)
diff --git a/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitPipActivityBase.kt b/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitPipActivityBase.kt
index 9054b4a..cfd8064 100644
--- a/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitPipActivityBase.kt
+++ b/window/window-samples/src/main/java/androidx/window/sample/embedding/SplitPipActivityBase.kt
@@ -260,7 +260,7 @@
             val isSticky = viewBinding.useStickyPlaceHolderCheckBox.isChecked
             val rule = SplitPlaceholderRule.Builder(activityFilters, intent, 0, 0)
                 .setSticky(isSticky)
-                .setFinishPrimaryWithSecondary(SplitRule.FINISH_ADJACENT)
+                .setFinishPrimaryWithPlaceholder(SplitRule.FINISH_ADJACENT)
                 .setSplitRatio(splitRatio)
                 .build()
             splitController.registerRule(rule)
diff --git a/window/window/api/public_plus_experimental_current.txt b/window/window/api/public_plus_experimental_current.txt
index d0abd6e..ee32d47 100644
--- a/window/window/api/public_plus_experimental_current.txt
+++ b/window/window/api/public_plus_experimental_current.txt
@@ -137,13 +137,13 @@
   }
 
   @androidx.window.core.ExperimentalWindowApi public final class SplitPlaceholderRule extends androidx.window.embedding.SplitRule {
-    ctor @Deprecated public SplitPlaceholderRule(java.util.Set<androidx.window.embedding.ActivityFilter> filters, android.content.Intent placeholderIntent, boolean isSticky, optional int finishPrimaryWithSecondary, optional @IntRange(from=0L) int minWidth, optional @IntRange(from=0L) int minSmallestWidth, optional @FloatRange(from=0.0, to=1.0) float splitRatio, optional int layoutDirection);
+    ctor @Deprecated public SplitPlaceholderRule(java.util.Set<androidx.window.embedding.ActivityFilter> filters, android.content.Intent placeholderIntent, boolean isSticky, optional int finishPrimaryWithPlaceholder, optional @IntRange(from=0L) int minWidth, optional @IntRange(from=0L) int minSmallestWidth, optional @FloatRange(from=0.0, to=1.0) float splitRatio, optional int layoutDirection);
     method public java.util.Set<androidx.window.embedding.ActivityFilter> getFilters();
-    method public int getFinishPrimaryWithSecondary();
+    method public int getFinishPrimaryWithPlaceholder();
     method public android.content.Intent getPlaceholderIntent();
     method public boolean isSticky();
     property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
-    property public final int finishPrimaryWithSecondary;
+    property public final int finishPrimaryWithPlaceholder;
     property public final boolean isSticky;
     property public final android.content.Intent placeholderIntent;
   }
@@ -151,7 +151,7 @@
   public static final class SplitPlaceholderRule.Builder {
     ctor public SplitPlaceholderRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters, android.content.Intent placeholderIntent, @IntRange(from=0L) int minWidth, @IntRange(from=0L) int minSmallestWidth);
     method public androidx.window.embedding.SplitPlaceholderRule build();
-    method public androidx.window.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithSecondary(int finishPrimaryWithSecondary);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(int finishPrimaryWithPlaceholder);
     method public androidx.window.embedding.SplitPlaceholderRule.Builder setLayoutDir(int layoutDir);
     method public androidx.window.embedding.SplitPlaceholderRule.Builder setSplitRatio(@FloatRange(from=0.0, to=1.0) float splitRatio);
     method public androidx.window.embedding.SplitPlaceholderRule.Builder setSticky(boolean isSticky);
diff --git a/window/window/build.gradle b/window/window/build.gradle
index 3f202c45..eb44810 100644
--- a/window/window/build.gradle
+++ b/window/window/build.gradle
@@ -47,7 +47,7 @@
     api(libs.kotlinCoroutinesAndroid)
     implementation("androidx.annotation:annotation:1.2.0")
     implementation("androidx.collection:collection:1.1.0")
-    implementation("androidx.core:core:1.3.2")
+    implementation("androidx.core:core:1.8.0")
 
     compileOnly(project(":window:sidecar:sidecar"))
     compileOnly(project(":window:extensions:extensions"))
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingRuleConstructionTests.kt b/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingRuleConstructionTests.kt
index 155ade0..7de3ec5 100644
--- a/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingRuleConstructionTests.kt
+++ b/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingRuleConstructionTests.kt
@@ -172,7 +172,7 @@
         val rules = SplitController.getInstance().getSplitRules()
         assertEquals(1, rules.size)
         val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
-        assertEquals(FINISH_ALWAYS, rule.finishPrimaryWithSecondary)
+        assertEquals(FINISH_ALWAYS, rule.finishPrimaryWithPlaceholder)
         assertEquals(false, rule.isSticky)
         assertEquals(0.5f, rule.splitRatio)
         assertEquals(LayoutDirection.LOCALE, rule.layoutDirection)
@@ -194,7 +194,7 @@
             456
         )
             .build()
-        assertEquals(FINISH_ALWAYS, rule.finishPrimaryWithSecondary)
+        assertEquals(FINISH_ALWAYS, rule.finishPrimaryWithPlaceholder)
         assertEquals(false, rule.isSticky)
         assertEquals(0.5f, rule.splitRatio)
         assertEquals(LayoutDirection.LOCALE, rule.layoutDirection)
@@ -220,12 +220,12 @@
             123,
             456
         )
-            .setFinishPrimaryWithSecondary(FINISH_ADJACENT)
+            .setFinishPrimaryWithPlaceholder(FINISH_ADJACENT)
             .setSticky(true)
             .setSplitRatio(0.3f)
             .setLayoutDir(LayoutDirection.LTR)
             .build()
-        assertEquals(FINISH_ADJACENT, rule.finishPrimaryWithSecondary)
+        assertEquals(FINISH_ADJACENT, rule.finishPrimaryWithPlaceholder)
         assertEquals(true, rule.isSticky)
         assertEquals(0.3f, rule.splitRatio)
         assertEquals(LayoutDirection.LTR, rule.layoutDirection)
@@ -264,6 +264,16 @@
                 minWidth = 123,
                 minSmallestWidth = 456
             )
+                .setFinishPrimaryWithPlaceholder(FINISH_NEVER)
+                .build()
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitPlaceholderRule.Builder(
+                HashSet(),
+                Intent(),
+                minWidth = 123,
+                minSmallestWidth = 456
+            )
                 .setSplitRatio(-1.0f)
                 .build()
         }
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt
index dd504ae..0ceb16b 100644
--- a/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt
@@ -30,6 +30,7 @@
 import androidx.window.extensions.embedding.SplitPairRule.Builder as SplitPairRuleBuilder
 import androidx.window.extensions.embedding.SplitPlaceholderRule as OEMSplitPlaceholderRule
 import androidx.window.extensions.embedding.SplitPlaceholderRule.Builder as SplitPlaceholderRuleBuilder
+import androidx.window.extensions.WindowExtensionsProvider
 
 /**
  * Adapter class that translates data classes between Extension and Jetpack interfaces.
@@ -151,10 +152,25 @@
             .setSplitRatio(rule.splitRatio)
             .setLayoutDirection(rule.layoutDirection)
             .setSticky(rule.isSticky)
-            .setFinishPrimaryWithSecondary(rule.finishPrimaryWithSecondary)
+            .safeSetFinishPrimaryWithPlaceholder(rule.finishPrimaryWithPlaceholder)
         return builder.build()
     }
 
+    @Suppress("DEPRECATION")
+    // setFinishPrimaryWithSecondary is to be deprecated but we want to make a safe fallback
+    // behavior here for compatibility reason.
+    // Suppressing deprecation warning to prevent breaking build.
+    private fun SplitPlaceholderRuleBuilder.safeSetFinishPrimaryWithPlaceholder(
+        behavior: @SplitPlaceholderRule.SplitPlaceholderFinishBehavior Int
+    ): SplitPlaceholderRuleBuilder {
+       var extensionApiLevel: Int = WindowExtensionsProvider.getWindowExtensions().vendorApiLevel
+        return if (extensionApiLevel >= 2) {
+            setFinishPrimaryWithPlaceholder(behavior)
+        } else {
+            setFinishPrimaryWithSecondary(behavior)
+        }
+    }
+
     private fun translateActivityRule(
         rule: ActivityRule,
         predicateClass: Class<*>
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitPlaceholderRule.kt b/window/window/src/main/java/androidx/window/embedding/SplitPlaceholderRule.kt
index c68e451..c12b7b7 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitPlaceholderRule.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitPlaceholderRule.kt
@@ -19,6 +19,7 @@
 import android.content.Intent
 import android.util.LayoutDirection
 import androidx.annotation.FloatRange
+import androidx.annotation.IntDef
 import androidx.annotation.IntRange
 import androidx.core.util.Preconditions.checkArgument
 import androidx.core.util.Preconditions.checkArgumentNonnegative
@@ -48,12 +49,21 @@
     val isSticky: Boolean
 
     /**
+     * Defines whether a container should be finished together when the associated placeholder
+     * activity is being finished based on current presentation mode.
+     */
+    @Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(FINISH_ALWAYS, FINISH_ADJACENT)
+    internal annotation class SplitPlaceholderFinishBehavior
+
+    /**
      * Determines what happens with the primary container when all activities are finished in the
      * associated placeholder container.
-     * @see SplitRule.SplitFinishBehavior
+     * @see SplitPlaceholderFinishBehavior
      */
-    @SplitFinishBehavior
-    val finishPrimaryWithSecondary: Int
+    @SplitPlaceholderFinishBehavior
+    val finishPrimaryWithPlaceholder: Int
 
     // TODO(b/229656253): Reduce visibility to remove from public API.
     @Deprecated(
@@ -64,7 +74,7 @@
         filters: Set<ActivityFilter>,
         placeholderIntent: Intent,
         isSticky: Boolean,
-        @SplitFinishBehavior finishPrimaryWithSecondary: Int = FINISH_ALWAYS,
+        @SplitPlaceholderFinishBehavior finishPrimaryWithPlaceholder: Int = FINISH_ALWAYS,
         @IntRange(from = 0) minWidth: Int = 0,
         @IntRange(from = 0) minSmallestWidth: Int = 0,
         @FloatRange(from = 0.0, to = 1.0) splitRatio: Float = 0.5f,
@@ -73,10 +83,13 @@
         checkArgumentNonnegative(minWidth, "minWidth must be non-negative")
         checkArgumentNonnegative(minSmallestWidth, "minSmallestWidth must be non-negative")
         checkArgument(splitRatio in 0.0..1.0, "splitRatio must be in 0.0..1.0 range")
+        checkArgument(finishPrimaryWithPlaceholder != FINISH_NEVER,
+            "FINISH_NEVER is not a valid configuration for SplitPlaceholderRule. " +
+                "Please use FINISH_ALWAYS or FINISH_ADJACENT instead or refer to the current API.")
         this.filters = filters.toSet()
         this.placeholderIntent = placeholderIntent
         this.isSticky = isSticky
-        this.finishPrimaryWithSecondary = finishPrimaryWithSecondary
+        this.finishPrimaryWithPlaceholder = finishPrimaryWithPlaceholder
     }
 
     /**
@@ -94,8 +107,8 @@
         @IntRange(from = 0)
         private val minSmallestWidth: Int
     ) {
-        @SplitFinishBehavior
-        private var finishPrimaryWithSecondary: Int = FINISH_ALWAYS
+        @SplitPlaceholderFinishBehavior
+        private var finishPrimaryWithPlaceholder: Int = FINISH_ALWAYS
         private var isSticky: Boolean = false
         @FloatRange(from = 0.0, to = 1.0)
         private var splitRatio: Float = 0.5f
@@ -103,12 +116,14 @@
         private var layoutDir: Int = LayoutDirection.LOCALE
 
         /**
-         * @see SplitPlaceholderRule.finishPrimaryWithSecondary
+         * @see SplitPlaceholderRule.finishPrimaryWithPlaceholder
          */
-        fun setFinishPrimaryWithSecondary(
-            @SplitFinishBehavior finishPrimaryWithSecondary: Int
+        fun setFinishPrimaryWithPlaceholder(
+            @SplitPlaceholderFinishBehavior finishPrimaryWithPlaceholder: Int
         ): Builder =
-            apply { this.finishPrimaryWithSecondary = finishPrimaryWithSecondary }
+            apply {
+               this.finishPrimaryWithPlaceholder = finishPrimaryWithPlaceholder
+            }
 
         /**
          * @see SplitPlaceholderRule.isSticky
@@ -131,7 +146,7 @@
 
         @Suppress("DEPRECATION")
         fun build() = SplitPlaceholderRule(filters, placeholderIntent, isSticky,
-            finishPrimaryWithSecondary, minWidth, minSmallestWidth, splitRatio, layoutDir)
+            finishPrimaryWithPlaceholder, minWidth, minSmallestWidth, splitRatio, layoutDir)
     }
 
     /**
@@ -147,7 +162,7 @@
             newSet.toSet(),
             placeholderIntent,
             isSticky,
-            finishPrimaryWithSecondary,
+            finishPrimaryWithPlaceholder,
             minWidth,
             minSmallestWidth,
             splitRatio,
@@ -162,7 +177,7 @@
 
         if (placeholderIntent != other.placeholderIntent) return false
         if (isSticky != other.isSticky) return false
-        if (finishPrimaryWithSecondary != other.finishPrimaryWithSecondary) return false
+        if (finishPrimaryWithPlaceholder != other.finishPrimaryWithPlaceholder) return false
         if (filters != other.filters) return false
 
         return true
@@ -172,7 +187,7 @@
         var result = super.hashCode()
         result = 31 * result + placeholderIntent.hashCode()
         result = 31 * result + isSticky.hashCode()
-        result = 31 * result + finishPrimaryWithSecondary.hashCode()
+        result = 31 * result + finishPrimaryWithPlaceholder.hashCode()
         result = 31 * result + filters.hashCode()
         return result
     }
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitRule.kt b/window/window/src/main/java/androidx/window/embedding/SplitRule.kt
index 7eb391d..50916c7 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitRule.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitRule.kt
@@ -85,7 +85,7 @@
      *
      * @see SplitPairRule.finishPrimaryWithSecondary
      * @see SplitPairRule.finishSecondaryWithPrimary
-     * @see SplitPlaceholderRule.finishPrimaryWithSecondary
+     * @see SplitPlaceholderRule.finishPrimaryWithPlaceholder
      */
     companion object {
         /**
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitRuleParser.kt b/window/window/src/main/java/androidx/window/embedding/SplitRuleParser.kt
index 5f2d301..8e086c9 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitRuleParser.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitRuleParser.kt
@@ -177,7 +177,7 @@
     ): SplitPlaceholderRule {
         val placeholderActivityIntentName: String?
         val stickyPlaceholder: Boolean
-        val finishPrimaryWithSecondary: Int
+        val finishPrimaryWithPlaceholder: Int
         val ratio: Float
         val minWidth: Int
         val minSmallestWidth: Int
@@ -193,8 +193,8 @@
             )
             stickyPlaceholder = getBoolean(R.styleable.SplitPlaceholderRule_stickyPlaceholder,
                 false)
-            finishPrimaryWithSecondary =
-                getInt(R.styleable.SplitPlaceholderRule_finishPrimaryWithSecondary, FINISH_ALWAYS)
+            finishPrimaryWithPlaceholder =
+                getInt(R.styleable.SplitPlaceholderRule_finishPrimaryWithPlaceholder, FINISH_ALWAYS)
             ratio = getFloat(R.styleable.SplitPlaceholderRule_splitRatio, 0.5f)
             minWidth = getDimension(
                 R.styleable.SplitPlaceholderRule_splitMinWidth,
@@ -209,6 +209,12 @@
                 LayoutDirection.LOCALE
             )
         }
+        if (finishPrimaryWithPlaceholder == FINISH_NEVER) {
+                throw IllegalArgumentException(
+                    "FINISH_NEVER is not a valid configuration for Placeholder activities. " +
+                        "Please use FINISH_ALWAYS or FINISH_ADJACENT instead or refer to the " +
+                        "current API")
+        }
         val packageName = context.applicationContext.packageName
         val placeholderActivityClassName = buildClassName(
             packageName,
@@ -220,7 +226,7 @@
             emptySet(),
             Intent().setComponent(placeholderActivityClassName),
             stickyPlaceholder,
-            finishPrimaryWithSecondary,
+            finishPrimaryWithPlaceholder,
             minWidth,
             minSmallestWidth,
             ratio,
diff --git a/window/window/src/main/res/values/attrs.xml b/window/window/src/main/res/values/attrs.xml
index a247d59..c7f284db 100644
--- a/window/window/src/main/res/values/attrs.xml
+++ b/window/window/src/main/res/values/attrs.xml
@@ -33,6 +33,10 @@
         <enum name="always" value="1" />
         <enum name="adjacent" value="2" />
     </attr>
+    <attr name="finishPrimaryWithPlaceholder" format="enum">
+        <enum name="always" value="1" />
+        <enum name="adjacent" value="2" />
+    </attr>
     <attr name="finishSecondaryWithPrimary" format="enum">
         <enum name="never" value="0" />
         <enum name="always" value="1" />
@@ -70,7 +74,7 @@
         <attr name="stickyPlaceholder" format="boolean" />
         <!-- When all activities are finished in the secondary container, the activity in the
          primary container that created the split should also be finished. Defaults to "always". -->
-        <attr name="finishPrimaryWithSecondary" />
+        <attr name="finishPrimaryWithPlaceholder" />
         <attr name="splitRatio"/>
         <attr name="splitMinWidth"/>
         <attr name="splitMinSmallestWidth"/>
diff --git a/window/window/src/test/java/androidx/window/embedding/ActivityFilterTest.kt b/window/window/src/test/java/androidx/window/embedding/ActivityFilterTest.kt
new file mode 100644
index 0000000..8d9987a
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/embedding/ActivityFilterTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 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.window.embedding
+
+import android.content.ComponentName
+import androidx.window.core.ExperimentalWindowApi
+import com.nhaarman.mockitokotlin2.mock
+import com.nhaarman.mockitokotlin2.whenever
+import org.junit.Test
+
+/**
+ * The unit tests for [ActivityFilter] that will test construction.
+ */
+@OptIn(ExperimentalWindowApi::class)
+class ActivityFilterTest {
+
+    @Test(expected = IllegalArgumentException::class)
+    fun packageNameMustNotBeEmpty() {
+        val component = componentName("", "class")
+        ActivityFilter(component, null)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun classNameMustNotBeEmpty() {
+        val component = componentName("package", "")
+        ActivityFilter(component, null)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun packageNameCannotContainWildcard() {
+        val component = componentName("fake.*.package", "class")
+        ActivityFilter(component, null)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun classNameCannotContainWildcard() {
+        val component = componentName("package", "fake.*.class")
+        ActivityFilter(component, null)
+    }
+
+    private fun componentName(pkg: String, cls: String?): ComponentName {
+        return mock<ComponentName>().apply {
+            whenever(this.packageName).thenReturn(pkg)
+            whenever(this.className).thenReturn(cls)
+        }
+    }
+}
\ No newline at end of file