• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 android.healthconnect.cts;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import android.content.Context;
22 import android.health.connect.DeleteUsingFiltersRequest;
23 import android.health.connect.HealthConnectException;
24 import android.health.connect.ReadRecordsRequestUsingFilters;
25 import android.health.connect.ReadRecordsRequestUsingIds;
26 import android.health.connect.RecordIdFilter;
27 import android.health.connect.TimeInstantRangeFilter;
28 import android.health.connect.changelog.ChangeLogTokenRequest;
29 import android.health.connect.changelog.ChangeLogTokenResponse;
30 import android.health.connect.changelog.ChangeLogsRequest;
31 import android.health.connect.changelog.ChangeLogsResponse;
32 import android.health.connect.datatypes.DataOrigin;
33 import android.health.connect.datatypes.Device;
34 import android.health.connect.datatypes.Metadata;
35 import android.health.connect.datatypes.Record;
36 import android.health.connect.datatypes.SpeedRecord;
37 import android.health.connect.datatypes.units.Velocity;
38 import android.platform.test.annotations.AppModeFull;
39 
40 import androidx.test.core.app.ApplicationProvider;
41 import androidx.test.runner.AndroidJUnit4;
42 
43 import org.junit.After;
44 import org.junit.Assert;
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.time.Instant;
50 import java.time.ZoneOffset;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Collections;
54 import java.util.List;
55 import java.util.UUID;
56 
57 @AppModeFull(reason = "HealthConnectManager is not accessible to instant apps")
58 @RunWith(AndroidJUnit4.class)
59 public class SpeedRecordTest {
60 
61     private static final String TAG = "SpeedRecordTest";
62 
63     @Before
setUp()64     public void setUp() {
65         // TODO(b/283737434): Update the HC code to use user aware context on permission change.
66         // Temporary fix to set firstGrantTime for the correct user in HSUM.
67         TestUtils.deleteAllStagedRemoteData();
68     }
69 
70     @After
tearDown()71     public void tearDown() throws InterruptedException {
72         TestUtils.verifyDeleteRecords(
73                 SpeedRecord.class,
74                 new TimeInstantRangeFilter.Builder()
75                         .setStartTime(Instant.EPOCH)
76                         .setEndTime(Instant.now())
77                         .build());
78         TestUtils.deleteAllStagedRemoteData();
79     }
80 
81     @Test
testInsertSpeedRecord()82     public void testInsertSpeedRecord() throws InterruptedException {
83         TestUtils.insertRecords(Arrays.asList(getBaseSpeedRecord(), getCompleteSpeedRecord()));
84     }
85 
86     @Test
testReadSpeedRecord_usingIds()87     public void testReadSpeedRecord_usingIds() throws InterruptedException {
88         testReadSpeedRecordIds();
89     }
90 
91     @Test
testReadSpeedRecord_invalidIds()92     public void testReadSpeedRecord_invalidIds() throws InterruptedException {
93         ReadRecordsRequestUsingIds<SpeedRecord> request =
94                 new ReadRecordsRequestUsingIds.Builder<>(SpeedRecord.class)
95                         .addId(UUID.randomUUID().toString())
96                         .build();
97         List<SpeedRecord> result = TestUtils.readRecords(request);
98         assertThat(result.size()).isEqualTo(0);
99     }
100 
101     @Test
testReadSpeedRecord_usingClientRecordIds()102     public void testReadSpeedRecord_usingClientRecordIds() throws InterruptedException {
103         List<Record> recordList = Arrays.asList(getCompleteSpeedRecord(), getCompleteSpeedRecord());
104         List<Record> insertedRecords = TestUtils.insertRecords(recordList);
105         readSpeedRecordUsingClientId(insertedRecords);
106     }
107 
108     @Test
testReadSpeedRecord_invalidClientRecordIds()109     public void testReadSpeedRecord_invalidClientRecordIds() throws InterruptedException {
110         ReadRecordsRequestUsingIds<SpeedRecord> request =
111                 new ReadRecordsRequestUsingIds.Builder<>(SpeedRecord.class)
112                         .addClientRecordId("abc")
113                         .build();
114         List<SpeedRecord> result = TestUtils.readRecords(request);
115         assertThat(result.size()).isEqualTo(0);
116     }
117 
118     @Test
testReadSpeedRecordUsingFilters_default()119     public void testReadSpeedRecordUsingFilters_default() throws InterruptedException {
120         List<SpeedRecord> oldSpeedRecords =
121                 TestUtils.readRecords(
122                         new ReadRecordsRequestUsingFilters.Builder<>(SpeedRecord.class).build());
123         SpeedRecord testRecord = getCompleteSpeedRecord();
124         TestUtils.insertRecords(Collections.singletonList(testRecord));
125         List<SpeedRecord> newSpeedRecords =
126                 TestUtils.readRecords(
127                         new ReadRecordsRequestUsingFilters.Builder<>(SpeedRecord.class).build());
128         assertThat(newSpeedRecords.size()).isEqualTo(oldSpeedRecords.size() + 1);
129         assertThat(newSpeedRecords.get(newSpeedRecords.size() - 1).equals(testRecord)).isTrue();
130     }
131 
132     @Test
testReadSpeedRecordUsingFilters_timeFilter()133     public void testReadSpeedRecordUsingFilters_timeFilter() throws InterruptedException {
134         TimeInstantRangeFilter filter =
135                 new TimeInstantRangeFilter.Builder()
136                         .setStartTime(Instant.now())
137                         .setEndTime(Instant.now().plusMillis(3000))
138                         .build();
139         SpeedRecord testRecord = getCompleteSpeedRecord();
140         TestUtils.insertRecords(Collections.singletonList(testRecord));
141         List<SpeedRecord> newSpeedRecords =
142                 TestUtils.readRecords(
143                         new ReadRecordsRequestUsingFilters.Builder<>(SpeedRecord.class)
144                                 .setTimeRangeFilter(filter)
145                                 .build());
146         assertThat(newSpeedRecords.size()).isEqualTo(1);
147         assertThat(newSpeedRecords.get(newSpeedRecords.size() - 1).equals(testRecord)).isTrue();
148     }
149 
150     @Test
testReadSpeedRecordUsingFilters_dataFilter_correct()151     public void testReadSpeedRecordUsingFilters_dataFilter_correct() throws InterruptedException {
152         Context context = ApplicationProvider.getApplicationContext();
153         List<SpeedRecord> oldSpeedRecords =
154                 TestUtils.readRecords(
155                         new ReadRecordsRequestUsingFilters.Builder<>(SpeedRecord.class)
156                                 .addDataOrigins(
157                                         new DataOrigin.Builder()
158                                                 .setPackageName(context.getPackageName())
159                                                 .build())
160                                 .build());
161         SpeedRecord testRecord = getCompleteSpeedRecord();
162         TestUtils.insertRecords(Collections.singletonList(testRecord));
163         List<SpeedRecord> newSpeedRecords =
164                 TestUtils.readRecords(
165                         new ReadRecordsRequestUsingFilters.Builder<>(SpeedRecord.class)
166                                 .addDataOrigins(
167                                         new DataOrigin.Builder()
168                                                 .setPackageName(context.getPackageName())
169                                                 .build())
170                                 .build());
171         assertThat(newSpeedRecords.size() - oldSpeedRecords.size()).isEqualTo(1);
172         assertThat(newSpeedRecords.get(newSpeedRecords.size() - 1).equals(testRecord)).isTrue();
173         SpeedRecord newRecord = newSpeedRecords.get(newSpeedRecords.size() - 1);
174         assertThat(newRecord.equals(testRecord)).isTrue();
175         for (int idx = 0; idx < newRecord.getSamples().size(); idx++) {
176             assertThat(newRecord.getSamples().get(idx).getTime().toEpochMilli())
177                     .isEqualTo(testRecord.getSamples().get(idx).getTime().toEpochMilli());
178             assertThat(newRecord.getSamples().get(idx).getSpeed())
179                     .isEqualTo(testRecord.getSamples().get(idx).getSpeed());
180         }
181     }
182 
183     @Test
testReadSpeedRecordUsingFilters_dataFilter_incorrect()184     public void testReadSpeedRecordUsingFilters_dataFilter_incorrect() throws InterruptedException {
185         TestUtils.insertRecords(Collections.singletonList(getCompleteSpeedRecord()));
186         List<SpeedRecord> newSpeedRecords =
187                 TestUtils.readRecords(
188                         new ReadRecordsRequestUsingFilters.Builder<>(SpeedRecord.class)
189                                 .addDataOrigins(
190                                         new DataOrigin.Builder().setPackageName("abc").build())
191                                 .build());
192         assertThat(newSpeedRecords.size()).isEqualTo(0);
193     }
194 
195     @Test
testDeleteSpeedRecord_no_filters()196     public void testDeleteSpeedRecord_no_filters() throws InterruptedException {
197         String id = TestUtils.insertRecordAndGetId(getCompleteSpeedRecord());
198         TestUtils.verifyDeleteRecords(new DeleteUsingFiltersRequest.Builder().build());
199         TestUtils.assertRecordNotFound(id, SpeedRecord.class);
200     }
201 
202     @Test
testDeleteSpeedRecord_time_filters()203     public void testDeleteSpeedRecord_time_filters() throws InterruptedException {
204         TimeInstantRangeFilter timeRangeFilter =
205                 new TimeInstantRangeFilter.Builder()
206                         .setStartTime(Instant.now())
207                         .setEndTime(Instant.now().plusMillis(1000))
208                         .build();
209         String id = TestUtils.insertRecordAndGetId(getCompleteSpeedRecord());
210         TestUtils.verifyDeleteRecords(
211                 new DeleteUsingFiltersRequest.Builder()
212                         .addRecordType(SpeedRecord.class)
213                         .setTimeRangeFilter(timeRangeFilter)
214                         .build());
215         TestUtils.assertRecordNotFound(id, SpeedRecord.class);
216     }
217 
218     @Test
testDeleteSpeedRecord_recordId_filters()219     public void testDeleteSpeedRecord_recordId_filters() throws InterruptedException {
220         List<Record> records = List.of(getBaseSpeedRecord(), getCompleteSpeedRecord());
221         TestUtils.insertRecords(records);
222 
223         for (Record record : records) {
224             TestUtils.verifyDeleteRecords(
225                     new DeleteUsingFiltersRequest.Builder()
226                             .addRecordType(record.getClass())
227                             .build());
228             TestUtils.assertRecordNotFound(record.getMetadata().getId(), record.getClass());
229         }
230     }
231 
232     @Test
testDeleteSpeedRecord_dataOrigin_filters()233     public void testDeleteSpeedRecord_dataOrigin_filters() throws InterruptedException {
234         Context context = ApplicationProvider.getApplicationContext();
235         String id = TestUtils.insertRecordAndGetId(getCompleteSpeedRecord());
236         TestUtils.verifyDeleteRecords(
237                 new DeleteUsingFiltersRequest.Builder()
238                         .addDataOrigin(
239                                 new DataOrigin.Builder()
240                                         .setPackageName(context.getPackageName())
241                                         .build())
242                         .build());
243         TestUtils.assertRecordNotFound(id, SpeedRecord.class);
244     }
245 
246     @Test
testDeleteSpeedRecord_dataOrigin_filter_incorrect()247     public void testDeleteSpeedRecord_dataOrigin_filter_incorrect() throws InterruptedException {
248         String id = TestUtils.insertRecordAndGetId(getCompleteSpeedRecord());
249         TestUtils.verifyDeleteRecords(
250                 new DeleteUsingFiltersRequest.Builder()
251                         .addDataOrigin(new DataOrigin.Builder().setPackageName("abc").build())
252                         .build());
253         TestUtils.assertRecordFound(id, SpeedRecord.class);
254     }
255 
256     @Test
testDeleteSpeedRecord_usingIds()257     public void testDeleteSpeedRecord_usingIds() throws InterruptedException {
258         List<Record> records = List.of(getBaseSpeedRecord(), getCompleteSpeedRecord());
259         List<Record> insertedRecord = TestUtils.insertRecords(records);
260         List<RecordIdFilter> recordIds = new ArrayList<>(records.size());
261         for (Record record : insertedRecord) {
262             recordIds.add(RecordIdFilter.fromId(record.getClass(), record.getMetadata().getId()));
263         }
264 
265         TestUtils.verifyDeleteRecords(recordIds);
266         for (Record record : records) {
267             TestUtils.assertRecordNotFound(record.getMetadata().getId(), record.getClass());
268         }
269     }
270 
271     @Test
testDeleteSpeedRecord_time_range()272     public void testDeleteSpeedRecord_time_range() throws InterruptedException {
273         TimeInstantRangeFilter timeRangeFilter =
274                 new TimeInstantRangeFilter.Builder()
275                         .setStartTime(Instant.now())
276                         .setEndTime(Instant.now().plusMillis(1000))
277                         .build();
278         String id = TestUtils.insertRecordAndGetId(getCompleteSpeedRecord());
279         TestUtils.verifyDeleteRecords(SpeedRecord.class, timeRangeFilter);
280         TestUtils.assertRecordNotFound(id, SpeedRecord.class);
281     }
282 
283     @Test
testZoneOffsets()284     public void testZoneOffsets() {
285         final ZoneOffset defaultZoneOffset =
286                 ZoneOffset.systemDefault().getRules().getOffset(Instant.now());
287         final ZoneOffset startZoneOffset = ZoneOffset.UTC;
288         final ZoneOffset endZoneOffset = ZoneOffset.MAX;
289         SpeedRecord.Builder builder =
290                 new SpeedRecord.Builder(
291                         new Metadata.Builder().build(),
292                         Instant.now(),
293                         Instant.now().plusMillis(1000),
294                         Collections.emptyList());
295 
296         assertThat(builder.setStartZoneOffset(startZoneOffset).build().getStartZoneOffset())
297                 .isEqualTo(startZoneOffset);
298         assertThat(builder.setEndZoneOffset(endZoneOffset).build().getEndZoneOffset())
299                 .isEqualTo(endZoneOffset);
300         assertThat(builder.clearStartZoneOffset().build().getStartZoneOffset())
301                 .isEqualTo(defaultZoneOffset);
302         assertThat(builder.clearEndZoneOffset().build().getEndZoneOffset())
303                 .isEqualTo(defaultZoneOffset);
304     }
305 
306     @Test
testInsertAndDeleteRecord_changelogs()307     public void testInsertAndDeleteRecord_changelogs() throws InterruptedException {
308         Context context = ApplicationProvider.getApplicationContext();
309         ChangeLogTokenResponse tokenResponse =
310                 TestUtils.getChangeLogToken(
311                         new ChangeLogTokenRequest.Builder()
312                                 .addDataOriginFilter(
313                                         new DataOrigin.Builder()
314                                                 .setPackageName(context.getPackageName())
315                                                 .build())
316                                 .addRecordType(SpeedRecord.class)
317                                 .build());
318         ChangeLogsRequest changeLogsRequest =
319                 new ChangeLogsRequest.Builder(tokenResponse.getToken()).build();
320         ChangeLogsResponse response = TestUtils.getChangeLogs(changeLogsRequest);
321         assertThat(response.getUpsertedRecords().size()).isEqualTo(0);
322         assertThat(response.getDeletedLogs().size()).isEqualTo(0);
323 
324         List<Record> testRecord = Collections.singletonList(getCompleteSpeedRecord());
325         TestUtils.insertRecords(testRecord);
326         response = TestUtils.getChangeLogs(changeLogsRequest);
327         assertThat(response.getUpsertedRecords().size()).isEqualTo(1);
328         assertThat(
329                         response.getUpsertedRecords().stream()
330                                 .map(Record::getMetadata)
331                                 .map(Metadata::getId)
332                                 .toList())
333                 .containsExactlyElementsIn(
334                         testRecord.stream().map(Record::getMetadata).map(Metadata::getId).toList());
335         assertThat(response.getDeletedLogs().size()).isEqualTo(0);
336 
337         TestUtils.verifyDeleteRecords(
338                 new DeleteUsingFiltersRequest.Builder().addRecordType(SpeedRecord.class).build());
339         response = TestUtils.getChangeLogs(changeLogsRequest);
340         assertThat(response.getDeletedLogs()).isEmpty();
341     }
342 
testReadSpeedRecordIds()343     private void testReadSpeedRecordIds() throws InterruptedException {
344         List<Record> recordList = Arrays.asList(getCompleteSpeedRecord(), getCompleteSpeedRecord());
345         readSpeedRecordUsingIds(recordList);
346     }
347 
readSpeedRecordUsingClientId(List<Record> insertedRecord)348     private void readSpeedRecordUsingClientId(List<Record> insertedRecord)
349             throws InterruptedException {
350         ReadRecordsRequestUsingIds.Builder<SpeedRecord> request =
351                 new ReadRecordsRequestUsingIds.Builder<>(SpeedRecord.class);
352         for (Record record : insertedRecord) {
353             request.addClientRecordId(record.getMetadata().getClientRecordId());
354         }
355         List<SpeedRecord> result = TestUtils.readRecords(request.build());
356         assertThat(result.size()).isEqualTo(insertedRecord.size());
357         assertThat(result).containsExactlyElementsIn(insertedRecord);
358     }
359 
readSpeedRecordUsingIds(List<Record> recordList)360     private void readSpeedRecordUsingIds(List<Record> recordList) throws InterruptedException {
361         List<Record> insertedRecords = TestUtils.insertRecords(recordList);
362         ReadRecordsRequestUsingIds.Builder<SpeedRecord> request =
363                 new ReadRecordsRequestUsingIds.Builder<>(SpeedRecord.class);
364         for (Record record : insertedRecords) {
365             request.addId(record.getMetadata().getId());
366         }
367         ReadRecordsRequestUsingIds requestUsingIds = request.build();
368         assertThat(requestUsingIds.getRecordType()).isEqualTo(SpeedRecord.class);
369         assertThat(requestUsingIds.getRecordIdFilters()).isNotNull();
370         List<SpeedRecord> result = TestUtils.readRecords(requestUsingIds);
371         assertThat(result).hasSize(insertedRecords.size());
372         assertThat(result.containsAll(insertedRecords)).isTrue();
373     }
374 
375     @Test(expected = IllegalArgumentException.class)
testCreateSpeedRecord_invalidValue()376     public void testCreateSpeedRecord_invalidValue() {
377         new SpeedRecord.SpeedRecordSample(
378                 Velocity.fromMetersPerSecond(1000001), Instant.now().plusMillis(100));
379     }
380 
381     @Test(expected = IllegalArgumentException.class)
testCreateSpeedRecord_invalidSampleTime()382     public void testCreateSpeedRecord_invalidSampleTime() {
383         Instant startTime = Instant.now();
384         Instant endTime = startTime.plusMillis(100);
385         SpeedRecord.SpeedRecordSample speedRecord =
386                 new SpeedRecord.SpeedRecordSample(
387                         Velocity.fromMetersPerSecond(10.0), endTime.plusMillis(1));
388         ArrayList<SpeedRecord.SpeedRecordSample> speedRecords = new ArrayList<>();
389         speedRecords.add(speedRecord);
390         new SpeedRecord.Builder(new Metadata.Builder().build(), startTime, endTime, speedRecords)
391                 .build();
392     }
393 
394     @Test
testUpdateRecords_validInput_dataBaseUpdatedSuccessfully()395     public void testUpdateRecords_validInput_dataBaseUpdatedSuccessfully()
396             throws InterruptedException {
397 
398         List<Record> insertedRecords =
399                 TestUtils.insertRecords(
400                         Arrays.asList(getCompleteSpeedRecord(), getCompleteSpeedRecord()));
401 
402         // read inserted records and verify that the data is same as inserted.
403         readSpeedRecordUsingIds(insertedRecords);
404 
405         // Generate a new set of records that will be used to perform the update operation.
406         List<Record> updateRecords =
407                 Arrays.asList(getCompleteSpeedRecord(), getCompleteSpeedRecord());
408 
409         // Modify the uid of the updateRecords to the uuid that was present in the insert records.
410         for (int itr = 0; itr < updateRecords.size(); itr++) {
411             updateRecords.set(
412                     itr,
413                     getSpeedRecord_update(
414                             updateRecords.get(itr),
415                             insertedRecords.get(itr).getMetadata().getId(),
416                             insertedRecords.get(itr).getMetadata().getClientRecordId()));
417         }
418 
419         TestUtils.updateRecords(updateRecords);
420 
421         // assert the inserted data has been modified by reading the data.
422         readSpeedRecordUsingIds(updateRecords);
423     }
424 
425     @Test
testUpdateRecords_invalidInputRecords_noChangeInDataBase()426     public void testUpdateRecords_invalidInputRecords_noChangeInDataBase()
427             throws InterruptedException {
428         List<Record> insertedRecords =
429                 TestUtils.insertRecords(
430                         Arrays.asList(getCompleteSpeedRecord(), getCompleteSpeedRecord()));
431 
432         // read inserted records and verify that the data is same as inserted.
433         readSpeedRecordUsingIds(insertedRecords);
434 
435         // Generate a second set of records that will be used to perform the update operation.
436         List<Record> updateRecords =
437                 Arrays.asList(getCompleteSpeedRecord(), getCompleteSpeedRecord());
438 
439         // Modify the Uid of the updateRecords to the UUID that was present in the insert records,
440         // leaving out alternate records so that they have a new UUID which is not present in the
441         // dataBase.
442         for (int itr = 0; itr < updateRecords.size(); itr++) {
443             updateRecords.set(
444                     itr,
445                     getSpeedRecord_update(
446                             updateRecords.get(itr),
447                             itr % 2 == 0
448                                     ? insertedRecords.get(itr).getMetadata().getId()
449                                     : UUID.randomUUID().toString(),
450                             itr % 2 == 0
451                                     ? insertedRecords.get(itr).getMetadata().getId()
452                                     : UUID.randomUUID().toString()));
453         }
454 
455         try {
456             TestUtils.updateRecords(updateRecords);
457             Assert.fail("Expected to fail due to invalid records ids.");
458         } catch (HealthConnectException exception) {
459             assertThat(exception.getErrorCode())
460                     .isEqualTo(HealthConnectException.ERROR_INVALID_ARGUMENT);
461         }
462 
463         // assert the inserted data has not been modified by reading the data.
464         readSpeedRecordUsingIds(insertedRecords);
465     }
466 
467     @Test
testUpdateRecords_recordWithInvalidPackageName_noChangeInDataBase()468     public void testUpdateRecords_recordWithInvalidPackageName_noChangeInDataBase()
469             throws InterruptedException {
470         List<Record> insertedRecords =
471                 TestUtils.insertRecords(
472                         Arrays.asList(getCompleteSpeedRecord(), getCompleteSpeedRecord()));
473 
474         // read inserted records and verify that the data is same as inserted.
475         readSpeedRecordUsingIds(insertedRecords);
476 
477         // Generate a second set of records that will be used to perform the update operation.
478         List<Record> updateRecords =
479                 Arrays.asList(getCompleteSpeedRecord(), getCompleteSpeedRecord());
480 
481         // Modify the Uuid of the updateRecords to the uuid that was present in the insert records.
482         for (int itr = 0; itr < updateRecords.size(); itr++) {
483             updateRecords.set(
484                     itr,
485                     getSpeedRecord_update(
486                             updateRecords.get(itr),
487                             insertedRecords.get(itr).getMetadata().getId(),
488                             insertedRecords.get(itr).getMetadata().getClientRecordId()));
489             //             adding an entry with invalid packageName.
490             updateRecords.set(itr, getCompleteSpeedRecord());
491         }
492 
493         try {
494             TestUtils.updateRecords(updateRecords);
495             Assert.fail("Expected to fail due to invalid package.");
496         } catch (Exception exception) {
497             // verify that the testcase failed due to invalid argument exception.
498             assertThat(exception).isNotNull();
499         }
500 
501         // assert the inserted data has not been modified by reading the data.
502         readSpeedRecordUsingIds(insertedRecords);
503     }
504 
getSpeedRecord_update(Record record, String id, String clientRecordId)505     SpeedRecord getSpeedRecord_update(Record record, String id, String clientRecordId) {
506         Metadata metadata = record.getMetadata();
507         Metadata metadataWithId =
508                 new Metadata.Builder()
509                         .setId(id)
510                         .setClientRecordId(clientRecordId)
511                         .setClientRecordVersion(metadata.getClientRecordVersion())
512                         .setDataOrigin(metadata.getDataOrigin())
513                         .setDevice(metadata.getDevice())
514                         .setLastModifiedTime(metadata.getLastModifiedTime())
515                         .build();
516 
517         SpeedRecord.SpeedRecordSample speedRecordSample =
518                 new SpeedRecord.SpeedRecordSample(
519                         Velocity.fromMetersPerSecond(8.0), Instant.now().plusMillis(100));
520 
521         return new SpeedRecord.Builder(
522                         metadataWithId,
523                         Instant.now(),
524                         Instant.now().plusMillis(2000),
525                         List.of(speedRecordSample, speedRecordSample))
526                 .setStartZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now()))
527                 .setEndZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now()))
528                 .build();
529     }
530 
getBaseSpeedRecord()531     private static SpeedRecord getBaseSpeedRecord() {
532         SpeedRecord.SpeedRecordSample speedRecord =
533                 new SpeedRecord.SpeedRecordSample(
534                         Velocity.fromMetersPerSecond(10.0), Instant.now().plusMillis(100));
535         ArrayList<SpeedRecord.SpeedRecordSample> speedRecords = new ArrayList<>();
536         speedRecords.add(speedRecord);
537         speedRecords.add(speedRecord);
538 
539         return new SpeedRecord.Builder(
540                         new Metadata.Builder().build(),
541                         Instant.now(),
542                         Instant.now().plusMillis(1000),
543                         speedRecords)
544                 .build();
545     }
546 
getCompleteSpeedRecord()547     private static SpeedRecord getCompleteSpeedRecord() {
548 
549         Device device =
550                 new Device.Builder()
551                         .setManufacturer("google")
552                         .setModel("Pixel4a")
553                         .setType(2)
554                         .build();
555         DataOrigin dataOrigin =
556                 new DataOrigin.Builder().setPackageName("android.healthconnect.cts").build();
557         Metadata.Builder testMetadataBuilder = new Metadata.Builder();
558         testMetadataBuilder.setDevice(device).setDataOrigin(dataOrigin);
559         testMetadataBuilder.setClientRecordId("SPR" + Math.random());
560         testMetadataBuilder.setRecordingMethod(Metadata.RECORDING_METHOD_ACTIVELY_RECORDED);
561 
562         SpeedRecord.SpeedRecordSample speedRecord =
563                 new SpeedRecord.SpeedRecordSample(
564                         Velocity.fromMetersPerSecond(10.0), Instant.now().plusMillis(100));
565 
566         ArrayList<SpeedRecord.SpeedRecordSample> speedRecords = new ArrayList<>();
567         speedRecords.add(speedRecord);
568         speedRecords.add(speedRecord);
569 
570         return new SpeedRecord.Builder(
571                         testMetadataBuilder.build(),
572                         Instant.now(),
573                         Instant.now().plusMillis(1000),
574                         speedRecords)
575                 .build();
576     }
577 }
578