Add segments and laps to the exercise session record

Add ExerciseSegment and ExerciseLap classes as parts of
the of exercise session record. Update session types.

Design doc: go/hc-platform-session-datatype
Bug: 262550571
Test: atest CtsHealthConnectDeviceTestCases

Change-Id: I115a2958984350b942e8f29d08e112549567e15a
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 067fa9b..5b02af7 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -479,6 +479,18 @@
     method @NonNull public android.healthconnect.datatypes.ElevationGainedRecord.Builder setStartZoneOffset(@NonNull java.time.ZoneOffset);
   }
 
+  public final class ExerciseLap {
+    method @NonNull public java.time.Instant getEndTime();
+    method @Nullable public android.healthconnect.datatypes.units.Length getLength();
+    method @NonNull public java.time.Instant getStartTime();
+  }
+
+  public static final class ExerciseLap.Builder {
+    ctor public ExerciseLap.Builder(@NonNull java.time.Instant, @NonNull java.time.Instant);
+    method @NonNull public android.healthconnect.datatypes.ExerciseLap build();
+    method @NonNull public android.healthconnect.datatypes.ExerciseLap.Builder setLength(@NonNull android.healthconnect.datatypes.units.Length);
+  }
+
   public final class ExerciseRoute {
     ctor public ExerciseRoute(@NonNull java.util.List<android.healthconnect.datatypes.ExerciseRoute.Location>);
     method @NonNull public java.util.List<android.healthconnect.datatypes.ExerciseRoute.Location> getRouteLocations();
@@ -501,10 +513,96 @@
     method @NonNull public android.healthconnect.datatypes.ExerciseRoute.Location.Builder setVerticalAccuracy(@NonNull android.healthconnect.datatypes.units.Length);
   }
 
+  public final class ExerciseSegment {
+    method @NonNull public java.time.Instant getEndTime();
+    method @IntRange(from=0) public int getRepetitionsCount();
+    method public int getSegmentType();
+    method @NonNull public java.time.Instant getStartTime();
+  }
+
+  public static final class ExerciseSegment.Builder {
+    ctor public ExerciseSegment.Builder(@NonNull java.time.Instant, @NonNull java.time.Instant, int);
+    method @NonNull public android.healthconnect.datatypes.ExerciseSegment build();
+    method @NonNull public android.healthconnect.datatypes.ExerciseSegment.Builder setRepetitionsCount(@IntRange(from=0) int);
+  }
+
+  public final class ExerciseSegmentType {
+    field public static final int EXERCISE_SEGMENT_TYPE_ARM_CURL = 26; // 0x1a
+    field public static final int EXERCISE_SEGMENT_TYPE_BACK_EXTENSION = 27; // 0x1b
+    field public static final int EXERCISE_SEGMENT_TYPE_BALL_SLAM = 28; // 0x1c
+    field public static final int EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS = 1; // 0x1
+    field public static final int EXERCISE_SEGMENT_TYPE_BENCH_PRESS = 29; // 0x1d
+    field public static final int EXERCISE_SEGMENT_TYPE_BENCH_SIT_UP = 2; // 0x2
+    field public static final int EXERCISE_SEGMENT_TYPE_BIKING = 3; // 0x3
+    field public static final int EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY = 4; // 0x4
+    field public static final int EXERCISE_SEGMENT_TYPE_BURPEE = 30; // 0x1e
+    field public static final int EXERCISE_SEGMENT_TYPE_CRUNCH = 31; // 0x1f
+    field public static final int EXERCISE_SEGMENT_TYPE_DEADLIFT = 32; // 0x20
+    field public static final int EXERCISE_SEGMENT_TYPE_DOUBLE_ARM_TRICEPS_EXTENSION = 33; // 0x21
+    field public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_LEFT_ARM = 5; // 0x5
+    field public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_RIGHT_ARM = 6; // 0x6
+    field public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_FRONT_RAISE = 7; // 0x7
+    field public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_LATERAL_RAISE = 8; // 0x8
+    field public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_ROW = 34; // 0x22
+    field public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_LEFT_ARM = 9; // 0x9
+    field public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_RIGHT_ARM = 10; // 0xa
+    field public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_TWO_ARM = 11; // 0xb
+    field public static final int EXERCISE_SEGMENT_TYPE_ELLIPTICAL = 12; // 0xc
+    field public static final int EXERCISE_SEGMENT_TYPE_FORWARD_TWIST = 13; // 0xd
+    field public static final int EXERCISE_SEGMENT_TYPE_FRONT_RAISE = 35; // 0x23
+    field public static final int EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING = 62; // 0x3e
+    field public static final int EXERCISE_SEGMENT_TYPE_HIP_THRUST = 36; // 0x24
+    field public static final int EXERCISE_SEGMENT_TYPE_HULA_HOOP = 37; // 0x25
+    field public static final int EXERCISE_SEGMENT_TYPE_JUMPING_JACK = 38; // 0x26
+    field public static final int EXERCISE_SEGMENT_TYPE_JUMP_ROPE = 39; // 0x27
+    field public static final int EXERCISE_SEGMENT_TYPE_KETTLEBELL_SWING = 40; // 0x28
+    field public static final int EXERCISE_SEGMENT_TYPE_LATERAL_RAISE = 41; // 0x29
+    field public static final int EXERCISE_SEGMENT_TYPE_LAT_PULL_DOWN = 42; // 0x2a
+    field public static final int EXERCISE_SEGMENT_TYPE_LEG_CURL = 43; // 0x2b
+    field public static final int EXERCISE_SEGMENT_TYPE_LEG_EXTENSION = 44; // 0x2c
+    field public static final int EXERCISE_SEGMENT_TYPE_LEG_PRESS = 45; // 0x2d
+    field public static final int EXERCISE_SEGMENT_TYPE_LEG_RAISE = 46; // 0x2e
+    field public static final int EXERCISE_SEGMENT_TYPE_LUNGE = 47; // 0x2f
+    field public static final int EXERCISE_SEGMENT_TYPE_MOUNTAIN_CLIMBER = 48; // 0x30
+    field public static final int EXERCISE_SEGMENT_TYPE_OTHER_WORKOUT = 64; // 0x40
+    field public static final int EXERCISE_SEGMENT_TYPE_PAUSE = 67; // 0x43
+    field public static final int EXERCISE_SEGMENT_TYPE_PILATES = 14; // 0xe
+    field public static final int EXERCISE_SEGMENT_TYPE_PLANK = 49; // 0x31
+    field public static final int EXERCISE_SEGMENT_TYPE_PULL_UP = 50; // 0x32
+    field public static final int EXERCISE_SEGMENT_TYPE_PUNCH = 51; // 0x33
+    field public static final int EXERCISE_SEGMENT_TYPE_REST = 66; // 0x42
+    field public static final int EXERCISE_SEGMENT_TYPE_ROWING_MACHINE = 15; // 0xf
+    field public static final int EXERCISE_SEGMENT_TYPE_RUNNING = 16; // 0x10
+    field public static final int EXERCISE_SEGMENT_TYPE_RUNNING_TREADMILL = 17; // 0x11
+    field public static final int EXERCISE_SEGMENT_TYPE_SHOULDER_PRESS = 52; // 0x34
+    field public static final int EXERCISE_SEGMENT_TYPE_SINGLE_ARM_TRICEPS_EXTENSION = 53; // 0x35
+    field public static final int EXERCISE_SEGMENT_TYPE_SIT_UP = 54; // 0x36
+    field public static final int EXERCISE_SEGMENT_TYPE_SQUAT = 55; // 0x37
+    field public static final int EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING = 18; // 0x12
+    field public static final int EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING_MACHINE = 19; // 0x13
+    field public static final int EXERCISE_SEGMENT_TYPE_STRETCHING = 20; // 0x14
+    field public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_BACKSTROKE = 57; // 0x39
+    field public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_BREASTSTROKE = 58; // 0x3a
+    field public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_BUTTERFLY = 59; // 0x3b
+    field public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_FREESTYLE = 56; // 0x38
+    field public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_MIXED = 60; // 0x3c
+    field public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_OPEN_WATER = 21; // 0x15
+    field public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_OTHER = 61; // 0x3d
+    field public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_POOL = 22; // 0x16
+    field public static final int EXERCISE_SEGMENT_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int EXERCISE_SEGMENT_TYPE_UPPER_TWIST = 23; // 0x17
+    field public static final int EXERCISE_SEGMENT_TYPE_WALKING = 24; // 0x18
+    field public static final int EXERCISE_SEGMENT_TYPE_WEIGHTLIFTING = 63; // 0x3f
+    field public static final int EXERCISE_SEGMENT_TYPE_WHEELCHAIR = 25; // 0x19
+    field public static final int EXERCISE_SEGMENT_TYPE_YOGA = 65; // 0x41
+  }
+
   public final class ExerciseSessionRecord extends android.healthconnect.datatypes.IntervalRecord {
     method public int getExerciseType();
+    method @NonNull public java.util.List<android.healthconnect.datatypes.ExerciseLap> getLaps();
     method @Nullable public CharSequence getNotes();
     method @Nullable public android.healthconnect.datatypes.ExerciseRoute getRoute();
+    method @NonNull public java.util.List<android.healthconnect.datatypes.ExerciseSegment> getSegments();
     method @Nullable public CharSequence getTitle();
     method @NonNull public boolean hasRoute();
   }
@@ -513,8 +611,10 @@
     ctor public ExerciseSessionRecord.Builder(@NonNull android.healthconnect.datatypes.Metadata, @NonNull java.time.Instant, @NonNull java.time.Instant, int);
     method @NonNull public android.healthconnect.datatypes.ExerciseSessionRecord build();
     method @NonNull public android.healthconnect.datatypes.ExerciseSessionRecord.Builder setEndZoneOffset(@NonNull java.time.ZoneOffset);
+    method @NonNull public android.healthconnect.datatypes.ExerciseSessionRecord.Builder setLaps(@NonNull java.util.List<android.healthconnect.datatypes.ExerciseLap>);
     method @NonNull public android.healthconnect.datatypes.ExerciseSessionRecord.Builder setNotes(@Nullable CharSequence);
     method @NonNull public android.healthconnect.datatypes.ExerciseSessionRecord.Builder setRoute(@Nullable android.healthconnect.datatypes.ExerciseRoute);
+    method @NonNull public android.healthconnect.datatypes.ExerciseSessionRecord.Builder setSegments(@NonNull java.util.List<android.healthconnect.datatypes.ExerciseSegment>);
     method @NonNull public android.healthconnect.datatypes.ExerciseSessionRecord.Builder setStartZoneOffset(@NonNull java.time.ZoneOffset);
     method @NonNull public android.healthconnect.datatypes.ExerciseSessionRecord.Builder setTitle(@Nullable CharSequence);
   }
@@ -530,6 +630,7 @@
     field public static final int EXERCISE_SESSION_TYPE_CALISTHENICS = 8; // 0x8
     field public static final int EXERCISE_SESSION_TYPE_CRICKET = 9; // 0x9
     field public static final int EXERCISE_SESSION_TYPE_DANCING = 10; // 0xa
+    field public static final int EXERCISE_SESSION_TYPE_ELLIPTICAL = 60; // 0x3c
     field public static final int EXERCISE_SESSION_TYPE_EXERCISE_CLASS = 11; // 0xb
     field public static final int EXERCISE_SESSION_TYPE_FENCING = 12; // 0xc
     field public static final int EXERCISE_SESSION_TYPE_FOOTBALL_AMERICAN = 13; // 0xd
@@ -552,6 +653,7 @@
     field public static final int EXERCISE_SESSION_TYPE_ROCK_CLIMBING = 29; // 0x1d
     field public static final int EXERCISE_SESSION_TYPE_ROLLER_HOCKEY = 30; // 0x1e
     field public static final int EXERCISE_SESSION_TYPE_ROWING = 31; // 0x1f
+    field public static final int EXERCISE_SESSION_TYPE_ROWING_MACHINE = 61; // 0x3d
     field public static final int EXERCISE_SESSION_TYPE_RUGBY = 32; // 0x20
     field public static final int EXERCISE_SESSION_TYPE_RUNNING = 33; // 0x21
     field public static final int EXERCISE_SESSION_TYPE_RUNNING_TREADMILL = 34; // 0x22
@@ -565,6 +667,7 @@
     field public static final int EXERCISE_SESSION_TYPE_SOFTBALL = 42; // 0x2a
     field public static final int EXERCISE_SESSION_TYPE_SQUASH = 43; // 0x2b
     field public static final int EXERCISE_SESSION_TYPE_STAIR_CLIMBING = 44; // 0x2c
+    field public static final int EXERCISE_SESSION_TYPE_STAIR_CLIMBING_MACHINE = 59; // 0x3b
     field public static final int EXERCISE_SESSION_TYPE_STRENGTH_TRAINING = 45; // 0x2d
     field public static final int EXERCISE_SESSION_TYPE_STRETCHING = 46; // 0x2e
     field public static final int EXERCISE_SESSION_TYPE_SURFING = 47; // 0x2f
diff --git a/framework/java/android/healthconnect/datatypes/ExerciseLap.java b/framework/java/android/healthconnect/datatypes/ExerciseLap.java
new file mode 100644
index 0000000..01622f0
--- /dev/null
+++ b/framework/java/android/healthconnect/datatypes/ExerciseLap.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.healthconnect.datatypes;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.healthconnect.datatypes.units.Length;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * Captures the time of a lap within exercise session. Part of {@link ExerciseSessionRecord}.
+ *
+ * <p>Each record contains the start and end time and optional {@link Length} of the lap (e.g. pool
+ * length while swimming or a track lap while running). There may or may not be direct correlation
+ * with {@link ExerciseSegment} start and end times, e.g. {@link ExerciseSessionRecord} of type
+ * running without any segments can be divided as laps of different lengths.
+ */
+public final class ExerciseLap {
+    private static final int MAX_LAP_LENGTH_METRES = 10000000;
+
+    private final TimeInterval mInterval;
+    private final Length mLength;
+
+    private ExerciseLap(@NonNull TimeInterval interval, @Nullable Length length) {
+        Objects.requireNonNull(interval);
+        mInterval = interval;
+        mLength = length;
+    }
+
+    /*
+     * Returns Length of the lap.
+     */
+    @Nullable
+    public Length getLength() {
+        return mLength;
+    }
+
+    /*
+     * Returns start time of the lap.
+     */
+    @NonNull
+    public Instant getStartTime() {
+        return mInterval.getStartTime();
+    }
+
+    /*
+     * Returns end time of the lap.
+     */
+    @NonNull
+    public Instant getEndTime() {
+        return mInterval.getEndTime();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ExerciseLap)) return false;
+        ExerciseLap that = (ExerciseLap) o;
+        return Objects.equals(mInterval, that.mInterval)
+                && Objects.equals(getLength(), that.getLength());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mInterval, getLength());
+    }
+
+    /** Builder class for {@link ExerciseLap} */
+    public static final class Builder {
+        private final TimeInterval mInterval;
+        private Length mLength;
+
+        public Builder(@NonNull Instant startTime, @NonNull Instant endTime) {
+            mInterval = new TimeInterval(startTime, endTime);
+        }
+
+        /**
+         * Sets the length of this lap
+         *
+         * @param length Length of the lap, in {@link Length} unit. Optional field. Valid range:
+         *     0-1000000 meters.
+         */
+        @NonNull
+        public ExerciseLap.Builder setLength(@NonNull Length length) {
+            Objects.requireNonNull(length);
+            if (length.getInMeters() < 0 || length.getInMeters() > MAX_LAP_LENGTH_METRES) {
+                throw new IllegalArgumentException("Length must be between 0-1000000 metres");
+            }
+            mLength = length;
+            return this;
+        }
+
+        /** Builds {@link ExerciseLap} instance. */
+        @NonNull
+        public ExerciseLap build() {
+            return new ExerciseLap(mInterval, mLength);
+        }
+    }
+}
diff --git a/framework/java/android/healthconnect/datatypes/ExerciseSegment.java b/framework/java/android/healthconnect/datatypes/ExerciseSegment.java
new file mode 100644
index 0000000..7f3cfcc
--- /dev/null
+++ b/framework/java/android/healthconnect/datatypes/ExerciseSegment.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.healthconnect.datatypes;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * Represents particular exercise within exercise session (see {@link ExerciseSessionRecord}).
+ *
+ * <p>Each record contains start and end time of the exercise, exercise type and optional number of
+ * repetitions.
+ */
+public final class ExerciseSegment {
+    private final TimeInterval mInterval;
+
+    @ExerciseSegmentType.ExerciseSegmentTypes private final int mSegmentType;
+
+    private final int mRepetitionsCount;
+
+    private ExerciseSegment(
+            @NonNull TimeInterval interval,
+            @ExerciseSegmentType.ExerciseSegmentTypes int segmentType,
+            @IntRange(from = 0) int repetitionsCount) {
+        Objects.requireNonNull(interval);
+        mInterval = interval;
+
+        mSegmentType = segmentType;
+
+        if (repetitionsCount < 0) {
+            throw new IllegalArgumentException("Repetitions count must be non-negative.");
+        }
+        mRepetitionsCount = repetitionsCount;
+    }
+
+    /*
+     * Returns type of the segment, one of {@link @ExerciseSegmentType.ExerciseSegmentTypes}.
+     */
+    @ExerciseSegmentType.ExerciseSegmentTypes
+    public int getSegmentType() {
+        return mSegmentType;
+    }
+
+    /*
+     * Returns number of repetitions in the current segment. Positive value.
+     */
+    @IntRange(from = 0)
+    public int getRepetitionsCount() {
+        return mRepetitionsCount;
+    }
+
+    /*
+     * Returns start time of the segment.
+     */
+    @NonNull
+    public Instant getStartTime() {
+        return mInterval.getStartTime();
+    }
+
+    /*
+     * Returns end time of the segment.
+     */
+    @NonNull
+    public Instant getEndTime() {
+        return mInterval.getEndTime();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ExerciseSegment)) return false;
+        ExerciseSegment that = (ExerciseSegment) o;
+        return mSegmentType == that.mSegmentType && mRepetitionsCount == that.mRepetitionsCount;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSegmentType, mRepetitionsCount);
+    }
+
+    /** Builder class for {@link ExerciseSegment} */
+    public static final class Builder {
+        private final TimeInterval mInterval;
+
+        @ExerciseSegmentType.ExerciseSegmentTypes private final int mSegmentType;
+
+        private int mRepetitionsCount = 0;
+
+        public Builder(
+                @NonNull Instant startTime,
+                @NonNull Instant endTime,
+                @ExerciseSegmentType.ExerciseSegmentTypes int segmentType) {
+            Objects.requireNonNull(startTime);
+            Objects.requireNonNull(endTime);
+            mInterval = new TimeInterval(startTime, endTime);
+            mSegmentType = segmentType;
+        }
+
+        /**
+         * Sets the number of repetitions to the current segment. Returns builder instance with
+         * repetitions count set.
+         */
+        @NonNull
+        public Builder setRepetitionsCount(@IntRange(from = 0) int repetitionsCount) {
+            if (repetitionsCount < 0) {
+                throw new IllegalArgumentException("Number of repetitions must be non negative.");
+            }
+            mRepetitionsCount = repetitionsCount;
+            return this;
+        }
+
+        /**
+         * Sets the number repetitions to the current segment. Returns {@link ExerciseSegment}
+         * instance.
+         */
+        @NonNull
+        public ExerciseSegment build() {
+            return new ExerciseSegment(mInterval, mSegmentType, mRepetitionsCount);
+        }
+    }
+}
diff --git a/framework/java/android/healthconnect/datatypes/ExerciseSegmentType.java b/framework/java/android/healthconnect/datatypes/ExerciseSegmentType.java
new file mode 100644
index 0000000..eb32102
--- /dev/null
+++ b/framework/java/android/healthconnect/datatypes/ExerciseSegmentType.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.healthconnect.datatypes;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Identifier for exercise types, as returned by {@link ExerciseSegment#getSegmentType()}. */
+public final class ExerciseSegmentType {
+
+    /** Use this type if the type of the exercise segment is not known. */
+    public static final int EXERCISE_SEGMENT_TYPE_UNKNOWN = 0;
+
+    /** Use this type for barbel shoulder press. */
+    public static final int EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS = 1;
+
+    /** Use this type for bench sit up. */
+    public static final int EXERCISE_SEGMENT_TYPE_BENCH_SIT_UP = 2;
+
+    /** Use this type for biking. */
+    public static final int EXERCISE_SEGMENT_TYPE_BIKING = 3;
+
+    /** Use this type for stationary biking. */
+    public static final int EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY = 4;
+
+    /** Use this type for left arm dumbbell curl. */
+    public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_LEFT_ARM = 5;
+
+    /** Use this type for right arm dumbbell curl. */
+    public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_RIGHT_ARM = 6;
+
+    /** Use this type for right arm dumbbell front raise. */
+    public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_FRONT_RAISE = 7;
+
+    /** Use this type for dumbbell lateral raises. */
+    public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_LATERAL_RAISE = 8;
+
+    /** Use this type for left arm triceps extensions. */
+    public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_LEFT_ARM = 9;
+
+    /** Use this type for right arm triceps extensions. */
+    public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_RIGHT_ARM = 10;
+
+    /** Use this type for two arms triceps extensions. */
+    public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_TWO_ARM = 11;
+
+    /** Use this type for elliptical workout. */
+    public static final int EXERCISE_SEGMENT_TYPE_ELLIPTICAL = 12;
+
+    /** Use this type for forward twists. */
+    public static final int EXERCISE_SEGMENT_TYPE_FORWARD_TWIST = 13;
+
+    /** Use this type for pilates. */
+    public static final int EXERCISE_SEGMENT_TYPE_PILATES = 14;
+
+    /** Use this type for rowing machine workout. */
+    public static final int EXERCISE_SEGMENT_TYPE_ROWING_MACHINE = 15;
+
+    /** Use this type for running. */
+    public static final int EXERCISE_SEGMENT_TYPE_RUNNING = 16;
+
+    /** Use this type for treadmill running. */
+    public static final int EXERCISE_SEGMENT_TYPE_RUNNING_TREADMILL = 17;
+
+    /** Use this type for stair climbing. */
+    public static final int EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING = 18;
+
+    /** Use this type for stair climbing machine. */
+    public static final int EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING_MACHINE = 19;
+
+    /** Use this type for stretching. */
+    public static final int EXERCISE_SEGMENT_TYPE_STRETCHING = 20;
+
+    /** Use this type for swimming in open water. */
+    public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_OPEN_WATER = 21;
+
+    /** Use this type for swimming in the pool. */
+    public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_POOL = 22;
+
+    /** Use this type for upper twists. */
+    public static final int EXERCISE_SEGMENT_TYPE_UPPER_TWIST = 23;
+
+    /** Use this type for walking. */
+    public static final int EXERCISE_SEGMENT_TYPE_WALKING = 24;
+
+    /** Use this type for wheelchair. */
+    public static final int EXERCISE_SEGMENT_TYPE_WHEELCHAIR = 25;
+
+    /** Use this type for arm curls. */
+    public static final int EXERCISE_SEGMENT_TYPE_ARM_CURL = 26;
+
+    /** Use this type for back extensions. */
+    public static final int EXERCISE_SEGMENT_TYPE_BACK_EXTENSION = 27;
+
+    /** Use this type for ball slams. */
+    public static final int EXERCISE_SEGMENT_TYPE_BALL_SLAM = 28;
+
+    /** Use this type for bench presses. */
+    public static final int EXERCISE_SEGMENT_TYPE_BENCH_PRESS = 29;
+
+    /** Use this type for burpees. */
+    public static final int EXERCISE_SEGMENT_TYPE_BURPEE = 30;
+
+    /** Use this type for crunches. */
+    public static final int EXERCISE_SEGMENT_TYPE_CRUNCH = 31;
+
+    /** Use this type for deadlifts. */
+    public static final int EXERCISE_SEGMENT_TYPE_DEADLIFT = 32;
+
+    /** Use this type for double arms triceps extensions. */
+    public static final int EXERCISE_SEGMENT_TYPE_DOUBLE_ARM_TRICEPS_EXTENSION = 33;
+
+    /** Use this type for dumbbells rows. */
+    public static final int EXERCISE_SEGMENT_TYPE_DUMBBELL_ROW = 34;
+
+    /** Use this type for front raises. */
+    public static final int EXERCISE_SEGMENT_TYPE_FRONT_RAISE = 35;
+
+    /** Use this type for hip thrusts. */
+    public static final int EXERCISE_SEGMENT_TYPE_HIP_THRUST = 36;
+
+    /** Use this type for hula-hoops. */
+    public static final int EXERCISE_SEGMENT_TYPE_HULA_HOOP = 37;
+
+    /** Use this type for jumping jacks. */
+    public static final int EXERCISE_SEGMENT_TYPE_JUMPING_JACK = 38;
+
+    /** Use this type for jump rope. */
+    public static final int EXERCISE_SEGMENT_TYPE_JUMP_ROPE = 39;
+
+    /** Use this type for kettlebell swings. */
+    public static final int EXERCISE_SEGMENT_TYPE_KETTLEBELL_SWING = 40;
+
+    /** Use this type for lateral raises. */
+    public static final int EXERCISE_SEGMENT_TYPE_LATERAL_RAISE = 41;
+
+    /** Use this type for lat pull-downs. */
+    public static final int EXERCISE_SEGMENT_TYPE_LAT_PULL_DOWN = 42;
+
+    /** Use this type for leg curls. */
+    public static final int EXERCISE_SEGMENT_TYPE_LEG_CURL = 43;
+
+    /** Use this type for leg extensions. */
+    public static final int EXERCISE_SEGMENT_TYPE_LEG_EXTENSION = 44;
+
+    /** Use this type for leg presses. */
+    public static final int EXERCISE_SEGMENT_TYPE_LEG_PRESS = 45;
+
+    /** Use this type for leg raises. */
+    public static final int EXERCISE_SEGMENT_TYPE_LEG_RAISE = 46;
+
+    /** Use this type for lunges. */
+    public static final int EXERCISE_SEGMENT_TYPE_LUNGE = 47;
+
+    /** Use this type for mountain climber. */
+    public static final int EXERCISE_SEGMENT_TYPE_MOUNTAIN_CLIMBER = 48;
+
+    /** Use this type for plank. */
+    public static final int EXERCISE_SEGMENT_TYPE_PLANK = 49;
+
+    /** Use this type for pull-ups. */
+    public static final int EXERCISE_SEGMENT_TYPE_PULL_UP = 50;
+
+    /** Use this type for punches. */
+    public static final int EXERCISE_SEGMENT_TYPE_PUNCH = 51;
+
+    /** Use this type for shoulder press. */
+    public static final int EXERCISE_SEGMENT_TYPE_SHOULDER_PRESS = 52;
+
+    /** Use this type for single arm triceps extension. */
+    public static final int EXERCISE_SEGMENT_TYPE_SINGLE_ARM_TRICEPS_EXTENSION = 53;
+
+    /** Use this type for sit-ups. */
+    public static final int EXERCISE_SEGMENT_TYPE_SIT_UP = 54;
+
+    /** Use this type for squats. */
+    public static final int EXERCISE_SEGMENT_TYPE_SQUAT = 55;
+
+    /** Use this type for freestyle swimming. */
+    public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_FREESTYLE = 56;
+
+    /** Use this type for backstroke swimming. */
+    public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_BACKSTROKE = 57;
+
+    /** Use this type for breaststroke swimming. */
+    public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_BREASTSTROKE = 58;
+
+    /** Use this type for butterfly swimming. */
+    public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_BUTTERFLY = 59;
+
+    /** Use this type for mixed swimming. */
+    public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_MIXED = 60;
+
+    /** Use this type if other swimming styles are not suitable. */
+    public static final int EXERCISE_SEGMENT_TYPE_SWIMMING_OTHER = 61;
+
+    /** Use this type for high intensity training. */
+    public static final int EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING = 62;
+
+    /** Use this type for weightlifting. */
+    public static final int EXERCISE_SEGMENT_TYPE_WEIGHTLIFTING = 63;
+
+    /** Use this type for other workout. */
+    public static final int EXERCISE_SEGMENT_TYPE_OTHER_WORKOUT = 64;
+
+    /** Use this type for yoga. */
+    public static final int EXERCISE_SEGMENT_TYPE_YOGA = 65;
+
+    /** Use this type for the rest. */
+    public static final int EXERCISE_SEGMENT_TYPE_REST = 66;
+
+    /** Use this type for the pause. */
+    public static final int EXERCISE_SEGMENT_TYPE_PAUSE = 67;
+
+    private ExerciseSegmentType() {}
+
+    /** @hide */
+    @IntDef({
+        EXERCISE_SEGMENT_TYPE_UNKNOWN,
+        EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS,
+        EXERCISE_SEGMENT_TYPE_BENCH_SIT_UP,
+        EXERCISE_SEGMENT_TYPE_BIKING,
+        EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY,
+        EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_LEFT_ARM,
+        EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_RIGHT_ARM,
+        EXERCISE_SEGMENT_TYPE_DUMBBELL_FRONT_RAISE,
+        EXERCISE_SEGMENT_TYPE_DUMBBELL_LATERAL_RAISE,
+        EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_LEFT_ARM,
+        EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_RIGHT_ARM,
+        EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_TWO_ARM,
+        EXERCISE_SEGMENT_TYPE_FORWARD_TWIST,
+        EXERCISE_SEGMENT_TYPE_ELLIPTICAL,
+        EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING,
+        EXERCISE_SEGMENT_TYPE_PILATES,
+        EXERCISE_SEGMENT_TYPE_ROWING_MACHINE,
+        EXERCISE_SEGMENT_TYPE_RUNNING,
+        EXERCISE_SEGMENT_TYPE_RUNNING_TREADMILL,
+        EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING,
+        EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING_MACHINE,
+        EXERCISE_SEGMENT_TYPE_STRETCHING,
+        EXERCISE_SEGMENT_TYPE_SWIMMING_OPEN_WATER,
+        EXERCISE_SEGMENT_TYPE_SWIMMING_POOL,
+        EXERCISE_SEGMENT_TYPE_UPPER_TWIST,
+        EXERCISE_SEGMENT_TYPE_WALKING,
+        EXERCISE_SEGMENT_TYPE_WEIGHTLIFTING,
+        EXERCISE_SEGMENT_TYPE_WHEELCHAIR,
+        EXERCISE_SEGMENT_TYPE_OTHER_WORKOUT,
+        EXERCISE_SEGMENT_TYPE_YOGA,
+        EXERCISE_SEGMENT_TYPE_ARM_CURL,
+        EXERCISE_SEGMENT_TYPE_BACK_EXTENSION,
+        EXERCISE_SEGMENT_TYPE_BALL_SLAM,
+        EXERCISE_SEGMENT_TYPE_BENCH_PRESS,
+        EXERCISE_SEGMENT_TYPE_BURPEE,
+        EXERCISE_SEGMENT_TYPE_CRUNCH,
+        EXERCISE_SEGMENT_TYPE_DEADLIFT,
+        EXERCISE_SEGMENT_TYPE_DOUBLE_ARM_TRICEPS_EXTENSION,
+        EXERCISE_SEGMENT_TYPE_DUMBBELL_ROW,
+        EXERCISE_SEGMENT_TYPE_FRONT_RAISE,
+        EXERCISE_SEGMENT_TYPE_HIP_THRUST,
+        EXERCISE_SEGMENT_TYPE_HULA_HOOP,
+        EXERCISE_SEGMENT_TYPE_JUMPING_JACK,
+        EXERCISE_SEGMENT_TYPE_JUMP_ROPE,
+        EXERCISE_SEGMENT_TYPE_KETTLEBELL_SWING,
+        EXERCISE_SEGMENT_TYPE_LATERAL_RAISE,
+        EXERCISE_SEGMENT_TYPE_LAT_PULL_DOWN,
+        EXERCISE_SEGMENT_TYPE_LEG_CURL,
+        EXERCISE_SEGMENT_TYPE_LEG_EXTENSION,
+        EXERCISE_SEGMENT_TYPE_LEG_PRESS,
+        EXERCISE_SEGMENT_TYPE_LEG_RAISE,
+        EXERCISE_SEGMENT_TYPE_LUNGE,
+        EXERCISE_SEGMENT_TYPE_MOUNTAIN_CLIMBER,
+        EXERCISE_SEGMENT_TYPE_PLANK,
+        EXERCISE_SEGMENT_TYPE_PULL_UP,
+        EXERCISE_SEGMENT_TYPE_PUNCH,
+        EXERCISE_SEGMENT_TYPE_SHOULDER_PRESS,
+        EXERCISE_SEGMENT_TYPE_SINGLE_ARM_TRICEPS_EXTENSION,
+        EXERCISE_SEGMENT_TYPE_SIT_UP,
+        EXERCISE_SEGMENT_TYPE_SQUAT,
+        EXERCISE_SEGMENT_TYPE_SWIMMING_FREESTYLE,
+        EXERCISE_SEGMENT_TYPE_SWIMMING_BACKSTROKE,
+        EXERCISE_SEGMENT_TYPE_SWIMMING_BREASTSTROKE,
+        EXERCISE_SEGMENT_TYPE_SWIMMING_BUTTERFLY,
+        EXERCISE_SEGMENT_TYPE_SWIMMING_MIXED,
+        EXERCISE_SEGMENT_TYPE_SWIMMING_OTHER,
+        EXERCISE_SEGMENT_TYPE_REST,
+        EXERCISE_SEGMENT_TYPE_PAUSE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ExerciseSegmentTypes {}
+}
diff --git a/framework/java/android/healthconnect/datatypes/ExerciseSessionRecord.java b/framework/java/android/healthconnect/datatypes/ExerciseSessionRecord.java
index b09f02a..b1ff9a1 100644
--- a/framework/java/android/healthconnect/datatypes/ExerciseSessionRecord.java
+++ b/framework/java/android/healthconnect/datatypes/ExerciseSessionRecord.java
@@ -21,14 +21,18 @@
 
 import java.time.Instant;
 import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
- * Captures any other exercise a user does for which there is not a custom record type. This can be
- * common fitness exercise like running or different sports.
+ * Captures exercise or a sequence of exercises. This can be a playing game like football or a
+ * sequence of fitness exercises.
  *
- * <p>Each record needs a start time and end time. Records don't need to be back-to-back or directly
- * after each other, there can be gaps in between.
+ * <p>Each record needs a start time, end time and session type. In addition, each record has two
+ * optional independent lists of time intervals: {@link ExerciseSegment} represents particular
+ * exercise within session, {@link ExerciseLap} represents a lap time within session.
  */
 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_EXERCISE_SESSION)
 public final class ExerciseSessionRecord extends IntervalRecord {
@@ -38,6 +42,9 @@
     private final CharSequence mTitle;
     private final ExerciseRoute mRoute;
 
+    private final List<ExerciseSegment> mSegments;
+    private final List<ExerciseLap> mLaps;
+
     /**
      * @param metadata Metadata to be associated with the record. See {@link Metadata}.
      * @param startTime Start time of this activity
@@ -58,12 +65,16 @@
             @Nullable CharSequence notes,
             @NonNull @ExerciseSessionType.ExerciseSessionTypes int exerciseType,
             @Nullable CharSequence title,
-            @Nullable ExerciseRoute route) {
+            @Nullable ExerciseRoute route,
+            @NonNull List<ExerciseSegment> segments,
+            @NonNull List<ExerciseLap> laps) {
         super(metadata, startTime, startZoneOffset, endTime, endZoneOffset);
         mNotes = notes;
         mExerciseType = exerciseType;
         mTitle = title;
         mRoute = route;
+        mSegments = Collections.unmodifiableList(segments);
+        mLaps = Collections.unmodifiableList(laps);
     }
 
     /** Returns exerciseType of this session. */
@@ -90,6 +101,23 @@
         return mRoute;
     }
 
+    /**
+     * Returns segments of this session. Returns empty list if the session doesn't have exercise
+     * segments.
+     */
+    @NonNull
+    public List<ExerciseSegment> getSegments() {
+        return mSegments;
+    }
+
+    /**
+     * Returns laps of this session. Returns empty list if the session doesn't have exercise laps.
+     */
+    @NonNull
+    public List<ExerciseLap> getLaps() {
+        return mLaps;
+    }
+
     /** Returns if this session has route. */
     @NonNull
     public boolean hasRoute() {
@@ -105,13 +133,19 @@
         return getExerciseType() == that.getExerciseType()
                 && RecordUtils.isEqualNullableCharSequences(getNotes(), that.getNotes())
                 && RecordUtils.isEqualNullableCharSequences(getTitle(), that.getTitle())
-                && Objects.equals(getRoute(), that.getRoute());
+                && Objects.equals(getRoute(), that.getRoute())
+                && Objects.equals(getSegments(), that.getSegments());
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(
-                super.hashCode(), getExerciseType(), getNotes(), getTitle(), getRoute());
+                super.hashCode(),
+                getExerciseType(),
+                getNotes(),
+                getTitle(),
+                getRoute(),
+                getSegments());
     }
 
     /** Builder class for {@link ExerciseSessionRecord} */
@@ -125,6 +159,8 @@
         private CharSequence mNotes;
         private CharSequence mTitle;
         private ExerciseRoute mRoute;
+        private List<ExerciseSegment> mSegments;
+        private List<ExerciseLap> mLaps;
 
         /**
          * @param metadata Metadata to be associated with the record. See {@link Metadata}.
@@ -147,6 +183,8 @@
             mStartZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(Instant.now());
             mEndZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(Instant.now());
             mExerciseType = exerciseType;
+            mSegments = new ArrayList<>();
+            mLaps = new ArrayList<>();
         }
 
         /** Sets the zone offset of the user when the session started */
@@ -200,6 +238,32 @@
             return this;
         }
 
+        /**
+         * Sets segments for this session.
+         *
+         * @param laps list of {@link ExerciseLap} of this session
+         */
+        @NonNull
+        public Builder setLaps(@NonNull List<ExerciseLap> laps) {
+            Objects.requireNonNull(laps);
+            mLaps.clear();
+            mLaps.addAll(laps);
+            return this;
+        }
+
+        /**
+         * Sets segments for this session.
+         *
+         * @param segments list of {@link ExerciseSegment} of this session
+         */
+        @NonNull
+        public Builder setSegments(@NonNull List<ExerciseSegment> segments) {
+            Objects.requireNonNull(segments);
+            mSegments.clear();
+            mSegments.addAll(segments);
+            return this;
+        }
+
         /** Returns {@link ExerciseSessionRecord} */
         @NonNull
         public ExerciseSessionRecord build() {
@@ -212,7 +276,9 @@
                     mNotes,
                     mExerciseType,
                     mTitle,
-                    mRoute);
+                    mRoute,
+                    mSegments,
+                    mLaps);
         }
     }
 }
diff --git a/framework/java/android/healthconnect/datatypes/ExerciseSessionType.java b/framework/java/android/healthconnect/datatypes/ExerciseSessionType.java
index 1b33981..7b7bd39 100644
--- a/framework/java/android/healthconnect/datatypes/ExerciseSessionType.java
+++ b/framework/java/android/healthconnect/datatypes/ExerciseSessionType.java
@@ -203,6 +203,15 @@
     /** Use this type for the other workout session. */
     public static final int EXERCISE_SESSION_TYPE_OTHER_WORKOUT = 58;
 
+    /** Use this type for the stair climbing machine session. */
+    public static final int EXERCISE_SESSION_TYPE_STAIR_CLIMBING_MACHINE = 59;
+
+    /** Use this type for elliptical workout. */
+    public static final int EXERCISE_SESSION_TYPE_ELLIPTICAL = 60;
+
+    /** Use this type for rowing machine. */
+    public static final int EXERCISE_SESSION_TYPE_ROWING_MACHINE = 61;
+
     private ExerciseSessionType() {}
 
     /** @hide */
@@ -218,6 +227,7 @@
         EXERCISE_SESSION_TYPE_CALISTHENICS,
         EXERCISE_SESSION_TYPE_CRICKET,
         EXERCISE_SESSION_TYPE_DANCING,
+        EXERCISE_SESSION_TYPE_ELLIPTICAL,
         EXERCISE_SESSION_TYPE_EXERCISE_CLASS,
         EXERCISE_SESSION_TYPE_FENCING,
         EXERCISE_SESSION_TYPE_FOOTBALL_AMERICAN,
@@ -239,6 +249,7 @@
         EXERCISE_SESSION_TYPE_ROCK_CLIMBING,
         EXERCISE_SESSION_TYPE_ROLLER_HOCKEY,
         EXERCISE_SESSION_TYPE_ROWING,
+        EXERCISE_SESSION_TYPE_ROWING_MACHINE,
         EXERCISE_SESSION_TYPE_RUGBY,
         EXERCISE_SESSION_TYPE_RUNNING,
         EXERCISE_SESSION_TYPE_RUNNING_TREADMILL,
@@ -252,6 +263,7 @@
         EXERCISE_SESSION_TYPE_SOFTBALL,
         EXERCISE_SESSION_TYPE_SQUASH,
         EXERCISE_SESSION_TYPE_STAIR_CLIMBING,
+        EXERCISE_SESSION_TYPE_STAIR_CLIMBING_MACHINE,
         EXERCISE_SESSION_TYPE_STRENGTH_TRAINING,
         EXERCISE_SESSION_TYPE_STRETCHING,
         EXERCISE_SESSION_TYPE_SURFING,
@@ -265,7 +277,7 @@
         EXERCISE_SESSION_TYPE_WEIGHTLIFTING,
         EXERCISE_SESSION_TYPE_WHEELCHAIR,
         EXERCISE_SESSION_TYPE_OTHER_WORKOUT,
-        EXERCISE_SESSION_TYPE_YOGA
+        EXERCISE_SESSION_TYPE_YOGA,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ExerciseSessionTypes {}
diff --git a/framework/java/android/healthconnect/datatypes/TimeInterval.java b/framework/java/android/healthconnect/datatypes/TimeInterval.java
new file mode 100644
index 0000000..d577316
--- /dev/null
+++ b/framework/java/android/healthconnect/datatypes/TimeInterval.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.healthconnect.datatypes;
+
+import android.annotation.NonNull;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * Represents time interval.
+ *
+ * <p>Each object has start time and end time. End time must be after start time.
+ *
+ * @hide
+ */
+public final class TimeInterval {
+    private final Instant mStartTime;
+    private final Instant mEndTime;
+
+    public TimeInterval(@NonNull Instant startTime, @NonNull Instant endTime) {
+        Objects.requireNonNull(startTime);
+        Objects.requireNonNull(endTime);
+        if (!endTime.isAfter(startTime)) {
+            throw new IllegalArgumentException("End time must be after start time.");
+        }
+        mStartTime = startTime;
+        mEndTime = endTime;
+    }
+
+    /*
+     * Returns start time of the interval.
+     */
+    @NonNull
+    public Instant getStartTime() {
+        return mStartTime;
+    }
+
+    /*
+     * Returns end time of the interval.
+     */
+    @NonNull
+    public Instant getEndTime() {
+        return mEndTime;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TimeInterval)) return false;
+        TimeInterval that = (TimeInterval) o;
+        return getStartTime().equals(that.getStartTime()) && getEndTime().equals(that.getEndTime());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getStartTime(), getEndTime());
+    }
+}
diff --git a/tests/cts/src/android/healthconnect/cts/ExerciseLapTest.java b/tests/cts/src/android/healthconnect/cts/ExerciseLapTest.java
new file mode 100644
index 0000000..b8ac2b4
--- /dev/null
+++ b/tests/cts/src/android/healthconnect/cts/ExerciseLapTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.healthconnect.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.healthconnect.datatypes.ExerciseLap;
+import android.healthconnect.datatypes.units.Length;
+
+import org.junit.Test;
+
+import java.time.Instant;
+
+public class ExerciseLapTest {
+    private static final Instant START_TIME = Instant.ofEpochMilli((long) 1e1);
+    private static final Instant END_TIME = Instant.ofEpochMilli((long) 1e2);
+
+    @Test
+    public void testExerciseLap_buildLap_buildCorrectObject() {
+        ExerciseLap lap =
+                new ExerciseLap.Builder(START_TIME, END_TIME)
+                        .setLength(Length.fromMeters(12))
+                        .build();
+        assertThat(lap.getStartTime()).isEqualTo(START_TIME);
+        assertThat(lap.getEndTime()).isEqualTo(END_TIME);
+        assertThat(lap.getLength()).isEqualTo(Length.fromMeters(12));
+    }
+
+    @Test
+    public void testExerciseLap_buildWithoutLength_lengthIsNull() {
+        ExerciseLap lap = new ExerciseLap.Builder(START_TIME, END_TIME).build();
+        assertThat(lap.getLength()).isNull();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testExerciseLap_endTimeEarlierThanStartTime_throwsException() {
+        new ExerciseLap.Builder(END_TIME, START_TIME).build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testExerciseLap_lengthIsNegative_throwsException() {
+        new ExerciseLap.Builder(START_TIME, END_TIME).setLength(Length.fromMeters(-10)).build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testExerciseLap_lengthIsHuge_throwsException() {
+        new ExerciseLap.Builder(START_TIME, END_TIME).setLength(Length.fromMeters(1e10)).build();
+    }
+}
diff --git a/tests/cts/src/android/healthconnect/cts/ExerciseSegmentTest.java b/tests/cts/src/android/healthconnect/cts/ExerciseSegmentTest.java
new file mode 100644
index 0000000..52ce6e5
--- /dev/null
+++ b/tests/cts/src/android/healthconnect/cts/ExerciseSegmentTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.healthconnect.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.healthconnect.datatypes.ExerciseSegment;
+import android.healthconnect.datatypes.ExerciseSegmentType;
+
+import org.junit.Test;
+
+import java.time.Instant;
+
+public class ExerciseSegmentTest {
+    private static final Instant START_TIME = Instant.ofEpochMilli((long) 1e1);
+    private static final Instant END_TIME = Instant.ofEpochMilli((long) 1e2);
+
+    @Test
+    public void testExerciseSegment_buildSegment_buildCorrectObject() {
+        ExerciseSegment segment =
+                new ExerciseSegment.Builder(
+                                START_TIME,
+                                END_TIME,
+                                ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_ARM_CURL)
+                        .setRepetitionsCount(10)
+                        .build();
+        assertThat(segment.getStartTime()).isEqualTo(START_TIME);
+        assertThat(segment.getEndTime()).isEqualTo(END_TIME);
+        assertThat(segment.getSegmentType())
+                .isEqualTo(ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_ARM_CURL);
+        assertThat(segment.getRepetitionsCount()).isEqualTo(10);
+    }
+
+    @Test
+    public void testExerciseSegment_buildWithoutRepetitions_repetitionsIsZero() {
+        ExerciseSegment segment =
+                new ExerciseSegment.Builder(
+                                START_TIME,
+                                END_TIME,
+                                ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_ARM_CURL)
+                        .build();
+        assertThat(segment.getRepetitionsCount()).isEqualTo(0);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testExerciseSegment_endTimeEarlierThanStartTime_throwsException() {
+        new ExerciseSegment.Builder(
+                        END_TIME, START_TIME, ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_ARM_CURL)
+                .build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testExerciseSegment_repetitionsIsNegative_throwsException() {
+        new ExerciseSegment.Builder(
+                        START_TIME, END_TIME, ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_ARM_CURL)
+                .setRepetitionsCount(-1)
+                .build();
+    }
+}
diff --git a/tests/cts/src/android/healthconnect/cts/ExerciseSessionRecordTest.java b/tests/cts/src/android/healthconnect/cts/ExerciseSessionRecordTest.java
index 7fea8ec..81c7a98 100644
--- a/tests/cts/src/android/healthconnect/cts/ExerciseSessionRecordTest.java
+++ b/tests/cts/src/android/healthconnect/cts/ExerciseSessionRecordTest.java
@@ -18,10 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.healthconnect.datatypes.ExerciseLap;
 import android.healthconnect.datatypes.ExerciseRoute;
+import android.healthconnect.datatypes.ExerciseSegment;
+import android.healthconnect.datatypes.ExerciseSegmentType;
 import android.healthconnect.datatypes.ExerciseSessionRecord;
 import android.healthconnect.datatypes.ExerciseSessionType;
 import android.healthconnect.datatypes.Metadata;
+import android.healthconnect.datatypes.units.Length;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -30,6 +34,7 @@
 
 import java.time.Instant;
 import java.time.ZoneOffset;
+import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
 public class ExerciseSessionRecordTest {
@@ -51,6 +56,8 @@
         assertThat(record.getRoute()).isNull();
         assertThat(record.getNotes()).isNull();
         assertThat(record.getTitle()).isNull();
+        assertThat(record.getSegments()).isEmpty();
+        assertThat(record.getLaps()).isEmpty();
     }
 
     @Test
@@ -78,6 +85,20 @@
         ExerciseRoute route = TestUtils.buildExerciseRoute();
         CharSequence notes = "rain";
         CharSequence title = "Morning training";
+        List<ExerciseSegment> segmentList =
+                List.of(
+                        new ExerciseSegment.Builder(
+                                        START_TIME,
+                                        END_TIME,
+                                        ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_ARM_CURL)
+                                .setRepetitionsCount(10)
+                                .build());
+
+        List<ExerciseLap> lapsList =
+                List.of(
+                        new ExerciseLap.Builder(START_TIME, END_TIME)
+                                .setLength(Length.fromMeters(10))
+                                .build());
         ExerciseSessionRecord record =
                 new ExerciseSessionRecord.Builder(
                                 TestUtils.generateMetadata(),
@@ -89,6 +110,8 @@
                         .setStartZoneOffset(ZoneOffset.MIN)
                         .setNotes(notes)
                         .setTitle(title)
+                        .setSegments(segmentList)
+                        .setLaps(lapsList)
                         .build();
         assertThat(record.hasRoute()).isTrue();
         assertThat(record.getRoute()).isEqualTo(route);
@@ -97,6 +120,8 @@
         assertThat(record.getExerciseType())
                 .isEqualTo(ExerciseSessionType.EXERCISE_SESSION_TYPE_FOOTBALL_AMERICAN);
         assertThat(record.getNotes()).isEqualTo(notes);
+        assertThat(record.getSegments()).isEqualTo(segmentList);
+        assertThat(record.getLaps()).isEqualTo(lapsList);
         assertThat(record.getTitle()).isEqualTo(title);
     }
 }
diff --git a/tests/unittests/src/android/healthconnect/ExerciseSegmentTypesTest.java b/tests/unittests/src/android/healthconnect/ExerciseSegmentTypesTest.java
new file mode 100644
index 0000000..c06405c
--- /dev/null
+++ b/tests/unittests/src/android/healthconnect/ExerciseSegmentTypesTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.healthconnect;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.healthconnect.datatypes.ExerciseSegmentType;
+import android.util.ArraySet;
+
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class ExerciseSegmentTypesTest {
+
+    @Test
+    public void testExerciseSegmentTypeStrings_allValuesAreUnique() throws IllegalAccessException {
+        List<Integer> types = getAllDeclaredExerciseSegmentTypes();
+        Set<Integer> uniqueTypes = new ArraySet<>(types);
+        assertWithMessage("All values of segment types must be unique.")
+                .that(types.size())
+                .isEqualTo(uniqueTypes.size());
+    }
+
+    private List<Integer> getAllDeclaredExerciseSegmentTypes() throws IllegalAccessException {
+        List<Integer> types = new ArrayList<>();
+        for (Field field : ExerciseSegmentType.class.getDeclaredFields()) {
+            if (field.getType().equals(int.class) && Modifier.isPublic(field.getModifiers())) {
+                types.add((Integer) field.get(ExerciseSegmentType.class));
+            }
+        }
+        return types;
+    }
+}