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.nopermission; 18 19 import static android.health.connect.HealthPermissions.READ_DISTANCE; 20 import static android.health.connect.HealthPermissions.READ_EXERCISE; 21 import static android.health.connect.HealthPermissions.READ_HEART_RATE; 22 import static android.health.connect.HealthPermissions.READ_SLEEP; 23 import static android.health.connect.HealthPermissions.READ_STEPS; 24 import static android.health.connect.HealthPermissions.READ_TOTAL_CALORIES_BURNED; 25 import static android.healthconnect.cts.utils.DataFactory.buildExerciseSession; 26 import static android.healthconnect.cts.utils.DataFactory.buildSleepSession; 27 import static android.healthconnect.cts.utils.DataFactory.getDistanceRecordWithNonEmptyId; 28 import static android.healthconnect.cts.utils.DataFactory.getHeartRateRecord; 29 import static android.healthconnect.cts.utils.DataFactory.getStepsRecord; 30 import static android.healthconnect.cts.utils.DataFactory.getTotalCaloriesBurnedRecord; 31 import static android.healthconnect.cts.utils.PermissionHelper.grantHealthPermissions; 32 import static android.healthconnect.cts.utils.PermissionHelper.revokeAllHealthPermissions; 33 import static android.healthconnect.cts.utils.PermissionHelper.revokeHealthPermission; 34 import static android.healthconnect.cts.utils.TestUtils.deleteRecords; 35 import static android.healthconnect.cts.utils.TestUtils.getChangeLogToken; 36 import static android.healthconnect.cts.utils.TestUtils.insertRecords; 37 import static android.healthconnect.cts.utils.TestUtils.updateRecords; 38 39 import static com.google.common.truth.Truth.assertThat; 40 41 import android.health.connect.HealthConnectException; 42 import android.health.connect.changelog.ChangeLogTokenRequest; 43 import android.health.connect.changelog.ChangeLogsRequest; 44 import android.health.connect.datatypes.DistanceRecord; 45 import android.health.connect.datatypes.ExerciseSessionRecord; 46 import android.health.connect.datatypes.HeartRateRecord; 47 import android.health.connect.datatypes.Record; 48 import android.health.connect.datatypes.SleepSessionRecord; 49 import android.health.connect.datatypes.StepsRecord; 50 import android.health.connect.datatypes.TotalCaloriesBurnedRecord; 51 import android.healthconnect.cts.lib.TestAppProxy; 52 import android.healthconnect.cts.utils.AssumptionCheckerRule; 53 import android.healthconnect.cts.utils.DeviceSupportUtils; 54 import android.platform.test.annotations.AppModeFull; 55 56 import androidx.test.ext.junit.runners.AndroidJUnit4; 57 58 import org.junit.Assert; 59 import org.junit.Rule; 60 import org.junit.Test; 61 import org.junit.runner.RunWith; 62 63 import java.util.Arrays; 64 import java.util.List; 65 66 /** These tests run under an environment which only some HC permissions are granted. */ 67 @AppModeFull(reason = "HealthConnectManager is not accessible to instant apps") 68 @RunWith(AndroidJUnit4.class) 69 public class HealthConnectManagerNotAllPermissionsAreGrantedTest { 70 private static final TestAppProxy APP_A_WITH_READ_WRITE_PERMS = 71 TestAppProxy.forPackageName("android.healthconnect.cts.testapp.readWritePerms.A"); 72 73 @Rule 74 public AssumptionCheckerRule mSupportedHardwareRule = 75 new AssumptionCheckerRule( 76 DeviceSupportUtils::isHealthConnectFullySupported, 77 "Tests should run on supported hardware only."); 78 79 @Test testInsert_somePermissionsAreNotGranted_expectError()80 public void testInsert_somePermissionsAreNotGranted_expectError() throws InterruptedException { 81 try { 82 insertRecords(getTestRecords()); 83 84 Assert.fail("WRITE_DISTANCE is not granted, this test should fail!"); 85 } catch (HealthConnectException healthConnectException) { 86 assertThat(healthConnectException.getErrorCode()) 87 .isEqualTo(HealthConnectException.ERROR_SECURITY); 88 } 89 } 90 91 @Test testUpdate_somePermissionsAreNotGranted_expectError()92 public void testUpdate_somePermissionsAreNotGranted_expectError() throws InterruptedException { 93 try { 94 updateRecords(getTestRecords()); 95 96 Assert.fail("WRITE_DISTANCE is not granted, this test should fail!"); 97 } catch (HealthConnectException healthConnectException) { 98 assertThat(healthConnectException.getErrorCode()) 99 .isEqualTo(HealthConnectException.ERROR_SECURITY); 100 } 101 } 102 103 @Test testDeleteUsingIds_somePermissionsAreNotGranted_expectError()104 public void testDeleteUsingIds_somePermissionsAreNotGranted_expectError() 105 throws InterruptedException { 106 try { 107 deleteRecords(getTestRecords()); 108 109 Assert.fail("WRITE_DISTANCE is not granted, this test should fail!"); 110 } catch (HealthConnectException healthConnectException) { 111 assertThat(healthConnectException.getErrorCode()) 112 .isEqualTo(HealthConnectException.ERROR_SECURITY); 113 } 114 } 115 116 @Test testGetChangeLogToken_somePermissionsAreNotGranted_expectError()117 public void testGetChangeLogToken_somePermissionsAreNotGranted_expectError() 118 throws InterruptedException { 119 try { 120 ChangeLogTokenRequest.Builder request = new ChangeLogTokenRequest.Builder(); 121 for (Record record : getTestRecords()) { 122 request.addRecordType(record.getClass()); 123 } 124 125 getChangeLogToken(request.build()); 126 127 Assert.fail("READ_DISTANCE is not granted, this test should fail!"); 128 } catch (HealthConnectException healthConnectException) { 129 assertThat(healthConnectException.getErrorCode()) 130 .isEqualTo(HealthConnectException.ERROR_SECURITY); 131 } 132 } 133 134 @Test testGetChangeLogs_somePermissionsAreNotGranted_expectError()135 public void testGetChangeLogs_somePermissionsAreNotGranted_expectError() throws Exception { 136 TestAppProxy testApp = APP_A_WITH_READ_WRITE_PERMS; 137 String packageName = testApp.getPackageName(); 138 revokeAllHealthPermissions(packageName, /* reason= */ "for test"); 139 grantHealthPermissions( 140 packageName, 141 List.of( 142 READ_STEPS, 143 READ_DISTANCE, 144 READ_HEART_RATE, 145 READ_SLEEP, 146 READ_EXERCISE, 147 READ_TOTAL_CALORIES_BURNED)); 148 String token = 149 testApp.getChangeLogToken( 150 new ChangeLogTokenRequest.Builder() 151 .addRecordType(StepsRecord.class) 152 .addRecordType(DistanceRecord.class) 153 .addRecordType(HeartRateRecord.class) 154 .addRecordType(SleepSessionRecord.class) 155 .addRecordType(ExerciseSessionRecord.class) 156 .addRecordType(TotalCaloriesBurnedRecord.class) 157 .build()); 158 159 // revoke one permission which the app needs so it can use the token 160 revokeHealthPermission(packageName, READ_DISTANCE); 161 162 try { 163 testApp.getChangeLogs(new ChangeLogsRequest.Builder(token).build()); 164 165 Assert.fail( 166 String.format( 167 "READ_DISTANCE is not granted to %s, this test should fail!", 168 packageName)); 169 } catch (HealthConnectException healthConnectException) { 170 assertThat(healthConnectException.getErrorCode()) 171 .isEqualTo(HealthConnectException.ERROR_SECURITY); 172 } 173 } 174 getTestRecords()175 private static List<Record> getTestRecords() { 176 return Arrays.asList( 177 getStepsRecord(), 178 getHeartRateRecord(), 179 buildSleepSession(), 180 getDistanceRecordWithNonEmptyId(), 181 getTotalCaloriesBurnedRecord("client_id"), 182 buildExerciseSession()); 183 } 184 } 185