Allow related users to show activities on primary user
Make ActivityManager and WindowManager understand related users.
Task stack will now contain interleaved tasks for related users,
but still group regular users separately from groups of related users.
InputMethodManagerService permits related users to invoke IME and receive
key events.
Change-Id: I3bd87b32aec69c3f8d470c8b29b144f4e849c808
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 0b3eb12..6827b3f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -59,6 +59,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -78,6 +79,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
@@ -441,6 +443,10 @@
hideInputMethodMenu();
// No need to updateActive
return;
+ } else if (Intent.ACTION_USER_ADDED.equals(action)
+ || Intent.ACTION_USER_REMOVED.equals(action)) {
+ updateRelatedUserIds();
+ return;
} else {
Slog.w(TAG, "Unexpected intent " + intent);
}
@@ -642,6 +648,8 @@
broadcastFilter.addAction(Intent.ACTION_SCREEN_ON);
broadcastFilter.addAction(Intent.ACTION_SCREEN_OFF);
broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
+ broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
mNotificationShown = false;
@@ -675,6 +683,7 @@
// mSettings should be created before buildInputMethodListLocked
mSettings = new InputMethodSettings(
mRes, context.getContentResolver(), mMethodMap, mMethodList, userId);
+ updateRelatedUserIds();
mFileManager = new InputMethodFileManager(mMethodMap, userId);
mSwitchingController = new InputMethodSubtypeSwitchingController(mSettings);
mSwitchingController.resetCircularListLocked(context);
@@ -790,6 +799,7 @@
private void switchUserLocked(int newUserId) {
mSettings.setCurrentUserId(newUserId);
+ updateRelatedUserIds();
// InputMethodFileManager should be reset when the user is changed
mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
final String defaultImiId = mSettings.getSelectedInputMethod();
@@ -810,6 +820,16 @@
}
}
+ void updateRelatedUserIds() {
+ List<UserInfo> relatedUsers =
+ UserManager.get(mContext).getRelatedUsers(mSettings.getCurrentUserId());
+ int[] relatedUserIds = new int[relatedUsers.size()]; // relatedUsers will not be null
+ for (int i = 0; i < relatedUserIds.length; i++) {
+ relatedUserIds[i] = relatedUsers.get(i).id;
+ }
+ mSettings.setRelatedUserIds(relatedUserIds);
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -905,7 +925,7 @@
+ mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()
+ InputMethodUtils.getApiCallStack());
}
- if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) {
+ if (uid == Process.SYSTEM_UID || mSettings.isRelatedToOrCurrentUser(userId)) {
return true;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 782868e..f39e634 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1015,6 +1015,7 @@
final ActivityThread mSystemThread;
int mCurrentUserId = 0;
+ int[] mRelatedUserIds = new int[0]; // Accessed by ActivityStack
private UserManagerService mUserManager;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -16097,6 +16098,20 @@
return startUser(userId, /* foreground */ false);
}
+ /**
+ * Refreshes the list of users related to the current user when either a
+ * user switch happens or when a new related user is started in the
+ * background.
+ */
+ private void updateRelatedUserIdsLocked() {
+ final List<UserInfo> relatedUsers = getUserManagerLocked().getRelatedUsers(mCurrentUserId);
+ int[] relatedUserIds = new int[relatedUsers.size()]; // relatedUsers will not be null
+ for (int i = 0; i < relatedUserIds.length; i++) {
+ relatedUserIds[i] = relatedUsers.get(i).id;
+ }
+ mRelatedUserIds = relatedUserIds;
+ }
+
@Override
public boolean switchUser(final int userId) {
return startUser(userId, /* foregound */ true);
@@ -16150,12 +16165,15 @@
if (foreground) {
mCurrentUserId = userId;
- mWindowManager.setCurrentUser(userId);
+ updateRelatedUserIdsLocked();
+ mWindowManager.setCurrentUser(userId, mRelatedUserIds);
// Once the internal notion of the active user has switched, we lock the device
// with the option to show the user switcher on the keyguard.
mWindowManager.lockNow(null);
} else {
final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId);
+ updateRelatedUserIdsLocked();
+ mWindowManager.updateRelatedUserIds(mRelatedUserIds);
mUserLru.remove(currentUserIdInt);
mUserLru.add(currentUserIdInt);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 922cef4..f3ccdd6 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -341,8 +341,19 @@
mCurrentUser = mService.mCurrentUserId;
}
- boolean okToShow(ActivityRecord r) {
- return r.userId == mCurrentUser
+ /**
+ * Checks whether the userid is either the current user or a related user.
+ */
+ private boolean isRelatedToOrCurrentUserLocked(int userId) {
+ if (mCurrentUser == userId) return true;
+ for (int i = 0; i < mService.mRelatedUserIds.length; i++) {
+ if (mService.mRelatedUserIds[i] == userId) return true;
+ }
+ return false;
+ }
+
+ boolean okToShowLocked(ActivityRecord r) {
+ return isRelatedToOrCurrentUserLocked(r.userId)
|| (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0;
}
@@ -362,7 +373,7 @@
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = activities.get(activityNdx);
- if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) {
+ if (!r.finishing && !r.delayedResume && r != notTop && okToShowLocked(r)) {
return r;
}
}
@@ -389,7 +400,7 @@
for (int i = activities.size() - 1; i >= 0; --i) {
final ActivityRecord r = activities.get(i);
// Note: the taskId check depends on real taskId fields being non-zero
- if (!r.finishing && (token != r.appToken) && okToShow(r)) {
+ if (!r.finishing && (token != r.appToken) && okToShowLocked(r)) {
return r;
}
}
@@ -542,7 +553,7 @@
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
TaskRecord task = mTaskHistory.get(taskNdx);
- if (task.userId != mCurrentUser) {
+ if (!isRelatedToOrCurrentUserLocked(task.userId)) {
return null;
}
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -573,7 +584,7 @@
int index = mTaskHistory.size();
for (int i = 0; i < index; ) {
TaskRecord task = mTaskHistory.get(i);
- if (task.userId == userId) {
+ if (isRelatedToOrCurrentUserLocked(task.userId)) {
if (DEBUG_TASKS) Slog.d(TAG, "switchUserLocked: stack=" + getStackId() +
" moving " + task + " to top");
mTaskHistory.remove(i);
@@ -1728,10 +1739,10 @@
mTaskHistory.remove(task);
// Now put task at top.
int stackNdx = mTaskHistory.size();
- if (task.userId != mCurrentUser) {
+ if (!isRelatedToOrCurrentUserLocked(task.userId)) {
// Put non-current user tasks below current user tasks.
while (--stackNdx >= 0) {
- if (mTaskHistory.get(stackNdx).userId != mCurrentUser) {
+ if (!isRelatedToOrCurrentUserLocked(mTaskHistory.get(stackNdx).userId)) {
break;
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 9740812..3a43521 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -153,7 +153,7 @@
ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = mActivities.get(activityNdx);
- if (!r.finishing && r != notTop && stack.okToShow(r)) {
+ if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
return r;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c006613..be94ca7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -19,7 +19,6 @@
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-
import android.app.AppOpsManager;
import android.util.ArraySet;
import android.util.TimeUtils;
@@ -302,8 +301,16 @@
}
};
- // Current user when multi-user is enabled. Don't show windows of non-current user.
+ /**
+ * Current user when multi-user is enabled. Don't show windows of
+ * non-current user. Also see mRelatedUserIds.
+ */
int mCurrentUserId;
+ /**
+ * Users related to the current user. These are also allowed to show windows
+ * on the current user.
+ */
+ int[] mRelatedUserIds = new int[0];
final Context mContext;
@@ -5284,10 +5291,16 @@
mPolicy.setTouchExplorationEnabled(enabled);
}
- public void setCurrentUser(final int newUserId) {
+ public void updateRelatedUserIds(final int[] relatedUserIds) {
synchronized (mWindowMap) {
- int oldUserId = mCurrentUserId;
+ mRelatedUserIds = relatedUserIds;
+ }
+ }
+
+ public void setCurrentUser(final int newUserId, final int[] relatedUserIds) {
+ synchronized (mWindowMap) {
mCurrentUserId = newUserId;
+ mRelatedUserIds = relatedUserIds;
mAppTransition.setCurrentUser(newUserId);
mPolicy.setCurrentUserLw(newUserId);
@@ -5302,6 +5315,15 @@
}
}
+ /* Called by WindowState */
+ boolean isRelatedToOrCurrentUserLocked(int userId) {
+ if (userId == mCurrentUserId) return true;
+ for (int i = 0; i < mRelatedUserIds.length; i++) {
+ if (mRelatedUserIds[i] == userId) return true;
+ }
+ return false;
+ }
+
public void enableScreenAfterBoot() {
synchronized(mWindowMap) {
if (DEBUG_BOOT) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a8e45c4..2c0e99e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1249,7 +1249,7 @@
}
return win.mShowToOwnerOnly
- && UserHandle.getUserId(win.mOwnerUid) != mService.mCurrentUserId;
+ && !mService.isRelatedToOrCurrentUserLocked(UserHandle.getUserId(win.mOwnerUid));
}
private static void applyInsets(Region outRegion, Rect frame, Rect inset) {