Merge "Changes AM.startUserInBgOnSecondaryDisplay() to check if display is valid."
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fb2c9e4..e08cef9 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -121,6 +121,7 @@
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 da2a76a..576b572 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4387,13 +4387,15 @@
* 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 @@
}
/**
+ * 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 980b79b..b4abd3c 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -770,4 +770,10 @@
"@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 50b47a6..7d85c13 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.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.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.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 @@
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 @@
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mUserController = new UserController(this);
+ mInjector.mUserController = mUserController;
mPendingIntentController = new PendingIntentController(
mHandlerThread.getLooper(), mUserController, mConstants);
@@ -6057,6 +6062,24 @@
* 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 @@
@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 @@
@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 @@
}
/**
+ * 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 b4f6e35..10e2aae 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 @@
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 @@
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 @@
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 @@
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 0000000..09df96f
--- /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 3c17102..0d6f326 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_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.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.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 @@
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 @@
@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 @@
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 @@
}
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 @@
@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;
}
}
}