Simplify aggregation extension methods.

Use AggregateRequest/ReadRecordsRequest instead of separate parameters and delete
unnecessary overloads.

Bug: 326414908
Test: Updated tests

Change-Id: Ic8456d04c3537111a5a1c735b42df1ce1ebadd79
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/BloodPressureAggregationExtensionsTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/BloodPressureAggregationExtensionsTest.kt
index 5d7f993..6d5ea0a 100644
--- a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/BloodPressureAggregationExtensionsTest.kt
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/BloodPressureAggregationExtensionsTest.kt
@@ -26,6 +26,7 @@
 import androidx.health.connect.client.records.BloodPressureRecord
 import androidx.health.connect.client.records.NutritionRecord
 import androidx.health.connect.client.records.metadata.DataOrigin
+import androidx.health.connect.client.request.AggregateRequest
 import androidx.health.connect.client.time.TimeRangeFilter
 import androidx.health.connect.client.units.millimetersOfMercury
 import androidx.test.core.app.ApplicationProvider
@@ -90,9 +91,7 @@
         assertThrows(IllegalStateException::class.java) {
             runBlocking {
                 healthConnectClient.aggregateBloodPressure(
-                    metrics,
-                    TimeRangeFilter.none(),
-                    emptySet()
+                    AggregateRequest(metrics, TimeRangeFilter.none(), emptySet())
                 )
             }
         }
@@ -111,7 +110,9 @@
             )
 
         val aggregationResult =
-            healthConnectClient.aggregateBloodPressure(metrics, TimeRangeFilter.none(), emptySet())
+            healthConnectClient.aggregateBloodPressure(
+                AggregateRequest(metrics, TimeRangeFilter.none(), emptySet())
+            )
 
         metrics.forEach { assertThat(it in aggregationResult).isFalse() }
         assertThat(aggregationResult.dataOrigins).isEmpty()
@@ -156,9 +157,11 @@
 
         val aggregationResult =
             healthConnectClient.aggregateBloodPressure(
-                setOf(BloodPressureRecord.SYSTOLIC_MAX),
-                TimeRangeFilter.none(),
-                emptySet()
+                AggregateRequest(
+                    setOf(BloodPressureRecord.SYSTOLIC_MAX),
+                    TimeRangeFilter.none(),
+                    emptySet()
+                )
             )
 
         assertThat(aggregationResult[BloodPressureRecord.SYSTOLIC_MAX])
@@ -187,16 +190,18 @@
 
         val aggregationResult =
             healthConnectClient.aggregateBloodPressure(
-                setOf(
-                    BloodPressureRecord.DIASTOLIC_AVG,
-                    BloodPressureRecord.DIASTOLIC_MAX,
-                    BloodPressureRecord.DIASTOLIC_MIN,
-                    BloodPressureRecord.SYSTOLIC_AVG,
-                    BloodPressureRecord.SYSTOLIC_MAX,
-                    BloodPressureRecord.SYSTOLIC_MIN,
-                ),
-                TimeRangeFilter.none(),
-                emptySet()
+                AggregateRequest(
+                    setOf(
+                        BloodPressureRecord.DIASTOLIC_AVG,
+                        BloodPressureRecord.DIASTOLIC_MAX,
+                        BloodPressureRecord.DIASTOLIC_MIN,
+                        BloodPressureRecord.SYSTOLIC_AVG,
+                        BloodPressureRecord.SYSTOLIC_MAX,
+                        BloodPressureRecord.SYSTOLIC_MIN,
+                    ),
+                    TimeRangeFilter.none(),
+                    emptySet()
+                )
             )
 
         assertEquals(
@@ -231,13 +236,15 @@
 
         val aggregationResult =
             healthConnectClient.aggregateBloodPressure(
-                setOf(
-                    BloodPressureRecord.DIASTOLIC_AVG,
-                    BloodPressureRecord.SYSTOLIC_MAX,
-                    BloodPressureRecord.SYSTOLIC_MIN,
-                ),
-                TimeRangeFilter.none(),
-                emptySet()
+                AggregateRequest(
+                    setOf(
+                        BloodPressureRecord.DIASTOLIC_AVG,
+                        BloodPressureRecord.SYSTOLIC_MAX,
+                        BloodPressureRecord.SYSTOLIC_MIN,
+                    ),
+                    TimeRangeFilter.none(),
+                    emptySet()
+                )
             )
 
         assertEquals(
@@ -293,9 +300,7 @@
 
         val aggregationResult =
             healthConnectClient.aggregateBloodPressure(
-                emptySet(),
-                TimeRangeFilter.none(),
-                emptySet()
+                AggregateRequest(emptySet(), TimeRangeFilter.none(), emptySet())
             )
 
         setOf(
@@ -349,12 +354,14 @@
 
         val aggregationResult =
             healthConnectClient.aggregateBloodPressure(
-                setOf(BloodPressureRecord.DIASTOLIC_MIN),
-                TimeRangeFilter.between(
-                    START_TIME + 30.seconds,
-                    START_TIME + 6.minutes + 45.seconds
-                ),
-                emptySet()
+                AggregateRequest(
+                    setOf(BloodPressureRecord.DIASTOLIC_MIN),
+                    TimeRangeFilter.between(
+                        START_TIME + 30.seconds,
+                        START_TIME + 6.minutes + 45.seconds
+                    ),
+                    emptySet()
+                )
             )
 
         assertThat(aggregationResult[BloodPressureRecord.DIASTOLIC_MIN])
@@ -383,12 +390,14 @@
 
         val aggregationResult =
             healthConnectClient.aggregateBloodPressure(
-                setOf(BloodPressureRecord.DIASTOLIC_AVG, BloodPressureRecord.SYSTOLIC_AVG),
-                TimeRangeFilter.between(
-                    START_TIME + 1.minutes,
-                    START_TIME + 1.minutes + 59.seconds
-                ),
-                emptySet()
+                AggregateRequest(
+                    setOf(BloodPressureRecord.DIASTOLIC_AVG, BloodPressureRecord.SYSTOLIC_AVG),
+                    TimeRangeFilter.between(
+                        START_TIME + 1.minutes,
+                        START_TIME + 1.minutes + 59.seconds
+                    ),
+                    emptySet()
+                )
             )
 
         assertThat(BloodPressureRecord.DIASTOLIC_AVG in aggregationResult).isFalse()
@@ -436,18 +445,20 @@
 
         val aggregationResult =
             healthConnectClient.aggregateBloodPressure(
-                setOf(BloodPressureRecord.DIASTOLIC_MIN, BloodPressureRecord.SYSTOLIC_AVG),
-                TimeRangeFilter.between(
-                    LocalDateTime.ofInstant(
-                        START_TIME + 2.hours + 30.seconds,
-                        ZoneOffset.ofHours(-2)
+                AggregateRequest(
+                    setOf(BloodPressureRecord.DIASTOLIC_MIN, BloodPressureRecord.SYSTOLIC_AVG),
+                    TimeRangeFilter.between(
+                        LocalDateTime.ofInstant(
+                            START_TIME + 2.hours + 30.seconds,
+                            ZoneOffset.ofHours(-2)
+                        ),
+                        LocalDateTime.ofInstant(
+                            START_TIME + 2.hours + 6.minutes + 45.seconds,
+                            ZoneOffset.ofHours(-2)
+                        )
                     ),
-                    LocalDateTime.ofInstant(
-                        START_TIME + 2.hours + 6.minutes + 45.seconds,
-                        ZoneOffset.ofHours(-2)
-                    )
-                ),
-                emptySet()
+                    emptySet()
+                )
             )
 
         assertThat(aggregationResult[BloodPressureRecord.DIASTOLIC_MIN])
@@ -473,9 +484,11 @@
 
         val aggregationResult =
             healthConnectClient.aggregateBloodPressure(
-                setOf(BloodPressureRecord.SYSTOLIC_AVG),
-                TimeRangeFilter.none(),
-                setOf(DataOrigin(context.packageName))
+                AggregateRequest(
+                    setOf(BloodPressureRecord.SYSTOLIC_AVG),
+                    TimeRangeFilter.none(),
+                    setOf(DataOrigin(context.packageName))
+                )
             )
 
         assertThat(aggregationResult[BloodPressureRecord.SYSTOLIC_AVG])
@@ -498,9 +511,11 @@
 
         val aggregationResult =
             healthConnectClient.aggregateBloodPressure(
-                setOf(BloodPressureRecord.SYSTOLIC_AVG),
-                TimeRangeFilter.after(START_TIME + 2.minutes),
-                emptySet()
+                AggregateRequest(
+                    setOf(BloodPressureRecord.SYSTOLIC_AVG),
+                    TimeRangeFilter.after(START_TIME + 2.minutes),
+                    emptySet()
+                )
             )
 
         assertThat(BloodPressureRecord.SYSTOLIC_AVG in aggregationResult).isFalse()
@@ -522,9 +537,11 @@
 
         val aggregationResult =
             healthConnectClient.aggregateBloodPressure(
-                setOf(BloodPressureRecord.SYSTOLIC_AVG),
-                TimeRangeFilter.none(),
-                setOf(DataOrigin("some random package name"))
+                AggregateRequest(
+                    setOf(BloodPressureRecord.SYSTOLIC_AVG),
+                    TimeRangeFilter.none(),
+                    setOf(DataOrigin("some random package name"))
+                )
             )
 
         assertThat(BloodPressureRecord.SYSTOLIC_AVG in aggregationResult).isFalse()
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/HealthConnectClientAggregationExtensionsTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/HealthConnectClientAggregationExtensionsTest.kt
index b48b88b..1175e34 100644
--- a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/HealthConnectClientAggregationExtensionsTest.kt
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/HealthConnectClientAggregationExtensionsTest.kt
@@ -34,6 +34,7 @@
 import androidx.health.connect.client.records.StepsRecord
 import androidx.health.connect.client.records.metadata.DataOrigin
 import androidx.health.connect.client.request.AggregateRequest
+import androidx.health.connect.client.request.ReadRecordsRequest
 import androidx.health.connect.client.time.TimeRangeFilter
 import androidx.health.connect.client.units.Velocity
 import androidx.health.connect.client.units.grams
@@ -308,7 +309,9 @@
 
         val count =
             healthConnectClient
-                .readRecordsFlow(StepsRecord::class, TimeRangeFilter.none(), emptySet())
+                .readRecordsFlow(
+                    ReadRecordsRequest(StepsRecord::class, TimeRangeFilter.none(), emptySet())
+                )
                 .fold(0) { currentCount, records -> currentCount + records.size }
 
         assertThat(count).isEqualTo(10_000L)
@@ -322,12 +325,14 @@
         val count =
             healthConnectClient
                 .readRecordsFlow(
-                    StepsRecord::class,
-                    TimeRangeFilter.between(
-                        START_TIME + 10_000.seconds,
-                        START_TIME + 90_000.seconds
-                    ),
-                    emptySet()
+                    ReadRecordsRequest(
+                        StepsRecord::class,
+                        TimeRangeFilter.between(
+                            START_TIME + 10_000.seconds,
+                            START_TIME + 90_000.seconds
+                        ),
+                        emptySet()
+                    )
                 )
                 .fold(0) { currentCount, records -> currentCount + records.size }
 
@@ -342,9 +347,11 @@
         val count =
             healthConnectClient
                 .readRecordsFlow(
-                    StepsRecord::class,
-                    TimeRangeFilter.none(),
-                    setOf(DataOrigin(context.packageName))
+                    ReadRecordsRequest(
+                        StepsRecord::class,
+                        TimeRangeFilter.none(),
+                        setOf(DataOrigin(context.packageName))
+                    )
                 )
                 .fold(0) { currentCount, records -> currentCount + records.size }
 
@@ -358,9 +365,11 @@
         val count =
             healthConnectClient
                 .readRecordsFlow(
-                    StepsRecord::class,
-                    TimeRangeFilter.none(),
-                    setOf(DataOrigin("some random package name"))
+                    ReadRecordsRequest(
+                        StepsRecord::class,
+                        TimeRangeFilter.none(),
+                        setOf(DataOrigin("some random package name"))
+                    )
                 )
                 .fold(0) { currentCount, records -> currentCount + records.size }
 
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/NutritionAggregationExtensionsTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/NutritionAggregationExtensionsTest.kt
index a197010..0e6d629 100644
--- a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/NutritionAggregationExtensionsTest.kt
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/NutritionAggregationExtensionsTest.kt
@@ -24,6 +24,7 @@
 import androidx.health.connect.client.permission.HealthPermission
 import androidx.health.connect.client.records.NutritionRecord
 import androidx.health.connect.client.records.metadata.DataOrigin
+import androidx.health.connect.client.request.AggregateRequest
 import androidx.health.connect.client.time.TimeRangeFilter
 import androidx.health.connect.client.units.Mass
 import androidx.test.core.app.ApplicationProvider
@@ -72,7 +73,9 @@
     @Test
     fun aggregateNutritionTransFatTotal_noData() = runTest {
         val aggregationResult =
-            healthConnectClient.aggregateNutritionTransFatTotal(TimeRangeFilter.none(), emptySet())
+            healthConnectClient.aggregateNutritionTransFatTotal(
+                AggregateRequest(emptySet(), TimeRangeFilter.none(), emptySet())
+            )
 
         assertThat(NutritionRecord.TRANS_FAT_TOTAL in aggregationResult).isFalse()
         assertThat(aggregationResult.dataOrigins).isEmpty()
@@ -121,7 +124,9 @@
         )
 
         val aggregationResult =
-            healthConnectClient.aggregateNutritionTransFatTotal(TimeRangeFilter.none(), emptySet())
+            healthConnectClient.aggregateNutritionTransFatTotal(
+                AggregateRequest(emptySet(), TimeRangeFilter.none(), emptySet())
+            )
 
         assertThat(aggregationResult[NutritionRecord.TRANS_FAT_TOTAL]).isEqualTo(Mass.grams(1.7))
         assertThat(aggregationResult.dataOrigins).containsExactly(DataOrigin(context.packageName))
@@ -171,11 +176,14 @@
 
         val aggregationResult =
             healthConnectClient.aggregateNutritionTransFatTotal(
-                TimeRangeFilter.between(
-                    START_TIME + 30.seconds,
-                    START_TIME + 6.minutes + 45.seconds
-                ),
-                emptySet()
+                AggregateRequest(
+                    emptySet(),
+                    TimeRangeFilter.between(
+                        START_TIME + 30.seconds,
+                        START_TIME + 6.minutes + 45.seconds
+                    ),
+                    emptySet()
+                )
             )
 
         assertThat(aggregationResult[NutritionRecord.TRANS_FAT_TOTAL])
@@ -207,8 +215,11 @@
 
             val aggregationResult =
                 healthConnectClient.aggregateNutritionTransFatTotal(
-                    TimeRangeFilter.between(START_TIME + 1.minutes, START_TIME + 2.minutes),
-                    emptySet()
+                    AggregateRequest(
+                        emptySet(),
+                        TimeRangeFilter.between(START_TIME + 1.minutes, START_TIME + 2.minutes),
+                        emptySet()
+                    )
                 )
 
             assertThat(NutritionRecord.TRANS_FAT_TOTAL in aggregationResult).isFalse()
@@ -239,8 +250,11 @@
 
             val aggregationResult =
                 healthConnectClient.aggregateNutritionTransFatTotal(
-                    TimeRangeFilter.between(START_TIME, START_TIME + 2.minutes),
-                    emptySet()
+                    AggregateRequest(
+                        emptySet(),
+                        TimeRangeFilter.between(START_TIME, START_TIME + 2.minutes),
+                        emptySet()
+                    )
                 )
 
             assertThat(aggregationResult[NutritionRecord.TRANS_FAT_TOTAL])
@@ -266,8 +280,11 @@
 
             val aggregationResult =
                 healthConnectClient.aggregateNutritionTransFatTotal(
-                    TimeRangeFilter.between(START_TIME + 15.seconds, START_TIME + 45.seconds),
-                    emptySet()
+                    AggregateRequest(
+                        emptySet(),
+                        TimeRangeFilter.between(START_TIME + 15.seconds, START_TIME + 45.seconds),
+                        emptySet()
+                    )
                 )
 
             assertThat(aggregationResult[NutritionRecord.TRANS_FAT_TOTAL])
@@ -320,11 +337,14 @@
 
         val aggregationResult =
             healthConnectClient.aggregateNutritionTransFatTotal(
-                TimeRangeFilter.between(
-                    LocalDateTime.ofInstant(START_TIME + 30.seconds, ZoneOffset.UTC),
-                    LocalDateTime.ofInstant(START_TIME + 6.minutes + 45.seconds, ZoneOffset.UTC)
-                ),
-                emptySet()
+                AggregateRequest(
+                    emptySet(),
+                    TimeRangeFilter.between(
+                        LocalDateTime.ofInstant(START_TIME + 30.seconds, ZoneOffset.UTC),
+                        LocalDateTime.ofInstant(START_TIME + 6.minutes + 45.seconds, ZoneOffset.UTC)
+                    ),
+                    emptySet()
+                )
             )
 
         assertThat(aggregationResult[NutritionRecord.TRANS_FAT_TOTAL])
@@ -349,17 +369,20 @@
 
             val aggregationResult =
                 healthConnectClient.aggregateNutritionTransFatTotal(
-                    TimeRangeFilter.between(
-                        LocalDateTime.ofInstant(
-                            START_TIME - 2.hours + 15.seconds,
-                            ZoneOffset.ofHours(2)
+                    AggregateRequest(
+                        emptySet(),
+                        TimeRangeFilter.between(
+                            LocalDateTime.ofInstant(
+                                START_TIME - 2.hours + 15.seconds,
+                                ZoneOffset.ofHours(2)
+                            ),
+                            LocalDateTime.ofInstant(
+                                START_TIME - 2.hours + 45.seconds,
+                                ZoneOffset.ofHours(2)
+                            )
                         ),
-                        LocalDateTime.ofInstant(
-                            START_TIME - 2.hours + 45.seconds,
-                            ZoneOffset.ofHours(2)
-                        )
-                    ),
-                    emptySet()
+                        emptySet()
+                    )
                 )
 
             assertThat(aggregationResult[NutritionRecord.TRANS_FAT_TOTAL])
@@ -385,8 +408,11 @@
 
         val aggregationResult =
             healthConnectClient.aggregateNutritionTransFatTotal(
-                TimeRangeFilter.none(),
-                setOf(DataOrigin(context.packageName))
+                AggregateRequest(
+                    emptySet(),
+                    TimeRangeFilter.none(),
+                    setOf(DataOrigin(context.packageName))
+                )
             )
 
         assertThat(aggregationResult[NutritionRecord.TRANS_FAT_TOTAL]).isEqualTo(Mass.grams(0.5))
@@ -409,8 +435,11 @@
 
         val aggregationResult =
             healthConnectClient.aggregateNutritionTransFatTotal(
-                TimeRangeFilter.after(START_TIME + 2.minutes),
-                emptySet()
+                AggregateRequest(
+                    emptySet(),
+                    TimeRangeFilter.after(START_TIME + 2.minutes),
+                    emptySet()
+                )
             )
 
         assertThat(NutritionRecord.TRANS_FAT_TOTAL in aggregationResult).isFalse()
@@ -433,11 +462,14 @@
 
         val aggregationResult =
             healthConnectClient.aggregateNutritionTransFatTotal(
-                TimeRangeFilter.between(
-                    LocalDateTime.ofInstant(START_TIME, ZoneOffset.UTC),
-                    LocalDateTime.ofInstant(START_TIME + 60.minutes, ZoneOffset.UTC)
-                ),
-                emptySet()
+                AggregateRequest(
+                    emptySet(),
+                    TimeRangeFilter.between(
+                        LocalDateTime.ofInstant(START_TIME, ZoneOffset.UTC),
+                        LocalDateTime.ofInstant(START_TIME + 60.minutes, ZoneOffset.UTC)
+                    ),
+                    emptySet()
+                )
             )
 
         assertThat(NutritionRecord.TRANS_FAT_TOTAL in aggregationResult).isFalse()
@@ -460,8 +492,11 @@
 
         val aggregationResult =
             healthConnectClient.aggregateNutritionTransFatTotal(
-                TimeRangeFilter.none(),
-                setOf(DataOrigin("some random package name"))
+                AggregateRequest(
+                    emptySet(),
+                    TimeRangeFilter.none(),
+                    setOf(DataOrigin("some random package name"))
+                )
             )
 
         assertThat(NutritionRecord.TRANS_FAT_TOTAL in aggregationResult).isFalse()
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/SeriesRecordAggregationExtensionsTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/SeriesRecordAggregationExtensionsTest.kt
index 5e42b96..3d67fb7 100644
--- a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/SeriesRecordAggregationExtensionsTest.kt
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/aggregate/SeriesRecordAggregationExtensionsTest.kt
@@ -585,15 +585,16 @@
         assertThrows(IllegalStateException::class.java) {
             runBlocking {
                 healthConnectClient.aggregateSeriesRecord(
-                    recordType = StepsCadenceRecord::class,
-                    aggregateMetrics =
+                    StepsCadenceRecord::class,
+                    AggregateRequest(
                         setOf(
                             SpeedRecord.SPEED_AVG,
                             StepsCadenceRecord.RATE_MAX,
                             StepsCadenceRecord.RATE_MIN
                         ),
-                    timeRangeFilter = TimeRangeFilter.none(),
-                    dataOriginFilter = emptySet()
+                        TimeRangeFilter.none(),
+                        emptySet()
+                    )
                 ) {
                     samples.map { SampleInfo(time = it.time, value = it.rate) }
                 }
@@ -607,14 +608,15 @@
             runBlocking {
                 healthConnectClient.aggregateSeriesRecord(
                     recordType = HeartRateRecord::class,
-                    aggregateMetrics =
+                    AggregateRequest(
                         setOf(
                             HeartRateRecord.BPM_AVG,
                             HeartRateRecord.BPM_MAX,
                             HeartRateRecord.BPM_MIN
                         ),
-                    timeRangeFilter = TimeRangeFilter.none(),
-                    dataOriginFilter = emptySet()
+                        TimeRangeFilter.none(),
+                        emptySet()
+                    )
                 ) {
                     samples.map { SampleInfo(time = it.time, value = it.beatsPerMinute.toDouble()) }
                 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientUpsideDownImpl.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientUpsideDownImpl.kt
index e6f24bb..72298c5 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientUpsideDownImpl.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientUpsideDownImpl.kt
@@ -45,7 +45,7 @@
 import androidx.health.connect.client.feature.HealthConnectFeaturesPlatformImpl
 import androidx.health.connect.client.impl.platform.aggregate.AGGREGATE_METRICS_ADDED_IN_SDK_EXT_10
 import androidx.health.connect.client.impl.platform.aggregate.aggregateFallback
-import androidx.health.connect.client.impl.platform.aggregate.platformMetrics
+import androidx.health.connect.client.impl.platform.aggregate.isPlatformSupportedMetric
 import androidx.health.connect.client.impl.platform.aggregate.plus
 import androidx.health.connect.client.impl.platform.records.toPlatformRecord
 import androidx.health.connect.client.impl.platform.records.toPlatformRecordClass
@@ -222,7 +222,7 @@
 
         val fallbackResponse = aggregateFallback(request)
 
-        if (request.platformMetrics.isEmpty()) {
+        if (request.metrics.none { it.isPlatformSupportedMetric() }) {
             return fallbackResponse
         }
 
@@ -236,7 +236,7 @@
                         )
                     }
                 }
-                .toSdkResponse(request.platformMetrics)
+                .toSdkResponse(request.metrics.filter { it.isPlatformSupportedMetric() }.toSet())
 
         return platformResponse + fallbackResponse
     }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/AggregationExtensions.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/AggregationExtensions.kt
index 787968d..31f7abb 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/AggregationExtensions.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/AggregationExtensions.kt
@@ -30,21 +30,18 @@
 import androidx.health.connect.client.records.StepsCadenceRecord
 import androidx.health.connect.client.request.AggregateRequest
 
-internal val AggregateRequest.platformMetrics: Set<AggregateMetric<*>>
-    get() {
-        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 10) {
-            return metrics
-        }
-        return metrics.filterNot { it in AGGREGATE_METRICS_ADDED_IN_SDK_EXT_10 }.toSet()
-    }
+internal fun AggregateRequest.withFilteredMetrics(predicate: (AggregateMetric<*>) -> Boolean) =
+    AggregateRequest(metrics.filter(predicate).toSet(), timeRangeFilter, dataOriginFilter)
 
-internal val AggregateRequest.fallbackMetrics: Set<AggregateMetric<*>>
-    get() {
-        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 10) {
-            return emptySet()
-        }
-        return metrics.filter { it in AGGREGATE_METRICS_ADDED_IN_SDK_EXT_10 }.toSet()
+// Only check against aggregate metrics added in sdk extension 10, to address b/326414908
+// Metrics added later on will be present dependent on feature availability
+internal fun AggregateMetric<*>.isPlatformSupportedMetric(): Boolean {
+    return if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 10) {
+        true
+    } else {
+        this !in AGGREGATE_METRICS_ADDED_IN_SDK_EXT_10
     }
+}
 
 internal operator fun AggregationResult.plus(other: AggregationResult): AggregationResult {
     return AggregationResult(
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/BloodPressureAggregationExtensions.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/BloodPressureAggregationExtensions.kt
index 5051700..bad2c991 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/BloodPressureAggregationExtensions.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/BloodPressureAggregationExtensions.kt
@@ -24,7 +24,8 @@
 import androidx.health.connect.client.aggregate.AggregationResult
 import androidx.health.connect.client.records.BloodPressureRecord
 import androidx.health.connect.client.records.metadata.DataOrigin
-import androidx.health.connect.client.time.TimeRangeFilter
+import androidx.health.connect.client.request.AggregateRequest
+import androidx.health.connect.client.request.ReadRecordsRequest
 import androidx.health.connect.client.units.Pressure
 import kotlin.math.max
 import kotlin.math.min
@@ -40,25 +41,17 @@
     )
 
 internal suspend fun HealthConnectClient.aggregateBloodPressure(
-    bloodPressureMetrics: Set<AggregateMetric<*>>,
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>
-) =
-    aggregateBloodPressure(
-        timeRangeFilter,
-        dataOriginFilter,
-        BloodPressureAggregator(bloodPressureMetrics)
-    )
-
-private suspend fun HealthConnectClient.aggregateBloodPressure(
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>,
-    aggregator: Aggregator<BloodPressureRecord>
+    aggregateRequest: AggregateRequest
 ): AggregationResult {
-    readRecordsFlow(BloodPressureRecord::class, timeRangeFilter, dataOriginFilter).collect { records
-        ->
-        records.forEach { aggregator += it }
-    }
+    val aggregator = BloodPressureAggregator(aggregateRequest.metrics)
+    readRecordsFlow(
+            ReadRecordsRequest(
+                BloodPressureRecord::class,
+                aggregateRequest.timeRangeFilter,
+                aggregateRequest.dataOriginFilter
+            )
+        )
+        .collect { records -> records.forEach { aggregator += it } }
     return aggregator.getResult()
 }
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/HealthConnectClientAggregationExtensions.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/HealthConnectClientAggregationExtensions.kt
index 9d81152..1adb5f6 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/HealthConnectClientAggregationExtensions.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/HealthConnectClientAggregationExtensions.kt
@@ -20,7 +20,6 @@
 
 import androidx.annotation.RequiresApi
 import androidx.health.connect.client.HealthConnectClient
-import androidx.health.connect.client.aggregate.AggregateMetric
 import androidx.health.connect.client.aggregate.AggregationResult
 import androidx.health.connect.client.impl.converters.datatype.RECORDS_CLASS_NAME_MAP
 import androidx.health.connect.client.impl.platform.toInstantWithDefaultZoneFallback
@@ -32,7 +31,6 @@
 import androidx.health.connect.client.records.Record
 import androidx.health.connect.client.records.SpeedRecord
 import androidx.health.connect.client.records.StepsCadenceRecord
-import androidx.health.connect.client.records.metadata.DataOrigin
 import androidx.health.connect.client.request.AggregateRequest
 import androidx.health.connect.client.request.ReadRecordsRequest
 import androidx.health.connect.client.time.TimeRangeFilter
@@ -60,12 +58,7 @@
 
     for (recordType in AGGREGATION_FALLBACK_RECORD_TYPES) {
         aggregationResult +=
-            aggregate(
-                recordType,
-                request.fallbackMetrics,
-                request.timeRangeFilter,
-                request.dataOriginFilter
-            )
+            aggregate(recordType, request.withFilteredMetrics { !it.isPlatformSupportedMetric() })
     }
 
     return aggregationResult
@@ -73,51 +66,45 @@
 
 private suspend fun <T : Record> HealthConnectClient.aggregate(
     recordType: KClass<T>,
-    metrics: Set<AggregateMetric<*>>,
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>
+    request: AggregateRequest,
 ): AggregationResult {
     val dataTypeName = RECORDS_CLASS_NAME_MAP[recordType]
-    val recordTypeMetrics = metrics.filter { it.dataTypeName == dataTypeName }.toSet()
+    val recordTypeRequest = request.withFilteredMetrics { it.dataTypeName == dataTypeName }
 
-    if (recordTypeMetrics.isEmpty()) {
+    if (recordTypeRequest.metrics.isEmpty()) {
         return emptyAggregationResult()
     }
 
     return when (recordType) {
-        BloodPressureRecord::class ->
-            aggregateBloodPressure(recordTypeMetrics, timeRangeFilter, dataOriginFilter)
+        BloodPressureRecord::class -> aggregateBloodPressure(recordTypeRequest)
         CyclingPedalingCadenceRecord::class ->
-            aggregateCyclingPedalingCadence(recordTypeMetrics, timeRangeFilter, dataOriginFilter)
-        NutritionRecord::class -> aggregateNutritionTransFatTotal(timeRangeFilter, dataOriginFilter)
-        SpeedRecord::class -> aggregateSpeed(recordTypeMetrics, timeRangeFilter, dataOriginFilter)
+            aggregateSeriesRecord(CyclingPedalingCadenceRecord::class, recordTypeRequest) {
+                samples.map { SampleInfo(it.time, it.revolutionsPerMinute) }
+            }
+        NutritionRecord::class -> aggregateNutritionTransFatTotal(recordTypeRequest)
+        SpeedRecord::class ->
+            aggregateSeriesRecord(SpeedRecord::class, recordTypeRequest) {
+                samples.map { SampleInfo(it.time, it.speed.inMetersPerSecond) }
+            }
         StepsCadenceRecord::class ->
-            aggregateStepsCadence(recordTypeMetrics, timeRangeFilter, dataOriginFilter)
+            aggregateSeriesRecord(StepsCadenceRecord::class, recordTypeRequest) {
+                samples.map { SampleInfo(it.time, it.rate) }
+            }
         else -> error("Invalid record type for aggregation fallback: $recordType")
     }
 }
 
-/** Reads all existing records that satisfy [timeRangeFilter] and [dataOriginFilter]. */
-suspend fun <T : Record> HealthConnectClient.readRecordsFlow(
-    recordType: KClass<T>,
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>
+/** Reads all existing records that satisfy [request]. */
+fun <T : Record> HealthConnectClient.readRecordsFlow(
+    request: ReadRecordsRequest<T>
 ): Flow<List<T>> {
     return flow {
-        var pageToken: String? = null
+        var currentRequest = request
         do {
-            val response =
-                readRecords(
-                    ReadRecordsRequest(
-                        recordType = recordType,
-                        timeRangeFilter = timeRangeFilter,
-                        dataOriginFilter = dataOriginFilter,
-                        pageToken = pageToken
-                    )
-                )
+            val response = readRecords(currentRequest)
             emit(response.records)
-            pageToken = response.pageToken
-        } while (pageToken != null)
+            currentRequest = currentRequest.withPageToken(response.pageToken)
+        } while (currentRequest.pageToken != null)
     }
 }
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/NutritionAggregationExtensions.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/NutritionAggregationExtensions.kt
index 56a255c..927132ad 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/NutritionAggregationExtensions.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/NutritionAggregationExtensions.kt
@@ -29,23 +29,28 @@
 import androidx.health.connect.client.records.IntervalRecord
 import androidx.health.connect.client.records.NutritionRecord
 import androidx.health.connect.client.records.metadata.DataOrigin
+import androidx.health.connect.client.request.AggregateRequest
+import androidx.health.connect.client.request.ReadRecordsRequest
 import androidx.health.connect.client.time.TimeRangeFilter
 import java.time.Instant
 import kotlin.math.max
 
 internal suspend fun HealthConnectClient.aggregateNutritionTransFatTotal(
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>
-) = aggregateNutrition(timeRangeFilter, dataOriginFilter, TransFatTotalAggregator(timeRangeFilter))
-
-private suspend fun HealthConnectClient.aggregateNutrition(
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>,
-    aggregator: Aggregator<NutritionRecord>
+    aggregateRequest: AggregateRequest
 ): AggregationResult {
-    readRecordsFlow(NutritionRecord::class, timeRangeFilter.withBufferedStart(), dataOriginFilter)
+    val aggregator = TransFatTotalAggregator(aggregateRequest.timeRangeFilter)
+
+    readRecordsFlow(
+            ReadRecordsRequest(
+                NutritionRecord::class,
+                aggregateRequest.timeRangeFilter.withBufferedStart(),
+                aggregateRequest.dataOriginFilter
+            )
+        )
         .collect { records ->
-            records.filter { it.overlaps(timeRangeFilter) }.forEach { aggregator += it }
+            records
+                .filter { it.overlaps(aggregateRequest.timeRangeFilter) }
+                .forEach { aggregator += it }
         }
 
     return aggregator.getResult()
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/SeriesRecordAggregationExtensions.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/SeriesRecordAggregationExtensions.kt
index 012c46d..958797e 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/SeriesRecordAggregationExtensions.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/aggregate/SeriesRecordAggregationExtensions.kt
@@ -30,6 +30,8 @@
 import androidx.health.connect.client.records.SpeedRecord
 import androidx.health.connect.client.records.StepsCadenceRecord
 import androidx.health.connect.client.records.metadata.DataOrigin
+import androidx.health.connect.client.request.AggregateRequest
+import androidx.health.connect.client.request.ReadRecordsRequest
 import androidx.health.connect.client.time.TimeRangeFilter
 import java.time.Instant
 import java.time.ZoneOffset
@@ -59,74 +61,21 @@
             )
     )
 
-internal suspend fun HealthConnectClient.aggregateCyclingPedalingCadence(
-    metrics: Set<AggregateMetric<*>>,
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>
-) =
-    aggregateSeriesRecord(
-        recordType = CyclingPedalingCadenceRecord::class,
-        aggregateMetrics = metrics,
-        timeRangeFilter = timeRangeFilter,
-        dataOriginFilter = dataOriginFilter
-    ) {
-        samples.map { SampleInfo(time = it.time, value = it.revolutionsPerMinute) }
-    }
-
-internal suspend fun HealthConnectClient.aggregateSpeed(
-    metrics: Set<AggregateMetric<*>>,
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>
-) =
-    aggregateSeriesRecord(
-        recordType = SpeedRecord::class,
-        aggregateMetrics = metrics,
-        timeRangeFilter = timeRangeFilter,
-        dataOriginFilter = dataOriginFilter
-    ) {
-        samples.map { SampleInfo(time = it.time, value = it.speed.inMetersPerSecond) }
-    }
-
-internal suspend fun HealthConnectClient.aggregateStepsCadence(
-    metrics: Set<AggregateMetric<*>>,
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>
-) =
-    aggregateSeriesRecord(
-        recordType = StepsCadenceRecord::class,
-        aggregateMetrics = metrics,
-        timeRangeFilter = timeRangeFilter,
-        dataOriginFilter = dataOriginFilter
-    ) {
-        samples.map { SampleInfo(time = it.time, value = it.rate) }
-    }
-
-@VisibleForTesting
 internal suspend fun <T : SeriesRecord<*>> HealthConnectClient.aggregateSeriesRecord(
     recordType: KClass<T>,
-    aggregateMetrics: Set<AggregateMetric<*>>,
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>,
-    getSampleInfo: T.() -> List<SampleInfo>
-) =
-    aggregateSeriesRecord(
-        recordType,
-        timeRangeFilter,
-        dataOriginFilter,
-        SeriesAggregator(recordType, aggregateMetrics),
-        getSampleInfo
-    )
-
-private suspend fun <T : SeriesRecord<*>> HealthConnectClient.aggregateSeriesRecord(
-    recordType: KClass<T>,
-    timeRangeFilter: TimeRangeFilter,
-    dataOriginFilter: Set<DataOrigin>,
-    aggregator: Aggregator<RecordInfo>,
+    aggregateRequest: AggregateRequest,
     getSampleInfo: T.() -> List<SampleInfo>
 ): AggregationResult {
     val readRecordsFlow =
-        readRecordsFlow(recordType, timeRangeFilter.withBufferedStart(), dataOriginFilter)
+        readRecordsFlow(
+            ReadRecordsRequest(
+                recordType,
+                aggregateRequest.timeRangeFilter.withBufferedStart(),
+                aggregateRequest.dataOriginFilter
+            )
+        )
 
+    val aggregator = SeriesAggregator(recordType, aggregateRequest.metrics)
     readRecordsFlow.collect { records ->
         records
             .asSequence()
@@ -136,7 +85,7 @@
                     samples =
                         it.getSampleInfo().filter { sample ->
                             sample.isWithin(
-                                timeRangeFilter = timeRangeFilter,
+                                timeRangeFilter = aggregateRequest.timeRangeFilter,
                                 zoneOffset = it.startZoneOffset
                             )
                         }
@@ -207,7 +156,6 @@
 @VisibleForTesting
 internal data class RecordInfo(val dataOrigin: DataOrigin, val samples: List<SampleInfo>)
 
-@VisibleForTesting
 internal data class SampleInfo(val time: Instant, val value: Double) {
     fun isWithin(timeRangeFilter: TimeRangeFilter, zoneOffset: ZoneOffset?): Boolean {
         if (timeRangeFilter.useLocalTime()) {
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/request/RequestConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/request/RequestConverters.kt
index b12909e..1bf55fc 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/request/RequestConverters.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/request/RequestConverters.kt
@@ -42,7 +42,7 @@
 import androidx.health.connect.client.impl.platform.aggregate.TEMPERATURE_DELTA_METRIC_TYPE_MAP
 import androidx.health.connect.client.impl.platform.aggregate.VELOCITY_AGGREGATION_METRIC_TYPE_MAP
 import androidx.health.connect.client.impl.platform.aggregate.VOLUME_AGGREGATION_METRIC_TYPE_MAP
-import androidx.health.connect.client.impl.platform.aggregate.platformMetrics
+import androidx.health.connect.client.impl.platform.aggregate.isPlatformSupportedMetric
 import androidx.health.connect.client.impl.platform.records.toPlatformDataOrigin
 import androidx.health.connect.client.impl.platform.records.toPlatformRecordClass
 import androidx.health.connect.client.records.Record
@@ -116,7 +116,9 @@
     return AggregateRecordsRequest.Builder<Any>(timeRangeFilter.toPlatformTimeRangeFilter())
         .apply {
             dataOriginFilter.forEach { addDataOriginsFilter(it.toPlatformDataOrigin()) }
-            platformMetrics.forEach { addAggregationType(it.toAggregationType()) }
+            metrics
+                .filter { it.isPlatformSupportedMetric() }
+                .forEach { addAggregationType(it.toAggregationType()) }
         }
         .build()
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/request/ReadRecordsRequest.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/request/ReadRecordsRequest.kt
index 6810ac0..f0120f9 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/request/ReadRecordsRequest.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/request/ReadRecordsRequest.kt
@@ -104,6 +104,19 @@
         require(pageSize > 0) { "pageSize must be positive." }
     }
 
+    @OptIn(ExperimentalDeduplicationApi::class)
+    internal fun withPageToken(newPageToken: String?): ReadRecordsRequest<T> {
+        return ReadRecordsRequest(
+            recordType = recordType,
+            timeRangeFilter = timeRangeFilter,
+            dataOriginFilter = dataOriginFilter,
+            ascendingOrder = ascendingOrder,
+            pageSize = pageSize,
+            pageToken = newPageToken,
+            deduplicateStrategy = deduplicateStrategy
+        )
+    }
+
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (javaClass != other?.javaClass) return false