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