1 /*
2  * Copyright (C) 2020 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.cts.statsdatom.permissionstate;
18 
19 import com.android.tradefed.util.RunUtil;
20 import static com.google.common.truth.Truth.assertThat;
21 
22 import android.cts.statsdatom.lib.AtomTestUtils;
23 import android.cts.statsdatom.lib.ConfigUtils;
24 import android.cts.statsdatom.lib.DeviceUtils;
25 import android.cts.statsdatom.lib.ReportUtils;
26 
27 import com.android.os.AtomsProto;
28 import com.android.tradefed.build.IBuildInfo;
29 import com.android.tradefed.testtype.DeviceTestCase;
30 import com.android.tradefed.testtype.IBuildReceiver;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 
35 public class DangerousPermissionStateTests extends DeviceTestCase implements IBuildReceiver {
36     private static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED = 1 << 8;
37 
38     private IBuildInfo mCtsBuild;
39 
40     @Override
setUp()41     protected void setUp() throws Exception {
42         super.setUp();
43         assertThat(mCtsBuild).isNotNull();
44         ConfigUtils.removeConfig(getDevice());
45         ReportUtils.clearReports(getDevice());
46         DeviceUtils.installStatsdTestApp(getDevice(), mCtsBuild);
47         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
48     }
49 
50     @Override
tearDown()51     protected void tearDown() throws Exception {
52         ConfigUtils.removeConfig(getDevice());
53         ReportUtils.clearReports(getDevice());
54         DeviceUtils.uninstallStatsdTestApp(getDevice());
55         super.tearDown();
56     }
57 
58     @Override
setBuild(IBuildInfo buildInfo)59     public void setBuild(IBuildInfo buildInfo) {
60         mCtsBuild = buildInfo;
61     }
62 
testDangerousPermissionState()63     public void testDangerousPermissionState() throws Exception {
64 
65         final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 1 << 9;
66         final int PROTECTION_FLAG_DANGEROUS = 1;
67         final int PROTECTION_FLAG_INSTANT = 0x1000;
68 
69         // Set up what to collect
70         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
71                 AtomsProto.Atom.DANGEROUS_PERMISSION_STATE_FIELD_NUMBER);
72 
73         boolean verifiedKnowPermissionState = false;
74 
75         // Pull a report
76         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
77         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
78 
79         int testAppId = getAppId(DeviceUtils.getStatsdTestAppUid(getDevice()));
80 
81         for (AtomsProto.Atom atom : ReportUtils.getGaugeMetricAtoms(getDevice())) {
82             AtomsProto.DangerousPermissionState permissionState = atom.getDangerousPermissionState();
83 
84             assertThat(permissionState.getPermissionName()).isNotNull();
85             assertThat(permissionState.getUid()).isAtLeast(0);
86             assertThat(permissionState.getPackageName()).isNotNull();
87 
88             if (getAppId(permissionState.getUid()) == testAppId) {
89 
90                 if (permissionState.getPermissionName().contains(
91                         "ACCESS_FINE_LOCATION")) {
92                     assertThat(permissionState.getIsGranted()).isTrue();
93                     assertThat(permissionState.getPermissionFlags() & ~(
94                             FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
95                                     | FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED))
96                             .isEqualTo(0);
97                     assertThat(permissionState.getProtectionFlags()).isEqualTo(
98                             PROTECTION_FLAG_DANGEROUS | PROTECTION_FLAG_INSTANT
99                     );
100 
101                     verifiedKnowPermissionState = true;
102                 }
103             }
104         }
105 
106         assertThat(verifiedKnowPermissionState).isTrue();
107     }
108 
testDangerousPermissionStateSampled()109     public void testDangerousPermissionStateSampled() throws Exception {
110         // get full atom for reference
111         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
112                 AtomsProto.Atom.DANGEROUS_PERMISSION_STATE_FIELD_NUMBER);
113 
114         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
115         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
116 
117         List<AtomsProto.DangerousPermissionState> fullDangerousPermissionState = new ArrayList<>();
118         for (AtomsProto.Atom atom : ReportUtils.getGaugeMetricAtoms(getDevice())) {
119             fullDangerousPermissionState.add(atom.getDangerousPermissionState());
120         }
121 
122         ConfigUtils.removeConfig(getDevice());
123         ReportUtils.clearReports(getDevice()); // Clears data.
124         List<AtomsProto.Atom> gaugeMetricDataList = null;
125 
126         // retries in case sampling returns full list or empty list - which should be extremely rare
127         for (int attempt = 0; attempt < 10; attempt++) {
128             // Set up what to collect
129             ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
130                     AtomsProto.Atom.DANGEROUS_PERMISSION_STATE_SAMPLED_FIELD_NUMBER);
131 
132             // Pull a report
133             AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
134             RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
135 
136             gaugeMetricDataList = ReportUtils.getGaugeMetricAtoms(getDevice());
137             if (gaugeMetricDataList.size() > 0
138                     && gaugeMetricDataList.size() < fullDangerousPermissionState.size()) {
139                 break;
140             }
141             ConfigUtils.removeConfig(getDevice());
142             ReportUtils.clearReports(getDevice()); // Clears data.
143         }
144         assertThat(gaugeMetricDataList.size()).isGreaterThan(0);
145         assertThat(gaugeMetricDataList.size()).isLessThan(fullDangerousPermissionState.size());
146 
147         long lastUid = -1;
148         int fullIndex = 0;
149 
150         for (AtomsProto.Atom atom : ReportUtils.getGaugeMetricAtoms(getDevice())) {
151             AtomsProto.DangerousPermissionStateSampled permissionState =
152                     atom.getDangerousPermissionStateSampled();
153 
154             AtomsProto.DangerousPermissionState referenceState
155                     = fullDangerousPermissionState.get(fullIndex);
156 
157             if (referenceState.getUid() != permissionState.getUid()) {
158                 // atoms are sampled on uid basis if uid is present, all related permissions must
159                 // be logged.
160                 assertThat(permissionState.getUid()).isNotEqualTo(lastUid);
161                 continue;
162             }
163 
164             lastUid = permissionState.getUid();
165 
166             assertThat(permissionState.getPermissionFlags()).isEqualTo(
167                     referenceState.getPermissionFlags());
168             assertThat(permissionState.getIsGranted()).isEqualTo(referenceState.getIsGranted());
169             assertThat(permissionState.getPermissionName()).isEqualTo(
170                     referenceState.getPermissionName());
171             assertThat(permissionState.getProtectionFlags()).isEqualTo(
172                     referenceState.getProtectionFlags());
173 
174             fullIndex++;
175         }
176     }
177 
178     /**
179      * The app id from a uid.
180      *
181      * @param uid The uid of the app
182      *
183      * @return The app id of the app
184      *
185      * @see android.os.UserHandle#getAppId
186      */
getAppId(int uid)187     private static int getAppId(int uid) {
188         return uid % 100000;
189     }
190 }
191