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