1 /* 2 * Copyright (C) 2017 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 package android.cts.statsd.atom; 17 18 import com.android.internal.os.StatsdConfigProto.FieldValueMatcher; 19 import com.android.internal.os.StatsdConfigProto.MessageMatcher; 20 import com.android.internal.os.StatsdConfigProto.Position; 21 import com.android.internal.os.StatsdConfigProto.StatsdConfig; 22 import com.android.os.StatsLog.EventMetricData; 23 import com.android.tradefed.log.LogUtil; 24 25 import java.util.Arrays; 26 import java.util.List; 27 28 /** 29 * Base class for testing Statsd atoms that report a uid. Tests are performed via a device-side app. 30 */ 31 public class DeviceAtomTestCase extends AtomTestCase { 32 33 protected static final String DEVICE_SIDE_TEST_APK = "CtsStatsdApp.apk"; 34 protected static final String DEVICE_SIDE_TEST_PACKAGE = 35 "com.android.server.cts.device.statsd"; 36 protected static final long DEVICE_SIDE_TEST_PKG_HASH = 37 Long.parseUnsignedLong("15694052924544098582"); 38 39 protected static final String CONFIG_NAME = "cts_config"; 40 41 @Override 42 protected void setUp() throws Exception { 43 super.setUp(); 44 getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE); 45 installTestApp(); 46 } 47 48 @Override 49 protected void tearDown() throws Exception { 50 getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE); 51 super.tearDown(); 52 } 53 54 /** 55 * Performs a device-side test by calling a method on the app and returns its stats events. 56 * @param methodName the name of the method in the app's AtomTests to perform 57 * @param atom atom tag (from atoms.proto) 58 * @param key atom's field corresponding to state 59 * @param stateOn 'on' value 60 * @param stateOff 'off' value 61 * @param minTimeDiffMs max allowed time between start and stop 62 * @param maxTimeDiffMs min allowed time between start and stop 63 * @param demandExactlyTwo whether there must be precisely two events logged (1 start, 1 stop) 64 * @return list of events with the app's uid matching the configuration defined by the params. 65 */ 66 protected List<EventMetricData> doDeviceMethodOnOff( 67 String methodName, int atom, int key, int stateOn, int stateOff, 68 int minTimeDiffMs, int maxTimeDiffMs, boolean demandExactlyTwo) throws Exception { 69 StatsdConfig.Builder conf = createConfigBuilder(); 70 addAtomEvent(conf, atom, createFvm(key).setEqInt(stateOn)); 71 addAtomEvent(conf, atom, createFvm(key).setEqInt(stateOff)); 72 List<EventMetricData> data = doDeviceMethod(methodName, conf); 73 74 if (demandExactlyTwo) { 75 assertTrue(data.size() == 2); 76 } else { 77 assertTrue(data.size() >= 2); 78 } 79 assertTimeDiffBetween(data.get(0), data.get(1), minTimeDiffMs, maxTimeDiffMs); 80 return data; 81 } 82 83 /** 84 * 85 * @param methodName the name of the method in the app's AtomTests to perform 86 * @param cfg statsd configuration 87 * @return list of events with the app's uid matching the configuration. 88 */ 89 protected List<EventMetricData> doDeviceMethod(String methodName, StatsdConfig.Builder cfg) 90 throws Exception { 91 removeConfig(CONFIG_ID); 92 getReportList(); // Clears previous data on disk. 93 uploadConfig(cfg); 94 int appUid = getUid(); 95 LogUtil.CLog.d("\nPerforming device-side test of " + methodName + " for uid " + appUid); 96 runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", methodName); 97 98 return getEventMetricDataList(); 99 } 100 101 protected void createAndUploadConfig(int atomTag, boolean useAttribution) throws Exception { 102 StatsdConfig.Builder conf = createConfigBuilder(); 103 addAtomEvent(conf, atomTag, useAttribution); 104 uploadConfig(conf); 105 } 106 107 /** 108 * Adds an event to the config for an atom that matches the given key AND has the app's uid. 109 * @param conf configuration 110 * @param atomTag atom tag (from atoms.proto) 111 * @param fvm FieldValueMatcher.Builder for the relevant key 112 */ 113 @Override 114 protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag, FieldValueMatcher.Builder fvm) 115 throws Exception { 116 117 final int UID_KEY = 1; 118 FieldValueMatcher.Builder fvmUid = createAttributionFvm(UID_KEY); 119 addAtomEvent(conf, atomTag, Arrays.asList(fvm, fvmUid)); 120 } 121 122 /** 123 * Adds an event to the config for an atom that matches the app's uid. 124 * @param conf configuration 125 * @param atomTag atom tag (from atoms.proto) 126 * @param useAttribution if the atom has a uid or AttributionNode 127 */ 128 protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag, 129 boolean useAttribution) throws Exception { 130 final int UID_KEY = 1; 131 FieldValueMatcher.Builder fvmUid; 132 if (useAttribution) { 133 fvmUid = createAttributionFvm(UID_KEY); 134 } else { 135 fvmUid = createFvm(UID_KEY).setEqInt(getUid()); 136 } 137 addAtomEvent(conf, atomTag, Arrays.asList(fvmUid)); 138 } 139 140 /** 141 * Creates a FieldValueMatcher for atoms that use AttributionNode 142 */ 143 protected FieldValueMatcher.Builder createAttributionFvm(int field) { 144 final int ATTRIBUTION_NODE_UID_KEY = 1; 145 return createFvm(field).setPosition(Position.ANY) 146 .setMatchesTuple(MessageMatcher.newBuilder() 147 .addFieldValueMatcher(createFvm(ATTRIBUTION_NODE_UID_KEY) 148 .setEqString(DEVICE_SIDE_TEST_PACKAGE))); 149 } 150 151 /** 152 * Gets the uid of the test app. 153 */ 154 protected int getUid() throws Exception { 155 String uidLine = getDevice().executeShellCommand("cmd package list packages -U " 156 + DEVICE_SIDE_TEST_PACKAGE); 157 String[] uidLineParts = uidLine.split(":"); 158 // 3rd entry is package uid 159 assertTrue(uidLineParts.length > 2); 160 int uid = Integer.parseInt(uidLineParts[2].trim()); 161 assertTrue(uid > 10000); 162 return uid; 163 } 164 165 /** 166 * Installs the test apk. 167 */ 168 protected void installTestApp() throws Exception { 169 installPackage(DEVICE_SIDE_TEST_APK, true); 170 LogUtil.CLog.i("Installing device-side test app with uid " + getUid()); 171 allowBackgroundServices(); 172 } 173 174 /** 175 * Required to successfully start a background service from adb in O. 176 */ 177 protected void allowBackgroundServices() throws Exception { 178 getDevice().executeShellCommand(String.format( 179 "cmd deviceidle tempwhitelist %s", DEVICE_SIDE_TEST_PACKAGE)); 180 } 181 182 /** Make the test app standby-active so it can run syncs and jobs immediately. */ 183 protected void allowImmediateSyncs() throws Exception { 184 getDevice().executeShellCommand("am set-standby-bucket " 185 + DEVICE_SIDE_TEST_PACKAGE + " active"); 186 } 187 188 /** 189 * Runs the specified activity. 190 */ 191 protected void runActivity(String activity, String actionKey, String actionValue) 192 throws Exception { 193 runActivity(activity, actionKey, actionValue, WAIT_TIME_LONG); 194 } 195 196 /** 197 * Runs the specified activity. 198 */ 199 protected void runActivity(String activity, String actionKey, String actionValue, 200 long waitTime) throws Exception { 201 String intentString = null; 202 if (actionKey != null && actionValue != null) { 203 intentString = actionKey + " " + actionValue; 204 } 205 if (intentString == null) { 206 getDevice().executeShellCommand( 207 "am start -n " + DEVICE_SIDE_TEST_PACKAGE + "/." + activity); 208 } else { 209 getDevice().executeShellCommand( 210 "am start -n " + DEVICE_SIDE_TEST_PACKAGE + "/." + activity + " -e " + 211 intentString); 212 } 213 214 Thread.sleep(waitTime); 215 getDevice().executeShellCommand( 216 "am force-stop " + DEVICE_SIDE_TEST_PACKAGE); 217 218 Thread.sleep(WAIT_TIME_SHORT); 219 } 220 221 protected void resetBatteryStats() throws Exception { 222 getDevice().executeShellCommand("dumpsys batterystats --reset"); 223 } 224 } 225