1 /* 2 * Copyright (C) 2020 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 17 package android.cts.statsdatom.lib; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import android.service.battery.BatteryServiceDumpProto; 23 24 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 25 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 26 import com.android.ddmlib.testrunner.TestResult.TestStatus; 27 import com.android.tradefed.build.IBuildInfo; 28 import com.android.tradefed.device.CollectingByteOutputReceiver; 29 import com.android.tradefed.device.DeviceNotAvailableException; 30 import com.android.tradefed.device.ITestDevice; 31 import com.android.tradefed.log.LogUtil; 32 import com.android.tradefed.log.LogUtil.CLog; 33 import com.android.tradefed.result.CollectingTestListener; 34 import com.android.tradefed.result.ITestInvocationListener; 35 import com.android.tradefed.result.ResultForwarder; 36 import com.android.tradefed.result.TestDescription; 37 import com.android.tradefed.result.TestResult; 38 import com.android.tradefed.result.TestRunResult; 39 import com.android.tradefed.util.Pair; 40 import com.android.tradefed.util.RunUtil; 41 42 import com.google.protobuf.ExtensionRegistry; 43 import com.google.protobuf.InvalidProtocolBufferException; 44 import com.google.protobuf.MessageLite; 45 import com.google.protobuf.Parser; 46 47 import java.io.FileNotFoundException; 48 import java.util.Map; 49 import java.util.Objects; 50 import java.util.StringTokenizer; 51 52 import javax.annotation.Nonnull; 53 import javax.annotation.Nullable; 54 55 /** 56 * Contains utility functions for interacting with the device. 57 * Largely copied from incident's ProtoDumpTestCase. 58 */ 59 public final class DeviceUtils { 60 public static final String STATSD_ATOM_TEST_APK = "CtsStatsdAtomApp.apk"; 61 public static final String STATSD_ATOM_TEST_PKG = "com.android.server.cts.device.statsdatom"; 62 63 private static final String TEST_RUNNER = "androidx.test.runner.AndroidJUnitRunner"; 64 65 private static final String KEY_ACTION = "action"; 66 67 // feature names 68 public static final String FEATURE_WATCH = "android.hardware.type.watch"; 69 70 public static final String DUMP_BATTERY_CMD = "dumpsys battery"; 71 72 /** 73 * Runs device side tests. 74 * 75 * @param device Can be retrieved by running getDevice() in a class that extends DeviceTestCase 76 * @param pkgName Test package name, such as "com.android.server.cts.statsdatom" 77 * @param testClassName Test class name which can either be a fully qualified name or "." + a 78 * class name; if null, all test in the package will be run 79 * @param testMethodName Test method name; if null, all tests in class or package will be run 80 * @return {@link TestRunResult} of this invocation 81 * @throws DeviceNotAvailableException 82 */ runDeviceTests(ITestDevice device, String pkgName, @Nullable String testClassName, @Nullable String testMethodName)83 public static @Nonnull TestRunResult runDeviceTests(ITestDevice device, String pkgName, 84 @Nullable String testClassName, @Nullable String testMethodName) 85 throws DeviceNotAvailableException { 86 return internalRunDeviceTests(device, pkgName, testClassName, testMethodName, null); 87 } 88 89 /** 90 * Runs device side tests. 91 * 92 * @param device Can be retrieved by running getDevice() in a class that extends DeviceTestCase 93 * @param pkgName Test package name, such as "com.android.server.cts.statsdatom" 94 * @param testClassName Test class name which can either be a fully qualified name or "." + a 95 * class name; if null, all test in the package will be run 96 * @param testMethodName Test method name; if null, all tests in class or package will be run 97 * @param listener Listener for test results from the test invocation. 98 * @return {@link TestRunResult} of this invocation 99 * @throws DeviceNotAvailableException 100 */ runDeviceTests(ITestDevice device, String pkgName, @Nullable String testClassName, @Nullable String testMethodName, ITestInvocationListener listener)101 public static @Nonnull TestRunResult runDeviceTests(ITestDevice device, String pkgName, 102 @Nullable String testClassName, @Nullable String testMethodName, 103 ITestInvocationListener listener) 104 throws DeviceNotAvailableException { 105 return internalRunDeviceTests(device, pkgName, testClassName, testMethodName, listener); 106 } 107 internalRunDeviceTests(ITestDevice device, String pkgName, @Nullable String testClassName, @Nullable String testMethodName, @Nullable ITestInvocationListener otherListener)108 private static @Nonnull TestRunResult internalRunDeviceTests(ITestDevice device, String pkgName, 109 @Nullable String testClassName, @Nullable String testMethodName, 110 @Nullable ITestInvocationListener otherListener) 111 throws DeviceNotAvailableException { 112 if (testClassName != null && testClassName.startsWith(".")) { 113 testClassName = pkgName + testClassName; 114 } 115 116 RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner( 117 pkgName, TEST_RUNNER, device.getIDevice()); 118 if (testClassName != null && testMethodName != null) { 119 testRunner.setMethodName(testClassName, testMethodName); 120 } else if (testClassName != null) { 121 testRunner.setClassName(testClassName); 122 } 123 124 CollectingTestListener collectingTestListener = new CollectingTestListener(); 125 ITestInvocationListener combinedLister; 126 if (otherListener != null) { 127 combinedLister = new ResultForwarder(otherListener, collectingTestListener); 128 } else { 129 combinedLister = collectingTestListener; 130 } 131 132 assertThat(device.runInstrumentationTests(testRunner, combinedLister)).isTrue(); 133 134 final TestRunResult result = collectingTestListener.getCurrentRunResults(); 135 if (result.isRunFailure()) { 136 throw new Error("Failed to successfully run device tests for " 137 + result.getName() + ": " + result.getRunFailureMessage()); 138 } 139 if (result.getNumTests() == 0) { 140 throw new Error("No tests were run on the device"); 141 } 142 if (result.hasFailedTests()) { 143 StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n"); 144 for (Map.Entry<TestDescription, TestResult> resultEntry : 145 result.getTestResults().entrySet()) { 146 if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) { 147 errorBuilder.append(resultEntry.getKey().toString()); 148 errorBuilder.append(":\n"); 149 errorBuilder.append(resultEntry.getValue().getStackTrace()); 150 } 151 } 152 throw new AssertionError(errorBuilder.toString()); 153 } 154 return result; 155 } 156 157 /** 158 * Runs device side tests from the com.android.server.cts.device.statsdatom package. 159 */ runDeviceTestsOnStatsdApp(ITestDevice device, @Nullable String testClassName, @Nullable String testMethodName)160 public static @Nonnull TestRunResult runDeviceTestsOnStatsdApp(ITestDevice device, 161 @Nullable String testClassName, @Nullable String testMethodName) 162 throws DeviceNotAvailableException { 163 return runDeviceTests(device, STATSD_ATOM_TEST_PKG, testClassName, testMethodName); 164 } 165 166 /** 167 * Install the statsdatom CTS app to the device. 168 */ installStatsdTestApp(ITestDevice device, IBuildInfo ctsBuildInfo)169 public static void installStatsdTestApp(ITestDevice device, IBuildInfo ctsBuildInfo) 170 throws FileNotFoundException, DeviceNotAvailableException { 171 installTestApp(device, STATSD_ATOM_TEST_APK, STATSD_ATOM_TEST_PKG, ctsBuildInfo); 172 } 173 174 /** 175 * Install a test app to the device. 176 */ installTestApp(ITestDevice device, String apkName, String pkgName, IBuildInfo ctsBuildInfo)177 public static void installTestApp(ITestDevice device, String apkName, String pkgName, 178 IBuildInfo ctsBuildInfo) throws FileNotFoundException, DeviceNotAvailableException { 179 CLog.d("Installing app " + apkName); 180 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(ctsBuildInfo); 181 final String result = device.installPackage( 182 buildHelper.getTestFile(apkName), /*reinstall=*/true, /*grantPermissions=*/true); 183 assertWithMessage("Failed to install " + apkName + ": " + result).that(result).isNull(); 184 allowBackgroundServices(device, pkgName); 185 } 186 187 /** 188 * Required to successfully start a background service from adb, starting in O. 189 */ allowBackgroundServices(ITestDevice device, String pkgName)190 private static void allowBackgroundServices(ITestDevice device, String pkgName) 191 throws DeviceNotAvailableException { 192 String cmd = "cmd deviceidle tempwhitelist " + pkgName; 193 device.executeShellCommand(cmd); 194 } 195 196 /** 197 * Uninstall the statsdatom CTS app from the device. 198 */ uninstallStatsdTestApp(ITestDevice device)199 public static void uninstallStatsdTestApp(ITestDevice device) throws Exception { 200 uninstallTestApp(device, STATSD_ATOM_TEST_PKG); 201 } 202 203 /** 204 * Uninstall the test app from the device. 205 */ uninstallTestApp(ITestDevice device, String pkgName)206 public static void uninstallTestApp(ITestDevice device, String pkgName) throws Exception { 207 device.uninstallPackage(pkgName); 208 } 209 210 /** 211 * Run an adb shell command on device and parse the results as a proto of a given type. 212 * 213 * @param device Device to run cmd on 214 * @param parser Protobuf parser object, which can be retrieved by running MyProto.parser() 215 * @param extensionRegistry ExtensionRegistry containing extensions that should be parsed 216 * @param cmd The adb shell command to run (e.g. "cmd stats update config") 217 * 218 * @throws DeviceNotAvailableException 219 * @throws InvalidProtocolBufferException Occurs if there was an error parsing the proto. Note 220 * that a 0 length buffer is not necessarily an error. 221 * @return Proto of specified type 222 */ getShellCommandOutput(@onnull ITestDevice device, Parser<T> parser, ExtensionRegistry extensionRegistry, String cmd)223 public static <T extends MessageLite> T getShellCommandOutput(@Nonnull ITestDevice device, 224 Parser<T> parser, ExtensionRegistry extensionRegistry, String cmd) 225 throws DeviceNotAvailableException, InvalidProtocolBufferException { 226 final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver(); 227 device.executeShellCommand(cmd, receiver); 228 try { 229 return parser.parseFrom(receiver.getOutput(), extensionRegistry); 230 } catch (Exception ex) { 231 CLog.d("Error parsing " + parser.getClass().getCanonicalName() + " for cmd " + cmd); 232 throw ex; 233 } 234 } 235 getShellCommandOutput( @onnull ITestDevice device, Parser<T> parser, String cmd)236 public static <T extends MessageLite> T getShellCommandOutput( 237 @Nonnull ITestDevice device, Parser<T> parser, String cmd) 238 throws DeviceNotAvailableException, InvalidProtocolBufferException { 239 return getShellCommandOutput(device, parser, ExtensionRegistry.getEmptyRegistry(), cmd); 240 } 241 242 /** 243 * Returns the UID of the host, which should always either be AID_SHELL (2000) or AID_ROOT (0). 244 */ getHostUid(ITestDevice device)245 public static int getHostUid(ITestDevice device) throws DeviceNotAvailableException { 246 String uidString = ""; 247 try { 248 uidString = device.executeShellCommand("id -u"); 249 return Integer.parseInt(uidString.trim()); 250 } catch (NumberFormatException ex) { 251 CLog.e("Failed to get host's uid via shell command. Found " + uidString); 252 // Fall back to alternative method... 253 if (device.isAdbRoot()) { 254 return 0; 255 } else { 256 return 2000; // SHELL 257 } 258 } 259 } 260 261 /** 262 * Returns the UID of the statsdatom CTS test app. 263 */ getStatsdTestAppUid(ITestDevice device)264 public static int getStatsdTestAppUid(ITestDevice device) throws DeviceNotAvailableException { 265 return getAppUid(device, STATSD_ATOM_TEST_PKG); 266 } 267 268 /** 269 * Returns the UID of the test app for the current user. 270 */ getAppUid(ITestDevice device, String pkgName)271 public static int getAppUid(ITestDevice device, String pkgName) 272 throws DeviceNotAvailableException { 273 int currentUser = device.getCurrentUser(); 274 return getAppUidForUser(device, pkgName, currentUser); 275 } 276 277 /** 278 * Returns the UID of the test app for the given user. 279 */ getAppUidForUser(ITestDevice device, String pkgName, int userId)280 public static int getAppUidForUser(ITestDevice device, String pkgName, int userId) 281 throws DeviceNotAvailableException { 282 String uidLine = device.executeShellCommand("cmd package list packages -U --user " 283 + userId + " " + pkgName); 284 285 // Split package list by lines 286 // Sample packages response: 287 // package:com.android.server.cts.device.statsd.host uid:1010033 288 // package:com.android.server.cts.device.statsd uid:1010034 289 final String[] lines = uidLine.split("\\R+"); 290 for (final String line : lines) { 291 if (line.startsWith("package:" + pkgName + " ")) { 292 final int uidIndex = line.lastIndexOf(":") + 1; 293 final int uid = Integer.parseInt(line.substring(uidIndex).trim()); 294 assertThat(uid).isGreaterThan(10_000); 295 return uid; 296 } 297 } 298 throw new Error( 299 String.format("Could not find installed package: %s", pkgName)); 300 } 301 302 /** 303 * Determines if the device has the given features. 304 * 305 * @param feature name of the feature (e.g. "android.hardware.bluetooth") 306 */ hasFeature(ITestDevice device, String feature)307 public static boolean hasFeature(ITestDevice device, String feature) throws Exception { 308 final String features = device.executeShellCommand("pm list features"); 309 StringTokenizer featureToken = new StringTokenizer(features, "\n"); 310 311 while(featureToken.hasMoreTokens()) { 312 if (("feature:" + feature).equals(featureToken.nextToken())) { 313 return true; 314 } 315 } 316 317 return false; 318 } 319 320 /** 321 * Runs an activity in a particular app. 322 */ runActivity(ITestDevice device, String pkgName, String activity, @Nullable String actionKey, @Nullable String actionValue)323 public static void runActivity(ITestDevice device, String pkgName, String activity, 324 @Nullable String actionKey, @Nullable String actionValue) throws Exception { 325 runActivity(device, pkgName, activity, actionKey, actionValue, 326 AtomTestUtils.WAIT_TIME_LONG); 327 } 328 329 /** 330 * Runs an activity in a particular app for a certain period of time. 331 * 332 * @param pkgName name of package that contains the Activity 333 * @param activity name of the Activity class 334 * @param actionKey key of extra data that is passed to the Activity via an Intent 335 * @param actionValue value of extra data that is passed to the Activity via an Intent 336 * @param waitTimeMs duration that the activity runs for 337 */ runActivity(ITestDevice device, String pkgName, String activity, @Nullable String actionKey, @Nullable String actionValue, long waitTimeMs)338 public static void runActivity(ITestDevice device, String pkgName, String activity, 339 @Nullable String actionKey, @Nullable String actionValue, long waitTimeMs) 340 throws Exception { 341 try (AutoCloseable a = withActivity(device, pkgName, activity, actionKey, actionValue)) { 342 RunUtil.getDefault().sleep(waitTimeMs); 343 } 344 } 345 346 /** 347 * Starts the specified activity and returns an {@link AutoCloseable} that stops the activity 348 * when closed. 349 * 350 * <p>Example usage: 351 * <pre> 352 * try (AutoClosable a = withActivity("activity", "action", "action-value")) { 353 * doStuff(); 354 * } 355 * </pre> 356 */ withActivity(ITestDevice device, String pkgName, String activity, @Nullable String actionKey, @Nullable String actionValue)357 public static AutoCloseable withActivity(ITestDevice device, String pkgName, String activity, 358 @Nullable String actionKey, @Nullable String actionValue) throws Exception { 359 String intentString; 360 if (actionKey != null && actionValue != null) { 361 intentString = actionKey + " " + actionValue; 362 } else { 363 intentString = null; 364 } 365 366 String cmd = "am start -n " + pkgName + "/." + activity; 367 if (intentString != null) { 368 cmd += " -e " + intentString; 369 } 370 device.executeShellCommand(cmd); 371 372 return () -> { 373 device.executeShellCommand("am force-stop " + pkgName); 374 RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT); 375 }; 376 } 377 setChargingState(ITestDevice device, int state)378 public static void setChargingState(ITestDevice device, int state) throws Exception { 379 device.executeShellCommand("cmd battery set status " + state); 380 } 381 unplugDevice(ITestDevice device)382 public static void unplugDevice(ITestDevice device) throws Exception { 383 // On batteryless devices on Android P or above, the 'unplug' command 384 // alone does not simulate the really unplugged state. 385 // 386 // This is because charging state is left as "unknown". Unless a valid 387 // state like 3 = BatteryManager.BATTERY_STATUS_DISCHARGING is set, 388 // framework does not consider the device as running on battery. 389 setChargingState(device, 3); 390 device.executeShellCommand("cmd battery unplug"); 391 } 392 plugInAc(ITestDevice device)393 public static void plugInAc(ITestDevice device) throws Exception { 394 device.executeShellCommand("cmd battery set ac 1"); 395 } 396 turnScreenOn(ITestDevice device)397 public static void turnScreenOn(ITestDevice device) throws Exception { 398 device.executeShellCommand("input keyevent KEYCODE_WAKEUP"); 399 device.executeShellCommand("wm dismiss-keyguard"); 400 } 401 turnScreenOff(ITestDevice device)402 public static void turnScreenOff(ITestDevice device) throws Exception { 403 device.executeShellCommand("input keyevent KEYCODE_SLEEP"); 404 } 405 turnBatteryStatsAutoResetOn(ITestDevice device)406 public static void turnBatteryStatsAutoResetOn(ITestDevice device) throws Exception { 407 device.executeShellCommand("dumpsys batterystats enable no-auto-reset"); 408 } 409 turnBatteryStatsAutoResetOff(ITestDevice device)410 public static void turnBatteryStatsAutoResetOff(ITestDevice device) throws Exception { 411 device.executeShellCommand("dumpsys batterystats enable no-auto-reset"); 412 } 413 flushBatteryStatsHandlers(ITestDevice device)414 public static void flushBatteryStatsHandlers(ITestDevice device) throws Exception { 415 // Dumping batterystats will flush everything in the batterystats handler threads. 416 device.executeShellCommand("dumpsys batterystats"); 417 } 418 hasBattery(ITestDevice device)419 public static boolean hasBattery(ITestDevice device) throws Exception { 420 try { 421 BatteryServiceDumpProto batteryProto = getShellCommandOutput(device, BatteryServiceDumpProto.parser(), 422 String.join(" ", DUMP_BATTERY_CMD, "--proto")); 423 LogUtil.CLog.d("Got battery service dump:\n " + batteryProto.toString()); 424 return batteryProto.getIsPresent(); 425 } catch (com.google.protobuf.InvalidProtocolBufferException e) { 426 LogUtil.CLog.e("Failed to dump batteryservice proto"); 427 throw (e); 428 } 429 } 430 resetBatteryStatus(ITestDevice device)431 public static void resetBatteryStatus(ITestDevice device) throws Exception { 432 device.executeShellCommand("cmd battery reset"); 433 } 434 getProperty(ITestDevice device, String prop)435 public static String getProperty(ITestDevice device, String prop) throws Exception { 436 return device.executeShellCommand("getprop " + prop).replace("\n", ""); 437 } 438 getDeviceConfigFeature(ITestDevice device, String namespace, String key)439 public static String getDeviceConfigFeature(ITestDevice device, String namespace, 440 String key) throws Exception { 441 return device.executeShellCommand( 442 "device_config get " + namespace + " " + key).replace("\n", ""); 443 } 444 putDeviceConfigFeature(ITestDevice device, String namespace, String key, String value)445 public static String putDeviceConfigFeature(ITestDevice device, String namespace, 446 String key, String value) throws Exception { 447 return device.executeShellCommand( 448 "device_config put " + namespace + " " + key + " " + value).replace("\n", ""); 449 } 450 deleteDeviceConfigFeature(ITestDevice device, String namespace, String key)451 public static String deleteDeviceConfigFeature(ITestDevice device, String namespace, 452 String key) throws Exception { 453 return device.executeShellCommand( 454 "device_config delete " + namespace + " " + key).replace("\n", ""); 455 } 456 isDebuggable(ITestDevice device)457 public static boolean isDebuggable(ITestDevice device) throws Exception { 458 return Integer.parseInt(getProperty(device, "ro.debuggable")) == 1; 459 } 460 checkDeviceFor(ITestDevice device, String methodName)461 public static boolean checkDeviceFor(ITestDevice device, String methodName) throws Exception { 462 try { 463 runDeviceTestsOnStatsdApp(device, ".Checkers", methodName); 464 // Test passes, meaning that the answer is true. 465 LogUtil.CLog.d(methodName + "() indicates true."); 466 return true; 467 } catch (AssertionError e) { 468 // Method is designed to fail if the answer is false. 469 LogUtil.CLog.d(methodName + "() indicates false."); 470 return false; 471 } 472 } 473 474 /** Make the test app standby-active so it can run syncs and jobs immediately. */ allowImmediateSyncs(ITestDevice device)475 public static void allowImmediateSyncs(ITestDevice device) throws Exception { 476 device.executeShellCommand("am set-standby-bucket " 477 + DeviceUtils.STATSD_ATOM_TEST_PKG + " active"); 478 } 479 480 /** 481 * Runs a (background) service to perform the given action. 482 * @param testPackage the test package that contains the background service. 483 * @param service the service name 484 * @param actionValue the action code constants indicating the desired action to perform. 485 */ executeBackgroundService(ITestDevice device, String testPackage, String service, String actionValue)486 public static void executeBackgroundService(ITestDevice device, String testPackage, 487 String service, String actionValue) throws Exception { 488 executeServiceAction(device, testPackage, service, actionValue); 489 } 490 491 492 /** 493 * Runs a (background) service to perform the given action. 494 * @param actionValue the action code constants indicating the desired action to perform. 495 */ executeBackgroundService(ITestDevice device, String actionValue)496 public static void executeBackgroundService(ITestDevice device, String actionValue) 497 throws Exception { 498 executeServiceAction(device, STATSD_ATOM_TEST_PKG, 499 "StatsdCtsBackgroundService", actionValue); 500 } 501 502 /** 503 * Runs the specified statsd package service to perform the given action. 504 * @param actionValue the action code constants indicating the desired action to perform. 505 */ executeServiceAction(ITestDevice device, String testPackage, String service, String actionValue)506 public static void executeServiceAction(ITestDevice device, String testPackage, 507 String service, String actionValue) throws Exception { 508 allowBackgroundServices(device); 509 device.executeShellCommand(String.format( 510 "am startservice -n '%s/.%s' -e %s %s", 511 testPackage, service, 512 KEY_ACTION, actionValue)); 513 } 514 rebootDeviceAndWaitUntilReady(ITestDevice device)515 public static void rebootDeviceAndWaitUntilReady(ITestDevice device) throws Exception { 516 device.rebootUntilOnline(); 517 // Wait for 3 mins. 518 assertWithMessage("Device failed to boot") 519 .that(device.waitForBootComplete(180_000)).isTrue(); 520 assertWithMessage("Stats service failed to start") 521 .that(waitForStatsServiceStart(device, 60_000)).isTrue(); 522 RunUtil.getDefault().sleep(2_000); 523 } 524 waitForStatsServiceStart(ITestDevice device, long waitTime)525 private static boolean waitForStatsServiceStart(ITestDevice device, long waitTime) 526 throws Exception { 527 LogUtil.CLog.i("Waiting %d ms for stats service to start", waitTime); 528 int counter = 1; 529 long startTime = System.currentTimeMillis(); 530 while ((System.currentTimeMillis() - startTime) < waitTime) { 531 if ("running".equals(getProperty(device, "init.svc.statsd"))) { 532 return true; 533 } 534 RunUtil.getDefault().sleep(Math.min(200 * counter, 2_000)); 535 counter++; 536 } 537 LogUtil.CLog.w("Stats service did not start after %d ms", waitTime); 538 return false; 539 } 540 541 /** 542 * Required to successfully start a background service from adb in Android O. 543 */ allowBackgroundServices(ITestDevice device)544 private static void allowBackgroundServices(ITestDevice device) throws Exception { 545 device.executeShellCommand(String.format( 546 "cmd deviceidle tempwhitelist %s", STATSD_ATOM_TEST_PKG)); 547 } 548 549 /** 550 * Returns the kernel major version as a pair of ints. 551 */ getKernelVersion(ITestDevice device)552 public static Pair<Integer, Integer> getKernelVersion(ITestDevice device) 553 throws Exception { 554 String[] version = device.executeShellCommand("uname -r").split("\\."); 555 if (version.length < 2) { 556 throw new RuntimeException("Could not parse kernel version"); 557 } 558 return Pair.create(Integer.parseInt(version[0]), Integer.parseInt(version[1])); 559 } 560 561 /** Returns if the device kernel version >= input kernel version. */ isKernelGreaterEqual(ITestDevice device, Pair<Integer, Integer> version)562 public static boolean isKernelGreaterEqual(ITestDevice device, Pair<Integer, Integer> version) 563 throws Exception { 564 Pair<Integer, Integer> kernelVersion = getKernelVersion(device); 565 return kernelVersion.first > version.first 566 || (Objects.equals(kernelVersion.first, version.first) 567 && kernelVersion.second >= version.second); 568 } 569 570 // Gets whether "Always on Display" setting is enabled. 571 // In rare cases, this is different from whether the device can enter SCREEN_STATE_DOZE. getAodState(ITestDevice device)572 public static String getAodState(ITestDevice device) throws Exception { 573 return device.executeShellCommand("settings get secure doze_always_on"); 574 } 575 setAodState(ITestDevice device, String state)576 public static void setAodState(ITestDevice device, String state) throws Exception { 577 device.executeShellCommand("settings put secure doze_always_on " + state); 578 } 579 DeviceUtils()580 private DeviceUtils() {} 581 }