ShortcutManaegr: bug fixes

Fixes 28590035
Fixes 28586105

Change-Id: Ia11d29e4996ad5b47b279a3c04d9586695cdc0a5
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 824722d..eaec1d7 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -243,7 +243,9 @@
         }
 
         /**
-         * If non-null, returns only shortcuts associated with the activity.
+         * If non-null, returns only shortcuts associated with the activity, which are
+         * {@link ShortcutInfo}s that have null {@link ShortcutInfo#getActivityComponent()}, or
+         * {@link ShortcutInfo#getActivityComponent()} equals to {@code activity}.
          */
         public void setActivity(@Nullable ComponentName activity) {
             mActivity = activity;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 0f17804..cf418b9 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1256,8 +1256,13 @@
     }
 
     private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
-        if (!mUserManager.isUserRunning(userId)) {
-            return;
+        final long token = injectClearCallingIdentity();
+        try {
+            if (!mUserManager.isUserRunning(userId)) {
+                return;
+            }
+        } finally {
+            injectRestoreCallingIdentity(token);
         }
         postToHandler(() -> {
             final ArrayList<ShortcutChangeListener> copy;
@@ -1771,9 +1776,11 @@
                         if (ids != null && !ids.contains(si.getId())) {
                             return false;
                         }
-                        if (componentName != null
-                                && !componentName.equals(si.getActivityComponent())) {
-                            return false;
+                        if (componentName != null) {
+                            if (si.getActivityComponent() != null
+                                    && !si.getActivityComponent().equals(componentName)) {
+                                return false;
+                            }
                         }
                         final boolean matchDynamic =
                                 ((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index ced7cf0..b6ca4dd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -115,6 +115,8 @@
 
 import org.junit.Assert;
 import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
@@ -583,7 +585,7 @@
         }).when(mMockUserManager).getUserInfo(eq(USER_P0));
 
         // User 0 is always running.
-        when(mMockUserManager.isUserRunning(eq(USER_0))).thenReturn(true);
+        when(mMockUserManager.isUserRunning(eq(USER_0))).thenAnswer(new AnswerIsUserRunning(true));
 
         initService();
         setCaller(CALLING_PACKAGE_1);
@@ -594,6 +596,24 @@
         mInternal.onSystemLocaleChangedNoLock();
     }
 
+    /**
+     * Returns a boolean but also checks if the current UID is SYSTEM_UID.
+     */
+    private class AnswerIsUserRunning implements Answer<Boolean> {
+        private final boolean mAnswer;
+
+        private AnswerIsUserRunning(boolean answer) {
+            mAnswer = answer;
+        }
+
+        @Override
+        public Boolean answer(InvocationOnMock invocation) throws Throwable {
+            assertEquals("isUserRunning() must be called on SYSTEM UID.",
+                    Process.SYSTEM_UID, mInjectedCallingUid);
+            return mAnswer;
+        }
+    }
+
     private static UserInfo withProfileGroupId(UserInfo in, int groupId) {
         in.profileGroupId = groupId;
         return in;
@@ -872,6 +892,18 @@
     }
 
     /**
+     * Make a shortcut with an ID, a timestamp and an activity component
+     */
+    private ShortcutInfo makeShortcutWithTimestampWithActivity(String id, long timestamp,
+            ComponentName activity) {
+        final ShortcutInfo s = makeShortcut(
+                id, "Title-" + id, activity, /* icon =*/ null,
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
+        s.setTimestamp(timestamp);
+        return s;
+    }
+
+    /**
      * Make a shortcut with an ID and icon.
      */
     private ShortcutInfo makeShortcutWithIcon(String id, Icon icon) {
@@ -2014,8 +2046,10 @@
 
         setCaller(CALLING_PACKAGE_2);
         final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
-        final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
-        final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
+        final ShortcutInfo s2_3 = makeShortcutWithTimestampWithActivity("s3", 3000,
+                makeComponent(ShortcutActivity2.class));
+        final ShortcutInfo s2_4 = makeShortcutWithTimestampWithActivity("s4", 500,
+                makeComponent(ShortcutActivity.class));
         assertTrue(mManager.setDynamicShortcuts(list(s2_2, s2_3, s2_4)));
 
         setCaller(CALLING_PACKAGE_3);
@@ -2056,6 +2090,15 @@
                         getCallingUser())),
                 "s2", "s3"))));
 
+        // Filter by activity
+        assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+                assertAllNotKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+                        /* time =*/ 0, CALLING_PACKAGE_2,
+                        new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+                        ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC),
+                        getCallingUser())),
+                "s2", "s4"))));
+
         // With ID.
         assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
                 assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
@@ -2115,7 +2158,7 @@
                     /* activity =*/ null, /* flags */ 0), getCallingUser());
         });
 
-        // TODO More tests: pinned but dynamic, filter by activity
+        // TODO More tests: pinned but dynamic.
     }
 
     public void testGetShortcutInfo() {
@@ -3302,9 +3345,8 @@
         assertCallbackNotReceived(c11_1);
 
         // Work profile, now running.
-
-        when(mMockUserManager.isUserRunning(anyInt())).thenReturn(false);
-        when(mMockUserManager.isUserRunning(eq(USER_P0))).thenReturn(true);
+        doAnswer(new AnswerIsUserRunning(false)).when(mMockUserManager).isUserRunning(anyInt());
+        doAnswer(new AnswerIsUserRunning(true)).when(mMockUserManager).isUserRunning(eq(USER_P0));
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -3323,8 +3365,8 @@
 
         // Normal secondary user.
 
-        when(mMockUserManager.isUserRunning(anyInt())).thenReturn(false);
-        when(mMockUserManager.isUserRunning(eq(USER_10))).thenReturn(true);
+        doAnswer(new AnswerIsUserRunning(false)).when(mMockUserManager).isUserRunning(anyInt());
+        doAnswer(new AnswerIsUserRunning(true)).when(mMockUserManager).isUserRunning(eq(USER_10));
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4377,7 +4419,7 @@
         // notification to the launcher.
         mInjectedCurrentTimeLillis = START_TIME + 200;
 
-        when(mMockUserManager.isUserRunning(eq(USER_10))).thenReturn(true);
+        doAnswer(new AnswerIsUserRunning(true)).when(mMockUserManager).isUserRunning(eq(USER_10));
 
         reset(c0);
         reset(c10);