Home | History | Annotate | Download | only in atom
      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
     50     protected void setUp() throws Exception {
     51         super.setUp();
     52         getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
     53         installTestApp();
     54     }
     55 
     56     @Override
     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      */
     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      */
     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 
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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. */
    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      */
    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      */
    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      */
    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 
    267     protected void resetBatteryStats() throws Exception {
    268         getDevice().executeShellCommand("dumpsys batterystats --reset");
    269     }
    270 
    271     protected void clearProcStats() throws Exception {
    272         getDevice().executeShellCommand("dumpsys procstats --clear");
    273     }
    274 
    275     protected void startProcStatsTesting() throws Exception {
    276         getDevice().executeShellCommand("dumpsys procstats --start-testing");
    277     }
    278 
    279     protected void stopProcStatsTesting() throws Exception {
    280         getDevice().executeShellCommand("dumpsys procstats --stop-testing");
    281     }
    282 
    283     protected void commitProcStatsToDisk() throws Exception {
    284         getDevice().executeShellCommand("dumpsys procstats --commit");
    285     }
    286 
    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 
    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