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