• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.healthconnect.backuprestore;
18 
19 import static android.health.connect.datatypes.units.Temperature.fromCelsius;
20 
21 import static java.util.stream.Collectors.toSet;
22 
23 import android.annotation.SuppressLint;
24 import android.health.connect.datatypes.RecordTypeIdentifier;
25 import android.health.connect.datatypes.units.Energy;
26 import android.health.connect.datatypes.units.Length;
27 import android.health.connect.datatypes.units.Mass;
28 import android.health.connect.datatypes.units.Power;
29 import android.health.connect.datatypes.units.Velocity;
30 import android.health.connect.internal.datatypes.ActiveCaloriesBurnedRecordInternal;
31 import android.health.connect.internal.datatypes.ActivityIntensityRecordInternal;
32 import android.health.connect.internal.datatypes.BasalBodyTemperatureRecordInternal;
33 import android.health.connect.internal.datatypes.BasalMetabolicRateRecordInternal;
34 import android.health.connect.internal.datatypes.BloodGlucoseRecordInternal;
35 import android.health.connect.internal.datatypes.BloodPressureRecordInternal;
36 import android.health.connect.internal.datatypes.BodyFatRecordInternal;
37 import android.health.connect.internal.datatypes.BodyTemperatureRecordInternal;
38 import android.health.connect.internal.datatypes.BodyWaterMassRecordInternal;
39 import android.health.connect.internal.datatypes.BoneMassRecordInternal;
40 import android.health.connect.internal.datatypes.CervicalMucusRecordInternal;
41 import android.health.connect.internal.datatypes.CyclingPedalingCadenceRecordInternal;
42 import android.health.connect.internal.datatypes.DistanceRecordInternal;
43 import android.health.connect.internal.datatypes.ElevationGainedRecordInternal;
44 import android.health.connect.internal.datatypes.ExerciseCompletionGoalInternal;
45 import android.health.connect.internal.datatypes.ExerciseLapInternal;
46 import android.health.connect.internal.datatypes.ExercisePerformanceGoalInternal;
47 import android.health.connect.internal.datatypes.ExerciseRouteInternal;
48 import android.health.connect.internal.datatypes.ExerciseRouteInternal.LocationInternal;
49 import android.health.connect.internal.datatypes.ExerciseSegmentInternal;
50 import android.health.connect.internal.datatypes.ExerciseSessionRecordInternal;
51 import android.health.connect.internal.datatypes.FloorsClimbedRecordInternal;
52 import android.health.connect.internal.datatypes.HeartRateRecordInternal;
53 import android.health.connect.internal.datatypes.HeartRateVariabilityRmssdRecordInternal;
54 import android.health.connect.internal.datatypes.HeightRecordInternal;
55 import android.health.connect.internal.datatypes.HydrationRecordInternal;
56 import android.health.connect.internal.datatypes.InstantRecordInternal;
57 import android.health.connect.internal.datatypes.IntermenstrualBleedingRecordInternal;
58 import android.health.connect.internal.datatypes.IntervalRecordInternal;
59 import android.health.connect.internal.datatypes.LeanBodyMassRecordInternal;
60 import android.health.connect.internal.datatypes.MenstruationFlowRecordInternal;
61 import android.health.connect.internal.datatypes.MenstruationPeriodRecordInternal;
62 import android.health.connect.internal.datatypes.MindfulnessSessionRecordInternal;
63 import android.health.connect.internal.datatypes.NutritionRecordInternal;
64 import android.health.connect.internal.datatypes.OvulationTestRecordInternal;
65 import android.health.connect.internal.datatypes.OxygenSaturationRecordInternal;
66 import android.health.connect.internal.datatypes.PlannedExerciseBlockInternal;
67 import android.health.connect.internal.datatypes.PlannedExerciseSessionRecordInternal;
68 import android.health.connect.internal.datatypes.PlannedExerciseStepInternal;
69 import android.health.connect.internal.datatypes.PowerRecordInternal;
70 import android.health.connect.internal.datatypes.RecordInternal;
71 import android.health.connect.internal.datatypes.RespiratoryRateRecordInternal;
72 import android.health.connect.internal.datatypes.RestingHeartRateRecordInternal;
73 import android.health.connect.internal.datatypes.SexualActivityRecordInternal;
74 import android.health.connect.internal.datatypes.SkinTemperatureRecordInternal;
75 import android.health.connect.internal.datatypes.SleepSessionRecordInternal;
76 import android.health.connect.internal.datatypes.SleepStageInternal;
77 import android.health.connect.internal.datatypes.SpeedRecordInternal;
78 import android.health.connect.internal.datatypes.StepsCadenceRecordInternal;
79 import android.health.connect.internal.datatypes.StepsRecordInternal;
80 import android.health.connect.internal.datatypes.TotalCaloriesBurnedRecordInternal;
81 import android.health.connect.internal.datatypes.Vo2MaxRecordInternal;
82 import android.health.connect.internal.datatypes.WeightRecordInternal;
83 import android.health.connect.internal.datatypes.WheelchairPushesRecordInternal;
84 import android.health.connect.internal.datatypes.utils.HealthConnectMappings;
85 
86 import com.android.server.healthconnect.proto.backuprestore.ActiveCaloriesBurned;
87 import com.android.server.healthconnect.proto.backuprestore.ActivityIntensity;
88 import com.android.server.healthconnect.proto.backuprestore.BasalBodyTemperature;
89 import com.android.server.healthconnect.proto.backuprestore.BasalMetabolicRate;
90 import com.android.server.healthconnect.proto.backuprestore.BloodGlucose;
91 import com.android.server.healthconnect.proto.backuprestore.BloodPressure;
92 import com.android.server.healthconnect.proto.backuprestore.BodyFat;
93 import com.android.server.healthconnect.proto.backuprestore.BodyTemperature;
94 import com.android.server.healthconnect.proto.backuprestore.BodyWaterMass;
95 import com.android.server.healthconnect.proto.backuprestore.BoneMass;
96 import com.android.server.healthconnect.proto.backuprestore.CervicalMucus;
97 import com.android.server.healthconnect.proto.backuprestore.CyclingPedalingCadence;
98 import com.android.server.healthconnect.proto.backuprestore.CyclingPedalingCadence.CyclingPedalingCadenceSample;
99 import com.android.server.healthconnect.proto.backuprestore.Distance;
100 import com.android.server.healthconnect.proto.backuprestore.ElevationGained;
101 import com.android.server.healthconnect.proto.backuprestore.ExerciseSession;
102 import com.android.server.healthconnect.proto.backuprestore.ExerciseSession.ExerciseLap;
103 import com.android.server.healthconnect.proto.backuprestore.ExerciseSession.ExerciseRoute;
104 import com.android.server.healthconnect.proto.backuprestore.ExerciseSession.ExerciseRoute.Location;
105 import com.android.server.healthconnect.proto.backuprestore.ExerciseSession.ExerciseSegment;
106 import com.android.server.healthconnect.proto.backuprestore.FloorsClimbed;
107 import com.android.server.healthconnect.proto.backuprestore.HeartRate;
108 import com.android.server.healthconnect.proto.backuprestore.HeartRateVariabilityRmssd;
109 import com.android.server.healthconnect.proto.backuprestore.Height;
110 import com.android.server.healthconnect.proto.backuprestore.Hydration;
111 import com.android.server.healthconnect.proto.backuprestore.InstantRecord;
112 import com.android.server.healthconnect.proto.backuprestore.IntermenstrualBleeding;
113 import com.android.server.healthconnect.proto.backuprestore.IntervalRecord;
114 import com.android.server.healthconnect.proto.backuprestore.LeanBodyMass;
115 import com.android.server.healthconnect.proto.backuprestore.MenstruationFlow;
116 import com.android.server.healthconnect.proto.backuprestore.MenstruationPeriod;
117 import com.android.server.healthconnect.proto.backuprestore.MindfulnessSession;
118 import com.android.server.healthconnect.proto.backuprestore.Nutrition;
119 import com.android.server.healthconnect.proto.backuprestore.OvulationTest;
120 import com.android.server.healthconnect.proto.backuprestore.OxygenSaturation;
121 import com.android.server.healthconnect.proto.backuprestore.PlannedExerciseSession;
122 import com.android.server.healthconnect.proto.backuprestore.PlannedExerciseSession.PlannedExerciseBlock;
123 import com.android.server.healthconnect.proto.backuprestore.PlannedExerciseSession.PlannedExerciseBlock.PlannedExerciseStep;
124 import com.android.server.healthconnect.proto.backuprestore.PlannedExerciseSession.PlannedExerciseBlock.PlannedExerciseStep.ExerciseCompletionGoal;
125 import com.android.server.healthconnect.proto.backuprestore.PlannedExerciseSession.PlannedExerciseBlock.PlannedExerciseStep.ExercisePerformanceGoal;
126 import com.android.server.healthconnect.proto.backuprestore.Record;
127 import com.android.server.healthconnect.proto.backuprestore.RespiratoryRate;
128 import com.android.server.healthconnect.proto.backuprestore.RestingHeartRate;
129 import com.android.server.healthconnect.proto.backuprestore.SexualActivity;
130 import com.android.server.healthconnect.proto.backuprestore.SkinTemperature;
131 import com.android.server.healthconnect.proto.backuprestore.SleepSession;
132 import com.android.server.healthconnect.proto.backuprestore.SleepSession.SleepStage;
133 import com.android.server.healthconnect.proto.backuprestore.Speed;
134 import com.android.server.healthconnect.proto.backuprestore.Speed.SpeedSample;
135 import com.android.server.healthconnect.proto.backuprestore.Steps;
136 import com.android.server.healthconnect.proto.backuprestore.StepsCadence;
137 import com.android.server.healthconnect.proto.backuprestore.TotalCaloriesBurned;
138 import com.android.server.healthconnect.proto.backuprestore.Vo2Max;
139 import com.android.server.healthconnect.proto.backuprestore.Weight;
140 import com.android.server.healthconnect.proto.backuprestore.WheelchairPushes;
141 
142 import java.lang.reflect.InvocationTargetException;
143 import java.time.Duration;
144 import java.util.List;
145 import java.util.Map;
146 import java.util.Objects;
147 import java.util.UUID;
148 
149 /**
150  * A helper class used to create {@link RecordInternal} objects using its proto.
151  *
152  * @hide
153  */
154 public final class RecordProtoConverter {
155 
156     public static final int PROTO_VERSION = 1;
157 
158     private final Map<Integer, Class<? extends RecordInternal<?>>> mDataTypeClassMap =
159             HealthConnectMappings.getInstance().getRecordIdToInternalRecordClassMap();
160 
161     /** Creates a {@link RecordInternal} from the {@link Record} */
toRecordInternal(Record recordProto)162     public RecordInternal<?> toRecordInternal(Record recordProto)
163             throws NoSuchMethodException,
164                     InvocationTargetException,
165                     InstantiationException,
166                     IllegalAccessException {
167         int recordTypeId = getRecordTypeId(recordProto);
168         Class<? extends RecordInternal<?>> recordClass = mDataTypeClassMap.get(recordTypeId);
169         Objects.requireNonNull(recordClass);
170         RecordInternal<?> recordInternal = recordClass.getConstructor().newInstance();
171         populateRecordInternal(recordProto, recordInternal);
172         return recordInternal;
173     }
174 
175     /** Creates a {@link Record} from the {@link RecordInternal} */
toRecordProto(RecordInternal<?> recordInternal)176     public Record toRecordProto(RecordInternal<?> recordInternal) {
177         Record.Builder builder =
178                 Record.newBuilder()
179                         .setUuid(
180                                 recordInternal.getUuid() == null
181                                         ? ""
182                                         : recordInternal.getUuid().toString())
183                         .setLastModifiedTime(recordInternal.getLastModifiedTime())
184                         .setClientRecordVersion(recordInternal.getClientRecordVersion())
185                         .setDeviceType(recordInternal.getDeviceType())
186                         .setRecordingMethod(recordInternal.getRecordingMethod());
187         if (recordInternal.getPackageName() != null) {
188             builder.setPackageName(recordInternal.getPackageName());
189         }
190         if (recordInternal.getClientRecordId() != null) {
191             builder.setClientRecordId(recordInternal.getClientRecordId());
192         }
193         if (recordInternal.getManufacturer() != null) {
194             builder.setManufacturer(recordInternal.getManufacturer());
195         }
196         if (recordInternal.getModel() != null) {
197             builder.setModel(recordInternal.getModel());
198         }
199 
200         if (recordInternal instanceof IntervalRecordInternal<?> intervalRecordInternal) {
201             builder.setIntervalRecord(toIntervalRecordProto(intervalRecordInternal));
202         } else if (recordInternal instanceof InstantRecordInternal<?> instantRecordInternal) {
203             builder.setInstantRecord(toInstantRecordProto(instantRecordInternal));
204         } else {
205             throw new IllegalArgumentException(
206                     "Unknown record type " + recordInternal.getClass().getSimpleName());
207         }
208 
209         return builder.build();
210     }
211 
toIntervalRecordProto(IntervalRecordInternal<?> intervalRecordInternal)212     private IntervalRecord toIntervalRecordProto(IntervalRecordInternal<?> intervalRecordInternal) {
213         IntervalRecord.Builder builder =
214                 IntervalRecord.newBuilder()
215                         .setStartTime(intervalRecordInternal.getStartTimeInMillis())
216                         .setStartZoneOffset(intervalRecordInternal.getStartZoneOffsetInSeconds())
217                         .setEndTime(intervalRecordInternal.getEndTimeInMillis())
218                         .setEndZoneOffset(intervalRecordInternal.getEndZoneOffsetInSeconds());
219 
220         if (intervalRecordInternal
221                 instanceof ActiveCaloriesBurnedRecordInternal activeCaloriesBurnedRecordInternal) {
222             builder.setActiveCaloriesBurned(
223                     toActiveCaloriesBurnedProto(activeCaloriesBurnedRecordInternal));
224         } else if (intervalRecordInternal
225                 instanceof ActivityIntensityRecordInternal activityIntensityRecordInternal) {
226             builder.setActivityIntensity(toActivityIntensityProto(activityIntensityRecordInternal));
227         } else if (intervalRecordInternal
228                 instanceof
229                 CyclingPedalingCadenceRecordInternal cyclingPedalingCadenceRecordInternal) {
230             builder.setCyclingPedalingCadence(
231                     toCyclingPedalingCadenceProto(cyclingPedalingCadenceRecordInternal));
232         } else if (intervalRecordInternal
233                 instanceof DistanceRecordInternal distanceRecordInternal) {
234             builder.setDistance(toDistanceProto(distanceRecordInternal));
235         } else if (intervalRecordInternal
236                 instanceof ElevationGainedRecordInternal elevationGainedRecordInternal) {
237             builder.setElevationGained(toElevationGainedProto(elevationGainedRecordInternal));
238         } else if (intervalRecordInternal
239                 instanceof ExerciseSessionRecordInternal exerciseSessionRecordInternal) {
240             builder.setExerciseSession(toExerciseSessionProto(exerciseSessionRecordInternal));
241         } else if (intervalRecordInternal
242                 instanceof FloorsClimbedRecordInternal floorsClimbedRecordInternal) {
243             builder.setFloorsClimbed(toFloorsClimbedProto(floorsClimbedRecordInternal));
244         } else if (intervalRecordInternal
245                 instanceof HeartRateRecordInternal heartRateRecordInternal) {
246             builder.setHeartRate(toHeartRateProto(heartRateRecordInternal));
247         } else if (intervalRecordInternal
248                 instanceof HydrationRecordInternal hydrationRecordInternal) {
249             builder.setHydration(toHydrationProto(hydrationRecordInternal));
250         } else if (intervalRecordInternal instanceof MenstruationPeriodRecordInternal) {
251             builder.setMenstruationPeriod(MenstruationPeriod.getDefaultInstance());
252         } else if (intervalRecordInternal
253                 instanceof MindfulnessSessionRecordInternal mindfulnessSessionRecordInternal) {
254             builder.setMindfulnessSession(
255                     toMindfulnessSessionProto(mindfulnessSessionRecordInternal));
256         } else if (intervalRecordInternal
257                 instanceof NutritionRecordInternal nutritionRecordInternal) {
258             builder.setNutrition(toNutritionProto(nutritionRecordInternal));
259         } else if (intervalRecordInternal
260                 instanceof
261                 PlannedExerciseSessionRecordInternal plannedExerciseSessionRecordInternal) {
262             builder.setPlannedExerciseSession(
263                     toPlannedExerciseSessionProto(plannedExerciseSessionRecordInternal));
264         } else if (intervalRecordInternal instanceof PowerRecordInternal powerRecordInternal) {
265             builder.setPower(toPowerProto(powerRecordInternal));
266         } else if (intervalRecordInternal
267                 instanceof SkinTemperatureRecordInternal skinTemperatureRecordInternal) {
268             builder.setSkinTemperature(toSkinTemperatureProto(skinTemperatureRecordInternal));
269         } else if (intervalRecordInternal
270                 instanceof SleepSessionRecordInternal sleepSessionRecordInternal) {
271             builder.setSleepSession(toSleepSessionProto(sleepSessionRecordInternal));
272         } else if (intervalRecordInternal instanceof SpeedRecordInternal speedRecordInternal) {
273             builder.setSpeed(toSpeedProto(speedRecordInternal));
274         } else if (intervalRecordInternal instanceof StepsRecordInternal stepsRecordInternal) {
275             builder.setSteps(toStepsProto(stepsRecordInternal));
276         } else if (intervalRecordInternal
277                 instanceof StepsCadenceRecordInternal stepsCadenceRecordInternal) {
278             builder.setStepsCadence(toStepsCadenceProto(stepsCadenceRecordInternal));
279         } else if (intervalRecordInternal
280                 instanceof TotalCaloriesBurnedRecordInternal totalCaloriesBurnedRecordInternal) {
281             builder.setTotalCaloriesBurned(
282                     toTotalCaloriesBurnedProto(totalCaloriesBurnedRecordInternal));
283         } else if (intervalRecordInternal
284                 instanceof WheelchairPushesRecordInternal wheelchairPushesRecordInternal) {
285             builder.setWheelchairPushes(toWheelchairPushesProto(wheelchairPushesRecordInternal));
286         } else {
287             throw new IllegalArgumentException(
288                     "Unknown interval record type "
289                             + intervalRecordInternal.getClass().getSimpleName());
290         }
291 
292         return builder.build();
293     }
294 
toActiveCaloriesBurnedProto( ActiveCaloriesBurnedRecordInternal activeCaloriesBurnedRecordInternal)295     private static ActiveCaloriesBurned toActiveCaloriesBurnedProto(
296             ActiveCaloriesBurnedRecordInternal activeCaloriesBurnedRecordInternal) {
297         return ActiveCaloriesBurned.newBuilder()
298                 .setEnergy(activeCaloriesBurnedRecordInternal.getEnergy())
299                 .build();
300     }
301 
toActivityIntensityProto( ActivityIntensityRecordInternal activityIntensityRecordInternal)302     private static ActivityIntensity toActivityIntensityProto(
303             ActivityIntensityRecordInternal activityIntensityRecordInternal) {
304         return ActivityIntensity.newBuilder()
305                 .setActivityIntensityType(
306                         activityIntensityRecordInternal.getActivityIntensityType())
307                 .build();
308     }
309 
toCyclingPedalingCadenceProto( CyclingPedalingCadenceRecordInternal cyclingPedalingCadenceRecordInternal)310     private static CyclingPedalingCadence toCyclingPedalingCadenceProto(
311             CyclingPedalingCadenceRecordInternal cyclingPedalingCadenceRecordInternal) {
312         List<CyclingPedalingCadenceSample> samples =
313                 cyclingPedalingCadenceRecordInternal.getSamples().stream()
314                         .map(
315                                 sample ->
316                                         CyclingPedalingCadenceSample.newBuilder()
317                                                 .setRevolutionsPerMinute(
318                                                         sample.getRevolutionsPerMinute())
319                                                 .setEpochMillis(sample.getEpochMillis())
320                                                 .build())
321                         .toList();
322 
323         return CyclingPedalingCadence.newBuilder().addAllSample(samples).build();
324     }
325 
toDistanceProto(DistanceRecordInternal distanceRecordInternal)326     private static Distance toDistanceProto(DistanceRecordInternal distanceRecordInternal) {
327         return Distance.newBuilder().setDistance(distanceRecordInternal.getDistance()).build();
328     }
329 
toElevationGainedProto( ElevationGainedRecordInternal elevationGainedRecordInternal)330     private static ElevationGained toElevationGainedProto(
331             ElevationGainedRecordInternal elevationGainedRecordInternal) {
332         return ElevationGained.newBuilder()
333                 .setElevation(elevationGainedRecordInternal.getElevation())
334                 .build();
335     }
336 
toExerciseSessionProto( ExerciseSessionRecordInternal exerciseSessionRecordInternal)337     private static ExerciseSession toExerciseSessionProto(
338             ExerciseSessionRecordInternal exerciseSessionRecordInternal) {
339         ExerciseSession.Builder builder =
340                 ExerciseSession.newBuilder()
341                         .setExerciseType(exerciseSessionRecordInternal.getExerciseType())
342                         .setHasRoute(exerciseSessionRecordInternal.hasRoute());
343 
344         if (exerciseSessionRecordInternal.getNotes() != null) {
345             builder.setNotes(exerciseSessionRecordInternal.getNotes());
346         }
347         if (exerciseSessionRecordInternal.getTitle() != null) {
348             builder.setTitle(exerciseSessionRecordInternal.getTitle());
349         }
350         if (exerciseSessionRecordInternal.getRoute() != null) {
351             List<Location> routeLocations =
352                     exerciseSessionRecordInternal.getRoute().getRouteLocations().stream()
353                             .map(RecordProtoConverter::toLocationProto)
354                             .toList();
355             builder.setRoute(ExerciseRoute.newBuilder().addAllRouteLocation(routeLocations));
356         }
357         if (exerciseSessionRecordInternal.getLaps() != null) {
358             builder.addAllLap(
359                     exerciseSessionRecordInternal.getLaps().stream()
360                             .map(RecordProtoConverter::toLapProto)
361                             .toList());
362         }
363         if (exerciseSessionRecordInternal.getSegments() != null) {
364             builder.addAllSegment(
365                     exerciseSessionRecordInternal.getSegments().stream()
366                             .map(RecordProtoConverter::toSegmentProto)
367                             .toList());
368         }
369         if (exerciseSessionRecordInternal.getPlannedExerciseSessionId() != null) {
370             builder.setPlannedExerciseSessionId(
371                     exerciseSessionRecordInternal.getPlannedExerciseSessionId().toString());
372         }
373 
374         return builder.build();
375     }
376 
toLocationProto(LocationInternal locationInternal)377     private static Location toLocationProto(LocationInternal locationInternal) {
378         return Location.newBuilder()
379                 .setTime(locationInternal.getTime())
380                 .setLatitude(locationInternal.getLatitude())
381                 .setLongitude(locationInternal.getLongitude())
382                 .setHorizontalAccuracy(locationInternal.getHorizontalAccuracy())
383                 .setVerticalAccuracy(locationInternal.getVerticalAccuracy())
384                 .setAltitude(locationInternal.getAltitude())
385                 .build();
386     }
387 
toLapProto(ExerciseLapInternal lapInternal)388     private static ExerciseLap toLapProto(ExerciseLapInternal lapInternal) {
389         return ExerciseLap.newBuilder()
390                 .setStartTime(lapInternal.getStartTime())
391                 .setEndTime(lapInternal.getEndTime())
392                 .setLength(lapInternal.getLength())
393                 .build();
394     }
395 
toSegmentProto(ExerciseSegmentInternal segmentInternal)396     private static ExerciseSegment toSegmentProto(ExerciseSegmentInternal segmentInternal) {
397         return ExerciseSegment.newBuilder()
398                 .setStartTime(segmentInternal.getStartTime())
399                 .setEndTime(segmentInternal.getEndTime())
400                 .setSegmentType(segmentInternal.getSegmentType())
401                 .setRepetitionsCount(segmentInternal.getRepetitionsCount())
402                 .build();
403     }
404 
toFloorsClimbedProto( FloorsClimbedRecordInternal floorsClimbedRecordInternal)405     private static FloorsClimbed toFloorsClimbedProto(
406             FloorsClimbedRecordInternal floorsClimbedRecordInternal) {
407         return FloorsClimbed.newBuilder()
408                 .setFloors(floorsClimbedRecordInternal.getFloors())
409                 .build();
410     }
411 
toHeartRateProto(HeartRateRecordInternal heartRateRecordInternal)412     private static HeartRate toHeartRateProto(HeartRateRecordInternal heartRateRecordInternal) {
413         List<HeartRate.HeartRateSample> samples =
414                 heartRateRecordInternal.getSamples().stream()
415                         .map(
416                                 sample ->
417                                         HeartRate.HeartRateSample.newBuilder()
418                                                 .setBeatsPerMinute(sample.getBeatsPerMinute())
419                                                 .setEpochMillis(sample.getEpochMillis())
420                                                 .build())
421                         .toList();
422 
423         return HeartRate.newBuilder().addAllSample(samples).build();
424     }
425 
toHydrationProto(HydrationRecordInternal hydrationRecordInternal)426     private static Hydration toHydrationProto(HydrationRecordInternal hydrationRecordInternal) {
427         return Hydration.newBuilder().setVolume(hydrationRecordInternal.getVolume()).build();
428     }
429 
toMindfulnessSessionProto( MindfulnessSessionRecordInternal mindfulnessSessionRecordInternal)430     private static MindfulnessSession toMindfulnessSessionProto(
431             MindfulnessSessionRecordInternal mindfulnessSessionRecordInternal) {
432         MindfulnessSession.Builder builder =
433                 MindfulnessSession.newBuilder()
434                         .setMindfulnessSessionType(
435                                 mindfulnessSessionRecordInternal.getMindfulnessSessionType());
436         if (mindfulnessSessionRecordInternal.getTitle() != null) {
437             builder.setTitle(mindfulnessSessionRecordInternal.getTitle());
438         }
439         if (mindfulnessSessionRecordInternal.getNotes() != null) {
440             builder.setNotes(mindfulnessSessionRecordInternal.getNotes());
441         }
442 
443         return builder.build();
444     }
445 
toNutritionProto(NutritionRecordInternal nutritionRecordInternal)446     private static Nutrition toNutritionProto(NutritionRecordInternal nutritionRecordInternal) {
447         Nutrition.Builder builder =
448                 Nutrition.newBuilder()
449                         .setUnsaturatedFat(nutritionRecordInternal.getUnsaturatedFat())
450                         .setPotassium(nutritionRecordInternal.getPotassium())
451                         .setThiamin(nutritionRecordInternal.getThiamin())
452                         .setMealType(nutritionRecordInternal.getMealType())
453                         .setTransFat(nutritionRecordInternal.getTransFat())
454                         .setManganese(nutritionRecordInternal.getManganese())
455                         .setEnergyFromFat(nutritionRecordInternal.getEnergyFromFat())
456                         .setCaffeine(nutritionRecordInternal.getCaffeine())
457                         .setDietaryFiber(nutritionRecordInternal.getDietaryFiber())
458                         .setSelenium(nutritionRecordInternal.getSelenium())
459                         .setVitaminB6(nutritionRecordInternal.getVitaminB6())
460                         .setProtein(nutritionRecordInternal.getProtein())
461                         .setChloride(nutritionRecordInternal.getChloride())
462                         .setCholesterol(nutritionRecordInternal.getCholesterol())
463                         .setCopper(nutritionRecordInternal.getCopper())
464                         .setIodine(nutritionRecordInternal.getIodine())
465                         .setVitaminB12(nutritionRecordInternal.getVitaminB12())
466                         .setZinc(nutritionRecordInternal.getZinc())
467                         .setRiboflavin(nutritionRecordInternal.getRiboflavin())
468                         .setEnergy(nutritionRecordInternal.getEnergy())
469                         .setMolybdenum(nutritionRecordInternal.getMolybdenum())
470                         .setPhosphorus(nutritionRecordInternal.getPhosphorus())
471                         .setChromium(nutritionRecordInternal.getChromium())
472                         .setTotalFat(nutritionRecordInternal.getTotalFat())
473                         .setCalcium(nutritionRecordInternal.getCalcium())
474                         .setVitaminC(nutritionRecordInternal.getVitaminC())
475                         .setVitaminE(nutritionRecordInternal.getVitaminE())
476                         .setBiotin(nutritionRecordInternal.getBiotin())
477                         .setVitaminD(nutritionRecordInternal.getVitaminD())
478                         .setNiacin(nutritionRecordInternal.getNiacin())
479                         .setMagnesium(nutritionRecordInternal.getMagnesium())
480                         .setTotalCarbohydrate(nutritionRecordInternal.getTotalCarbohydrate())
481                         .setVitaminK(nutritionRecordInternal.getVitaminK())
482                         .setPolyunsaturatedFat(nutritionRecordInternal.getPolyunsaturatedFat())
483                         .setSaturatedFat(nutritionRecordInternal.getSaturatedFat())
484                         .setSodium(nutritionRecordInternal.getSodium())
485                         .setFolate(nutritionRecordInternal.getFolate())
486                         .setMonounsaturatedFat(nutritionRecordInternal.getMonounsaturatedFat())
487                         .setPantothenicAcid(nutritionRecordInternal.getPantothenicAcid())
488                         .setIron(nutritionRecordInternal.getIron())
489                         .setVitaminA(nutritionRecordInternal.getVitaminA())
490                         .setFolicAcid(nutritionRecordInternal.getFolicAcid())
491                         .setSugar(nutritionRecordInternal.getSugar());
492 
493         if (nutritionRecordInternal.getMealName() != null) {
494             builder.setMealName(nutritionRecordInternal.getMealName());
495         }
496 
497         return builder.build();
498     }
499 
toPlannedExerciseSessionProto( PlannedExerciseSessionRecordInternal plannedExerciseSessionRecordInternal)500     private static PlannedExerciseSession toPlannedExerciseSessionProto(
501             PlannedExerciseSessionRecordInternal plannedExerciseSessionRecordInternal) {
502         PlannedExerciseSession.Builder builder =
503                 PlannedExerciseSession.newBuilder()
504                         .setExerciseType(plannedExerciseSessionRecordInternal.getExerciseType())
505                         .setHasExplicitTime(
506                                 plannedExerciseSessionRecordInternal.getHasExplicitTime())
507                         .addAllExerciseBlock(
508                                 plannedExerciseSessionRecordInternal.getExerciseBlocks().stream()
509                                         .map(RecordProtoConverter::toPlannedExerciseBlockProto)
510                                         .toList());
511         if (plannedExerciseSessionRecordInternal.getNotes() != null) {
512             builder.setNotes(plannedExerciseSessionRecordInternal.getNotes());
513         }
514         if (plannedExerciseSessionRecordInternal.getTitle() != null) {
515             builder.setTitle(plannedExerciseSessionRecordInternal.getTitle());
516         }
517 
518         return builder.build();
519     }
520 
toPlannedExerciseBlockProto( PlannedExerciseBlockInternal plannedExerciseBlockInternal)521     private static PlannedExerciseBlock toPlannedExerciseBlockProto(
522             PlannedExerciseBlockInternal plannedExerciseBlockInternal) {
523         PlannedExerciseBlock.Builder builder =
524                 PlannedExerciseBlock.newBuilder()
525                         .setRepetitions(plannedExerciseBlockInternal.getRepetitions())
526                         .addAllStep(
527                                 plannedExerciseBlockInternal.getExerciseSteps().stream()
528                                         .map(RecordProtoConverter::toPlannedExerciseStepProto)
529                                         .toList());
530         if (plannedExerciseBlockInternal.getDescription() != null) {
531             builder.setDescription(plannedExerciseBlockInternal.getDescription());
532         }
533 
534         return builder.build();
535     }
536 
toPlannedExerciseStepProto( PlannedExerciseStepInternal plannedExerciseStepInternal)537     private static PlannedExerciseStep toPlannedExerciseStepProto(
538             PlannedExerciseStepInternal plannedExerciseStepInternal) {
539         PlannedExerciseStep.Builder builder =
540                 PlannedExerciseStep.newBuilder()
541                         .setExerciseType(plannedExerciseStepInternal.getExerciseType())
542                         .setExerciseCategory(plannedExerciseStepInternal.getExerciseCategory());
543         if (plannedExerciseStepInternal.getDescription() != null) {
544             builder.setDescription(plannedExerciseStepInternal.getDescription());
545         }
546         if (plannedExerciseStepInternal.getCompletionGoal() != null) {
547             builder.setCompletionGoal(
548                     toCompletionGoalProto(plannedExerciseStepInternal.getCompletionGoal()));
549         }
550         if (plannedExerciseStepInternal.getPerformanceGoals() != null) {
551             builder.addAllPerformanceGoal(
552                     plannedExerciseStepInternal.getPerformanceGoals().stream()
553                             .map(RecordProtoConverter::toPerformanceGoalProto)
554                             .toList());
555         }
556 
557         return builder.build();
558     }
559 
toPerformanceGoalProto( ExercisePerformanceGoalInternal performanceGoalInternal)560     private static ExercisePerformanceGoal toPerformanceGoalProto(
561             ExercisePerformanceGoalInternal performanceGoalInternal) {
562         if (performanceGoalInternal
563                 instanceof ExercisePerformanceGoalInternal.PowerGoalInternal powerGoalInternal) {
564             return ExercisePerformanceGoal.newBuilder()
565                     .setPowerGoal(
566                             ExercisePerformanceGoal.PowerGoal.newBuilder()
567                                     .setMinPower(powerGoalInternal.getMinPower().getInWatts())
568                                     .setMaxPower(powerGoalInternal.getMaxPower().getInWatts()))
569                     .build();
570         } else if (performanceGoalInternal
571                 instanceof ExercisePerformanceGoalInternal.SpeedGoalInternal speedGoalInternal) {
572             return ExercisePerformanceGoal.newBuilder()
573                     .setSpeedGoal(
574                             ExercisePerformanceGoal.SpeedGoal.newBuilder()
575                                     .setMinSpeed(
576                                             speedGoalInternal.getMinSpeed().getInMetersPerSecond())
577                                     .setMaxSpeed(
578                                             speedGoalInternal.getMaxSpeed().getInMetersPerSecond()))
579                     .build();
580         } else if (performanceGoalInternal
581                 instanceof
582                 ExercisePerformanceGoalInternal.CadenceGoalInternal cadenceGoalInternal) {
583             return ExercisePerformanceGoal.newBuilder()
584                     .setCadenceGoal(
585                             ExercisePerformanceGoal.CadenceGoal.newBuilder()
586                                     .setMinRpm(cadenceGoalInternal.getMinRpm())
587                                     .setMaxRpm(cadenceGoalInternal.getMaxRpm()))
588                     .build();
589         } else if (performanceGoalInternal
590                 instanceof
591                 ExercisePerformanceGoalInternal.HeartRateGoalInternal heartRateGoalInternal) {
592             return ExercisePerformanceGoal.newBuilder()
593                     .setHeartRateGoal(
594                             ExercisePerformanceGoal.HeartRateGoal.newBuilder()
595                                     .setMinBpm(heartRateGoalInternal.getMinBpm())
596                                     .setMaxBpm(heartRateGoalInternal.getMaxBpm()))
597                     .build();
598         } else if (performanceGoalInternal
599                 instanceof ExercisePerformanceGoalInternal.WeightGoalInternal weightGoalInternal) {
600             return ExercisePerformanceGoal.newBuilder()
601                     .setWeightGoal(
602                             ExercisePerformanceGoal.WeightGoal.newBuilder()
603                                     .setMass(weightGoalInternal.getMass().getInGrams()))
604                     .build();
605         } else if (performanceGoalInternal
606                 instanceof
607                 ExercisePerformanceGoalInternal.RateOfPerceivedExertionGoalInternal
608                                 rateOfPerceivedExertionGoalInternal) {
609             return ExercisePerformanceGoal.newBuilder()
610                     .setRateOfPerceivedExertionGoal(
611                             ExercisePerformanceGoal.RateOfPerceivedExertionGoal.newBuilder()
612                                     .setRpe(rateOfPerceivedExertionGoalInternal.getRpe()))
613                     .build();
614         } else if (performanceGoalInternal
615                 instanceof ExercisePerformanceGoalInternal.AmrapGoalInternal) {
616             return ExercisePerformanceGoal.newBuilder()
617                     .setAmrapGoal(ExercisePerformanceGoal.AmrapGoal.getDefaultInstance())
618                     .build();
619         } else if (performanceGoalInternal
620                 instanceof ExercisePerformanceGoalInternal.UnknownGoalInternal) {
621             return ExercisePerformanceGoal.newBuilder()
622                     .setUnknownGoal(ExercisePerformanceGoal.UnknownGoal.getDefaultInstance())
623                     .build();
624         } else {
625             throw new IllegalArgumentException(
626                     "Unknown performance goal type "
627                             + performanceGoalInternal.getClass().getSimpleName());
628         }
629     }
630 
toCompletionGoalProto( ExerciseCompletionGoalInternal completionGoalInternal)631     private static ExerciseCompletionGoal toCompletionGoalProto(
632             ExerciseCompletionGoalInternal completionGoalInternal) {
633         if (completionGoalInternal
634                 instanceof
635                 ExerciseCompletionGoalInternal.DistanceGoalInternal distanceGoalInternal) {
636             return ExerciseCompletionGoal.newBuilder()
637                     .setDistanceGoal(
638                             ExerciseCompletionGoal.DistanceGoal.newBuilder()
639                                     .setDistance(distanceGoalInternal.getDistance().getInMeters()))
640                     .build();
641         } else if (completionGoalInternal
642                 instanceof ExerciseCompletionGoalInternal.StepsGoalInternal stepsGoalInternal) {
643             return ExerciseCompletionGoal.newBuilder()
644                     .setStepsGoal(
645                             ExerciseCompletionGoal.StepsGoal.newBuilder()
646                                     .setSteps(stepsGoalInternal.getSteps()))
647                     .build();
648         } else if (completionGoalInternal
649                 instanceof
650                 ExerciseCompletionGoalInternal.DurationGoalInternal durationGoalInternal) {
651             return ExerciseCompletionGoal.newBuilder()
652                     .setDurationGoal(
653                             ExerciseCompletionGoal.DurationGoal.newBuilder()
654                                     .setDuration(durationGoalInternal.getDuration().toMillis()))
655                     .build();
656         } else if (completionGoalInternal
657                 instanceof
658                 ExerciseCompletionGoalInternal.RepetitionsGoalInternal repetitionsGoalInternal) {
659             return ExerciseCompletionGoal.newBuilder()
660                     .setRepetitionsGoal(
661                             ExerciseCompletionGoal.RepetitionsGoal.newBuilder()
662                                     .setRepetitions(repetitionsGoalInternal.getReps()))
663                     .build();
664         } else if (completionGoalInternal
665                 instanceof
666                 ExerciseCompletionGoalInternal.TotalCaloriesBurnedGoalInternal
667                                 totalCaloriesBurnedGoalInternal) {
668             return ExerciseCompletionGoal.newBuilder()
669                     .setTotalCaloriesBurnedGoal(
670                             ExerciseCompletionGoal.TotalCaloriesBurnedGoal.newBuilder()
671                                     .setTotalCalories(
672                                             totalCaloriesBurnedGoalInternal
673                                                     .getTotalCalories()
674                                                     .getInCalories()))
675                     .build();
676         } else if (completionGoalInternal
677                 instanceof
678                 ExerciseCompletionGoalInternal.ActiveCaloriesBurnedGoalInternal
679                                 activeCaloriesBurnedGoalInternal) {
680             return ExerciseCompletionGoal.newBuilder()
681                     .setActiveCaloriesBurnedGoal(
682                             ExerciseCompletionGoal.ActiveCaloriesBurnedGoal.newBuilder()
683                                     .setActiveCalories(
684                                             activeCaloriesBurnedGoalInternal
685                                                     .getActiveCalories()
686                                                     .getInCalories()))
687                     .build();
688         } else if (completionGoalInternal
689                 instanceof
690                 ExerciseCompletionGoalInternal.DistanceWithVariableRestGoalInternal
691                                 distanceWithVariableRestGoalInternal) {
692             return ExerciseCompletionGoal.newBuilder()
693                     .setDistanceWithVariableRestGoal(
694                             ExerciseCompletionGoal.DistanceWithVariableRestGoal.newBuilder()
695                                     .setDistance(
696                                             distanceWithVariableRestGoalInternal
697                                                     .getDistance()
698                                                     .getInMeters())
699                                     .setDuration(
700                                             distanceWithVariableRestGoalInternal
701                                                     .getDuration()
702                                                     .toMillis()))
703                     .build();
704         } else if (completionGoalInternal
705                 instanceof ExerciseCompletionGoalInternal.UnspecifiedGoalInternal) {
706             return ExerciseCompletionGoal.newBuilder()
707                     .setUnspecifiedGoal(ExerciseCompletionGoal.UnspecifiedGoal.getDefaultInstance())
708                     .build();
709         } else if (completionGoalInternal
710                 instanceof ExerciseCompletionGoalInternal.UnknownGoalInternal) {
711             return ExerciseCompletionGoal.newBuilder()
712                     .setUnknownGoal(ExerciseCompletionGoal.UnknownGoal.getDefaultInstance())
713                     .build();
714         } else {
715             throw new IllegalArgumentException(
716                     "Unknown completion goal type "
717                             + completionGoalInternal.getClass().getSimpleName());
718         }
719     }
720 
toPowerProto( PowerRecordInternal powerRecordInternal)721     private static com.android.server.healthconnect.proto.backuprestore.Power toPowerProto(
722             PowerRecordInternal powerRecordInternal) {
723         List<com.android.server.healthconnect.proto.backuprestore.Power.PowerSample> samples =
724                 powerRecordInternal.getSamples().stream()
725                         .map(
726                                 sample ->
727                                         com.android.server.healthconnect.proto.backuprestore.Power
728                                                 .PowerSample.newBuilder()
729                                                 .setPower(sample.getPower())
730                                                 .setEpochMillis(sample.getEpochMillis())
731                                                 .build())
732                         .toList();
733 
734         return com.android.server.healthconnect.proto.backuprestore.Power.newBuilder()
735                 .addAllSample(samples)
736                 .build();
737     }
738 
toSkinTemperatureProto( SkinTemperatureRecordInternal skinTemperatureRecordInternal)739     private static SkinTemperature toSkinTemperatureProto(
740             SkinTemperatureRecordInternal skinTemperatureRecordInternal) {
741         List<SkinTemperature.SkinTemperatureDeltaSample> samples =
742                 skinTemperatureRecordInternal.getSamples().stream()
743                         .map(
744                                 sample ->
745                                         SkinTemperature.SkinTemperatureDeltaSample.newBuilder()
746                                                 .setTemperatureDeltaInCelsius(
747                                                         sample.mTemperatureDeltaInCelsius())
748                                                 .setEpochMillis(sample.mEpochMillis())
749                                                 .build())
750                         .toList();
751 
752         return SkinTemperature.newBuilder()
753                 .setMeasurementLocation(skinTemperatureRecordInternal.getMeasurementLocation())
754                 .setBaseline(skinTemperatureRecordInternal.getBaseline().getInCelsius())
755                 .addAllSample(samples)
756                 .build();
757     }
758 
toSleepSessionProto( SleepSessionRecordInternal sleepSessionRecordInternal)759     private static SleepSession toSleepSessionProto(
760             SleepSessionRecordInternal sleepSessionRecordInternal) {
761         SleepSession.Builder builder = SleepSession.newBuilder();
762 
763         if (sleepSessionRecordInternal.getNotes() != null) {
764             builder.setNotes(sleepSessionRecordInternal.getNotes());
765         }
766         if (sleepSessionRecordInternal.getTitle() != null) {
767             builder.setTitle(sleepSessionRecordInternal.getTitle());
768         }
769         if (sleepSessionRecordInternal.getSleepStages() != null) {
770             List<SleepStage> stages =
771                     sleepSessionRecordInternal.getSleepStages().stream()
772                             .map(RecordProtoConverter::toSleepStageProto)
773                             .toList();
774             builder.addAllStage(stages);
775         }
776 
777         return builder.build();
778     }
779 
toSleepStageProto(SleepStageInternal sleepStageInternal)780     private static SleepStage toSleepStageProto(SleepStageInternal sleepStageInternal) {
781         return SleepStage.newBuilder()
782                 .setStartTime(sleepStageInternal.getStartTime())
783                 .setEndTime(sleepStageInternal.getEndTime())
784                 .setStageType(sleepStageInternal.getStageType())
785                 .build();
786     }
787 
toSpeedProto(SpeedRecordInternal speedRecordInternal)788     private static Speed toSpeedProto(SpeedRecordInternal speedRecordInternal) {
789         List<SpeedSample> samples =
790                 speedRecordInternal.getSamples().stream()
791                         .map(
792                                 sample ->
793                                         SpeedSample.newBuilder()
794                                                 .setSpeed(sample.getSpeed())
795                                                 .setEpochMillis(sample.getEpochMillis())
796                                                 .build())
797                         .toList();
798 
799         return Speed.newBuilder().addAllSample(samples).build();
800     }
801 
toStepsProto(StepsRecordInternal stepsRecordInternal)802     private static Steps toStepsProto(StepsRecordInternal stepsRecordInternal) {
803         return Steps.newBuilder().setCount(stepsRecordInternal.getCount()).build();
804     }
805 
toStepsCadenceProto( StepsCadenceRecordInternal stepsCadenceRecordInternal)806     private static StepsCadence toStepsCadenceProto(
807             StepsCadenceRecordInternal stepsCadenceRecordInternal) {
808         List<StepsCadence.StepsCadenceSample> samples =
809                 stepsCadenceRecordInternal.getSamples().stream()
810                         .map(
811                                 sample ->
812                                         StepsCadence.StepsCadenceSample.newBuilder()
813                                                 .setRate(sample.getRate())
814                                                 .setEpochMillis(sample.getEpochMillis())
815                                                 .build())
816                         .toList();
817 
818         return StepsCadence.newBuilder().addAllSample(samples).build();
819     }
820 
toTotalCaloriesBurnedProto( TotalCaloriesBurnedRecordInternal totalCaloriesBurnedRecordInternal)821     private static TotalCaloriesBurned toTotalCaloriesBurnedProto(
822             TotalCaloriesBurnedRecordInternal totalCaloriesBurnedRecordInternal) {
823         return TotalCaloriesBurned.newBuilder()
824                 .setEnergy(totalCaloriesBurnedRecordInternal.getEnergy())
825                 .build();
826     }
827 
toWheelchairPushesProto( WheelchairPushesRecordInternal wheelchairPushesRecordInternal)828     private static WheelchairPushes toWheelchairPushesProto(
829             WheelchairPushesRecordInternal wheelchairPushesRecordInternal) {
830         return WheelchairPushes.newBuilder()
831                 .setCount((int) wheelchairPushesRecordInternal.getCount())
832                 .build();
833     }
834 
toInstantRecordProto(InstantRecordInternal<?> instantRecordInternal)835     private InstantRecord toInstantRecordProto(InstantRecordInternal<?> instantRecordInternal) {
836         InstantRecord.Builder builder =
837                 InstantRecord.newBuilder()
838                         .setTime(instantRecordInternal.getTimeInMillis())
839                         .setZoneOffset(instantRecordInternal.getZoneOffsetInSeconds());
840 
841         if (instantRecordInternal
842                 instanceof BasalBodyTemperatureRecordInternal basalBodyTemperatureRecordInternal) {
843             builder.setBasalBodyTemperature(
844                     toBasalBodyTemperatureProto(basalBodyTemperatureRecordInternal));
845         } else if (instantRecordInternal
846                 instanceof BasalMetabolicRateRecordInternal basalMetabolicRateRecordInternal) {
847             builder.setBasalMetabolicRate(
848                     toBasalMetabolicRateProto(basalMetabolicRateRecordInternal));
849         } else if (instantRecordInternal
850                 instanceof BloodGlucoseRecordInternal bloodGlucoseRecordInternal) {
851             builder.setBloodGlucose(toBloodGlucoseProto(bloodGlucoseRecordInternal));
852         } else if (instantRecordInternal
853                 instanceof BloodPressureRecordInternal bloodPressureRecordInternal) {
854             builder.setBloodPressure(toBloodPressureProto(bloodPressureRecordInternal));
855         } else if (instantRecordInternal instanceof BodyFatRecordInternal bodyFatRecordInternal) {
856             builder.setBodyFat(toBodyFatProto(bodyFatRecordInternal));
857         } else if (instantRecordInternal
858                 instanceof BodyTemperatureRecordInternal bodyTemperatureRecordInternal) {
859             builder.setBodyTemperature(toBodyTemperatureProto(bodyTemperatureRecordInternal));
860         } else if (instantRecordInternal
861                 instanceof BodyWaterMassRecordInternal bodyWaterMassRecordInternal) {
862             builder.setBodyWaterMass(toBodyWaterMassProto(bodyWaterMassRecordInternal));
863         } else if (instantRecordInternal instanceof BoneMassRecordInternal boneMassRecordInternal) {
864             builder.setBoneMass(toBoneMassProto(boneMassRecordInternal));
865         } else if (instantRecordInternal
866                 instanceof CervicalMucusRecordInternal cervicalMucusRecordInternal) {
867             builder.setCervicalMucus(toCervicalMucusProto(cervicalMucusRecordInternal));
868         } else if (instantRecordInternal
869                 instanceof
870                 HeartRateVariabilityRmssdRecordInternal heartRateVariabilityRmssdRecordInternal) {
871             builder.setHeartRateVariabilityRmssd(
872                     toHeartRateVariabilityRmssdProto(heartRateVariabilityRmssdRecordInternal));
873         } else if (instantRecordInternal instanceof HeightRecordInternal heightRecordInternal) {
874             builder.setHeight(toHeightProto(heightRecordInternal));
875         } else if (instantRecordInternal instanceof IntermenstrualBleedingRecordInternal) {
876             builder.setIntermenstrualBleeding(IntermenstrualBleeding.getDefaultInstance());
877         } else if (instantRecordInternal
878                 instanceof LeanBodyMassRecordInternal leanBodyMassRecordInternal) {
879             builder.setLeanBodyMass(toLeanBodyMassProto(leanBodyMassRecordInternal));
880         } else if (instantRecordInternal
881                 instanceof MenstruationFlowRecordInternal menstruationFlowRecordInternal) {
882             builder.setMenstruationFlow(toMenstruationFlowProto(menstruationFlowRecordInternal));
883         } else if (instantRecordInternal
884                 instanceof OvulationTestRecordInternal ovulationTestRecordInternal) {
885             builder.setOvulationTest(toOvulationTestProto(ovulationTestRecordInternal));
886         } else if (instantRecordInternal
887                 instanceof OxygenSaturationRecordInternal oxygenSaturationRecordInternal) {
888             builder.setOxygenSaturation(toOxygenSaturationProto(oxygenSaturationRecordInternal));
889         } else if (instantRecordInternal
890                 instanceof RespiratoryRateRecordInternal respiratoryRateRecordInternal) {
891             builder.setRespiratoryRate(toRespiratoryRateProto(respiratoryRateRecordInternal));
892         } else if (instantRecordInternal
893                 instanceof RestingHeartRateRecordInternal restingHeartRateRecordInternal) {
894             builder.setRestingHeartRate(toRestingHeartRateProto(restingHeartRateRecordInternal));
895         } else if (instantRecordInternal
896                 instanceof SexualActivityRecordInternal sexualActivityRecordInternal) {
897             builder.setSexualActivity(toSexualActivityProto(sexualActivityRecordInternal));
898         } else if (instantRecordInternal instanceof Vo2MaxRecordInternal vo2MaxRecordInternal) {
899             builder.setVo2Max(toVo2MaxProto(vo2MaxRecordInternal));
900         } else if (instantRecordInternal instanceof WeightRecordInternal weightRecordInternal) {
901             builder.setWeight(toWeightProto(weightRecordInternal));
902         } else {
903             throw new IllegalArgumentException(
904                     "Unknown instant record type "
905                             + instantRecordInternal.getClass().getSimpleName());
906         }
907 
908         return builder.build();
909     }
910 
toBasalBodyTemperatureProto( BasalBodyTemperatureRecordInternal basalBodyTemperatureRecordInternal)911     private static BasalBodyTemperature toBasalBodyTemperatureProto(
912             BasalBodyTemperatureRecordInternal basalBodyTemperatureRecordInternal) {
913         return BasalBodyTemperature.newBuilder()
914                 .setMeasurementLocation(basalBodyTemperatureRecordInternal.getMeasurementLocation())
915                 .setTemperature(basalBodyTemperatureRecordInternal.getTemperature())
916                 .build();
917     }
918 
toBasalMetabolicRateProto( BasalMetabolicRateRecordInternal basalMetabolicRateRecordInternal)919     private static BasalMetabolicRate toBasalMetabolicRateProto(
920             BasalMetabolicRateRecordInternal basalMetabolicRateRecordInternal) {
921         return BasalMetabolicRate.newBuilder()
922                 .setBasalMetabolicRate(basalMetabolicRateRecordInternal.getBasalMetabolicRate())
923                 .build();
924     }
925 
toBloodGlucoseProto( BloodGlucoseRecordInternal bloodGlucoseRecordInternal)926     private static BloodGlucose toBloodGlucoseProto(
927             BloodGlucoseRecordInternal bloodGlucoseRecordInternal) {
928         return BloodGlucose.newBuilder()
929                 .setSpecimenSource(bloodGlucoseRecordInternal.getSpecimenSource())
930                 .setLevel(bloodGlucoseRecordInternal.getLevel())
931                 .setRelationToMeal(bloodGlucoseRecordInternal.getRelationToMeal())
932                 .setMealType(bloodGlucoseRecordInternal.getMealType())
933                 .build();
934     }
935 
toBloodPressureProto( BloodPressureRecordInternal bloodPressureRecordInternal)936     private static BloodPressure toBloodPressureProto(
937             BloodPressureRecordInternal bloodPressureRecordInternal) {
938         return BloodPressure.newBuilder()
939                 .setMeasurementLocation(bloodPressureRecordInternal.getMeasurementLocation())
940                 .setSystolic(bloodPressureRecordInternal.getSystolic())
941                 .setDiastolic(bloodPressureRecordInternal.getDiastolic())
942                 .setBodyPosition(bloodPressureRecordInternal.getBodyPosition())
943                 .build();
944     }
945 
toBodyFatProto(BodyFatRecordInternal bodyFatRecordInternal)946     private static BodyFat toBodyFatProto(BodyFatRecordInternal bodyFatRecordInternal) {
947         return BodyFat.newBuilder().setPercentage(bodyFatRecordInternal.getPercentage()).build();
948     }
949 
toBodyTemperatureProto( BodyTemperatureRecordInternal bodyTemperatureRecordInternal)950     private static BodyTemperature toBodyTemperatureProto(
951             BodyTemperatureRecordInternal bodyTemperatureRecordInternal) {
952         return BodyTemperature.newBuilder()
953                 .setMeasurementLocation(bodyTemperatureRecordInternal.getMeasurementLocation())
954                 .setTemperature(bodyTemperatureRecordInternal.getTemperature())
955                 .build();
956     }
957 
toBodyWaterMassProto( BodyWaterMassRecordInternal bodyWaterMassRecordInternal)958     private static BodyWaterMass toBodyWaterMassProto(
959             BodyWaterMassRecordInternal bodyWaterMassRecordInternal) {
960         return BodyWaterMass.newBuilder()
961                 .setBodyWaterMass(bodyWaterMassRecordInternal.getBodyWaterMass())
962                 .build();
963     }
964 
toBoneMassProto(BoneMassRecordInternal boneMassRecordInternal)965     private static BoneMass toBoneMassProto(BoneMassRecordInternal boneMassRecordInternal) {
966         return BoneMass.newBuilder().setMass(boneMassRecordInternal.getMass()).build();
967     }
968 
toCervicalMucusProto( CervicalMucusRecordInternal cervicalMucusRecordInternal)969     private static CervicalMucus toCervicalMucusProto(
970             CervicalMucusRecordInternal cervicalMucusRecordInternal) {
971         return CervicalMucus.newBuilder()
972                 .setSensation(cervicalMucusRecordInternal.getSensation())
973                 .setAppearance(cervicalMucusRecordInternal.getAppearance())
974                 .build();
975     }
976 
toHeartRateVariabilityRmssdProto( HeartRateVariabilityRmssdRecordInternal heartRateVariabilityRmssdRecordInternal)977     private static HeartRateVariabilityRmssd toHeartRateVariabilityRmssdProto(
978             HeartRateVariabilityRmssdRecordInternal heartRateVariabilityRmssdRecordInternal) {
979         return HeartRateVariabilityRmssd.newBuilder()
980                 .setHeartRateVariabilityMillis(
981                         heartRateVariabilityRmssdRecordInternal.getHeartRateVariabilityMillis())
982                 .build();
983     }
984 
toHeightProto(HeightRecordInternal heightRecordInternal)985     private static Height toHeightProto(HeightRecordInternal heightRecordInternal) {
986         return Height.newBuilder().setHeight(heightRecordInternal.getHeight()).build();
987     }
988 
toLeanBodyMassProto( LeanBodyMassRecordInternal leanBodyMassRecordInternal)989     private static LeanBodyMass toLeanBodyMassProto(
990             LeanBodyMassRecordInternal leanBodyMassRecordInternal) {
991         return LeanBodyMass.newBuilder().setMass(leanBodyMassRecordInternal.getMass()).build();
992     }
993 
toMenstruationFlowProto( MenstruationFlowRecordInternal menstruationFlowRecordInternal)994     private static MenstruationFlow toMenstruationFlowProto(
995             MenstruationFlowRecordInternal menstruationFlowRecordInternal) {
996         return MenstruationFlow.newBuilder()
997                 .setFlow(menstruationFlowRecordInternal.getFlow())
998                 .build();
999     }
1000 
toOvulationTestProto( OvulationTestRecordInternal ovulationTestRecordInternal)1001     private static OvulationTest toOvulationTestProto(
1002             OvulationTestRecordInternal ovulationTestRecordInternal) {
1003         return OvulationTest.newBuilder()
1004                 .setResult(ovulationTestRecordInternal.getResult())
1005                 .build();
1006     }
1007 
toOxygenSaturationProto( OxygenSaturationRecordInternal oxygenSaturationRecordInternal)1008     private static OxygenSaturation toOxygenSaturationProto(
1009             OxygenSaturationRecordInternal oxygenSaturationRecordInternal) {
1010         return OxygenSaturation.newBuilder()
1011                 .setPercentage(oxygenSaturationRecordInternal.getPercentage())
1012                 .build();
1013     }
1014 
toRespiratoryRateProto( RespiratoryRateRecordInternal respiratoryRateRecordInternal)1015     private static RespiratoryRate toRespiratoryRateProto(
1016             RespiratoryRateRecordInternal respiratoryRateRecordInternal) {
1017         return RespiratoryRate.newBuilder()
1018                 .setRate(respiratoryRateRecordInternal.getRate())
1019                 .build();
1020     }
1021 
toRestingHeartRateProto( RestingHeartRateRecordInternal restingHeartRateRecordInternal)1022     private static RestingHeartRate toRestingHeartRateProto(
1023             RestingHeartRateRecordInternal restingHeartRateRecordInternal) {
1024         return RestingHeartRate.newBuilder()
1025                 .setBeatsPerMinute(restingHeartRateRecordInternal.getBeatsPerMinute())
1026                 .build();
1027     }
1028 
toSexualActivityProto( SexualActivityRecordInternal sexualActivityRecordInternal)1029     private static SexualActivity toSexualActivityProto(
1030             SexualActivityRecordInternal sexualActivityRecordInternal) {
1031         return SexualActivity.newBuilder()
1032                 .setProtectionUsed(sexualActivityRecordInternal.getProtectionUsed())
1033                 .build();
1034     }
1035 
toVo2MaxProto(Vo2MaxRecordInternal vo2MaxRecordInternal)1036     private static Vo2Max toVo2MaxProto(Vo2MaxRecordInternal vo2MaxRecordInternal) {
1037         return Vo2Max.newBuilder()
1038                 .setMeasurementMethod(vo2MaxRecordInternal.getMeasurementMethod())
1039                 .setVo2MillilitersPerMinuteKilogram(
1040                         vo2MaxRecordInternal.getVo2MillilitersPerMinuteKilogram())
1041                 .build();
1042     }
1043 
toWeightProto(WeightRecordInternal weightRecordInternal)1044     private static Weight toWeightProto(WeightRecordInternal weightRecordInternal) {
1045         return Weight.newBuilder().setWeight(weightRecordInternal.getWeight()).build();
1046     }
1047 
1048     @SuppressLint("WrongConstant") // Proto doesn't know about the device type & rec method IntDefs
populateRecordInternal( Record recordProto, RecordInternal<?> recordInternal)1049     private static void populateRecordInternal(
1050             Record recordProto, RecordInternal<?> recordInternal) {
1051         String uuidString = recordProto.getUuid();
1052         if (!uuidString.isEmpty()) {
1053             recordInternal.setUuid(UUID.fromString(uuidString));
1054         }
1055         if (recordProto.hasPackageName()) {
1056             recordInternal.setPackageName(recordProto.getPackageName());
1057         }
1058         recordInternal.setLastModifiedTime(recordProto.getLastModifiedTime());
1059         if (recordProto.hasClientRecordId()) {
1060             recordInternal.setClientRecordId(recordProto.getClientRecordId());
1061         }
1062         recordInternal.setClientRecordVersion(recordProto.getClientRecordVersion());
1063         if (recordProto.hasManufacturer()) {
1064             recordInternal.setManufacturer(recordProto.getManufacturer());
1065         }
1066         if (recordProto.hasModel()) {
1067             recordInternal.setModel(recordProto.getModel());
1068         }
1069         recordInternal.setDeviceType(recordProto.getDeviceType());
1070         recordInternal.setRecordingMethod(recordProto.getRecordingMethod());
1071 
1072         switch (recordProto.getSubRecordCase()) {
1073             case INTERVAL_RECORD ->
1074                     populateIntervalRecordInternal(
1075                             recordProto.getIntervalRecord(),
1076                             (IntervalRecordInternal<?>) recordInternal);
1077             case INSTANT_RECORD ->
1078                     populateInstantRecordInternal(
1079                             recordProto.getInstantRecord(),
1080                             (InstantRecordInternal<?>) recordInternal);
1081             default ->
1082                     throw new IllegalArgumentException(
1083                             "Unknown record type " + recordProto.getSubRecordCase());
1084         }
1085     }
1086 
populateIntervalRecordInternal( IntervalRecord intervalRecordProto, IntervalRecordInternal<?> intervalRecordInternal)1087     private static void populateIntervalRecordInternal(
1088             IntervalRecord intervalRecordProto, IntervalRecordInternal<?> intervalRecordInternal) {
1089         intervalRecordInternal
1090                 .setStartTime(intervalRecordProto.getStartTime())
1091                 .setStartZoneOffset(intervalRecordProto.getStartZoneOffset())
1092                 .setEndTime(intervalRecordProto.getEndTime())
1093                 .setEndZoneOffset(intervalRecordProto.getEndZoneOffset());
1094 
1095         switch (intervalRecordProto.getDataCase()) {
1096             case ACTIVE_CALORIES_BURNED ->
1097                     populateActiveCaloriesBurnedRecordInternal(
1098                             intervalRecordProto.getActiveCaloriesBurned(),
1099                             (ActiveCaloriesBurnedRecordInternal) intervalRecordInternal);
1100             case ACTIVITY_INTENSITY ->
1101                     populateActivityIntensityRecordInternal(
1102                             intervalRecordProto.getActivityIntensity(),
1103                             (ActivityIntensityRecordInternal) intervalRecordInternal);
1104             case CYCLING_PEDALING_CADENCE ->
1105                     populateCyclingPedalingCadenceRecordInternal(
1106                             intervalRecordProto.getCyclingPedalingCadence(),
1107                             (CyclingPedalingCadenceRecordInternal) intervalRecordInternal);
1108             case DISTANCE ->
1109                     populateDistanceRecordInternal(
1110                             intervalRecordProto.getDistance(),
1111                             (DistanceRecordInternal) intervalRecordInternal);
1112             case ELEVATION_GAINED ->
1113                     populateElevationGainedRecordInternal(
1114                             intervalRecordProto.getElevationGained(),
1115                             (ElevationGainedRecordInternal) intervalRecordInternal);
1116             case EXERCISE_SESSION ->
1117                     populateExerciseSessionRecordInternal(
1118                             intervalRecordProto.getExerciseSession(),
1119                             (ExerciseSessionRecordInternal) intervalRecordInternal);
1120             case FLOORS_CLIMBED ->
1121                     populateFloorsClimbedRecordInternal(
1122                             intervalRecordProto.getFloorsClimbed(),
1123                             (FloorsClimbedRecordInternal) intervalRecordInternal);
1124             case HEART_RATE ->
1125                     populateHeartRateRecordInternal(
1126                             intervalRecordProto.getHeartRate(),
1127                             (HeartRateRecordInternal) intervalRecordInternal);
1128             case HYDRATION ->
1129                     populateHydrationRecordInternal(
1130                             intervalRecordProto.getHydration(),
1131                             (HydrationRecordInternal) intervalRecordInternal);
1132             case MENSTRUATION_PERIOD -> {
1133                 // No data to populate.
1134             }
1135             case MINDFULNESS_SESSION ->
1136                     populateMindfulnessSessionRecordInternal(
1137                             intervalRecordProto.getMindfulnessSession(),
1138                             (MindfulnessSessionRecordInternal) intervalRecordInternal);
1139             case NUTRITION ->
1140                     populateNutritionRecordInternal(
1141                             intervalRecordProto.getNutrition(),
1142                             (NutritionRecordInternal) intervalRecordInternal);
1143             case PLANNED_EXERCISE_SESSION ->
1144                     populatePlannedExerciseSessionRecordInternal(
1145                             intervalRecordProto.getPlannedExerciseSession(),
1146                             (PlannedExerciseSessionRecordInternal) intervalRecordInternal);
1147             case POWER ->
1148                     populatePowerRecordInternal(
1149                             intervalRecordProto.getPower(),
1150                             (PowerRecordInternal) intervalRecordInternal);
1151             case SKIN_TEMPERATURE ->
1152                     populateSkinTemperatureRecordInternal(
1153                             intervalRecordProto.getSkinTemperature(),
1154                             (SkinTemperatureRecordInternal) intervalRecordInternal);
1155             case SLEEP_SESSION ->
1156                     populateSleepSessionRecordInternal(
1157                             intervalRecordProto.getSleepSession(),
1158                             (SleepSessionRecordInternal) intervalRecordInternal);
1159             case SPEED ->
1160                     populateSpeedRecordInternal(
1161                             intervalRecordProto.getSpeed(),
1162                             (SpeedRecordInternal) intervalRecordInternal);
1163             case STEPS ->
1164                     populateStepsRecordInternal(
1165                             intervalRecordProto.getSteps(),
1166                             (StepsRecordInternal) intervalRecordInternal);
1167             case STEPS_CADENCE ->
1168                     populateStepsCadenceRecordInternal(
1169                             intervalRecordProto.getStepsCadence(),
1170                             (StepsCadenceRecordInternal) intervalRecordInternal);
1171             case TOTAL_CALORIES_BURNED ->
1172                     populateTotalCaloriesBurnedRecordInternal(
1173                             intervalRecordProto.getTotalCaloriesBurned(),
1174                             (TotalCaloriesBurnedRecordInternal) intervalRecordInternal);
1175             case WHEELCHAIR_PUSHES ->
1176                     populateWheelchairPushesRecordInternal(
1177                             intervalRecordProto.getWheelchairPushes(),
1178                             (WheelchairPushesRecordInternal) intervalRecordInternal);
1179             default ->
1180                     throw new IllegalArgumentException(
1181                             "Unknown record type " + intervalRecordProto.getDataCase());
1182         }
1183     }
1184 
populateActiveCaloriesBurnedRecordInternal( ActiveCaloriesBurned activeCaloriesBurnedProto, ActiveCaloriesBurnedRecordInternal activeCaloriesBurnedRecordInternal)1185     private static void populateActiveCaloriesBurnedRecordInternal(
1186             ActiveCaloriesBurned activeCaloriesBurnedProto,
1187             ActiveCaloriesBurnedRecordInternal activeCaloriesBurnedRecordInternal) {
1188         activeCaloriesBurnedRecordInternal.setEnergy(activeCaloriesBurnedProto.getEnergy());
1189     }
1190 
populateActivityIntensityRecordInternal( ActivityIntensity activityIntensityProto, ActivityIntensityRecordInternal activityIntensityRecordInternal)1191     private static void populateActivityIntensityRecordInternal(
1192             ActivityIntensity activityIntensityProto,
1193             ActivityIntensityRecordInternal activityIntensityRecordInternal) {
1194         activityIntensityRecordInternal.setActivityIntensityType(
1195                 activityIntensityProto.getActivityIntensityType());
1196     }
1197 
populateCyclingPedalingCadenceRecordInternal( CyclingPedalingCadence cyclingPedalingCadenceProto, CyclingPedalingCadenceRecordInternal cyclingPedalingCadenceRecordInternal)1198     private static void populateCyclingPedalingCadenceRecordInternal(
1199             CyclingPedalingCadence cyclingPedalingCadenceProto,
1200             CyclingPedalingCadenceRecordInternal cyclingPedalingCadenceRecordInternal) {
1201         cyclingPedalingCadenceRecordInternal.setSamples(
1202                 cyclingPedalingCadenceProto.getSampleList().stream()
1203                         .map(
1204                                 sample ->
1205                                         new CyclingPedalingCadenceRecordInternal
1206                                                 .CyclingPedalingCadenceRecordSample(
1207                                                 sample.getRevolutionsPerMinute(),
1208                                                 sample.getEpochMillis()))
1209                         .collect(toSet()));
1210     }
1211 
populateDistanceRecordInternal( Distance distanceProto, DistanceRecordInternal distanceRecordInternal)1212     private static void populateDistanceRecordInternal(
1213             Distance distanceProto, DistanceRecordInternal distanceRecordInternal) {
1214         distanceRecordInternal.setDistance(distanceProto.getDistance());
1215     }
1216 
populateElevationGainedRecordInternal( ElevationGained elevationGainedProto, ElevationGainedRecordInternal elevationGainedRecordInternal)1217     private static void populateElevationGainedRecordInternal(
1218             ElevationGained elevationGainedProto,
1219             ElevationGainedRecordInternal elevationGainedRecordInternal) {
1220         elevationGainedRecordInternal.setElevation(elevationGainedProto.getElevation());
1221     }
1222 
populateExerciseSessionRecordInternal( ExerciseSession exerciseSessionProto, ExerciseSessionRecordInternal exerciseSessionRecordInternal)1223     private static void populateExerciseSessionRecordInternal(
1224             ExerciseSession exerciseSessionProto,
1225             ExerciseSessionRecordInternal exerciseSessionRecordInternal) {
1226         if (exerciseSessionProto.hasNotes()) {
1227             exerciseSessionRecordInternal.setNotes(exerciseSessionProto.getNotes());
1228         }
1229         exerciseSessionRecordInternal.setExerciseType(exerciseSessionProto.getExerciseType());
1230         if (exerciseSessionProto.hasTitle()) {
1231             exerciseSessionRecordInternal.setTitle(exerciseSessionProto.getTitle());
1232         }
1233         exerciseSessionRecordInternal.setHasRoute(exerciseSessionProto.getHasRoute());
1234         if (exerciseSessionProto.hasRoute()) {
1235             List<LocationInternal> locations =
1236                     exerciseSessionProto.getRoute().getRouteLocationList().stream()
1237                             .map(
1238                                     location ->
1239                                             new LocationInternal()
1240                                                     .setTime(location.getTime())
1241                                                     .setLatitude(location.getLatitude())
1242                                                     .setLongitude(location.getLongitude())
1243                                                     .setHorizontalAccuracy(
1244                                                             location.getHorizontalAccuracy())
1245                                                     .setVerticalAccuracy(
1246                                                             location.getVerticalAccuracy())
1247                                                     .setAltitude(location.getAltitude()))
1248                             .toList();
1249             exerciseSessionRecordInternal.setRoute(new ExerciseRouteInternal(locations));
1250         }
1251         exerciseSessionRecordInternal.setExerciseLaps(
1252                 exerciseSessionProto.getLapList().stream()
1253                         .map(
1254                                 lap ->
1255                                         new ExerciseLapInternal()
1256                                                 .setStartTime(lap.getStartTime())
1257                                                 .setEndTime(lap.getEndTime())
1258                                                 .setLength(lap.getLength()))
1259                         .toList());
1260         exerciseSessionRecordInternal.setExerciseSegments(
1261                 exerciseSessionProto.getSegmentList().stream()
1262                         .map(
1263                                 segment ->
1264                                         new ExerciseSegmentInternal()
1265                                                 .setStartTime(segment.getStartTime())
1266                                                 .setEndTime(segment.getEndTime())
1267                                                 .setSegmentType(segment.getSegmentType())
1268                                                 .setRepetitionsCount(segment.getRepetitionsCount()))
1269                         .toList());
1270         if (exerciseSessionProto.hasPlannedExerciseSessionId()) {
1271             exerciseSessionRecordInternal.setPlannedExerciseSessionId(
1272                     UUID.fromString(exerciseSessionProto.getPlannedExerciseSessionId()));
1273         }
1274     }
1275 
populateFloorsClimbedRecordInternal( FloorsClimbed floorsClimbedProto, FloorsClimbedRecordInternal floorsClimbedRecordInternal)1276     private static void populateFloorsClimbedRecordInternal(
1277             FloorsClimbed floorsClimbedProto,
1278             FloorsClimbedRecordInternal floorsClimbedRecordInternal) {
1279         floorsClimbedRecordInternal.setFloors(floorsClimbedProto.getFloors());
1280     }
1281 
populateHeartRateRecordInternal( HeartRate heartRateProto, HeartRateRecordInternal heartRateRecordInternal)1282     private static void populateHeartRateRecordInternal(
1283             HeartRate heartRateProto, HeartRateRecordInternal heartRateRecordInternal) {
1284         heartRateRecordInternal.setSamples(
1285                 heartRateProto.getSampleList().stream()
1286                         .map(
1287                                 sample ->
1288                                         new HeartRateRecordInternal.HeartRateSample(
1289                                                 sample.getBeatsPerMinute(),
1290                                                 sample.getEpochMillis()))
1291                         .collect(toSet()));
1292     }
1293 
populateHydrationRecordInternal( Hydration hydrationProto, HydrationRecordInternal hydrationRecordInternal)1294     private static void populateHydrationRecordInternal(
1295             Hydration hydrationProto, HydrationRecordInternal hydrationRecordInternal) {
1296         hydrationRecordInternal.setVolume(hydrationProto.getVolume());
1297     }
1298 
populateMindfulnessSessionRecordInternal( MindfulnessSession mindfulnessSessionProto, MindfulnessSessionRecordInternal mindfulnessSessionRecordInternal)1299     private static void populateMindfulnessSessionRecordInternal(
1300             MindfulnessSession mindfulnessSessionProto,
1301             MindfulnessSessionRecordInternal mindfulnessSessionRecordInternal) {
1302         mindfulnessSessionRecordInternal.setMindfulnessSessionType(
1303                 mindfulnessSessionProto.getMindfulnessSessionType());
1304         if (mindfulnessSessionProto.hasTitle()) {
1305             mindfulnessSessionRecordInternal.setTitle(mindfulnessSessionProto.getTitle());
1306         }
1307         if (mindfulnessSessionProto.hasNotes()) {
1308             mindfulnessSessionRecordInternal.setNotes(mindfulnessSessionProto.getNotes());
1309         }
1310     }
1311 
populateNutritionRecordInternal( Nutrition nutritionProto, NutritionRecordInternal nutritionRecordInternal)1312     private static void populateNutritionRecordInternal(
1313             Nutrition nutritionProto, NutritionRecordInternal nutritionRecordInternal) {
1314         nutritionRecordInternal
1315                 .setUnsaturatedFat(nutritionProto.getUnsaturatedFat())
1316                 .setPotassium(nutritionProto.getPotassium())
1317                 .setThiamin(nutritionProto.getThiamin())
1318                 .setMealType(nutritionProto.getMealType())
1319                 .setTransFat(nutritionProto.getTransFat())
1320                 .setManganese(nutritionProto.getManganese())
1321                 .setEnergyFromFat(nutritionProto.getEnergyFromFat())
1322                 .setCaffeine(nutritionProto.getCaffeine())
1323                 .setDietaryFiber(nutritionProto.getDietaryFiber())
1324                 .setSelenium(nutritionProto.getSelenium())
1325                 .setVitaminB6(nutritionProto.getVitaminB6())
1326                 .setProtein(nutritionProto.getProtein())
1327                 .setChloride(nutritionProto.getChloride())
1328                 .setCholesterol(nutritionProto.getCholesterol())
1329                 .setCopper(nutritionProto.getCopper())
1330                 .setIodine(nutritionProto.getIodine())
1331                 .setVitaminB12(nutritionProto.getVitaminB12())
1332                 .setZinc(nutritionProto.getZinc())
1333                 .setRiboflavin(nutritionProto.getRiboflavin())
1334                 .setEnergy(nutritionProto.getEnergy())
1335                 .setMolybdenum(nutritionProto.getMolybdenum())
1336                 .setPhosphorus(nutritionProto.getPhosphorus())
1337                 .setChromium(nutritionProto.getChromium())
1338                 .setTotalFat(nutritionProto.getTotalFat())
1339                 .setCalcium(nutritionProto.getCalcium())
1340                 .setVitaminC(nutritionProto.getVitaminC())
1341                 .setVitaminE(nutritionProto.getVitaminE())
1342                 .setBiotin(nutritionProto.getBiotin())
1343                 .setVitaminD(nutritionProto.getVitaminD())
1344                 .setNiacin(nutritionProto.getNiacin())
1345                 .setMagnesium(nutritionProto.getMagnesium())
1346                 .setTotalCarbohydrate(nutritionProto.getTotalCarbohydrate())
1347                 .setVitaminK(nutritionProto.getVitaminK())
1348                 .setPolyunsaturatedFat(nutritionProto.getPolyunsaturatedFat())
1349                 .setSaturatedFat(nutritionProto.getSaturatedFat())
1350                 .setSodium(nutritionProto.getSodium())
1351                 .setFolate(nutritionProto.getFolate())
1352                 .setMonounsaturatedFat(nutritionProto.getMonounsaturatedFat())
1353                 .setPantothenicAcid(nutritionProto.getPantothenicAcid())
1354                 .setIron(nutritionProto.getIron())
1355                 .setVitaminA(nutritionProto.getVitaminA())
1356                 .setFolicAcid(nutritionProto.getFolicAcid())
1357                 .setSugar(nutritionProto.getSugar());
1358 
1359         if (nutritionProto.hasMealName()) {
1360             nutritionRecordInternal.setMealName(nutritionProto.getMealName());
1361         }
1362     }
1363 
populatePlannedExerciseSessionRecordInternal( PlannedExerciseSession plannedExerciseSessionProto, PlannedExerciseSessionRecordInternal plannedExerciseSessionRecordInternal)1364     private static void populatePlannedExerciseSessionRecordInternal(
1365             PlannedExerciseSession plannedExerciseSessionProto,
1366             PlannedExerciseSessionRecordInternal plannedExerciseSessionRecordInternal) {
1367         if (plannedExerciseSessionProto.hasNotes()) {
1368             plannedExerciseSessionRecordInternal.setNotes(plannedExerciseSessionProto.getNotes());
1369         }
1370         plannedExerciseSessionRecordInternal.setExerciseType(
1371                 plannedExerciseSessionProto.getExerciseType());
1372         if (plannedExerciseSessionProto.hasTitle()) {
1373             plannedExerciseSessionRecordInternal.setTitle(plannedExerciseSessionProto.getTitle());
1374         }
1375         plannedExerciseSessionRecordInternal.setHasExplicitTime(
1376                 plannedExerciseSessionProto.getHasExplicitTime());
1377         plannedExerciseSessionRecordInternal.setExerciseBlocks(
1378                 plannedExerciseSessionProto.getExerciseBlockList().stream()
1379                         .map(RecordProtoConverter::convertToPlannedExerciseBlockInternal)
1380                         .toList());
1381     }
1382 
convertToPlannedExerciseBlockInternal( PlannedExerciseBlock plannedExerciseBlockProto)1383     private static PlannedExerciseBlockInternal convertToPlannedExerciseBlockInternal(
1384             PlannedExerciseBlock plannedExerciseBlockProto) {
1385         var plannedExerciseBlockInternal =
1386                 new PlannedExerciseBlockInternal(plannedExerciseBlockProto.getRepetitions());
1387 
1388         if (plannedExerciseBlockProto.hasDescription()) {
1389             plannedExerciseBlockInternal.setDescription(plannedExerciseBlockProto.getDescription());
1390         }
1391         plannedExerciseBlockInternal.setExerciseSteps(
1392                 plannedExerciseBlockProto.getStepList().stream()
1393                         .map(RecordProtoConverter::convertToPlannedExerciseStepInternal)
1394                         .toList());
1395 
1396         return plannedExerciseBlockInternal;
1397     }
1398 
convertToPlannedExerciseStepInternal( PlannedExerciseStep plannedExerciseStepProto)1399     private static PlannedExerciseStepInternal convertToPlannedExerciseStepInternal(
1400             PlannedExerciseStep plannedExerciseStepProto) {
1401         var plannedExerciseStepInternal =
1402                 new PlannedExerciseStepInternal(
1403                         plannedExerciseStepProto.getExerciseType(),
1404                         plannedExerciseStepProto.getExerciseCategory(),
1405                         convertToExerciseCompletionGoalInternal(
1406                                 plannedExerciseStepProto.getCompletionGoal()));
1407 
1408         if (plannedExerciseStepProto.hasDescription()) {
1409             plannedExerciseStepInternal.setDescription(plannedExerciseStepProto.getDescription());
1410         }
1411         plannedExerciseStepInternal.setPerformanceGoals(
1412                 plannedExerciseStepProto.getPerformanceGoalList().stream()
1413                         .map(RecordProtoConverter::convertToExercisePerformanceGoalInternal)
1414                         .toList());
1415 
1416         return plannedExerciseStepInternal;
1417     }
1418 
convertToExerciseCompletionGoalInternal( ExerciseCompletionGoal exerciseCompletionGoalProto)1419     private static ExerciseCompletionGoalInternal convertToExerciseCompletionGoalInternal(
1420             ExerciseCompletionGoal exerciseCompletionGoalProto) {
1421         return switch (exerciseCompletionGoalProto.getGoalCase()) {
1422             case DISTANCE_GOAL ->
1423                     new ExerciseCompletionGoalInternal.DistanceGoalInternal(
1424                             Length.fromMeters(
1425                                     exerciseCompletionGoalProto.getDistanceGoal().getDistance()));
1426             case STEPS_GOAL ->
1427                     new ExerciseCompletionGoalInternal.StepsGoalInternal(
1428                             exerciseCompletionGoalProto.getStepsGoal().getSteps());
1429             case DURATION_GOAL ->
1430                     new ExerciseCompletionGoalInternal.DurationGoalInternal(
1431                             Duration.ofMillis(
1432                                     exerciseCompletionGoalProto.getDurationGoal().getDuration()));
1433             case REPETITIONS_GOAL ->
1434                     new ExerciseCompletionGoalInternal.RepetitionsGoalInternal(
1435                             exerciseCompletionGoalProto.getRepetitionsGoal().getRepetitions());
1436             case TOTAL_CALORIES_BURNED_GOAL ->
1437                     new ExerciseCompletionGoalInternal.TotalCaloriesBurnedGoalInternal(
1438                             Energy.fromCalories(
1439                                     exerciseCompletionGoalProto
1440                                             .getTotalCaloriesBurnedGoal()
1441                                             .getTotalCalories()));
1442             case ACTIVE_CALORIES_BURNED_GOAL ->
1443                     new ExerciseCompletionGoalInternal.ActiveCaloriesBurnedGoalInternal(
1444                             Energy.fromCalories(
1445                                     exerciseCompletionGoalProto
1446                                             .getActiveCaloriesBurnedGoal()
1447                                             .getActiveCalories()));
1448             case DISTANCE_WITH_VARIABLE_REST_GOAL ->
1449                     new ExerciseCompletionGoalInternal.DistanceWithVariableRestGoalInternal(
1450                             Length.fromMeters(
1451                                     exerciseCompletionGoalProto
1452                                             .getDistanceWithVariableRestGoal()
1453                                             .getDistance()),
1454                             Duration.ofMillis(
1455                                     exerciseCompletionGoalProto
1456                                             .getDistanceWithVariableRestGoal()
1457                                             .getDuration()));
1458             case UNSPECIFIED_GOAL ->
1459                     ExerciseCompletionGoalInternal.UnspecifiedGoalInternal.INSTANCE;
1460             case UNKNOWN_GOAL, GOAL_NOT_SET ->
1461                     ExerciseCompletionGoalInternal.UnknownGoalInternal.INSTANCE;
1462         };
1463     }
1464 
convertToExercisePerformanceGoalInternal( ExercisePerformanceGoal exercisePerformanceGoalProto)1465     private static ExercisePerformanceGoalInternal convertToExercisePerformanceGoalInternal(
1466             ExercisePerformanceGoal exercisePerformanceGoalProto) {
1467         return switch (exercisePerformanceGoalProto.getGoalCase()) {
1468             case POWER_GOAL ->
1469                     new ExercisePerformanceGoalInternal.PowerGoalInternal(
1470                             Power.fromWatts(
1471                                     exercisePerformanceGoalProto.getPowerGoal().getMinPower()),
1472                             Power.fromWatts(
1473                                     exercisePerformanceGoalProto.getPowerGoal().getMaxPower()));
1474             case SPEED_GOAL ->
1475                     new ExercisePerformanceGoalInternal.SpeedGoalInternal(
1476                             Velocity.fromMetersPerSecond(
1477                                     exercisePerformanceGoalProto.getSpeedGoal().getMinSpeed()),
1478                             Velocity.fromMetersPerSecond(
1479                                     exercisePerformanceGoalProto.getSpeedGoal().getMaxSpeed()));
1480             case CADENCE_GOAL ->
1481                     new ExercisePerformanceGoalInternal.CadenceGoalInternal(
1482                             exercisePerformanceGoalProto.getCadenceGoal().getMinRpm(),
1483                             exercisePerformanceGoalProto.getCadenceGoal().getMaxRpm());
1484             case HEART_RATE_GOAL ->
1485                     new ExercisePerformanceGoalInternal.HeartRateGoalInternal(
1486                             exercisePerformanceGoalProto.getHeartRateGoal().getMinBpm(),
1487                             exercisePerformanceGoalProto.getHeartRateGoal().getMaxBpm());
1488             case WEIGHT_GOAL ->
1489                     new ExercisePerformanceGoalInternal.WeightGoalInternal(
1490                             Mass.fromGrams(exercisePerformanceGoalProto.getWeightGoal().getMass()));
1491             case RATE_OF_PERCEIVED_EXERTION_GOAL ->
1492                     new ExercisePerformanceGoalInternal.RateOfPerceivedExertionGoalInternal(
1493                             exercisePerformanceGoalProto.getRateOfPerceivedExertionGoal().getRpe());
1494             case AMRAP_GOAL -> ExercisePerformanceGoalInternal.AmrapGoalInternal.INSTANCE;
1495             case UNKNOWN_GOAL, GOAL_NOT_SET ->
1496                     ExercisePerformanceGoalInternal.UnknownGoalInternal.INSTANCE;
1497         };
1498     }
1499 
1500     private static void populatePowerRecordInternal(
1501             com.android.server.healthconnect.proto.backuprestore.Power powerProto,
1502             PowerRecordInternal powerRecordInternal) {
1503         powerRecordInternal.setSamples(
1504                 powerProto.getSampleList().stream()
1505                         .map(
1506                                 sample ->
1507                                         new PowerRecordInternal.PowerRecordSample(
1508                                                 sample.getPower(), sample.getEpochMillis()))
1509                         .collect(toSet()));
1510     }
1511 
1512     private static void populateSkinTemperatureRecordInternal(
1513             SkinTemperature skinTemperatureProto,
1514             SkinTemperatureRecordInternal skinTemperatureRecordInternal) {
1515         skinTemperatureRecordInternal
1516                 .setMeasurementLocation(skinTemperatureProto.getMeasurementLocation())
1517                 .setBaseline(fromCelsius(skinTemperatureProto.getBaseline()))
1518                 .setSamples(
1519                         skinTemperatureProto.getSampleList().stream()
1520                                 .map(
1521                                         sample ->
1522                                                 new SkinTemperatureRecordInternal
1523                                                         .SkinTemperatureDeltaSample(
1524                                                         sample.getTemperatureDeltaInCelsius(),
1525                                                         sample.getEpochMillis()))
1526                                 .collect(toSet()));
1527     }
1528 
1529     @SuppressLint("WrongConstant")
1530     private static void populateSleepSessionRecordInternal(
1531             SleepSession sleepSessionProto, SleepSessionRecordInternal sleepSessionRecordInternal) {
1532         if (sleepSessionProto.hasNotes()) {
1533             sleepSessionRecordInternal.setNotes(sleepSessionProto.getNotes());
1534         }
1535         if (sleepSessionProto.hasTitle()) {
1536             sleepSessionRecordInternal.setTitle(sleepSessionProto.getTitle());
1537         }
1538         if (sleepSessionProto.getStageCount() > 0) {
1539             sleepSessionRecordInternal.setSleepStages(
1540                     sleepSessionProto.getStageList().stream()
1541                             .map(
1542                                     sleepStage ->
1543                                             new SleepStageInternal()
1544                                                     .setStartTime(sleepStage.getStartTime())
1545                                                     .setEndTime(sleepStage.getEndTime())
1546                                                     .setStageType(sleepStage.getStageType()))
1547                             .toList());
1548         }
1549     }
1550 
1551     private static void populateSpeedRecordInternal(
1552             Speed speedProto, SpeedRecordInternal speedRecordInternal) {
1553         speedRecordInternal.setSamples(
1554                 speedProto.getSampleList().stream()
1555                         .map(
1556                                 sample ->
1557                                         new SpeedRecordInternal.SpeedRecordSample(
1558                                                 sample.getSpeed(), sample.getEpochMillis()))
1559                         .collect(toSet()));
1560     }
1561 
1562     private static void populateStepsRecordInternal(
1563             Steps stepsProto, StepsRecordInternal stepsRecordInternal) {
1564         stepsRecordInternal.setCount(stepsProto.getCount());
1565     }
1566 
1567     private static void populateTotalCaloriesBurnedRecordInternal(
1568             TotalCaloriesBurned totalCaloriesBurnedProto,
1569             TotalCaloriesBurnedRecordInternal totalCaloriesBurnedRecordInternal) {
1570         totalCaloriesBurnedRecordInternal.setEnergy(totalCaloriesBurnedProto.getEnergy());
1571     }
1572 
1573     private static void populateStepsCadenceRecordInternal(
1574             StepsCadence stepsCadenceProto, StepsCadenceRecordInternal stepsCadenceRecordInternal) {
1575         stepsCadenceRecordInternal.setSamples(
1576                 stepsCadenceProto.getSampleList().stream()
1577                         .map(
1578                                 sample ->
1579                                         new StepsCadenceRecordInternal.StepsCadenceRecordSample(
1580                                                 sample.getRate(), sample.getEpochMillis()))
1581                         .collect(toSet()));
1582     }
1583 
1584     private static void populateWheelchairPushesRecordInternal(
1585             WheelchairPushes wheelchairPushesProto,
1586             WheelchairPushesRecordInternal wheelchairPushesRecordInternal) {
1587         wheelchairPushesRecordInternal.setCount(wheelchairPushesProto.getCount());
1588     }
1589 
1590     private static void populateInstantRecordInternal(
1591             InstantRecord instantRecordProto, InstantRecordInternal<?> instantRecordInternal) {
1592         instantRecordInternal
1593                 .setTime(instantRecordProto.getTime())
1594                 .setZoneOffset(instantRecordProto.getZoneOffset());
1595 
1596         switch (instantRecordProto.getDataCase()) {
1597             case BASAL_BODY_TEMPERATURE ->
1598                     populateBasalBodyTemperatureRecordInternal(
1599                             instantRecordProto.getBasalBodyTemperature(),
1600                             (BasalBodyTemperatureRecordInternal) instantRecordInternal);
1601             case BASAL_METABOLIC_RATE ->
1602                     populateBasalMetabolicRateRecordInternal(
1603                             instantRecordProto.getBasalMetabolicRate(),
1604                             (BasalMetabolicRateRecordInternal) instantRecordInternal);
1605             case BLOOD_GLUCOSE ->
1606                     populateBloodGlucoseRecordInternal(
1607                             instantRecordProto.getBloodGlucose(),
1608                             (BloodGlucoseRecordInternal) instantRecordInternal);
1609             case BLOOD_PRESSURE ->
1610                     populateBloodPressureRecordInternal(
1611                             instantRecordProto.getBloodPressure(),
1612                             (BloodPressureRecordInternal) instantRecordInternal);
1613             case BODY_FAT ->
1614                     populateBodyFatRecordInternal(
1615                             instantRecordProto.getBodyFat(),
1616                             (BodyFatRecordInternal) instantRecordInternal);
1617             case BODY_TEMPERATURE ->
1618                     populateBodyTemperatureRecordInternal(
1619                             instantRecordProto.getBodyTemperature(),
1620                             (BodyTemperatureRecordInternal) instantRecordInternal);
1621             case BODY_WATER_MASS ->
1622                     populateBodyWaterMassRecordInternal(
1623                             instantRecordProto.getBodyWaterMass(),
1624                             (BodyWaterMassRecordInternal) instantRecordInternal);
1625             case BONE_MASS ->
1626                     populateBoneMassRecordInternal(
1627                             instantRecordProto.getBoneMass(),
1628                             (BoneMassRecordInternal) instantRecordInternal);
1629             case CERVICAL_MUCUS ->
1630                     populateCervicalMucusRecordInternal(
1631                             instantRecordProto.getCervicalMucus(),
1632                             (CervicalMucusRecordInternal) instantRecordInternal);
1633             case HEART_RATE_VARIABILITY_RMSSD ->
1634                     populateHeartRateVariabilityRmssdRecordInternal(
1635                             instantRecordProto.getHeartRateVariabilityRmssd(),
1636                             (HeartRateVariabilityRmssdRecordInternal) instantRecordInternal);
1637             case HEIGHT ->
1638                     populateHeightRecordInternal(
1639                             instantRecordProto.getHeight(),
1640                             (HeightRecordInternal) instantRecordInternal);
1641             case INTERMENSTRUAL_BLEEDING -> {
1642                 // No data to populate.
1643             }
1644             case LEAN_BODY_MASS ->
1645                     populateLeanBodyMassRecordInternal(
1646                             instantRecordProto.getLeanBodyMass(),
1647                             (LeanBodyMassRecordInternal) instantRecordInternal);
1648             case MENSTRUATION_FLOW ->
1649                     populateMenstruationFlowRecordInternal(
1650                             instantRecordProto.getMenstruationFlow(),
1651                             (MenstruationFlowRecordInternal) instantRecordInternal);
1652             case OVULATION_TEST ->
1653                     populateOvulationTestRecordInternal(
1654                             instantRecordProto.getOvulationTest(),
1655                             (OvulationTestRecordInternal) instantRecordInternal);
1656             case OXYGEN_SATURATION ->
1657                     populateOxygenSaturationRecordInternal(
1658                             instantRecordProto.getOxygenSaturation(),
1659                             (OxygenSaturationRecordInternal) instantRecordInternal);
1660             case RESPIRATORY_RATE ->
1661                     populateRespiratoryRateRecordInternal(
1662                             instantRecordProto.getRespiratoryRate(),
1663                             (RespiratoryRateRecordInternal) instantRecordInternal);
1664             case RESTING_HEART_RATE ->
1665                     populateRestingHeartRateRecordInternal(
1666                             instantRecordProto.getRestingHeartRate(),
1667                             (RestingHeartRateRecordInternal) instantRecordInternal);
1668             case SEXUAL_ACTIVITY ->
1669                     populateSexualActivityRecordInternal(
1670                             instantRecordProto.getSexualActivity(),
1671                             (SexualActivityRecordInternal) instantRecordInternal);
1672             case VO2_MAX ->
1673                     populateVo2MaxRecordInternal(
1674                             instantRecordProto.getVo2Max(),
1675                             (Vo2MaxRecordInternal) instantRecordInternal);
1676             case WEIGHT ->
1677                     populateWeightRecordInternal(
1678                             instantRecordProto.getWeight(),
1679                             (WeightRecordInternal) instantRecordInternal);
1680             default ->
1681                     throw new IllegalArgumentException(
1682                             "Unknown record type " + instantRecordProto.getDataCase());
1683         }
1684     }
1685 
1686     private static void populateBasalBodyTemperatureRecordInternal(
1687             BasalBodyTemperature basalBodyTemperatureProto,
1688             BasalBodyTemperatureRecordInternal basalBodyTemperatureRecordInternal) {
1689         basalBodyTemperatureRecordInternal
1690                 .setTemperature(basalBodyTemperatureProto.getTemperature())
1691                 .setMeasurementLocation(basalBodyTemperatureProto.getMeasurementLocation());
1692     }
1693 
1694     private static void populateBasalMetabolicRateRecordInternal(
1695             BasalMetabolicRate basalMetabolicRateProto,
1696             BasalMetabolicRateRecordInternal basalMetabolicRateRecordInternal) {
1697         basalMetabolicRateRecordInternal.setBasalMetabolicRate(
1698                 basalMetabolicRateProto.getBasalMetabolicRate());
1699     }
1700 
1701     private static void populateBloodGlucoseRecordInternal(
1702             BloodGlucose bloodGlucoseProto, BloodGlucoseRecordInternal bloodGlucoseRecordInternal) {
1703         bloodGlucoseRecordInternal
1704                 .setSpecimenSource(bloodGlucoseProto.getSpecimenSource())
1705                 .setLevel(bloodGlucoseProto.getLevel())
1706                 .setRelationToMeal(bloodGlucoseProto.getRelationToMeal())
1707                 .setMealType(bloodGlucoseProto.getMealType());
1708     }
1709 
1710     private static void populateBloodPressureRecordInternal(
1711             BloodPressure bloodPressureProto,
1712             BloodPressureRecordInternal bloodPressureRecordInternal) {
1713         bloodPressureRecordInternal
1714                 .setMeasurementLocation(bloodPressureProto.getMeasurementLocation())
1715                 .setSystolic(bloodPressureProto.getSystolic())
1716                 .setDiastolic(bloodPressureProto.getDiastolic())
1717                 .setBodyPosition(bloodPressureProto.getBodyPosition());
1718     }
1719 
1720     private static void populateBodyFatRecordInternal(
1721             BodyFat bodyFatProto, BodyFatRecordInternal bodyFatRecordInternal) {
1722         bodyFatRecordInternal.setPercentage(bodyFatProto.getPercentage());
1723     }
1724 
1725     private static void populateBodyTemperatureRecordInternal(
1726             BodyTemperature bodyTemperatureProto,
1727             BodyTemperatureRecordInternal bodyTemperatureRecordInternal) {
1728         bodyTemperatureRecordInternal
1729                 .setTemperature(bodyTemperatureProto.getTemperature())
1730                 .setMeasurementLocation(bodyTemperatureProto.getMeasurementLocation());
1731     }
1732 
1733     private static void populateBodyWaterMassRecordInternal(
1734             BodyWaterMass bodyWaterMassProto,
1735             BodyWaterMassRecordInternal bodyWaterMassRecordInternal) {
1736         bodyWaterMassRecordInternal.setBodyWaterMass(bodyWaterMassProto.getBodyWaterMass());
1737     }
1738 
1739     private static void populateBoneMassRecordInternal(
1740             BoneMass boneMassProto, BoneMassRecordInternal boneMassRecordInternal) {
1741         boneMassRecordInternal.setMass(boneMassProto.getMass());
1742     }
1743 
1744     private static void populateCervicalMucusRecordInternal(
1745             CervicalMucus cervicalMucusProto,
1746             CervicalMucusRecordInternal cervicalMucusRecordInternal) {
1747         cervicalMucusRecordInternal
1748                 .setSensation(cervicalMucusProto.getSensation())
1749                 .setAppearance(cervicalMucusProto.getAppearance());
1750     }
1751 
1752     private static void populateMenstruationFlowRecordInternal(
1753             MenstruationFlow menstruationFlowProto,
1754             MenstruationFlowRecordInternal menstruationFlowRecordInternal) {
1755         menstruationFlowRecordInternal.setFlow(menstruationFlowProto.getFlow());
1756     }
1757 
1758     private static void populateOvulationTestRecordInternal(
1759             OvulationTest ovulationTestProto,
1760             OvulationTestRecordInternal ovulationTestRecordInternal) {
1761         ovulationTestRecordInternal.setResult(ovulationTestProto.getResult());
1762     }
1763 
1764     private static void populateOxygenSaturationRecordInternal(
1765             OxygenSaturation oxygenSaturationProto,
1766             OxygenSaturationRecordInternal oxygenSaturationRecordInternal) {
1767         oxygenSaturationRecordInternal.setPercentage(oxygenSaturationProto.getPercentage());
1768     }
1769 
1770     private static void populateHeartRateVariabilityRmssdRecordInternal(
1771             HeartRateVariabilityRmssd heartRateVariabilityRmssdProto,
1772             HeartRateVariabilityRmssdRecordInternal heartRateVariabilityRmssdRecordInternal) {
1773         heartRateVariabilityRmssdRecordInternal.setHeartRateVariabilityMillis(
1774                 heartRateVariabilityRmssdProto.getHeartRateVariabilityMillis());
1775     }
1776 
1777     private static void populateHeightRecordInternal(
1778             Height heightProto, HeightRecordInternal heightRecordInternal) {
1779         heightRecordInternal.setHeight(heightProto.getHeight());
1780     }
1781 
1782     private static void populateLeanBodyMassRecordInternal(
1783             LeanBodyMass leanBodyMassProto, LeanBodyMassRecordInternal leanBodyMassRecordInternal) {
1784         leanBodyMassRecordInternal.setMass(leanBodyMassProto.getMass());
1785     }
1786 
1787     private static void populateRespiratoryRateRecordInternal(
1788             RespiratoryRate respiratoryRateProto,
1789             RespiratoryRateRecordInternal respiratoryRateRecordInternal) {
1790         respiratoryRateRecordInternal.setRate(respiratoryRateProto.getRate());
1791     }
1792 
1793     private static void populateRestingHeartRateRecordInternal(
1794             RestingHeartRate restingHeartRateProto,
1795             RestingHeartRateRecordInternal restingHeartRateRecordInternal) {
1796         restingHeartRateRecordInternal.setBeatsPerMinute(restingHeartRateProto.getBeatsPerMinute());
1797     }
1798 
1799     private static void populateSexualActivityRecordInternal(
1800             SexualActivity sexualActivityProto,
1801             SexualActivityRecordInternal sexualActivityRecordInternal) {
1802         sexualActivityRecordInternal.setProtectionUsed(sexualActivityProto.getProtectionUsed());
1803     }
1804 
1805     private static void populateVo2MaxRecordInternal(
1806             Vo2Max vo2MaxProto, Vo2MaxRecordInternal vo2MaxRecordInternal) {
1807         vo2MaxRecordInternal
1808                 .setMeasurementMethod(vo2MaxProto.getMeasurementMethod())
1809                 .setVo2MillilitersPerMinuteKilogram(
1810                         vo2MaxProto.getVo2MillilitersPerMinuteKilogram());
1811     }
1812 
1813     private static void populateWeightRecordInternal(
1814             Weight weightProto, WeightRecordInternal weightRecordInternal) {
1815         weightRecordInternal.setWeight(weightProto.getWeight());
1816     }
1817 
1818     @RecordTypeIdentifier.RecordType
1819     int getRecordTypeId(Record protoRecord) {
1820         if (protoRecord.hasIntervalRecord()) {
1821             return getIntervalRecordId(protoRecord.getIntervalRecord());
1822         }
1823         if (protoRecord.hasInstantRecord()) {
1824             return getInstantRecordId(protoRecord.getInstantRecord());
1825         }
1826         throw new IllegalArgumentException("Record proto doesn't have a sub record defined.");
1827     }
1828 
1829     @RecordTypeIdentifier.RecordType
1830     private int getIntervalRecordId(IntervalRecord protoRecord) {
1831         return switch (protoRecord.getDataCase()) {
1832             case ACTIVE_CALORIES_BURNED -> RecordTypeIdentifier.RECORD_TYPE_ACTIVE_CALORIES_BURNED;
1833             case ACTIVITY_INTENSITY -> RecordTypeIdentifier.RECORD_TYPE_ACTIVITY_INTENSITY;
1834             case CYCLING_PEDALING_CADENCE ->
1835                     RecordTypeIdentifier.RECORD_TYPE_CYCLING_PEDALING_CADENCE;
1836             case DISTANCE -> RecordTypeIdentifier.RECORD_TYPE_DISTANCE;
1837             case ELEVATION_GAINED -> RecordTypeIdentifier.RECORD_TYPE_ELEVATION_GAINED;
1838             case EXERCISE_SESSION -> RecordTypeIdentifier.RECORD_TYPE_EXERCISE_SESSION;
1839             case FLOORS_CLIMBED -> RecordTypeIdentifier.RECORD_TYPE_FLOORS_CLIMBED;
1840             case HEART_RATE -> RecordTypeIdentifier.RECORD_TYPE_HEART_RATE;
1841             case HYDRATION -> RecordTypeIdentifier.RECORD_TYPE_HYDRATION;
1842             case MENSTRUATION_PERIOD -> RecordTypeIdentifier.RECORD_TYPE_MENSTRUATION_PERIOD;
1843             case MINDFULNESS_SESSION -> RecordTypeIdentifier.RECORD_TYPE_MINDFULNESS_SESSION;
1844             case NUTRITION -> RecordTypeIdentifier.RECORD_TYPE_NUTRITION;
1845             case PLANNED_EXERCISE_SESSION ->
1846                     RecordTypeIdentifier.RECORD_TYPE_PLANNED_EXERCISE_SESSION;
1847             case POWER -> RecordTypeIdentifier.RECORD_TYPE_POWER;
1848             case SKIN_TEMPERATURE -> RecordTypeIdentifier.RECORD_TYPE_SKIN_TEMPERATURE;
1849             case SLEEP_SESSION -> RecordTypeIdentifier.RECORD_TYPE_SLEEP_SESSION;
1850             case SPEED -> RecordTypeIdentifier.RECORD_TYPE_SPEED;
1851             case STEPS -> RecordTypeIdentifier.RECORD_TYPE_STEPS;
1852             case STEPS_CADENCE -> RecordTypeIdentifier.RECORD_TYPE_STEPS_CADENCE;
1853             case TOTAL_CALORIES_BURNED -> RecordTypeIdentifier.RECORD_TYPE_TOTAL_CALORIES_BURNED;
1854             case WHEELCHAIR_PUSHES -> RecordTypeIdentifier.RECORD_TYPE_WHEELCHAIR_PUSHES;
1855             case DATA_NOT_SET -> throw new IllegalArgumentException("Interval record not set");
1856         };
1857     }
1858 
1859     @RecordTypeIdentifier.RecordType
1860     private int getInstantRecordId(InstantRecord protoRecord) {
1861         return switch (protoRecord.getDataCase()) {
1862             case BASAL_BODY_TEMPERATURE -> RecordTypeIdentifier.RECORD_TYPE_BASAL_BODY_TEMPERATURE;
1863             case BASAL_METABOLIC_RATE -> RecordTypeIdentifier.RECORD_TYPE_BASAL_METABOLIC_RATE;
1864             case BLOOD_GLUCOSE -> RecordTypeIdentifier.RECORD_TYPE_BLOOD_GLUCOSE;
1865             case BLOOD_PRESSURE -> RecordTypeIdentifier.RECORD_TYPE_BLOOD_PRESSURE;
1866             case BODY_FAT -> RecordTypeIdentifier.RECORD_TYPE_BODY_FAT;
1867             case BODY_TEMPERATURE -> RecordTypeIdentifier.RECORD_TYPE_BODY_TEMPERATURE;
1868             case BODY_WATER_MASS -> RecordTypeIdentifier.RECORD_TYPE_BODY_WATER_MASS;
1869             case BONE_MASS -> RecordTypeIdentifier.RECORD_TYPE_BONE_MASS;
1870             case CERVICAL_MUCUS -> RecordTypeIdentifier.RECORD_TYPE_CERVICAL_MUCUS;
1871             case HEART_RATE_VARIABILITY_RMSSD ->
1872                     RecordTypeIdentifier.RECORD_TYPE_HEART_RATE_VARIABILITY_RMSSD;
1873             case HEIGHT -> RecordTypeIdentifier.RECORD_TYPE_HEIGHT;
1874             case INTERMENSTRUAL_BLEEDING ->
1875                     RecordTypeIdentifier.RECORD_TYPE_INTERMENSTRUAL_BLEEDING;
1876             case LEAN_BODY_MASS -> RecordTypeIdentifier.RECORD_TYPE_LEAN_BODY_MASS;
1877             case MENSTRUATION_FLOW -> RecordTypeIdentifier.RECORD_TYPE_MENSTRUATION_FLOW;
1878             case OVULATION_TEST -> RecordTypeIdentifier.RECORD_TYPE_OVULATION_TEST;
1879             case OXYGEN_SATURATION -> RecordTypeIdentifier.RECORD_TYPE_OXYGEN_SATURATION;
1880             case RESPIRATORY_RATE -> RecordTypeIdentifier.RECORD_TYPE_RESPIRATORY_RATE;
1881             case RESTING_HEART_RATE -> RecordTypeIdentifier.RECORD_TYPE_RESTING_HEART_RATE;
1882             case SEXUAL_ACTIVITY -> RecordTypeIdentifier.RECORD_TYPE_SEXUAL_ACTIVITY;
1883             case VO2_MAX -> RecordTypeIdentifier.RECORD_TYPE_VO2_MAX;
1884             case WEIGHT -> RecordTypeIdentifier.RECORD_TYPE_WEIGHT;
1885             case DATA_NOT_SET -> throw new IllegalArgumentException("Instant record not set");
1886         };
1887     }
1888 }
1889