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.car.cts; 18 19 import static com.android.tradefed.targetprep.UserHelper.RUN_TESTS_AS_USER_KEY; 20 21 import static com.google.common.truth.Truth.assertWithMessage; 22 23 import static org.junit.Assert.fail; 24 import static org.junit.Assume.assumeTrue; 25 26 import android.service.pm.PackageProto; 27 import android.service.pm.PackageProto.UserPermissionsProto; 28 import android.service.pm.PackageServiceDumpProto; 29 30 import com.android.compatibility.common.util.CommonTestUtils; 31 import com.android.compatibility.common.util.CommonTestUtils.BooleanSupplierWithThrow; 32 import com.android.tradefed.device.CollectingByteOutputReceiver; 33 import com.android.tradefed.device.DeviceNotAvailableException; 34 import com.android.tradefed.device.ITestDevice; 35 import com.android.tradefed.log.LogUtil.CLog; 36 import com.android.tradefed.testtype.ITestInformationReceiver; 37 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 38 39 import org.junit.After; 40 import org.junit.AssumptionViolatedException; 41 import org.junit.Before; 42 import org.junit.Rule; 43 import org.junit.rules.TestRule; 44 import org.junit.runner.Description; 45 import org.junit.runners.model.Statement; 46 47 import java.util.ArrayList; 48 import java.util.HashMap; 49 import java.util.HashSet; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.function.Function; 53 import java.util.regex.Matcher; 54 import java.util.regex.Pattern; 55 import java.util.stream.Collectors; 56 57 /** 58 * Base class for all test cases. 59 */ 60 // NOTE: must be public because of @Rules 61 public abstract class CarHostJUnit4TestCase extends BaseHostJUnit4Test { 62 63 private static final int SYSTEM_USER_ID = 0; 64 65 private static final int DEFAULT_TIMEOUT_SEC = 20; 66 protected static final int SYSTEM_RESTART_TIMEOUT_SEC = 120; 67 68 private static final Pattern CREATE_USER_OUTPUT_PATTERN = Pattern.compile("id=(\\d+)"); 69 70 private static final String USER_PREFIX = "CtsCarHostTestCases"; 71 72 /** 73 * User pattern in the output of "cmd user list --all -v" 74 * TEXT id=<id> TEXT name=<name>, TEX flags=<flags> TEXT 75 * group 1: id group 2: name group 3: flags group 4: other state(like "(running)") 76 */ 77 private static final Pattern USER_PATTERN = Pattern.compile( 78 ".*id=(\\d+).*name=([^\\s,]+).*flags=(\\S+)(.*)"); 79 80 private static final int USER_PATTERN_GROUP_ID = 1; 81 private static final int USER_PATTERN_GROUP_NAME = 2; 82 private static final int USER_PATTERN_GROUP_FLAGS = 3; 83 private static final int USER_PATTERN_GROUP_OTHER_STATE = 4; 84 85 /** 86 * User's package permission pattern string format in the output of "dumpsys package PKG_NAME" 87 */ 88 protected static final String APP_APK = "CtsCarApp.apk"; 89 protected static final String APP_PKG = "android.car.cts.app"; 90 91 @Rule 92 public final RequiredFeatureRule mHasAutomotiveRule = new RequiredFeatureRule(this, 93 "android.hardware.type.automotive"); 94 95 private final HashSet<Integer> mUsersToBeRemoved = new HashSet<>(); 96 97 private int mInitialUserId; 98 private int mTestRunningUserId; 99 private Integer mInitialMaximumNumberOfUsers; 100 101 // It is possible that during test initial user is deleted and it is not possible to switch 102 // to the initial User. This boolean controls if test should switch to initial user on clean up. 103 private boolean mSwitchToInitialUser = true; 104 105 /** 106 * Saves multi-user state so it can be restored after the test. 107 */ 108 @Before saveUserState()109 public void saveUserState() throws Exception { 110 removeUsers(USER_PREFIX); 111 112 mInitialUserId = getCurrentUserId(); 113 114 // The test runs as the current user in most cases. For secondary_user_on_secondary_display 115 // case, we set mTestRunningUserId from RUN_TEST_AS_USER. 116 mTestRunningUserId = getCurrentUserId(); 117 if (getDevice().isVisibleBackgroundUsersSupported()) { 118 try { 119 mTestRunningUserId = Integer.parseInt( 120 getTestInformation().properties().get(RUN_TESTS_AS_USER_KEY)); 121 } catch (Exception e) { 122 CLog.e("Failed to parse the userId for " + RUN_TESTS_AS_USER_KEY + " due to " + e); 123 } 124 } 125 } 126 127 /** 128 * Restores multi-user state from before the test. 129 */ 130 @After restoreUsersState()131 public void restoreUsersState() throws Exception { 132 int currentUserId = getCurrentUserId(); 133 CLog.d("restoreUsersState(): initial user: %d, current user: %d, created users: %s " 134 + "max number of users: %d", 135 mInitialUserId, currentUserId, mUsersToBeRemoved, mInitialMaximumNumberOfUsers); 136 if (currentUserId != mInitialUserId && mSwitchToInitialUser) { 137 CLog.i("Switching back from %d to %d", currentUserId, mInitialUserId); 138 switchUser(mInitialUserId); 139 } 140 141 if (!mUsersToBeRemoved.isEmpty()) { 142 CLog.i("Removing users %s", mUsersToBeRemoved); 143 for (int userId : mUsersToBeRemoved) { 144 removeUser(userId); 145 } 146 } 147 148 // Should have been removed above, but as the saying goes, better safe than sorry... 149 removeUsers(USER_PREFIX); 150 151 if (mInitialMaximumNumberOfUsers != null) { 152 CLog.i("Restoring max number of users to %d", mInitialMaximumNumberOfUsers); 153 setMaxNumberUsers(mInitialMaximumNumberOfUsers); 154 } 155 } 156 157 /** 158 * It is possible that during test initial user is deleted and it is not possible to switch to 159 * the initial User. This method controls if test should switch to initial user on clean up. 160 */ doNotSwitchToInitialUserAfterTest()161 public void doNotSwitchToInitialUserAfterTest() { 162 mSwitchToInitialUser = false; 163 } 164 165 /** 166 * Returns whether device is in headless system user mode. 167 */ isHeadlessSystemUserMode()168 boolean isHeadlessSystemUserMode() throws Exception { 169 return getDevice().isHeadlessSystemUserMode(); 170 } 171 172 /** 173 * Makes sure the device supports multiple users, throwing {@link AssumptionViolatedException} 174 * if it doesn't. 175 */ assumeSupportsMultipleUsers()176 protected final void assumeSupportsMultipleUsers() throws Exception { 177 assumeTrue("device does not support multi-user", 178 getDevice().getMaxNumberOfUsersSupported() > 1); 179 } 180 181 /** 182 * Makes sure the device can add {@code numberOfUsers} new users, increasing limit if needed or 183 * failing if not possible. 184 */ requiresExtraUsers(int numberOfUsers)185 protected final void requiresExtraUsers(int numberOfUsers) throws Exception { 186 assumeSupportsMultipleUsers(); 187 188 int maxNumber = getDevice().getMaxNumberOfUsersSupported(); 189 int currentNumber = getDevice().listUsers().size(); 190 191 if (currentNumber + numberOfUsers <= maxNumber) return; 192 193 if (!getDevice().isAdbRoot()) { 194 failCannotCreateUsers(numberOfUsers, currentNumber, maxNumber, /* isAdbRoot= */ false); 195 } 196 197 // Increase limit... 198 mInitialMaximumNumberOfUsers = maxNumber; 199 setMaxNumberUsers(maxNumber + numberOfUsers); 200 201 // ...and try again 202 maxNumber = getDevice().getMaxNumberOfUsersSupported(); 203 if (currentNumber + numberOfUsers > maxNumber) { 204 failCannotCreateUsers(numberOfUsers, currentNumber, maxNumber, /* isAdbRoot= */ true); 205 } 206 } 207 failCannotCreateUsers(int numberOfUsers, int currentNumber, int maxNumber, boolean isAdbRoot)208 private void failCannotCreateUsers(int numberOfUsers, int currentNumber, int maxNumber, 209 boolean isAdbRoot) { 210 String reason = isAdbRoot ? "failed to increase it" 211 : "cannot be increased without adb root"; 212 String existingUsers = ""; 213 try { 214 existingUsers = "Existing users: " + executeCommand("cmd user list --all -v"); 215 } catch (Exception e) { 216 // ignore 217 } 218 fail("Cannot create " + numberOfUsers + " users: current number is " + currentNumber 219 + ", limit is " + maxNumber + " and could not be increased (" + reason + "). " 220 + existingUsers); 221 } 222 223 /** 224 * Executes the shell command and returns the output. 225 */ executeCommand(String command, Object... args)226 protected String executeCommand(String command, Object... args) throws Exception { 227 String fullCommand = String.format(command, args); 228 return getDevice().executeShellCommand(fullCommand); 229 } 230 231 /** 232 * Executes the shell command and parses output with {@code resultParser}. 233 */ executeAndParseCommand(Function<String, T> resultParser, String command, Object... args)234 protected <T> T executeAndParseCommand(Function<String, T> resultParser, 235 String command, Object... args) throws Exception { 236 String output = executeCommand(command, args); 237 return resultParser.apply(output); 238 } 239 240 /** 241 * Executes the shell command and parses the Matcher output with {@code resultParser}, failing 242 * with {@code matchNotFoundErrorMessage} if it didn't match the {@code regex}. 243 */ executeAndParseCommand(Pattern regex, String matchNotFoundErrorMessage, Function<Matcher, T> resultParser, String command, Object... args)244 protected <T> T executeAndParseCommand(Pattern regex, String matchNotFoundErrorMessage, 245 Function<Matcher, T> resultParser, 246 String command, Object... args) throws Exception { 247 String output = executeCommand(command, args); 248 Matcher matcher = regex.matcher(output); 249 if (!matcher.find()) { 250 fail(matchNotFoundErrorMessage + ". Shell command: '" + String.format(command, args) 251 + "'. Output: " + output.trim() + ". Regex: " + regex); 252 } 253 return resultParser.apply(matcher); 254 } 255 256 /** 257 * Executes the shell command and parses the Matcher output with {@code resultParser}. 258 */ executeAndParseCommand(Pattern regex, Function<Matcher, T> resultParser, String command, Object... args)259 protected <T> T executeAndParseCommand(Pattern regex, Function<Matcher, T> resultParser, 260 String command, Object... args) throws Exception { 261 String output = executeCommand(command, args); 262 return resultParser.apply(regex.matcher(output)); 263 } 264 265 /** 266 * Executes the shell command that returns all users (including pre-created and partial) 267 * and returns {@code function} applied to them. 268 */ onAllUsers(Function<List<UserInfo>, T> function)269 public <T> T onAllUsers(Function<List<UserInfo>, T> function) throws Exception { 270 ArrayList<UserInfo> allUsers = executeAndParseCommand(USER_PATTERN, (matcher) -> { 271 ArrayList<UserInfo> users = new ArrayList<>(); 272 while (matcher.find()) { 273 users.add(new UserInfo(matcher)); 274 } 275 return users; 276 }, "cmd user list --all -v"); 277 return function.apply(allUsers); 278 } 279 280 /** 281 * Gets the info for the given user. 282 */ getUserInfo(int userId)283 public UserInfo getUserInfo(int userId) throws Exception { 284 return onAllUsers((allUsers) -> allUsers.stream() 285 .filter((u) -> u.id == userId)) 286 .findFirst().get(); 287 } 288 289 /** 290 * Gets all persistent (i.e., non-ephemeral) users. 291 */ getAllPersistentUsers()292 protected List<Integer> getAllPersistentUsers() throws Exception { 293 return onAllUsers((allUsers) -> allUsers.stream() 294 .filter((u) -> !u.flags.contains("DISABLED") && !u.flags.contains("EPHEMERAL") 295 && !u.otherState.contains("pre-created") 296 && !u.otherState.contains("partial")) 297 .map((u) -> u.id).collect(Collectors.toList())); 298 } 299 300 /** 301 * Sets the maximum number of users that can be created for this car. 302 * 303 * @throws IllegalStateException if adb is not running as root 304 */ setMaxNumberUsers(int numUsers)305 protected void setMaxNumberUsers(int numUsers) throws Exception { 306 if (!getDevice().isAdbRoot()) { 307 throw new IllegalStateException("must be running adb root"); 308 } 309 executeCommand("setprop fw.max_users %d", numUsers); 310 } 311 312 /** 313 * Gets the user's id that is running the test. 314 * 315 * <p>The tests run as the current user so this is same as {@link #getCurrentUserId()} in most 316 * cases. For secondary_user_on_secondary_display case, this is returned from RUN_TEST_AS_USER. 317 */ getTestRunningUserId()318 protected int getTestRunningUserId() { 319 return mTestRunningUserId; 320 } 321 322 /** 323 * Gets the current user's id. 324 */ getCurrentUserId()325 protected int getCurrentUserId() throws DeviceNotAvailableException { 326 return getDevice().getCurrentUser(); 327 } 328 329 /** 330 * Creates a full user with car service shell command. 331 */ createFullUser(String name)332 protected int createFullUser(String name) throws Exception { 333 return createUser(name, /* flags= */ 0, /* isGuest= */ false); 334 } 335 336 /** 337 * Creates a full guest with car service shell command. 338 */ createGuestUser(String name)339 protected int createGuestUser(String name) throws Exception { 340 return createUser(name, /* flags= */ 0, /* isGuest= */ true); 341 } 342 343 /** 344 * Creates a flexible user with car service shell command. 345 * 346 * <p><b>NOTE: </b>it uses User HAL flags, not core Android's. 347 */ createUser(String name, int flags, boolean isGuest)348 protected int createUser(String name, int flags, boolean isGuest) throws Exception { 349 name = USER_PREFIX + "." + name; 350 waitForCarServiceReady(); 351 int userId = executeAndParseCommand(CREATE_USER_OUTPUT_PATTERN, 352 "Could not create user with name " + name 353 + ", flags " + flags + ", guest " + isGuest, 354 matcher -> Integer.parseInt(matcher.group(1)), 355 "cmd car_service create-user --flags %d %s%s", 356 flags, (isGuest ? "--guest " : ""), name); 357 markUserForRemovalAfterTest(userId); 358 return userId; 359 } 360 361 /** 362 * Marks a user to be removed at the end of the tests. 363 */ markUserForRemovalAfterTest(int userId)364 protected void markUserForRemovalAfterTest(int userId) { 365 mUsersToBeRemoved.add(userId); 366 } 367 368 /** 369 * Waits until the given user is initialized. 370 */ waitForUserInitialized(int userId)371 protected void waitForUserInitialized(int userId) throws Exception { 372 CommonTestUtils.waitUntil("timed out waiting for user " + userId + " initialization", 373 DEFAULT_TIMEOUT_SEC, () -> isUserInitialized(userId)); 374 } 375 376 /** 377 * Waits until the system server is ready. 378 */ waitForCarServiceReady()379 protected void waitForCarServiceReady() throws Exception { 380 CommonTestUtils.waitUntil("timed out waiting for car service", 381 DEFAULT_TIMEOUT_SEC, () -> isCarServiceReady()); 382 } 383 isCarServiceReady()384 protected boolean isCarServiceReady() { 385 String cmd = "service check car_service"; 386 try { 387 String output = getDevice().executeShellCommand(cmd); 388 return !output.endsWith("not found"); 389 } catch (Exception e) { 390 CLog.i("%s failed: %s", cmd, e.getMessage()); 391 } 392 return false; 393 } 394 395 /** 396 * Asserts that the given user is initialized. 397 */ assertUserInitialized(int userId)398 protected void assertUserInitialized(int userId) throws Exception { 399 assertWithMessage("User %s not initialized", userId).that(isUserInitialized(userId)) 400 .isTrue(); 401 CLog.v("User %d is initialized", userId); 402 } 403 404 /** 405 * Checks if the given user is initialized. 406 */ isUserInitialized(int userId)407 protected boolean isUserInitialized(int userId) throws Exception { 408 UserInfo userInfo = getUserInfo(userId); 409 CLog.v("isUserInitialized(%d): %s", userId, userInfo); 410 return userInfo.flags.contains("INITIALIZED"); 411 } 412 413 /** 414 * Checks if the given user is ephemeral. 415 */ isUserEphemeral(int userId)416 protected boolean isUserEphemeral(int userId) throws Exception { 417 UserInfo userInfo = getUserInfo(userId); 418 CLog.v("isUserEphemeral(%d): %s", userId, userInfo); 419 return userInfo.flags.contains("EPHEMERAL"); 420 } 421 422 /** 423 * Switches the current user. 424 */ switchUser(int userId)425 protected void switchUser(int userId) throws Exception { 426 waitForCarServiceReady(); 427 String output = executeCommand("cmd car_service switch-user %d", userId); 428 if (!output.contains("STATUS_SUCCESSFUL")) { 429 throw new IllegalStateException("Failed to switch to user " + userId + ": " + output); 430 } 431 waitUntilCurrentUser(userId); 432 } 433 434 /** 435 * Waits until the given user is the current foreground user. 436 */ waitUntilCurrentUser(int userId)437 protected void waitUntilCurrentUser(int userId) throws Exception { 438 CommonTestUtils.waitUntil("timed out (" + DEFAULT_TIMEOUT_SEC 439 + "s) waiting for current user to be " + userId 440 + " (it is " + getCurrentUserId() + ")", DEFAULT_TIMEOUT_SEC, 441 () -> (getCurrentUserId() == userId)); 442 } 443 444 /** 445 * Waits until the current user is not the system user. 446 */ waitUntilCurrentUserIsNotSystem(int timeoutSec)447 protected void waitUntilCurrentUserIsNotSystem(int timeoutSec) throws Exception { 448 CommonTestUtils.waitUntil("timed out (" + timeoutSec + "s) waiting for current user to NOT " 449 + "be the system user", timeoutSec, () -> getCurrentUserId() != SYSTEM_USER_ID); 450 } 451 452 /** 453 * Waits until {@code n} persistent (i.e., non-ephemeral) users are available. 454 */ waitUntilAtLeastNPersistentUsersAreAvailable(int timeoutSec, int n)455 protected void waitUntilAtLeastNPersistentUsersAreAvailable(int timeoutSec, int n) 456 throws Exception { 457 waitUntil(timeoutSec, () -> getAllPersistentUsers().size() >= n, "%d persistent users", n); 458 } 459 460 /** 461 * Waits until the given condition is reached. 462 */ waitUntil(long timeoutSeconds, BooleanSupplierWithThrow<Exception> predicate, String msgPattern, Object... msgArgs)463 protected void waitUntil(long timeoutSeconds, BooleanSupplierWithThrow<Exception> predicate, 464 String msgPattern, Object... msgArgs) throws Exception { 465 CommonTestUtils.waitUntil("timed out (" + timeoutSeconds + "s) waiting for " 466 + String.format(msgPattern, msgArgs), timeoutSeconds, predicate); 467 } 468 469 // TODO(b/230500604): refactor other CommonTestUtils.waitUntil() calls to use this one insteads 470 /** 471 * Waits until the given condition is reached, using the default timeout. 472 */ waitUntil(BooleanSupplierWithThrow<Exception> predicate, String msgPattern, Object... msgArgs)473 protected void waitUntil(BooleanSupplierWithThrow<Exception> predicate, 474 String msgPattern, Object... msgArgs) throws Exception { 475 waitUntil(DEFAULT_TIMEOUT_SEC, predicate, msgPattern, msgArgs); 476 } 477 478 /** 479 * Removes a user by user ID and update the list of users to be removed. 480 */ removeUser(int userId)481 protected boolean removeUser(int userId) throws Exception { 482 String result = executeCommand("cmd car_service remove-user %d", userId); 483 if (result.contains("STATUS_SUCCESSFUL")) { 484 return true; 485 } 486 return false; 487 } 488 489 /** 490 * Removes users whose name start with the given prefix. 491 */ removeUsers(String prefix)492 protected void removeUsers(String prefix) throws Exception { 493 Pattern pattern = Pattern.compile("^.*id=(\\d+), name=(" + prefix + ".*),.*$"); 494 String output = executeCommand("cmd user list --all -v"); 495 for (String line : output.split("\\n")) { 496 Matcher matcher = pattern.matcher(line); 497 if (!matcher.find()) continue; 498 499 int userId = Integer.parseInt(matcher.group(1)); 500 String name = matcher.group(2); 501 CLog.e("Removing user with %s prefix (id=%d, name='%s')", prefix, userId, name); 502 removeUser(userId); 503 } 504 } 505 506 /** 507 * Checks if an app is installed for a given user. 508 */ isAppInstalledForUser(String packageName, int userId)509 protected boolean isAppInstalledForUser(String packageName, int userId) 510 throws DeviceNotAvailableException { 511 return getDevice().isPackageInstalled(packageName, Integer.toString(userId)); 512 } 513 514 /** 515 * Fails the test if the app is installed for the given user. 516 */ assertAppInstalledForUser(String packageName, int userId)517 protected void assertAppInstalledForUser(String packageName, int userId) 518 throws DeviceNotAvailableException { 519 assertWithMessage("%s should BE installed for user %s", packageName, userId).that( 520 isAppInstalledForUser(packageName, userId)).isTrue(); 521 } 522 523 /** 524 * Fails the test if the app is NOT installed for the given user. 525 */ assertAppNotInstalledForUser(String packageName, int userId)526 protected void assertAppNotInstalledForUser(String packageName, int userId) 527 throws DeviceNotAvailableException { 528 assertWithMessage("%s should NOT be installed for user %s", packageName, userId).that( 529 isAppInstalledForUser(packageName, userId)).isFalse(); 530 } 531 532 /** 533 * Restarts the system server process. 534 * 535 * <p>Useful for cases where the test case changes system properties, as 536 * {@link ITestDevice#reboot()} would reset them. 537 */ restartSystemServer()538 protected void restartSystemServer() throws Exception { 539 long uptimeBefore = getSystemServerUptime(); 540 CLog.d("Uptime before restart: %d", uptimeBefore); 541 542 restartOrReboot(); 543 544 getDevice().waitForDeviceAvailable(); 545 546 // Also checks for uptime - it might be an overkill, but at least it will add more logs, 547 // which could help in case of issues 548 CommonTestUtils.waitUntil("timed out waiting until for new system server uptime", 549 SYSTEM_RESTART_TIMEOUT_SEC, () -> { 550 long uptimeAfter = getSystemServerUptime(); 551 CLog.d("Uptime after restart: %d", uptimeAfter); 552 return uptimeAfter != -1 && uptimeAfter != uptimeBefore; 553 }); 554 555 waitForCarServiceReady(); 556 } 557 restartOrReboot()558 private void restartOrReboot() throws DeviceNotAvailableException { 559 ITestDevice device = getDevice(); 560 561 if (device.isAdbRoot()) { 562 CLog.d("Restarting system server"); 563 device.executeShellCommand("stop"); 564 device.executeShellCommand("start"); 565 return; 566 } 567 568 CLog.d("Only root user can restart system server; rebooting instead"); 569 getDevice().reboot(); 570 } 571 572 /** 573 * Gets the system server uptime (or {@code -1} if not available). 574 */ getSystemServerUptime()575 protected long getSystemServerUptime() throws DeviceNotAvailableException { 576 return getDevice().getIntProperty("sys.system_server.start_uptime", -1); 577 } 578 579 /** 580 * Reboots the device. 581 */ reboot()582 protected void reboot() throws Exception { 583 CLog.d("Rebooting device"); 584 getDevice().reboot(); 585 } 586 587 /** 588 * Gets mapping of package and permissions granted for requested user id. 589 * 590 * @return Map<String, List<String>> where key is the package name and 591 * the value is list of permissions granted for this user. 592 */ getPackagesAndPermissionsForUser(int userId)593 protected Map<String, List<String>> getPackagesAndPermissionsForUser(int userId) 594 throws Exception { 595 CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver(); 596 getDevice().executeShellCommand("dumpsys package --proto", receiver); 597 598 PackageServiceDumpProto dump = PackageServiceDumpProto.parser() 599 .parseFrom(receiver.getOutput()); 600 601 CLog.v("Device has %d packages while getPackagesAndPermissions", dump.getPackagesCount()); 602 Map<String, List<String>> pkgMap = new HashMap<>(); 603 for (PackageProto pkg : dump.getPackagesList()) { 604 String pkgName = pkg.getName(); 605 for (UserPermissionsProto userPermissions : pkg.getUserPermissionsList()) { 606 if (userPermissions.getId() == userId) { 607 pkgMap.put(pkg.getName(), userPermissions.getGrantedPermissionsList()); 608 break; 609 } 610 } 611 } 612 return pkgMap; 613 } 614 615 /** 616 * Checks if the given package has a process running on the device. 617 */ isPackageRunning(String packageName)618 protected boolean isPackageRunning(String packageName) throws Exception { 619 return !executeCommand("pidof %s", packageName).isEmpty(); 620 } 621 622 /** 623 * Sleeps for the given amount of milliseconds. 624 */ sleep(long ms)625 protected void sleep(long ms) throws InterruptedException { 626 CLog.v("Sleeping for %dms", ms); 627 Thread.sleep(ms); 628 CLog.v("Woke up; Little Susie woke up!"); 629 } 630 631 // TODO(b/169341308): move to common infra code 632 private static final class RequiredFeatureRule implements TestRule { 633 634 private final ITestInformationReceiver mReceiver; 635 private final String mFeature; 636 RequiredFeatureRule(ITestInformationReceiver receiver, String feature)637 RequiredFeatureRule(ITestInformationReceiver receiver, String feature) { 638 mReceiver = receiver; 639 mFeature = feature; 640 } 641 642 @Override apply(Statement base, Description description)643 public Statement apply(Statement base, Description description) { 644 return new Statement() { 645 646 @Override 647 public void evaluate() throws Throwable { 648 boolean hasFeature = false; 649 try { 650 hasFeature = mReceiver.getTestInformation().getDevice() 651 .hasFeature(mFeature); 652 } catch (DeviceNotAvailableException e) { 653 CLog.e("Could not check if device has feature %s: %e", mFeature, e); 654 return; 655 } 656 657 if (!hasFeature) { 658 CLog.d("skipping %s#%s" 659 + " because device does not have feature '%s'", 660 description.getClassName(), description.getMethodName(), mFeature); 661 throw new AssumptionViolatedException("Device does not have feature '" 662 + mFeature + "'"); 663 } 664 base.evaluate(); 665 } 666 }; 667 } 668 669 @Override toString()670 public String toString() { 671 return "RequiredFeatureRule[" + mFeature + "]"; 672 } 673 } 674 675 /** 676 * Represents a user as returned by {@code cmd user list -v}. 677 */ 678 public static final class UserInfo { 679 public final int id; 680 public final String flags; 681 public final String name; 682 public final String otherState; 683 684 private UserInfo(Matcher matcher) { 685 id = Integer.parseInt(matcher.group(USER_PATTERN_GROUP_ID)); 686 flags = matcher.group(USER_PATTERN_GROUP_FLAGS); 687 name = matcher.group(USER_PATTERN_GROUP_NAME); 688 otherState = matcher.group(USER_PATTERN_GROUP_OTHER_STATE); 689 } 690 691 @Override 692 public String toString() { 693 return "[UserInfo: id=" + id + ", flags=" + flags + ", name=" + name 694 + ", otherState=" + otherState + "]"; 695 } 696 } 697 } 698