• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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