1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.healthconnect.cts.route; 18 19 import static android.health.connect.HealthPermissions.READ_EXERCISE; 20 import static android.health.connect.HealthPermissions.READ_EXERCISE_ROUTES; 21 import static android.health.connect.HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND; 22 import static android.health.connect.HealthPermissions.WRITE_EXERCISE; 23 import static android.health.connect.HealthPermissions.WRITE_EXERCISE_ROUTE; 24 import static android.healthconnect.cts.utils.PermissionHelper.READ_EXERCISE_ROUTE_PERMISSION; 25 import static android.healthconnect.cts.utils.PermissionHelper.getGrantedHealthPermissions; 26 import static android.healthconnect.cts.utils.TestUtils.readRecords; 27 import static android.healthconnect.cts.utils.TestUtils.yesterdayAt; 28 29 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; 30 31 import static com.google.common.truth.Truth.assertThat; 32 33 import static java.time.Duration.ofMinutes; 34 35 import android.content.Context; 36 import android.health.connect.ReadRecordsRequestUsingFilters; 37 import android.health.connect.datatypes.ExerciseRoute; 38 import android.health.connect.datatypes.ExerciseSessionRecord; 39 import android.health.connect.datatypes.ExerciseSessionType; 40 import android.health.connect.datatypes.Metadata; 41 import android.health.connect.datatypes.units.Length; 42 import android.healthconnect.cts.lib.TestAppProxy; 43 44 import androidx.test.core.app.ApplicationProvider; 45 46 import java.time.Instant; 47 import java.time.temporal.ChronoUnit; 48 import java.util.List; 49 50 final class ExerciseRouteTestHelper { 51 52 static final TestAppProxy ROUTE_WRITER_APP = 53 TestAppProxy.forPackageName("android.healthconnect.cts.route.testapp.writer"); 54 55 static final TestAppProxy ROUTES_READER_WRITER_APP = 56 TestAppProxy.forPackageName("android.healthconnect.cts.route.testapp.readerWriter"); 57 58 static final Instant START_TIME = yesterdayAt("11:00").truncatedTo(ChronoUnit.MILLIS); 59 60 /** 61 * Make sure test app permissions didn't change between the runs. 62 * 63 * <p>Fail quickly and explicitly if the permissions were not cleaned up by a previous test 64 * case. Also because permissions in CTS tests are auto granted it's easy to misconfigure 65 * permissions without noticing it, so this works as a double check. 66 */ assertCorrectHealthPermissions()67 static void assertCorrectHealthPermissions() { 68 Context context = ApplicationProvider.getApplicationContext(); 69 assertThat(getGrantedHealthPermissions(context.getPackageName())) 70 .containsExactly(READ_EXERCISE, WRITE_EXERCISE, WRITE_EXERCISE_ROUTE); 71 assertThat(getGrantedHealthPermissions(ROUTE_WRITER_APP.getPackageName())) 72 .containsExactly(WRITE_EXERCISE, WRITE_EXERCISE_ROUTE); 73 assertThat(getGrantedHealthPermissions(ROUTES_READER_WRITER_APP.getPackageName())) 74 .containsExactly( 75 READ_EXERCISE, 76 READ_EXERCISE_ROUTES, 77 READ_HEALTH_DATA_IN_BACKGROUND, 78 WRITE_EXERCISE, 79 WRITE_EXERCISE_ROUTE); 80 } 81 getExerciseSessionWithRoute(Metadata metadata)82 static ExerciseSessionRecord getExerciseSessionWithRoute(Metadata metadata) { 83 return getExerciseSessionWithRoute(metadata, START_TIME); 84 } 85 getExerciseSessionWithRoute(Metadata metadata, Instant startTime)86 static ExerciseSessionRecord getExerciseSessionWithRoute(Metadata metadata, Instant startTime) { 87 return getExerciseSession(metadata, startTime, getRoute(startTime)); 88 } 89 getExerciseSessionWithAnotherRoute(Metadata metadata)90 static ExerciseSessionRecord getExerciseSessionWithAnotherRoute(Metadata metadata) { 91 return getExerciseSession(metadata, START_TIME, getAnotherRoute(START_TIME)); 92 } 93 getExerciseSessionWithoutRoute(Metadata metadata)94 static ExerciseSessionRecord getExerciseSessionWithoutRoute(Metadata metadata) { 95 return getExerciseSession(metadata, START_TIME, null); 96 } 97 getExerciseSessionWithoutRoute( Metadata metadata, Instant startTime)98 static ExerciseSessionRecord getExerciseSessionWithoutRoute( 99 Metadata metadata, Instant startTime) { 100 return getExerciseSession(metadata, startTime, null); 101 } 102 getExerciseSession( Metadata metadata, Instant startTime, ExerciseRoute route)103 static ExerciseSessionRecord getExerciseSession( 104 Metadata metadata, Instant startTime, ExerciseRoute route) { 105 return new ExerciseSessionRecord.Builder( 106 metadata, 107 startTime, 108 startTime.plus(ofMinutes(30)), 109 ExerciseSessionType.EXERCISE_SESSION_TYPE_RUNNING) 110 .setRoute(route) 111 .build(); 112 } 113 getRoute(Instant startTime)114 static ExerciseRoute getRoute(Instant startTime) { 115 return new ExerciseRoute( 116 List.of( 117 new ExerciseRoute.Location.Builder(startTime, 52., 48.).build(), 118 new ExerciseRoute.Location.Builder(startTime.plusSeconds(5), -51., -49.) 119 .setAltitude(Length.fromMeters(14)) 120 .setHorizontalAccuracy(Length.fromMeters(3)) 121 .setVerticalAccuracy(Length.fromMeters(5)) 122 .build())); 123 } 124 getAnotherRoute(Instant startTime)125 static ExerciseRoute getAnotherRoute(Instant startTime) { 126 return new ExerciseRoute( 127 List.of( 128 new ExerciseRoute.Location.Builder(startTime.plusSeconds(10), -53., -47.) 129 .setAltitude(Length.fromMeters(123)) 130 .setHorizontalAccuracy(Length.fromMeters(7)) 131 .setVerticalAccuracy(Length.fromMeters(11)) 132 .build())); 133 } 134 readAllExerciseSessionRecordsPrivileged()135 static List<ExerciseSessionRecord> readAllExerciseSessionRecordsPrivileged() { 136 return runWithShellPermissionIdentity( 137 () -> 138 readRecords( 139 new ReadRecordsRequestUsingFilters.Builder<>( 140 ExerciseSessionRecord.class) 141 .build()), 142 READ_EXERCISE_ROUTE_PERMISSION); 143 } 144 ExerciseRouteTestHelper()145 private ExerciseRouteTestHelper() {} 146 } 147