Added UserVisibilityListener logic to UserVisibilityMediator.
This CL only adds logic on UserVisibilityMediator so it can call
the UserVisibilityListener callbacks upon users starting or stopping,
but it doesn't change UserController to use it yet - that will be done
in a separate CL. In fact, there are still some corner-case scenarios
there the logic is not working (for example, when the system user is
switched out, it's not sending a visibility change event for its
profiles)
Bug: 244333150
Test: atest UserVisibilityMediatorMUMDTest \
UserVisibilityMediatorSUSDTest
Change-Id: Id10462768b905c5e72b5fa9fb33af048dadd2ad0
This commit is contained in:
@@ -634,7 +634,7 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
@GuardedBy("mUserStates")
|
||||
private final WatchedUserStates mUserStates = new WatchedUserStates();
|
||||
|
||||
private final UserVisibilityMediator mUserVisibilityMediator = new UserVisibilityMediator();
|
||||
private final UserVisibilityMediator mUserVisibilityMediator;
|
||||
|
||||
private static UserManagerService sInstance;
|
||||
|
||||
@@ -733,6 +733,7 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
mPackagesLock = packagesLock;
|
||||
mUsers = users != null ? users : new SparseArray<>();
|
||||
mHandler = new MainHandler();
|
||||
mUserVisibilityMediator = new UserVisibilityMediator(mHandler);
|
||||
mUserDataPreparer = userDataPreparer;
|
||||
mUserTypes = UserTypeFactory.getUserTypes();
|
||||
invalidateOwnerNameIfNecessary(context.getResources(), true /* forceUpdate */);
|
||||
|
||||
@@ -28,6 +28,7 @@ import static com.android.server.pm.UserManagerInternal.userAssignmentResultToSt
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Dumpable;
|
||||
@@ -40,9 +41,11 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.pm.UserManagerInternal.UserAssignmentResult;
|
||||
import com.android.server.pm.UserManagerInternal.UserVisibilityListener;
|
||||
import com.android.server.utils.Slogf;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Class responsible for deciding whether a user is visible (or visible for a given display).
|
||||
@@ -110,15 +113,24 @@ public final class UserVisibilityMediator implements Dumpable {
|
||||
@GuardedBy("mLock")
|
||||
private final SparseIntArray mStartedProfileGroupIds = new SparseIntArray();
|
||||
|
||||
UserVisibilityMediator() {
|
||||
this(UserManager.isUsersOnSecondaryDisplaysEnabled());
|
||||
/**
|
||||
* Handler user to call listeners
|
||||
*/
|
||||
private final Handler mHandler;
|
||||
|
||||
// @GuardedBy("mLock") - hold lock for writes, no lock necessary for simple reads
|
||||
final CopyOnWriteArrayList<UserVisibilityListener> mListeners =
|
||||
new CopyOnWriteArrayList<>();
|
||||
|
||||
UserVisibilityMediator(Handler handler) {
|
||||
this(UserManager.isUsersOnSecondaryDisplaysEnabled(), handler);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
UserVisibilityMediator(boolean usersOnSecondaryDisplaysEnabled) {
|
||||
UserVisibilityMediator(boolean usersOnSecondaryDisplaysEnabled, Handler handler) {
|
||||
mUsersOnSecondaryDisplaysEnabled = usersOnSecondaryDisplaysEnabled;
|
||||
mUsersOnSecondaryDisplays = mUsersOnSecondaryDisplaysEnabled ? new SparseIntArray() : null;
|
||||
|
||||
mHandler = handler;
|
||||
// TODO(b/242195409): might need to change this if boot logic is refactored for HSUM devices
|
||||
mStartedProfileGroupIds.put(INITIAL_CURRENT_USER_ID, INITIAL_CURRENT_USER_ID);
|
||||
}
|
||||
@@ -151,6 +163,7 @@ public final class UserVisibilityMediator implements Dumpable {
|
||||
}
|
||||
|
||||
int result;
|
||||
IntArray visibleUsersBefore, visibleUsersAfter;
|
||||
synchronized (mLock) {
|
||||
result = getUserVisibilityOnStartLocked(userId, profileGroupId, foreground, displayId);
|
||||
if (DBG) {
|
||||
@@ -166,6 +179,8 @@ public final class UserVisibilityMediator implements Dumpable {
|
||||
return USER_ASSIGNMENT_RESULT_FAILURE;
|
||||
}
|
||||
|
||||
visibleUsersBefore = getVisibleUsers();
|
||||
|
||||
// Set current user / profiles state
|
||||
if (foreground) {
|
||||
mCurrentUserId = userId;
|
||||
@@ -195,8 +210,12 @@ public final class UserVisibilityMediator implements Dumpable {
|
||||
Slogf.wtf(TAG, "invalid resut from canAssignUserToDisplayLocked: %d",
|
||||
mappingResult);
|
||||
}
|
||||
|
||||
visibleUsersAfter = getVisibleUsers();
|
||||
}
|
||||
|
||||
dispatchVisibilityChanged(visibleUsersBefore, visibleUsersAfter);
|
||||
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "returning %s", userAssignmentResultToString(result));
|
||||
}
|
||||
@@ -320,29 +339,40 @@ public final class UserVisibilityMediator implements Dumpable {
|
||||
/**
|
||||
* See {@link UserManagerInternal#unassignUserFromDisplayOnStop(int)}.
|
||||
*/
|
||||
public void unassignUserFromDisplayOnStop(int userId) {
|
||||
public void unassignUserFromDisplayOnStop(@UserIdInt int userId) {
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "unassignUserFromDisplayOnStop(%d)", userId);
|
||||
}
|
||||
IntArray visibleUsersBefore, visibleUsersAfter;
|
||||
synchronized (mLock) {
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "Removing %d from mStartedProfileGroupIds (%s)", userId,
|
||||
mStartedProfileGroupIds);
|
||||
}
|
||||
mStartedProfileGroupIds.delete(userId);
|
||||
visibleUsersBefore = getVisibleUsers();
|
||||
|
||||
if (!mUsersOnSecondaryDisplaysEnabled) {
|
||||
// Don't need to do update mUsersOnSecondaryDisplays because methods (such as
|
||||
// isUserVisible()) already know that the current user (and their profiles) is
|
||||
// assigned to the default display.
|
||||
return;
|
||||
}
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "Removing %d from mUsersOnSecondaryDisplays (%s)", userId,
|
||||
mUsersOnSecondaryDisplays);
|
||||
}
|
||||
mUsersOnSecondaryDisplays.delete(userId);
|
||||
unassignUserFromDisplayOnStopLocked(userId);
|
||||
|
||||
visibleUsersAfter = getVisibleUsers();
|
||||
}
|
||||
dispatchVisibilityChanged(visibleUsersBefore, visibleUsersAfter);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void unassignUserFromDisplayOnStopLocked(@UserIdInt int userId) {
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "Removing %d from mStartedProfileGroupIds (%s)", userId,
|
||||
mStartedProfileGroupIds);
|
||||
}
|
||||
mStartedProfileGroupIds.delete(userId);
|
||||
|
||||
if (!mUsersOnSecondaryDisplaysEnabled) {
|
||||
// Don't need to do update mUsersOnSecondaryDisplays because methods (such as
|
||||
// isUserVisible()) already know that the current user (and their profiles) is
|
||||
// assigned to the default display.
|
||||
return;
|
||||
}
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "Removing %d from mUsersOnSecondaryDisplays (%s)", userId,
|
||||
mUsersOnSecondaryDisplays);
|
||||
}
|
||||
mUsersOnSecondaryDisplays.delete(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,18 +381,29 @@ public final class UserVisibilityMediator implements Dumpable {
|
||||
public boolean isUserVisible(@UserIdInt int userId) {
|
||||
// First check current foreground user and their profiles (on main display)
|
||||
if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "isUserVisible(%d): true to current user or profile", userId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Device doesn't support multiple users on multiple displays, so only users checked above
|
||||
// can be visible
|
||||
if (!mUsersOnSecondaryDisplaysEnabled) {
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "isUserVisible(%d): false for non-current user on MUMD", userId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean visible;
|
||||
synchronized (mLock) {
|
||||
return mUsersOnSecondaryDisplays.indexOfKey(userId) >= 0;
|
||||
visible = mUsersOnSecondaryDisplays.indexOfKey(userId) >= 0;
|
||||
}
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "isUserVisible(%d): %b from mapping", userId, visible);
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -481,6 +522,79 @@ public final class UserVisibilityMediator implements Dumpable {
|
||||
return visibleUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link UserVisibilityListener listener}.
|
||||
*/
|
||||
public void addListener(UserVisibilityListener listener) {
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "adding listener %s", listener);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a {@link UserVisibilityListener listener}.
|
||||
*/
|
||||
public void removeListener(UserVisibilityListener listener) {
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "removing listener %s", listener);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nofify all listeners about the visibility changes from before / after a change of state.
|
||||
*/
|
||||
private void dispatchVisibilityChanged(IntArray visibleUsersBefore,
|
||||
IntArray visibleUsersAfter) {
|
||||
if (visibleUsersBefore == null) {
|
||||
// Optimization - it's only null when listeners is empty
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "dispatchVisibilityChanged(): ignoring, no listeners");
|
||||
}
|
||||
return;
|
||||
}
|
||||
CopyOnWriteArrayList<UserVisibilityListener> listeners = mListeners;
|
||||
if (DBG) {
|
||||
Slogf.d(TAG,
|
||||
"dispatchVisibilityChanged(): visibleUsersBefore=%s, visibleUsersAfter=%s, "
|
||||
+ "%d listeners (%s)", visibleUsersBefore, visibleUsersAfter, listeners.size(),
|
||||
mListeners);
|
||||
}
|
||||
for (int i = 0; i < visibleUsersBefore.size(); i++) {
|
||||
int userId = visibleUsersBefore.get(i);
|
||||
if (visibleUsersAfter.indexOf(userId) == -1) {
|
||||
dispatchVisibilityChanged(listeners, userId, /* visible= */ false);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < visibleUsersAfter.size(); i++) {
|
||||
int userId = visibleUsersAfter.get(i);
|
||||
if (visibleUsersBefore.indexOf(userId) == -1) {
|
||||
dispatchVisibilityChanged(listeners, userId, /* visible= */ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchVisibilityChanged(CopyOnWriteArrayList<UserVisibilityListener> listeners,
|
||||
@UserIdInt int userId, boolean visible) {
|
||||
if (DBG) {
|
||||
Slogf.d(TAG, "dispatchVisibilityChanged(%d -> %b): sending to %d listeners",
|
||||
userId, visible, listeners.size());
|
||||
}
|
||||
for (int i = 0; i < mListeners.size(); i++) {
|
||||
UserVisibilityListener listener = mListeners.get(i);
|
||||
if (DBG) {
|
||||
Slogf.v(TAG, "dispatchVisibilityChanged(%d -> %b): sending to %s",
|
||||
userId, visible, listener);
|
||||
}
|
||||
mHandler.post(() -> listener.onUserVisibilityChanged(userId, visible));
|
||||
}
|
||||
}
|
||||
|
||||
private void dump(IndentingPrintWriter ipw) {
|
||||
ipw.println("UserVisibilityMediator");
|
||||
ipw.increaseIndent();
|
||||
@@ -504,6 +618,18 @@ public final class UserVisibilityMediator implements Dumpable {
|
||||
dumpSparseIntArray(ipw, mUsersOnSecondaryDisplays,
|
||||
"background user / secondary display", "u", "d");
|
||||
}
|
||||
int numberListeners = mListeners.size();
|
||||
ipw.print("Number of listeners: ");
|
||||
ipw.println(numberListeners);
|
||||
if (numberListeners > 0) {
|
||||
ipw.increaseIndent();
|
||||
for (int i = 0; i < numberListeners; i++) {
|
||||
ipw.print(i);
|
||||
ipw.print(": ");
|
||||
ipw.println(mListeners.get(i));
|
||||
}
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
||||
ipw.decreaseIndent();
|
||||
|
||||
@@ -43,7 +43,7 @@ public abstract class ExtendedMockitoTestCase {
|
||||
|
||||
private MockitoSession mSession;
|
||||
|
||||
private final Expect mExpect = Expect.create();
|
||||
protected final Expect mExpect = Expect.create();
|
||||
protected final DumpableDumperRule mDumpableDumperRule = new DumpableDumperRule();
|
||||
|
||||
@Rule
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.pm;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.pm.UserManagerInternal.UserVisibilityListener;
|
||||
|
||||
import com.google.common.truth.Expect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* {@link UserVisibilityListener} implementation that expects callback events to be asynchronously
|
||||
* received.
|
||||
*/
|
||||
public final class AsyncUserVisibilityListener implements UserVisibilityListener {
|
||||
|
||||
private static final String TAG = AsyncUserVisibilityListener.class.getSimpleName();
|
||||
|
||||
private static final long WAIT_TIMEOUT_MS = 2_000;
|
||||
private static final long WAIT_NO_EVENTS_TIMEOUT_MS = 1_000;
|
||||
|
||||
private static int sNextId;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final Expect mExpect;
|
||||
private final int mId = ++sNextId;
|
||||
private final Thread mExpectedReceiverThread;
|
||||
private final CountDownLatch mLatch;
|
||||
private final List<UserVisibilityChangedEvent> mExpectedEvents;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final List<UserVisibilityChangedEvent> mReceivedEvents = new ArrayList<>();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final List<String> mErrors = new ArrayList<>();
|
||||
|
||||
private AsyncUserVisibilityListener(Expect expect, Thread expectedReceiverThread,
|
||||
List<UserVisibilityChangedEvent> expectedEvents) {
|
||||
mExpect = expect;
|
||||
mExpectedReceiverThread = expectedReceiverThread;
|
||||
mExpectedEvents = expectedEvents;
|
||||
mLatch = new CountDownLatch(expectedEvents.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserVisibilityChanged(int userId, boolean visible) {
|
||||
UserVisibilityChangedEvent event = new UserVisibilityChangedEvent(userId, visible);
|
||||
Thread callingThread = Thread.currentThread();
|
||||
Log.d(TAG, "Received event (" + event + ") on thread " + callingThread);
|
||||
|
||||
if (callingThread != mExpectedReceiverThread) {
|
||||
addError("event %s received in on thread %s but was expected on thread %s",
|
||||
event, callingThread, mExpectedReceiverThread);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mReceivedEvents.add(event);
|
||||
mLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the expected events were called.
|
||||
*/
|
||||
public void verify() throws InterruptedException {
|
||||
waitForEventsAndCheckErrors();
|
||||
|
||||
List<UserVisibilityChangedEvent> receivedEvents = getReceivedEvents();
|
||||
|
||||
if (receivedEvents.isEmpty()) {
|
||||
mExpect.withMessage("received events").that(receivedEvents).isEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: check "inOrder" might be too harsh in some cases (for example, if the fg user
|
||||
// has 2 profiles, the order of the events on the profiles wouldn't matter), but we
|
||||
// still need some dependency (like "user A became invisible before user B became
|
||||
// visible", so this is fine for now (but eventually we might need to add more
|
||||
// sophisticated assertions)
|
||||
mExpect.withMessage("received events").that(receivedEvents)
|
||||
.containsExactlyElementsIn(mExpectedEvents).inOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
List<UserVisibilityChangedEvent> receivedEvents = getReceivedEvents();
|
||||
return "[" + getClass().getSimpleName() + ": id=" + mId
|
||||
+ ", creationThread=" + mExpectedReceiverThread
|
||||
+ ", received=" + receivedEvents.size()
|
||||
+ ", events=" + receivedEvents + "]";
|
||||
}
|
||||
|
||||
private List<UserVisibilityChangedEvent> getReceivedEvents() {
|
||||
synchronized (mLock) {
|
||||
return Collections.unmodifiableList(mReceivedEvents);
|
||||
}
|
||||
}
|
||||
|
||||
private void waitForEventsAndCheckErrors() throws InterruptedException {
|
||||
waitForEvents();
|
||||
synchronized (mLock) {
|
||||
if (!mErrors.isEmpty()) {
|
||||
fail(mErrors.size() + " errors on received events: " + mErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void waitForEvents() throws InterruptedException {
|
||||
if (mExpectedEvents.isEmpty()) {
|
||||
Log.v(TAG, "Sleeping " + WAIT_NO_EVENTS_TIMEOUT_MS + "ms to make sure no event is "
|
||||
+ "received");
|
||||
Thread.sleep(WAIT_NO_EVENTS_TIMEOUT_MS);
|
||||
return;
|
||||
}
|
||||
|
||||
int expectedNumberEvents = mExpectedEvents.size();
|
||||
Log.v(TAG, "Waiting up to " + WAIT_TIMEOUT_MS + "ms until " + expectedNumberEvents
|
||||
+ " events are received");
|
||||
if (!mLatch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
|
||||
List<UserVisibilityChangedEvent> receivedEvents = getReceivedEvents();
|
||||
addError("Timed out (%d ms) waiting for %d events; received %d so far (%s), "
|
||||
+ "but expecting %d (%s)", WAIT_NO_EVENTS_TIMEOUT_MS, expectedNumberEvents,
|
||||
receivedEvents.size(), receivedEvents, expectedNumberEvents, mExpectedEvents);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("AnnotateFormatMethod")
|
||||
private void addError(String format, Object...args) {
|
||||
synchronized (mLock) {
|
||||
mErrors.add(String.format(format, args));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for {@link AsyncUserVisibilityListener} objects.
|
||||
*/
|
||||
public static final class Factory {
|
||||
private final Expect mExpect;
|
||||
private final Thread mExpectedReceiverThread;
|
||||
|
||||
public Factory(Expect expect, Thread expectedReceiverThread) {
|
||||
mExpect = expect;
|
||||
mExpectedReceiverThread = expectedReceiverThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link AsyncUserVisibilityListener} that is expecting the given events.
|
||||
*/
|
||||
public AsyncUserVisibilityListener forEvents(UserVisibilityChangedEvent...expectedEvents) {
|
||||
return new AsyncUserVisibilityListener(mExpect, mExpectedReceiverThread,
|
||||
Arrays.asList(expectedEvents));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link AsyncUserVisibilityListener} that is expecting no events.
|
||||
*/
|
||||
public AsyncUserVisibilityListener forNoEvents() {
|
||||
return new AsyncUserVisibilityListener(mExpect, mExpectedReceiverThread,
|
||||
Collections.emptyList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.pm;
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
|
||||
/**
|
||||
* Representation of a {@link UserManagerInternal.UserVisibilityListener} event.
|
||||
*/
|
||||
public final class UserVisibilityChangedEvent {
|
||||
|
||||
public @UserIdInt int userId;
|
||||
public boolean visible;
|
||||
|
||||
UserVisibilityChangedEvent(@UserIdInt int userId, boolean visible) {
|
||||
this.userId = userId;
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + userId;
|
||||
result = prime * result + (visible ? 1231 : 1237);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
UserVisibilityChangedEvent other = (UserVisibilityChangedEvent) obj;
|
||||
if (userId != other.userId) return false;
|
||||
if (visible != other.visible) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return userId + ":" + (visible ? "visible" : "invisible");
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method.
|
||||
*/
|
||||
public static UserVisibilityChangedEvent onVisible(@UserIdInt int userId) {
|
||||
return new UserVisibilityChangedEvent(userId, /* visible= */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method.
|
||||
*/
|
||||
public static UserVisibilityChangedEvent onInvisible(@UserIdInt int userId) {
|
||||
return new UserVisibilityChangedEvent(userId, /* visible= */ false);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import static android.view.Display.INVALID_DISPLAY;
|
||||
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE;
|
||||
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
|
||||
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE;
|
||||
import static com.android.server.pm.UserVisibilityChangedEvent.onVisible;
|
||||
import static com.android.server.pm.UserVisibilityMediator.INITIAL_CURRENT_USER_ID;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -34,28 +35,38 @@ import org.junit.Test;
|
||||
*/
|
||||
public final class UserVisibilityMediatorMUMDTest extends UserVisibilityMediatorTestCase {
|
||||
|
||||
public UserVisibilityMediatorMUMDTest() {
|
||||
public UserVisibilityMediatorMUMDTest() throws Exception {
|
||||
super(/* usersOnSecondaryDisplaysEnabled= */ true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartFgUser_onInvalidDisplay() {
|
||||
public void testStartFgUser_onInvalidDisplay() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, FG, INVALID_DISPLAY);
|
||||
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBgUser_onInvalidDisplay() {
|
||||
public void testStartBgUser_onInvalidDisplay() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, BG, INVALID_DISPLAY);
|
||||
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
|
||||
|
||||
expectUserIsNotVisibleAtAll(USER_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBgUser_onSecondaryDisplay_displayAvailable() {
|
||||
public void testStartBgUser_onSecondaryDisplay_displayAvailable() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForEvents(onVisible(USER_ID));
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, BG,
|
||||
SECONDARY_DISPLAY_ID);
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE);
|
||||
@@ -68,10 +79,13 @@ public final class UserVisibilityMediatorMUMDTest extends UserVisibilityMediator
|
||||
|
||||
expectDisplayAssignedToUser(USER_ID, SECONDARY_DISPLAY_ID);
|
||||
expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, USER_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVisibilityOfCurrentUserAndProfilesOnDisplayAssignedToAnotherUser() {
|
||||
public void testVisibilityOfCurrentUserAndProfilesOnDisplayAssignedToAnotherUser()
|
||||
throws Exception {
|
||||
startDefaultProfile();
|
||||
|
||||
// Make sure they were visible before
|
||||
@@ -87,7 +101,8 @@ public final class UserVisibilityMediatorMUMDTest extends UserVisibilityMediator
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBgUser_onSecondaryDisplay_displayAlreadyAssigned() {
|
||||
public void testStartBgUser_onSecondaryDisplay_displayAlreadyAssigned() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForEvents(onVisible(OTHER_USER_ID));
|
||||
startUserInSecondaryDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID);
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, BG,
|
||||
@@ -97,10 +112,13 @@ public final class UserVisibilityMediatorMUMDTest extends UserVisibilityMediator
|
||||
expectUserIsNotVisibleAtAll(USER_ID);
|
||||
expectNoDisplayAssignedToUser(USER_ID);
|
||||
expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, OTHER_USER_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBgUser_onSecondaryDisplay_userAlreadyAssigned() {
|
||||
public void testStartBgUser_onSecondaryDisplay_userAlreadyAssigned() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForEvents(onVisible(USER_ID));
|
||||
startUserInSecondaryDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID);
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, BG,
|
||||
@@ -116,10 +134,14 @@ public final class UserVisibilityMediatorMUMDTest extends UserVisibilityMediator
|
||||
|
||||
expectDisplayAssignedToUser(USER_ID, OTHER_SECONDARY_DISPLAY_ID);
|
||||
expectUserAssignedToDisplay(OTHER_SECONDARY_DISPLAY_ID, USER_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBgProfile_onDefaultDisplay_whenParentVisibleOnSecondaryDisplay() {
|
||||
public void testStartBgProfile_onDefaultDisplay_whenParentVisibleOnSecondaryDisplay()
|
||||
throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForEvents(onVisible(PARENT_USER_ID));
|
||||
startUserInSecondaryDisplay(PARENT_USER_ID, OTHER_SECONDARY_DISPLAY_ID);
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG,
|
||||
@@ -129,5 +151,7 @@ public final class UserVisibilityMediatorMUMDTest extends UserVisibilityMediator
|
||||
expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
|
||||
expectNoDisplayAssignedToUser(PROFILE_USER_ID);
|
||||
expectUserAssignedToDisplay(OTHER_SECONDARY_DISPLAY_ID, PARENT_USER_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ package com.android.server.pm;
|
||||
|
||||
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE;
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
@@ -35,26 +33,18 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBgUser_onSecondaryDisplay() {
|
||||
startUserInBackgroundOnSecondaryDisplayAndAssertFailure(USER_ID, USER_ID);
|
||||
public void testStartBgUser_onSecondaryDisplay() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
|
||||
expectNoUserAssignedToDisplay(SECONDARY_DISPLAY_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBgProfileUser_onSecondaryDisplay() {
|
||||
startForegroundUser(PARENT_USER_ID);
|
||||
|
||||
startUserInBackgroundOnSecondaryDisplayAndAssertFailure(PROFILE_USER_ID, PARENT_USER_ID);
|
||||
}
|
||||
|
||||
private void startUserInBackgroundOnSecondaryDisplayAndAssertFailure(@UserIdInt int userId,
|
||||
@UserIdInt int profileGroupId) {
|
||||
int result = mMediator.assignUserToDisplayOnStart(userId, profileGroupId, BG,
|
||||
int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, BG,
|
||||
SECONDARY_DISPLAY_ID);
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
|
||||
|
||||
expectUserIsNotVisibleAtAll(userId);
|
||||
expectNoDisplayAssignedToUser(userId);
|
||||
expectUserIsNotVisibleAtAll(USER_ID);
|
||||
expectNoDisplayAssignedToUser(USER_ID);
|
||||
|
||||
expectNoUserAssignedToDisplay(SECONDARY_DISPLAY_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_F
|
||||
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
|
||||
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE;
|
||||
import static com.android.server.pm.UserManagerInternal.userAssignmentResultToString;
|
||||
import static com.android.server.pm.UserVisibilityChangedEvent.onInvisible;
|
||||
import static com.android.server.pm.UserVisibilityChangedEvent.onVisible;
|
||||
import static com.android.server.pm.UserVisibilityMediator.INITIAL_CURRENT_USER_ID;
|
||||
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
@@ -34,13 +36,16 @@ import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
import android.os.HandlerThread;
|
||||
import android.util.IntArray;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.ExtendedMockitoTestCase;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -98,6 +103,11 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
protected static final boolean FG = true;
|
||||
protected static final boolean BG = false;
|
||||
|
||||
private static final HandlerThread sHandlerThread = new HandlerThread(TAG);
|
||||
|
||||
protected final AsyncUserVisibilityListener.Factory mListenerFactory =
|
||||
new AsyncUserVisibilityListener.Factory(mExpect, sHandlerThread);
|
||||
|
||||
private final boolean mUsersOnSecondaryDisplaysEnabled;
|
||||
|
||||
protected UserVisibilityMediator mMediator;
|
||||
@@ -106,9 +116,24 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
mUsersOnSecondaryDisplaysEnabled = usersOnSecondaryDisplaysEnabled;
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static final void startHandlerThread() {
|
||||
Log.d(TAG, "Starting handler thread " + sHandlerThread);
|
||||
sHandlerThread.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static final void quitHandlerThread() {
|
||||
Log.d(TAG, "Quitting handler thread " + sHandlerThread);
|
||||
if (!sHandlerThread.quit()) {
|
||||
Log.w(TAG, "sHandlerThread(" + sHandlerThread + ").quit() returned false");
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public final void setMediator() {
|
||||
mMediator = new UserVisibilityMediator(mUsersOnSecondaryDisplaysEnabled);
|
||||
public final void setFixtures() {
|
||||
mMediator = new UserVisibilityMediator(mUsersOnSecondaryDisplaysEnabled,
|
||||
sHandlerThread.getThreadHandler());
|
||||
mDumpableDumperRule.addDumpable(mMediator);
|
||||
}
|
||||
|
||||
@@ -125,7 +150,11 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testStartFgUser_onDefaultDisplay() {
|
||||
public final void testStartFgUser_onDefaultDisplay() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForEvents(
|
||||
onInvisible(INITIAL_CURRENT_USER_ID),
|
||||
onVisible(USER_ID));
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, FG,
|
||||
DEFAULT_DISPLAY);
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE);
|
||||
@@ -144,12 +173,19 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, USER_ID);
|
||||
|
||||
expectDisplayAssignedToUser(USER_NULL, INVALID_DISPLAY);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testSwitchFgUser_onDefaultDisplay() {
|
||||
public final void testSwitchFgUser_onDefaultDisplay() throws Exception {
|
||||
int previousCurrentUserId = OTHER_USER_ID;
|
||||
int currentUserId = USER_ID;
|
||||
AsyncUserVisibilityListener listener = addListenerForEvents(
|
||||
onInvisible(INITIAL_CURRENT_USER_ID),
|
||||
onVisible(previousCurrentUserId),
|
||||
onInvisible(previousCurrentUserId),
|
||||
onVisible(currentUserId));
|
||||
startForegroundUser(previousCurrentUserId);
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(currentUserId, currentUserId, FG,
|
||||
@@ -169,22 +205,29 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
|
||||
expectUserIsNotVisibleAtAll(previousCurrentUserId);
|
||||
expectNoDisplayAssignedToUser(previousCurrentUserId);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testStartFgUser_onSecondaryDisplay() {
|
||||
int userId = USER_ID;
|
||||
public final void testStartFgUser_onSecondaryDisplay() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(userId, userId, FG, SECONDARY_DISPLAY_ID);
|
||||
int result =
|
||||
mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, FG, SECONDARY_DISPLAY_ID);
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
|
||||
|
||||
expectUserIsNotVisibleAtAll(userId);
|
||||
expectUserIsNotVisibleAtAll(USER_ID);
|
||||
expectNoDisplayAssignedToUser(USER_ID);
|
||||
expectNoUserAssignedToDisplay(DEFAULT_DISPLAY);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testStartBgUser_onDefaultDisplay() {
|
||||
public final void testStartBgUser_onDefaultDisplay() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, BG,
|
||||
DEFAULT_DISPLAY);
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE);
|
||||
@@ -192,10 +235,15 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
expectUserIsNotVisibleAtAll(USER_ID);
|
||||
expectNoDisplayAssignedToUser(USER_ID);
|
||||
expectNoUserAssignedToDisplay(DEFAULT_DISPLAY);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testStartBgSystemUser_onSecondaryDisplay() {
|
||||
public final void testStartBgSystemUser_onSecondaryDisplay() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForEvents(
|
||||
onInvisible(INITIAL_CURRENT_USER_ID),
|
||||
onVisible(USER_ID));
|
||||
// Must explicitly set current user, as USER_SYSTEM is the default current user
|
||||
startForegroundUser(USER_ID);
|
||||
|
||||
@@ -207,10 +255,17 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
|
||||
expectNoDisplayAssignedToUser(USER_SYSTEM);
|
||||
expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, USER_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testStartBgProfile_onDefaultDisplay_whenParentIsCurrentUser() {
|
||||
public final void testStartBgProfile_onDefaultDisplay_whenParentIsCurrentUser()
|
||||
throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForEvents(
|
||||
onInvisible(INITIAL_CURRENT_USER_ID),
|
||||
onVisible(PARENT_USER_ID),
|
||||
onVisible(PROFILE_USER_ID));
|
||||
startForegroundUser(PARENT_USER_ID);
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG,
|
||||
@@ -226,10 +281,16 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
expectDisplayAssignedToUser(PROFILE_USER_ID, DEFAULT_DISPLAY);
|
||||
expectUserAssignedToDisplay(DEFAULT_DISPLAY, PARENT_USER_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testStopVisibleProfile() {
|
||||
public final void testStopVisibleProfile() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForEvents(
|
||||
onInvisible(INITIAL_CURRENT_USER_ID),
|
||||
onVisible(PARENT_USER_ID),
|
||||
onVisible(PROFILE_USER_ID),
|
||||
onInvisible(PROFILE_USER_ID));
|
||||
startDefaultProfile();
|
||||
|
||||
mMediator.unassignUserFromDisplayOnStop(PROFILE_USER_ID);
|
||||
@@ -237,10 +298,19 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
|
||||
expectNoDisplayAssignedToUser(PROFILE_USER_ID);
|
||||
expectUserAssignedToDisplay(DEFAULT_DISPLAY, PARENT_USER_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testVisibleProfileBecomesInvisibleWhenParentIsSwitchedOut() {
|
||||
public final void testVisibleProfileBecomesInvisibleWhenParentIsSwitchedOut() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForEvents(
|
||||
onInvisible(INITIAL_CURRENT_USER_ID),
|
||||
onVisible(PARENT_USER_ID),
|
||||
onVisible(PROFILE_USER_ID),
|
||||
onInvisible(PARENT_USER_ID),
|
||||
onInvisible(PROFILE_USER_ID),
|
||||
onVisible(OTHER_USER_ID));
|
||||
startDefaultProfile();
|
||||
|
||||
startForegroundUser(OTHER_USER_ID);
|
||||
@@ -248,20 +318,29 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
|
||||
expectNoDisplayAssignedToUser(PROFILE_USER_ID);
|
||||
expectUserAssignedToDisplay(DEFAULT_DISPLAY, OTHER_USER_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testStartBgProfile_onDefaultDisplay_whenParentIsNotStarted() {
|
||||
public final void testStartBgProfile_onDefaultDisplay_whenParentIsNotStarted()
|
||||
throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG,
|
||||
DEFAULT_DISPLAY);
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE);
|
||||
|
||||
expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
|
||||
expectNoDisplayAssignedToUser(PROFILE_USER_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testStartBgProfile_onDefaultDisplay_whenParentIsStartedOnBg() {
|
||||
public final void testStartBgProfile_onDefaultDisplay_whenParentIsStartedOnBg()
|
||||
throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
startBackgroundUser(PARENT_USER_ID);
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG,
|
||||
@@ -272,11 +351,15 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
|
||||
expectNoDisplayAssignedToUser(PROFILE_USER_ID);
|
||||
expectNoUserAssignedToDisplay(DEFAULT_DISPLAY);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
// Not supported - profiles can only be started on default display
|
||||
@Test
|
||||
public final void testStartBgProfile_onSecondaryDisplay() {
|
||||
public final void testStartBgProfile_onSecondaryDisplay() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG,
|
||||
SECONDARY_DISPLAY_ID);
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
|
||||
@@ -284,10 +367,14 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
|
||||
expectNoDisplayAssignedToUser(PROFILE_USER_ID);
|
||||
expectNoUserAssignedToDisplay(SECONDARY_DISPLAY_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testStartFgProfile_onDefaultDisplay() {
|
||||
public final void testStartFgProfile_onDefaultDisplay() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, FG,
|
||||
DEFAULT_DISPLAY);
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
|
||||
@@ -296,10 +383,14 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
|
||||
expectNoDisplayAssignedToUser(PROFILE_USER_ID);
|
||||
expectNoUserAssignedToDisplay(DEFAULT_DISPLAY);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testStartFgProfile_onSecondaryDisplay() {
|
||||
public final void testStartFgProfile_onSecondaryDisplay() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
|
||||
int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, FG,
|
||||
SECONDARY_DISPLAY_ID);
|
||||
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
|
||||
@@ -307,10 +398,12 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
|
||||
expectNoDisplayAssignedToUser(PROFILE_USER_ID);
|
||||
expectNoUserAssignedToDisplay(SECONDARY_DISPLAY_ID);
|
||||
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testIsUserVisible_invalidUsers() {
|
||||
public final void testIsUserVisible_invalidUsers() throws Exception {
|
||||
expectWithMessage("isUserVisible(%s)", USER_NULL)
|
||||
.that(mMediator.isUserVisible(USER_NULL))
|
||||
.isFalse();
|
||||
@@ -325,6 +418,16 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testRemoveListener() throws Exception {
|
||||
AsyncUserVisibilityListener listener = addListenerForNoEvents();
|
||||
|
||||
mMediator.removeListener(listener);
|
||||
|
||||
startForegroundUser(USER_ID);
|
||||
listener.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a user in foreground on the default display, asserting it was properly started.
|
||||
*
|
||||
@@ -397,6 +500,19 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
protected AsyncUserVisibilityListener addListenerForNoEvents() {
|
||||
AsyncUserVisibilityListener listener = mListenerFactory.forNoEvents();
|
||||
mMediator.addListener(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
protected AsyncUserVisibilityListener addListenerForEvents(
|
||||
UserVisibilityChangedEvent... events) {
|
||||
AsyncUserVisibilityListener listener = mListenerFactory.forEvents(events);
|
||||
mMediator.addListener(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
protected void assertStartUserResult(int actualResult, int expectedResult) {
|
||||
assertWithMessage("startUser() result (where %s=%s and %s=%s)",
|
||||
expectedResult, userAssignmentResultToString(expectedResult),
|
||||
|
||||
Reference in New Issue
Block a user