DO NOT MERGE Fix work profile change race condition in LMS am: 9b0b32bd17

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11707507

Change-Id: I575fb88dd9b9569caebbd2c442b7a1aee456d23c
This commit is contained in:
Soonil Nagarkar
2020-06-03 19:46:37 +00:00
committed by Automerger Merge Worker
3 changed files with 187 additions and 246 deletions

View File

@@ -28,6 +28,9 @@ import static android.os.PowerManager.locationPowerSaveModeToString;
import static com.android.server.location.CallerIdentity.PERMISSION_COARSE; import static com.android.server.location.CallerIdentity.PERMISSION_COARSE;
import static com.android.server.location.CallerIdentity.PERMISSION_FINE; import static com.android.server.location.CallerIdentity.PERMISSION_FINE;
import static com.android.server.location.UserInfoHelper.UserListener.CURRENT_USER_CHANGED;
import static com.android.server.location.UserInfoHelper.UserListener.USER_STARTED;
import static com.android.server.location.UserInfoHelper.UserListener.USER_STOPPED;
import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS;
@@ -101,7 +104,7 @@ import com.android.server.location.AbstractLocationProvider.State;
import com.android.server.location.CallerIdentity.PermissionLevel; import com.android.server.location.CallerIdentity.PermissionLevel;
import com.android.server.location.LocationRequestStatistics.PackageProviderKey; import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
import com.android.server.location.LocationRequestStatistics.PackageStatistics; import com.android.server.location.LocationRequestStatistics.PackageStatistics;
import com.android.server.location.UserInfoHelper.UserListener; import com.android.server.location.UserInfoHelper.UserListener.UserChange;
import com.android.server.location.gnss.GnssManagerService; import com.android.server.location.gnss.GnssManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -132,11 +135,13 @@ public class LocationManagerService extends ILocationManager.Stub {
*/ */
public static class Lifecycle extends SystemService { public static class Lifecycle extends SystemService {
private final UserInfoHelper mUserInfoHelper;
private final LocationManagerService mService; private final LocationManagerService mService;
public Lifecycle(Context context) { public Lifecycle(Context context) {
super(context); super(context);
mService = new LocationManagerService(context); mUserInfoHelper = new SystemUserInfoHelper(context);
mService = new LocationManagerService(context, mUserInfoHelper);
} }
@Override @Override
@@ -161,6 +166,29 @@ public class LocationManagerService extends ILocationManager.Stub {
mService.onSystemThirdPartyAppsCanStart(); mService.onSystemThirdPartyAppsCanStart();
} }
} }
@Override
public void onUserStarting(TargetUser user) {
mUserInfoHelper.dispatchOnUserStarted(user.getUserIdentifier());
}
@Override
public void onUserSwitching(TargetUser from, TargetUser to) {
mUserInfoHelper.dispatchOnCurrentUserChanged(from.getUserIdentifier(),
to.getUserIdentifier());
}
@Override
public void onUserStopped(TargetUser user) {
mUserInfoHelper.dispatchOnUserStopped(user.getUserIdentifier());
}
private static class SystemUserInfoHelper extends UserInfoHelper {
SystemUserInfoHelper(Context context) {
super(context);
}
}
} }
public static final String TAG = "LocationManagerService"; public static final String TAG = "LocationManagerService";
@@ -232,7 +260,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@PowerManager.LocationPowerSaveMode @PowerManager.LocationPowerSaveMode
private int mBatterySaverMode; private int mBatterySaverMode;
private LocationManagerService(Context context) { private LocationManagerService(Context context, UserInfoHelper userInfoHelper) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG); mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mHandler = FgThread.getHandler(); mHandler = FgThread.getHandler();
mLocalService = new LocalService(); mLocalService = new LocalService();
@@ -240,7 +268,7 @@ public class LocationManagerService extends ILocationManager.Stub {
LocalServices.addService(LocationManagerInternal.class, mLocalService); LocalServices.addService(LocationManagerInternal.class, mLocalService);
mAppOpsHelper = new AppOpsHelper(mContext); mAppOpsHelper = new AppOpsHelper(mContext);
mUserInfoHelper = new UserInfoHelper(mContext); mUserInfoHelper = userInfoHelper;
mSettingsHelper = new SettingsHelper(mContext, mHandler); mSettingsHelper = new SettingsHelper(mContext, mHandler);
mAppForegroundHelper = new AppForegroundHelper(mContext); mAppForegroundHelper = new AppForegroundHelper(mContext);
mLocationUsageLogger = new LocationUsageLogger(); mLocationUsageLogger = new LocationUsageLogger();
@@ -342,7 +370,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// initialize the current users. we would get the user started notifications for these // initialize the current users. we would get the user started notifications for these
// users eventually anyways, but this takes care of it as early as possible. // users eventually anyways, but this takes care of it as early as possible.
for (int userId: mUserInfoHelper.getCurrentUserIds()) { for (int userId: mUserInfoHelper.getCurrentUserIds()) {
onUserChanged(userId, UserListener.USER_STARTED); onUserChanged(userId, USER_STARTED);
} }
} }
} }
@@ -596,32 +624,23 @@ public class LocationManagerService extends ILocationManager.Stub {
} }
} }
private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) { private void onUserChanged(@UserIdInt int userId, @UserChange int change) {
switch (change) { switch (change) {
case UserListener.USER_SWITCHED: case CURRENT_USER_CHANGED:
if (D) {
Log.d(TAG, "user " + userId + " current status changed");
}
synchronized (mLock) { synchronized (mLock) {
for (LocationProviderManager manager : mProviderManagers) { for (LocationProviderManager manager : mProviderManagers) {
manager.onEnabledChangedLocked(userId); manager.onEnabledChangedLocked(userId);
} }
} }
break; break;
case UserListener.USER_STARTED: case USER_STARTED:
if (D) {
Log.d(TAG, "user " + userId + " started");
}
synchronized (mLock) { synchronized (mLock) {
for (LocationProviderManager manager : mProviderManagers) { for (LocationProviderManager manager : mProviderManagers) {
manager.onUserStarted(userId); manager.onUserStarted(userId);
} }
} }
break; break;
case UserListener.USER_STOPPED: case USER_STOPPED:
if (D) {
Log.d(TAG, "user " + userId + " stopped");
}
synchronized (mLock) { synchronized (mLock) {
for (LocationProviderManager manager : mProviderManagers) { for (LocationProviderManager manager : mProviderManagers) {
manager.onUserStopped(userId); manager.onUserStopped(userId);
@@ -957,10 +976,22 @@ public class LocationManagerService extends ILocationManager.Stub {
pw.increaseIndent(); pw.increaseIndent();
// for now we only dump for the parent user // for now we only dump for the parent user
int userId = mUserInfoHelper.getCurrentUserIds()[0]; int[] userIds = mUserInfoHelper.getCurrentUserIds();
pw.println("last location=" + mLastLocation.get(userId)); if (userIds.length == 1) {
pw.println("last coarse location=" + mLastCoarseLocation.get(userId)); int userId = userIds[0];
pw.println("enabled=" + isEnabled(userId)); pw.println("last location=" + mLastLocation.get(userId));
pw.println("last coarse location=" + mLastCoarseLocation.get(userId));
pw.println("enabled=" + isEnabled(userId));
} else {
for (int userId : userIds) {
pw.println("user " + userId + ":");
pw.increaseIndent();
pw.println("last location=" + mLastLocation.get(userId));
pw.println("last coarse location=" + mLastCoarseLocation.get(userId));
pw.println("enabled=" + isEnabled(userId));
pw.decreaseIndent();
}
}
} }
mProvider.dump(fd, pw, args); mProvider.dump(fd, pw, args);

View File

@@ -20,48 +20,48 @@ import static android.os.UserManager.DISALLOW_SHARE_LOCATION;
import static com.android.server.location.LocationManagerService.D; import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG; import static com.android.server.location.LocationManagerService.TAG;
import static com.android.server.location.UserInfoHelper.UserListener.CURRENT_USER_CHANGED;
import static com.android.server.location.UserInfoHelper.UserListener.USER_STARTED;
import static com.android.server.location.UserInfoHelper.UserListener.USER_STOPPED;
import android.annotation.CallSuper;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.app.ActivityManager; import android.app.ActivityManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder; import android.os.Binder;
import android.os.Build;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.util.Log; import android.util.Log;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import com.android.server.FgThread; import com.android.server.LocalServices;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* Provides accessors and listeners for all user info. * Provides accessors and listeners for all user info.
*/ */
public class UserInfoHelper { public abstract class UserInfoHelper {
/** /**
* Listener for current user changes. * Listener for current user changes.
*/ */
public interface UserListener { public interface UserListener {
int USER_SWITCHED = 1; int CURRENT_USER_CHANGED = 1;
int USER_STARTED = 2; int USER_STARTED = 2;
int USER_STOPPED = 3; int USER_STOPPED = 3;
@IntDef({USER_SWITCHED, USER_STARTED, USER_STOPPED}) @IntDef({CURRENT_USER_CHANGED, USER_STARTED, USER_STOPPED})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@interface UserChange {} @interface UserChange {}
@@ -74,144 +74,102 @@ public class UserInfoHelper {
private final Context mContext; private final Context mContext;
private final CopyOnWriteArrayList<UserListener> mListeners; private final CopyOnWriteArrayList<UserListener> mListeners;
@GuardedBy("this")
@Nullable private ActivityManagerInternal mActivityManagerInternal;
@GuardedBy("this") @GuardedBy("this")
@Nullable private UserManager mUserManager; @Nullable private UserManager mUserManager;
@UserIdInt private volatile int mCurrentUserId;
@GuardedBy("this")
@UserIdInt private int mCachedParentUserId;
@GuardedBy("this")
private int[] mCachedProfileUserIds;
public UserInfoHelper(Context context) { public UserInfoHelper(Context context) {
mContext = context; mContext = context;
mListeners = new CopyOnWriteArrayList<>(); mListeners = new CopyOnWriteArrayList<>();
mCurrentUserId = UserHandle.USER_NULL;
mCachedParentUserId = UserHandle.USER_NULL;
mCachedProfileUserIds = new int[]{UserHandle.USER_NULL};
} }
/** Called when system is ready. */ /** Called when system is ready. */
@CallSuper
public synchronized void onSystemReady() { public synchronized void onSystemReady() {
if (mUserManager != null) { if (mActivityManagerInternal != null) {
return; return;
} }
mActivityManagerInternal = Objects.requireNonNull(
LocalServices.getService(ActivityManagerInternal.class));
mUserManager = mContext.getSystemService(UserManager.class); mUserManager = mContext.getSystemService(UserManager.class);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
intentFilter.addAction(Intent.ACTION_USER_STARTED);
intentFilter.addAction(Intent.ACTION_USER_STOPPED);
intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null) {
return;
}
int userId;
switch (action) {
case Intent.ACTION_USER_SWITCHED:
userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
if (userId != UserHandle.USER_NULL) {
onCurrentUserChanged(userId);
}
break;
case Intent.ACTION_USER_STARTED:
userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
if (userId != UserHandle.USER_NULL) {
onUserStarted(userId);
}
break;
case Intent.ACTION_USER_STOPPED:
userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
if (userId != UserHandle.USER_NULL) {
onUserStopped(userId);
}
break;
case Intent.ACTION_MANAGED_PROFILE_ADDED:
case Intent.ACTION_MANAGED_PROFILE_REMOVED:
onUserProfilesChanged();
break;
}
}
}, UserHandle.ALL, intentFilter, null, FgThread.getHandler());
mCurrentUserId = ActivityManager.getCurrentUser();
} }
/** /**
* Adds a listener for user changed events. Callbacks occur on an unspecified thread. * Adds a listener for user changed events. Callbacks occur on an unspecified thread.
*/ */
public void addListener(UserListener listener) { public final void addListener(UserListener listener) {
mListeners.add(listener); mListeners.add(listener);
} }
/** /**
* Removes a listener for user changed events. * Removes a listener for user changed events.
*/ */
public void removeListener(UserListener listener) { public final void removeListener(UserListener listener) {
mListeners.remove(listener); mListeners.remove(listener);
} }
private void onCurrentUserChanged(@UserIdInt int newUserId) { protected void dispatchOnUserStarted(@UserIdInt int userId) {
if (newUserId == mCurrentUserId) {
return;
}
if (D) {
Log.d(TAG, "current user switched from u" + mCurrentUserId + " to u" + newUserId);
}
int oldUserId = mCurrentUserId;
mCurrentUserId = newUserId;
onUserChanged(oldUserId, UserListener.USER_SWITCHED);
onUserChanged(newUserId, UserListener.USER_SWITCHED);
}
private void onUserStarted(@UserIdInt int userId) {
if (D) { if (D) {
Log.d(TAG, "u" + userId + " started"); Log.d(TAG, "u" + userId + " started");
} }
onUserChanged(userId, UserListener.USER_STARTED); for (UserListener listener : mListeners) {
listener.onUserChanged(userId, USER_STARTED);
}
} }
private void onUserStopped(@UserIdInt int userId) { protected void dispatchOnUserStopped(@UserIdInt int userId) {
if (D) { if (D) {
Log.d(TAG, "u" + userId + " stopped"); Log.d(TAG, "u" + userId + " stopped");
} }
onUserChanged(userId, UserListener.USER_STOPPED);
}
private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) {
for (UserListener listener : mListeners) { for (UserListener listener : mListeners) {
listener.onUserChanged(userId, change); listener.onUserChanged(userId, USER_STOPPED);
} }
} }
private synchronized void onUserProfilesChanged() { protected void dispatchOnCurrentUserChanged(@UserIdInt int fromUserId,
// this intent is only sent to the current user @UserIdInt int toUserId) {
if (mCachedParentUserId == mCurrentUserId) { int[] fromUserIds = getProfileIds(fromUserId);
mCachedParentUserId = UserHandle.USER_NULL; int[] toUserIds = getProfileIds(toUserId);
mCachedProfileUserIds = new int[]{UserHandle.USER_NULL}; if (D) {
Log.d(TAG, "current user changed from u" + Arrays.toString(fromUserIds) + " to u"
+ Arrays.toString(toUserIds));
}
for (UserListener listener : mListeners) {
for (int userId : fromUserIds) {
listener.onUserChanged(userId, CURRENT_USER_CHANGED);
}
}
for (UserListener listener : mListeners) {
for (int userId : toUserIds) {
listener.onUserChanged(userId, CURRENT_USER_CHANGED);
}
} }
} }
/** /**
* Returns an array of current user ids. This will always include the current user, and will * Returns an array of current user ids. This will always include the current user, and will
* also include any profiles of the current user. * also include any profiles of the current user. The caller must never mutate the returned
* array.
*/ */
public int[] getCurrentUserIds() { public int[] getCurrentUserIds() {
return getProfileUserIdsForParentUser(mCurrentUserId); synchronized (this) {
if (mActivityManagerInternal == null) {
return new int[] {};
}
}
long identity = Binder.clearCallingIdentity();
try {
return mActivityManagerInternal.getCurrentProfileIds();
} finally {
Binder.restoreCallingIdentity(identity);
}
} }
/** /**
@@ -219,54 +177,47 @@ public class UserInfoHelper {
* user. * user.
*/ */
public boolean isCurrentUserId(@UserIdInt int userId) { public boolean isCurrentUserId(@UserIdInt int userId) {
int currentUserId = mCurrentUserId; synchronized (this) {
return userId == currentUserId || ArrayUtils.contains( if (mActivityManagerInternal == null) {
getProfileUserIdsForParentUser(currentUserId), userId); return false;
}
@GuardedBy("this")
private synchronized int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) {
if (parentUserId != mCachedParentUserId) {
long identity = Binder.clearCallingIdentity();
try {
Preconditions.checkState(mUserManager != null);
// more expensive check - check that argument really is a parent user id
if (Build.IS_DEBUGGABLE) {
Preconditions.checkArgument(
mUserManager.getProfileParent(parentUserId) == null);
}
mCachedParentUserId = parentUserId;
mCachedProfileUserIds = mUserManager.getProfileIdsWithDisabled(parentUserId);
} finally {
Binder.restoreCallingIdentity(identity);
} }
} }
return mCachedProfileUserIds; long identity = Binder.clearCallingIdentity();
try {
return mActivityManagerInternal.isCurrentProfile(userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private int[] getProfileIds(@UserIdInt int userId) {
synchronized (this) {
Preconditions.checkState(mUserManager != null);
}
long identity = Binder.clearCallingIdentity();
try {
return mUserManager.getEnabledProfileIds(userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
} }
/** /**
* Dump info for debugging. * Dump info for debugging.
*/ */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
boolean systemRunning; int[] currentUserProfiles = getCurrentUserIds();
synchronized (this) { pw.println("current users: " + Arrays.toString(currentUserProfiles));
systemRunning = mUserManager != null; UserManager userManager = mContext.getSystemService(UserManager.class);
} if (userManager != null) {
for (int userId : currentUserProfiles) {
if (systemRunning) { if (userManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION,
int[] currentUserIds = getProfileUserIdsForParentUser(mCurrentUserId);
pw.println("current users: " + Arrays.toString(currentUserIds));
for (int userId : currentUserIds) {
if (mUserManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION,
UserHandle.of(userId))) { UserHandle.of(userId))) {
pw.println(" u" + userId + " restricted"); pw.println(" u" + userId + " restricted");
} }
} }
} else {
pw.println("current user: " + mCurrentUserId);
} }
} }
} }

View File

@@ -16,34 +16,23 @@
package com.android.server.location; package com.android.server.location;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
import android.app.ActivityManager; import android.app.ActivityManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.server.LocalServices;
import com.android.server.location.UserInfoHelper.UserListener; import com.android.server.location.UserInfoHelper.UserListener;
import org.junit.After; import org.junit.After;
@@ -51,16 +40,18 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.List;
@Presubmit @Presubmit
@SmallTest @SmallTest
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class UserInfoHelperTest { public class UserInfoHelperTest {
private static class TestUserInfoHelper extends UserInfoHelper {
TestUserInfoHelper(Context context) {
super(context);
}
}
private static final int USER1_ID = 1; private static final int USER1_ID = 1;
private static final int USER1_MANAGED_ID = 11; private static final int USER1_MANAGED_ID = 11;
private static final int[] USER1_PROFILES = new int[]{USER1_ID, USER1_MANAGED_ID}; private static final int[] USER1_PROFILES = new int[]{USER1_ID, USER1_MANAGED_ID};
@@ -70,69 +61,30 @@ public class UserInfoHelperTest {
@Mock private Context mContext; @Mock private Context mContext;
@Mock private UserManager mUserManager; @Mock private UserManager mUserManager;
@Mock private ActivityManagerInternal mActivityManagerInternal;
private StaticMockitoSession mMockingSession; private TestUserInfoHelper mHelper;
private List<BroadcastReceiver> mBroadcastReceivers = new ArrayList<>();
private UserInfoHelper mHelper;
@Before @Before
public void setUp() { public void setUp() {
mMockingSession = mockitoSession() initMocks(this);
.initMocks(this)
.spyStatic(ActivityManager.class)
.strictness(Strictness.WARN)
.startMocking();
LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInternal);
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
doAnswer(invocation -> {
mBroadcastReceivers.add(invocation.getArgument(0));
return null;
}).when(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), any(
UserHandle.class), any(IntentFilter.class), isNull(), any(Handler.class));
doReturn(USER1_PROFILES).when(mUserManager).getProfileIdsWithDisabled(USER1_ID);
doReturn(USER2_PROFILES).when(mUserManager).getProfileIdsWithDisabled(USER2_ID);
doReturn(new UserInfo(USER1_ID, "", 0)).when(mUserManager).getProfileParent(
USER1_MANAGED_ID);
doReturn(new UserInfo(USER2_ID, "", 0)).when(mUserManager).getProfileParent(
USER2_MANAGED_ID);
doReturn(USER1_ID).when(ActivityManager::getCurrentUser); doReturn(USER1_PROFILES).when(mUserManager).getEnabledProfileIds(USER1_ID);
doReturn(USER2_PROFILES).when(mUserManager).getEnabledProfileIds(USER2_ID);
doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER1_ID);
doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER1_MANAGED_ID);
doReturn(USER1_PROFILES).when(mActivityManagerInternal).getCurrentProfileIds();
mHelper = new UserInfoHelper(mContext); mHelper = new TestUserInfoHelper(mContext);
mHelper.onSystemReady(); mHelper.onSystemReady();
} }
@After @After
public void tearDown() { public void tearDown() {
if (mMockingSession != null) { LocalServices.removeServiceForTest(ActivityManagerInternal.class);
mMockingSession.finishMocking();
}
}
private void switchUser(int userId) {
doReturn(userId).when(ActivityManager::getCurrentUser);
Intent intent = new Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE,
userId);
for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) {
broadcastReceiver.onReceive(mContext, intent);
}
}
private void startUser(int userId) {
Intent intent = new Intent(Intent.ACTION_USER_STARTED).putExtra(Intent.EXTRA_USER_HANDLE,
userId);
for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) {
broadcastReceiver.onReceive(mContext, intent);
}
}
private void stopUser(int userId) {
Intent intent = new Intent(Intent.ACTION_USER_STOPPED).putExtra(Intent.EXTRA_USER_HANDLE,
userId);
for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) {
broadcastReceiver.onReceive(mContext, intent);
}
} }
@Test @Test
@@ -140,16 +92,21 @@ public class UserInfoHelperTest {
UserListener listener = mock(UserListener.class); UserListener listener = mock(UserListener.class);
mHelper.addListener(listener); mHelper.addListener(listener);
switchUser(USER1_ID); mHelper.dispatchOnCurrentUserChanged(USER1_ID, USER2_ID);
verify(listener, never()).onUserChanged(anyInt(), anyInt()); verify(listener, times(1)).onUserChanged(USER1_ID, UserListener.CURRENT_USER_CHANGED);
verify(listener, times(1)).onUserChanged(USER1_MANAGED_ID,
UserListener.CURRENT_USER_CHANGED);
verify(listener, times(1)).onUserChanged(USER2_ID, UserListener.CURRENT_USER_CHANGED);
verify(listener, times(1)).onUserChanged(USER2_MANAGED_ID,
UserListener.CURRENT_USER_CHANGED);
switchUser(USER2_ID); mHelper.dispatchOnCurrentUserChanged(USER2_ID, USER1_ID);
verify(listener, times(1)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED); verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.CURRENT_USER_CHANGED);
verify(listener, times(1)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED); verify(listener, times(2)).onUserChanged(USER2_MANAGED_ID,
UserListener.CURRENT_USER_CHANGED);
switchUser(USER1_ID); verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.CURRENT_USER_CHANGED);
verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED); verify(listener, times(2)).onUserChanged(USER1_MANAGED_ID,
verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED); UserListener.CURRENT_USER_CHANGED);
} }
@Test @Test
@@ -157,11 +114,11 @@ public class UserInfoHelperTest {
UserListener listener = mock(UserListener.class); UserListener listener = mock(UserListener.class);
mHelper.addListener(listener); mHelper.addListener(listener);
startUser(USER1_ID); mHelper.dispatchOnUserStarted(USER1_ID);
verify(listener).onUserChanged(USER1_ID, UserListener.USER_STARTED); verify(listener).onUserChanged(USER1_ID, UserListener.USER_STARTED);
startUser(USER2_ID); mHelper.dispatchOnUserStarted(USER1_MANAGED_ID);
verify(listener).onUserChanged(USER2_ID, UserListener.USER_STARTED); verify(listener).onUserChanged(USER1_MANAGED_ID, UserListener.USER_STARTED);
} }
@Test @Test
@@ -169,24 +126,22 @@ public class UserInfoHelperTest {
UserListener listener = mock(UserListener.class); UserListener listener = mock(UserListener.class);
mHelper.addListener(listener); mHelper.addListener(listener);
stopUser(USER1_ID); mHelper.dispatchOnUserStopped(USER2_ID);
verify(listener).onUserChanged(USER1_ID, UserListener.USER_STOPPED);
stopUser(USER2_ID);
verify(listener).onUserChanged(USER2_ID, UserListener.USER_STOPPED); verify(listener).onUserChanged(USER2_ID, UserListener.USER_STOPPED);
mHelper.dispatchOnUserStopped(USER2_MANAGED_ID);
verify(listener).onUserChanged(USER2_MANAGED_ID, UserListener.USER_STOPPED);
} }
@Test @Test
public void testCurrentUserIds() { public void testCurrentUserIds() {
assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES); assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES);
switchUser(USER2_ID); doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_ID);
doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_MANAGED_ID);
doReturn(USER2_PROFILES).when(mActivityManagerInternal).getCurrentProfileIds();
assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER2_PROFILES); assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER2_PROFILES);
switchUser(USER1_ID);
assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES);
} }
@Test @Test
@@ -196,7 +151,11 @@ public class UserInfoHelperTest {
assertThat(mHelper.isCurrentUserId(USER2_ID)).isFalse(); assertThat(mHelper.isCurrentUserId(USER2_ID)).isFalse();
assertThat(mHelper.isCurrentUserId(USER2_MANAGED_ID)).isFalse(); assertThat(mHelper.isCurrentUserId(USER2_MANAGED_ID)).isFalse();
switchUser(USER2_ID); doReturn(false).when(mActivityManagerInternal).isCurrentProfile(USER1_ID);
doReturn(false).when(mActivityManagerInternal).isCurrentProfile(USER1_MANAGED_ID);
doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_ID);
doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_MANAGED_ID);
doReturn(USER2_PROFILES).when(mActivityManagerInternal).getCurrentProfileIds();
assertThat(mHelper.isCurrentUserId(USER1_ID)).isFalse(); assertThat(mHelper.isCurrentUserId(USER1_ID)).isFalse();
assertThat(mHelper.isCurrentUserId(USER2_ID)).isTrue(); assertThat(mHelper.isCurrentUserId(USER2_ID)).isTrue();