Home | History | Annotate | Download | only in cts
      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 com.android.server.cts;
     17 
     18 import com.android.tradefed.log.LogUtil;
     19 
     20 import java.util.Random;
     21 
     22 /**
     23  * Test for "dumpsys batterystats -c
     24  *
     25  * Validates reporting of battery stats based on different events
     26  */
     27 public class BatteryStatsValidationTest extends ProtoDumpTestCase {
     28     private static final String TAG = "BatteryStatsValidationTest";
     29 
     30     private static final String DEVICE_SIDE_TEST_APK = "CtsBatteryStatsApp.apk";
     31     private static final String DEVICE_SIDE_TEST_PACKAGE
     32             = "com.android.server.cts.device.batterystats";
     33     private static final String DEVICE_SIDE_BG_SERVICE_COMPONENT
     34             = "com.android.server.cts.device.batterystats/.BatteryStatsBackgroundService";
     35     private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT
     36             = "com.android.server.cts.device.batterystats/.BatteryStatsForegroundActivity";
     37     private static final String DEVICE_SIDE_JOB_COMPONENT
     38             = "com.android.server.cts.device.batterystats/.SimpleJobService";
     39     private static final String DEVICE_SIDE_SYNC_COMPONENT
     40             = "com.android.server.cts.device.batterystats.provider/"
     41             + "com.android.server.cts.device.batterystats";
     42 
     43     // These constants are those in PackageManager.
     44     public static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
     45     public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
     46     public static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
     47 
     48     private static final int STATE_TIME_TOP_INDEX = 4;
     49     private static final int STATE_TIME_FOREGROUND_SERVICE_INDEX = 5;
     50     private static final int STATE_TIME_FOREGROUND_INDEX = 6;
     51     private static final int STATE_TIME_BACKGROUND_INDEX = 7;
     52     private static final int STATE_TIME_CACHED_INDEX = 10;
     53 
     54     private static final long TIME_SPENT_IN_TOP = 2000;
     55     private static final long TIME_SPENT_IN_FOREGROUND = 2000;
     56     private static final long TIME_SPENT_IN_BACKGROUND = 2000;
     57     private static final long TIME_SPENT_IN_CACHED = 4000;
     58     private static final long SCREEN_STATE_CHANGE_TIMEOUT = 4000;
     59     private static final long SCREEN_STATE_POLLING_INTERVAL = 500;
     60 
     61     // Constants from BatteryStatsBgVsFgActions.java (not directly accessible here).
     62     public static final String KEY_ACTION = "action";
     63     public static final String ACTION_BLE_SCAN_OPTIMIZED = "action.ble_scan_optimized";
     64     public static final String ACTION_BLE_SCAN_UNOPTIMIZED = "action.ble_scan_unoptimized";
     65     public static final String ACTION_GPS = "action.gps";
     66     public static final String ACTION_JOB_SCHEDULE = "action.jobs";
     67     public static final String ACTION_SYNC = "action.sync";
     68     public static final String ACTION_SLEEP_WHILE_BACKGROUND = "action.sleep_background";
     69     public static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
     70     public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
     71 
     72     public static final String KEY_REQUEST_CODE = "request_code";
     73     public static final String BG_VS_FG_TAG = "BatteryStatsBgVsFgActions";
     74 
     75     // Constants from BatteryMangager.
     76     public static final int BATTERY_STATUS_DISCHARGING = 3;
     77 
     78     @Override
     79     protected void setUp() throws Exception {
     80         super.setUp();
     81 
     82         // Uninstall to clear the history in case it's still on the device.
     83         getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
     84     }
     85 
     86     @Override
     87     protected void tearDown() throws Exception {
     88         getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
     89 
     90         batteryOffScreenOn();
     91         super.tearDown();
     92     }
     93 
     94     protected void screenOff() throws Exception {
     95         getDevice().executeShellCommand("dumpsys batterystats enable pretend-screen-off");
     96     }
     97 
     98     /**
     99      * This will turn the screen on for real, not just disabling pretend-screen-off
    100      */
    101     protected void turnScreenOnForReal() throws Exception {
    102         getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
    103         getDevice().executeShellCommand("wm dismiss-keyguard");
    104     }
    105 
    106     /**
    107      * This will send the screen to sleep
    108      */
    109     protected void turnScreenOffForReal() throws Exception {
    110         getDevice().executeShellCommand("input keyevent KEYCODE_SLEEP");
    111     }
    112 
    113     protected void batteryOnScreenOn() throws Exception {
    114         getDevice().executeShellCommand("dumpsys battery unplug");
    115         getDevice().executeShellCommand("dumpsys battery set status " + BATTERY_STATUS_DISCHARGING);
    116         getDevice().executeShellCommand("dumpsys batterystats disable pretend-screen-off");
    117     }
    118 
    119     protected void batteryOnScreenOff() throws Exception {
    120         getDevice().executeShellCommand("dumpsys battery unplug");
    121         getDevice().executeShellCommand("dumpsys battery set status " + BATTERY_STATUS_DISCHARGING);
    122         getDevice().executeShellCommand("dumpsys batterystats enable pretend-screen-off");
    123     }
    124 
    125     protected void batteryOffScreenOn() throws Exception {
    126         getDevice().executeShellCommand("dumpsys battery reset");
    127         getDevice().executeShellCommand("dumpsys batterystats disable pretend-screen-off");
    128     }
    129 
    130     private void forceStop() throws Exception {
    131         getDevice().executeShellCommand("am force-stop " + DEVICE_SIDE_TEST_PACKAGE);
    132     }
    133 
    134     private void resetBatteryStats() throws Exception {
    135         getDevice().executeShellCommand("dumpsys batterystats --reset");
    136     }
    137 
    138     public void testAlarms() throws Exception {
    139         batteryOnScreenOff();
    140 
    141         installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
    142 
    143         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsAlarmTest", "testAlarms");
    144 
    145         assertValueRange("wua", "*walarm*:com.android.server.cts.device.batterystats.ALARM",
    146                 5, 3, 3);
    147 
    148         batteryOffScreenOn();
    149     }
    150 
    151     public void testWakeLockDuration() throws Exception {
    152         batteryOnScreenOff();
    153 
    154         installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
    155 
    156         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsWakeLockTests",
    157                 "testHoldShortWakeLock");
    158 
    159         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsWakeLockTests",
    160                 "testHoldLongWakeLock");
    161 
    162         assertValueRange("wl", "BSShortWakeLock", 15, (long) (500 * 0.9), 500 * 2); // partial max duration
    163         assertValueRange("wl", "BSLongWakeLock", 15, (long) (3000 * 0.9), 3000 * 2);  // partial max duration
    164 
    165         batteryOffScreenOn();
    166     }
    167 
    168     private void startSimpleActivity() throws Exception {
    169         getDevice().executeShellCommand(
    170                 "am start -n com.android.server.cts.device.batterystats/.SimpleActivity");
    171     }
    172 
    173     public void testServiceForegroundDuration() throws Exception {
    174         batteryOnScreenOff();
    175         installPackage(DEVICE_SIDE_TEST_APK, true);
    176 
    177         startSimpleActivity();
    178         assertValueRange("st", "", STATE_TIME_FOREGROUND_SERVICE_INDEX, 0,
    179                 0); // No foreground service time before test
    180         final long startTime = System.nanoTime();
    181         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsProcessStateTests",
    182                 "testForegroundService");
    183         assertValueRange("st", "", STATE_TIME_FOREGROUND_SERVICE_INDEX, (long) (2000 * 0.8),
    184                 (System.nanoTime() - startTime) / 1000000);
    185         batteryOffScreenOn();
    186     }
    187 
    188     public void testUidForegroundDuration() throws Exception {
    189         batteryOnScreenOff();
    190         installPackage(DEVICE_SIDE_TEST_APK, true);
    191         // No foreground time before test
    192         assertValueRange("st", "", STATE_TIME_FOREGROUND_INDEX, 0, 0);
    193         turnScreenOnForReal();
    194         assertScreenOn();
    195         executeForeground(ACTION_SHOW_APPLICATION_OVERLAY, 2000);
    196         Thread.sleep(TIME_SPENT_IN_FOREGROUND); // should be in foreground for about this long
    197         assertApproximateTimeInState(STATE_TIME_FOREGROUND_INDEX, TIME_SPENT_IN_FOREGROUND);
    198         batteryOffScreenOn();
    199     }
    200 
    201     public void testUidBackgroundDuration() throws Exception {
    202         batteryOnScreenOff();
    203         installPackage(DEVICE_SIDE_TEST_APK, true);
    204         // No background time before test
    205         assertValueRange("st", "", STATE_TIME_BACKGROUND_INDEX, 0, 0);
    206         executeBackground(ACTION_SLEEP_WHILE_BACKGROUND, 4000);
    207         assertApproximateTimeInState(STATE_TIME_BACKGROUND_INDEX, TIME_SPENT_IN_BACKGROUND);
    208         batteryOffScreenOn();
    209     }
    210 
    211     public void testTopDuration() throws Exception {
    212         batteryOnScreenOff();
    213         installPackage(DEVICE_SIDE_TEST_APK, true);
    214         // No top time before test
    215         assertValueRange("st", "", STATE_TIME_TOP_INDEX, 0, 0);
    216         turnScreenOnForReal();
    217         assertScreenOn();
    218         executeForeground(ACTION_SLEEP_WHILE_TOP, 4000);
    219         assertApproximateTimeInState(STATE_TIME_TOP_INDEX, TIME_SPENT_IN_TOP);
    220         batteryOffScreenOn();
    221     }
    222 
    223     public void testCachedDuration() throws Exception {
    224         batteryOnScreenOff();
    225         installPackage(DEVICE_SIDE_TEST_APK, true);
    226         // No cached time before test
    227         assertValueRange("st", "", STATE_TIME_CACHED_INDEX, 0, 0);
    228         startSimpleActivity();
    229         Thread.sleep(TIME_SPENT_IN_CACHED); // process should be in cached state for about this long
    230         assertApproximateTimeInState(STATE_TIME_CACHED_INDEX, TIME_SPENT_IN_CACHED);
    231         batteryOffScreenOn();
    232     }
    233 
    234     private void assertScreenOff() throws Exception {
    235         final long deadLine = System.currentTimeMillis() + SCREEN_STATE_CHANGE_TIMEOUT;
    236         boolean screenAwake = true;
    237         do {
    238             final String dumpsysPower = getDevice().executeShellCommand("dumpsys power").trim();
    239             for (String line : dumpsysPower.split("\n")) {
    240                 if (line.contains("Display Power")) {
    241                     screenAwake = line.trim().endsWith("ON");
    242                     break;
    243                 }
    244             }
    245             Thread.sleep(SCREEN_STATE_POLLING_INTERVAL);
    246         } while (screenAwake && System.currentTimeMillis() < deadLine);
    247         assertFalse("Screen could not be turned off", screenAwake);
    248     }
    249 
    250     private void assertScreenOn() throws Exception {
    251         // this also checks that the keyguard is dismissed
    252         final long deadLine = System.currentTimeMillis() + SCREEN_STATE_CHANGE_TIMEOUT;
    253         boolean screenAwake;
    254         do {
    255             final String dumpsysWindowPolicy =
    256                     getDevice().executeShellCommand("dumpsys window policy").trim();
    257             boolean keyguardStateLines = false;
    258             screenAwake = true;
    259             for (String line : dumpsysWindowPolicy.split("\n")) {
    260                 if (line.contains("KeyguardServiceDelegate")) {
    261                     keyguardStateLines = true;
    262                 } else if (keyguardStateLines && line.contains("showing=")) {
    263                     screenAwake &= line.trim().endsWith("false");
    264                 } else if (keyguardStateLines && line.contains("screenState=")) {
    265                     screenAwake &= line.trim().endsWith("SCREEN_STATE_ON");
    266                 }
    267             }
    268             Thread.sleep(SCREEN_STATE_POLLING_INTERVAL);
    269         } while (!screenAwake && System.currentTimeMillis() < deadLine);
    270         assertTrue("Screen could not be turned on", screenAwake);
    271     }
    272 
    273     public void testBleScans() throws Exception {
    274         if (noBattery() || !hasFeature(FEATURE_BLUETOOTH_LE, true)) {
    275             return;
    276         }
    277 
    278         batteryOnScreenOff();
    279         installPackage(DEVICE_SIDE_TEST_APK, true);
    280         turnScreenOnForReal();
    281         assertScreenOn();
    282 
    283         // Background test.
    284         executeBackground(ACTION_BLE_SCAN_UNOPTIMIZED, 40_000);
    285         assertValueRange("blem", "", 5, 1, 1); // ble_scan_count
    286         assertValueRange("blem", "", 6, 1, 1); // ble_scan_count_bg
    287 
    288         // Foreground test.
    289         executeForeground(ACTION_BLE_SCAN_UNOPTIMIZED, 40_000);
    290         assertValueRange("blem", "", 5, 2, 2); // ble_scan_count
    291         assertValueRange("blem", "", 6, 1, 1); // ble_scan_count_bg
    292 
    293         batteryOffScreenOn();
    294     }
    295 
    296 
    297     public void testUnoptimizedBleScans() throws Exception {
    298         if (noBattery() || !hasFeature(FEATURE_BLUETOOTH_LE, true)) {
    299             return;
    300         }
    301         batteryOnScreenOff();
    302         installPackage(DEVICE_SIDE_TEST_APK, true);
    303         turnScreenOnForReal();
    304         assertScreenOn();
    305         // Ble scan time in BatteryStatsBgVsFgActions is 2 seconds, but be lenient.
    306         final int minTime = 1500; // min single scan time in ms
    307         final int maxTime = 3000; // max single scan time in ms
    308 
    309         // Optimized - Background.
    310         executeBackground(ACTION_BLE_SCAN_OPTIMIZED, 40_000);
    311         assertValueRange("blem", "", 7, 1*minTime, 1*maxTime); // actualTime
    312         assertValueRange("blem", "", 8, 1*minTime, 1*maxTime); // actualTimeBg
    313         assertValueRange("blem", "", 11, 0, 0); // unoptimizedScanTotalTime
    314         assertValueRange("blem", "", 12, 0, 0); // unoptimizedScanTotalTimeBg
    315         assertValueRange("blem", "", 13, 0, 0); // unoptimizedScanMaxTime
    316         assertValueRange("blem", "", 14, 0, 0); // unoptimizedScanMaxTimeBg
    317 
    318         // Optimized - Foreground.
    319         executeForeground(ACTION_BLE_SCAN_OPTIMIZED, 40_000);
    320         assertValueRange("blem", "", 7, 2*minTime, 2*maxTime); // actualTime
    321         assertValueRange("blem", "", 8, 1*minTime, 1*maxTime); // actualTimeBg
    322         assertValueRange("blem", "", 11, 0, 0); // unoptimizedScanTotalTime
    323         assertValueRange("blem", "", 12, 0, 0); // unoptimizedScanTotalTimeBg
    324         assertValueRange("blem", "", 13, 0, 0); // unoptimizedScanMaxTime
    325         assertValueRange("blem", "", 14, 0, 0); // unoptimizedScanMaxTimeBg
    326 
    327         // Unoptimized - Background.
    328         executeBackground(ACTION_BLE_SCAN_UNOPTIMIZED, 40_000);
    329         assertValueRange("blem", "", 7, 3*minTime, 3*maxTime); // actualTime
    330         assertValueRange("blem", "", 8, 2*minTime, 2*maxTime); // actualTimeBg
    331         assertValueRange("blem", "", 11, 1*minTime, 1*maxTime); // unoptimizedScanTotalTime
    332         assertValueRange("blem", "", 12, 1*minTime, 1*maxTime); // unoptimizedScanTotalTimeBg
    333         assertValueRange("blem", "", 13, 1*minTime, 1*maxTime); // unoptimizedScanMaxTime
    334         assertValueRange("blem", "", 14, 1*minTime, 1*maxTime); // unoptimizedScanMaxTimeBg
    335 
    336         // Unoptimized - Foreground.
    337         executeForeground(ACTION_BLE_SCAN_UNOPTIMIZED, 40_000);
    338         assertValueRange("blem", "", 7, 4*minTime, 4*maxTime); // actualTime
    339         assertValueRange("blem", "", 8, 2*minTime, 2*maxTime); // actualTimeBg
    340         assertValueRange("blem", "", 11, 2*minTime, 2*maxTime); // unoptimizedScanTotalTime
    341         assertValueRange("blem", "", 12, 1*minTime, 1*maxTime); // unoptimizedScanTotalTimeBg
    342         assertValueRange("blem", "", 13, 1*minTime, 1*maxTime); // unoptimizedScanMaxTime
    343         assertValueRange("blem", "", 14, 1*minTime, 1*maxTime); // unoptimizedScanMaxTimeBg
    344 
    345         batteryOffScreenOn();
    346     }
    347 
    348     public void testGpsUpdates() throws Exception {
    349         if (noBattery() || !hasFeature(FEATURE_LOCATION_GPS, true)) {
    350             return;
    351         }
    352 
    353         final String gpsSensorNumber = "-10000";
    354 
    355         batteryOnScreenOff();
    356         installPackage(DEVICE_SIDE_TEST_APK, true);
    357         // Whitelist this app against background location request throttling
    358         String origWhitelist = getDevice().executeShellCommand(
    359                 "settings get global location_background_throttle_package_whitelist").trim();
    360         getDevice().executeShellCommand(String.format(
    361                 "settings put global location_background_throttle_package_whitelist %s",
    362                 DEVICE_SIDE_TEST_PACKAGE));
    363 
    364         try {
    365             // Background test.
    366             executeBackground(ACTION_GPS, 60_000);
    367             assertValueRange("sr", gpsSensorNumber, 6, 1, 1); // count
    368             assertValueRange("sr", gpsSensorNumber, 7, 1, 1); // background_count
    369 
    370             // Foreground test.
    371             executeForeground(ACTION_GPS, 60_000);
    372             assertValueRange("sr", gpsSensorNumber, 6, 2, 2); // count
    373             assertValueRange("sr", gpsSensorNumber, 7, 1, 1); // background_count
    374         } finally {
    375             if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
    376                 getDevice().executeShellCommand(
    377                         "settings delete global location_background_throttle_package_whitelist");
    378             } else {
    379                 getDevice().executeShellCommand(String.format(
    380                         "settings put global location_background_throttle_package_whitelist %s",
    381                         origWhitelist));
    382             }
    383             batteryOffScreenOn();
    384         }
    385     }
    386 
    387     public void testJobBgVsFg() throws Exception {
    388         if (noBattery()) {
    389             return;
    390         }
    391         batteryOnScreenOff();
    392         installPackage(DEVICE_SIDE_TEST_APK, true);
    393         turnScreenOnForReal();
    394         assertScreenOn();
    395         allowImmediateSyncs();
    396 
    397         // Background test.
    398         executeBackground(ACTION_JOB_SCHEDULE, 60_000);
    399         assertValueRange("jb", "", 6, 1, 1); // count
    400         assertValueRange("jb", "", 8, 1, 1); // background_count
    401 
    402         // Foreground test.
    403         executeForeground(ACTION_JOB_SCHEDULE, 60_000);
    404         assertValueRange("jb", "", 6, 2, 2); // count
    405         assertValueRange("jb", "", 8, 1, 1); // background_count
    406 
    407         batteryOffScreenOn();
    408     }
    409 
    410     public void testSyncBgVsFg() throws Exception {
    411         if (noBattery()) {
    412             return;
    413         }
    414         batteryOnScreenOff();
    415         installPackage(DEVICE_SIDE_TEST_APK, true);
    416         turnScreenOnForReal();
    417         assertScreenOn();
    418         allowImmediateSyncs();
    419 
    420         // Background test.
    421         executeBackground(ACTION_SYNC, 60_000);
    422         // Allow one or two syncs in this time frame (not just one) due to unpredictable syncs.
    423         assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 1, 2); // count
    424         assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 8, 1, 2); // background_count
    425 
    426         // Foreground test.
    427         executeForeground(ACTION_SYNC, 60_000);
    428         assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 2, 4); // count
    429         assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 8, 1, 2); // background_count
    430 
    431         batteryOffScreenOn();
    432     }
    433 
    434     /**
    435      * Tests whether the on-battery realtime and total realtime values
    436      * are properly updated in battery stats.
    437      */
    438     public void testRealtime() throws Exception {
    439         batteryOnScreenOff();
    440         long startingValueRealtime = getLongValue(0, "bt", "", 7);
    441         long startingValueBatteryRealtime = getLongValue(0, "bt", "", 5);
    442         // After going on battery
    443         Thread.sleep(2000);
    444         batteryOffScreenOn();
    445         // After going off battery
    446         Thread.sleep(2000);
    447 
    448         long currentValueRealtime = getLongValue(0, "bt", "", 7);
    449         long currentValueBatteryRealtime = getLongValue(0, "bt", "", 5);
    450 
    451         // Total realtime increase should be 4000ms at least
    452         assertTrue(currentValueRealtime >= startingValueRealtime + 4000);
    453         // But not too much more
    454         assertTrue(currentValueRealtime < startingValueRealtime + 6000);
    455         // Battery on realtime should be more than 2000 but less than 4000
    456         assertTrue(currentValueBatteryRealtime >= startingValueBatteryRealtime + 2000);
    457         assertTrue(currentValueBatteryRealtime < startingValueBatteryRealtime + 4000);
    458     }
    459 
    460     /**
    461      * Tests the total duration reported for jobs run on the job scheduler.
    462      */
    463     public void testJobDuration() throws Exception {
    464         batteryOnScreenOff();
    465 
    466         installPackage(DEVICE_SIDE_TEST_APK, true);
    467         allowImmediateSyncs();
    468 
    469         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsJobDurationTests",
    470                 "testJobDuration");
    471 
    472         // Should be approximately 15000 ms (3 x 5000 ms). Use 0.8x and 2x as the lower and upper
    473         // bounds to account for possible errors due to thread scheduling and cpu load.
    474         assertValueRange("jb", DEVICE_SIDE_JOB_COMPONENT, 5, (long) (15000 * 0.8), 15000 * 2);
    475         batteryOffScreenOn();
    476     }
    477 
    478     /**
    479      * Tests the total duration and # of syncs reported for sync activities.
    480      */
    481     public void testSyncs() throws Exception {
    482         batteryOnScreenOff();
    483 
    484         installPackage(DEVICE_SIDE_TEST_APK, true);
    485         allowImmediateSyncs();
    486 
    487         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsSyncTest", "testRunSyncs");
    488 
    489         // First, check the count, which should be 10.
    490         // (It could be 11, if the initial sync actually happened before getting cancelled.)
    491         assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 10L, 11L);
    492 
    493         // Should be approximately, but at least 10 seconds. Use 2x as the upper
    494         // bounds to account for possible errors due to thread scheduling and cpu load.
    495         assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 5, 10000, 10000 * 2);
    496     }
    497 
    498     private int getUid() throws Exception {
    499         String uidLine = getDevice().executeShellCommand("cmd package list packages -U "
    500                 + DEVICE_SIDE_TEST_PACKAGE);
    501         String[] uidLineParts = uidLine.split(":");
    502         // 3rd entry is package uid
    503         assertTrue(uidLineParts.length > 2);
    504         int uid = Integer.parseInt(uidLineParts[2].trim());
    505         assertTrue(uid > 10000);
    506         return uid;
    507     }
    508 
    509     private void assertApproximateTimeInState(int index, long duration) throws Exception {
    510         assertValueRange("st", "", index, (long) (0.7 * duration), 2 * duration);
    511     }
    512 
    513     /**
    514      * Verifies that the recorded time for the specified tag and name in the test package
    515      * is within the specified range.
    516      */
    517     private void assertValueRange(String tag, String optionalAfterTag,
    518             int index, long min, long max) throws Exception {
    519         int uid = getUid();
    520         long value = getLongValue(uid, tag, optionalAfterTag, index);
    521         assertTrue("Value " + value + " is less than min " + min, value >= min);
    522         assertTrue("Value " + value + " is greater than max " + max, value <= max);
    523     }
    524 
    525     /**
    526      * Returns a particular long value from a line matched by uid, tag and the optionalAfterTag.
    527      */
    528     private long getLongValue(int uid, String tag, String optionalAfterTag, int index)
    529             throws Exception {
    530         String dumpsys = getDevice().executeShellCommand("dumpsys batterystats --checkin");
    531         String[] lines = dumpsys.split("\n");
    532         long value = 0;
    533         if (optionalAfterTag == null) {
    534             optionalAfterTag = "";
    535         }
    536         for (int i = lines.length - 1; i >= 0; i--) {
    537             String line = lines[i];
    538             if (line.contains(uid + ",l," + tag + "," + optionalAfterTag)
    539                     || (!optionalAfterTag.equals("") &&
    540                         line.contains(uid + ",l," + tag + ",\"" + optionalAfterTag))) {
    541                 String[] wlParts = line.split(",");
    542                 value = Long.parseLong(wlParts[index]);
    543             }
    544         }
    545         return value;
    546     }
    547 
    548     /**
    549      * Runs a (background) service to perform the given action, and waits for
    550      * the device to report that the action has finished (via a logcat message) before returning.
    551      * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
    552      *                    action to perform.
    553      * @param maxTimeMs max time to wait (in ms) for action to report that it has completed.
    554      * @return A string, representing a random integer, assigned to this particular request for the
    555      *                     device to perform the given action. This value can be used to receive
    556      *                     communications via logcat from the device about this action.
    557      */
    558     private String executeBackground(String actionValue, int maxTimeMs) throws Exception {
    559         String requestCode = executeBackground(actionValue);
    560         String searchString = getCompletedActionString(actionValue, requestCode);
    561         checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
    562         return requestCode;
    563     }
    564 
    565     /**
    566      * Runs a (background) service to perform the given action.
    567      * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
    568      *                    action to perform.
    569      * @return A string, representing a random integer, assigned to this particular request for the
    570       *                     device to perform the given action. This value can be used to receive
    571       *                     communications via logcat from the device about this action.
    572      */
    573     private String executeBackground(String actionValue) throws Exception {
    574         allowBackgroundServices();
    575         String requestCode = Integer.toString(new Random().nextInt());
    576         getDevice().executeShellCommand(String.format(
    577                 "am startservice -n '%s' -e %s %s -e %s %s",
    578                 DEVICE_SIDE_BG_SERVICE_COMPONENT,
    579                 KEY_ACTION, actionValue,
    580                 KEY_REQUEST_CODE, requestCode));
    581         return requestCode;
    582     }
    583 
    584     /** Required to successfully start a background service from adb in O. */
    585     private void allowBackgroundServices() throws Exception {
    586         getDevice().executeShellCommand(String.format(
    587                 "cmd deviceidle tempwhitelist %s", DEVICE_SIDE_TEST_PACKAGE));
    588     }
    589 
    590     /** Make the test-app standby-active so it can run syncs and jobs immediately. */
    591     protected void allowImmediateSyncs() throws Exception {
    592         getDevice().executeShellCommand("am set-standby-bucket "
    593                 + DEVICE_SIDE_TEST_PACKAGE + " active");
    594     }
    595 
    596     /**
    597      * Runs an activity (in the foreground) to perform the given action, and waits
    598      * for the device to report that the action has finished (via a logcat message) before returning.
    599      * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
    600      *                    action to perform.
    601      * @param maxTimeMs max time to wait (in ms) for action to report that it has completed.
    602      * @return A string, representing a random integer, assigned to this particular request for the
    603      *                     device to perform the given action. This value can be used to receive
    604      *                     communications via logcat from the device about this action.
    605      */
    606     private String executeForeground(String actionValue, int maxTimeMs) throws Exception {
    607         String requestCode = executeForeground(actionValue);
    608         String searchString = getCompletedActionString(actionValue, requestCode);
    609         checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
    610         return requestCode;
    611     }
    612 
    613     /**
    614      * Runs an activity (in the foreground) to perform the given action.
    615      * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
    616      *                    action to perform.
    617      * @return A string, representing a random integer, assigned to this particular request for the
    618      *                     device to perform the given action. This value can be used to receive
    619      *                     communications via logcat from the device about this action.
    620      */
    621     private String executeForeground(String actionValue) throws Exception {
    622         String requestCode = Integer.toString(new Random().nextInt());
    623         getDevice().executeShellCommand(String.format(
    624                 "am start -n '%s' -e %s %s -e %s %s",
    625                 DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
    626                 KEY_ACTION, actionValue,
    627                 KEY_REQUEST_CODE, requestCode));
    628         return requestCode;
    629     }
    630 
    631     /**
    632      * The string that will be printed in the logcat when the action completes. This needs to be
    633      * identical to {@link com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions#tellHostActionFinished}.
    634      */
    635     private String getCompletedActionString(String actionValue, String requestCode) {
    636         return String.format("Completed performing %s for request %s", actionValue, requestCode);
    637     }
    638 
    639     /** Determine if device has no battery and is not expected to have proper batterystats. */
    640     private boolean noBattery() throws Exception {
    641         final String batteryinfo = getDevice().executeShellCommand("dumpsys battery");
    642         boolean hasBattery = batteryinfo.contains("present: true");
    643         if (!hasBattery) {
    644             LogUtil.CLog.w("Device does not have a battery");
    645         }
    646         return !hasBattery;
    647     }
    648 
    649     /**
    650      * Determines if the device has the given feature.
    651      * Prints a warning if its value differs from requiredAnswer.
    652      */
    653     private boolean hasFeature(String featureName, boolean requiredAnswer) throws Exception {
    654         final String features = getDevice().executeShellCommand("pm list features");
    655         boolean hasIt = features.contains(featureName);
    656         if (hasIt != requiredAnswer) {
    657             LogUtil.CLog.w("Device does " + (requiredAnswer ? "not " : "") + "have feature "
    658                     + featureName);
    659         }
    660         return hasIt;
    661     }
    662 }
    663