Merge "Make ModifierShortcutManager multiuser aware." into main
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