• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     public static final String DEVICE_SIDE_TEST_APK = "CtsStatsdApp.apk";
34     public static final String DEVICE_SIDE_TEST_PACKAGE =
35             "com.android.server.cts.device.statsd";
36     public static final String DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME =
37             "com.android.server.cts.device.statsd.StatsdCtsForegroundService";
38     private static final String DEVICE_SIDE_BG_SERVICE_COMPONENT =
39             "com.android.server.cts.device.statsd/.StatsdCtsBackgroundService";
40     public static final long DEVICE_SIDE_TEST_PKG_HASH =
41             Long.parseUnsignedLong("15694052924544098582");
42 
43     // Constants from device side tests (not directly accessible here).
44     public static final String KEY_ACTION = "action";
45     public static final String ACTION_LMK = "action.lmk";
46 
47     public static final String CONFIG_NAME = "cts_config";
48 
49     @Override
setUp()50     protected void setUp() throws Exception {
51         super.setUp();
52         getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
53         installTestApp();
54     }
55 
56     @Override
tearDown()57     protected void tearDown() throws Exception {
58         getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
59         super.tearDown();
60     }
61 
62     /**
63      * Performs a device-side test by calling a method on the app and returns its stats events.
64      * @param methodName the name of the method in the app's AtomTests to perform
65      * @param atom atom tag (from atoms.proto)
66      * @param key atom's field corresponding to state
67      * @param stateOn 'on' value
68      * @param stateOff 'off' value
69      * @param minTimeDiffMs max allowed time between start and stop
70      * @param maxTimeDiffMs min allowed time between start and stop
71      * @param demandExactlyTwo whether there must be precisely two events logged (1 start, 1 stop)
72      * @return list of events with the app's uid matching the configuration defined by the params.
73      */
doDeviceMethodOnOff( String methodName, int atom, int key, int stateOn, int stateOff, int minTimeDiffMs, int maxTimeDiffMs, boolean demandExactlyTwo)74     protected List<EventMetricData> doDeviceMethodOnOff(
75             String methodName, int atom, int key, int stateOn, int stateOff,
76             int minTimeDiffMs, int maxTimeDiffMs, boolean demandExactlyTwo) throws Exception {
77         StatsdConfig.Builder conf = createConfigBuilder();
78         addAtomEvent(conf, atom, createFvm(key).setEqInt(stateOn));
79         addAtomEvent(conf, atom, createFvm(key).setEqInt(stateOff));
80         List<EventMetricData> data = doDeviceMethod(methodName, conf);
81 
82         if (demandExactlyTwo) {
83             assertEquals(2, data.size());
84         } else {
85             assertTrue("data.size() [" + data.size() + "] should be >= 2", data.size() >= 2);
86         }
87         assertTimeDiffBetween(data.get(0), data.get(1), minTimeDiffMs, maxTimeDiffMs);
88         return data;
89     }
90 
91     /**
92      *
93      * @param methodName the name of the method in the app's AtomTests to perform
94      * @param cfg statsd configuration
95      * @return list of events with the app's uid matching the configuration.
96      */
doDeviceMethod(String methodName, StatsdConfig.Builder cfg)97     protected List<EventMetricData> doDeviceMethod(String methodName, StatsdConfig.Builder cfg)
98             throws Exception {
99         removeConfig(CONFIG_ID);
100         getReportList();  // Clears previous data on disk.
101         uploadConfig(cfg);
102         int appUid = getUid();
103         LogUtil.CLog.d("\nPerforming device-side test of " + methodName + " for uid " + appUid);
104         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", methodName);
105 
106         return getEventMetricDataList();
107     }
108 
createAndUploadConfig(int atomTag, boolean useAttribution)109     protected void createAndUploadConfig(int atomTag, boolean useAttribution) throws Exception {
110         StatsdConfig.Builder conf = createConfigBuilder();
111         addAtomEvent(conf, atomTag, useAttribution);
112         uploadConfig(conf);
113     }
114 
115     /**
116      * Adds an event to the config for an atom that matches the given key AND has the app's uid.
117      * @param conf configuration
118      * @param atomTag atom tag (from atoms.proto)
119      * @param fvm FieldValueMatcher.Builder for the relevant key
120      */
121     @Override
addAtomEvent(StatsdConfig.Builder conf, int atomTag, FieldValueMatcher.Builder fvm)122     protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag, FieldValueMatcher.Builder fvm)
123             throws Exception {
124 
125         final int UID_KEY = 1;
126         FieldValueMatcher.Builder fvmUid = createAttributionFvm(UID_KEY);
127         addAtomEvent(conf, atomTag, Arrays.asList(fvm, fvmUid));
128     }
129 
130     /**
131      * Adds an event to the config for an atom that matches the app's uid.
132      * @param conf configuration
133      * @param atomTag atom tag (from atoms.proto)
134      * @param useAttribution if the atom has a uid or AttributionNode
135      */
addAtomEvent(StatsdConfig.Builder conf, int atomTag, boolean useAttribution)136     protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag,
137             boolean useAttribution) throws Exception {
138         final int UID_KEY = 1;
139         FieldValueMatcher.Builder fvmUid;
140         if (useAttribution) {
141             fvmUid = createAttributionFvm(UID_KEY);
142         } else {
143             fvmUid = createFvm(UID_KEY).setEqInt(getUid());
144         }
145         addAtomEvent(conf, atomTag, Arrays.asList(fvmUid));
146     }
147 
148     /**
149      * Creates a FieldValueMatcher for atoms that use AttributionNode
150      */
createAttributionFvm(int field)151     protected FieldValueMatcher.Builder createAttributionFvm(int field) {
152         final int ATTRIBUTION_NODE_UID_KEY = 1;
153         return createFvm(field).setPosition(Position.ANY)
154                 .setMatchesTuple(MessageMatcher.newBuilder()
155                         .addFieldValueMatcher(createFvm(ATTRIBUTION_NODE_UID_KEY)
156                                 .setEqString(DEVICE_SIDE_TEST_PACKAGE)));
157     }
158 
159     /**
160      * Gets the uid of the test app.
161      */
getUid()162     protected int getUid() throws Exception {
163         int currentUser = getDevice().getCurrentUser();
164         String uidLine = getDevice().executeShellCommand("cmd package list packages -U --user "
165                 + currentUser + " " + DEVICE_SIDE_TEST_PACKAGE);
166         String[] uidLineParts = uidLine.split(":");
167         // 3rd entry is package uid
168         assertTrue(uidLineParts.length > 2);
169         int uid = Integer.parseInt(uidLineParts[2].trim());
170         assertTrue(uid > 10000);
171         return uid;
172     }
173 
174     /**
175      * Installs the test apk.
176      */
installTestApp()177     protected void installTestApp() throws Exception {
178         installPackage(DEVICE_SIDE_TEST_APK, true);
179         LogUtil.CLog.i("Installing device-side test app with uid " + getUid());
180         allowBackgroundServices();
181     }
182 
183     /**
184      * Uninstalls the test apk.
185      */
uninstallPackage()186     protected void uninstallPackage() throws Exception{
187         getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
188     }
189 
190     /**
191      * Required to successfully start a background service from adb in O.
192      */
allowBackgroundServices()193     protected void allowBackgroundServices() throws Exception {
194         getDevice().executeShellCommand(String.format(
195                 "cmd deviceidle tempwhitelist %s", DEVICE_SIDE_TEST_PACKAGE));
196     }
197 
198     /**
199      * Runs a (background) service to perform the given action.
200      * @param actionValue the action code constants indicating the desired action to perform.
201      */
executeBackgroundService(String actionValue)202     protected void executeBackgroundService(String actionValue) throws Exception {
203         allowBackgroundServices();
204         getDevice().executeShellCommand(String.format(
205                 "am startservice -n '%s' -e %s %s",
206                 DEVICE_SIDE_BG_SERVICE_COMPONENT,
207                 KEY_ACTION, actionValue));
208     }
209 
210 
211     /** Make the test app standby-active so it can run syncs and jobs immediately. */
allowImmediateSyncs()212     protected void allowImmediateSyncs() throws Exception {
213         getDevice().executeShellCommand("am set-standby-bucket "
214                 + DEVICE_SIDE_TEST_PACKAGE + " active");
215     }
216 
217     /**
218      * Runs the specified activity.
219      */
runActivity(String activity, String actionKey, String actionValue)220     protected void runActivity(String activity, String actionKey, String actionValue)
221             throws Exception {
222         runActivity(activity, actionKey, actionValue, WAIT_TIME_LONG);
223     }
224 
225     /**
226      * Runs the specified activity.
227      */
runActivity(String activity, String actionKey, String actionValue, long waitTime)228     protected void runActivity(String activity, String actionKey, String actionValue,
229             long waitTime) throws Exception {
230         try (AutoCloseable a = withActivity(activity, actionKey, actionValue)) {
231             Thread.sleep(waitTime);
232         }
233     }
234 
235     /**
236      * Starts the specified activity and returns an {@link AutoCloseable} that stops the activity
237      * when closed.
238      *
239      * <p>Example usage:
240      * <pre>
241      *     try (AutoClosable a = withActivity("activity", "action", "action-value")) {
242      *         doStuff();
243      *     }
244      * </pre>
245      */
withActivity(String activity, String actionKey, String actionValue)246     protected AutoCloseable withActivity(String activity, String actionKey, String actionValue)
247             throws Exception {
248         String intentString = null;
249         if (actionKey != null && actionValue != null) {
250             intentString = actionKey + " " + actionValue;
251         }
252         if (intentString == null) {
253             getDevice().executeShellCommand(
254                     "am start -n " + DEVICE_SIDE_TEST_PACKAGE + "/." + activity);
255         } else {
256             getDevice().executeShellCommand(
257                     "am start -n " + DEVICE_SIDE_TEST_PACKAGE + "/." + activity + " -e " +
258                             intentString);
259         }
260         return () -> {
261             getDevice().executeShellCommand(
262                     "am force-stop " + DEVICE_SIDE_TEST_PACKAGE);
263             Thread.sleep(WAIT_TIME_SHORT);
264         };
265     }
266 
resetBatteryStats()267     protected void resetBatteryStats() throws Exception {
268         getDevice().executeShellCommand("dumpsys batterystats --reset");
269     }
270 
clearProcStats()271     protected void clearProcStats() throws Exception {
272         getDevice().executeShellCommand("dumpsys procstats --clear");
273     }
274 
startProcStatsTesting()275     protected void startProcStatsTesting() throws Exception {
276         getDevice().executeShellCommand("dumpsys procstats --start-testing");
277     }
278 
stopProcStatsTesting()279     protected void stopProcStatsTesting() throws Exception {
280         getDevice().executeShellCommand("dumpsys procstats --stop-testing");
281     }
282 
commitProcStatsToDisk()283     protected void commitProcStatsToDisk() throws Exception {
284         getDevice().executeShellCommand("dumpsys procstats --commit");
285     }
286 
rebootDeviceAndWaitUntilReady()287     protected void rebootDeviceAndWaitUntilReady() throws Exception {
288         rebootDevice();
289         // Wait for 2 mins.
290         assertTrue("Device failed to boot", getDevice().waitForBootComplete(120_000));
291         assertTrue("Stats service failed to start", waitForStatsServiceStart(60_000));
292         Thread.sleep(2_000);
293     }
294 
waitForStatsServiceStart(final long waitTime)295     protected boolean waitForStatsServiceStart(final long waitTime) throws Exception {
296         LogUtil.CLog.i("Waiting %d ms for stats service to start", waitTime);
297         int counter = 1;
298         long startTime = System.currentTimeMillis();
299         while ((System.currentTimeMillis() - startTime) < waitTime) {
300             if ("running".equals(getProperty("init.svc.statsd"))) {
301                 return true;
302             }
303             Thread.sleep(Math.min(200 * counter, 2_000));
304             counter++;
305         }
306         LogUtil.CLog.w("Stats service did not start after %d ms", waitTime);
307         return false;
308     }
309 }
310