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