Make ModifierShortcutManager multiuser aware.
Instead of using the cached system user context, we now keep
track of the current foreground user (maintained by PhoneWindowManager)
and use that user to resolve Roles and PackageManager queries. This
means that we will resolve the correct application launch intents for
the appropriate user.
This is especially important in HSUM builds, where the system user is
never the foreground user and so roles would never resolve correctly.
Flag: com.android.server.flags.modifier_shortcut_manager_multiuser
Bug: 351963350
Test: atest ModifierShortcutManagerTests ModifierShortcutTests
Change-Id: I98d70ebd1f067d27df52275bf82517fab1473ec8
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 9b64488..40b2ff9 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -16,6 +16,8 @@
package com.android.server.policy;
+import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
+
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.role.RoleManager;
@@ -82,6 +84,10 @@
private final SparseArray<String> mRoleShortcuts = new SparseArray<String>();
private final SparseArray<String> mShiftRoleShortcuts = new SparseArray<String>();
private final Map<String, Intent> mRoleIntents = new HashMap<String, Intent>();
+ private final SparseArray<ComponentName> mComponentShortcuts = new SparseArray<>();
+ private final SparseArray<ComponentName> mShiftComponentShortcuts = new SparseArray<>();
+ private final Map<ComponentName, Intent> mComponentIntents =
+ new HashMap<ComponentName, Intent>();
private LongSparseArray<IShortcutService> mShortcutKeyServices = new LongSparseArray<>();
@@ -115,23 +121,31 @@
private final Context mContext;
private final Handler mHandler;
- private final RoleManager mRoleManager;
- private final PackageManager mPackageManager;
private boolean mSearchKeyShortcutPending = false;
private boolean mConsumeSearchKeyUp = true;
+ private UserHandle mCurrentUser;
- ModifierShortcutManager(Context context, Handler handler) {
+ ModifierShortcutManager(Context context, Handler handler, UserHandle currentUser) {
mContext = context;
mHandler = handler;
- mPackageManager = mContext.getPackageManager();
- mRoleManager = mContext.getSystemService(RoleManager.class);
- mRoleManager.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(),
+ RoleManager rm = mContext.getSystemService(RoleManager.class);
+ rm.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(),
(String roleName, UserHandle user) -> {
mRoleIntents.remove(roleName);
}, UserHandle.ALL);
+ mCurrentUser = currentUser;
loadShortcuts();
}
+ void setCurrentUser(UserHandle newUser) {
+ mCurrentUser = newUser;
+
+ // Role based shortcuts may resolve to different apps for different users
+ // so clear the cache.
+ mRoleIntents.clear();
+ mComponentIntents.clear();
+ }
+
/**
* Gets the shortcut intent for a given keycode+modifier. Make sure you
* strip whatever modifier is used for invoking shortcuts (for example,
@@ -147,9 +161,11 @@
* to invoke the shortcut.
* @return The intent that matches the shortcut, or null if not found.
*/
+ @Nullable
private Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
// If a modifier key other than shift is also pressed, skip it.
- final boolean isShiftOn = KeyEvent.metaStateHasModifiers(metaState, KeyEvent.META_SHIFT_ON);
+ final boolean isShiftOn = KeyEvent.metaStateHasModifiers(
+ metaState, KeyEvent.META_SHIFT_ON);
if (!isShiftOn && !KeyEvent.metaStateHasNoModifiers(metaState)) {
return null;
}
@@ -161,37 +177,54 @@
// First try the exact keycode (with modifiers).
int shortcutChar = kcm.get(keyCode, metaState);
- if (shortcutChar != 0) {
+ if (shortcutChar == 0) {
+ return null;
+ }
+ shortcutIntent = shortcutMap.get(shortcutChar);
+
+ if (shortcutIntent == null) {
+ // Next try the primary character on that key.
+ shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
+ if (shortcutChar == 0) {
+ return null;
+ }
shortcutIntent = shortcutMap.get(shortcutChar);
}
- // Next try the primary character on that key.
if (shortcutIntent == null) {
- shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
- if (shortcutChar != 0) {
- shortcutIntent = shortcutMap.get(shortcutChar);
-
- if (shortcutIntent == null) {
- // Check for role based shortcut
- String role = isShiftOn ? mShiftRoleShortcuts.get(shortcutChar)
- : mRoleShortcuts.get(shortcutChar);
- if (role != null) {
- shortcutIntent = getRoleLaunchIntent(role);
- }
- }
+ // Next check for role based shortcut with primary character.
+ String role = isShiftOn ? mShiftRoleShortcuts.get(shortcutChar)
+ : mRoleShortcuts.get(shortcutChar);
+ if (role != null) {
+ shortcutIntent = getRoleLaunchIntent(role);
}
}
+ if (modifierShortcutManagerMultiuser()) {
+ if (shortcutIntent == null) {
+ // Next check component based shortcuts with primary character.
+ ComponentName component = isShiftOn
+ ? mShiftComponentShortcuts.get(shortcutChar)
+ : mComponentShortcuts.get(shortcutChar);
+ if (component != null) {
+ shortcutIntent = resolveComponentNameIntent(component);
+ }
+ }
+ }
return shortcutIntent;
}
private Intent getRoleLaunchIntent(String role) {
Intent intent = mRoleIntents.get(role);
if (intent == null) {
- if (mRoleManager.isRoleAvailable(role)) {
- String rolePackage = mRoleManager.getDefaultApplication(role);
+ Context context = modifierShortcutManagerMultiuser()
+ ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+ RoleManager rm = context.getSystemService(RoleManager.class);
+ PackageManager pm = context.getPackageManager();
+ if (rm.isRoleAvailable(role)) {
+ String rolePackage = rm.getDefaultApplication(role);
if (rolePackage != null) {
- intent = mPackageManager.getLaunchIntentForPackage(rolePackage);
+ intent = pm.getLaunchIntentForPackage(rolePackage);
if (intent != null) {
intent.putExtra(EXTRA_ROLE, role);
mRoleIntents.put(role, intent);
@@ -249,7 +282,17 @@
+ " className=" + className + " shortcutChar=" + shortcutChar);
continue;
}
- intent = resolveComponentNameIntent(packageName, className);
+ if (modifierShortcutManagerMultiuser()) {
+ ComponentName componentName = new ComponentName(packageName, className);
+ if (isShiftShortcut) {
+ mShiftComponentShortcuts.put(shortcutChar, componentName);
+ } else {
+ mComponentShortcuts.put(shortcutChar, componentName);
+ }
+ continue;
+ } else {
+ intent = resolveComponentNameIntent(packageName, className);
+ }
} else if (categoryName != null) {
if (roleName != null) {
Log.w(TAG, "Cannot specify role bookmark when category is present for"
@@ -288,19 +331,37 @@
}
@Nullable
+ private Intent resolveComponentNameIntent(ComponentName componentName) {
+ Intent intent = mComponentIntents.get(componentName);
+ if (intent == null) {
+ intent = resolveComponentNameIntent(
+ componentName.getPackageName(), componentName.getClassName());
+ if (intent != null) {
+ mComponentIntents.put(componentName, intent);
+ }
+ }
+ return intent;
+ }
+
+ @Nullable
private Intent resolveComponentNameIntent(String packageName, String className) {
- int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+ Context context = modifierShortcutManagerMultiuser()
+ ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+ PackageManager pm = context.getPackageManager();
+ int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ if (!modifierShortcutManagerMultiuser()) {
+ flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+ }
ComponentName componentName = new ComponentName(packageName, className);
try {
- mPackageManager.getActivityInfo(componentName, flags);
+ pm.getActivityInfo(componentName, flags);
} catch (PackageManager.NameNotFoundException e) {
- String[] packages = mPackageManager.canonicalToCurrentPackageNames(
+ String[] packages = pm.canonicalToCurrentPackageNames(
new String[] { packageName });
componentName = new ComponentName(packages[0], className);
try {
- mPackageManager.getActivityInfo(componentName, flags);
+ pm.getActivityInfo(componentName, flags);
} catch (PackageManager.NameNotFoundException e1) {
Log.w(TAG, "Unable to add bookmark: " + packageName
+ "/" + className + " not found.");
@@ -399,7 +460,11 @@
if (intent != null) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ if (modifierShortcutManagerMultiuser()) {
+ mContext.startActivityAsUser(intent, mCurrentUser);
+ } else {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
} catch (ActivityNotFoundException ex) {
Slog.w(TAG, "Dropping application launch key because "
+ "the activity to which it is registered was not found: "
@@ -417,7 +482,11 @@
if (shortcutIntent != null) {
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- mContext.startActivityAsUser(shortcutIntent, UserHandle.CURRENT);
+ if (modifierShortcutManagerMultiuser()) {
+ mContext.startActivityAsUser(shortcutIntent, mCurrentUser);
+ } else {
+ mContext.startActivityAsUser(shortcutIntent, UserHandle.CURRENT);
+ }
} catch (ActivityNotFoundException ex) {
Slog.w(TAG, "Dropping shortcut key combination because "
+ "the activity to which it is registered was not found: "
@@ -526,6 +595,30 @@
}
}
+ if (modifierShortcutManagerMultiuser()) {
+ for (int i = 0; i < mComponentShortcuts.size(); i++) {
+ ComponentName component = mComponentShortcuts.valueAt(i);
+ KeyboardShortcutInfo info = shortcutInfoFromIntent(
+ (char) (mComponentShortcuts.keyAt(i)),
+ resolveComponentNameIntent(component),
+ false);
+ if (info != null) {
+ shortcuts.add(info);
+ }
+ }
+
+ for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
+ ComponentName component = mShiftComponentShortcuts.valueAt(i);
+ KeyboardShortcutInfo info = shortcutInfoFromIntent(
+ (char) (mShiftComponentShortcuts.keyAt(i)),
+ resolveComponentNameIntent(component),
+ true);
+ if (info != null) {
+ shortcuts.add(info);
+ }
+ }
+ }
+
return new KeyboardShortcutGroup(
mContext.getString(R.string.keyboard_shortcut_group_applications),
shortcuts);
@@ -548,23 +641,26 @@
CharSequence label;
Icon icon;
+ Context context = modifierShortcutManagerMultiuser()
+ ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+ PackageManager pm = context.getPackageManager();
ActivityInfo resolvedActivity = intent.resolveActivityInfo(
- mPackageManager, PackageManager.MATCH_DEFAULT_ONLY);
+ pm, PackageManager.MATCH_DEFAULT_ONLY);
if (resolvedActivity == null) {
return null;
}
boolean isResolver = com.android.internal.app.ResolverActivity.class.getName().equals(
resolvedActivity.name);
if (isResolver) {
- label = getIntentCategoryLabel(mContext,
+ label = getIntentCategoryLabel(context,
intent.getSelector().getCategories().iterator().next());
if (label == null) {
return null;
}
- icon = Icon.createWithResource(mContext, R.drawable.sym_def_app_icon);
+ icon = Icon.createWithResource(context, R.drawable.sym_def_app_icon);
} else {
- label = resolvedActivity.loadLabel(mPackageManager);
+ label = resolvedActivity.loadLabel(pm);
icon = Icon.createWithResource(
resolvedActivity.packageName, resolvedActivity.getIconResource());
}
@@ -609,5 +705,4 @@
}
return context.getString(resid);
};
-
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index df97313..7534bfe 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -76,6 +76,7 @@
import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;
import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable;
+import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
@@ -2281,7 +2282,8 @@
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
- mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler);
+ mModifierShortcutManager = new ModifierShortcutManager(
+ mContext, mHandler, UserHandle.of(mCurrentUserId));
mUiMode = mContext.getResources().getInteger(
com.android.internal.R.integer.config_defaultUiModeType);
mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
@@ -6506,6 +6508,9 @@
if (statusBar != null) {
statusBar.setCurrentUser(newUserId);
}
+ if (modifierShortcutManagerMultiuser()) {
+ mModifierShortcutManager.setCurrentUser(UserHandle.of(newUserId));
+ }
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index 5533ff9..50041d0 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -39,6 +39,7 @@
import android.content.res.XmlResourceParser;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserHandle;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
@@ -77,6 +78,7 @@
XmlResourceParser testBookmarks = mResources.getXml(
com.android.frameworks.wmtests.R.xml.bookmarks);
+ doReturn(mContext).when(mContext).createContextAsUser(anyObject(), anyInt());
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mResources.getXml(R.xml.bookmarks)).thenReturn(testBookmarks);
@@ -98,7 +100,8 @@
.canonicalToCurrentPackageNames(aryEq(new String[] { "com.test2" }));
- mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler);
+ mModifierShortcutManager = new ModifierShortcutManager(
+ mContext, mHandler, UserHandle.SYSTEM);
}
@Test