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