diff options
| author | 2022-09-28 05:12:07 +0000 | |
|---|---|---|
| committer | 2022-09-28 05:12:07 +0000 | |
| commit | 29a55b03a1320354222a685dfbcce7f339e73bcd (patch) | |
| tree | 377fe939a87b3525327308b80475759dfaeda409 | |
| parent | 5897b777b80fe6df83107e64ecd0e7011da92968 (diff) | |
| parent | 23fbfb82b4a3e8609c4e8f2d61d33eab5ef6d5c0 (diff) | |
Merge "Changes AM.startUserInBgOnSecondaryDisplay() to check if display is valid."
7 files changed, 433 insertions, 9 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index fb2c9e43497f..e08cef92fc6c 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -121,6 +121,7 @@ package android.app { public class ActivityManager { method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener); method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int[] getSecondaryDisplayIdsForStartingBackgroundUsers(); method public long getTotalRam(); method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessCapabilities(int); method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessState(int); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index da2a76a7b319..576b572dcc9a 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4387,13 +4387,15 @@ public class ActivityManager { * a profile, the {@link #getCurrentUser()}, the {@link UserHandle#SYSTEM system user}, or * does not exist. * - * @param displayId id of the display, it must exist. + * @param displayId id of the display. * * @return whether the operation succeeded. Notice that if the user was already started in such * display before, it will return {@code false}. * * @throws UnsupportedOperationException if the device does not support background users on * secondary displays. + * @throws IllegalArgumentException if the display doesn't exist or is not a valid display to + * start secondary users on. * * @hide */ @@ -4414,6 +4416,24 @@ public class ActivityManager { } /** + * Gets the id of displays that can be used by + * {@link #startUserInBackgroundOnSecondaryDisplay(int, int)}. + * + * @hide + */ + @TestApi + @Nullable + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}) + public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() { + try { + return getService().getSecondaryDisplayIdsForStartingBackgroundUsers(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Gets the message that is shown when a user is switched from. * * @hide diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 980b79ba01e8..b4abd3c69482 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -770,4 +770,10 @@ interface IActivityManager { "@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional = true)") boolean startUserInBackgroundOnSecondaryDisplay(int userid, int displayId); + /** + * Gets the ids of displays that can be used on {@link #startUserInBackgroundOnSecondaryDisplay(int userId, int displayId)}. + * + * <p>Typically used only by automotive builds when the vehicle has multiple displays. + */ + @nullable int[] getSecondaryDisplayIdsForStartingBackgroundUsers(); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 50b47a6b22f1..7d85c1333b1b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.FILTER_EVENTS; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; +import static android.Manifest.permission.MANAGE_USERS; import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND; import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE; @@ -258,6 +259,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Rect; +import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.media.audiofx.AudioEffect; import android.net.ConnectivityManager; @@ -332,6 +334,7 @@ import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoUtils; +import android.view.Display; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -2366,6 +2369,7 @@ public class ActivityManagerService extends IActivityManager.Stub mUiHandler = injector.getUiHandler(null /* service */); mUidObserverController = new UidObserverController(mUiHandler); mUserController = new UserController(this); + mInjector.mUserController = mUserController; mPendingIntentController = new PendingIntentController(handlerThread.getLooper(), mUserController, mConstants); mAppRestrictionController = new AppRestrictionController(mContext, this); @@ -2480,6 +2484,7 @@ public class ActivityManagerService extends IActivityManager.Stub mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); mUserController = new UserController(this); + mInjector.mUserController = mUserController; mPendingIntentController = new PendingIntentController( mHandlerThread.getLooper(), mUserController, mConstants); @@ -6057,6 +6062,24 @@ public class ActivityManagerService extends IActivityManager.Stub * This can be called with or without the global lock held. */ @PermissionMethod + private void enforceCallingHasAtLeastOnePermission(String func, String... permissions) { + for (String permission : permissions) { + if (checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) { + return; + } + } + + String msg = "Permission Denial: " + func + " from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires one of " + Arrays.toString(permissions); + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + + /** + * This can be called with or without the global lock held. + */ void enforcePermission(String permission, int pid, int uid, String func) { if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) { return; @@ -16333,8 +16356,34 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public boolean startUserInBackgroundOnSecondaryDisplay(int userId, int displayId) { + int[] displayIds = getSecondaryDisplayIdsForStartingBackgroundUsers(); + boolean validDisplay = false; + if (displayIds != null) { + for (int i = 0; i < displayIds.length; i++) { + if (displayId == displayIds[i]) { + validDisplay = true; + break; + } + } + } + if (!validDisplay) { + throw new IllegalArgumentException("Invalid display (" + displayId + ") to start user. " + + "Valid options are: " + Arrays.toString(displayIds)); + } + + if (DEBUG_MU) { + Slogf.d(TAG_MU, "Calling startUserOnSecondaryDisplay(%d, %d) using injector %s", userId, + displayId, mInjector); + } // Permission check done inside UserController. - return mUserController.startUserOnSecondaryDisplay(userId, displayId); + return mInjector.startUserOnSecondaryDisplay(userId, displayId); + } + + @Override + public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() { + enforceCallingHasAtLeastOnePermission("getSecondaryDisplayIdsForStartingBackgroundUsers()", + MANAGE_USERS, INTERACT_ACROSS_USERS); + return mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers(); } /** @@ -18343,8 +18392,10 @@ public class ActivityManagerService extends IActivityManager.Stub @VisibleForTesting public static class Injector { + private final Context mContext; private NetworkManagementInternal mNmi; - private Context mContext; + + private UserController mUserController; public Injector(Context context) { mContext = context; @@ -18370,6 +18421,103 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * Called by {@code AMS.getSecondaryDisplayIdsForStartingBackgroundUsers()}. + */ + // NOTE: ideally Injector should have no complex logic, but if this logic was moved to AMS, + // it could not be tested with the existing ActivityManagerServiceTest (as DisplayManager, + // DisplayInfo, etc... are final and UserManager.isUsersOnSecondaryDisplaysEnabled is + // static). + // So, the logic was added here, and tested on ActivityManagerServiceInjectorTest (which + // was added on FrameworksMockingServicesTests and hence uses Extended Mockito to mock + // final and static stuff) + @Nullable + public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() { + if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) { + Slogf.w(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): not supported"); + return null; + } + + // NOTE: DisplayManagerInternal doesn't have a method to list all displays + DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); + + Display[] allDisplays = displayManager.getDisplays(); + + // allDisplays should contain at least Display.DEFAULT_DISPLAY, but it's better to + // double check, just in case... + if (allDisplays == null || allDisplays.length == 0) { + Slogf.wtf(TAG, "displayManager (%s) returned no displays", displayManager); + return null; + } + boolean hasDefaultDisplay = false; + for (Display display : allDisplays) { + if (display.getDisplayId() == Display.DEFAULT_DISPLAY) { + hasDefaultDisplay = true; + break; + } + } + if (!hasDefaultDisplay) { + Slogf.wtf(TAG, "displayManager (%s) has %d displays (%s), but none has id " + + "DEFAULT_DISPLAY (%d)", displayManager, allDisplays.length, + Arrays.toString(allDisplays), Display.DEFAULT_DISPLAY); + return null; + } + + // Starts with all displays but DEFAULT_DISPLAY + int[] displayIds = new int[allDisplays.length - 1]; + + // TODO(b/247592632): check for other properties like isSecure or proper display type + int numberValidDisplays = 0; + for (Display display : allDisplays) { + int displayId = display.getDisplayId(); + if (display.isValid() && displayId != Display.DEFAULT_DISPLAY) { + displayIds[numberValidDisplays++] = displayId; + } + } + + if (numberValidDisplays == 0) { + // TODO(b/247580038): remove this workaround once a virtual display on Car's + // KitchenSink (or other app) can be used while running CTS tests on devices that + // don't have a real display. + // STOPSHIP: if not removed, it should at least be unit tested + String testingProp = "fw.secondary_display_for_starting_users_for_testing_purposes"; + int displayId = SystemProperties.getInt(testingProp, Display.DEFAULT_DISPLAY); + if (displayId != Display.DEFAULT_DISPLAY && displayId > 0) { + Slogf.w(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): no valid " + + "display found, but returning %d as set by property %s", displayId, + testingProp); + return new int[] { displayId }; + } + Slogf.e(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): no valid display" + + " on %s", Arrays.toString(allDisplays)); + return null; + } + + if (numberValidDisplays != displayIds.length) { + int[] validDisplayIds = new int[numberValidDisplays]; + System.arraycopy(displayIds, 0, validDisplayIds, 0, numberValidDisplays); + if (DEBUG_MU) { + Slogf.d(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): returning " + + "only valid displays (%d instead of %d): %s", numberValidDisplays, + displayIds.length, Arrays.toString(validDisplayIds)); + } + return validDisplayIds; + } + + if (DEBUG_MU) { + Slogf.d(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): returning all " + + "(but DEFAULT_DISPLAY) displays : %s", Arrays.toString(displayIds)); + } + return displayIds; + } + + /** + * Called by {@code AMS.startUserOnSecondaryDisplay()}. + */ + public boolean startUserOnSecondaryDisplay(int userId, int displayId) { + return mUserController.startUserOnSecondaryDisplay(userId, displayId); + } + + /** * Return the process list instance */ public ProcessList getProcessList(ActivityManagerService service) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index b4f6e35b3df3..10e2aae9a8d1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -367,6 +367,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runGetCurrentForegroundProcess(pw, mInternal, mTaskInterface); case "reset-dropbox-rate-limiter": return runResetDropboxRateLimiter(); + case "list-secondary-displays-for-starting-users": + return runListSecondaryDisplaysForStartingUsers(pw); default: return handleDefaultCommands(cmd); } @@ -2068,6 +2070,10 @@ final class ActivityManagerShellCommand extends ShellCommand { success = mInterface.startUserInBackgroundWithListener(userId, waiter); displaySuffix = ""; } else { + if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) { + pw.println("Not supported"); + return -1; + } success = mInterface.startUserInBackgroundOnSecondaryDisplay(userId, displayId); displaySuffix = " on display " + displayId; } @@ -3591,6 +3597,14 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } + int runListSecondaryDisplaysForStartingUsers(PrintWriter pw) throws RemoteException { + int[] displayIds = mInterface.getSecondaryDisplayIdsForStartingBackgroundUsers(); + pw.println(displayIds == null || displayIds.length == 0 + ? "none" + : Arrays.toString(displayIds)); + return 0; + } + private Resources getResources(PrintWriter pw) throws RemoteException { // system resources does not contain all the device configuration, construct it manually. Configuration config = mInterface.getConfiguration(); @@ -3951,6 +3965,9 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" Set an app's background restriction level which in turn map to a app standby bucket."); pw.println(" get-bg-restriction-level [--user <USER_ID>] <PACKAGE>"); pw.println(" Get an app's background restriction level."); + pw.println(" list-secondary-displays-for-starting-users"); + pw.println(" Lists the id of displays that can be used to start users on " + + "background."); pw.println(); Intent.printIntentArgsHelp(pw, ""); } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java new file mode 100644 index 000000000000..09df96f4b917 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static android.view.Display.DEFAULT_DISPLAY; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.os.UserManager; +import android.util.Log; +import android.view.Display; + +import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; +import com.android.server.ExtendedMockitoTestCase; +import com.android.server.am.ActivityManagerService.Injector; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import java.util.Arrays; + +/** + * Run as {@code atest + * FrameworksMockingServicesTests:com.android.server.am.ActivityManagerServiceInjectorTest} + */ +public final class ActivityManagerServiceInjectorTest extends ExtendedMockitoTestCase { + + private static final String TAG = ActivityManagerServiceInjectorTest.class.getSimpleName(); + + private final Display mDefaultDisplay = validDisplay(DEFAULT_DISPLAY); + + @Mock private Context mContext; + @Mock private DisplayManager mDisplayManager; + + private Injector mInjector; + + @Before + public void setFixture() { + mInjector = new Injector(mContext); + + when(mContext.getSystemService(DisplayManager.class)).thenReturn(mDisplayManager); + } + + @Override + protected void initializeSession(StaticMockitoSessionBuilder builder) { + builder.spyStatic(UserManager.class); + } + + @Test + public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_notSupported() { + mockUmIsUsersOnSecondaryDisplaysEnabled(false); + + int [] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers(); + + assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()") + .that(displayIds).isNull(); + } + + @Test + public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_noDisplaysAtAll() { + mockUmIsUsersOnSecondaryDisplaysEnabled(true); + mockGetDisplays(); + + int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers(); + + assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()") + .that(displayIds).isNull(); + } + + @Test + public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_defaultDisplayOnly() { + mockUmIsUsersOnSecondaryDisplaysEnabled(true); + mockGetDisplays(mDefaultDisplay); + + int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers(); + + assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()") + .that(displayIds).isNull(); + } + + @Test + public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_noDefaultDisplay() { + mockUmIsUsersOnSecondaryDisplaysEnabled(true); + mockGetDisplays(validDisplay(42)); + + int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers(); + + assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()") + .that(displayIds).isNull(); + } + + @Test + public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_mixed() { + mockUmIsUsersOnSecondaryDisplaysEnabled(true); + mockGetDisplays(mDefaultDisplay, validDisplay(42), invalidDisplay(108)); + + int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers(); + + assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()") + .that(displayIds).isNotNull(); + assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()") + .that(displayIds).asList().containsExactly(42); + } + + // Extra test to make sure the array is properly copied... + @Test + public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_mixed_invalidFirst() { + mockUmIsUsersOnSecondaryDisplaysEnabled(true); + mockGetDisplays(invalidDisplay(108), mDefaultDisplay, validDisplay(42)); + + int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers(); + + assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()") + .that(displayIds).asList().containsExactly(42); + } + + private Display validDisplay(int displayId) { + return mockDisplay(displayId, /* valid= */ true); + } + + private Display invalidDisplay(int displayId) { + return mockDisplay(displayId, /* valid= */ false); + } + + private Display mockDisplay(int displayId, boolean valid) { + Display display = mock(Display.class); + + when(display.getDisplayId()).thenReturn(displayId); + when(display.isValid()).thenReturn(valid); + + return display; + } + + private void mockGetDisplays(Display... displays) { + Log.d(TAG, "mockGetDisplays(): " + Arrays.toString(displays)); + when(mDisplayManager.getDisplays()).thenReturn(displays); + } + + private void mockUmIsUsersOnSecondaryDisplaysEnabled(boolean enabled) { + Log.d(TAG, "Mocking UserManager.isUsersOnSecondaryDisplaysEnabled() to return " + enabled); + doReturn(enabled).when(() -> UserManager.isUsersOnSecondaryDisplaysEnabled()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java index 3c1710250803..0d6f3267f41d 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -37,11 +37,14 @@ import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK; import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE; import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -62,7 +65,6 @@ import android.app.SyncNotedAppOp; import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.Handler; import android.os.HandlerThread; @@ -74,6 +76,8 @@ import android.os.RemoteException; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.IntArray; +import android.util.Log; +import android.util.Pair; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; @@ -116,6 +120,7 @@ public class ActivityManagerServiceTest { private static final String TAG = ActivityManagerServiceTest.class.getSimpleName(); private static final int TEST_UID = 11111; + private static final int USER_ID = 666; private static final long TEST_PROC_STATE_SEQ1 = 555; private static final long TEST_PROC_STATE_SEQ2 = 556; @@ -147,8 +152,8 @@ public class ActivityManagerServiceTest { @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule(); private Context mContext = getInstrumentation().getTargetContext(); + @Mock private AppOpsService mAppOpsService; - @Mock private PackageManager mPackageManager; private TestInjector mInjector; private ActivityManagerService mAms; @@ -828,6 +833,57 @@ public class ActivityManagerServiceTest { true); // expectWait } + @Test + public void testGetSecondaryDisplayIdsForStartingBackgroundUsers() { + mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{4, 8, 15, 16, 23, 42}; + + int [] displayIds = mAms.getSecondaryDisplayIdsForStartingBackgroundUsers(); + + assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()") + .that(displayIds).asList().containsExactly(4, 8, 15, 16, 23, 42); + } + + @Test + public void testStartUserInBackgroundOnSecondaryDisplay_invalidDisplay() { + mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{4, 8, 15, 16, 23, 42}; + + assertThrows(IllegalArgumentException.class, + () -> mAms.startUserInBackgroundOnSecondaryDisplay(USER_ID, 666)); + + assertWithMessage("UserController.startUserOnSecondaryDisplay() calls") + .that(mInjector.usersStartedOnSecondaryDisplays).isEmpty(); + } + + @Test + public void testStartUserInBackgroundOnSecondaryDisplay_validDisplay_failed() { + mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{ 4, 8, 15, 16, 23, 42 }; + mInjector.returnValueForstartUserOnSecondaryDisplay = false; + + boolean started = mAms.startUserInBackgroundOnSecondaryDisplay(USER_ID, 42); + Log.v(TAG, "Started: " + started); + + assertWithMessage("mAms.startUserInBackgroundOnSecondaryDisplay(%s, 42)", USER_ID) + .that(started).isFalse(); + assertWithMessage("UserController.startUserOnSecondaryDisplay() calls") + .that(mInjector.usersStartedOnSecondaryDisplays) + .containsExactly(new Pair<>(USER_ID, 42)); + } + + @Test + public void testStartUserInBackgroundOnSecondaryDisplay_validDisplay_success() { + mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{ 4, 8, 15, 16, 23, 42 }; + mInjector.returnValueForstartUserOnSecondaryDisplay = true; + + boolean started = mAms.startUserInBackgroundOnSecondaryDisplay(USER_ID, 42); + Log.v(TAG, "Started: " + started); + + assertWithMessage("mAms.startUserInBackgroundOnSecondaryDisplay(%s, 42)", USER_ID) + .that(started).isTrue(); + assertWithMessage("UserController.startUserOnSecondaryDisplay() calls") + .that(mInjector.usersStartedOnSecondaryDisplays) + .containsExactly(new Pair<>(USER_ID, 42)); + } + private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq, long lastNetworkUpdatedProcStateSeq, final long procStateSeqToWait, boolean expectWait) throws Exception { @@ -922,7 +978,11 @@ public class ActivityManagerServiceTest { } private class TestInjector extends Injector { - private boolean mRestricted = true; + public boolean restricted = true; + public int[] secondaryDisplayIdsForStartingBackgroundUsers; + + public boolean returnValueForstartUserOnSecondaryDisplay; + public List<Pair<Integer, Integer>> usersStartedOnSecondaryDisplays = new ArrayList<>(); TestInjector(Context context) { super(context); @@ -940,11 +1000,18 @@ public class ActivityManagerServiceTest { @Override public boolean isNetworkRestrictedForUid(int uid) { - return mRestricted; + return restricted; } - public void setNetworkRestrictedForUid(boolean restricted) { - mRestricted = restricted; + @Override + public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() { + return secondaryDisplayIdsForStartingBackgroundUsers; + } + + @Override + public boolean startUserOnSecondaryDisplay(int userId, int displayId) { + usersStartedOnSecondaryDisplays.add(new Pair<>(userId, displayId)); + return returnValueForstartUserOnSecondaryDisplay; } } } |