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 user switch to {@code userId} completes. 446 * 447 * <p>There is asynchronous part of a user switch after the core user switch. This method 448 * ensures a user switch to {@code userId} completes by {@code CarService}. 449 */ waitForUserSwitchCompleted(int userId)450 protected void waitForUserSwitchCompleted(int userId) throws Exception { 451 waitUntil(() -> getLastActiveUserId() == userId, 452 "the last active userId to be %d, but it is %d", userId, getLastActiveUserId()); 453 } 454 455 /** 456 * Gets the global settings value of android.car.LAST_ACTIVE_USER_ID, which is set by 457 * {@code CarUserService} when a user switch completes. 458 * 459 * @return userId of the current active user. 460 */ getLastActiveUserId()461 protected int getLastActiveUserId() throws Exception { 462 return executeAndParseCommand(output -> Integer.parseInt(output.trim()), 463 "cmd settings get global android.car.LAST_ACTIVE_USER_ID"); 464 } 465 466 /** 467 * Waits until the current user is not the system user. 468 */ waitUntilCurrentUserIsNotSystem(int timeoutSec)469 protected void waitUntilCurrentUserIsNotSystem(int timeoutSec) throws Exception { 470 CommonTestUtils.waitUntil("timed out (" + timeoutSec + "s) waiting for current user to NOT " 471 + "be the system user", timeoutSec, () -> getCurrentUserId() != SYSTEM_USER_ID); 472 } 473 474 /** 475 * Waits until {@code n} persistent (i.e., non-ephemeral) users are available. 476 */ waitUntilAtLeastNPersistentUsersAreAvailable(int timeoutSec, int n)477 protected void waitUntilAtLeastNPersistentUsersAreAvailable(int timeoutSec, int n) 478 throws Exception { 479 waitUntil(timeoutSec, () -> getAllPersistentUsers().size() >= n, "%d persistent users", n); 480 } 481 482 /** 483 * Waits until the given condition is reached. 484 */ waitUntil(long timeoutSeconds, BooleanSupplierWithThrow<Exception> predicate, String msgPattern, Object... msgArgs)485 protected void waitUntil(long timeoutSeconds, BooleanSupplierWithThrow<Exception> predicate, 486 String msgPattern, Object... msgArgs) throws Exception { 487 CommonTestUtils.waitUntil("timed out (" + timeoutSeconds + "s) waiting for " 488 + String.format(msgPattern, msgArgs), timeoutSeconds, predicate); 489 } 490 491 // TODO(b/230500604): refactor other CommonTestUtils.waitUntil() calls to use this one insteads 492 /** 493 * Waits until the given condition is reached, using the default timeout. 494 */ waitUntil(BooleanSupplierWithThrow<Exception> predicate, String msgPattern, Object... msgArgs)495 protected void waitUntil(BooleanSupplierWithThrow<Exception> predicate, 496 String msgPattern, Object... msgArgs) throws Exception { 497 waitUntil(DEFAULT_TIMEOUT_SEC, predicate, msgPattern, msgArgs); 498 } 499 500 /** 501 * Removes a user by user ID and update the list of users to be removed. 502 */ removeUser(int userId)503 protected boolean removeUser(int userId) throws Exception { 504 String result = executeCommand("cmd car_service remove-user %d", userId); 505 if (result.contains("STATUS_SUCCESSFUL")) { 506 return true; 507 } 508 return false; 509 } 510 511 /** 512 * Removes users whose name start with the given prefix. 513 */ removeUsers(String prefix)514 protected void removeUsers(String prefix) throws Exception { 515 Pattern pattern = Pattern.compile("^.*id=(\\d+), name=(" + prefix + ".*),.*$"); 516 String output = executeCommand("cmd user list --all -v"); 517 for (String line : output.split("\\n")) { 518 Matcher matcher = pattern.matcher(line); 519 if (!matcher.find()) continue; 520 521 int userId = Integer.parseInt(matcher.group(1)); 522 String name = matcher.group(2); 523 CLog.e("Removing user with %s prefix (id=%d, name='%s')", prefix, userId, name); 524 removeUser(userId); 525 } 526 } 527 528 /** 529 * Checks if an app is installed for a given user. 530 */ isAppInstalledForUser(String packageName, int userId)531 protected boolean isAppInstalledForUser(String packageName, int userId) 532 throws DeviceNotAvailableException { 533 return getDevice().isPackageInstalled(packageName, Integer.toString(userId)); 534 } 535 536 /** 537 * Fails the test if the app is installed for the given user. 538 */ assertAppInstalledForUser(String packageName, int userId)539 protected void assertAppInstalledForUser(String packageName, int userId) 540 throws DeviceNotAvailableException { 541 assertWithMessage("%s should BE installed for user %s", packageName, userId).that( 542 isAppInstalledForUser(packageName, userId)).isTrue(); 543 } 544 545 /** 546 * Fails the test if the app is NOT installed for the given user. 547 */ assertAppNotInstalledForUser(String packageName, int userId)548 protected void assertAppNotInstalledForUser(String packageName, int userId) 549 throws DeviceNotAvailableException { 550 assertWithMessage("%s should NOT be installed for user %s", packageName, userId).that( 551 isAppInstalledForUser(packageName, userId)).isFalse(); 552 } 553 554 /** 555 * Restarts the system server process. 556 * 557 * <p>Useful for cases where the test case changes system properties, as 558 * {@link ITestDevice#reboot()} would reset them. 559 */ restartSystemServer()560 protected void restartSystemServer() throws Exception { 561 restartOrReboot(); 562 563 getDevice().waitForDeviceAvailable(); 564 waitForCarServiceReady(); 565 } 566 restartOrReboot()567 private void restartOrReboot() throws DeviceNotAvailableException { 568 ITestDevice device = getDevice(); 569 570 if (device.isAdbRoot()) { 571 CLog.d("Restarting system server"); 572 device.executeShellCommand("stop"); 573 device.executeShellCommand("start"); 574 return; 575 } 576 577 CLog.d("Only root user can restart system server; rebooting instead"); 578 getDevice().reboot(); 579 } 580 581 /** 582 * Reboots the device. 583 */ reboot()584 protected void reboot() throws Exception { 585 CLog.d("Rebooting device"); 586 getDevice().reboot(); 587 } 588 589 /** 590 * Gets mapping of package and permissions granted for requested user id. 591 * 592 * @return Map<String, List<String>> where key is the package name and 593 * the value is list of permissions granted for this user. 594 */ getPackagesAndPermissionsForUser(int userId)595 protected Map<String, List<String>> getPackagesAndPermissionsForUser(int userId) 596 throws Exception { 597 CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver(); 598 getDevice().executeShellCommand("dumpsys package --proto", receiver); 599 600 PackageServiceDumpProto dump = PackageServiceDumpProto.parser() 601 .parseFrom(receiver.getOutput()); 602 603 CLog.v("Device has %d packages while getPackagesAndPermissions", dump.getPackagesCount()); 604 Map<String, List<String>> pkgMap = new HashMap<>(); 605 for (PackageProto pkg : dump.getPackagesList()) { 606 String pkgName = pkg.getName(); 607 for (UserPermissionsProto userPermissions : pkg.getUserPermissionsList()) { 608 if (userPermissions.getId() == userId) { 609 pkgMap.put(pkg.getName(), userPermissions.getGrantedPermissionsList()); 610 break; 611 } 612 } 613 } 614 return pkgMap; 615 } 616 617 /** 618 * Checks if the given package has a process running on the device. 619 */ isPackageRunning(String packageName)620 protected boolean isPackageRunning(String packageName) throws Exception { 621 return !executeCommand("pidof %s", packageName).isEmpty(); 622 } 623 624 /** 625 * Sleeps for the given amount of milliseconds. 626 */ sleep(long ms)627 protected void sleep(long ms) throws InterruptedException { 628 CLog.v("Sleeping for %dms", ms); 629 Thread.sleep(ms); 630 CLog.v("Woke up; Little Susie woke up!"); 631 } 632 633 // TODO(b/169341308): move to common infra code 634 private static final class RequiredFeatureRule implements TestRule { 635 636 private final ITestInformationReceiver mReceiver; 637 private final String mFeature; 638 RequiredFeatureRule(ITestInformationReceiver receiver, String feature)639 RequiredFeatureRule(ITestInformationReceiver receiver, String feature) { 640 mReceiver = receiver; 641 mFeature = feature; 642 } 643 644 @Override apply(Statement base, Description description)645 public Statement apply(Statement base, Description description) { 646 return new Statement() { 647 648 @Override 649 public void evaluate() throws Throwable { 650 boolean hasFeature = false; 651 try { 652 hasFeature = mReceiver.getTestInformation().getDevice() 653 .hasFeature(mFeature); 654 } catch (DeviceNotAvailableException e) { 655 CLog.e("Could not check if device has feature %s: %e", mFeature, e); 656 return; 657 } 658 659 if (!hasFeature) { 660 CLog.d("skipping %s#%s" 661 + " because device does not have feature '%s'", 662 description.getClassName(), description.getMethodName(), mFeature); 663 throw new AssumptionViolatedException("Device does not have feature '" 664 + mFeature + "'"); 665 } 666 base.evaluate(); 667 } 668 }; 669 } 670 671 @Override toString()672 public String toString() { 673 return "RequiredFeatureRule[" + mFeature + "]"; 674 } 675 } 676 677 /** 678 * Represents a user as returned by {@code cmd user list -v}. 679 */ 680 public static final class UserInfo { 681 public final int id; 682 public final String flags; 683 public final String name; 684 public final String otherState; 685 686 private UserInfo(Matcher matcher) { 687 id = Integer.parseInt(matcher.group(USER_PATTERN_GROUP_ID)); 688 flags = matcher.group(USER_PATTERN_GROUP_FLAGS); 689 name = matcher.group(USER_PATTERN_GROUP_NAME); 690 otherState = matcher.group(USER_PATTERN_GROUP_OTHER_STATE); 691 } 692 693 @Override 694 public String toString() { 695 return "[UserInfo: id=" + id + ", flags=" + flags + ", name=" + name 696 + ", otherState=" + otherState + "]"; 697 } 698 } 699 } 700