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.health.connect; 18 19 import static android.health.connect.HealthPermissions.READ_EXERCISE_ROUTE; 20 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_ALLERGIES_INTOLERANCES; 21 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_CONDITIONS; 22 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_LABORATORY_RESULTS; 23 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_MEDICATIONS; 24 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_PERSONAL_DETAILS; 25 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_PRACTITIONER_DETAILS; 26 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_PREGNANCY; 27 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_PROCEDURES; 28 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_SOCIAL_HISTORY; 29 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_VACCINES; 30 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_VISITS; 31 import static android.health.connect.HealthPermissions.READ_MEDICAL_DATA_VITAL_SIGNS; 32 import static android.health.connect.HealthPermissions.WRITE_MEDICAL_DATA; 33 import static android.health.connect.HealthPermissions.isValidHealthPermission; 34 35 import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD; 36 import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD_DATABASE; 37 38 import static com.google.common.truth.Truth.assertThat; 39 import static com.google.common.truth.Truth.assertWithMessage; 40 41 import static org.mockito.Mockito.reset; 42 43 import android.content.Context; 44 import android.content.pm.PackageInfo; 45 import android.content.pm.PackageManager; 46 import android.content.pm.PermissionInfo; 47 import android.platform.test.annotations.EnableFlags; 48 import android.platform.test.flag.junit.SetFlagsRule; 49 50 import androidx.test.InstrumentationRegistry; 51 import androidx.test.ext.junit.runners.AndroidJUnit4; 52 53 import com.android.healthfitness.flags.Flags; 54 55 import org.junit.After; 56 import org.junit.Before; 57 import org.junit.Rule; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 import org.mockito.Mock; 61 import org.mockito.MockitoAnnotations; 62 63 import java.util.Set; 64 import java.util.stream.Collectors; 65 import java.util.stream.Stream; 66 67 @RunWith(AndroidJUnit4.class) 68 public class HealthPermissionsTest { 69 private static final String FAIL_MESSAGE = 70 "Add new health permission to ALL_EXPECTED_HEALTH_PERMISSIONS and " 71 + " android.healthconnect.cts.HealthPermissionsPresenceTest.HEALTH_PERMISSIONS " 72 + "sets."; 73 74 private static final String PHR_FAIL_MESSAGE = 75 "Add new medical permissions to ALL_EXPECTED_HEALTH_PERMISSIONS and " 76 + "apk/HealthPermissionsManifest.xml."; 77 78 private static final String MEDICAL_PERMISSION_IDENTIFIER = "MEDICAL_DATA"; 79 80 // Add new health permission to ALL_EXPECTED_HEALTH_PERMISSIONS and 81 // {@link android.healthconnect.cts.HealthPermissionsPresenceTest.HEALTH_PERMISSIONS} 82 // sets. 83 private static final Set<String> ALL_EXPECTED_HEALTH_PERMISSIONS = 84 Stream.concat( 85 Stream.of( 86 HealthPermissions.READ_HEALTH_DATA_HISTORY, 87 HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND, 88 HealthPermissions.READ_ACTIVE_CALORIES_BURNED, 89 HealthPermissions.READ_DISTANCE, 90 HealthPermissions.READ_ELEVATION_GAINED, 91 HealthPermissions.READ_EXERCISE, 92 HealthPermissions.READ_EXERCISE_ROUTES, 93 HealthPermissions.READ_PLANNED_EXERCISE, 94 HealthPermissions.READ_FLOORS_CLIMBED, 95 HealthPermissions.READ_STEPS, 96 HealthPermissions.READ_TOTAL_CALORIES_BURNED, 97 HealthPermissions.READ_VO2_MAX, 98 HealthPermissions.READ_WHEELCHAIR_PUSHES, 99 HealthPermissions.READ_POWER, 100 HealthPermissions.READ_SPEED, 101 HealthPermissions.READ_BASAL_METABOLIC_RATE, 102 HealthPermissions.READ_BODY_FAT, 103 HealthPermissions.READ_BODY_WATER_MASS, 104 HealthPermissions.READ_BONE_MASS, 105 HealthPermissions.READ_HEIGHT, 106 HealthPermissions.READ_LEAN_BODY_MASS, 107 HealthPermissions.READ_WEIGHT, 108 HealthPermissions.READ_CERVICAL_MUCUS, 109 HealthPermissions.READ_INTERMENSTRUAL_BLEEDING, 110 HealthPermissions.READ_MENSTRUATION, 111 HealthPermissions.READ_OVULATION_TEST, 112 HealthPermissions.READ_SEXUAL_ACTIVITY, 113 HealthPermissions.READ_HYDRATION, 114 HealthPermissions.READ_NUTRITION, 115 HealthPermissions.READ_SLEEP, 116 HealthPermissions.READ_BASAL_BODY_TEMPERATURE, 117 HealthPermissions.READ_BLOOD_GLUCOSE, 118 HealthPermissions.READ_BLOOD_PRESSURE, 119 HealthPermissions.READ_BODY_TEMPERATURE, 120 HealthPermissions.READ_HEART_RATE, 121 HealthPermissions.READ_HEART_RATE_VARIABILITY, 122 HealthPermissions.READ_OXYGEN_SATURATION, 123 HealthPermissions.READ_RESPIRATORY_RATE, 124 HealthPermissions.READ_RESTING_HEART_RATE, 125 HealthPermissions.READ_SKIN_TEMPERATURE, 126 HealthPermissions.READ_MINDFULNESS, 127 HealthPermissions.READ_MEDICAL_DATA_ALLERGIES_INTOLERANCES, 128 HealthPermissions.READ_MEDICAL_DATA_CONDITIONS, 129 HealthPermissions.READ_MEDICAL_DATA_LABORATORY_RESULTS, 130 HealthPermissions.READ_MEDICAL_DATA_MEDICATIONS, 131 HealthPermissions.READ_MEDICAL_DATA_PERSONAL_DETAILS, 132 HealthPermissions.READ_MEDICAL_DATA_PRACTITIONER_DETAILS, 133 HealthPermissions.READ_MEDICAL_DATA_PREGNANCY, 134 HealthPermissions.READ_MEDICAL_DATA_PROCEDURES, 135 HealthPermissions.READ_MEDICAL_DATA_SOCIAL_HISTORY, 136 HealthPermissions.READ_MEDICAL_DATA_VACCINES, 137 HealthPermissions.READ_MEDICAL_DATA_VISITS, 138 HealthPermissions.READ_MEDICAL_DATA_VITAL_SIGNS, 139 HealthPermissions.WRITE_ACTIVE_CALORIES_BURNED, 140 HealthPermissions.WRITE_DISTANCE, 141 HealthPermissions.WRITE_ELEVATION_GAINED, 142 HealthPermissions.WRITE_EXERCISE, 143 HealthPermissions.WRITE_EXERCISE_ROUTE, 144 HealthPermissions.WRITE_PLANNED_EXERCISE, 145 HealthPermissions.WRITE_FLOORS_CLIMBED, 146 HealthPermissions.WRITE_STEPS, 147 HealthPermissions.WRITE_TOTAL_CALORIES_BURNED, 148 HealthPermissions.WRITE_VO2_MAX, 149 HealthPermissions.WRITE_WHEELCHAIR_PUSHES, 150 HealthPermissions.WRITE_POWER, 151 HealthPermissions.WRITE_SPEED, 152 HealthPermissions.WRITE_BASAL_METABOLIC_RATE, 153 HealthPermissions.WRITE_BODY_FAT, 154 HealthPermissions.WRITE_BODY_WATER_MASS, 155 HealthPermissions.WRITE_BONE_MASS, 156 HealthPermissions.WRITE_HEIGHT, 157 HealthPermissions.WRITE_LEAN_BODY_MASS, 158 HealthPermissions.WRITE_WEIGHT, 159 HealthPermissions.WRITE_CERVICAL_MUCUS, 160 HealthPermissions.WRITE_INTERMENSTRUAL_BLEEDING, 161 HealthPermissions.WRITE_MENSTRUATION, 162 HealthPermissions.WRITE_OVULATION_TEST, 163 HealthPermissions.WRITE_SEXUAL_ACTIVITY, 164 HealthPermissions.WRITE_HYDRATION, 165 HealthPermissions.WRITE_NUTRITION, 166 HealthPermissions.WRITE_SLEEP, 167 HealthPermissions.WRITE_BASAL_BODY_TEMPERATURE, 168 HealthPermissions.WRITE_BLOOD_GLUCOSE, 169 HealthPermissions.WRITE_BLOOD_PRESSURE, 170 HealthPermissions.WRITE_BODY_TEMPERATURE, 171 HealthPermissions.WRITE_HEART_RATE, 172 HealthPermissions.WRITE_HEART_RATE_VARIABILITY, 173 HealthPermissions.WRITE_OXYGEN_SATURATION, 174 HealthPermissions.WRITE_RESPIRATORY_RATE, 175 HealthPermissions.WRITE_RESTING_HEART_RATE, 176 HealthPermissions.WRITE_SKIN_TEMPERATURE, 177 HealthPermissions.WRITE_MINDFULNESS, 178 HealthPermissions.WRITE_MEDICAL_DATA), 179 Flags.activityIntensity() 180 ? Stream.of( 181 HealthPermissions.READ_ACTIVITY_INTENSITY, 182 HealthPermissions.WRITE_ACTIVITY_INTENSITY) 183 : Stream.of()) 184 .collect(Collectors.toSet()); 185 private PackageManager mPackageManager; 186 private Context mContext; 187 @Mock private PackageInfo mPackageInfo1; 188 189 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 190 191 @Before setUp()192 public void setUp() { 193 MockitoAnnotations.initMocks(this); 194 mContext = InstrumentationRegistry.getTargetContext(); 195 mPackageManager = mContext.getPackageManager(); 196 } 197 198 @After tearDown()199 public void tearDown() { 200 reset(mPackageInfo1); 201 } 202 203 @Test testHealthGroupPermissions_noUnexpectedPermissionsDefined()204 public void testHealthGroupPermissions_noUnexpectedPermissionsDefined() throws Exception { 205 PermissionInfo[] permissionInfos = getHealthPermissionInfos(); 206 for (PermissionInfo permissionInfo : permissionInfos) { 207 if (isValidHealthPermission(permissionInfo)) { 208 assertWithMessage(FAIL_MESSAGE) 209 .that(ALL_EXPECTED_HEALTH_PERMISSIONS) 210 .contains(permissionInfo.name); 211 } 212 } 213 } 214 215 @Test testHealthConnectManager_noUnexpectedPermissionsReturned()216 public void testHealthConnectManager_noUnexpectedPermissionsReturned() throws Exception { 217 assertWithMessage(FAIL_MESSAGE) 218 .that(HealthConnectManager.getHealthPermissions(mContext)) 219 .isEqualTo(ALL_EXPECTED_HEALTH_PERMISSIONS); 220 } 221 222 @Test testReadExerciseRoutePerm_hasSignatureProtection()223 public void testReadExerciseRoutePerm_hasSignatureProtection() throws Exception { 224 assertThat( 225 mPackageManager 226 .getPermissionInfo(READ_EXERCISE_ROUTE, /* flags= */ 0) 227 .getProtection()) 228 .isEqualTo(PermissionInfo.PROTECTION_SIGNATURE); 229 } 230 231 @Test testReadExerciseRoutePerm_controllerUiHoldsIt()232 public void testReadExerciseRoutePerm_controllerUiHoldsIt() throws Exception { 233 String healthControllerPackageName = getControllerUiPackageName(); 234 assertThat( 235 mContext.getPackageManager() 236 .checkPermission(READ_EXERCISE_ROUTE, healthControllerPackageName)) 237 .isEqualTo(PackageManager.PERMISSION_GRANTED); 238 } 239 240 @Test testGetDataCategoriesWithWritePermissionsForPackage_returnsCorrectSet()241 public void testGetDataCategoriesWithWritePermissionsForPackage_returnsCorrectSet() { 242 mPackageInfo1.requestedPermissions = 243 new String[] { 244 HealthPermissions.WRITE_STEPS, 245 HealthPermissions.WRITE_SLEEP, 246 HealthPermissions.READ_HEART_RATE, 247 HealthPermissions.WRITE_OVULATION_TEST 248 }; 249 mPackageInfo1.requestedPermissionsFlags = 250 new int[] { 251 PackageInfo.REQUESTED_PERMISSION_GRANTED, 252 0, 253 PackageInfo.REQUESTED_PERMISSION_GRANTED, 254 PackageInfo.REQUESTED_PERMISSION_GRANTED 255 }; 256 257 Set<Integer> expectedResult = 258 Set.of(HealthDataCategory.ACTIVITY, HealthDataCategory.CYCLE_TRACKING); 259 Set<Integer> actualResult = 260 HealthPermissions.getDataCategoriesWithWritePermissionsForPackage( 261 mPackageInfo1, mContext); 262 assertThat(actualResult).isEqualTo(expectedResult); 263 } 264 265 @Test 266 public void testPackageHasWriteHealthPermissionsForCategory_ifNoWritePermissions_returnsFalse()267 testPackageHasWriteHealthPermissionsForCategory_ifNoWritePermissions_returnsFalse() { 268 mPackageInfo1.requestedPermissions = 269 new String[] { 270 HealthPermissions.WRITE_EXERCISE, 271 HealthPermissions.WRITE_SLEEP, 272 HealthPermissions.READ_HEART_RATE, 273 HealthPermissions.WRITE_OVULATION_TEST 274 }; 275 mPackageInfo1.requestedPermissionsFlags = 276 new int[] { 277 PackageInfo.REQUESTED_PERMISSION_GRANTED, 278 0, 279 PackageInfo.REQUESTED_PERMISSION_GRANTED, 280 PackageInfo.REQUESTED_PERMISSION_GRANTED 281 }; 282 283 assertThat( 284 HealthPermissions.getPackageHasWriteHealthPermissionsForCategory( 285 mPackageInfo1, HealthDataCategory.SLEEP, mContext)) 286 .isFalse(); 287 } 288 289 @Test testPackageHasWriteHealthPermissionsForCategory_ifWritePermissions_returnsTrue()290 public void testPackageHasWriteHealthPermissionsForCategory_ifWritePermissions_returnsTrue() { 291 mPackageInfo1.requestedPermissions = 292 new String[] { 293 HealthPermissions.WRITE_STEPS, 294 HealthPermissions.WRITE_SLEEP, 295 HealthPermissions.READ_HEART_RATE, 296 HealthPermissions.WRITE_OVULATION_TEST 297 }; 298 mPackageInfo1.requestedPermissionsFlags = 299 new int[] { 300 PackageInfo.REQUESTED_PERMISSION_GRANTED, 301 0, 302 PackageInfo.REQUESTED_PERMISSION_GRANTED, 303 PackageInfo.REQUESTED_PERMISSION_GRANTED 304 }; 305 306 assertThat( 307 HealthPermissions.getPackageHasWriteHealthPermissionsForCategory( 308 mPackageInfo1, HealthDataCategory.ACTIVITY, mContext)) 309 .isTrue(); 310 } 311 312 @Test 313 @EnableFlags({FLAG_PERSONAL_HEALTH_RECORD, FLAG_PERSONAL_HEALTH_RECORD_DATABASE}) testGetMedicalPermissions_returnsValidPermissions()314 public void testGetMedicalPermissions_returnsValidPermissions() { 315 Set<String> permissions = HealthPermissions.getAllMedicalPermissions(); 316 assertThat(permissions) 317 .containsAtLeast( 318 WRITE_MEDICAL_DATA, 319 READ_MEDICAL_DATA_ALLERGIES_INTOLERANCES, 320 READ_MEDICAL_DATA_CONDITIONS, 321 READ_MEDICAL_DATA_LABORATORY_RESULTS, 322 READ_MEDICAL_DATA_MEDICATIONS, 323 READ_MEDICAL_DATA_PERSONAL_DETAILS, 324 READ_MEDICAL_DATA_PRACTITIONER_DETAILS, 325 READ_MEDICAL_DATA_PREGNANCY, 326 READ_MEDICAL_DATA_PROCEDURES, 327 READ_MEDICAL_DATA_SOCIAL_HISTORY, 328 READ_MEDICAL_DATA_VACCINES, 329 READ_MEDICAL_DATA_VISITS, 330 READ_MEDICAL_DATA_VITAL_SIGNS); 331 } 332 333 @Test 334 @EnableFlags({FLAG_PERSONAL_HEALTH_RECORD, FLAG_PERSONAL_HEALTH_RECORD_DATABASE}) testGetMedicalPermissions_returnsAllMedicalPermissions()335 public void testGetMedicalPermissions_returnsAllMedicalPermissions() throws Exception { 336 Set<String> permissions = HealthPermissions.getAllMedicalPermissions(); 337 338 PermissionInfo[] permissionInfos = getHealthPermissionInfos(); 339 for (PermissionInfo permissionInfo : permissionInfos) { 340 if (isValidHealthPermission(permissionInfo) 341 && permissionInfo.name.contains(MEDICAL_PERMISSION_IDENTIFIER)) { 342 assertWithMessage(PHR_FAIL_MESSAGE).that(permissions).contains(permissionInfo.name); 343 } 344 } 345 } 346 getHealthPermissionInfos()347 private PermissionInfo[] getHealthPermissionInfos() throws Exception { 348 String healthControllerPackageName = getControllerUiPackageName(); 349 350 PackageInfo packageInfo = 351 mPackageManager.getPackageInfo( 352 healthControllerPackageName, 353 PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS)); 354 return packageInfo.permissions; 355 } 356 getControllerUiPackageName()357 private String getControllerUiPackageName() throws Exception { 358 return mPackageManager.getPermissionInfo(HealthPermissions.READ_STEPS, /* flags= */ 0) 359 .packageName; 360 } 361 } 362