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