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