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