DO NOT MERGE Fix work profile change race condition in LMS am: 9b0b32bd17 am: 2b5034513b
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11707507 Change-Id: I4aa3d4941ce6eb2e790f09709035428443746bf8
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_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);
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user