DO NOT MERGE Fix work profile change race condition in LMS
It's possible for LMS to find out about work profile changes through external clients before LMS is notified via the UserInfoHelper. This means LMS will have stale information on work profiles, and will not refresh that information when appropriate. We add notifications for profile users as well so that LMS will refresh appropriately. Bug: 157444953 Test: manual Change-Id: I4dc9de6019847b790f2d4aa90b66484cf0a14674
This commit is contained in:
@@ -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_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;
|
||||
|
||||
@@ -101,7 +104,7 @@ import com.android.server.location.AbstractLocationProvider.State;
|
||||
import com.android.server.location.CallerIdentity.PermissionLevel;
|
||||
import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
|
||||
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.pm.permission.PermissionManagerServiceInternal;
|
||||
|
||||
@@ -132,11 +135,13 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
*/
|
||||
public static class Lifecycle extends SystemService {
|
||||
|
||||
private final UserInfoHelper mUserInfoHelper;
|
||||
private final LocationManagerService mService;
|
||||
|
||||
public Lifecycle(Context context) {
|
||||
super(context);
|
||||
mService = new LocationManagerService(context);
|
||||
mUserInfoHelper = new SystemUserInfoHelper(context);
|
||||
mService = new LocationManagerService(context, mUserInfoHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,6 +166,29 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
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";
|
||||
@@ -232,7 +260,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
@PowerManager.LocationPowerSaveMode
|
||||
private int mBatterySaverMode;
|
||||
|
||||
private LocationManagerService(Context context) {
|
||||
private LocationManagerService(Context context, UserInfoHelper userInfoHelper) {
|
||||
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
|
||||
mHandler = FgThread.getHandler();
|
||||
mLocalService = new LocalService();
|
||||
@@ -240,7 +268,7 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
LocalServices.addService(LocationManagerInternal.class, mLocalService);
|
||||
|
||||
mAppOpsHelper = new AppOpsHelper(mContext);
|
||||
mUserInfoHelper = new UserInfoHelper(mContext);
|
||||
mUserInfoHelper = userInfoHelper;
|
||||
mSettingsHelper = new SettingsHelper(mContext, mHandler);
|
||||
mAppForegroundHelper = new AppForegroundHelper(mContext);
|
||||
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
|
||||
// users eventually anyways, but this takes care of it as early as possible.
|
||||
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) {
|
||||
case UserListener.USER_SWITCHED:
|
||||
if (D) {
|
||||
Log.d(TAG, "user " + userId + " current status changed");
|
||||
}
|
||||
case CURRENT_USER_CHANGED:
|
||||
synchronized (mLock) {
|
||||
for (LocationProviderManager manager : mProviderManagers) {
|
||||
manager.onEnabledChangedLocked(userId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UserListener.USER_STARTED:
|
||||
if (D) {
|
||||
Log.d(TAG, "user " + userId + " started");
|
||||
}
|
||||
case USER_STARTED:
|
||||
synchronized (mLock) {
|
||||
for (LocationProviderManager manager : mProviderManagers) {
|
||||
manager.onUserStarted(userId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UserListener.USER_STOPPED:
|
||||
if (D) {
|
||||
Log.d(TAG, "user " + userId + " stopped");
|
||||
}
|
||||
case USER_STOPPED:
|
||||
synchronized (mLock) {
|
||||
for (LocationProviderManager manager : mProviderManagers) {
|
||||
manager.onUserStopped(userId);
|
||||
@@ -957,10 +976,22 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
pw.increaseIndent();
|
||||
|
||||
// for now we only dump for the parent user
|
||||
int userId = mUserInfoHelper.getCurrentUserIds()[0];
|
||||
pw.println("last location=" + mLastLocation.get(userId));
|
||||
pw.println("last coarse location=" + mLastCoarseLocation.get(userId));
|
||||
pw.println("enabled=" + isEnabled(userId));
|
||||
int[] userIds = mUserInfoHelper.getCurrentUserIds();
|
||||
if (userIds.length == 1) {
|
||||
int userId = userIds[0];
|
||||
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);
|
||||
|
||||
@@ -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.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.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.FgThread;
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Provides accessors and listeners for all user info.
|
||||
*/
|
||||
public class UserInfoHelper {
|
||||
public abstract class UserInfoHelper {
|
||||
|
||||
/**
|
||||
* Listener for current user changes.
|
||||
*/
|
||||
public interface UserListener {
|
||||
|
||||
int USER_SWITCHED = 1;
|
||||
int CURRENT_USER_CHANGED = 1;
|
||||
int USER_STARTED = 2;
|
||||
int USER_STOPPED = 3;
|
||||
|
||||
@IntDef({USER_SWITCHED, USER_STARTED, USER_STOPPED})
|
||||
@IntDef({CURRENT_USER_CHANGED, USER_STARTED, USER_STOPPED})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface UserChange {}
|
||||
|
||||
@@ -74,144 +74,102 @@ public class UserInfoHelper {
|
||||
private final Context mContext;
|
||||
private final CopyOnWriteArrayList<UserListener> mListeners;
|
||||
|
||||
@GuardedBy("this")
|
||||
@Nullable private ActivityManagerInternal mActivityManagerInternal;
|
||||
@GuardedBy("this")
|
||||
@Nullable private UserManager mUserManager;
|
||||
|
||||
@UserIdInt private volatile int mCurrentUserId;
|
||||
|
||||
@GuardedBy("this")
|
||||
@UserIdInt private int mCachedParentUserId;
|
||||
@GuardedBy("this")
|
||||
private int[] mCachedProfileUserIds;
|
||||
|
||||
public UserInfoHelper(Context context) {
|
||||
mContext = context;
|
||||
mListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
mCurrentUserId = UserHandle.USER_NULL;
|
||||
mCachedParentUserId = UserHandle.USER_NULL;
|
||||
mCachedProfileUserIds = new int[]{UserHandle.USER_NULL};
|
||||
}
|
||||
|
||||
/** Called when system is ready. */
|
||||
@CallSuper
|
||||
public synchronized void onSystemReady() {
|
||||
if (mUserManager != null) {
|
||||
if (mActivityManagerInternal != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mActivityManagerInternal = Objects.requireNonNull(
|
||||
LocalServices.getService(ActivityManagerInternal.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.
|
||||
*/
|
||||
public void addListener(UserListener listener) {
|
||||
public final void addListener(UserListener listener) {
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for user changed events.
|
||||
*/
|
||||
public void removeListener(UserListener listener) {
|
||||
public final void removeListener(UserListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
private void onCurrentUserChanged(@UserIdInt int newUserId) {
|
||||
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) {
|
||||
protected void dispatchOnUserStarted(@UserIdInt int userId) {
|
||||
if (D) {
|
||||
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) {
|
||||
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) {
|
||||
listener.onUserChanged(userId, change);
|
||||
listener.onUserChanged(userId, USER_STOPPED);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void onUserProfilesChanged() {
|
||||
// this intent is only sent to the current user
|
||||
if (mCachedParentUserId == mCurrentUserId) {
|
||||
mCachedParentUserId = UserHandle.USER_NULL;
|
||||
mCachedProfileUserIds = new int[]{UserHandle.USER_NULL};
|
||||
protected void dispatchOnCurrentUserChanged(@UserIdInt int fromUserId,
|
||||
@UserIdInt int toUserId) {
|
||||
int[] fromUserIds = getProfileIds(fromUserId);
|
||||
int[] toUserIds = getProfileIds(toUserId);
|
||||
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
|
||||
* 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() {
|
||||
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.
|
||||
*/
|
||||
public boolean isCurrentUserId(@UserIdInt int userId) {
|
||||
int currentUserId = mCurrentUserId;
|
||||
return userId == currentUserId || ArrayUtils.contains(
|
||||
getProfileUserIdsForParentUser(currentUserId), userId);
|
||||
}
|
||||
|
||||
@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);
|
||||
synchronized (this) {
|
||||
if (mActivityManagerInternal == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
boolean systemRunning;
|
||||
synchronized (this) {
|
||||
systemRunning = mUserManager != null;
|
||||
}
|
||||
|
||||
if (systemRunning) {
|
||||
int[] currentUserIds = getProfileUserIdsForParentUser(mCurrentUserId);
|
||||
pw.println("current users: " + Arrays.toString(currentUserIds));
|
||||
for (int userId : currentUserIds) {
|
||||
if (mUserManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION,
|
||||
int[] currentUserProfiles = getCurrentUserIds();
|
||||
pw.println("current users: " + Arrays.toString(currentUserProfiles));
|
||||
UserManager userManager = mContext.getSystemService(UserManager.class);
|
||||
if (userManager != null) {
|
||||
for (int userId : currentUserProfiles) {
|
||||
if (userManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION,
|
||||
UserHandle.of(userId))) {
|
||||
pw.println(" u" + userId + " restricted");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pw.println("current user: " + mCurrentUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,34 +16,23 @@
|
||||
package com.android.server.location;
|
||||
|
||||
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 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.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.app.ActivityManagerInternal;
|
||||
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.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
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 org.junit.After;
|
||||
@@ -51,16 +40,18 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
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_MANAGED_ID = 11;
|
||||
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 UserManager mUserManager;
|
||||
@Mock private ActivityManagerInternal mActivityManagerInternal;
|
||||
|
||||
private StaticMockitoSession mMockingSession;
|
||||
private List<BroadcastReceiver> mBroadcastReceivers = new ArrayList<>();
|
||||
|
||||
private UserInfoHelper mHelper;
|
||||
private TestUserInfoHelper mHelper;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mMockingSession = mockitoSession()
|
||||
.initMocks(this)
|
||||
.spyStatic(ActivityManager.class)
|
||||
.strictness(Strictness.WARN)
|
||||
.startMocking();
|
||||
initMocks(this);
|
||||
|
||||
LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInternal);
|
||||
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();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (mMockingSession != null) {
|
||||
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);
|
||||
}
|
||||
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -140,16 +92,21 @@ public class UserInfoHelperTest {
|
||||
UserListener listener = mock(UserListener.class);
|
||||
mHelper.addListener(listener);
|
||||
|
||||
switchUser(USER1_ID);
|
||||
verify(listener, never()).onUserChanged(anyInt(), anyInt());
|
||||
mHelper.dispatchOnCurrentUserChanged(USER1_ID, USER2_ID);
|
||||
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);
|
||||
verify(listener, times(1)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED);
|
||||
verify(listener, times(1)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED);
|
||||
|
||||
switchUser(USER1_ID);
|
||||
verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED);
|
||||
verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED);
|
||||
mHelper.dispatchOnCurrentUserChanged(USER2_ID, USER1_ID);
|
||||
verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.CURRENT_USER_CHANGED);
|
||||
verify(listener, times(2)).onUserChanged(USER2_MANAGED_ID,
|
||||
UserListener.CURRENT_USER_CHANGED);
|
||||
verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.CURRENT_USER_CHANGED);
|
||||
verify(listener, times(2)).onUserChanged(USER1_MANAGED_ID,
|
||||
UserListener.CURRENT_USER_CHANGED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -157,11 +114,11 @@ public class UserInfoHelperTest {
|
||||
UserListener listener = mock(UserListener.class);
|
||||
mHelper.addListener(listener);
|
||||
|
||||
startUser(USER1_ID);
|
||||
mHelper.dispatchOnUserStarted(USER1_ID);
|
||||
verify(listener).onUserChanged(USER1_ID, UserListener.USER_STARTED);
|
||||
|
||||
startUser(USER2_ID);
|
||||
verify(listener).onUserChanged(USER2_ID, UserListener.USER_STARTED);
|
||||
mHelper.dispatchOnUserStarted(USER1_MANAGED_ID);
|
||||
verify(listener).onUserChanged(USER1_MANAGED_ID, UserListener.USER_STARTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -169,24 +126,22 @@ public class UserInfoHelperTest {
|
||||
UserListener listener = mock(UserListener.class);
|
||||
mHelper.addListener(listener);
|
||||
|
||||
stopUser(USER1_ID);
|
||||
verify(listener).onUserChanged(USER1_ID, UserListener.USER_STOPPED);
|
||||
|
||||
stopUser(USER2_ID);
|
||||
mHelper.dispatchOnUserStopped(USER2_ID);
|
||||
verify(listener).onUserChanged(USER2_ID, UserListener.USER_STOPPED);
|
||||
|
||||
mHelper.dispatchOnUserStopped(USER2_MANAGED_ID);
|
||||
verify(listener).onUserChanged(USER2_MANAGED_ID, UserListener.USER_STOPPED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrentUserIds() {
|
||||
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);
|
||||
|
||||
switchUser(USER1_ID);
|
||||
|
||||
assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -196,7 +151,11 @@ public class UserInfoHelperTest {
|
||||
assertThat(mHelper.isCurrentUserId(USER2_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(USER2_ID)).isTrue();
|
||||
|
||||
Reference in New Issue
Block a user