Changes AM.startUserInBgOnSecondaryDisplay() to check if display is valid.

Also adds APIs to list the valid displays:
- @TestApi AM getSecondaryDisplayIdsForStartingBackgroundUsers()
- cmd activity list-secondary-displays-for-starting-users
- system property
  (fw.secondary_display_for_starting_users_for_testing_purposes)
  to override it for testing purposes

Test: m -j update-api
Test: adb shell cmd activity list-secondary-displays-for-starting-users
Test: atest MultipleUsersOnMultipleDisplaysTest # CTS
Test: atest ActivityManagerServiceTest ActivityManagerServiceInjectorTest # unit

Bug: 239982558

Change-Id: Iaf5c83344075cce9985885a3f9359ebbd0174d41
This commit is contained in:
Felipe Leme
2022-09-16 16:26:18 -07:00
parent d274a8df5d
commit 23fbfb82b4
7 changed files with 433 additions and 9 deletions

View File

@@ -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);

View File

@@ -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
*/
@@ -4413,6 +4415,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.
*

View File

@@ -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();
}

View File

@@ -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;
@@ -18369,6 +18420,103 @@ public class ActivityManagerService extends IActivityManager.Stub
return false;
}
/**
* 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
*/

View File

@@ -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, "");
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}
}