1 /* 2 * Copyright (C) 2023 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 package com.android.tradefed.targetprep; 17 18 import static com.android.tradefed.targetprep.UserHelper.RUN_TESTS_AS_USER_KEY; 19 20 import com.android.annotations.VisibleForTesting; 21 import com.android.tradefed.config.Option; 22 import com.android.tradefed.config.OptionClass; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.invoker.TestInformation; 26 import com.android.tradefed.log.LogUtil.CLog; 27 28 import java.util.Iterator; 29 import java.util.Set; 30 31 import javax.annotation.Nullable; 32 33 /** Target preparer for running tests in a user that is started in the visible in the background. */ 34 @OptionClass(alias = "visible-background-user-preparer") 35 public class VisibleBackgroundUserPreparer extends BaseTargetPreparer { 36 37 @VisibleForTesting public static final int INVALID_DISPLAY = -1; // same as android.view.Display 38 @VisibleForTesting public static final int DEFAULT_DISPLAY = 0; // same as android.view.Display 39 40 /** 41 * Property used to indicate whether to install test apk for all users. 42 * TODO: b/367468564 - Remove this property once we have fixed the tests so that 43 * installation for the system user is no longer required 44 */ 45 static final String INSTALL_TEST_APK_FOR_ALL_USERS = "INSTALL_TEST_APK_FOR_ALL_USERS"; 46 47 @Option( 48 name = "reuse-test-user", 49 description = 50 "Whether or not to reuse already created tradefed test user, or remove them " 51 + " and re-create them between module runs.") 52 private boolean mReuseTestUser; 53 54 @Option(name = "display-id", description = "Which display to start the user visible on") 55 private int mDisplayId = INVALID_DISPLAY; 56 57 private Integer mUserId; 58 private boolean mUserAlreadyVisible; 59 60 @Override setUp(TestInformation testInfo)61 public void setUp(TestInformation testInfo) 62 throws TargetSetupError, BuildError, DeviceNotAvailableException { 63 ITestDevice device = testInfo.getDevice(); 64 if (!device.isVisibleBackgroundUsersSupported()) { 65 throw new TargetSetupError("feature not supported", device.getDeviceDescriptor()); 66 } 67 CLog.i("setUp(): mReuseTestUser=%b, mDisplayId=%d", mReuseTestUser, mDisplayId); 68 69 mUserId = UserHelper.createUser(device, mReuseTestUser); 70 71 startUserVisibleOnBackground(testInfo, device, mUserId); 72 73 device.waitForDeviceAvailable(); 74 device.postBootSetup(); 75 } 76 setDisplayId(int displayId)77 public void setDisplayId(int displayId) { 78 if (displayId == INVALID_DISPLAY) { 79 throw new IllegalArgumentException( 80 "Cannot set it as INVALID_DISPLAY (" + INVALID_DISPLAY + ")"); 81 } 82 mDisplayId = displayId; 83 } 84 85 @VisibleForTesting getDisplayId()86 public @Nullable Integer getDisplayId() { 87 return mDisplayId; 88 } 89 startUserVisibleOnBackground( TestInformation testInfo, ITestDevice device, int userId)90 private void startUserVisibleOnBackground( 91 TestInformation testInfo, ITestDevice device, int userId) 92 throws TargetSetupError, DeviceNotAvailableException { 93 int displayId = mDisplayId; 94 if (displayId == INVALID_DISPLAY) { 95 // If display is not explicitly set (by option / setter), get the first available one 96 Set<Integer> displays = device.listDisplayIdsForStartingVisibleBackgroundUsers(); 97 CLog.d("Displays: %s", displays); 98 if (displays.isEmpty()) { 99 throw new TargetSetupError( 100 String.format("No display available to start to user '%d'", userId), 101 device.getDeviceDescriptor()); 102 } 103 Iterator<Integer> iterator = displays.iterator(); 104 displayId = iterator.next(); 105 if (displayId == DEFAULT_DISPLAY 106 && device.isVisibleBackgroundUsersOnDefaultDisplaySupported()) { 107 // Ignore default display - it's a special case where the display id should have 108 // been passed directly 109 CLog.d( 110 "Ignoring DEFAULT_DISPLAY because device supports background users on" 111 + " default display"); 112 if (!iterator.hasNext()) { 113 throw new TargetSetupError( 114 String.format( 115 "Only DEFAULT_DISPLAY available to start to user '%d'", userId), 116 device.getDeviceDescriptor()); 117 } 118 displayId = iterator.next(); 119 } 120 } 121 122 mUserAlreadyVisible = device.isUserVisibleOnDisplay(userId, displayId); 123 if (mUserAlreadyVisible) { 124 CLog.d( 125 "startUserVisibleOnBackground(): user %d already visible on display %d", 126 userId, displayId); 127 } else { 128 CLog.d( 129 "startUserVisibleOnBackground(): starting user %d visible on display %d", 130 userId, displayId); 131 132 if (!device.startVisibleBackgroundUser(userId, displayId, /* waitFlag= */ true)) { 133 throw new TargetSetupError( 134 String.format( 135 "Failed to start to user '%s' on display %d", mUserId, displayId), 136 device.getDeviceDescriptor()); 137 } 138 } 139 140 CLog.i("Setting test property %s=%d", RUN_TESTS_AS_USER_KEY, mUserId); 141 testInfo.properties().put(RUN_TESTS_AS_USER_KEY, Integer.toString(mUserId)); 142 // TODO: b/367468564 - Remove this property once we have fixed the tests so that 143 // installation for the system user is no longer required 144 testInfo.properties().put(INSTALL_TEST_APK_FOR_ALL_USERS, "true"); 145 } 146 147 @Override tearDown(TestInformation testInfo, Throwable e)148 public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException { 149 if (mUserId == null) { 150 CLog.d("Skipping teardown because no user was created or reused"); 151 return; 152 } 153 // Clean property at teardown 154 testInfo.properties().remove(RUN_TESTS_AS_USER_KEY); 155 if (e instanceof DeviceNotAvailableException) { 156 CLog.d("Skipping teardown due to dnae: %s", e.getMessage()); 157 return; 158 } 159 ITestDevice device = testInfo.getDevice(); 160 161 stopTestUser(device); 162 163 if (!mReuseTestUser) { 164 device.removeUser(mUserId); 165 } 166 } 167 stopTestUser(ITestDevice device)168 private void stopTestUser(ITestDevice device) throws DeviceNotAvailableException { 169 if (mUserAlreadyVisible) { 170 CLog.d("stopTestUser(): user %d was already visible on start", mUserId); 171 return; 172 } 173 CLog.d("stopTestUser(): stopping user %d ", mUserId); 174 if (!device.stopUser(mUserId, /* waitFlag= */ true, /* forceFlag= */ true)) { 175 CLog.e("Failed to stop user '%d'", mUserId); 176 } 177 } 178 } 179