Home | History | Annotate | Download | only in os
      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.internal.os;
     17 
     18 import static android.os.BatteryStats.UID_TIMES_TYPE_ALL;
     19 import static android.os.BatteryStats.Uid.NUM_PROCESS_STATE;
     20 import static android.os.BatteryStats.Uid.PROCESS_STATE_BACKGROUND;
     21 import static android.os.BatteryStats.Uid.PROCESS_STATE_CACHED;
     22 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND;
     23 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE;
     24 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
     25 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING;
     26 import static android.os.BatteryStats.Uid.UID_PROCESS_TYPES;
     27 
     28 import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS;
     29 import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_TRACK_CPU_TIMES_BY_PROC_STATE;
     30 
     31 import static junit.framework.Assert.assertNotNull;
     32 import static junit.framework.Assert.assertNull;
     33 import static junit.framework.Assert.assertTrue;
     34 import static junit.framework.Assert.fail;
     35 
     36 import com.android.frameworks.coretests.aidl.ICmdCallback;
     37 import com.android.frameworks.coretests.aidl.ICmdReceiver;
     38 
     39 import android.app.ActivityManager;
     40 import android.app.KeyguardManager;
     41 import android.content.ComponentName;
     42 import android.content.Context;
     43 import android.content.Intent;
     44 import android.content.IntentFilter;
     45 import android.content.ServiceConnection;
     46 import android.content.pm.PackageManager;
     47 import android.os.BatteryManager;
     48 import android.os.BatteryStats;
     49 import android.os.Bundle;
     50 import android.os.IBinder;
     51 import android.os.PowerManager;
     52 import android.os.Process;
     53 import android.os.SystemClock;
     54 import android.provider.Settings;
     55 import android.support.test.InstrumentationRegistry;
     56 import android.support.test.filters.LargeTest;
     57 import android.support.test.runner.AndroidJUnit4;
     58 import android.support.test.uiautomator.UiDevice;
     59 import android.text.TextUtils;
     60 import android.util.DebugUtils;
     61 import android.util.Log;
     62 
     63 import org.junit.AfterClass;
     64 import org.junit.BeforeClass;
     65 import org.junit.Rule;
     66 import org.junit.Test;
     67 import org.junit.rules.TestName;
     68 import org.junit.runner.RunWith;
     69 
     70 import java.util.Arrays;
     71 import java.util.concurrent.CountDownLatch;
     72 import java.util.concurrent.TimeUnit;
     73 import java.util.regex.Matcher;
     74 import java.util.regex.Pattern;
     75 
     76 @LargeTest
     77 @RunWith(AndroidJUnit4.class)
     78 public class BstatsCpuTimesValidationTest {
     79     private static final String TAG = BstatsCpuTimesValidationTest.class.getSimpleName();
     80 
     81     private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp";
     82     private static final String TEST_ACTIVITY = TEST_PKG + ".TestActivity";
     83     private static final String TEST_SERVICE = TEST_PKG + ".TestService";
     84     private static final String ISOLATED_TEST_SERVICE = TEST_PKG + ".IsolatedTestService";
     85 
     86     private static final String EXTRA_KEY_CMD_RECEIVER = "cmd_receiver";
     87     private static final int FLAG_START_FOREGROUND = 1;
     88 
     89     private static final int BATTERY_STATE_TIMEOUT_MS = 2000;
     90     private static final int BATTERY_STATE_CHECK_INTERVAL_MS = 200;
     91 
     92     private static final int START_ACTIVITY_TIMEOUT_MS = 2000;
     93     private static final int START_FG_SERVICE_TIMEOUT_MS = 2000;
     94     private static final int START_SERVICE_TIMEOUT_MS = 2000;
     95     private static final int START_ISOLATED_SERVICE_TIMEOUT_MS = 2000;
     96 
     97     private static final int SETTING_UPDATE_TIMEOUT_MS = 2000;
     98     private static final int SETTING_UPDATE_CHECK_INTERVAL_MS = 200;
     99 
    100     private static final int GENERAL_TIMEOUT_MS = 4000;
    101     private static final int GENERAL_INTERVAL_MS = 200;
    102 
    103     private static final int WORK_DURATION_MS = 2000;
    104 
    105     private static final String DESIRED_PROC_STATE_CPU_TIMES_DELAY = "0";
    106 
    107     private static boolean sBatteryStatsConstsUpdated;
    108     private static String sOriginalBatteryStatsConsts;
    109 
    110     private static Context sContext;
    111     private static UiDevice sUiDevice;
    112     private static int sTestPkgUid;
    113     private static boolean sCpuFreqTimesAvailable;
    114     private static boolean sPerProcStateTimesAvailable;
    115 
    116     @Rule public TestName testName = new TestName();
    117 
    118     @BeforeClass
    119     public static void setupOnce() throws Exception {
    120         sContext = InstrumentationRegistry.getContext();
    121         sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    122         sContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG,
    123                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
    124         sTestPkgUid = sContext.getPackageManager().getPackageUid(TEST_PKG, 0);
    125         checkCpuTimesAvailability();
    126         if (sPerProcStateTimesAvailable && sCpuFreqTimesAvailable) {
    127             setDesiredReadyDelay();
    128         }
    129     }
    130 
    131     @AfterClass
    132     public static void tearDownOnce() throws Exception {
    133         if (sBatteryStatsConstsUpdated) {
    134             Settings.Global.putString(sContext.getContentResolver(),
    135                     Settings.Global.BATTERY_STATS_CONSTANTS, sOriginalBatteryStatsConsts);
    136         }
    137         batteryReset();
    138     }
    139 
    140     private static void setDesiredReadyDelay() {
    141         sOriginalBatteryStatsConsts = Settings.Global.getString(sContext.getContentResolver(),
    142                 Settings.Global.BATTERY_STATS_CONSTANTS);
    143         String newBatteryStatsConstants;
    144         final String newConstant = KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS
    145                 + "=" + DESIRED_PROC_STATE_CPU_TIMES_DELAY;
    146         if (sOriginalBatteryStatsConsts == null || "null".equals(sOriginalBatteryStatsConsts)) {
    147             // battery_stats_constants is initially empty, so just assign the desired value.
    148             newBatteryStatsConstants = newConstant;
    149         } else if (sOriginalBatteryStatsConsts.contains(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS)) {
    150             // battery_stats_constants contains delay duration, so replace it
    151             // with the desired value.
    152             newBatteryStatsConstants = sOriginalBatteryStatsConsts.replaceAll(
    153                     KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS + "=\\d+", newConstant);
    154         } else {
    155             // battery_stats_constants didn't contain any delay, so append the desired value.
    156             newBatteryStatsConstants = sOriginalBatteryStatsConsts + "," + newConstant;
    157         }
    158         Settings.Global.putString(sContext.getContentResolver(),
    159                 Settings.Global.BATTERY_STATS_CONSTANTS, newBatteryStatsConstants);
    160         sBatteryStatsConstsUpdated = true;
    161     }
    162 
    163     // Checks cpu freq times of system uid as an indication of whether /proc/uid_time_in_state
    164     // and /proc/uid/<uid>/time_in_state kernel nodes are available.
    165     private static void checkCpuTimesAvailability() throws Exception {
    166         batteryOn();
    167         SystemClock.sleep(GENERAL_TIMEOUT_MS);
    168         batteryOff();
    169         final long[] totalCpuTimes = getAllCpuFreqTimes(Process.SYSTEM_UID);
    170         sCpuFreqTimesAvailable = totalCpuTimes != null;
    171         final long[] fgCpuTimes = getAllCpuFreqTimes(Process.SYSTEM_UID,
    172                 PROCESS_STATE_FOREGROUND);
    173         sPerProcStateTimesAvailable = fgCpuTimes != null;
    174     }
    175 
    176     @Test
    177     public void testCpuFreqTimes() throws Exception {
    178         if (!sCpuFreqTimesAvailable) {
    179             Log.w(TAG, "Skipping " + testName.getMethodName()
    180                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    181                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    182             return;
    183         }
    184 
    185         batteryOnScreenOn();
    186         forceStop();
    187         resetBatteryStats();
    188         final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    189         assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot),
    190                 initialSnapshot);
    191         doSomeWork();
    192         forceStop();
    193 
    194         final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid);
    195         assertCpuTimesValid(cpuTimesMs);
    196         long actualCpuTimeMs = 0;
    197         for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    198             actualCpuTimeMs += cpuTimesMs[i];
    199         }
    200         assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualCpuTimeMs);
    201         batteryOffScreenOn();
    202     }
    203 
    204     @Test
    205     public void testCpuFreqTimes_screenOff() throws Exception {
    206         if (!sCpuFreqTimesAvailable) {
    207             Log.w(TAG, "Skipping " + testName.getMethodName()
    208                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    209                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    210             return;
    211         }
    212 
    213         batteryOnScreenOff();
    214         forceStop();
    215         resetBatteryStats();
    216         final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    217         assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot),
    218                 initialSnapshot);
    219         doSomeWork();
    220         forceStop();
    221 
    222         final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid);
    223         assertCpuTimesValid(cpuTimesMs);
    224         long actualTotalCpuTimeMs = 0;
    225         for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    226             actualTotalCpuTimeMs += cpuTimesMs[i];
    227         }
    228         assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualTotalCpuTimeMs);
    229         long actualScreenOffCpuTimeMs = 0;
    230         for (int i = cpuTimesMs.length / 2; i < cpuTimesMs.length; ++i) {
    231             actualScreenOffCpuTimeMs += cpuTimesMs[i];
    232         }
    233         assertApproximateValue("Incorrect screen-off cpu time",
    234                 WORK_DURATION_MS, actualScreenOffCpuTimeMs);
    235         batteryOffScreenOn();
    236     }
    237 
    238     @Test
    239     public void testCpuFreqTimes_isolatedProcess() throws Exception {
    240         if (!sCpuFreqTimesAvailable) {
    241             Log.w(TAG, "Skipping " + testName.getMethodName()
    242                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    243                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    244             return;
    245         }
    246 
    247         batteryOnScreenOn();
    248         forceStop();
    249         resetBatteryStats();
    250         final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    251         assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot),
    252                 initialSnapshot);
    253         doSomeWorkInIsolatedProcess();
    254         forceStop();
    255 
    256         final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid);
    257         assertCpuTimesValid(cpuTimesMs);
    258         long actualCpuTimeMs = 0;
    259         for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    260             actualCpuTimeMs += cpuTimesMs[i];
    261         }
    262         assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualCpuTimeMs);
    263         batteryOffScreenOn();
    264     }
    265 
    266     @Test
    267     public void testCpuFreqTimes_stateTop() throws Exception {
    268         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
    269             Log.w(TAG, "Skipping " + testName.getMethodName()
    270                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    271                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    272             return;
    273         }
    274 
    275         batteryOnScreenOn();
    276         forceStop();
    277         resetBatteryStats();
    278         final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    279         assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot),
    280                 initialSnapshot);
    281         assertNull("Initial top state snapshot should be null",
    282                 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP));
    283 
    284         doSomeWork(PROCESS_STATE_TOP);
    285         forceStop();
    286 
    287         final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
    288         final String msgCpuTimes = getAllCpuTimesMsg();
    289         assertCpuTimesValid(cpuTimesMs);
    290         long actualCpuTimeMs = 0;
    291         for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    292             actualCpuTimeMs += cpuTimesMs[i];
    293         }
    294         assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
    295                 WORK_DURATION_MS, actualCpuTimeMs);
    296         batteryOffScreenOn();
    297     }
    298 
    299     @Test
    300     public void testIsolatedCpuFreqTimes_stateService() throws Exception {
    301         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
    302             Log.w(TAG, "Skipping " + testName.getMethodName()
    303                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    304                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    305             return;
    306         }
    307 
    308         batteryOnScreenOn();
    309         forceStop();
    310         resetBatteryStats();
    311         final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    312         assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot),
    313                 initialSnapshot);
    314         assertNull("Initial top state snapshot should be null",
    315                 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP));
    316 
    317         final ICmdReceiver activityReceiver = ICmdReceiver.Stub.asInterface(startActivity());
    318         final ICmdReceiver isolatedReceiver = ICmdReceiver.Stub.asInterface(startIsolatedService());
    319         try {
    320             assertProcState(PROCESS_STATE_TOP);
    321             isolatedReceiver.doSomeWork(WORK_DURATION_MS);
    322         } finally {
    323             activityReceiver.finishHost();
    324             isolatedReceiver.finishHost();
    325         }
    326         forceStop();
    327 
    328         final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
    329         final String msgCpuTimes = getAllCpuTimesMsg();
    330         assertCpuTimesValid(cpuTimesMs);
    331         long actualCpuTimeMs = 0;
    332         for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    333             actualCpuTimeMs += cpuTimesMs[i];
    334         }
    335         assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
    336                 WORK_DURATION_MS, actualCpuTimeMs);
    337         batteryOffScreenOn();
    338     }
    339 
    340     @Test
    341     public void testCpuFreqTimes_stateTopSleeping() throws Exception {
    342         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
    343             Log.w(TAG, "Skipping " + testName.getMethodName()
    344                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    345                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    346             return;
    347         }
    348 
    349         batteryOnScreenOff();
    350         forceStop();
    351         resetBatteryStats();
    352         final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    353         assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot),
    354                 initialSnapshot);
    355         assertNull("Initial top state snapshot should be null",
    356                 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP_SLEEPING));
    357 
    358         doSomeWork(PROCESS_STATE_TOP_SLEEPING);
    359         forceStop();
    360 
    361         final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP_SLEEPING);
    362         final String msgCpuTimes = getAllCpuTimesMsg();
    363         assertCpuTimesValid(cpuTimesMs);
    364         long actualCpuTimeMs = 0;
    365         for (int i = cpuTimesMs.length / 2; i < cpuTimesMs.length; ++i) {
    366             actualCpuTimeMs += cpuTimesMs[i];
    367         }
    368         assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
    369                 WORK_DURATION_MS, actualCpuTimeMs);
    370         batteryOffScreenOn();
    371     }
    372 
    373     @Test
    374     public void testCpuFreqTimes_stateFgService() throws Exception {
    375         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
    376             Log.w(TAG, "Skipping " + testName.getMethodName()
    377                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    378                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    379             return;
    380         }
    381 
    382         batteryOnScreenOff();
    383         forceStop();
    384         resetBatteryStats();
    385         final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    386         assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot),
    387                 initialSnapshot);
    388         assertNull("Initial top state snapshot should be null",
    389                 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_FOREGROUND_SERVICE));
    390 
    391         doSomeWork(PROCESS_STATE_FOREGROUND_SERVICE);
    392         forceStop();
    393 
    394         final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_FOREGROUND_SERVICE);
    395         final String msgCpuTimes = getAllCpuTimesMsg();
    396         assertCpuTimesValid(cpuTimesMs);
    397         long actualCpuTimeMs = 0;
    398         for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    399             actualCpuTimeMs += cpuTimesMs[i];
    400         }
    401         assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
    402                 WORK_DURATION_MS, actualCpuTimeMs);
    403         batteryOffScreenOn();
    404     }
    405 
    406     @Test
    407     public void testCpuFreqTimes_stateFg() throws Exception {
    408         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
    409             Log.w(TAG, "Skipping " + testName.getMethodName()
    410                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    411                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    412             return;
    413         }
    414 
    415         batteryOnScreenOn();
    416         forceStop();
    417         resetBatteryStats();
    418         final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    419         assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot),
    420                 initialSnapshot);
    421         assertNull("Initial top state snapshot should be null",
    422                 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_FOREGROUND));
    423 
    424         doSomeWork(PROCESS_STATE_FOREGROUND);
    425         forceStop();
    426 
    427         final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_FOREGROUND);
    428         final String msgCpuTimes = getAllCpuTimesMsg();
    429         assertCpuTimesValid(cpuTimesMs);
    430         long actualCpuTimeMs = 0;
    431         for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    432             actualCpuTimeMs += cpuTimesMs[i];
    433         }
    434         assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
    435                 WORK_DURATION_MS, actualCpuTimeMs);
    436         batteryOff();
    437     }
    438 
    439     @Test
    440     public void testCpuFreqTimes_stateBg() throws Exception {
    441         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
    442             Log.w(TAG, "Skipping " + testName.getMethodName()
    443                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    444                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    445             return;
    446         }
    447 
    448         batteryOnScreenOff();
    449         forceStop();
    450         resetBatteryStats();
    451         final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    452         assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot),
    453                 initialSnapshot);
    454         assertNull("Initial top state snapshot should be null",
    455                 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_BACKGROUND));
    456 
    457         doSomeWork(PROCESS_STATE_BACKGROUND);
    458         forceStop();
    459 
    460         final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_BACKGROUND);
    461         final String msgCpuTimes = getAllCpuTimesMsg();
    462         assertCpuTimesValid(cpuTimesMs);
    463         long actualCpuTimeMs = 0;
    464         for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    465             actualCpuTimeMs += cpuTimesMs[i];
    466         }
    467         assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
    468                 WORK_DURATION_MS, actualCpuTimeMs);
    469         batteryOffScreenOn();
    470     }
    471 
    472     @Test
    473     public void testCpuFreqTimes_stateCached() throws Exception {
    474         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
    475             Log.w(TAG, "Skipping " + testName.getMethodName()
    476                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    477                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    478             return;
    479         }
    480 
    481         batteryOnScreenOn();
    482         forceStop();
    483         resetBatteryStats();
    484         final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    485         assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot),
    486                 initialSnapshot);
    487         assertNull("Initial top state snapshot should be null",
    488                 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_CACHED));
    489 
    490         doSomeWork(PROCESS_STATE_CACHED);
    491         forceStop();
    492 
    493         final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_CACHED);
    494         final String msgCpuTimes = getAllCpuTimesMsg();
    495         assertCpuTimesValid(cpuTimesMs);
    496         long actualCpuTimeMs = 0;
    497         for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    498             actualCpuTimeMs += cpuTimesMs[i];
    499         }
    500         assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
    501                 WORK_DURATION_MS, actualCpuTimeMs);
    502         batteryOffScreenOn();
    503     }
    504 
    505     @Test
    506     public void testCpuFreqTimes_trackingDisabled() throws Exception {
    507         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
    508             Log.w(TAG, "Skipping " + testName.getMethodName()
    509                     + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
    510                     + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
    511             return;
    512         }
    513 
    514         final String bstatsConstants = Settings.Global.getString(sContext.getContentResolver(),
    515                 Settings.Global.BATTERY_STATS_CONSTANTS);
    516         try {
    517             batteryOnScreenOn();
    518             forceStop();
    519             resetBatteryStats();
    520             final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
    521             assertNull("Initial snapshot should be null, initial="
    522                     + Arrays.toString(initialSnapshot), initialSnapshot);
    523             assertNull("Initial top state snapshot should be null",
    524                     getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP));
    525 
    526             doSomeWork(PROCESS_STATE_TOP);
    527             forceStop();
    528 
    529             final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
    530             final String msgCpuTimes = getAllCpuTimesMsg();
    531             assertCpuTimesValid(cpuTimesMs);
    532             long actualCpuTimeMs = 0;
    533             for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    534                 actualCpuTimeMs += cpuTimesMs[i];
    535             }
    536             assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
    537                     WORK_DURATION_MS, actualCpuTimeMs);
    538 
    539             updateTrackPerProcStateCpuTimesSetting(bstatsConstants, false);
    540 
    541             doSomeWork(PROCESS_STATE_TOP);
    542             forceStop();
    543 
    544             final long[] cpuTimesMs2 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
    545             assertCpuTimesValid(cpuTimesMs2);
    546             assertCpuTimesEqual(cpuTimesMs2, cpuTimesMs, 20,
    547                     "Unexpected cpu times with tracking off");
    548 
    549             updateTrackPerProcStateCpuTimesSetting(bstatsConstants, true);
    550 
    551             final long[] cpuTimesMs3 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
    552             assertCpuTimesValid(cpuTimesMs3);
    553             assertCpuTimesEqual(cpuTimesMs3, cpuTimesMs, 20,
    554                     "Unexpected cpu times after turning on tracking");
    555 
    556             doSomeWork(PROCESS_STATE_TOP);
    557             forceStop();
    558 
    559             final long[] cpuTimesMs4 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
    560             assertCpuTimesValid(cpuTimesMs4);
    561             actualCpuTimeMs = 0;
    562             for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
    563                 actualCpuTimeMs += cpuTimesMs[i];
    564             }
    565             assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
    566                     2 * WORK_DURATION_MS, actualCpuTimeMs);
    567 
    568             batteryOffScreenOn();
    569         } finally {
    570             Settings.Global.putString(sContext.getContentResolver(),
    571                     Settings.Global.BATTERY_STATS_CONSTANTS, bstatsConstants);
    572         }
    573     }
    574 
    575     private void assertCpuTimesEqual(long[] actual, long[] expected, long delta, String errMsg) {
    576         for (int i = actual.length - 1; i >= 0; --i) {
    577             if (actual[i] > expected[i] + delta || actual[i] < expected[i]) {
    578                 fail(errMsg + ", actual=" + actual + ", expected=" + expected + ", delta=" + delta);
    579             }
    580         }
    581     }
    582 
    583     private void updateTrackPerProcStateCpuTimesSetting(String originalConstants, boolean enabled)
    584             throws Exception {
    585         final String newConstants;
    586         final String setting = KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=" + enabled;
    587         if (originalConstants == null || "null".equals(originalConstants)) {
    588             newConstants = setting;
    589         } else if (originalConstants.contains(KEY_TRACK_CPU_TIMES_BY_PROC_STATE)) {
    590             newConstants = originalConstants.replaceAll(
    591                     KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=(true|false)", setting);
    592         } else {
    593             newConstants = originalConstants + "," + setting;
    594         }
    595         Settings.Global.putString(sContext.getContentResolver(),
    596                 Settings.Global.BATTERY_STATS_CONSTANTS, newConstants);
    597         assertTrackPerProcStateCpuTimesSetting(enabled);
    598     }
    599 
    600     private void assertTrackPerProcStateCpuTimesSetting(boolean enabled) throws Exception {
    601         final String expectedValue = Boolean.toString(enabled);
    602         assertDelayedCondition("Unexpected value for " + KEY_TRACK_CPU_TIMES_BY_PROC_STATE, () -> {
    603             final String actualValue = getSettingValueFromDump(KEY_TRACK_CPU_TIMES_BY_PROC_STATE);
    604             return expectedValue.equals(actualValue)
    605                     ? null : "expected=" + expectedValue + ", actual=" + actualValue;
    606         }, SETTING_UPDATE_TIMEOUT_MS, SETTING_UPDATE_CHECK_INTERVAL_MS);
    607     }
    608 
    609     private String getSettingValueFromDump(String key) throws Exception {
    610         final String settingsDump = executeCmdSilent("dumpsys batterystats --settings");
    611         final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
    612         splitter.setString(settingsDump);
    613         String next;
    614         while (splitter.hasNext()) {
    615             next = splitter.next();
    616             if (next.startsWith(key)) {
    617                 return next.split("=")[1];
    618             }
    619         }
    620         return null;
    621     }
    622 
    623     private void assertCpuTimesValid(long[] cpuTimes) {
    624         assertNotNull(cpuTimes);
    625         for (int i = 0; i < cpuTimes.length; ++i) {
    626             if (cpuTimes[i] < 0) {
    627                 fail("Malformed cpu times data (-ve values): " + Arrays.toString(cpuTimes));
    628             }
    629         }
    630         final int numFreqs = cpuTimes.length / 2;
    631         for (int i = 0; i < numFreqs; ++i) {
    632             if (cpuTimes[i] < cpuTimes[numFreqs + i]) {
    633                 fail("Malformed cpu times data (screen-off > total)" + Arrays.toString(cpuTimes));
    634             }
    635         }
    636     }
    637 
    638     private void assertApproximateValue(String errorPrefix, long expectedValue, long actualValue) {
    639         assertValueRange(errorPrefix, actualValue, expectedValue * 0.5, expectedValue * 1.5);
    640     }
    641 
    642     private void assertValueRange(String errorPrefix,
    643             long actualvalue, double minValue, double maxValue) {
    644         final String errorMsg = String.format(errorPrefix + "; actual=%s; min=%s; max=%s",
    645                 actualvalue, minValue, maxValue);
    646         assertTrue(errorMsg, actualvalue < maxValue);
    647         assertTrue(errorMsg, actualvalue > minValue);
    648     }
    649 
    650     private void doSomeWork() throws Exception {
    651         final ICmdReceiver receiver = ICmdReceiver.Stub.asInterface(startActivity());
    652         receiver.doSomeWork(WORK_DURATION_MS);
    653         receiver.finishHost();
    654     }
    655 
    656     private void doSomeWorkInIsolatedProcess() throws Exception {
    657         final ICmdReceiver receiver = ICmdReceiver.Stub.asInterface(startIsolatedService());
    658         receiver.doSomeWork(WORK_DURATION_MS);
    659         receiver.finishHost();
    660     }
    661 
    662     private void doSomeWork(int procState) throws Exception {
    663         final ICmdReceiver receiver;
    664         switch (procState) {
    665             case PROCESS_STATE_TOP:
    666                 receiver = ICmdReceiver.Stub.asInterface(startActivity());
    667                 break;
    668             case PROCESS_STATE_TOP_SLEEPING:
    669                 receiver = ICmdReceiver.Stub.asInterface(startActivity());
    670                 break;
    671             case PROCESS_STATE_FOREGROUND_SERVICE:
    672                 receiver = ICmdReceiver.Stub.asInterface(startForegroundService());
    673                 break;
    674             case PROCESS_STATE_FOREGROUND:
    675                 receiver = ICmdReceiver.Stub.asInterface(startService());
    676                 receiver.showApplicationOverlay();
    677                 break;
    678             case PROCESS_STATE_BACKGROUND:
    679                 receiver = ICmdReceiver.Stub.asInterface(startService());
    680                 break;
    681             case PROCESS_STATE_CACHED:
    682                 receiver = ICmdReceiver.Stub.asInterface(startActivity());
    683                 receiver.finishHost();
    684                 break;
    685             default:
    686                 throw new IllegalArgumentException("Unknown state: " + procState);
    687         }
    688         try {
    689             assertProcState(procState);
    690             receiver.doSomeWork(WORK_DURATION_MS);
    691         } finally {
    692             receiver.finishHost();
    693         }
    694     }
    695 
    696     private void assertProcState(String state) throws Exception {
    697         final String expectedState = "(" + state + ")";
    698         assertDelayedCondition("", () -> {
    699             final String uidStateStr = executeCmd("cmd activity get-uid-state " + sTestPkgUid);
    700             final String actualState = uidStateStr.split(" ")[1];
    701             return expectedState.equals(actualState) ? null
    702                     : "expected=" + expectedState + ", actual" + actualState;
    703         });
    704     }
    705 
    706     private void assertProcState(int expectedState) throws Exception {
    707         assertDelayedCondition("Unexpected proc state", () -> {
    708             final String uidStateStr = executeCmd("cmd activity get-uid-state " + sTestPkgUid);
    709             final int amProcState = Integer.parseInt(uidStateStr.split(" ")[0]);
    710             final int actualState = BatteryStats.mapToInternalProcessState(amProcState);
    711             return (actualState == expectedState) ? null
    712                     : "expected=" + getStateName(BatteryStats.Uid.class, expectedState)
    713                             + ", actual=" + getStateName(BatteryStats.Uid.class, actualState)
    714                             + ", amState=" + getStateName(ActivityManager.class, amProcState);
    715         });
    716     }
    717 
    718     private String getStateName(Class clazz, int procState) {
    719         return DebugUtils.valueToString(clazz, "PROCESS_STATE_", procState);
    720     }
    721 
    722     private IBinder startIsolatedService() throws Exception {
    723         final CountDownLatch latch = new CountDownLatch(1);
    724         final IBinder[] binders = new IBinder[1];
    725         final ServiceConnection connection = new ServiceConnection() {
    726             @Override
    727             public void onServiceConnected(ComponentName name, IBinder service) {
    728                 binders[0] = service;
    729                 latch.countDown();
    730             }
    731 
    732             @Override
    733             public void onServiceDisconnected(ComponentName name) {
    734             }
    735         };
    736         final Intent launchIntent = new Intent()
    737                 .setComponent(new ComponentName(TEST_PKG, ISOLATED_TEST_SERVICE));
    738         sContext.bindService(launchIntent, connection, Context.BIND_AUTO_CREATE
    739                 | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_FOREGROUND);
    740         if (latch.await(START_ISOLATED_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
    741             if (binders[0] == null) {
    742                 fail("Receiver binder should not be null");
    743             }
    744             return binders[0];
    745         } else {
    746             fail("Timed out waiting for the isolated test service to start");
    747         }
    748         return null;
    749     }
    750 
    751     private IBinder startForegroundService() throws Exception {
    752         final CountDownLatch latch = new CountDownLatch(1);
    753         final Intent launchIntent = new Intent()
    754                 .setComponent(new ComponentName(TEST_PKG, TEST_SERVICE))
    755                 .setFlags(FLAG_START_FOREGROUND);
    756         final Bundle extras = new Bundle();
    757         final IBinder[] binders = new IBinder[1];
    758         extras.putBinder(EXTRA_KEY_CMD_RECEIVER, new ICmdCallback.Stub() {
    759             @Override
    760             public void onLaunched(IBinder receiver) {
    761                 binders[0] = receiver;
    762                 latch.countDown();
    763             }
    764         });
    765         launchIntent.putExtras(extras);
    766         sContext.startForegroundService(launchIntent);
    767         if (latch.await(START_FG_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
    768             if (binders[0] == null) {
    769                 fail("Receiver binder should not be null");
    770             }
    771             return binders[0];
    772         } else {
    773             fail("Timed out waiting for the test fg service to start; testUid=" + sTestPkgUid);
    774         }
    775         return null;
    776     }
    777 
    778     private IBinder startService() throws Exception {
    779         final CountDownLatch latch = new CountDownLatch(1);
    780         final Intent launchIntent = new Intent()
    781                 .setComponent(new ComponentName(TEST_PKG, TEST_SERVICE));
    782         final Bundle extras = new Bundle();
    783         final IBinder[] binders = new IBinder[1];
    784         extras.putBinder(EXTRA_KEY_CMD_RECEIVER, new ICmdCallback.Stub() {
    785             @Override
    786             public void onLaunched(IBinder receiver) {
    787                 binders[0] = receiver;
    788                 latch.countDown();
    789             }
    790         });
    791         launchIntent.putExtras(extras);
    792         sContext.startService(launchIntent);
    793         if (latch.await(START_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
    794             if (binders[0] == null) {
    795                 fail("Receiver binder should not be null");
    796             }
    797             return binders[0];
    798         } else {
    799             fail("Timed out waiting for the test service to start; testUid=" + sTestPkgUid);
    800         }
    801         return null;
    802     }
    803 
    804     private IBinder startActivity() throws Exception {
    805         final CountDownLatch latch = new CountDownLatch(1);
    806         final Intent launchIntent = new Intent()
    807                 .setComponent(new ComponentName(TEST_PKG, TEST_ACTIVITY));
    808         final Bundle extras = new Bundle();
    809         final IBinder[] binders = new IBinder[1];
    810         extras.putBinder(EXTRA_KEY_CMD_RECEIVER, new ICmdCallback.Stub() {
    811             @Override
    812             public void onLaunched(IBinder receiver) {
    813                 binders[0] = receiver;
    814                 latch.countDown();
    815             }
    816         });
    817         launchIntent.putExtras(extras)
    818                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    819         sContext.startActivity(launchIntent);
    820         if (latch.await(START_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
    821             if (binders[0] == null) {
    822                 fail("Receiver binder should not be null");
    823             }
    824             return binders[0];
    825         } else {
    826             fail("Timed out waiting for the test activity to start; testUid=" + sTestPkgUid);
    827         }
    828         return null;
    829     }
    830 
    831     private static String getAllCpuTimesMsg() throws Exception {
    832         final StringBuilder sb = new StringBuilder();
    833         sb.append("uid=" + sTestPkgUid + ";");
    834         sb.append(UID_TIMES_TYPE_ALL + "=" + getMsgCpuTimesSum(getAllCpuFreqTimes(sTestPkgUid)));
    835         for (int i = 0; i < NUM_PROCESS_STATE; ++i) {
    836             sb.append("|");
    837             sb.append(UID_PROCESS_TYPES[i] + "="
    838                     + getMsgCpuTimesSum(getAllCpuFreqTimes(sTestPkgUid, i)));
    839         }
    840         return sb.toString();
    841     }
    842 
    843     private static String getMsgCpuTimesSum(long[] cpuTimes) throws Exception {
    844         if (cpuTimes == null) {
    845             return "(0,0)";
    846         }
    847         long totalTime = 0;
    848         for (int i = 0; i < cpuTimes.length / 2; ++i) {
    849             totalTime += cpuTimes[i];
    850         }
    851         long screenOffTime = 0;
    852         for (int i = cpuTimes.length / 2; i < cpuTimes.length; ++i) {
    853             screenOffTime += cpuTimes[i];
    854         }
    855         return "(" + totalTime + "," + screenOffTime + ")";
    856     }
    857 
    858     private static long[] getAllCpuFreqTimes(int uid) throws Exception {
    859         final String checkinDump = executeCmdSilent("dumpsys batterystats --checkin");
    860         final Pattern pattern = Pattern.compile(uid + ",l,ctf," + UID_TIMES_TYPE_ALL + ",(.*?)\n");
    861         final Matcher matcher = pattern.matcher(checkinDump);
    862         if (!matcher.find()) {
    863             return null;
    864         }
    865         return parseCpuTimesStr(matcher.group(1));
    866     }
    867 
    868     private static long[] getAllCpuFreqTimes(int uid, int procState) throws Exception {
    869         final String checkinDump = executeCmdSilent("dumpsys batterystats --checkin");
    870         final Pattern pattern = Pattern.compile(
    871                 uid + ",l,ctf," + UID_PROCESS_TYPES[procState] + ",(.*?)\n");
    872         final Matcher matcher = pattern.matcher(checkinDump);
    873         if (!matcher.find()) {
    874             return null;
    875         }
    876         return parseCpuTimesStr(matcher.group(1));
    877     }
    878 
    879     private static long[] parseCpuTimesStr(String str) {
    880         final String[] cpuTimesStr = str.split(",");
    881         final int freqCount = Integer.parseInt(cpuTimesStr[0]);
    882         if (cpuTimesStr.length != (2 * freqCount + 1)) {
    883             fail("Malformed data: " + Arrays.toString(cpuTimesStr));
    884         }
    885         final long[] cpuTimes = new long[freqCount * 2];
    886         for (int i = 0; i < cpuTimes.length; ++i) {
    887             cpuTimes[i] = Long.parseLong(cpuTimesStr[i + 1]);
    888         }
    889         return cpuTimes;
    890     }
    891 
    892     private void resetBatteryStats() throws Exception {
    893         executeCmd("dumpsys batterystats --reset");
    894     }
    895 
    896     private void batteryOnScreenOn() throws Exception {
    897         batteryOn();
    898         screenOn();
    899     }
    900 
    901     private void batteryOnScreenOff() throws Exception {
    902         batteryOn();
    903         screenoff();
    904     }
    905 
    906     private void batteryOffScreenOn() throws Exception {
    907         batteryOff();
    908         screenOn();
    909     }
    910 
    911     private static void batteryOn() throws Exception {
    912         executeCmd("dumpsys battery unplug");
    913         assertBatteryState(false /* pluggedIn */);
    914     }
    915 
    916     private static void batteryOff() throws Exception {
    917         executeCmd("dumpsys battery set ac " + BatteryManager.BATTERY_PLUGGED_AC);
    918         assertBatteryState(true /* pluggedIn */);
    919     }
    920 
    921     private static void batteryReset() throws Exception {
    922         executeCmd("dumpsys battery reset");
    923     }
    924 
    925     private void screenOn() throws Exception {
    926         executeCmd("input keyevent KEYCODE_WAKEUP");
    927         executeCmd("wm dismiss-keyguard");
    928         assertKeyguardUnLocked();
    929         assertScreenInteractive(true);
    930     }
    931 
    932     private void screenoff() throws Exception {
    933         executeCmd("input keyevent KEYCODE_SLEEP");
    934         assertScreenInteractive(false);
    935     }
    936 
    937     private void forceStop() throws Exception {
    938         executeCmd("cmd activity force-stop " + TEST_PKG);
    939         assertProcState("NONEXISTENT");
    940     }
    941 
    942     private void assertKeyguardUnLocked() throws Exception {
    943         final KeyguardManager keyguardManager =
    944                 (KeyguardManager) sContext.getSystemService(Context.KEYGUARD_SERVICE);
    945         assertDelayedCondition("Unexpected Keyguard state", () ->
    946                 keyguardManager.isKeyguardLocked() ? "expected=unlocked" : null
    947         );
    948     }
    949 
    950     private void assertScreenInteractive(boolean interactive) throws Exception {
    951         final PowerManager powerManager =
    952                 (PowerManager) sContext.getSystemService(Context.POWER_SERVICE);
    953         assertDelayedCondition("Unexpected screen interactive state", () ->
    954                 interactive == powerManager.isInteractive() ? null : "expected=" + interactive
    955         );
    956     }
    957 
    958     private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition)
    959         throws Exception {
    960         assertDelayedCondition(errMsgPrefix, condition, GENERAL_TIMEOUT_MS, GENERAL_INTERVAL_MS);
    961     }
    962 
    963     private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition,
    964             long timeoutMs, long checkIntervalMs) throws Exception {
    965         final long endTime = SystemClock.uptimeMillis() + timeoutMs;
    966         while (SystemClock.uptimeMillis() <= endTime) {
    967             if (condition.getErrIfNotTrue() == null) {
    968                 return;
    969             }
    970             SystemClock.sleep(checkIntervalMs);
    971         }
    972         final String errMsg = condition.getErrIfNotTrue();
    973         if (errMsg != null) {
    974             fail(errMsgPrefix + ": " + errMsg);
    975         }
    976     }
    977 
    978     private static void assertBatteryState(boolean pluggedIn) throws Exception {
    979         final long endTime = SystemClock.uptimeMillis() + BATTERY_STATE_TIMEOUT_MS;
    980         while (isDevicePluggedIn() != pluggedIn && SystemClock.uptimeMillis() <= endTime) {
    981             Thread.sleep(BATTERY_STATE_CHECK_INTERVAL_MS);
    982         }
    983         if (isDevicePluggedIn() != pluggedIn) {
    984             fail("Timed out waiting for the plugged-in state to change,"
    985                     + " expected pluggedIn: " + pluggedIn);
    986         }
    987     }
    988 
    989     private static boolean isDevicePluggedIn() {
    990         final Intent batteryIntent = sContext.registerReceiver(null,
    991                 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    992         return batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) > 0;
    993     }
    994 
    995     private static String executeCmd(String cmd) throws Exception {
    996         final String result = sUiDevice.executeShellCommand(cmd).trim();
    997         Log.d(TAG, String.format("Result for '%s': %s", cmd, result));
    998         return result;
    999     }
   1000 
   1001     private static String executeCmdSilent(String cmd) throws Exception {
   1002         return sUiDevice.executeShellCommand(cmd).trim();
   1003     }
   1004 
   1005     private interface ExpectedCondition {
   1006         String getErrIfNotTrue() throws Exception;
   1007     }
   1008 }
   1009