1 /* 2 * Copyright (C) 2024 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.app.cts; 18 19 import static android.Manifest.permission.CREATE_USERS; 20 import static android.car.CarOccupantZoneManager.INVALID_USER_ID; 21 import static android.car.cts.utils.ShellPermissionUtils.runWithShellPermissionIdentity; 22 23 import static com.google.common.truth.Truth.assertThat; 24 import static com.google.common.truth.Truth.assertWithMessage; 25 26 import static org.junit.Assume.assumeTrue; 27 28 import android.app.ActivityManager; 29 import android.car.Car; 30 import android.car.CarOccupantZoneManager; 31 import android.car.SyncResultCallback; 32 import android.car.test.PermissionsCheckerRule; 33 import android.car.test.PermissionsCheckerRule.EnsureHasPermission; 34 import android.car.user.CarUserManager; 35 import android.car.user.UserCreationRequest; 36 import android.car.user.UserCreationResult; 37 import android.content.ComponentName; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.pm.ResolveInfo; 41 import android.os.UserHandle; 42 import android.os.UserManager; 43 import android.view.Display; 44 45 import androidx.test.filters.SmallTest; 46 import androidx.test.platform.app.InstrumentationRegistry; 47 import androidx.test.uiautomator.By; 48 import androidx.test.uiautomator.BySelector; 49 import androidx.test.uiautomator.UiDevice; 50 import androidx.test.uiautomator.UiObject2; 51 import androidx.test.uiautomator.Until; 52 53 import com.android.compatibility.common.util.ShellUtils; 54 55 import org.junit.After; 56 import org.junit.Before; 57 import org.junit.Rule; 58 import org.junit.Test; 59 60 import java.util.ArrayList; 61 import java.util.List; 62 import java.util.concurrent.TimeUnit; 63 64 @SmallTest 65 public class MultiUserMultiDisplayWindowSecurityTest { 66 private static final long TIMEOUT_MS = 5_000L; 67 private static final long USER_CREATION_TIMEOUT_MS = 20_000L; 68 69 private static final String EXTRA_DISPLAY_ID_TO_SHOW_OVERLAY = 70 "android.car.app.cts.DISPLAY_ID_TO_SHOW_OVERLAY"; 71 private static final ComponentName COMPONENT_SDK22_OVERLAY_WINDOW_TEST_ACTIVITY = 72 ComponentName.createRelative("android.car.app.cts.overlaywindowappsdk22", 73 ".OverlayWindowTestActivitySdk22"); 74 private static final ComponentName COMPONENT_DEPRECATED_SDK_TEST_ACTIVITY = 75 ComponentName.createRelative("android.car.app.cts.deprecatedsdk", ".MainActivity"); 76 77 private static final String PREFIX_NEW_USER_NAME = "newTestUser"; 78 79 @Rule 80 public final PermissionsCheckerRule mPermissionsCheckerRule = new PermissionsCheckerRule(); 81 82 private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); 83 private final ActivityManager mActivityManager = mContext.getSystemService( 84 ActivityManager.class); 85 private final UserManager mUserManager = mContext.getSystemService(UserManager.class); 86 private final List<Integer> mUsersToRemove = new ArrayList<>(); 87 private CarUserManager mCarUserManager; 88 private CarOccupantZoneManager mCarOccupantZoneManager; 89 private UiDevice mDevice; 90 private List<Integer> mUnassignedDisplays; 91 92 @Before setUp()93 public void setUp() throws Exception { 94 assumeTrue("Skipping test: Device doesn't support visible background users", 95 mUserManager.isVisibleBackgroundUsersSupported()); 96 97 Car car = Car.createCar(mContext); 98 mCarUserManager = car.getCarManager(CarUserManager.class); 99 mCarOccupantZoneManager = car.getCarManager(CarOccupantZoneManager.class); 100 mUnassignedDisplays = getUnassignedDisplaysForUser(UserHandle.myUserId()); 101 assumeTrue("There should be at least one unassigned display.", 102 mUnassignedDisplays.size() > 0); 103 104 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 105 mDevice.wakeUp(); 106 } 107 108 @After tearDown()109 public void tearDown() throws Exception { 110 stopTestPackage(COMPONENT_DEPRECATED_SDK_TEST_ACTIVITY); 111 stopTestPackage(COMPONENT_SDK22_OVERLAY_WINDOW_TEST_ACTIVITY); 112 cleanUpTestUsers(); 113 } 114 115 @Test testDeprecatedSdkVersionDialog()116 public void testDeprecatedSdkVersionDialog() { 117 mContext.startActivity(createIntent(COMPONENT_DEPRECATED_SDK_TEST_ACTIVITY)); 118 119 String windowName = COMPONENT_DEPRECATED_SDK_TEST_ACTIVITY.getPackageName(); 120 BySelector appWarningDialogSelector = By.pkg("android").text(windowName); 121 assumeTrue(mDevice.wait(Until.hasObject(appWarningDialogSelector), TIMEOUT_MS)); 122 123 UiObject2 appWarningUiObject = mDevice.findObject(appWarningDialogSelector); 124 for (int unassignedDisplayId : mUnassignedDisplays) { 125 assertWithMessage("AppWarning dialog must not be displayed on the unassigned display.") 126 .that(appWarningUiObject.getDisplayId() == unassignedDisplayId) 127 .isFalse(); 128 } 129 130 // Go back to dismiss the app warning dialog. 131 mDevice.pressBack(); 132 } 133 134 @Test 135 @EnsureHasPermission({CREATE_USERS}) testShowOverlayWindowOnDisplayAssignedToDifferentUser()136 public void testShowOverlayWindowOnDisplayAssignedToDifferentUser() throws Exception { 137 // UserPicker has the flag to hide non-system overlay windows when it is visible. 138 // (@see WindowManager.LayoutParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) 139 // To ensure the test is performed normally, assign each display to a user, 140 // leading to the dismissal of UserPicker. 141 ensureAllDisplaysAreAssignedToUser(); 142 // Just pick the first one. 143 int displayAssignedToDifferentUser = mUnassignedDisplays.get(0); 144 Intent intent = createIntent(COMPONENT_SDK22_OVERLAY_WINDOW_TEST_ACTIVITY); 145 intent.putExtra(EXTRA_DISPLAY_ID_TO_SHOW_OVERLAY, displayAssignedToDifferentUser); 146 147 mContext.startActivity(intent); 148 String packageName = COMPONENT_SDK22_OVERLAY_WINDOW_TEST_ACTIVITY.getPackageName(); 149 // Wait for the app to appear 150 mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), TIMEOUT_MS); 151 152 BySelector overlayWindowSelector = By.text(packageName) 153 .displayId(displayAssignedToDifferentUser).depth(0); 154 assertWithMessage("Should not display the overlay window on a display assigned to a user" 155 + " other than user %s", UserHandle.myUserId()) 156 .that(mDevice.hasObject(overlayWindowSelector)) 157 .isFalse(); 158 } 159 getUnassignedDisplaysForUser(int userId)160 private List<Integer> getUnassignedDisplaysForUser(int userId) { 161 List<CarOccupantZoneManager.OccupantZoneInfo> zonelist = 162 mCarOccupantZoneManager.getAllOccupantZones(); 163 List<Integer> displayList = new ArrayList<>(); 164 for (CarOccupantZoneManager.OccupantZoneInfo zone : zonelist) { 165 int userForOccupantZone = mCarOccupantZoneManager.getUserForOccupant(zone); 166 if (userForOccupantZone == userId) { 167 // Skip the assigned zone. 168 continue; 169 } 170 Display display = mCarOccupantZoneManager.getDisplayForOccupant(zone, 171 CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 172 if (display != null) { 173 displayList.add(display.getDisplayId()); 174 } 175 } 176 return displayList; 177 } 178 ensureAllDisplaysAreAssignedToUser()179 private void ensureAllDisplaysAreAssignedToUser() throws Exception { 180 String passengerLauncherPkg = getPassengerLauncherPackageName(); 181 for (int displayId : mUnassignedDisplays) { 182 int userId = mCarOccupantZoneManager.getUserForDisplayId(displayId); 183 if (userId != INVALID_USER_ID) { 184 // A user is already assigned to the display 185 continue; 186 } 187 userId = createNewUser(displayId); 188 mUsersToRemove.add(userId); 189 // Skip the setup wizard and make sure the passenger home shows up. 190 setUserSetupComplete(userId); 191 startUserOnDisplay(userId, displayId); 192 waitForPassengerLauncherOnDisplay(passengerLauncherPkg, displayId); 193 } 194 } 195 createNewUser(int displayId)196 private int createNewUser(int displayId) throws Exception { 197 String userName = new StringBuilder(PREFIX_NEW_USER_NAME).append(displayId).toString(); 198 SyncResultCallback<UserCreationResult> userCreationResultCallback = 199 new SyncResultCallback<>(); 200 201 mCarUserManager.createUser(new UserCreationRequest.Builder().setName(userName).build(), 202 Runnable::run, userCreationResultCallback); 203 UserCreationResult result = userCreationResultCallback.get(USER_CREATION_TIMEOUT_MS, 204 TimeUnit.MILLISECONDS); 205 206 assertThat(result).isNotNull(); 207 assertThat(result.isSuccess()).isTrue(); 208 UserHandle user = result.getUser(); 209 assertThat(user).isNotNull(); 210 return user.getIdentifier(); 211 } 212 createIntent(ComponentName activity)213 private static Intent createIntent(ComponentName activity) { 214 Intent intent = new Intent(); 215 intent.setComponent(activity); 216 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 217 return intent; 218 } 219 setUserSetupComplete(int userId)220 private static void setUserSetupComplete(int userId) { 221 ShellUtils.runShellCommand("settings put --user %d secure user_setup_complete 1", userId); 222 assertWithMessage("User setup complete").that( 223 ShellUtils.runShellCommand("settings get --user %d secure user_setup_complete", 224 userId)).isEqualTo("1"); 225 } 226 deleteUserSetupComplete(int userId)227 private static void deleteUserSetupComplete(int userId) { 228 assertWithMessage("User setup complete setting deleted").that( 229 ShellUtils.runShellCommand("settings delete --user %d secure user_setup_complete", 230 userId)).contains("Deleted 1 rows"); 231 } 232 startUserOnDisplay(int userId, int displayId)233 private static void startUserOnDisplay(int userId, int displayId) { 234 assertWithMessage("User started").that( 235 ShellUtils.runShellCommand("am start-user -w --display %d %d", displayId, userId)) 236 .contains("Success: user started on display " + displayId); 237 } 238 waitForPassengerLauncherOnDisplay(String passengerLauncherPkg, int displayId)239 private void waitForPassengerLauncherOnDisplay(String passengerLauncherPkg, int displayId) { 240 mDevice.wait(Until.hasObject(By.pkg(passengerLauncherPkg).displayId(displayId).depth(0)), 241 TIMEOUT_MS); 242 } 243 cleanUpTestUsers()244 private void cleanUpTestUsers() { 245 if (mUsersToRemove.isEmpty()) { 246 return; 247 } 248 for (int userId : mUsersToRemove) { 249 deleteUserSetupComplete(userId); 250 stopUser(userId); 251 removeUser(userId); 252 } 253 mUsersToRemove.clear(); 254 } 255 stopUser(int userId)256 private static void stopUser(int userId) { 257 ShellUtils.runShellCommand("am stop-user -w -f %d", userId); 258 } 259 removeUser(int userId)260 private static void removeUser(int userId) { 261 assertWithMessage("User removed").that( 262 ShellUtils.runShellCommand("pm remove-user --wait %d", 263 userId)).contains("Success: removed user"); 264 } 265 stopTestPackage(ComponentName activityName)266 private void stopTestPackage(ComponentName activityName) { 267 runWithShellPermissionIdentity(() -> mActivityManager.forceStopPackage( 268 activityName.getPackageName())); 269 } 270 getPassengerLauncherPackageName()271 private String getPassengerLauncherPackageName() { 272 Intent queryIntent = new Intent(Intent.ACTION_MAIN); 273 queryIntent.addCategory(Intent.CATEGORY_SECONDARY_HOME); 274 List<ResolveInfo> resolveInfo = mContext.getPackageManager() 275 .queryIntentActivities(queryIntent, /* flags= */ 0); 276 assertThat(resolveInfo).isNotNull(); 277 assertThat(resolveInfo).isNotEmpty(); 278 return resolveInfo.get(0).activityInfo.packageName; 279 } 280 } 281