• 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 android.health.connect.HealthDataCategory.ACTIVITY;
20 import static android.health.connect.HealthDataCategory.BODY_MEASUREMENTS;
21 import static android.health.connect.HealthDataCategory.CYCLE_TRACKING;
22 import static android.health.connect.HealthDataCategory.NUTRITION;
23 import static android.health.connect.HealthDataCategory.SLEEP;
24 import static android.health.connect.HealthDataCategory.VITALS;
25 import static android.health.connect.HealthPermissionCategory.BASAL_METABOLIC_RATE;
26 import static android.health.connect.HealthPermissionCategory.EXERCISE;
27 import static android.health.connect.HealthPermissionCategory.HEART_RATE;
28 import static android.health.connect.HealthPermissionCategory.STEPS;
29 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_BASAL_METABOLIC_RATE;
30 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_HEART_RATE;
31 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_STEPS;
32 
33 import static com.android.compatibility.common.util.FeatureUtil.AUTOMOTIVE_FEATURE;
34 import static com.android.compatibility.common.util.FeatureUtil.hasSystemFeature;
35 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
36 
37 import static com.google.common.truth.Truth.assertThat;
38 
39 import android.app.UiAutomation;
40 import android.content.Context;
41 import android.health.connect.AggregateRecordsGroupedByDurationResponse;
42 import android.health.connect.AggregateRecordsGroupedByPeriodResponse;
43 import android.health.connect.AggregateRecordsRequest;
44 import android.health.connect.AggregateRecordsResponse;
45 import android.health.connect.ApplicationInfoResponse;
46 import android.health.connect.DeleteUsingFiltersRequest;
47 import android.health.connect.HealthConnectDataState;
48 import android.health.connect.HealthConnectException;
49 import android.health.connect.HealthConnectManager;
50 import android.health.connect.HealthPermissionCategory;
51 import android.health.connect.HealthPermissions;
52 import android.health.connect.InsertRecordsResponse;
53 import android.health.connect.ReadRecordsRequest;
54 import android.health.connect.ReadRecordsRequestUsingIds;
55 import android.health.connect.ReadRecordsResponse;
56 import android.health.connect.RecordIdFilter;
57 import android.health.connect.RecordTypeInfoResponse;
58 import android.health.connect.TimeInstantRangeFilter;
59 import android.health.connect.accesslog.AccessLog;
60 import android.health.connect.changelog.ChangeLogTokenRequest;
61 import android.health.connect.changelog.ChangeLogTokenResponse;
62 import android.health.connect.changelog.ChangeLogsRequest;
63 import android.health.connect.changelog.ChangeLogsResponse;
64 import android.health.connect.datatypes.ActiveCaloriesBurnedRecord;
65 import android.health.connect.datatypes.AppInfo;
66 import android.health.connect.datatypes.BasalBodyTemperatureRecord;
67 import android.health.connect.datatypes.BasalMetabolicRateRecord;
68 import android.health.connect.datatypes.BloodGlucoseRecord;
69 import android.health.connect.datatypes.BloodPressureRecord;
70 import android.health.connect.datatypes.BodyFatRecord;
71 import android.health.connect.datatypes.BodyTemperatureRecord;
72 import android.health.connect.datatypes.BodyWaterMassRecord;
73 import android.health.connect.datatypes.BoneMassRecord;
74 import android.health.connect.datatypes.CervicalMucusRecord;
75 import android.health.connect.datatypes.CyclingPedalingCadenceRecord;
76 import android.health.connect.datatypes.DataOrigin;
77 import android.health.connect.datatypes.Device;
78 import android.health.connect.datatypes.DistanceRecord;
79 import android.health.connect.datatypes.ElevationGainedRecord;
80 import android.health.connect.datatypes.ExerciseLap;
81 import android.health.connect.datatypes.ExerciseRoute;
82 import android.health.connect.datatypes.ExerciseSegment;
83 import android.health.connect.datatypes.ExerciseSegmentType;
84 import android.health.connect.datatypes.ExerciseSessionRecord;
85 import android.health.connect.datatypes.ExerciseSessionType;
86 import android.health.connect.datatypes.FloorsClimbedRecord;
87 import android.health.connect.datatypes.HeartRateRecord;
88 import android.health.connect.datatypes.HeartRateVariabilityRmssdRecord;
89 import android.health.connect.datatypes.HeightRecord;
90 import android.health.connect.datatypes.HydrationRecord;
91 import android.health.connect.datatypes.IntermenstrualBleedingRecord;
92 import android.health.connect.datatypes.LeanBodyMassRecord;
93 import android.health.connect.datatypes.MenstruationFlowRecord;
94 import android.health.connect.datatypes.MenstruationPeriodRecord;
95 import android.health.connect.datatypes.Metadata;
96 import android.health.connect.datatypes.NutritionRecord;
97 import android.health.connect.datatypes.OvulationTestRecord;
98 import android.health.connect.datatypes.OxygenSaturationRecord;
99 import android.health.connect.datatypes.PowerRecord;
100 import android.health.connect.datatypes.Record;
101 import android.health.connect.datatypes.RespiratoryRateRecord;
102 import android.health.connect.datatypes.RestingHeartRateRecord;
103 import android.health.connect.datatypes.SexualActivityRecord;
104 import android.health.connect.datatypes.SleepSessionRecord;
105 import android.health.connect.datatypes.SpeedRecord;
106 import android.health.connect.datatypes.StepsCadenceRecord;
107 import android.health.connect.datatypes.StepsRecord;
108 import android.health.connect.datatypes.TotalCaloriesBurnedRecord;
109 import android.health.connect.datatypes.Vo2MaxRecord;
110 import android.health.connect.datatypes.WeightRecord;
111 import android.health.connect.datatypes.WheelchairPushesRecord;
112 import android.health.connect.datatypes.units.Length;
113 import android.health.connect.datatypes.units.Power;
114 import android.health.connect.migration.MigrationException;
115 import android.os.OutcomeReceiver;
116 import android.util.Log;
117 import android.util.Pair;
118 
119 import androidx.annotation.NonNull;
120 import androidx.test.core.app.ApplicationProvider;
121 import androidx.test.platform.app.InstrumentationRegistry;
122 
123 import java.time.Duration;
124 import java.time.Instant;
125 import java.time.LocalDate;
126 import java.time.Period;
127 import java.time.ZoneOffset;
128 import java.time.temporal.ChronoUnit;
129 import java.util.ArrayList;
130 import java.util.Arrays;
131 import java.util.Collections;
132 import java.util.HashMap;
133 import java.util.List;
134 import java.util.Map;
135 import java.util.UUID;
136 import java.util.concurrent.CountDownLatch;
137 import java.util.concurrent.Executors;
138 import java.util.concurrent.TimeUnit;
139 import java.util.concurrent.atomic.AtomicReference;
140 import java.util.stream.Collectors;
141 
142 public class TestUtils {
143     public static final String MANAGE_HEALTH_DATA = HealthPermissions.MANAGE_HEALTH_DATA_PERMISSION;
144     public static final Instant SESSION_START_TIME = Instant.now().minus(10, ChronoUnit.DAYS);
145     public static final Instant SESSION_END_TIME =
146             Instant.now().minus(10, ChronoUnit.DAYS).plus(1, ChronoUnit.HOURS);
147     private static final String TAG = "HCTestUtils";
148     private static final int TIMEOUT_SECONDS = 5;
149 
isHardwareAutomotive()150     public static boolean isHardwareAutomotive() {
151         return hasSystemFeature(AUTOMOTIVE_FEATURE);
152     }
153 
getChangeLogToken(ChangeLogTokenRequest request)154     public static ChangeLogTokenResponse getChangeLogToken(ChangeLogTokenRequest request)
155             throws InterruptedException {
156         Context context = ApplicationProvider.getApplicationContext();
157         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
158         assertThat(service).isNotNull();
159         CountDownLatch latch = new CountDownLatch(1);
160         AtomicReference<ChangeLogTokenResponse> response = new AtomicReference<>();
161         AtomicReference<HealthConnectException> exceptionAtomicReference = new AtomicReference<>();
162         service.getChangeLogToken(
163                 request,
164                 Executors.newSingleThreadExecutor(),
165                 new OutcomeReceiver<>() {
166                     @Override
167                     public void onResult(ChangeLogTokenResponse result) {
168                         response.set(result);
169                         latch.countDown();
170                     }
171 
172                     @Override
173                     public void onError(HealthConnectException exception) {
174                         Log.e(TAG, exception.getMessage());
175                         exceptionAtomicReference.set(exception);
176                         latch.countDown();
177                     }
178                 });
179         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
180         if (exceptionAtomicReference.get() != null) {
181             throw exceptionAtomicReference.get();
182         }
183         return response.get();
184     }
185 
insertRecordAndGetId(Record record)186     public static String insertRecordAndGetId(Record record) throws InterruptedException {
187         return insertRecords(Collections.singletonList(record)).get(0).getMetadata().getId();
188     }
189 
190     /**
191      * Inserts records to the database.
192      *
193      * @param records records to insert
194      * @return inserted records
195      */
insertRecords(List<Record> records)196     public static List<Record> insertRecords(List<Record> records) throws InterruptedException {
197         Context context = ApplicationProvider.getApplicationContext();
198         CountDownLatch latch = new CountDownLatch(1);
199         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
200         assertThat(service).isNotNull();
201         AtomicReference<List<Record>> response = new AtomicReference<>();
202         AtomicReference<HealthConnectException> exceptionAtomicReference = new AtomicReference<>();
203         service.insertRecords(
204                 records,
205                 Executors.newSingleThreadExecutor(),
206                 new OutcomeReceiver<>() {
207                     @Override
208                     public void onResult(InsertRecordsResponse result) {
209                         response.set(result.getRecords());
210                         latch.countDown();
211                     }
212 
213                     @Override
214                     public void onError(HealthConnectException exception) {
215                         exceptionAtomicReference.set(exception);
216                         latch.countDown();
217                     }
218                 });
219         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
220         if (exceptionAtomicReference.get() != null) {
221             throw exceptionAtomicReference.get();
222         }
223         assertThat(response.get()).hasSize(records.size());
224 
225         return response.get();
226     }
227 
updateRecords(List<Record> records)228     public static void updateRecords(List<Record> records) throws InterruptedException {
229         Context context = ApplicationProvider.getApplicationContext();
230         CountDownLatch latch = new CountDownLatch(1);
231         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
232         assertThat(service).isNotNull();
233         AtomicReference<HealthConnectException> exceptionAtomicReference = new AtomicReference<>();
234         service.updateRecords(
235                 records,
236                 Executors.newSingleThreadExecutor(),
237                 new OutcomeReceiver<>() {
238                     @Override
239                     public void onResult(Void result) {
240                         latch.countDown();
241                     }
242 
243                     @Override
244                     public void onError(HealthConnectException exception) {
245                         exceptionAtomicReference.set(exception);
246                         latch.countDown();
247                     }
248                 });
249         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
250         if (exceptionAtomicReference.get() != null) {
251             throw exceptionAtomicReference.get();
252         }
253     }
254 
getChangeLogs(ChangeLogsRequest changeLogsRequest)255     public static ChangeLogsResponse getChangeLogs(ChangeLogsRequest changeLogsRequest)
256             throws InterruptedException {
257         Context context = ApplicationProvider.getApplicationContext();
258         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
259         assertThat(service).isNotNull();
260 
261         CountDownLatch latch = new CountDownLatch(1);
262         AtomicReference<ChangeLogsResponse> response = new AtomicReference<>();
263         AtomicReference<HealthConnectException> healthConnectExceptionAtomicReference =
264                 new AtomicReference<>();
265         service.getChangeLogs(
266                 changeLogsRequest,
267                 Executors.newSingleThreadExecutor(),
268                 new OutcomeReceiver<>() {
269                     @Override
270                     public void onResult(ChangeLogsResponse result) {
271                         response.set(result);
272                         latch.countDown();
273                     }
274 
275                     @Override
276                     public void onError(HealthConnectException exception) {
277                         healthConnectExceptionAtomicReference.set(exception);
278                         latch.countDown();
279                     }
280                 });
281         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
282         if (healthConnectExceptionAtomicReference.get() != null) {
283             throw healthConnectExceptionAtomicReference.get();
284         }
285 
286         return response.get();
287     }
288 
buildDevice()289     public static Device buildDevice() {
290         return new Device.Builder()
291                 .setManufacturer("google")
292                 .setModel("Pixel4a")
293                 .setType(2)
294                 .build();
295     }
296 
getTestRecords()297     public static List<Record> getTestRecords() {
298         return Arrays.asList(
299                 getStepsRecord(),
300                 getHeartRateRecord(),
301                 getBasalMetabolicRateRecord(),
302                 buildExerciseSession());
303     }
304 
getRecordsAndIdentifiers()305     public static List<RecordAndIdentifier> getRecordsAndIdentifiers() {
306         return Arrays.asList(
307                 new RecordAndIdentifier(RECORD_TYPE_STEPS, getStepsRecord()),
308                 new RecordAndIdentifier(RECORD_TYPE_HEART_RATE, getHeartRateRecord()),
309                 new RecordAndIdentifier(
310                         RECORD_TYPE_BASAL_METABOLIC_RATE, getBasalMetabolicRateRecord()));
311     }
312 
buildLocationTimePoint(Instant startTime)313     public static ExerciseRoute.Location buildLocationTimePoint(Instant startTime) {
314         return new ExerciseRoute.Location.Builder(
315                         Instant.ofEpochMilli(
316                                 (long) (startTime.toEpochMilli() + 10 + Math.random() * 50)),
317                         Math.random() * 50,
318                         Math.random() * 50)
319                 .build();
320     }
321 
buildExerciseRoute()322     public static ExerciseRoute buildExerciseRoute() {
323         return new ExerciseRoute(
324                 List.of(
325                         buildLocationTimePoint(SESSION_START_TIME),
326                         buildLocationTimePoint(SESSION_START_TIME),
327                         buildLocationTimePoint(SESSION_START_TIME)));
328     }
329 
getStepsRecord()330     public static StepsRecord getStepsRecord() {
331         Context context = ApplicationProvider.getApplicationContext();
332         Device device =
333                 new Device.Builder().setManufacturer("google").setModel("Pixel").setType(1).build();
334         DataOrigin dataOrigin =
335                 new DataOrigin.Builder().setPackageName(context.getPackageName()).build();
336         return new StepsRecord.Builder(
337                         new Metadata.Builder()
338                                 .setDevice(device)
339                                 .setDataOrigin(dataOrigin)
340                                 .setClientRecordId("SR" + Math.random())
341                                 .build(),
342                         Instant.now(),
343                         Instant.now().plusMillis(1000),
344                         10)
345                 .build();
346     }
347 
getStepsRecord(String id)348     public static StepsRecord getStepsRecord(String id) {
349         Context context = ApplicationProvider.getApplicationContext();
350         Device device =
351                 new Device.Builder().setManufacturer("google").setModel("Pixel").setType(1).build();
352         DataOrigin dataOrigin =
353                 new DataOrigin.Builder().setPackageName(context.getPackageName()).build();
354         return new StepsRecord.Builder(
355                         new Metadata.Builder()
356                                 .setDevice(device)
357                                 .setId(id)
358                                 .setDataOrigin(dataOrigin)
359                                 .build(),
360                         Instant.now(),
361                         Instant.now().plusMillis(1000),
362                         10)
363                 .build();
364     }
365 
getHeartRateRecord()366     public static HeartRateRecord getHeartRateRecord() {
367         Context context = ApplicationProvider.getApplicationContext();
368 
369         HeartRateRecord.HeartRateSample heartRateSample =
370                 new HeartRateRecord.HeartRateSample(72, Instant.now().plusMillis(100));
371         ArrayList<HeartRateRecord.HeartRateSample> heartRateSamples = new ArrayList<>();
372         heartRateSamples.add(heartRateSample);
373         heartRateSamples.add(heartRateSample);
374         Device device =
375                 new Device.Builder().setManufacturer("google").setModel("Pixel").setType(1).build();
376         DataOrigin dataOrigin =
377                 new DataOrigin.Builder().setPackageName(context.getPackageName()).build();
378 
379         return new HeartRateRecord.Builder(
380                         new Metadata.Builder()
381                                 .setDevice(device)
382                                 .setDataOrigin(dataOrigin)
383                                 .setClientRecordId("HR" + Math.random())
384                                 .build(),
385                         Instant.now(),
386                         Instant.now().plusMillis(500),
387                         heartRateSamples)
388                 .build();
389     }
390 
getHeartRateRecord(int heartRate)391     public static HeartRateRecord getHeartRateRecord(int heartRate) {
392         HeartRateRecord.HeartRateSample heartRateSample =
393                 new HeartRateRecord.HeartRateSample(heartRate, Instant.now().plusMillis(100));
394         ArrayList<HeartRateRecord.HeartRateSample> heartRateSamples = new ArrayList<>();
395         heartRateSamples.add(heartRateSample);
396         heartRateSamples.add(heartRateSample);
397 
398         return new HeartRateRecord.Builder(
399                         new Metadata.Builder().build(),
400                         Instant.now(),
401                         Instant.now().plusMillis(500),
402                         heartRateSamples)
403                 .build();
404     }
405 
getHeartRateRecord(int heartRate, Instant instant)406     public static HeartRateRecord getHeartRateRecord(int heartRate, Instant instant) {
407         HeartRateRecord.HeartRateSample heartRateSample =
408                 new HeartRateRecord.HeartRateSample(heartRate, instant);
409         ArrayList<HeartRateRecord.HeartRateSample> heartRateSamples = new ArrayList<>();
410         heartRateSamples.add(heartRateSample);
411         heartRateSamples.add(heartRateSample);
412 
413         return new HeartRateRecord.Builder(
414                         new Metadata.Builder().build(),
415                         instant,
416                         instant.plusMillis(1000),
417                         heartRateSamples)
418                 .build();
419     }
420 
getBasalMetabolicRateRecord()421     public static BasalMetabolicRateRecord getBasalMetabolicRateRecord() {
422         Context context = ApplicationProvider.getApplicationContext();
423         Device device =
424                 new Device.Builder()
425                         .setManufacturer("google")
426                         .setModel("Pixel4a")
427                         .setType(2)
428                         .build();
429         DataOrigin dataOrigin =
430                 new DataOrigin.Builder().setPackageName(context.getPackageName()).build();
431         return new BasalMetabolicRateRecord.Builder(
432                         new Metadata.Builder()
433                                 .setDevice(device)
434                                 .setDataOrigin(dataOrigin)
435                                 .setClientRecordId("BMR" + Math.random())
436                                 .build(),
437                         Instant.now(),
438                         Power.fromWatts(100.0))
439                 .build();
440     }
441 
getAggregateResponse( AggregateRecordsRequest<T> request, List<Record> recordsToInsert)442     public static <T> AggregateRecordsResponse<T> getAggregateResponse(
443             AggregateRecordsRequest<T> request, List<Record> recordsToInsert)
444             throws InterruptedException {
445         Context context = ApplicationProvider.getApplicationContext();
446         if (recordsToInsert != null) {
447             insertRecords(recordsToInsert);
448         }
449         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
450         assertThat(service).isNotNull();
451         CountDownLatch latch = new CountDownLatch(1);
452         AtomicReference<AggregateRecordsResponse<T>> response = new AtomicReference<>();
453         AtomicReference<HealthConnectException> healthConnectExceptionAtomicReference =
454                 new AtomicReference<>();
455         service.aggregate(
456                 request,
457                 Executors.newSingleThreadExecutor(),
458                 new OutcomeReceiver<>() {
459                     @Override
460                     public void onResult(AggregateRecordsResponse<T> result) {
461                         response.set(result);
462                         latch.countDown();
463                     }
464 
465                     @Override
466                     public void onError(HealthConnectException healthConnectException) {
467                         healthConnectExceptionAtomicReference.set(healthConnectException);
468                         latch.countDown();
469                     }
470                 });
471         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
472         if (healthConnectExceptionAtomicReference.get() != null) {
473             throw healthConnectExceptionAtomicReference.get();
474         }
475 
476         return response.get();
477     }
478 
479     public static <T>
getAggregateResponseGroupByDuration( AggregateRecordsRequest<T> request, Duration duration)480             List<AggregateRecordsGroupedByDurationResponse<T>> getAggregateResponseGroupByDuration(
481                     AggregateRecordsRequest<T> request, Duration duration)
482                     throws InterruptedException {
483         Context context = ApplicationProvider.getApplicationContext();
484         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
485         assertThat(service).isNotNull();
486         CountDownLatch latch = new CountDownLatch(1);
487         AtomicReference<List<AggregateRecordsGroupedByDurationResponse<T>>> response =
488                 new AtomicReference<>();
489         AtomicReference<HealthConnectException> healthConnectExceptionAtomicReference =
490                 new AtomicReference<>();
491         service.aggregateGroupByDuration(
492                 request,
493                 duration,
494                 Executors.newSingleThreadExecutor(),
495                 new OutcomeReceiver<>() {
496                     @Override
497                     public void onResult(
498                             List<AggregateRecordsGroupedByDurationResponse<T>> result) {
499                         response.set(result);
500                         latch.countDown();
501                     }
502 
503                     @Override
504                     public void onError(HealthConnectException healthConnectException) {
505                         healthConnectExceptionAtomicReference.set(healthConnectException);
506                         latch.countDown();
507                     }
508                 });
509         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
510         if (healthConnectExceptionAtomicReference.get() != null) {
511             throw healthConnectExceptionAtomicReference.get();
512         }
513         return response.get();
514     }
515 
516     public static <T>
getAggregateResponseGroupByPeriod( AggregateRecordsRequest<T> request, Period period)517             List<AggregateRecordsGroupedByPeriodResponse<T>> getAggregateResponseGroupByPeriod(
518                     AggregateRecordsRequest<T> request, Period period) throws InterruptedException {
519         Context context = ApplicationProvider.getApplicationContext();
520         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
521         assertThat(service).isNotNull();
522         CountDownLatch latch = new CountDownLatch(1);
523         AtomicReference<List<AggregateRecordsGroupedByPeriodResponse<T>>> response =
524                 new AtomicReference<>();
525         AtomicReference<HealthConnectException> healthConnectExceptionAtomicReference =
526                 new AtomicReference<>();
527         service.aggregateGroupByPeriod(
528                 request,
529                 period,
530                 Executors.newSingleThreadExecutor(),
531                 new OutcomeReceiver<>() {
532                     @Override
533                     public void onResult(List<AggregateRecordsGroupedByPeriodResponse<T>> result) {
534                         response.set(result);
535                         latch.countDown();
536                     }
537 
538                     @Override
539                     public void onError(HealthConnectException healthConnectException) {
540                         healthConnectExceptionAtomicReference.set(healthConnectException);
541                         latch.countDown();
542                     }
543                 });
544         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
545         if (healthConnectExceptionAtomicReference.get() != null) {
546             throw healthConnectExceptionAtomicReference.get();
547         }
548 
549         return response.get();
550     }
551 
readRecords(ReadRecordsRequest<T> request)552     public static <T extends Record> List<T> readRecords(ReadRecordsRequest<T> request)
553             throws InterruptedException {
554         Context context = ApplicationProvider.getApplicationContext();
555         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
556         CountDownLatch latch = new CountDownLatch(1);
557         assertThat(service).isNotNull();
558         assertThat(request.getRecordType()).isNotNull();
559         AtomicReference<List<T>> response = new AtomicReference<>();
560         AtomicReference<HealthConnectException> healthConnectExceptionAtomicReference =
561                 new AtomicReference<>();
562         service.readRecords(
563                 request,
564                 Executors.newSingleThreadExecutor(),
565                 new OutcomeReceiver<>() {
566                     @Override
567                     public void onResult(ReadRecordsResponse<T> result) {
568                         response.set(result.getRecords());
569                         latch.countDown();
570                     }
571 
572                     @Override
573                     public void onError(HealthConnectException exception) {
574                         Log.e(TAG, exception.getMessage());
575                         healthConnectExceptionAtomicReference.set(exception);
576                         latch.countDown();
577                     }
578                 });
579         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
580         if (healthConnectExceptionAtomicReference.get() != null) {
581             throw healthConnectExceptionAtomicReference.get();
582         }
583         return response.get();
584     }
585 
assertRecordNotFound(String uuid, Class<T> recordType)586     public static <T extends Record> void assertRecordNotFound(String uuid, Class<T> recordType)
587             throws InterruptedException {
588         assertThat(
589                         readRecords(
590                                 new ReadRecordsRequestUsingIds.Builder<>(recordType)
591                                         .addId(uuid)
592                                         .build()))
593                 .isEmpty();
594     }
595 
assertRecordFound(String uuid, Class<T> recordType)596     public static <T extends Record> void assertRecordFound(String uuid, Class<T> recordType)
597             throws InterruptedException {
598         assertThat(
599                         readRecords(
600                                 new ReadRecordsRequestUsingIds.Builder<>(recordType)
601                                         .addId(uuid)
602                                         .build()))
603                 .isNotEmpty();
604     }
605 
readRecordsWithPagination( ReadRecordsRequest<T> request)606     public static <T extends Record> Pair<List<T>, Long> readRecordsWithPagination(
607             ReadRecordsRequest<T> request) throws InterruptedException {
608         Context context = ApplicationProvider.getApplicationContext();
609         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
610         CountDownLatch latch = new CountDownLatch(1);
611         assertThat(service).isNotNull();
612         AtomicReference<List<T>> response = new AtomicReference<>();
613         AtomicReference<HealthConnectException> healthConnectExceptionAtomicReference =
614                 new AtomicReference<>();
615         AtomicReference<Long> pageToken = new AtomicReference<>();
616         service.readRecords(
617                 request,
618                 Executors.newSingleThreadExecutor(),
619                 new OutcomeReceiver<>() {
620                     @Override
621                     public void onResult(ReadRecordsResponse<T> result) {
622                         response.set(result.getRecords());
623                         pageToken.set(result.getNextPageToken());
624                         latch.countDown();
625                     }
626 
627                     @Override
628                     public void onError(HealthConnectException exception) {
629                         healthConnectExceptionAtomicReference.set(exception);
630                         latch.countDown();
631                     }
632                 });
633         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
634         if (healthConnectExceptionAtomicReference.get() != null) {
635             throw healthConnectExceptionAtomicReference.get();
636         }
637         return Pair.create(response.get(), pageToken.get());
638     }
639 
setAutoDeletePeriod(int period)640     public static void setAutoDeletePeriod(int period) throws InterruptedException {
641         UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
642         uiAutomation.adoptShellPermissionIdentity(MANAGE_HEALTH_DATA);
643         try {
644             Context context = ApplicationProvider.getApplicationContext();
645             HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
646             CountDownLatch latch = new CountDownLatch(1);
647             assertThat(service).isNotNull();
648             AtomicReference<HealthConnectException> exceptionAtomicReference =
649                     new AtomicReference<>();
650             service.setRecordRetentionPeriodInDays(
651                     period,
652                     Executors.newSingleThreadExecutor(),
653                     new OutcomeReceiver<>() {
654                         @Override
655                         public void onResult(Void result) {
656                             latch.countDown();
657                         }
658 
659                         @Override
660                         public void onError(HealthConnectException healthConnectException) {
661                             exceptionAtomicReference.set(healthConnectException);
662                             latch.countDown();
663                         }
664                     });
665             assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
666             if (exceptionAtomicReference.get() != null) {
667                 throw exceptionAtomicReference.get();
668             }
669         } finally {
670             uiAutomation.dropShellPermissionIdentity();
671         }
672     }
673 
verifyDeleteRecords(DeleteUsingFiltersRequest request)674     public static void verifyDeleteRecords(DeleteUsingFiltersRequest request)
675             throws InterruptedException {
676         UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
677         uiAutomation.adoptShellPermissionIdentity(MANAGE_HEALTH_DATA);
678         try {
679             Context context = ApplicationProvider.getApplicationContext();
680             HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
681             CountDownLatch latch = new CountDownLatch(1);
682             AtomicReference<HealthConnectException> exceptionAtomicReference =
683                     new AtomicReference<>();
684             assertThat(service).isNotNull();
685             service.deleteRecords(
686                     request,
687                     Executors.newSingleThreadExecutor(),
688                     new OutcomeReceiver<>() {
689                         @Override
690                         public void onResult(Void result) {
691                             latch.countDown();
692                         }
693 
694                         @Override
695                         public void onError(HealthConnectException healthConnectException) {
696                             exceptionAtomicReference.set(healthConnectException);
697                             latch.countDown();
698                         }
699                     });
700             assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
701             if (exceptionAtomicReference.get() != null) {
702                 throw exceptionAtomicReference.get();
703             }
704         } finally {
705             uiAutomation.dropShellPermissionIdentity();
706         }
707     }
708 
verifyDeleteRecords(List<RecordIdFilter> request)709     public static void verifyDeleteRecords(List<RecordIdFilter> request)
710             throws InterruptedException {
711 
712         Context context = ApplicationProvider.getApplicationContext();
713         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
714         CountDownLatch latch = new CountDownLatch(1);
715         AtomicReference<HealthConnectException> exceptionAtomicReference = new AtomicReference<>();
716         assertThat(service).isNotNull();
717         service.deleteRecords(
718                 request,
719                 Executors.newSingleThreadExecutor(),
720                 new OutcomeReceiver<>() {
721                     @Override
722                     public void onResult(Void result) {
723                         latch.countDown();
724                     }
725 
726                     @Override
727                     public void onError(HealthConnectException healthConnectException) {
728                         exceptionAtomicReference.set(healthConnectException);
729                         latch.countDown();
730                     }
731                 });
732         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
733         if (exceptionAtomicReference.get() != null) {
734             throw exceptionAtomicReference.get();
735         }
736     }
737 
verifyDeleteRecords( Class<? extends Record> recordType, TimeInstantRangeFilter timeRangeFilter)738     public static void verifyDeleteRecords(
739             Class<? extends Record> recordType, TimeInstantRangeFilter timeRangeFilter)
740             throws InterruptedException {
741         Context context = ApplicationProvider.getApplicationContext();
742         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
743         CountDownLatch latch = new CountDownLatch(1);
744         assertThat(service).isNotNull();
745         AtomicReference<HealthConnectException> exceptionAtomicReference = new AtomicReference<>();
746 
747         service.deleteRecords(
748                 recordType,
749                 timeRangeFilter,
750                 Executors.newSingleThreadExecutor(),
751                 new OutcomeReceiver<>() {
752                     @Override
753                     public void onResult(Void result) {
754                         latch.countDown();
755                     }
756 
757                     @Override
758                     public void onError(HealthConnectException healthConnectException) {
759                         exceptionAtomicReference.set(healthConnectException);
760                         latch.countDown();
761                     }
762                 });
763         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
764         if (exceptionAtomicReference.get() != null) {
765             throw exceptionAtomicReference.get();
766         }
767     }
768 
deleteRecords(List<Record> records)769     public static void deleteRecords(List<Record> records) throws InterruptedException {
770         List<RecordIdFilter> recordIdFilters =
771                 records.stream()
772                         .map(
773                                 (record ->
774                                         RecordIdFilter.fromId(
775                                                 record.getClass(), record.getMetadata().getId())))
776                         .collect(Collectors.toList());
777         verifyDeleteRecords(recordIdFilters);
778     }
779 
queryAccessLogs()780     public static List<AccessLog> queryAccessLogs() throws InterruptedException {
781         UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
782         uiAutomation.adoptShellPermissionIdentity(MANAGE_HEALTH_DATA);
783         try {
784             Context context = ApplicationProvider.getApplicationContext();
785             HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
786             assertThat(service).isNotNull();
787 
788             CountDownLatch latch = new CountDownLatch(1);
789             AtomicReference<List<AccessLog>> response = new AtomicReference<>();
790             AtomicReference<HealthConnectException> exceptionAtomicReference =
791                     new AtomicReference<>();
792             service.queryAccessLogs(
793                     Executors.newSingleThreadExecutor(),
794                     new OutcomeReceiver<List<AccessLog>, HealthConnectException>() {
795 
796                         @Override
797                         public void onResult(List<AccessLog> result) {
798                             response.set(result);
799                             latch.countDown();
800                         }
801 
802                         @Override
803                         public void onError(@NonNull HealthConnectException exception) {
804                             exceptionAtomicReference.set(exception);
805                             latch.countDown();
806                         }
807                     });
808             assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
809             if (exceptionAtomicReference.get() != null) {
810                 throw exceptionAtomicReference.get();
811             }
812             return response.get();
813         } finally {
814             uiAutomation.dropShellPermissionIdentity();
815         }
816     }
817 
queryAllRecordTypesInfo()818     public static Map<Class<? extends Record>, RecordTypeInfoResponse> queryAllRecordTypesInfo()
819             throws InterruptedException, NullPointerException {
820         UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
821         uiAutomation.adoptShellPermissionIdentity(MANAGE_HEALTH_DATA);
822         AtomicReference<Map<Class<? extends Record>, RecordTypeInfoResponse>> response =
823                 new AtomicReference<>();
824         AtomicReference<HealthConnectException> responseException = new AtomicReference<>();
825         try {
826             Context context = ApplicationProvider.getApplicationContext();
827             CountDownLatch latch = new CountDownLatch(1);
828             HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
829             assertThat(service).isNotNull();
830             service.queryAllRecordTypesInfo(
831                     Executors.newSingleThreadExecutor(),
832                     new OutcomeReceiver<
833                             Map<Class<? extends Record>, RecordTypeInfoResponse>,
834                             HealthConnectException>() {
835                         @Override
836                         public void onResult(
837                                 Map<Class<? extends Record>, RecordTypeInfoResponse> result) {
838                             response.set(result);
839                             latch.countDown();
840                         }
841 
842                         @Override
843                         public void onError(HealthConnectException exception) {
844                             responseException.set(exception);
845                             latch.countDown();
846                         }
847                     });
848             assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
849             assertThat(responseException.get()).isNull();
850             assertThat(response).isNotNull();
851         } finally {
852             uiAutomation.dropShellPermissionIdentity();
853         }
854         return response.get();
855     }
856 
getActivityDates(List<Class<? extends Record>> recordTypes)857     public static List<LocalDate> getActivityDates(List<Class<? extends Record>> recordTypes)
858             throws InterruptedException {
859         UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
860         uiAutomation.adoptShellPermissionIdentity(MANAGE_HEALTH_DATA);
861         try {
862             Context context = ApplicationProvider.getApplicationContext();
863             HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
864             CountDownLatch latch = new CountDownLatch(1);
865             assertThat(service).isNotNull();
866             AtomicReference<List<LocalDate>> response = new AtomicReference<>();
867             AtomicReference<HealthConnectException> exceptionAtomicReference =
868                     new AtomicReference<>();
869             service.queryActivityDates(
870                     recordTypes,
871                     Executors.newSingleThreadExecutor(),
872                     new OutcomeReceiver<>() {
873                         @Override
874                         public void onResult(List<LocalDate> result) {
875                             response.set(result);
876                             latch.countDown();
877                         }
878 
879                         @Override
880                         public void onError(HealthConnectException exception) {
881                             exceptionAtomicReference.set(exception);
882                             latch.countDown();
883                         }
884                     });
885 
886             assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
887             if (exceptionAtomicReference.get() != null) {
888                 throw exceptionAtomicReference.get();
889             }
890 
891             return response.get();
892         } finally {
893             uiAutomation.dropShellPermissionIdentity();
894         }
895     }
896 
buildExerciseSession()897     public static ExerciseSessionRecord buildExerciseSession() {
898         return buildExerciseSession(buildExerciseRoute(), "Morning training", "rain");
899     }
900 
buildSleepSession()901     public static SleepSessionRecord buildSleepSession() {
902         return new SleepSessionRecord.Builder(
903                         generateMetadata(), SESSION_START_TIME, SESSION_END_TIME)
904                 .setNotes("warm")
905                 .setTitle("Afternoon nap")
906                 .setStages(
907                         List.of(
908                                 new SleepSessionRecord.Stage(
909                                         SESSION_START_TIME,
910                                         SESSION_START_TIME.plusSeconds(300),
911                                         SleepSessionRecord.StageType.STAGE_TYPE_SLEEPING_LIGHT),
912                                 new SleepSessionRecord.Stage(
913                                         SESSION_START_TIME.plusSeconds(300),
914                                         SESSION_START_TIME.plusSeconds(600),
915                                         SleepSessionRecord.StageType.STAGE_TYPE_SLEEPING_REM),
916                                 new SleepSessionRecord.Stage(
917                                         SESSION_START_TIME.plusSeconds(900),
918                                         SESSION_START_TIME.plusSeconds(1200),
919                                         SleepSessionRecord.StageType.STAGE_TYPE_SLEEPING_DEEP)))
920                 .build();
921     }
922 
startMigration()923     public static void startMigration() throws InterruptedException {
924         Context context = ApplicationProvider.getApplicationContext();
925         CountDownLatch latch = new CountDownLatch(1);
926 
927         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
928         assertThat(service).isNotNull();
929         service.startMigration(
930                 Executors.newSingleThreadExecutor(),
931                 new OutcomeReceiver<Void, MigrationException>() {
932                     @Override
933                     public void onResult(Void result) {
934                         latch.countDown();
935                     }
936 
937                     @Override
938                     public void onError(MigrationException exception) {
939                         Log.e(TAG, exception.getMessage());
940                     }
941                 });
942         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
943     }
944 
finishMigration()945     public static void finishMigration() throws InterruptedException {
946         Context context = ApplicationProvider.getApplicationContext();
947         CountDownLatch latch = new CountDownLatch(1);
948 
949         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
950         assertThat(service).isNotNull();
951         service.finishMigration(
952                 Executors.newSingleThreadExecutor(),
953                 new OutcomeReceiver<Void, MigrationException>() {
954 
955                     @Override
956                     public void onResult(Void result) {
957                         latch.countDown();
958                     }
959 
960                     @Override
961                     public void onError(MigrationException exception) {
962                         Log.e(TAG, exception.getMessage());
963                     }
964                 });
965         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
966     }
967 
insertMinDataMigrationSdkExtensionVersion(int version)968     public static void insertMinDataMigrationSdkExtensionVersion(int version)
969             throws InterruptedException {
970         Context context = ApplicationProvider.getApplicationContext();
971         CountDownLatch latch = new CountDownLatch(1);
972         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
973         assertThat(service).isNotNull();
974         AtomicReference<MigrationException> migrationExceptionAtomicReference =
975                 new AtomicReference<>();
976         service.insertMinDataMigrationSdkExtensionVersion(
977                 version,
978                 Executors.newSingleThreadExecutor(),
979                 new OutcomeReceiver<>() {
980 
981                     @Override
982                     public void onResult(Void result) {
983                         latch.countDown();
984                     }
985 
986                     @Override
987                     public void onError(MigrationException exception) {
988                         migrationExceptionAtomicReference.set(exception);
989                         Log.e(TAG, exception.getMessage());
990                         latch.countDown();
991                     }
992                 });
993         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
994         if (migrationExceptionAtomicReference.get() != null) {
995             throw migrationExceptionAtomicReference.get();
996         }
997     }
998 
deleteAllStagedRemoteData()999     public static void deleteAllStagedRemoteData() {
1000         Context context = ApplicationProvider.getApplicationContext();
1001         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
1002         assertThat(service).isNotNull();
1003         runWithShellPermissionIdentity(
1004                 () ->
1005                         // TODO(b/241542162): Avoid reflection once TestApi can be called from CTS
1006                         service.getClass().getMethod("deleteAllStagedRemoteData").invoke(service),
1007                 "android.permission.DELETE_STAGED_HEALTH_CONNECT_REMOTE_DATA");
1008     }
1009 
getHealthConnectDataMigrationState()1010     public static int getHealthConnectDataMigrationState() throws InterruptedException {
1011         CountDownLatch latch = new CountDownLatch(1);
1012         Context context = ApplicationProvider.getApplicationContext();
1013         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
1014         assertThat(service).isNotNull();
1015         AtomicReference<HealthConnectDataState> returnedHealthConnectDataState =
1016                 new AtomicReference<>();
1017         AtomicReference<HealthConnectException> responseException = new AtomicReference<>();
1018         service.getHealthConnectDataState(
1019                 Executors.newSingleThreadExecutor(),
1020                 new OutcomeReceiver<>() {
1021                     @Override
1022                     public void onResult(HealthConnectDataState healthConnectDataState) {
1023                         returnedHealthConnectDataState.set(healthConnectDataState);
1024                         latch.countDown();
1025                     }
1026 
1027                     @Override
1028                     public void onError(@NonNull HealthConnectException exception) {
1029                         responseException.set(exception);
1030                         latch.countDown();
1031                     }
1032                 });
1033         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
1034         if (responseException.get() != null) {
1035             throw responseException.get();
1036         }
1037         return returnedHealthConnectDataState.get().getDataMigrationState();
1038     }
1039 
getApplicationInfo()1040     public static List<AppInfo> getApplicationInfo() throws InterruptedException {
1041         Context context = ApplicationProvider.getApplicationContext();
1042         HealthConnectManager service = context.getSystemService(HealthConnectManager.class);
1043         CountDownLatch latch = new CountDownLatch(1);
1044         assertThat(service).isNotNull();
1045         AtomicReference<List<AppInfo>> response = new AtomicReference<>();
1046         AtomicReference<HealthConnectException> exceptionAtomicReference = new AtomicReference<>();
1047         service.getContributorApplicationsInfo(
1048                 Executors.newSingleThreadExecutor(),
1049                 new OutcomeReceiver<>() {
1050                     @Override
1051                     public void onResult(ApplicationInfoResponse result) {
1052                         response.set(result.getApplicationInfoList());
1053                         latch.countDown();
1054                     }
1055 
1056                     @Override
1057                     public void onError(HealthConnectException exception) {
1058                         exceptionAtomicReference.set(exception);
1059                         Log.e(TAG, exception.getMessage());
1060                         latch.countDown();
1061                     }
1062                 });
1063         assertThat(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isEqualTo(true);
1064         if (exceptionAtomicReference.get() != null) {
1065             throw exceptionAtomicReference.get();
1066         }
1067         return response.get();
1068     }
1069 
getRecordById(List<T> list, String id)1070     public static <T extends Record> T getRecordById(List<T> list, String id) {
1071         for (T record : list) {
1072             if (record.getMetadata().getId().equals(id)) {
1073                 return record;
1074             }
1075         }
1076 
1077         throw new AssertionError("Record not found with id: " + id);
1078     }
1079 
generateMetadata()1080     static Metadata generateMetadata() {
1081         Context context = ApplicationProvider.getApplicationContext();
1082         return new Metadata.Builder()
1083                 .setDevice(buildDevice())
1084                 .setId(UUID.randomUUID().toString())
1085                 .setClientRecordId("clientRecordId" + Math.random())
1086                 .setDataOrigin(
1087                         new DataOrigin.Builder().setPackageName(context.getPackageName()).build())
1088                 .setDevice(buildDevice())
1089                 .setRecordingMethod(Metadata.RECORDING_METHOD_UNKNOWN)
1090                 .build();
1091     }
1092 
buildExerciseSession( ExerciseRoute route, String title, String notes)1093     private static ExerciseSessionRecord buildExerciseSession(
1094             ExerciseRoute route, String title, String notes) {
1095         return new ExerciseSessionRecord.Builder(
1096                         generateMetadata(),
1097                         SESSION_START_TIME,
1098                         SESSION_END_TIME,
1099                         ExerciseSessionType.EXERCISE_SESSION_TYPE_OTHER_WORKOUT)
1100                 .setRoute(route)
1101                 .setLaps(
1102                         List.of(
1103                                 new ExerciseLap.Builder(
1104                                                 SESSION_START_TIME,
1105                                                 SESSION_START_TIME.plusSeconds(20))
1106                                         .setLength(Length.fromMeters(10))
1107                                         .build(),
1108                                 new ExerciseLap.Builder(
1109                                                 SESSION_END_TIME.minusSeconds(20), SESSION_END_TIME)
1110                                         .build()))
1111                 .setSegments(
1112                         List.of(
1113                                 new ExerciseSegment.Builder(
1114                                                 SESSION_START_TIME.plusSeconds(1),
1115                                                 SESSION_START_TIME.plusSeconds(10),
1116                                                 ExerciseSegmentType
1117                                                         .EXERCISE_SEGMENT_TYPE_BENCH_PRESS)
1118                                         .build(),
1119                                 new ExerciseSegment.Builder(
1120                                                 SESSION_START_TIME.plusSeconds(21),
1121                                                 SESSION_START_TIME.plusSeconds(124),
1122                                                 ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BURPEE)
1123                                         .setRepetitionsCount(15)
1124                                         .build()))
1125                 .setEndZoneOffset(ZoneOffset.MAX)
1126                 .setStartZoneOffset(ZoneOffset.MIN)
1127                 .setNotes(notes)
1128                 .setTitle(title)
1129                 .build();
1130     }
1131 
populateAndResetExpectedResponseMap( HashMap<Class<? extends Record>, RecordTypeInfoTestResponse> expectedResponseMap)1132     static void populateAndResetExpectedResponseMap(
1133             HashMap<Class<? extends Record>, RecordTypeInfoTestResponse> expectedResponseMap) {
1134         expectedResponseMap.put(
1135                 ElevationGainedRecord.class,
1136                 new RecordTypeInfoTestResponse(
1137                         ACTIVITY, HealthPermissionCategory.ELEVATION_GAINED, new ArrayList<>()));
1138         expectedResponseMap.put(
1139                 OvulationTestRecord.class,
1140                 new RecordTypeInfoTestResponse(
1141                         CYCLE_TRACKING,
1142                         HealthPermissionCategory.OVULATION_TEST,
1143                         new ArrayList<>()));
1144         expectedResponseMap.put(
1145                 DistanceRecord.class,
1146                 new RecordTypeInfoTestResponse(
1147                         ACTIVITY, HealthPermissionCategory.DISTANCE, new ArrayList<>()));
1148         expectedResponseMap.put(
1149                 SpeedRecord.class,
1150                 new RecordTypeInfoTestResponse(
1151                         ACTIVITY, HealthPermissionCategory.SPEED, new ArrayList<>()));
1152 
1153         expectedResponseMap.put(
1154                 Vo2MaxRecord.class,
1155                 new RecordTypeInfoTestResponse(
1156                         ACTIVITY, HealthPermissionCategory.VO2_MAX, new ArrayList<>()));
1157         expectedResponseMap.put(
1158                 OxygenSaturationRecord.class,
1159                 new RecordTypeInfoTestResponse(
1160                         VITALS, HealthPermissionCategory.OXYGEN_SATURATION, new ArrayList<>()));
1161         expectedResponseMap.put(
1162                 TotalCaloriesBurnedRecord.class,
1163                 new RecordTypeInfoTestResponse(
1164                         ACTIVITY,
1165                         HealthPermissionCategory.TOTAL_CALORIES_BURNED,
1166                         new ArrayList<>()));
1167         expectedResponseMap.put(
1168                 HydrationRecord.class,
1169                 new RecordTypeInfoTestResponse(
1170                         NUTRITION, HealthPermissionCategory.HYDRATION, new ArrayList<>()));
1171         expectedResponseMap.put(
1172                 StepsRecord.class,
1173                 new RecordTypeInfoTestResponse(ACTIVITY, STEPS, new ArrayList<>()));
1174         expectedResponseMap.put(
1175                 CervicalMucusRecord.class,
1176                 new RecordTypeInfoTestResponse(
1177                         CYCLE_TRACKING,
1178                         HealthPermissionCategory.CERVICAL_MUCUS,
1179                         new ArrayList<>()));
1180         expectedResponseMap.put(
1181                 ExerciseSessionRecord.class,
1182                 new RecordTypeInfoTestResponse(ACTIVITY, EXERCISE, new ArrayList<>()));
1183         expectedResponseMap.put(
1184                 HeartRateRecord.class,
1185                 new RecordTypeInfoTestResponse(VITALS, HEART_RATE, new ArrayList<>()));
1186         expectedResponseMap.put(
1187                 RespiratoryRateRecord.class,
1188                 new RecordTypeInfoTestResponse(
1189                         VITALS, HealthPermissionCategory.RESPIRATORY_RATE, new ArrayList<>()));
1190         expectedResponseMap.put(
1191                 BasalBodyTemperatureRecord.class,
1192                 new RecordTypeInfoTestResponse(
1193                         VITALS,
1194                         HealthPermissionCategory.BASAL_BODY_TEMPERATURE,
1195                         new ArrayList<>()));
1196         expectedResponseMap.put(
1197                 WheelchairPushesRecord.class,
1198                 new RecordTypeInfoTestResponse(
1199                         ACTIVITY, HealthPermissionCategory.WHEELCHAIR_PUSHES, new ArrayList<>()));
1200         expectedResponseMap.put(
1201                 PowerRecord.class,
1202                 new RecordTypeInfoTestResponse(
1203                         ACTIVITY, HealthPermissionCategory.POWER, new ArrayList<>()));
1204         expectedResponseMap.put(
1205                 BodyWaterMassRecord.class,
1206                 new RecordTypeInfoTestResponse(
1207                         BODY_MEASUREMENTS,
1208                         HealthPermissionCategory.BODY_WATER_MASS,
1209                         new ArrayList<>()));
1210         expectedResponseMap.put(
1211                 WeightRecord.class,
1212                 new RecordTypeInfoTestResponse(
1213                         BODY_MEASUREMENTS, HealthPermissionCategory.WEIGHT, new ArrayList<>()));
1214         expectedResponseMap.put(
1215                 BoneMassRecord.class,
1216                 new RecordTypeInfoTestResponse(
1217                         BODY_MEASUREMENTS, HealthPermissionCategory.BONE_MASS, new ArrayList<>()));
1218         expectedResponseMap.put(
1219                 RestingHeartRateRecord.class,
1220                 new RecordTypeInfoTestResponse(
1221                         VITALS, HealthPermissionCategory.RESTING_HEART_RATE, new ArrayList<>()));
1222         expectedResponseMap.put(
1223                 ActiveCaloriesBurnedRecord.class,
1224                 new RecordTypeInfoTestResponse(
1225                         ACTIVITY,
1226                         HealthPermissionCategory.ACTIVE_CALORIES_BURNED,
1227                         new ArrayList<>()));
1228         expectedResponseMap.put(
1229                 BodyFatRecord.class,
1230                 new RecordTypeInfoTestResponse(
1231                         BODY_MEASUREMENTS, HealthPermissionCategory.BODY_FAT, new ArrayList<>()));
1232         expectedResponseMap.put(
1233                 BodyTemperatureRecord.class,
1234                 new RecordTypeInfoTestResponse(
1235                         VITALS, HealthPermissionCategory.BODY_TEMPERATURE, new ArrayList<>()));
1236         expectedResponseMap.put(
1237                 NutritionRecord.class,
1238                 new RecordTypeInfoTestResponse(
1239                         NUTRITION, HealthPermissionCategory.NUTRITION, new ArrayList<>()));
1240         expectedResponseMap.put(
1241                 LeanBodyMassRecord.class,
1242                 new RecordTypeInfoTestResponse(
1243                         BODY_MEASUREMENTS,
1244                         HealthPermissionCategory.LEAN_BODY_MASS,
1245                         new ArrayList<>()));
1246         expectedResponseMap.put(
1247                 HeartRateVariabilityRmssdRecord.class,
1248                 new RecordTypeInfoTestResponse(
1249                         VITALS,
1250                         HealthPermissionCategory.HEART_RATE_VARIABILITY,
1251                         new ArrayList<>()));
1252         expectedResponseMap.put(
1253                 MenstruationFlowRecord.class,
1254                 new RecordTypeInfoTestResponse(
1255                         CYCLE_TRACKING, HealthPermissionCategory.MENSTRUATION, new ArrayList<>()));
1256         expectedResponseMap.put(
1257                 BloodGlucoseRecord.class,
1258                 new RecordTypeInfoTestResponse(
1259                         VITALS, HealthPermissionCategory.BLOOD_GLUCOSE, new ArrayList<>()));
1260         expectedResponseMap.put(
1261                 BloodPressureRecord.class,
1262                 new RecordTypeInfoTestResponse(
1263                         VITALS, HealthPermissionCategory.BLOOD_PRESSURE, new ArrayList<>()));
1264         expectedResponseMap.put(
1265                 CyclingPedalingCadenceRecord.class,
1266                 new RecordTypeInfoTestResponse(ACTIVITY, EXERCISE, new ArrayList<>()));
1267         expectedResponseMap.put(
1268                 IntermenstrualBleedingRecord.class,
1269                 new RecordTypeInfoTestResponse(
1270                         CYCLE_TRACKING,
1271                         HealthPermissionCategory.INTERMENSTRUAL_BLEEDING,
1272                         new ArrayList<>()));
1273         expectedResponseMap.put(
1274                 FloorsClimbedRecord.class,
1275                 new RecordTypeInfoTestResponse(
1276                         ACTIVITY, HealthPermissionCategory.FLOORS_CLIMBED, new ArrayList<>()));
1277         expectedResponseMap.put(
1278                 StepsCadenceRecord.class,
1279                 new RecordTypeInfoTestResponse(ACTIVITY, STEPS, new ArrayList<>()));
1280         expectedResponseMap.put(
1281                 HeightRecord.class,
1282                 new RecordTypeInfoTestResponse(
1283                         BODY_MEASUREMENTS, HealthPermissionCategory.HEIGHT, new ArrayList<>()));
1284         expectedResponseMap.put(
1285                 SexualActivityRecord.class,
1286                 new RecordTypeInfoTestResponse(
1287                         CYCLE_TRACKING,
1288                         HealthPermissionCategory.SEXUAL_ACTIVITY,
1289                         new ArrayList<>()));
1290         expectedResponseMap.put(
1291                 MenstruationPeriodRecord.class,
1292                 new RecordTypeInfoTestResponse(
1293                         CYCLE_TRACKING, HealthPermissionCategory.MENSTRUATION, new ArrayList<>()));
1294         expectedResponseMap.put(
1295                 SleepSessionRecord.class,
1296                 new RecordTypeInfoTestResponse(
1297                         SLEEP, HealthPermissionCategory.SLEEP, new ArrayList<>()));
1298         expectedResponseMap.put(
1299                 BasalMetabolicRateRecord.class,
1300                 new RecordTypeInfoTestResponse(
1301                         BODY_MEASUREMENTS, BASAL_METABOLIC_RATE, new ArrayList<>()));
1302     }
1303 
1304     static final class RecordAndIdentifier {
1305         private final int id;
1306         private final Record recordClass;
1307 
RecordAndIdentifier(int id, Record recordClass)1308         public RecordAndIdentifier(int id, Record recordClass) {
1309             this.id = id;
1310             this.recordClass = recordClass;
1311         }
1312 
getId()1313         public int getId() {
1314             return id;
1315         }
1316 
getRecordClass()1317         public Record getRecordClass() {
1318             return recordClass;
1319         }
1320     }
1321 
1322     static class RecordTypeInfoTestResponse {
1323         private final int mRecordTypePermission;
1324         private final ArrayList<String> mContributingPackages;
1325         private final int mRecordTypeCategory;
1326 
RecordTypeInfoTestResponse( int recordTypeCategory, int recordTypePermission, ArrayList<String> contributingPackages)1327         RecordTypeInfoTestResponse(
1328                 int recordTypeCategory,
1329                 int recordTypePermission,
1330                 ArrayList<String> contributingPackages) {
1331             mRecordTypeCategory = recordTypeCategory;
1332             mRecordTypePermission = recordTypePermission;
1333             mContributingPackages = contributingPackages;
1334         }
1335 
getRecordTypeCategory()1336         public int getRecordTypeCategory() {
1337             return mRecordTypeCategory;
1338         }
1339 
getRecordTypePermission()1340         public int getRecordTypePermission() {
1341             return mRecordTypePermission;
1342         }
1343 
getContributingPackages()1344         public ArrayList<String> getContributingPackages() {
1345             return mContributingPackages;
1346         }
1347     }
1348 }
1349