ShortcutManagerTest8.java revision 7f7372ae2abeecb564d426ec3efbfa039c797885
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.pm;
17
18import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
19import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertForLauncherCallbackNoThrow;
20import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
21import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
22
23import static org.mockito.Matchers.any;
24import static org.mockito.Matchers.eq;
25import static org.mockito.Matchers.isNull;
26import static org.mockito.Matchers.notNull;
27import static org.mockito.Mockito.reset;
28import static org.mockito.Mockito.times;
29import static org.mockito.Mockito.verify;
30
31import android.annotation.Nullable;
32import android.app.PendingIntent;
33import android.content.ComponentName;
34import android.content.Intent;
35import android.content.IntentSender;
36import android.content.pm.LauncherApps;
37import android.content.pm.LauncherApps.PinItemRequest;
38import android.content.pm.ShortcutInfo;
39import android.content.pm.ShortcutManager;
40import android.graphics.drawable.Icon;
41import android.os.UserHandle;
42import android.test.MoreAsserts;
43import android.test.suitebuilder.annotation.SmallTest;
44import android.util.Log;
45import android.util.Pair;
46
47import com.android.frameworks.servicestests.R;
48
49import org.mockito.ArgumentCaptor;
50
51/**
52 * Tests for {@link ShortcutManager#requestPinShortcut} and relevant APIs.
53 *
54 m FrameworksServicesTests &&
55 adb install \
56 -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
57 adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest8 \
58 -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
59
60 * TODO for CTS
61 * - Foreground check.
62 * - Reading icons from requested shortcuts.
63 * - Invalid pre-approved token.
64 */
65@SmallTest
66public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
67    private ShortcutRequestPinProcessor mProcessor;
68
69    @Override
70    protected void initService() {
71        super.initService();
72        mProcessor = mService.getShortcutRequestPinProcessorForTest();
73    }
74
75    @Override
76    protected void setCaller(String packageName, int userId) {
77        super.setCaller(packageName, userId);
78
79        // Note during this test, assume all callers are in the foreground by default.
80        makeCallerForeground();
81    }
82
83    public void testGetParentOrSelfUserId() {
84        assertEquals(USER_0, mService.getParentOrSelfUserId(USER_0));
85        assertEquals(USER_10, mService.getParentOrSelfUserId(USER_10));
86        assertEquals(USER_11, mService.getParentOrSelfUserId(USER_11));
87        assertEquals(USER_0, mService.getParentOrSelfUserId(USER_P0));
88    }
89
90    public void testIsRequestPinShortcutSupported() {
91        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
92        setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10));
93
94        Pair<ComponentName, Integer> actual;
95        // User 0
96        actual = mProcessor.getRequestPinConfirmationActivity(USER_0,
97                PinItemRequest.REQUEST_TYPE_SHORTCUT);
98
99        assertEquals(LAUNCHER_1, actual.first.getPackageName());
100        assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
101        assertEquals(USER_0, (int) actual.second);
102
103        // User 10
104        actual = mProcessor.getRequestPinConfirmationActivity(USER_10,
105                PinItemRequest.REQUEST_TYPE_SHORTCUT);
106
107        assertEquals(LAUNCHER_2, actual.first.getPackageName());
108        assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
109        assertEquals(USER_10, (int) actual.second);
110
111        // User P0 -> managed profile, return user-0's launcher.
112        actual = mProcessor.getRequestPinConfirmationActivity(USER_P0,
113                PinItemRequest.REQUEST_TYPE_SHORTCUT);
114
115        assertEquals(LAUNCHER_1, actual.first.getPackageName());
116        assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
117        assertEquals(USER_0, (int) actual.second);
118
119        // Check from the public API.
120        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
121            assertTrue(mManager.isRequestPinShortcutSupported());
122        });
123        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
124            assertTrue(mManager.isRequestPinShortcutSupported());
125        });
126        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
127            assertTrue(mManager.isRequestPinShortcutSupported());
128        });
129        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
130            assertTrue(mManager.isRequestPinShortcutSupported());
131        });
132
133        // Now, USER_0's launcher no longer has a confirm activity.
134        mPinConfirmActivityFetcher = (packageName, userId) ->
135                !LAUNCHER_2.equals(packageName)
136                        ? null : new ComponentName(packageName, PIN_CONFIRM_ACTIVITY_CLASS);
137
138        // User 10 -- still has confirm activity.
139        actual = mProcessor.getRequestPinConfirmationActivity(USER_10,
140                PinItemRequest.REQUEST_TYPE_SHORTCUT);
141
142        assertEquals(LAUNCHER_2, actual.first.getPackageName());
143        assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
144        assertEquals(USER_10, (int) actual.second);
145
146        // But user-0 and user p0 no longer has a confirmation activity.
147        assertNull(mProcessor.getRequestPinConfirmationActivity(USER_0,
148                PinItemRequest.REQUEST_TYPE_SHORTCUT));
149        assertNull(mProcessor.getRequestPinConfirmationActivity(USER_P0,
150                PinItemRequest.REQUEST_TYPE_SHORTCUT));
151
152        // Check from the public API.
153        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
154            assertFalse(mManager.isRequestPinShortcutSupported());
155        });
156        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
157            assertFalse(mManager.isRequestPinShortcutSupported());
158        });
159        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
160            assertTrue(mManager.isRequestPinShortcutSupported());
161        });
162        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
163            assertFalse(mManager.isRequestPinShortcutSupported());
164        });
165    }
166
167    public void testRequestPinShortcut_notSupported() {
168        // User-0's launcher has no confirmation activity.
169        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
170
171        mPinConfirmActivityFetcher = (packageName, userId) ->
172                !LAUNCHER_2.equals(packageName)
173                        ? null : new ComponentName(packageName, PIN_CONFIRM_ACTIVITY_CLASS);
174
175        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
176            ShortcutInfo s1 = makeShortcut("s1");
177
178            assertFalse(mManager.requestPinShortcut(s1,
179                    /*PendingIntent=*/ null));
180
181            verify(mServiceContext, times(0))
182                    .startActivityAsUser(any(Intent.class), any(UserHandle.class));
183            verify(mServiceContext, times(0))
184                    .sendIntentSender(any(IntentSender.class));
185        });
186
187        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
188            ShortcutInfo s1 = makeShortcut("s1");
189
190            assertFalse(mManager.requestPinShortcut(s1,
191                    /*PendingIntent=*/ null));
192
193            verify(mServiceContext, times(0))
194                    .startActivityAsUser(any(Intent.class), any(UserHandle.class));
195            verify(mServiceContext, times(0))
196                    .sendIntentSender(any(IntentSender.class));
197        });
198
199        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
200            ShortcutInfo s1 = makeShortcut("s1");
201
202            assertFalse(mManager.requestPinShortcut(s1,
203                    /*PendingIntent=*/ null));
204
205            verify(mServiceContext, times(0))
206                    .startActivityAsUser(any(Intent.class), any(UserHandle.class));
207            verify(mServiceContext, times(0))
208                    .sendIntentSender(any(IntentSender.class));
209        });
210    }
211
212    private void assertPinItemRequestIntent(Intent actualIntent, String expectedPackage) {
213        assertEquals(LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT, actualIntent.getAction());
214        assertEquals(expectedPackage, actualIntent.getComponent().getPackageName());
215        assertEquals(PIN_CONFIRM_ACTIVITY_CLASS,
216                actualIntent.getComponent().getClassName());
217        assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK,
218                actualIntent.getFlags());
219    }
220
221    public void testNotForeground() {
222        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
223
224        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
225            makeCallerBackground();
226
227            assertExpectException(IllegalStateException.class, "foreground activity", () -> {
228                assertTrue(mManager.requestPinShortcut(makeShortcut("s1"),
229                        /* resultIntent= */ null));
230            });
231
232            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
233            verify(mServiceContext, times(0)).startActivityAsUser(
234                    any(Intent.class), any(UserHandle.class));
235        });
236    }
237
238    private void assertPinItemRequest(PinItemRequest actualRequest) {
239        assertNotNull(actualRequest);
240        assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, actualRequest.getRequestType());
241
242        Log.i(TAG, "Requested shortcut: " + actualRequest.getShortcutInfo().toInsecureString());
243    }
244
245    /**
246     * Basic flow:
247     * - Launcher supports the feature.
248     * - Shortcut doesn't pre-exist.
249     */
250    private void checkRequestPinShortcut(@Nullable IntentSender resultIntent) {
251        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
252        setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10));
253
254        final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
255
256        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
257            /// Create a shortcut with no target activity.
258            final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext, "s1")
259                    .setShortLabel("Title-" + "s1")
260                    .setIcon(res32x32)
261                    .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class));
262            final ShortcutInfo s = b.build();
263
264            assertNull(s.getActivity());
265
266            assertTrue(mManager.requestPinShortcut(s, resultIntent));
267
268            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
269
270            // Shortcut shouldn't be registered yet.
271            assertWith(getCallerShortcuts())
272                    .isEmpty();
273        });
274
275        runWithCaller(LAUNCHER_1, USER_0, () -> {
276            // Check the intent passed to startActivityAsUser().
277            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
278
279            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
280
281            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
282
283            // Check the request object.
284            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
285
286            assertPinItemRequest(request);
287
288            assertWith(request.getShortcutInfo())
289                    .haveIds("s1")
290                    .areAllOrphan()
291                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, MAIN_ACTIVITY_CLASS))
292                    .areAllWithNoIntent();
293
294            assertAllHaveIcon(list(request.getShortcutInfo()));
295
296            // Accept the request.
297            assertForLauncherCallbackNoThrow(mLauncherApps,
298                    () -> assertTrue(request.accept()))
299                    .assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_P0)
300                    .haveIds("s1");
301        });
302
303        // This method is always called, even with PI == null.
304        if (resultIntent == null) {
305            verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
306        } else {
307            verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
308        }
309
310        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
311            assertWith(getCallerShortcuts())
312                    .haveIds("s1")
313                    .areAllNotDynamic()
314                    .areAllEnabled()
315                    .areAllPinned()
316                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, MAIN_ACTIVITY_CLASS))
317                    .areAllWithIntent();
318        });
319    }
320
321    public void testRequestPinShortcut() {
322        checkRequestPinShortcut(/* resultIntent=*/ null);
323    }
324
325    private IntentSender makeResultIntent() {
326        return PendingIntent.getActivity(getTestContext(), 0, new Intent(), 0).getIntentSender();
327    }
328
329    public void testRequestPinShortcut_withCallback() {
330        checkRequestPinShortcut(makeResultIntent());
331    }
332
333    public void testRequestPinShortcut_explicitTargetActivity() {
334        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
335        setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10));
336
337        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
338            ShortcutInfo s1 = makeShortcutWithActivity("s1",
339                    new ComponentName(CALLING_PACKAGE_1, "different_activity"));
340
341            assertTrue(mManager.requestPinShortcut(s1, null));
342
343            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
344
345            // Shortcut shouldn't be registered yet.
346            assertWith(getCallerShortcuts())
347                    .isEmpty();
348        });
349
350        runWithCaller(LAUNCHER_1, USER_0, () -> {
351            // Check the intent passed to startActivityAsUser().
352            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
353
354            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
355
356            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
357
358            // Check the request object.
359            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
360
361            assertPinItemRequest(request);
362
363            assertWith(request.getShortcutInfo())
364                    .haveIds("s1")
365                    .areAllOrphan()
366                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "different_activity"))
367                    .areAllWithNoIntent();
368
369            // Accept the request.
370            assertForLauncherCallbackNoThrow(mLauncherApps,
371                    () -> assertTrue(request.accept()))
372                    .assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_P0)
373                    .haveIds("s1");
374        });
375
376        verify(mServiceContext, times(1)).sendIntentSender(eq(null));
377
378        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
379            assertWith(getCallerShortcuts())
380                    .haveIds("s1")
381                    .areAllNotDynamic()
382                    .areAllEnabled()
383                    .areAllPinned()
384                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "different_activity"))
385                    .areAllWithIntent();
386        });
387    }
388
389    public void testRequestPinShortcut_wrongTargetActivity() {
390        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
391
392        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
393            // Create dynamic shortcut
394            ShortcutInfo s1 = makeShortcutWithActivity("s1",
395                    new ComponentName("wrong_package", "different_activity"));
396
397            assertExpectException(IllegalStateException.class, "not belong to package", () -> {
398                assertTrue(mManager.requestPinShortcut(s1, /* resultIntent=*/ null));
399            });
400
401            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
402            verify(mServiceContext, times(0)).startActivityAsUser(
403                    any(Intent.class), any(UserHandle.class));
404        });
405    }
406
407    public void testRequestPinShortcut_noTargetActivity_noMainActivity() {
408        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
409        setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10));
410
411        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
412            /// Create a shortcut with no target activity.
413            final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext, "s1")
414                    .setShortLabel("Title-" + "s1")
415                    .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class));
416            final ShortcutInfo s = b.build();
417
418            assertNull(s.getActivity());
419
420            // Caller has no main activity.
421            mMainActivityFetcher = (packageName, userId) -> null;
422
423            assertTrue(mManager.requestPinShortcut(s, null));
424
425            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
426
427            // Shortcut shouldn't be registered yet.
428            assertWith(getCallerShortcuts())
429                    .isEmpty();
430        });
431
432        runWithCaller(LAUNCHER_1, USER_0, () -> {
433            // Check the intent passed to startActivityAsUser().
434            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
435
436            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
437
438            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
439
440            // Check the request object.
441            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
442
443            assertPinItemRequest(request);
444
445            assertWith(request.getShortcutInfo())
446                    .haveIds("s1")
447                    .areAllOrphan()
448                    .areAllWithNoActivity() // Activity is not set; expected.
449                    .areAllWithNoIntent();
450
451            // Accept the request.
452            assertForLauncherCallbackNoThrow(mLauncherApps,
453                    () -> assertTrue(request.accept()))
454                    .assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_P0)
455                    .haveIds("s1");
456        });
457
458        verify(mServiceContext, times(1)).sendIntentSender(eq(null));
459
460        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
461            assertWith(getCallerShortcuts())
462                    .haveIds("s1")
463                    .areAllNotDynamic()
464                    .areAllEnabled()
465                    .areAllPinned()
466                    .areAllWithNoActivity() // Activity is not set; expected.
467                    .areAllWithIntent();
468        });
469
470    }
471
472    public void testRequestPinShortcut_dynamicExists() {
473        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
474
475        final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
476
477        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
478            // Create dynamic shortcut
479            ShortcutInfo s1 = makeShortcutWithIcon("s1", res32x32);
480            assertTrue(mManager.setDynamicShortcuts(list(s1)));
481
482            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
483                    /* resultIntent=*/ null));
484
485            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
486
487            assertWith(getCallerShortcuts())
488                    .haveIds("s1")
489                    .areAllDynamic()
490                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
491                    .areAllNotPinned();
492        });
493
494        runWithCaller(LAUNCHER_1, USER_0, () -> {
495            // Check the intent passed to startActivityAsUser().
496            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
497
498            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
499
500            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
501
502            // Check the request object.
503            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
504
505            assertPinItemRequest(request);
506
507            assertWith(request.getShortcutInfo())
508                    .haveIds("s1")
509                    .areAllDynamic()
510                    .areAllNotPinned()
511                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
512                    .areAllWithNoIntent();
513
514            assertAllHaveIcon(list(request.getShortcutInfo()));
515
516            // Accept the request.
517            assertTrue(request.accept());
518        });
519
520        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
521            assertWith(getCallerShortcuts())
522                    .haveIds("s1")
523                    .areAllDynamic()
524                    .areAllEnabled()
525                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
526                    .areAllPinned();
527        });
528    }
529
530    public void testRequestPinShortcut_manifestExists() {
531        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
532
533        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
534            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
535
536            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
537                    /* resultIntent=*/ null));
538
539            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
540
541            assertWith(getCallerShortcuts())
542                    .haveIds("ms1")
543                    .areAllManifest()
544                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
545                            ShortcutActivity.class.getName()))
546                    .areAllNotPinned();
547        });
548
549        runWithCaller(LAUNCHER_1, USER_0, () -> {
550            // Check the intent passed to startActivityAsUser().
551            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
552
553            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
554
555            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
556
557            // Check the request object.
558            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
559
560            assertPinItemRequest(request);
561
562            assertWith(request.getShortcutInfo())
563                    .haveIds("ms1")
564                    .areAllManifest()
565                    .areAllNotPinned()
566                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
567                            ShortcutActivity.class.getName()))
568                    .areAllWithNoIntent();
569
570            assertAllHaveIcon(list(request.getShortcutInfo()));
571
572            // Accept the request.
573            assertTrue(request.accept());
574        });
575
576        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
577            assertWith(getCallerShortcuts())
578                    .haveIds("ms1")
579                    .areAllManifest()
580                    .areAllEnabled()
581                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
582                            ShortcutActivity.class.getName()))
583                    .areAllPinned();
584        });
585    }
586
587    public void testRequestPinShortcut_dynamicExists_alreadyPinned() {
588        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
589
590        final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
591
592        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
593            final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext, "s1")
594                    .setShortLabel("Title-" + "s1")
595                    .setIcon(res32x32)
596                    .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class));
597            final ShortcutInfo s = b.build();
598            assertTrue(mManager.setDynamicShortcuts(list(s)));
599        });
600
601        runWithCaller(LAUNCHER_1, USER_0, () -> {
602            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
603        });
604
605        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
606            assertWith(getCallerShortcuts())
607                    .haveIds("s1")
608                    .areAllDynamic()
609                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
610                    .areAllPinned();
611
612            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
613                    makeResultIntent()));
614
615            // The intent should be sent right away.
616            verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
617        });
618
619        // Already pinned.
620        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
621            assertWith(getCallerShortcuts())
622                    .haveIds("s1")
623                    .areAllDynamic()
624                    .areAllEnabled()
625                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
626                    .areAllPinned();
627        });
628
629        // ... But the launcher will still receive the request.
630        runWithCaller(LAUNCHER_1, USER_0, () -> {
631            // Check the intent passed to startActivityAsUser().
632            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
633
634            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
635
636            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
637
638            // Check the request object.
639            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
640
641            assertPinItemRequest(request);
642
643            assertWith(request.getShortcutInfo())
644                    .haveIds("s1")
645                    .areAllDynamic()
646                    .areAllPinned() // Note it's pinned already.
647                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
648                    .areAllWithNoIntent();
649
650            assertAllHaveIcon(list(request.getShortcutInfo()));
651
652            reset(mServiceContext);
653
654            // Accept the request.
655            assertTrue(request.accept());
656
657            // The intent is only sent once, so times(1).
658            verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
659        });
660
661        // Still pinned.
662        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
663            assertWith(getCallerShortcuts())
664                    .haveIds("s1")
665                    .areAllDynamic()
666                    .areAllEnabled()
667                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
668                    .areAllPinned();
669        });
670    }
671
672    public void testRequestPinShortcut_manifestExists_alreadyPinned() {
673        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
674
675        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
676            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
677        });
678
679        runWithCaller(LAUNCHER_1, USER_0, () -> {
680            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1"), HANDLE_USER_P0);
681        });
682
683        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
684            assertWith(getCallerShortcuts())
685                    .haveIds("ms1")
686                    .areAllManifest()
687                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
688                            ShortcutActivity.class.getName()))
689                    .areAllPinned();
690
691            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
692                    makeResultIntent()));
693
694            // The intent should be sent right away.
695            verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
696        });
697
698        // Already pinned.
699        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
700            assertWith(getCallerShortcuts())
701                    .haveIds("ms1")
702                    .areAllManifest()
703                    .areAllEnabled()
704                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
705                            ShortcutActivity.class.getName()))
706                    .areAllPinned();
707        });
708
709        // ... But the launcher will still receive the request.
710        runWithCaller(LAUNCHER_1, USER_0, () -> {
711            // Check the intent passed to startActivityAsUser().
712            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
713
714            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
715
716            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
717
718            // Check the request object.
719            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
720
721            assertPinItemRequest(request);
722
723            assertWith(request.getShortcutInfo())
724                    .haveIds("ms1")
725                    .areAllManifest()
726                    .areAllPinned() // Note it's pinned already.
727                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
728                            ShortcutActivity.class.getName()))
729                    .areAllWithNoIntent();
730
731            assertAllHaveIcon(list(request.getShortcutInfo()));
732
733            reset(mServiceContext);
734
735            // Accept the request.
736            assertTrue(request.accept());
737
738            // The intent is only sent once, so times(1).
739            verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
740        });
741
742        // Still pinned.
743        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
744            assertWith(getCallerShortcuts())
745                    .haveIds("ms1")
746                    .areAllManifest()
747                    .areAllEnabled()
748                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
749                            ShortcutActivity.class.getName()))
750                    .areAllPinned();
751        });
752    }
753
754    public void testRequestPinShortcut_wasDynamic_alreadyPinned() {
755        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
756
757        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
758            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
759        });
760
761        runWithCaller(LAUNCHER_1, USER_0, () -> {
762            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
763        });
764
765        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
766            mManager.removeAllDynamicShortcuts();
767            assertWith(getCallerShortcuts())
768                    .haveIds("s1")
769                    .areAllNotDynamic()
770                    .areAllEnabled()
771                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
772                    .areAllPinned();
773
774            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
775                    /* resultIntent=*/ null));
776
777            // The intent should be sent right away.
778            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
779        });
780    }
781
782    public void testRequestPinShortcut_wasDynamic_disabled_alreadyPinned() {
783        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
784
785        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
786            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
787        });
788
789        runWithCaller(LAUNCHER_1, USER_0, () -> {
790            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
791        });
792
793        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
794            mManager.disableShortcuts(list("s1"));
795
796            assertWith(getCallerShortcuts())
797                    .haveIds("s1")
798                    .areAllNotDynamic()
799                    .areAllDisabled()
800                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
801                    .areAllPinned();
802
803            assertExpectException(IllegalArgumentException.class, "exists but disabled", () -> {
804                mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
805                        /* resultIntent=*/ null);
806            });
807
808            // Shouldn't be called.
809            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
810        });
811    }
812
813    public void testRequestPinShortcut_wasManifest_alreadyPinned() {
814        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
815
816        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
817            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
818        });
819
820        runWithCaller(LAUNCHER_1, USER_0, () -> {
821            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1"), HANDLE_USER_P0);
822        });
823
824        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
825            publishManifestShortcutsAsCaller(R.xml.shortcut_0);
826
827            assertWith(getCallerShortcuts())
828                    .haveIds("ms1")
829                    .areAllNotManifest()
830                    .areAllDisabled()
831                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
832                            ShortcutActivity.class.getName()))
833                    .areAllPinned();
834
835            assertExpectException(IllegalArgumentException.class, "exists but disabled", () -> {
836                mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
837                        /* resultIntent=*/ null);
838            });
839
840            // Shouldn't be called.
841            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
842        });
843    }
844
845    public void testRequestPinShortcut_dynamicExists_alreadyPinnedByAnother() {
846        // Initially all launchers have the shortcut permission, until we call setDefaultLauncher().
847
848        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
849            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
850        });
851
852        runWithCaller(LAUNCHER_2, USER_0, () -> {
853            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
854        });
855
856        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
857
858        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
859            assertWith(getCallerShortcuts())
860                    .haveIds("s1")
861                    .areAllDynamic()
862                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
863                    .areAllPinned();
864
865            // The shortcut is already pinned, but not by the current launcher, so it'll still
866            // invoke the whole flow.
867            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
868                    /* resultIntent=*/ null));
869
870            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
871        });
872
873        runWithCaller(LAUNCHER_1, USER_0, () -> {
874            // Check the intent passed to startActivityAsUser().
875            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
876
877            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
878
879            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
880
881            // Check the request object.
882            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
883
884            assertPinItemRequest(request);
885
886            assertWith(request.getShortcutInfo())
887                    .haveIds("s1")
888                    .areAllDynamic()
889                    .areAllNotPinned() // Note it's not pinned by this launcher.
890                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
891                    .areAllWithNoIntent();
892
893            // Accept the request.
894            assertTrue(request.accept());
895        });
896
897        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
898            assertWith(getCallerShortcuts())
899                    .haveIds("s1")
900                    .areAllDynamic()
901                    .areAllEnabled()
902                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
903                    .areAllPinned();
904        });
905    }
906
907    public void testRequestPinShortcut_manifestExists_alreadyPinnedByAnother() {
908        // Initially all launchers have the shortcut permission, until we call setDefaultLauncher().
909
910        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
911            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
912        });
913
914        runWithCaller(LAUNCHER_2, USER_0, () -> {
915            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1"), HANDLE_USER_P0);
916        });
917
918        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
919
920        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
921            assertWith(getCallerShortcuts())
922                    .haveIds("ms1")
923                    .areAllManifest()
924                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
925                            ShortcutActivity.class.getName()))
926                    .areAllPinned();
927
928            // The shortcut is already pinned, but not by the current launcher, so it'll still
929            // invoke the whole flow.
930            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
931                    /* resultIntent=*/ null));
932
933            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
934        });
935
936        runWithCaller(LAUNCHER_1, USER_0, () -> {
937            // Check the intent passed to startActivityAsUser().
938            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
939
940            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
941
942            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
943
944            // Check the request object.
945            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
946
947            assertPinItemRequest(request);
948
949            assertWith(request.getShortcutInfo())
950                    .haveIds("ms1")
951                    .areAllManifest()
952                    .areAllNotPinned() // Note it's not pinned by this launcher.
953                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
954                            ShortcutActivity.class.getName()))
955                    .areAllWithNoIntent();
956
957            // Accept the request.
958            assertTrue(request.accept());
959        });
960
961        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
962            assertWith(getCallerShortcuts())
963                    .haveIds("ms1")
964                    .areAllManifest()
965                    .areAllEnabled()
966                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
967                            ShortcutActivity.class.getName()))
968                    .areAllPinned();
969        });
970    }
971
972    /**
973     * The launcher already has a pinned shortuct.  The new one should be added, not replace
974     * the existing one.
975     */
976    public void testRequestPinShortcut_launcherAlreadyHasPinned() {
977        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
978
979        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
980            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"), makeShortcut("s2"))));
981        });
982
983        runWithCaller(LAUNCHER_1, USER_0, () -> {
984            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_P0);
985        });
986
987        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
988            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
989                    /* resultIntent=*/ null));
990
991            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
992        });
993
994        runWithCaller(LAUNCHER_1, USER_0, () -> {
995            // Check the intent passed to startActivityAsUser().
996            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
997
998            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
999
1000            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
1001
1002            // Check the request object.
1003            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
1004
1005            assertPinItemRequest(request);
1006
1007            assertWith(request.getShortcutInfo())
1008                    .haveIds("s1")
1009                    .areAllDynamic()
1010                    .areAllNotPinned()
1011                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1012                    .areAllWithNoIntent();
1013
1014            // Accept the request.
1015            assertTrue(request.accept());
1016
1017            assertWith(getShortcutAsLauncher(USER_P0))
1018                    .haveIds("s1", "s2")
1019                    .areAllDynamic()
1020                    .areAllEnabled()
1021                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1022                    .areAllPinned();
1023        });
1024
1025        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1026            assertWith(getCallerShortcuts())
1027                    .haveIds("s1", "s2")
1028                    .areAllDynamic()
1029                    .areAllEnabled()
1030                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1031                    .areAllPinned();
1032        });
1033    }
1034
1035    /**
1036     * When trying to pin an existing shortcut, the new fields shouldn't override existing fields.
1037     */
1038    public void testRequestPinShortcut_dynamicExists_titleWontChange() {
1039        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1040
1041        final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
1042
1043        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1044            // Create dynamic shortcut
1045            ShortcutInfo s1 = makeShortcutWithIcon("s1", res32x32);
1046            assertTrue(mManager.setDynamicShortcuts(list(s1)));
1047
1048            assertTrue(mManager.requestPinShortcut(makeShortcutWithShortLabel("s1", "xxx"),
1049                    /* resultIntent=*/ null));
1050
1051            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1052
1053            assertWith(getCallerShortcuts())
1054                    .haveIds("s1")
1055                    .areAllDynamic()
1056                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1057                    .areAllNotPinned();
1058        });
1059
1060        runWithCaller(LAUNCHER_1, USER_0, () -> {
1061            // Check the intent passed to startActivityAsUser().
1062            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
1063
1064            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
1065
1066            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
1067
1068            // Check the request object.
1069            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
1070
1071            assertPinItemRequest(request);
1072
1073            assertWith(request.getShortcutInfo())
1074                    .haveIds("s1")
1075                    .areAllDynamic()
1076                    .areAllNotPinned()
1077                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1078                    .areAllWithNoIntent();
1079
1080            assertAllHaveIcon(list(request.getShortcutInfo()));
1081
1082            // Accept the request.
1083            assertTrue(request.accept());
1084        });
1085
1086        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1087            assertWith(getCallerShortcuts())
1088                    .haveIds("s1")
1089                    .areAllDynamic()
1090                    .areAllEnabled()
1091                    .areAllPinned()
1092                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1093                    .forShortcutWithId("s1", (si) -> {
1094                        // Still the original title.
1095                        assertEquals("Title-s1", si.getShortLabel());
1096                    });
1097        });
1098    }
1099
1100    /**
1101     * When trying to pin an existing shortcut, the new fields shouldn't override existing fields.
1102     */
1103    public void testRequestPinShortcut_manifestExists_titleWontChange() {
1104        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1105
1106        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1107            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
1108
1109            assertTrue(mManager.requestPinShortcut(makeShortcutWithShortLabel("ms1", "xxx"),
1110                    /* resultIntent=*/ null));
1111
1112            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1113
1114            assertWith(getCallerShortcuts())
1115                    .haveIds("ms1")
1116                    .areAllManifest()
1117                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
1118                            ShortcutActivity.class.getName()))
1119                    .areAllNotPinned();
1120        });
1121
1122        runWithCaller(LAUNCHER_1, USER_0, () -> {
1123            // Check the intent passed to startActivityAsUser().
1124            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
1125
1126            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
1127
1128            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
1129
1130            // Check the request object.
1131            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
1132
1133            assertPinItemRequest(request);
1134
1135            assertWith(request.getShortcutInfo())
1136                    .haveIds("ms1")
1137                    .areAllManifest()
1138                    .areAllNotPinned()
1139                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
1140                            ShortcutActivity.class.getName()))
1141                    .areAllWithNoIntent();
1142
1143            assertAllHaveIcon(list(request.getShortcutInfo()));
1144
1145            // Accept the request.
1146            assertTrue(request.accept());
1147        });
1148
1149        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1150            assertWith(getCallerShortcuts())
1151                    .haveIds("ms1")
1152                    .areAllManifest()
1153                    .areAllEnabled()
1154                    .areAllPinned()
1155                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
1156                            ShortcutActivity.class.getName()))
1157                    .forShortcutWithId("ms1", (si) -> {
1158                        // Still the original title.
1159                        // Title should be something like:
1160                        // "string-com.android.test.1-user:20-res:2131034112/en"
1161                        MoreAsserts.assertContainsRegex("^string-", si.getShortLabel().toString());
1162                    });
1163        });
1164    }
1165
1166    /**
1167     * The dynamic shortcut existed, but before accepting(), it's removed.  Because the request
1168     * has a partial shortcut, accept() should fail.
1169     */
1170    public void testRequestPinShortcut_dynamicExists_thenRemoved_error() {
1171        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1172
1173        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1174            // Create dynamic shortcut
1175            ShortcutInfo s1 = makeShortcut("s1");
1176            assertTrue(mManager.setDynamicShortcuts(list(s1)));
1177
1178            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
1179                    /* resultIntent=*/ null));
1180
1181            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1182
1183            mManager.removeAllDynamicShortcuts();
1184
1185            assertWith(getCallerShortcuts())
1186                    .isEmpty();
1187        });
1188
1189        runWithCaller(LAUNCHER_1, USER_0, () -> {
1190            // Check the intent passed to startActivityAsUser().
1191            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
1192
1193            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
1194
1195            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
1196
1197            // Check the request object.
1198            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
1199
1200            assertPinItemRequest(request);
1201
1202            assertWith(request.getShortcutInfo())
1203                    .haveIds("s1")
1204                    .areAllDynamic()
1205                    .areAllNotPinned()
1206                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1207                    .areAllWithNoIntent();
1208
1209            // Accept the request -> should fail.
1210            assertForLauncherCallbackNoThrow(mLauncherApps,
1211                    () -> assertFalse(request.accept()))
1212                    .assertNoCallbackCalled();
1213        });
1214
1215        // Intent shouldn't be sent.
1216        verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1217
1218        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1219            assertWith(getCallerShortcuts())
1220                    .isEmpty();
1221        });
1222    }
1223
1224    /**
1225     * The dynamic shortcut existed, but before accepting(), it's removed.  Because the request
1226     * has all the mandatory fields, we can go ahead and still publish it.
1227     */
1228    public void testRequestPinShortcut_dynamicExists_thenRemoved_okay() {
1229        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1230
1231        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1232            // Create dynamic shortcut
1233            ShortcutInfo s1 = makeShortcut("s1");
1234            assertTrue(mManager.setDynamicShortcuts(list(s1)));
1235
1236            assertTrue(mManager.requestPinShortcut(makeShortcutWithShortLabel("s1", "new"),
1237                    /* resultIntent=*/ null));
1238
1239            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1240
1241            mManager.removeAllDynamicShortcuts();
1242
1243            assertWith(getCallerShortcuts())
1244                    .isEmpty();
1245        });
1246
1247        runWithCaller(LAUNCHER_1, USER_0, () -> {
1248            // Check the intent passed to startActivityAsUser().
1249            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
1250
1251            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
1252
1253            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
1254
1255            // Check the request object.
1256            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
1257
1258            assertPinItemRequest(request);
1259
1260            assertWith(request.getShortcutInfo())
1261                    .haveIds("s1")
1262                    .areAllDynamic()
1263                    .areAllNotPinned()
1264                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1265                    .areAllWithNoIntent();
1266
1267            assertTrue(request.accept());
1268        });
1269
1270        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1271            assertWith(getCallerShortcuts())
1272                    .haveIds("s1")
1273                    .areAllFloating()
1274                    .forShortcutWithId("s1", si -> {
1275                        assertEquals("new", si.getShortLabel());
1276                    });
1277        });
1278    }
1279
1280    /**
1281     * The manifest shortcut existed, but before accepting(), it's removed.  Because the request
1282     * has a partial shortcut, accept() should fail.
1283     */
1284    public void testRequestPinShortcut_manifestExists_thenRemoved_error() {
1285        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1286
1287        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1288            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
1289
1290            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
1291                    /* resultIntent=*/ null));
1292
1293            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1294
1295            publishManifestShortcutsAsCaller(R.xml.shortcut_0);
1296
1297            assertWith(getCallerShortcuts())
1298                    .isEmpty();
1299        });
1300
1301        runWithCaller(LAUNCHER_1, USER_0, () -> {
1302            // Check the intent passed to startActivityAsUser().
1303            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
1304
1305            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
1306
1307            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
1308
1309            // Check the request object.
1310            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
1311
1312            assertPinItemRequest(request);
1313
1314            assertWith(request.getShortcutInfo())
1315                    .haveIds("ms1")
1316                    .areAllManifest()
1317                    .areAllNotPinned()
1318                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
1319                            ShortcutActivity.class.getName()))
1320                    .areAllWithNoIntent();
1321
1322            // Accept the request -> should fail.
1323            assertForLauncherCallbackNoThrow(mLauncherApps,
1324                    () -> assertFalse(request.accept()))
1325                    .assertNoCallbackCalled();
1326        });
1327
1328        // Intent shouldn't be sent.
1329        verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1330
1331        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1332            assertWith(getCallerShortcuts())
1333                    .isEmpty();
1334        });
1335    }
1336
1337    /**
1338     * The manifest shortcut existed, but before accepting(), it's removed.  Because the request
1339     * has all the mandatory fields, we can go ahead and still publish it.
1340     */
1341    public void testRequestPinShortcut_manifestExists_thenRemoved_okay() {
1342        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1343
1344        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1345            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
1346
1347            assertTrue(mManager.requestPinShortcut(makeShortcutWithShortLabel("ms1", "new"),
1348                    /* resultIntent=*/ null));
1349
1350            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1351
1352            publishManifestShortcutsAsCaller(R.xml.shortcut_0);
1353
1354            assertWith(getCallerShortcuts())
1355                    .isEmpty();
1356        });
1357
1358        runWithCaller(LAUNCHER_1, USER_0, () -> {
1359            // Check the intent passed to startActivityAsUser().
1360            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
1361
1362            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
1363
1364            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
1365
1366            // Check the request object.
1367            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
1368
1369            assertPinItemRequest(request);
1370
1371            assertWith(request.getShortcutInfo())
1372                    .haveIds("ms1")
1373                    .areAllManifest()
1374                    .areAllNotPinned()
1375                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
1376                            ShortcutActivity.class.getName()))
1377                    .areAllWithNoIntent();
1378
1379
1380            assertTrue(request.accept());
1381        });
1382
1383        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1384            assertWith(getCallerShortcuts())
1385                    .haveIds("ms1")
1386                    .areAllMutable() // Note it's no longer immutable.
1387                    .areAllFloating()
1388
1389                    // Note it's the activity from makeShortcutWithShortLabel().
1390                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1391                    .forShortcutWithId("ms1", si -> {
1392                        assertEquals("new", si.getShortLabel());
1393                    });
1394        });
1395    }
1396
1397    /**
1398     * The dynamic shortcut existed, but before accepting(), it's removed.  Because the request
1399     * has a partial shortcut, accept() should fail.
1400     */
1401    public void testRequestPinShortcut_dynamicExists_thenDisabled_error() {
1402        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1403
1404        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1405            ShortcutInfo s1 = makeShortcut("s1");
1406            assertTrue(mManager.setDynamicShortcuts(list(s1)));
1407
1408            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
1409                    /* resultIntent=*/ null));
1410
1411            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1412        });
1413
1414        // Then, pin by another launcher and disable it.
1415        // We have to pin it here so that disable() won't remove it.
1416        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_2, USER_0));
1417        runWithCaller(LAUNCHER_2, USER_0, () -> {
1418            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
1419        });
1420        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1421            mManager.disableShortcuts(list("s1"));
1422            assertWith(getCallerShortcuts())
1423                    .haveIds("s1")
1424                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1425                    .areAllDisabled();
1426        });
1427
1428        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1429        runWithCaller(LAUNCHER_1, USER_0, () -> {
1430            // Check the intent passed to startActivityAsUser().
1431            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
1432
1433            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
1434
1435            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
1436
1437            // Check the request object.
1438            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
1439
1440            assertPinItemRequest(request);
1441
1442            assertWith(request.getShortcutInfo())
1443                    .haveIds("s1")
1444                    .areAllDynamic()
1445                    .areAllNotPinned()
1446                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1447                    .areAllWithNoIntent();
1448
1449            // Accept the request -> should fail.
1450            assertForLauncherCallbackNoThrow(mLauncherApps,
1451                    () -> assertFalse(request.accept()))
1452                    .assertNoCallbackCalled();
1453
1454            // Note s1 is floating and pinned by another launcher, so it shouldn't be
1455            // visible here.
1456            assertWith(getShortcutAsLauncher(USER_P0))
1457                    .isEmpty();
1458        });
1459
1460        // Intent shouldn't be sent.
1461        verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1462
1463        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1464            assertWith(getCallerShortcuts())
1465                    .haveIds("s1")
1466                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
1467                    .areAllDisabled();
1468        });
1469    }
1470
1471    /**
1472     * The manifest shortcut existed, but before accepting(), it's removed.  Because the request
1473     * has a partial shortcut, accept() should fail.
1474     */
1475    public void testRequestPinShortcut_manifestExists_thenDisabled_error() {
1476        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1477
1478        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1479            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
1480
1481            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
1482                    /* resultIntent=*/ null));
1483
1484            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1485        });
1486
1487        // Then, pin by another launcher and disable it.
1488        // We have to pin it here so that disable() won't remove it.
1489        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_2, USER_0));
1490        runWithCaller(LAUNCHER_2, USER_0, () -> {
1491            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1"), HANDLE_USER_P0);
1492        });
1493        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1494            publishManifestShortcutsAsCaller(R.xml.shortcut_0);
1495            assertWith(getCallerShortcuts())
1496                    .haveIds("ms1")
1497                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
1498                            ShortcutActivity.class.getName()))
1499                    .areAllDisabled();
1500        });
1501
1502        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1503        runWithCaller(LAUNCHER_1, USER_0, () -> {
1504            // Check the intent passed to startActivityAsUser().
1505            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
1506
1507            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
1508
1509            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
1510
1511            // Check the request object.
1512            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
1513
1514            assertPinItemRequest(request);
1515
1516            assertWith(request.getShortcutInfo())
1517                    .haveIds("ms1")
1518                    .areAllManifest()
1519                    .areAllNotPinned()
1520                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
1521                            ShortcutActivity.class.getName()))
1522                    .areAllWithNoIntent();
1523
1524            // Accept the request -> should fail.
1525            assertForLauncherCallbackNoThrow(mLauncherApps,
1526                    () -> assertFalse(request.accept()))
1527                    .assertNoCallbackCalled();
1528
1529            // Note ms1 is floating and pinned by another launcher, so it shouldn't be
1530            // visible here.
1531            assertWith(getShortcutAsLauncher(USER_P0))
1532                    .isEmpty();
1533        });
1534
1535        // Intent shouldn't be sent.
1536        verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1537
1538        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1539            assertWith(getCallerShortcuts())
1540                    .haveIds("ms1")
1541                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
1542                            ShortcutActivity.class.getName()))
1543                    .areAllDisabled();
1544        });
1545    }
1546
1547    public void testRequestPinShortcut_wrongLauncherCannotAccept() {
1548        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
1549
1550        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
1551            ShortcutInfo s1 = makeShortcut("s1");
1552            assertTrue(mManager.requestPinShortcut(s1, null));
1553            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
1554        });
1555
1556        final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
1557        verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
1558        final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
1559
1560        // Verify that other launcher can't use this request
1561        runWithCaller(LAUNCHER_1, USER_0, () -> {
1562            // Set some random caller UID.
1563            mInjectedCallingUid = 12345;
1564
1565            assertFalse(request.isValid());
1566            assertExpectException(SecurityException.class, "Calling uid mismatch", request::accept);
1567        });
1568
1569        // The default launcher can still use this request
1570        runWithCaller(LAUNCHER_1, USER_0, () -> {
1571            assertTrue(request.isValid());
1572            assertTrue(request.accept());
1573        });
1574    }
1575
1576    // TODO More tests:
1577
1578    // Cancel previous pending request and release memory?
1579
1580    // Check the launcher callback too.
1581
1582    // Missing fields -- pre and post, both.
1583}
1584