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