1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.appwidget.cts; 18 19 import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN; 20 import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.assertFalse; 26 import static org.junit.Assert.assertNotNull; 27 import static org.junit.Assert.assertNull; 28 import static org.junit.Assert.assertSame; 29 import static org.junit.Assert.assertThrows; 30 import static org.junit.Assert.assertTrue; 31 import static org.junit.Assert.fail; 32 import static org.mockito.Matchers.any; 33 import static org.mockito.Matchers.eq; 34 import static org.mockito.Matchers.same; 35 import static org.mockito.Mockito.atLeastOnce; 36 import static org.mockito.Mockito.doAnswer; 37 import static org.mockito.Mockito.inOrder; 38 import static org.mockito.Mockito.mock; 39 import static org.mockito.Mockito.times; 40 import static org.mockito.Mockito.verify; 41 import static org.mockito.hamcrest.MockitoHamcrest.argThat; 42 43 import android.appwidget.AppWidgetHost; 44 import android.appwidget.AppWidgetHostView; 45 import android.appwidget.AppWidgetManager; 46 import android.appwidget.AppWidgetProviderInfo; 47 import android.appwidget.cts.provider.AppWidgetProviderCallbacks; 48 import android.appwidget.cts.provider.AppWidgetProviderWithFeatures; 49 import android.appwidget.cts.provider.FirstAppWidgetProvider; 50 import android.appwidget.cts.provider.SecondAppWidgetProvider; 51 import android.appwidget.cts.service.MyAppWidgetService; 52 import android.appwidget.flags.Flags; 53 import android.content.ComponentName; 54 import android.content.Context; 55 import android.content.Intent; 56 import android.content.pm.PackageManager; 57 import android.graphics.drawable.Drawable; 58 import android.net.Uri; 59 import android.os.Bundle; 60 import android.os.Process; 61 import android.os.StrictMode; 62 import android.os.SystemClock; 63 import android.os.UserHandle; 64 import android.os.UserManager; 65 import android.platform.test.annotations.AppModeFull; 66 import android.platform.test.annotations.AppModeInstant; 67 import android.platform.test.annotations.RequiresFlagsEnabled; 68 import android.platform.test.flag.junit.CheckFlagsRule; 69 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 70 import android.provider.DeviceConfig; 71 import android.text.TextUtils; 72 import android.util.DisplayMetrics; 73 import android.widget.RemoteViews; 74 import android.widget.RemoteViewsService.RemoteViewsFactory; 75 76 import com.android.compatibility.common.util.ApiTest; 77 import com.android.compatibility.common.util.DeviceConfigStateHelper; 78 79 import org.hamcrest.BaseMatcher; 80 import org.hamcrest.Description; 81 import org.junit.After; 82 import org.junit.Assume; 83 import org.junit.Before; 84 import org.junit.Ignore; 85 import org.junit.Rule; 86 import org.junit.Test; 87 import org.mockito.InOrder; 88 import org.mockito.invocation.InvocationOnMock; 89 import org.mockito.stubbing.Answer; 90 91 import java.time.Duration; 92 import java.util.ArrayList; 93 import java.util.Arrays; 94 import java.util.List; 95 import java.util.concurrent.CountDownLatch; 96 import java.util.concurrent.atomic.AtomicInteger; 97 import java.util.function.IntConsumer; 98 99 public class AppWidgetTest extends AppWidgetTestCase { 100 101 @Rule(order = 0) 102 public final CheckFlagsRule checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 103 104 private static final long OPERATION_TIMEOUT = 20 * 1000; // 20 sec 105 106 private final Object mLock = new Object(); 107 private final DeviceConfigStateHelper mDeviceConfigStateHelper = new DeviceConfigStateHelper( 108 DeviceConfig.NAMESPACE_SYSTEMUI); 109 110 @Before setUpDexmaker()111 public void setUpDexmaker() throws Exception { 112 // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2 113 // Dexmaker is used by mockito. 114 System.setProperty("dexmaker.dexcache", getInstrumentation() 115 .getTargetContext().getCacheDir().getPath()); 116 } 117 118 @After cleanUp()119 public void cleanUp() throws Exception { 120 mDeviceConfigStateHelper.close(); 121 } 122 123 @AppModeInstant(reason = "Instant apps cannot provide or host app widgets") 124 @Test testInstantAppsCannotProvideAppWidgets()125 public void testInstantAppsCannotProvideAppWidgets() { 126 Assume.assumeTrue(getInstrumentation().getTargetContext() 127 .getPackageManager().isInstantApp()); 128 assertNull(getFirstAppWidgetProviderInfo()); 129 } 130 131 @AppModeInstant(reason = "Instant apps cannot provide or host app widgets") 132 @Test testInstantAppsCannotHostAppWidgets()133 public void testInstantAppsCannotHostAppWidgets() { 134 Assume.assumeTrue(getInstrumentation().getTargetContext() 135 .getPackageManager().isInstantApp()); 136 // Create a host and start listening. 137 AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0); 138 // Allocate an app widget id to bind. 139 assertSame(AppWidgetManager.INVALID_APPWIDGET_ID, host.allocateAppWidgetId()); 140 } 141 142 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 143 @Test testGetAppInstalledProvidersForCurrentUserLegacy()144 public void testGetAppInstalledProvidersForCurrentUserLegacy() throws Exception { 145 // By default we should get only providers for the current user. 146 List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders(); 147 148 // Make sure we have our two providers in the list. 149 assertExpectedInstalledProviders(providers); 150 } 151 152 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 153 @Test testGetAppInstalledProvidersForCurrentUserNewCurrentProfile()154 public void testGetAppInstalledProvidersForCurrentUserNewCurrentProfile() throws Exception { 155 // We ask only for providers for the current user. 156 List<AppWidgetProviderInfo> providers = getAppWidgetManager() 157 .getInstalledProvidersForProfile(Process.myUserHandle()); 158 159 // Make sure we have our two providers in the list. 160 assertExpectedInstalledProviders(providers); 161 } 162 163 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 164 @Test testGetAppInstalledProvidersForCurrentUserNewAllProfiles()165 public void testGetAppInstalledProvidersForCurrentUserNewAllProfiles() throws Exception { 166 // We ask only for providers for all current user's profiles 167 UserManager userManager = (UserManager) getInstrumentation() 168 .getTargetContext().getSystemService(Context.USER_SERVICE); 169 170 List<AppWidgetProviderInfo> allProviders = new ArrayList<>(); 171 172 List<UserHandle> profiles = userManager.getUserProfiles(); 173 final int profileCount = profiles.size(); 174 for (int i = 0; i < profileCount; i++) { 175 UserHandle profile = profiles.get(i); 176 List<AppWidgetProviderInfo> providers = getAppWidgetManager() 177 .getInstalledProvidersForProfile(profile); 178 allProviders.addAll(providers); 179 } 180 181 // Make sure we have our two providers in the list. 182 assertExpectedInstalledProviders(allProviders); 183 } 184 185 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 186 @Test testBindAppWidget()187 public void testBindAppWidget() throws Exception { 188 // Create a host and start listening. 189 AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0); 190 host.deleteHost(); 191 host.startListening(); 192 193 // Allocate an app widget id to bind. 194 final int appWidgetId = host.allocateAppWidgetId(); 195 196 // Grab a provider we defined to be bound. 197 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 198 199 // Bind the widget. 200 boolean widgetBound = getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 201 provider.getProfile(), provider.provider, null); 202 assertFalse(widgetBound); 203 204 // Well, app do not have this permission unless explicitly granted 205 // by the user. Now we will pretend for the user and grant it. 206 grantBindAppWidgetPermission(); 207 208 try { 209 // Bind the widget as we have a permission for that. 210 widgetBound = getAppWidgetManager().bindAppWidgetIdIfAllowed( 211 appWidgetId, provider.getProfile(), provider.provider, null); 212 assertTrue(widgetBound); 213 214 // Deallocate the app widget id. 215 host.deleteAppWidgetId(appWidgetId); 216 } finally { 217 // Clean up. 218 host.deleteAppWidgetId(appWidgetId); 219 host.deleteHost(); 220 revokeBindAppWidgetPermission(); 221 } 222 } 223 224 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 225 @Test testGetAppWidgetIdsForHost()226 public void testGetAppWidgetIdsForHost() throws Exception { 227 AppWidgetHost host1 = new AppWidgetHost(getInstrumentation().getTargetContext(), 1); 228 AppWidgetHost host2 = new AppWidgetHost(getInstrumentation().getTargetContext(), 2); 229 230 host1.deleteHost(); 231 host2.deleteHost(); 232 233 assertTrue(Arrays.equals(host1.getAppWidgetIds(), new int[]{})); 234 assertTrue(Arrays.equals(host2.getAppWidgetIds(), new int[]{})); 235 236 int id1 = host1.allocateAppWidgetId(); 237 assertTrue(Arrays.equals(host1.getAppWidgetIds(), new int[]{id1})); 238 assertTrue(Arrays.equals(host2.getAppWidgetIds(), new int[]{})); 239 240 int id2 = host1.allocateAppWidgetId(); 241 assertTrue(Arrays.equals(host1.getAppWidgetIds(), new int[]{id1, id2})); 242 assertTrue(Arrays.equals(host2.getAppWidgetIds(), new int[]{})); 243 244 int id3 = host2.allocateAppWidgetId(); 245 assertTrue(Arrays.equals(host1.getAppWidgetIds(), new int[]{id1, id2})); 246 assertTrue(Arrays.equals(host2.getAppWidgetIds(), new int[]{id3})); 247 248 host1.deleteHost(); 249 assertTrue(Arrays.equals(host1.getAppWidgetIds(), new int[]{})); 250 assertTrue(Arrays.equals(host2.getAppWidgetIds(), new int[]{id3})); 251 252 host2.deleteHost(); 253 } 254 255 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 256 @Test testAppWidgetProviderCallbacks()257 public void testAppWidgetProviderCallbacks() throws Exception { 258 AtomicInteger invocationCounter = new AtomicInteger(); 259 260 // Set a mock to intercept provider callbacks. 261 AppWidgetProviderCallbacks callbacks = createAppWidgetProviderCallbacks(invocationCounter); 262 FirstAppWidgetProvider.setCallbacks(callbacks); 263 264 int firstAppWidgetId = 0; 265 int secondAppWidgetId = 0; 266 267 final Bundle firstOptions; 268 final Bundle secondOptions; 269 270 // Create a host and start listening. 271 AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0); 272 host.deleteHost(); 273 host.startListening(); 274 275 // We want to bind a widget. 276 grantBindAppWidgetPermission(); 277 try { 278 // Allocate the first widget id to bind. 279 firstAppWidgetId = host.allocateAppWidgetId(); 280 281 // Grab a provider we defined to be bound. 282 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 283 284 // Bind the first widget. 285 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 286 provider.getProfile(), provider.provider, null); 287 288 // Wait for onEnabled and onUpdate 289 waitForCallCount(invocationCounter, 2); 290 291 // Update the first widget options. 292 firstOptions = new Bundle(); 293 firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 1); 294 firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 2); 295 firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 3); 296 firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 4); 297 getAppWidgetManager().updateAppWidgetOptions(firstAppWidgetId, firstOptions); 298 299 // Wait for onAppWidgetOptionsChanged 300 waitForCallCount(invocationCounter, 3); 301 302 // Allocate the second app widget id to bind. 303 secondAppWidgetId = host.allocateAppWidgetId(); 304 305 // Bind the second widget. 306 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 307 provider.getProfile(), provider.provider, null); 308 309 // Wait for onUpdate 310 waitForCallCount(invocationCounter, 4); 311 312 // Update the second widget options. 313 secondOptions = new Bundle(); 314 secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 5); 315 secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 6); 316 secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 7); 317 secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 8); 318 getAppWidgetManager().updateAppWidgetOptions(secondAppWidgetId, secondOptions); 319 320 // Wait for onAppWidgetOptionsChanged 321 waitForCallCount(invocationCounter, 5); 322 323 // Delete the first widget. 324 host.deleteAppWidgetId(firstAppWidgetId); 325 326 // Wait for onDeleted 327 waitForCallCount(invocationCounter, 6); 328 329 // Delete the second widget. 330 host.deleteAppWidgetId(secondAppWidgetId); 331 332 // Wait for onDeleted and onDisabled 333 waitForCallCount(invocationCounter, 8); 334 335 // Make sure the provider callbacks are correct. 336 InOrder inOrder = inOrder(callbacks); 337 338 inOrder.verify(callbacks).onEnabled(any(Context.class)); 339 inOrder.verify(callbacks).onUpdate(any(Context.class), 340 any(AppWidgetManager.class), eq(new int[] {firstAppWidgetId})); 341 inOrder.verify(callbacks).onAppWidgetOptionsChanged(any(Context.class), 342 any(AppWidgetManager.class), eq(firstAppWidgetId), argThat( 343 new OptionsMatcher(firstOptions))); 344 inOrder.verify(callbacks).onUpdate(any(Context.class), 345 any(AppWidgetManager.class), eq(new int[] {secondAppWidgetId})); 346 inOrder.verify(callbacks).onAppWidgetOptionsChanged(any(Context.class), 347 any(AppWidgetManager.class), eq(secondAppWidgetId), argThat( 348 new OptionsMatcher(secondOptions))); 349 inOrder.verify(callbacks).onDeleted(any(Context.class), 350 argThat(new WidgetIdsMatcher(new int[]{firstAppWidgetId}))); 351 inOrder.verify(callbacks).onDeleted(any(Context.class), 352 argThat(new WidgetIdsMatcher(new int[]{secondAppWidgetId}))); 353 inOrder.verify(callbacks).onDisabled(any(Context.class)); 354 } finally { 355 // Clean up. 356 host.deleteAppWidgetId(firstAppWidgetId); 357 host.deleteAppWidgetId(secondAppWidgetId); 358 host.deleteHost(); 359 FirstAppWidgetProvider.setCallbacks(null); 360 revokeBindAppWidgetPermission(); 361 } 362 } 363 364 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 365 @Test testTwoAppWidgetProviderCallbacks()366 public void testTwoAppWidgetProviderCallbacks() throws Exception { 367 AtomicInteger invocationCounter = new AtomicInteger(); 368 369 // Set a mock to intercept first provider callbacks. 370 AppWidgetProviderCallbacks firstCallbacks = createAppWidgetProviderCallbacks( 371 invocationCounter); 372 FirstAppWidgetProvider.setCallbacks(firstCallbacks); 373 374 // Set a mock to intercept second provider callbacks. 375 AppWidgetProviderCallbacks secondCallbacks = createAppWidgetProviderCallbacks( 376 invocationCounter); 377 SecondAppWidgetProvider.setCallbacks(secondCallbacks); 378 379 int firstAppWidgetId = 0; 380 int secondAppWidgetId = 0; 381 382 // Create a host and start listening. 383 AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0); 384 host.deleteHost(); 385 host.startListening(); 386 387 // We want to bind widgets. 388 grantBindAppWidgetPermission(); 389 try { 390 // Allocate the first widget id to bind. 391 firstAppWidgetId = host.allocateAppWidgetId(); 392 393 // Allocate the second widget id to bind. 394 secondAppWidgetId = host.allocateAppWidgetId(); 395 396 // Grab the first provider we defined to be bound. 397 AppWidgetProviderInfo firstProvider = getFirstAppWidgetProviderInfo(); 398 399 // Bind the first widget. 400 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 401 firstProvider.getProfile(), firstProvider.provider, null); 402 403 // Wait for onEnabled and onUpdate 404 waitForCallCount(invocationCounter, 2); 405 406 // Grab the second provider we defined to be bound. 407 AppWidgetProviderInfo secondProvider = getSecondAppWidgetProviderInfo(); 408 409 // Bind the second widget. 410 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 411 secondProvider.getProfile(), secondProvider.provider, null); 412 413 // Wait for onEnabled and onUpdate 414 waitForCallCount(invocationCounter, 4); 415 416 // Delete the first widget. 417 host.deleteAppWidgetId(firstAppWidgetId); 418 419 // Wait for onDeleted and onDisabled 420 waitForCallCount(invocationCounter, 6); 421 422 // Delete the second widget. 423 host.deleteAppWidgetId(secondAppWidgetId); 424 425 // Wait for onDeleted and onDisabled 426 waitForCallCount(invocationCounter, 8); 427 428 // Make sure the first provider callbacks are correct. 429 InOrder firstInOrder = inOrder(firstCallbacks); 430 firstInOrder.verify(firstCallbacks).onEnabled(any(Context.class)); 431 firstInOrder.verify(firstCallbacks).onUpdate(any(Context.class), 432 any(AppWidgetManager.class), eq(new int[]{firstAppWidgetId})); 433 firstInOrder.verify(firstCallbacks).onDeleted(any(Context.class), 434 argThat(new WidgetIdsMatcher(new int[]{firstAppWidgetId}))); 435 firstInOrder.verify(firstCallbacks).onDisabled(any(Context.class)); 436 437 // Make sure the second provider callbacks are correct. 438 InOrder secondInOrder = inOrder(secondCallbacks); 439 secondInOrder.verify(secondCallbacks).onEnabled(any(Context.class)); 440 secondInOrder.verify(secondCallbacks).onUpdate(any(Context.class), 441 any(AppWidgetManager.class), eq(new int[]{secondAppWidgetId})); 442 secondInOrder.verify(secondCallbacks).onDeleted(any(Context.class), 443 argThat(new WidgetIdsMatcher(new int[] {secondAppWidgetId}))); 444 secondInOrder.verify(secondCallbacks).onDisabled(any(Context.class)); 445 } finally { 446 // Clean up. 447 host.deleteAppWidgetId(firstAppWidgetId); 448 host.deleteAppWidgetId(secondAppWidgetId); 449 host.deleteHost(); 450 FirstAppWidgetProvider.setCallbacks(null); 451 SecondAppWidgetProvider.setCallbacks(null); 452 revokeBindAppWidgetPermission(); 453 } 454 } 455 456 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 457 @Test testGetAppWidgetIdsForProvider()458 public void testGetAppWidgetIdsForProvider() throws Exception { 459 // We want to bind widgets. 460 grantBindAppWidgetPermission(); 461 462 // Create a host and start listening. 463 AppWidgetHost host = new AppWidgetHost( 464 getInstrumentation().getTargetContext(), 0); 465 host.deleteHost(); 466 host.startListening(); 467 468 int firstAppWidgetId = 0; 469 int secondAppWidgetId = 0; 470 471 try { 472 // Grab the provider we defined to be bound. 473 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 474 475 // Initially we have no widgets. 476 int[] widgetsIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 477 assertTrue(widgetsIds.length == 0); 478 479 // Allocate the first widget id to bind. 480 firstAppWidgetId = host.allocateAppWidgetId(); 481 482 // Bind the first widget. 483 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 484 provider.getProfile(), provider.provider, null); 485 486 // Allocate the second widget id to bind. 487 secondAppWidgetId = host.allocateAppWidgetId(); 488 489 // Bind the second widget. 490 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 491 provider.getProfile(), provider.provider, null); 492 493 // Now we have two widgets, 494 widgetsIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 495 assertTrue(Arrays.equals(widgetsIds, new int[]{firstAppWidgetId, secondAppWidgetId})); 496 } finally { 497 // Clean up. 498 host.deleteAppWidgetId(firstAppWidgetId); 499 host.deleteAppWidgetId(secondAppWidgetId); 500 host.deleteHost(); 501 revokeBindAppWidgetPermission(); 502 } 503 } 504 505 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 506 @Test testGetAppWidgetInfo()507 public void testGetAppWidgetInfo() throws Exception { 508 // We want to bind widgets. 509 grantBindAppWidgetPermission(); 510 511 // Create a host and start listening. 512 AppWidgetHost host = new AppWidgetHost( 513 getInstrumentation().getTargetContext(), 0); 514 host.deleteHost(); 515 host.startListening(); 516 517 int appWidgetId = 0; 518 519 try { 520 // Allocate an widget id to bind. 521 appWidgetId = host.allocateAppWidgetId(); 522 523 // The widget is not bound, so no info. 524 AppWidgetProviderInfo foundProvider = getAppWidgetManager() 525 .getAppWidgetInfo(appWidgetId); 526 assertNull(foundProvider); 527 528 // Grab the provider we defined to be bound. 529 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 530 531 // Bind the app widget. 532 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 533 provider.getProfile(), provider.provider, null); 534 535 // The widget is bound, so the provider info should be there. 536 foundProvider = getAppWidgetManager().getAppWidgetInfo(appWidgetId); 537 assertEquals(provider.provider, foundProvider.provider); 538 assertEquals(provider.getProfile(), foundProvider.getProfile()); 539 540 Context context = getInstrumentation().getTargetContext(); 541 542 // Let us make sure the provider info is sane. 543 String label = foundProvider.loadLabel(context.getPackageManager()); 544 assertTrue(!TextUtils.isEmpty(label)); 545 546 Drawable icon = foundProvider.loadIcon(context, DisplayMetrics.DENSITY_DEFAULT); 547 assertNotNull(icon); 548 549 Drawable previewImage = foundProvider.loadPreviewImage(context, 0); 550 assertNotNull(previewImage); 551 552 assertThat(foundProvider.loadDescription(context)).isEqualTo("Widget description"); 553 554 assertThat(foundProvider.previewLayout).isEqualTo(R.layout.preview_layout); 555 } finally { 556 // Clean up. 557 host.deleteAppWidgetId(appWidgetId); 558 host.deleteHost(); 559 revokeBindAppWidgetPermission(); 560 } 561 } 562 563 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 564 @Test testGetAppWidgetOptions()565 public void testGetAppWidgetOptions() throws Exception { 566 // We want to bind widgets. 567 grantBindAppWidgetPermission(); 568 569 // Create a host and start listening. 570 AppWidgetHost host = new AppWidgetHost( 571 getInstrumentation().getTargetContext(), 0); 572 host.deleteHost(); 573 host.startListening(); 574 575 int appWidgetId = 0; 576 577 try { 578 // Grab the provider we defined to be bound. 579 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 580 581 // Allocate an widget id to bind. 582 appWidgetId = host.allocateAppWidgetId(); 583 584 // Initially we have no options. 585 Bundle foundOptions = getAppWidgetManager().getAppWidgetOptions(appWidgetId); 586 assertTrue(foundOptions.isEmpty()); 587 588 // We want to set the options when binding. 589 Bundle setOptions = new Bundle(); 590 setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 1); 591 setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 2); 592 setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 3); 593 setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 4); 594 595 // Bind the app widget. 596 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 597 provider.getProfile(), provider.provider, setOptions); 598 599 // Make sure we get the options used when binding. 600 foundOptions = getAppWidgetManager().getAppWidgetOptions(appWidgetId); 601 assertTrue(equalOptions(setOptions, foundOptions)); 602 } finally { 603 // Clean up. 604 host.deleteAppWidgetId(appWidgetId); 605 host.deleteHost(); 606 revokeBindAppWidgetPermission(); 607 } 608 } 609 610 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 611 @Test testDeleteHost()612 public void testDeleteHost() throws Exception { 613 // We want to bind widgets. 614 grantBindAppWidgetPermission(); 615 616 // Create a host and start listening. 617 AppWidgetHost host = new AppWidgetHost( 618 getInstrumentation().getTargetContext(), 0); 619 host.deleteHost(); 620 host.startListening(); 621 622 int appWidgetId = 0; 623 624 try { 625 // Allocate an widget id to bind. 626 appWidgetId = host.allocateAppWidgetId(); 627 628 // Grab the provider we defined to be bound. 629 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 630 631 // Bind the app widget. 632 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 633 provider.getProfile(), provider.provider, null); 634 635 // The widget should be there. 636 int[] widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 637 assertTrue(Arrays.equals(widgetIds, new int[]{appWidgetId})); 638 639 // Delete the host. 640 host.deleteHost(); 641 642 // The host is gone and with it the widgets. 643 widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 644 assertTrue(widgetIds.length == 0); 645 } finally { 646 // Clean up. 647 host.deleteAppWidgetId(appWidgetId); 648 host.deleteHost(); 649 revokeBindAppWidgetPermission(); 650 } 651 } 652 653 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 654 @Test testDeleteHosts()655 public void testDeleteHosts() throws Exception { 656 // We want to bind widgets. 657 grantBindAppWidgetPermission(); 658 659 // Create the first host and start listening. 660 AppWidgetHost firstHost = new AppWidgetHost( 661 getInstrumentation().getTargetContext(), 0); 662 firstHost.deleteHost(); 663 firstHost.startListening(); 664 665 // Create the second host and start listening. 666 AppWidgetHost secondHost = new AppWidgetHost( 667 getInstrumentation().getTargetContext(), 1); 668 secondHost.deleteHost(); 669 secondHost.startListening(); 670 671 int firstAppWidgetId = 0; 672 int secondAppWidgetId = 0; 673 674 try { 675 // Grab the provider we defined to be bound. 676 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 677 678 // Allocate the first widget id to bind. 679 firstAppWidgetId = firstHost.allocateAppWidgetId(); 680 681 // Bind the first app widget. 682 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 683 provider.getProfile(), provider.provider, null); 684 685 // Allocate the second widget id to bind. 686 secondAppWidgetId = secondHost.allocateAppWidgetId(); 687 688 // Bind the second app widget. 689 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 690 provider.getProfile(), provider.provider, null); 691 692 // The widgets should be there. 693 int[] widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 694 assertTrue(Arrays.equals(widgetIds, new int[]{firstAppWidgetId, secondAppWidgetId})); 695 696 // Delete all hosts. 697 AppWidgetHost.deleteAllHosts(); 698 699 // The hosts are gone and with it the widgets. 700 widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 701 assertTrue(widgetIds.length == 0); 702 } finally { 703 // Clean up. 704 firstHost.deleteAppWidgetId(firstAppWidgetId); 705 secondHost.deleteAppWidgetId(secondAppWidgetId); 706 AppWidgetHost.deleteAllHosts(); 707 revokeBindAppWidgetPermission(); 708 } 709 } 710 711 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 712 @Test testOnProvidersChanged()713 public void testOnProvidersChanged() throws Exception { 714 // We want to bind widgets. 715 grantBindAppWidgetPermission(); 716 717 final AtomicInteger onProvidersChangedCallCounter = new AtomicInteger(); 718 719 // Create a host and start listening. 720 AppWidgetHost host = new AppWidgetHost( 721 getInstrumentation().getTargetContext(), 0) { 722 @Override 723 public void onProvidersChanged() { 724 synchronized (mLock) { 725 onProvidersChangedCallCounter.incrementAndGet(); 726 mLock.notifyAll(); 727 } 728 } 729 }; 730 host.deleteHost(); 731 host.startListening(); 732 733 int appWidgetId = 0; 734 735 try { 736 // Grab the provider we defined to be bound. 737 AppWidgetProviderInfo firstLookupProvider = getFirstAppWidgetProviderInfo(); 738 739 // Allocate a widget id to bind. 740 appWidgetId = host.allocateAppWidgetId(); 741 742 // Bind the first app widget. 743 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 744 firstLookupProvider.getProfile(), firstLookupProvider.provider, null); 745 746 // Disable the provider we just bound to. 747 PackageManager packageManager = getInstrumentation().getTargetContext() 748 .getApplicationContext().getPackageManager(); 749 packageManager.setComponentEnabledSetting(firstLookupProvider.provider, 750 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 751 PackageManager.DONT_KILL_APP); 752 753 // Wait for the package change to propagate. 754 waitForCallCount(onProvidersChangedCallCounter, 1); 755 756 // The provider should not be present anymore. 757 AppWidgetProviderInfo secondLookupProvider = getFirstAppWidgetProviderInfo(); 758 assertNull(secondLookupProvider); 759 760 // Enable the provider we disabled. 761 packageManager.setComponentEnabledSetting(firstLookupProvider.provider, 762 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 763 PackageManager.DONT_KILL_APP); 764 765 // Wait for the package change to propagate. 766 waitForCallCount(onProvidersChangedCallCounter, 2); 767 } finally { 768 // Clean up. 769 host.deleteAppWidgetId(appWidgetId); 770 host.deleteHost(); 771 revokeBindAppWidgetPermission(); 772 } 773 } 774 775 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 776 @Test testAppWidgetRemoved()777 public void testAppWidgetRemoved() throws Exception { 778 779 // We want to bind widgets. 780 grantBindAppWidgetPermission(); 781 782 final AtomicInteger onAppWidgetRemovedCounter = new AtomicInteger(); 783 IntConsumer callback = mock(IntConsumer.class); 784 785 // Create a host and start listening. 786 AppWidgetHost host = new AppWidgetHost( 787 getInstrumentation().getTargetContext(), 0) { 788 @Override 789 public void onAppWidgetRemoved(int widgetId) { 790 synchronized (mLock) { 791 onAppWidgetRemovedCounter.incrementAndGet(); 792 mLock.notifyAll(); 793 callback.accept(widgetId); 794 } 795 } 796 }; 797 host.deleteHost(); 798 host.startListening(); 799 800 int firstAppWidgetId = 0; 801 int secondAppWidgetId = 0; 802 803 try { 804 // Grab the provider we defined to be bound. 805 AppWidgetProviderInfo firstProviderInfo = getFirstAppWidgetProviderInfo(); 806 AppWidgetProviderInfo secondProviderInfo = getSecondAppWidgetProviderInfo(); 807 808 // Allocate widget id to bind. 809 firstAppWidgetId = host.allocateAppWidgetId(); 810 secondAppWidgetId = host.allocateAppWidgetId(); 811 812 //create listeners 813 MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener = 814 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 815 816 // Bind the first app widget. 817 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 818 firstProviderInfo.getProfile(), firstProviderInfo.provider, null); 819 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 820 secondProviderInfo.getProfile(), secondProviderInfo.provider, null); 821 822 // Disable the first widget while host is listening 823 PackageManager packageManager = getInstrumentation().getTargetContext() 824 .getApplicationContext().getPackageManager(); 825 packageManager.setComponentEnabledSetting(firstProviderInfo.provider, 826 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 827 PackageManager.DONT_KILL_APP); 828 829 waitForCallCount(onAppWidgetRemovedCounter, 1); 830 831 // Disable the second widget while host is paused 832 host.stopListening(); 833 packageManager.setComponentEnabledSetting(secondProviderInfo.provider, 834 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 835 PackageManager.DONT_KILL_APP); 836 837 838 assertEquals(onAppWidgetRemovedCounter.get(),1); 839 verify(callback).accept(eq(firstAppWidgetId)); 840 841 // resume listening 842 host.startListening(); 843 844 // Wait for the package change to propagate. 845 waitForCallCount(onAppWidgetRemovedCounter, 2); 846 verify(callback).accept(eq(secondAppWidgetId)); 847 848 // Reenable disabled items 849 packageManager.setComponentEnabledSetting(secondProviderInfo.provider, 850 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 851 PackageManager.DONT_KILL_APP); 852 packageManager.setComponentEnabledSetting(firstProviderInfo.provider, 853 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 854 PackageManager.DONT_KILL_APP); 855 856 } finally { 857 // Clean up. 858 host.deleteAppWidgetId(firstAppWidgetId); 859 host.deleteAppWidgetId(secondAppWidgetId); 860 host.deleteHost(); 861 revokeBindAppWidgetPermission(); 862 } 863 } 864 865 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 866 @Test testUpdateAppWidgetViaComponentName()867 public void testUpdateAppWidgetViaComponentName() throws Exception { 868 // We want to bind widgets. 869 grantBindAppWidgetPermission(); 870 871 final AtomicInteger updateAppWidgetCallCount = new AtomicInteger(); 872 873 // Create a host and start listening. 874 AppWidgetHost host = new AppWidgetHost( 875 getInstrumentation().getTargetContext(), 0) { 876 @Override 877 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 878 AppWidgetProviderInfo appWidget) { 879 return new MyAppWidgetHostView(context); 880 } 881 }; 882 host.deleteHost(); 883 host.startListening(); 884 885 int firstAppWidgetId = 0; 886 int secondAppWidgetId = 0; 887 888 try { 889 // Grab the provider to be bound. 890 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 891 892 // Allocate the first widget id to bind. 893 firstAppWidgetId = host.allocateAppWidgetId(); 894 895 // Bind the first app widget. 896 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 897 provider.getProfile(), provider.provider, null); 898 899 // Create the first host view. 900 MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView( 901 getInstrumentation().getContext(), firstAppWidgetId, provider); 902 MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener = 903 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 904 firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener); 905 906 // Allocate the second widget id to bind. 907 secondAppWidgetId = host.allocateAppWidgetId(); 908 909 // Bind the second app widget. 910 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 911 provider.getProfile(), provider.provider, null); 912 913 // Create the second host view. 914 MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView( 915 getInstrumentation().getContext(), secondAppWidgetId, provider); 916 MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener = 917 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 918 doAnswer(new Answer<Void>() { 919 @Override 920 public Void answer(InvocationOnMock invocation) throws Throwable { 921 synchronized (mLock) { 922 updateAppWidgetCallCount.incrementAndGet(); 923 mLock.notifyAll(); 924 } 925 return null; 926 } 927 }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 928 secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener); 929 930 // Update all app widgets. 931 final RemoteViews content = new RemoteViews( 932 getInstrumentation().getContext().getPackageName(), 933 R.layout.second_initial_layout); 934 getAppWidgetManager().updateAppWidget(provider.provider, content); 935 936 waitForCallCount(updateAppWidgetCallCount, 1); 937 938 // Verify the expected callbacks. 939 InOrder firstInOrder = inOrder(firstAppHostViewListener); 940 firstInOrder.verify(firstAppHostViewListener).onUpdateAppWidget(argThat( 941 new RemoteViewsMatcher(content.getLayoutId(), 942 provider.provider.getPackageName()))); 943 944 InOrder secondInOrder = inOrder(secondAppHostViewListener); 945 secondInOrder.verify(secondAppHostViewListener).onUpdateAppWidget(argThat( 946 new RemoteViewsMatcher(content.getLayoutId(), 947 provider.provider.getPackageName()))); 948 } finally { 949 // Clean up. 950 host.deleteAppWidgetId(firstAppWidgetId); 951 host.deleteAppWidgetId(secondAppWidgetId); 952 host.deleteHost(); 953 revokeBindAppWidgetPermission(); 954 } 955 } 956 957 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 958 @Test testUpdateAppWidgetViaWidgetId()959 public void testUpdateAppWidgetViaWidgetId() throws Exception { 960 // We want to bind widgets. 961 grantBindAppWidgetPermission(); 962 963 final AtomicInteger updateAppWidgetCallCount = new AtomicInteger(); 964 965 // Create a host and start listening. 966 AppWidgetHost host = new AppWidgetHost( 967 getInstrumentation().getTargetContext(), 0) { 968 @Override 969 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 970 AppWidgetProviderInfo appWidget) { 971 return new MyAppWidgetHostView(context); 972 } 973 }; 974 host.deleteHost(); 975 host.startListening(); 976 977 int firstAppWidgetId = 0; 978 979 try { 980 // Grab the provider to be bound. 981 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 982 983 // Allocate the first widget id to bind. 984 firstAppWidgetId = host.allocateAppWidgetId(); 985 986 // Bind the first app widget. 987 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 988 provider.getProfile(), provider.provider, null); 989 990 // Create the first host view. 991 MyAppWidgetHostView hostView = (MyAppWidgetHostView) host.createView( 992 getInstrumentation().getContext(), firstAppWidgetId, provider); 993 MyAppWidgetHostView.OnUpdateAppWidgetListener appHostViewListener = 994 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 995 doAnswer(new Answer<Void>() { 996 @Override 997 public Void answer(InvocationOnMock invocation) throws Throwable { 998 synchronized (mLock) { 999 updateAppWidgetCallCount.incrementAndGet(); 1000 mLock.notifyAll(); 1001 } 1002 return null; 1003 } 1004 }).when(appHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 1005 hostView.setOnUpdateAppWidgetListener(appHostViewListener); 1006 1007 // Update all app widgets. 1008 RemoteViews content = new RemoteViews( 1009 getInstrumentation().getContext().getPackageName(), 1010 R.layout.second_initial_layout); 1011 getAppWidgetManager().updateAppWidget(firstAppWidgetId, content); 1012 1013 waitForCallCount(updateAppWidgetCallCount, 1); 1014 1015 // Verify the expected callbacks. 1016 InOrder inOrder = inOrder(appHostViewListener); 1017 inOrder.verify(appHostViewListener).onUpdateAppWidget(argThat( 1018 new RemoteViewsMatcher(content.getLayoutId(), 1019 provider.provider.getPackageName()) 1020 )); 1021 } finally { 1022 // Clean up. 1023 host.deleteAppWidgetId(firstAppWidgetId); 1024 host.deleteHost(); 1025 revokeBindAppWidgetPermission(); 1026 } 1027 } 1028 1029 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1030 @Test testUpdateAppWidgetViaWidgetIds()1031 public void testUpdateAppWidgetViaWidgetIds() throws Exception { 1032 // We want to bind widgets. 1033 grantBindAppWidgetPermission(); 1034 1035 final AtomicInteger onUpdateAppWidgetCallCount = new AtomicInteger(); 1036 1037 // Create a host and start listening. 1038 AppWidgetHost host = new AppWidgetHost( 1039 getInstrumentation().getTargetContext(), 0) { 1040 @Override 1041 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 1042 AppWidgetProviderInfo appWidget) { 1043 return new MyAppWidgetHostView(context); 1044 } 1045 }; 1046 host.deleteHost(); 1047 host.startListening(); 1048 1049 int firstAppWidgetId = 0; 1050 int secondAppWidgetId = 0; 1051 1052 try { 1053 // Grab the provider to be bound. 1054 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 1055 1056 // Allocate the first widget id to bind. 1057 firstAppWidgetId = host.allocateAppWidgetId(); 1058 1059 // Bind the first app widget. 1060 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 1061 provider.getProfile(), provider.provider, null); 1062 1063 // Create the first host view. 1064 MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView( 1065 getInstrumentation().getContext(), firstAppWidgetId, provider); 1066 MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener = 1067 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 1068 firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener); 1069 1070 // Allocate the second widget id to bind. 1071 secondAppWidgetId = host.allocateAppWidgetId(); 1072 1073 // Bind the second app widget. 1074 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 1075 provider.getProfile(), provider.provider, null); 1076 1077 // Create the second host view. 1078 MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView( 1079 getInstrumentation().getContext(), secondAppWidgetId, provider); 1080 MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener = 1081 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 1082 doAnswer(new Answer<Void>() { 1083 @Override 1084 public Void answer(InvocationOnMock invocation) throws Throwable { 1085 synchronized (mLock) { 1086 onUpdateAppWidgetCallCount.incrementAndGet(); 1087 mLock.notifyAll(); 1088 } 1089 return null; 1090 } 1091 }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 1092 secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener); 1093 1094 // Update all app widgets. 1095 RemoteViews content = new RemoteViews( 1096 getInstrumentation().getContext().getPackageName(), 1097 R.layout.second_initial_layout); 1098 getAppWidgetManager().updateAppWidget(new int[] {firstAppWidgetId, 1099 secondAppWidgetId}, content); 1100 1101 waitForCallCount(onUpdateAppWidgetCallCount, 1); 1102 1103 // Verify the expected callbacks. 1104 InOrder firstInOrder = inOrder(firstAppHostViewListener); 1105 firstInOrder.verify(firstAppHostViewListener).onUpdateAppWidget( 1106 argThat(new RemoteViewsMatcher(content.getLayoutId(), 1107 provider.provider.getPackageName()))); 1108 1109 InOrder secondInOrder = inOrder(secondAppHostViewListener); 1110 secondInOrder.verify(secondAppHostViewListener).onUpdateAppWidget( 1111 argThat(new RemoteViewsMatcher(content.getLayoutId(), 1112 provider.provider.getPackageName())) 1113 ); 1114 } finally { 1115 // Clean up. 1116 host.deleteAppWidgetId(firstAppWidgetId); 1117 host.deleteAppWidgetId(secondAppWidgetId); 1118 host.deleteHost(); 1119 revokeBindAppWidgetPermission(); 1120 } 1121 } 1122 1123 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1124 @Test testPartiallyUpdateAppWidgetViaWidgetId()1125 public void testPartiallyUpdateAppWidgetViaWidgetId() throws Exception { 1126 // We want to bind widgets. 1127 grantBindAppWidgetPermission(); 1128 1129 final AtomicInteger updateAppWidgetCallCount = new AtomicInteger(); 1130 1131 // Create a host and start listening. 1132 AppWidgetHost host = new AppWidgetHost( 1133 getInstrumentation().getTargetContext(), 0) { 1134 @Override 1135 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 1136 AppWidgetProviderInfo appWidget) { 1137 return new MyAppWidgetHostView(context); 1138 } 1139 }; 1140 host.deleteHost(); 1141 host.startListening(); 1142 1143 int firstAppWidgetId = 0; 1144 1145 try { 1146 // Grab the provider to be bound. 1147 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 1148 1149 // Allocate the first widget id to bind. 1150 firstAppWidgetId = host.allocateAppWidgetId(); 1151 1152 // Bind the first app widget. 1153 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 1154 provider.getProfile(), provider.provider, null); 1155 1156 // Create the first host view. 1157 MyAppWidgetHostView hostView = (MyAppWidgetHostView) host.createView( 1158 getInstrumentation().getContext(), firstAppWidgetId, provider); 1159 MyAppWidgetHostView.OnUpdateAppWidgetListener appHostViewListener = 1160 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 1161 doAnswer(new Answer<Void>() { 1162 @Override 1163 public Void answer(InvocationOnMock invocation) throws Throwable { 1164 synchronized (mLock) { 1165 updateAppWidgetCallCount.incrementAndGet(); 1166 mLock.notifyAll(); 1167 } 1168 return null; 1169 } 1170 }).when(appHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 1171 hostView.setOnUpdateAppWidgetListener(appHostViewListener); 1172 1173 // Set the content for all app widgets. 1174 RemoteViews content = new RemoteViews( 1175 getInstrumentation().getContext().getPackageName(), 1176 R.layout.first_initial_layout); 1177 getAppWidgetManager().updateAppWidget(firstAppWidgetId, content); 1178 1179 waitForCallCount(updateAppWidgetCallCount, 1); 1180 1181 // Partially update the content for all app widgets (pretend we changed something). 1182 getAppWidgetManager().partiallyUpdateAppWidget(firstAppWidgetId, content); 1183 1184 waitForCallCount(updateAppWidgetCallCount, 2); 1185 1186 // Verify the expected callbacks. 1187 InOrder inOrder = inOrder(appHostViewListener); 1188 inOrder.verify(appHostViewListener, times(2)).onUpdateAppWidget( 1189 argThat(new RemoteViewsMatcher(content.getLayoutId(), 1190 provider.provider.getPackageName()))); 1191 } finally { 1192 // Clean up. 1193 host.deleteAppWidgetId(firstAppWidgetId); 1194 host.deleteHost(); 1195 revokeBindAppWidgetPermission(); 1196 } 1197 } 1198 1199 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1200 @Test testPartiallyUpdateAppWidgetViaWidgetIds()1201 public void testPartiallyUpdateAppWidgetViaWidgetIds() throws Exception { 1202 // We want to bind widgets. 1203 grantBindAppWidgetPermission(); 1204 1205 final AtomicInteger firstAppWidgetCallCounter = new AtomicInteger(); 1206 final AtomicInteger secondAppWidgetCallCounter = new AtomicInteger(); 1207 1208 // Create a host and start listening. 1209 AppWidgetHost host = new AppWidgetHost( 1210 getInstrumentation().getTargetContext(), 0) { 1211 @Override 1212 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 1213 AppWidgetProviderInfo appWidget) { 1214 return new MyAppWidgetHostView(context); 1215 } 1216 }; 1217 host.deleteHost(); 1218 host.startListening(); 1219 1220 int firstAppWidgetId = 0; 1221 int secondAppWidgetId = 0; 1222 1223 try { 1224 // Grab the provider to be bound. 1225 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 1226 1227 // Allocate the first widget id to bind. 1228 firstAppWidgetId = host.allocateAppWidgetId(); 1229 1230 // Bind the first app widget. 1231 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 1232 provider.getProfile(), provider.provider, null); 1233 1234 // Create the first host view. 1235 MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView( 1236 getInstrumentation().getContext(), firstAppWidgetId, provider); 1237 MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener = 1238 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 1239 doAnswer(new Answer<Void>() { 1240 @Override 1241 public Void answer(InvocationOnMock invocation) throws Throwable { 1242 synchronized (mLock) { 1243 firstAppWidgetCallCounter.incrementAndGet(); 1244 mLock.notifyAll(); 1245 } 1246 return null; 1247 } 1248 }).when(firstAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 1249 firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener); 1250 1251 // Allocate the second widget id to bind. 1252 secondAppWidgetId = host.allocateAppWidgetId(); 1253 1254 // Bind the second app widget. 1255 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 1256 provider.getProfile(), provider.provider, null); 1257 1258 // Create the second host view. 1259 MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView( 1260 getInstrumentation().getContext(), secondAppWidgetId, provider); 1261 MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener = 1262 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 1263 doAnswer(new Answer<Void>() { 1264 @Override 1265 public Void answer(InvocationOnMock invocation) throws Throwable { 1266 synchronized (mLock) { 1267 secondAppWidgetCallCounter.incrementAndGet(); 1268 mLock.notifyAll(); 1269 } 1270 return null; 1271 } 1272 }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 1273 secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener); 1274 1275 // Set the content for all app widgets. 1276 RemoteViews content = new RemoteViews( 1277 getInstrumentation().getContext().getPackageName(), 1278 R.layout.first_initial_layout); 1279 getAppWidgetManager().updateAppWidget(new int[]{firstAppWidgetId, 1280 secondAppWidgetId}, content); 1281 1282 waitForCallCount(firstAppWidgetCallCounter, 1); 1283 waitForCallCount(secondAppWidgetCallCounter, 1); 1284 1285 // Partially update the content for all app widgets (pretend we changed something). 1286 getAppWidgetManager().partiallyUpdateAppWidget(new int[] {firstAppWidgetId, 1287 secondAppWidgetId}, content); 1288 1289 waitForCallCount(firstAppWidgetCallCounter, 2); 1290 waitForCallCount(secondAppWidgetCallCounter, 2); 1291 1292 // Verify the expected callbacks. 1293 InOrder firstInOrder = inOrder(firstAppHostViewListener); 1294 firstInOrder.verify(firstAppHostViewListener, times(2)).onUpdateAppWidget( 1295 argThat(new RemoteViewsMatcher(content.getLayoutId(), 1296 provider.provider.getPackageName()))); 1297 1298 InOrder secondInOrder = inOrder(secondAppHostViewListener); 1299 secondInOrder.verify(secondAppHostViewListener, times(2)).onUpdateAppWidget( 1300 argThat(new RemoteViewsMatcher(content.getLayoutId(), 1301 provider.provider.getPackageName()))); 1302 } finally { 1303 // Clean up. 1304 host.deleteAppWidgetId(firstAppWidgetId); 1305 host.deleteAppWidgetId(secondAppWidgetId); 1306 host.deleteHost(); 1307 revokeBindAppWidgetPermission(); 1308 } 1309 } 1310 1311 @Ignore("b/355285596") 1312 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1313 @Test testCollectionWidgets()1314 public void testCollectionWidgets() throws Exception { 1315 // We want to bind widgets. 1316 grantBindAppWidgetPermission(); 1317 1318 final AtomicInteger invocationCounter = new AtomicInteger(); 1319 final Context context = getInstrumentation().getTargetContext(); 1320 1321 // Create a host and start listening. 1322 final AppWidgetHost host = new AppWidgetHost(context, 0); 1323 host.deleteHost(); 1324 host.startListening(); 1325 1326 final int appWidgetId; 1327 1328 try { 1329 // Configure the provider behavior. 1330 AppWidgetProviderCallbacks callbacks = createAppWidgetProviderCallbacks( 1331 invocationCounter); 1332 doAnswer(new Answer<Void>() { 1333 @Override 1334 public Void answer(InvocationOnMock invocation) throws Throwable { 1335 final int appWidgetId = ((int[]) invocation.getArguments()[2])[0]; 1336 1337 Intent intent = new Intent(context, MyAppWidgetService.class); 1338 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 1339 intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); 1340 1341 RemoteViews removeViews = new RemoteViews(context.getPackageName(), 1342 R.layout.collection_widget_layout); 1343 removeViews.setRemoteAdapter(R.id.stack_view, intent); 1344 1345 getAppWidgetManager().updateAppWidget(appWidgetId, removeViews); 1346 1347 synchronized (mLock) { 1348 invocationCounter.incrementAndGet(); 1349 mLock.notifyAll(); 1350 } 1351 1352 return null; 1353 } 1354 }).when(callbacks).onUpdate(any(Context.class), any(AppWidgetManager.class), 1355 any(int[].class)); 1356 FirstAppWidgetProvider.setCallbacks(callbacks); 1357 1358 // Grab the provider to be bound. 1359 final AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 1360 1361 // Allocate a widget id to bind. 1362 appWidgetId = host.allocateAppWidgetId(); 1363 1364 // Bind the app widget. 1365 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 1366 provider.getProfile(), provider.provider, null); 1367 1368 // Wait for onEnabled and onUpdate 1369 waitForCallCount(invocationCounter, 2); 1370 1371 // Configure the app widget service behavior. 1372 RemoteViewsFactory factory = mock(RemoteViewsFactory.class); 1373 doAnswer(new Answer<Integer>() { 1374 @Override 1375 public Integer answer(InvocationOnMock invocation) throws Throwable { 1376 return 1; 1377 } 1378 }).when(factory).getCount(); 1379 doAnswer(new Answer<RemoteViews>() { 1380 @Override 1381 public RemoteViews answer(InvocationOnMock invocation) throws Throwable { 1382 RemoteViews remoteViews = new RemoteViews(context.getPackageName(), 1383 R.layout.collection_widget_item_layout); 1384 remoteViews.setTextViewText(R.id.text_view, context.getText(R.string.foo)); 1385 synchronized (mLock) { 1386 invocationCounter.incrementAndGet(); 1387 } 1388 return remoteViews; 1389 } 1390 }).when(factory).getViewAt(any(int.class)); 1391 doAnswer(new Answer<Integer>() { 1392 @Override 1393 public Integer answer(InvocationOnMock invocation) throws Throwable { 1394 return 1; 1395 } 1396 }).when(factory).getViewTypeCount(); 1397 MyAppWidgetService.setFactory(factory); 1398 1399 getInstrumentation().runOnMainSync(new Runnable() { 1400 @Override 1401 public void run() { 1402 host.createView(context, appWidgetId, provider); 1403 } 1404 }); 1405 1406 // Wait for the interactions to occur. 1407 waitForCallCount(invocationCounter, 3); 1408 1409 // Verify the interactions. 1410 verify(factory, atLeastOnce()).hasStableIds(); 1411 verify(factory, atLeastOnce()).getViewTypeCount(); 1412 verify(factory, atLeastOnce()).getCount(); 1413 verify(factory, atLeastOnce()).getLoadingView(); 1414 verify(factory, atLeastOnce()).getViewAt(same(0)); 1415 } finally { 1416 // Clean up. 1417 FirstAppWidgetProvider.setCallbacks(null); 1418 host.deleteHost(); 1419 revokeBindAppWidgetPermission(); 1420 } 1421 } 1422 1423 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1424 @Test testWidgetFeaturesParsed()1425 public void testWidgetFeaturesParsed() throws Exception { 1426 assertEquals(0, getFirstAppWidgetProviderInfo().widgetFeatures); 1427 String packageName = getInstrumentation().getTargetContext().getPackageName(); 1428 1429 assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE, 1430 getProviderInfo(new ComponentName(packageName, 1431 AppWidgetProviderWithFeatures.Provider1.class.getName())).widgetFeatures); 1432 assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER, 1433 getProviderInfo(new ComponentName(packageName, 1434 AppWidgetProviderWithFeatures.Provider2.class.getName())).widgetFeatures); 1435 assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE 1436 | AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER, 1437 getProviderInfo(new ComponentName(packageName, 1438 AppWidgetProviderWithFeatures.Provider3.class.getName())).widgetFeatures); 1439 } 1440 1441 1442 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1443 @Test testAppWidgetGetActivityInfo()1444 public void testAppWidgetGetActivityInfo() { 1445 AppWidgetProviderInfo info = getFirstAppWidgetProviderInfo(); 1446 assertNotNull(info.getActivityInfo()); 1447 assertEquals(info.provider.getClassName(), info.getActivityInfo().name); 1448 assertEquals(info.provider.getPackageName(), info.getActivityInfo().packageName); 1449 } 1450 1451 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1452 @RequiresFlagsEnabled(Flags.FLAG_GENERATED_PREVIEWS) 1453 @ApiTest(apis = { 1454 "android.appwidget.AppWidgetManager#setWidgetPreview", 1455 "android.appwidget.AppWidgetManager#getWidgetPreview", 1456 "android.appwidget.AppWidgetManager#removeWidgetPreview", 1457 "android.appwidget.AppWidgetProviderInfo#generatedPreviewCategories", 1458 }) 1459 @Test testSetGeneratedPreview()1460 public void testSetGeneratedPreview() { 1461 // AppWidgetHost is used to wait until provider change notification is sent. 1462 final Context context = getInstrumentation().getTargetContext(); 1463 final AppWidgetHostWithLatch host = new AppWidgetHostWithLatch(context, 0); 1464 host.deleteHost(); 1465 host.startListening(); 1466 1467 try { 1468 // Disable setWidgetPreview rate limit for tests 1469 setGeneratedPreviewRateLimit(/* resetIntervalMs= */ 0, 1470 /* maxCallsPerInterval */ Integer.MAX_VALUE); 1471 1472 final ComponentName provider = getFirstWidgetComponent(); 1473 final AppWidgetProviderInfo initialInfo = getFirstAppWidgetProviderInfo(); 1474 1475 // Clear providers and verify initial state 1476 if (initialInfo.generatedPreviewCategories != 0) { 1477 host.latch = new CountDownLatch(1); 1478 getAppWidgetManager().removeWidgetPreview(provider, 1479 initialInfo.generatedPreviewCategories); 1480 host.latch.await(); 1481 1482 final AppWidgetProviderInfo info = getFirstAppWidgetProviderInfo(); 1483 assertThat(info.generatedPreviewCategories).isEqualTo(0); 1484 assertThat(getAppWidgetManager().getWidgetPreview(info.provider, 1485 info.getProfile(), WIDGET_CATEGORY_HOME_SCREEN)).isNull(); 1486 assertThat(getAppWidgetManager().getWidgetPreview(info.provider, 1487 info.getProfile(), WIDGET_CATEGORY_KEYGUARD)).isNull(); 1488 } 1489 1490 final RemoteViews whiteLayout = new RemoteViews(context.getPackageName(), 1491 R.layout.simple_white_layout); 1492 final RemoteViews blackLayout = new RemoteViews(context.getPackageName(), 1493 R.layout.simple_black_layout); 1494 1495 host.latch = new CountDownLatch(2); 1496 assertThat(getAppWidgetManager().setWidgetPreview(provider, WIDGET_CATEGORY_HOME_SCREEN, 1497 whiteLayout)).isTrue(); 1498 assertThat(getAppWidgetManager().setWidgetPreview(provider, WIDGET_CATEGORY_KEYGUARD, 1499 blackLayout)).isTrue(); 1500 host.latch.await(); 1501 1502 final AppWidgetProviderInfo info = getFirstAppWidgetProviderInfo(); 1503 assertThat(info.generatedPreviewCategories).isEqualTo( 1504 WIDGET_CATEGORY_HOME_SCREEN | WIDGET_CATEGORY_KEYGUARD); 1505 final RemoteViews homePreview = getAppWidgetManager().getWidgetPreview(info.provider, 1506 info.getProfile(), WIDGET_CATEGORY_HOME_SCREEN); 1507 assertThat(homePreview).isNotNull(); 1508 final RemoteViews keyguardPreview = getAppWidgetManager().getWidgetPreview( 1509 info.provider, info.getProfile(), WIDGET_CATEGORY_KEYGUARD); 1510 assertThat(keyguardPreview).isNotNull(); 1511 1512 assertThat(homePreview.getLayoutId()).isEqualTo(whiteLayout.getLayoutId()); 1513 assertThat(keyguardPreview.getLayoutId()).isEqualTo(blackLayout.getLayoutId()); 1514 1515 // Update previews for homescreen 1516 host.latch = new CountDownLatch(1); 1517 assertThat(getAppWidgetManager().setWidgetPreview(provider, WIDGET_CATEGORY_HOME_SCREEN, 1518 blackLayout)).isTrue(); 1519 host.latch.await(); 1520 1521 final RemoteViews updatedPreview = getAppWidgetManager().getWidgetPreview(info.provider, 1522 info.getProfile(), WIDGET_CATEGORY_HOME_SCREEN); 1523 assertThat(updatedPreview).isNotNull(); 1524 assertThat(updatedPreview.getLayoutId()).isEqualTo(blackLayout.getLayoutId()); 1525 1526 // remove previews 1527 host.latch = new CountDownLatch(1); 1528 getAppWidgetManager().removeWidgetPreview(provider, 1529 WIDGET_CATEGORY_HOME_SCREEN | WIDGET_CATEGORY_KEYGUARD); 1530 host.latch.await(); 1531 1532 final AppWidgetProviderInfo newInfo = getFirstAppWidgetProviderInfo(); 1533 assertThat(newInfo.generatedPreviewCategories).isEqualTo(0); 1534 assertThat( 1535 getAppWidgetManager().getWidgetPreview(newInfo.provider, newInfo.getProfile(), 1536 WIDGET_CATEGORY_HOME_SCREEN)).isNull(); 1537 assertThat( 1538 getAppWidgetManager().getWidgetPreview(newInfo.provider, newInfo.getProfile(), 1539 WIDGET_CATEGORY_KEYGUARD)).isNull(); 1540 } catch (InterruptedException e) { 1541 throw new RuntimeException(e); 1542 } finally { 1543 host.deleteHost(); 1544 } 1545 } 1546 1547 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1548 @RequiresFlagsEnabled(Flags.FLAG_GENERATED_PREVIEWS) 1549 @ApiTest(apis = { 1550 "android.appwidget.AppWidgetManager#setWidgetPreview", 1551 "android.appwidget.AppWidgetManager#removeWidgetPreview", 1552 }) 1553 @Test testGeneratedPreviewRateLimiting()1554 public void testGeneratedPreviewRateLimiting() { 1555 // Set rate limit to 1 per 5 seconds. 1556 final long resetIntervalMs = Duration.ofSeconds(5).toMillis(); 1557 setGeneratedPreviewRateLimit(resetIntervalMs, /* maxCallsPerInterval= */ 1); 1558 1559 // Disable and reenable provider to ensure provider's API call count is reset. 1560 final Context context = getInstrumentation().getTargetContext(); 1561 final ComponentName provider = getFirstWidgetComponent(); 1562 context.getPackageManager().setComponentEnabledSetting(provider, 1563 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 1564 /* flags= */ PackageManager.SYNCHRONOUS | PackageManager.DONT_KILL_APP); 1565 context.getPackageManager().setComponentEnabledSetting(provider, 1566 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 1567 /* flags= */ PackageManager.SYNCHRONOUS | PackageManager.DONT_KILL_APP); 1568 1569 final RemoteViews whiteLayout = new RemoteViews(context.getPackageName(), 1570 R.layout.simple_white_layout); 1571 assertThat(getAppWidgetManager().setWidgetPreview(provider, WIDGET_CATEGORY_HOME_SCREEN, 1572 whiteLayout)).isTrue(); 1573 assertThat(getAppWidgetManager().setWidgetPreview(provider, WIDGET_CATEGORY_HOME_SCREEN, 1574 whiteLayout)).isFalse(); 1575 try { 1576 Thread.sleep(resetIntervalMs); 1577 } catch (InterruptedException e) { 1578 throw new RuntimeException(e); 1579 } 1580 1581 assertThat(getAppWidgetManager().setWidgetPreview(provider, WIDGET_CATEGORY_HOME_SCREEN, 1582 whiteLayout)).isTrue(); 1583 getAppWidgetManager().removeWidgetPreview(provider, WIDGET_CATEGORY_HOME_SCREEN); 1584 } 1585 1586 @RequiresFlagsEnabled({Flags.FLAG_CHECK_REMOTE_VIEWS_URI_PERMISSION}) 1587 @Test testCheckRemoteViewsUri()1588 public void testCheckRemoteViewsUri() throws Exception { 1589 final Context context = getInstrumentation().getTargetContext(); 1590 1591 final AppWidgetHost host = new AppWidgetHost(context, 0); 1592 // Delete host to clear up any old ones. 1593 host.deleteHost(); 1594 host.startListening(); 1595 1596 final int myUser = UserHandle.myUserId(); 1597 int targetUser; 1598 if (UserManager.isHeadlessSystemUserMode()) { 1599 if (myUser == 10) { 1600 targetUser = 11; 1601 } else { 1602 targetUser = 10; 1603 } 1604 } else { 1605 if (myUser == 0) { 1606 targetUser = 10; 1607 } else { 1608 targetUser = 0; 1609 } 1610 } 1611 final Uri invalidContentUri = Uri.parse( 1612 "content://" + targetUser + "@android.appwidget.cts/foobar.png"); 1613 final Uri validUri = Uri.parse( 1614 "content://" + myUser + "@android.appwidget.cts/foobar.png"); 1615 final Uri resourceUri = Uri.parse("android.resource://android/drawable/foobar"); 1616 final Uri fileUri = Uri.parse("file:///data/media"); 1617 1618 final int appWidgetId; 1619 try { 1620 // Configure the provider behavior. 1621 final AtomicInteger invocationCounter = new AtomicInteger(); 1622 AppWidgetProviderCallbacks callbacks = createAppWidgetProviderCallbacks( 1623 invocationCounter); 1624 doAnswer(new Answer<Void>() { 1625 @Override 1626 public Void answer(InvocationOnMock invocation) throws Throwable { 1627 final int appWidgetId = ((int[]) invocation.getArguments()[2])[0]; 1628 1629 // Provider should not be able to use content URI for package in another user. 1630 RemoteViews invalidRemoteViews = new RemoteViews(context.getPackageName(), 1631 R.layout.image_view); 1632 invalidRemoteViews.setImageViewUri(R.id.hello, invalidContentUri); 1633 assertThrows(SecurityException.class, () -> { 1634 getAppWidgetManager().updateAppWidget(appWidgetId, invalidRemoteViews); 1635 }); 1636 1637 // Content URI for own package should not throw. 1638 RemoteViews contentUriViews = new RemoteViews(context.getPackageName(), 1639 R.layout.image_view); 1640 contentUriViews.setImageViewUri(R.id.hello, validUri); 1641 getAppWidgetManager().updateAppWidget(appWidgetId, contentUriViews); 1642 1643 // Resource URI should not throw. 1644 RemoteViews resourceUriViews = new RemoteViews(context.getPackageName(), 1645 R.layout.image_view); 1646 resourceUriViews.setImageViewUri(R.id.hello, resourceUri); 1647 getAppWidgetManager().updateAppWidget(appWidgetId, resourceUriViews); 1648 1649 // File URI should throw. 1650 RemoteViews fileUriViews = new RemoteViews(context.getPackageName(), 1651 R.layout.image_view); 1652 // Disable StrictMode temporarily so setImageViewUri will not throw with file 1653 // URI. 1654 final StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); 1655 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().build()); 1656 try { 1657 fileUriViews.setImageViewUri(R.id.hello, fileUri); 1658 } finally { 1659 StrictMode.setVmPolicy(oldPolicy); 1660 } 1661 assertThrows(SecurityException.class, () -> { 1662 getAppWidgetManager().updateAppWidget(appWidgetId, fileUriViews); 1663 }); 1664 1665 synchronized (mLock) { 1666 invocationCounter.incrementAndGet(); 1667 mLock.notifyAll(); 1668 } 1669 return null; 1670 } 1671 }).when(callbacks).onUpdate(any(Context.class), any(AppWidgetManager.class), 1672 any(int[].class)); 1673 FirstAppWidgetProvider.setCallbacks(callbacks); 1674 1675 // Bind an instance of the widget 1676 grantBindAppWidgetPermission(); 1677 final AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 1678 appWidgetId = host.allocateAppWidgetId(); 1679 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 1680 provider.getProfile(), provider.provider, null); 1681 1682 // Wait for onEnabled and onUpdate 1683 waitForCallCount(invocationCounter, 2); 1684 1685 } finally { 1686 FirstAppWidgetProvider.setCallbacks(null); 1687 host.deleteHost(); 1688 revokeBindAppWidgetPermission(); 1689 } 1690 } 1691 setGeneratedPreviewRateLimit(long resetIntervalMs, int maxCallsPerInterval)1692 private void setGeneratedPreviewRateLimit(long resetIntervalMs, int maxCallsPerInterval) { 1693 final DeviceConfig.Properties props = new DeviceConfig.Properties.Builder( 1694 DeviceConfig.NAMESPACE_SYSTEMUI) 1695 .setLong("generated_preview_api_reset_interval_ms", resetIntervalMs) 1696 .setInt("generated_preview_api_max_calls_per_interval", maxCallsPerInterval) 1697 .build(); 1698 mDeviceConfigStateHelper.set(props); 1699 } 1700 waitForCallCount(AtomicInteger counter, int expectedCount)1701 private void waitForCallCount(AtomicInteger counter, int expectedCount) { 1702 synchronized (mLock) { 1703 final long startTimeMillis = SystemClock.uptimeMillis(); 1704 while (counter.get() < expectedCount) { 1705 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; 1706 final long remainingTimeMillis = OPERATION_TIMEOUT - elapsedTimeMillis; 1707 if (remainingTimeMillis <= 0) { 1708 fail("Did not get expected call"); 1709 } 1710 try { 1711 mLock.wait(remainingTimeMillis); 1712 } catch (InterruptedException ie) { 1713 /* ignore */ 1714 } 1715 } 1716 } 1717 } 1718 1719 @SuppressWarnings("deprecation") assertExpectedInstalledProviders(List<AppWidgetProviderInfo> providers)1720 private void assertExpectedInstalledProviders(List<AppWidgetProviderInfo> providers) { 1721 boolean[] verifiedWidgets = verifyInstalledProviders(providers); 1722 assertTrue(verifiedWidgets[0]); 1723 assertTrue(verifiedWidgets[1]); 1724 } 1725 getFirstAppWidgetProviderInfo()1726 private AppWidgetProviderInfo getFirstAppWidgetProviderInfo() { 1727 return getProviderInfo(getFirstWidgetComponent()); 1728 } 1729 getSecondAppWidgetProviderInfo()1730 private AppWidgetProviderInfo getSecondAppWidgetProviderInfo() { 1731 return getProviderInfo(getSecondWidgetComponent()); 1732 } 1733 createAppWidgetProviderCallbacks( final AtomicInteger callCounter)1734 private AppWidgetProviderCallbacks createAppWidgetProviderCallbacks( 1735 final AtomicInteger callCounter) { 1736 // Set a mock to intercept provider callbacks. 1737 AppWidgetProviderCallbacks callbacks = mock(AppWidgetProviderCallbacks.class); 1738 1739 // onEnabled 1740 doAnswer(new Answer<Void>() { 1741 @Override 1742 public Void answer(InvocationOnMock invocation) throws Throwable { 1743 synchronized (mLock) { 1744 callCounter.incrementAndGet(); 1745 mLock.notifyAll(); 1746 } 1747 return null; 1748 } 1749 }).when(callbacks).onEnabled(any(Context.class)); 1750 1751 // onUpdate 1752 doAnswer(new Answer<Void>() { 1753 @Override 1754 public Void answer(InvocationOnMock invocation) throws Throwable { 1755 synchronized (mLock) { 1756 callCounter.incrementAndGet(); 1757 mLock.notifyAll(); 1758 } 1759 return null; 1760 } 1761 }).when(callbacks).onUpdate(any(Context.class), any(AppWidgetManager.class), 1762 any(int[].class)); 1763 1764 // onAppWidgetOptionsChanged 1765 doAnswer(new Answer<Void>() { 1766 @Override 1767 public Void answer(InvocationOnMock invocation) throws Throwable { 1768 synchronized (mLock) { 1769 callCounter.incrementAndGet(); 1770 mLock.notifyAll(); 1771 } 1772 return null; 1773 } 1774 }).when(callbacks).onAppWidgetOptionsChanged(any(Context.class), 1775 any(AppWidgetManager.class), any(int.class), any(Bundle.class)); 1776 1777 // onDeleted 1778 doAnswer(new Answer<Void>() { 1779 @Override 1780 public Void answer(InvocationOnMock invocation) throws Throwable { 1781 synchronized (mLock) { 1782 callCounter.incrementAndGet(); 1783 mLock.notifyAll(); 1784 } 1785 return null; 1786 } 1787 }).when(callbacks).onDeleted(any(Context.class), any(int[].class)); 1788 1789 // onDisabled 1790 doAnswer(new Answer<Void>() { 1791 @Override 1792 public Void answer(InvocationOnMock invocation) throws Throwable { 1793 synchronized (mLock) { 1794 callCounter.incrementAndGet(); 1795 mLock.notifyAll(); 1796 } 1797 return null; 1798 } 1799 }).when(callbacks).onDisabled(any(Context.class)); 1800 1801 // onRestored 1802 doAnswer(new Answer<Void>() { 1803 @Override 1804 public Void answer(InvocationOnMock invocation) throws Throwable { 1805 synchronized (mLock) { 1806 callCounter.incrementAndGet(); 1807 mLock.notifyAll(); 1808 } 1809 return null; 1810 } 1811 }).when(callbacks).onRestored(any(Context.class), any(int[].class), 1812 any(int[].class)); 1813 1814 return callbacks; 1815 } 1816 equalOptions(Bundle first, Bundle second)1817 private static boolean equalOptions(Bundle first, Bundle second) { 1818 return first.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) 1819 == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) 1820 && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT) 1821 == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT) 1822 && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH) 1823 == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH) 1824 && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT) 1825 == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT); 1826 } 1827 1828 private static final class OptionsMatcher extends BaseMatcher<Bundle> { 1829 private Bundle mOptions; 1830 OptionsMatcher(Bundle options)1831 public OptionsMatcher(Bundle options) { 1832 mOptions = options; 1833 } 1834 1835 @Override matches(Object item)1836 public boolean matches(Object item) { 1837 Bundle options = (Bundle) item; 1838 return equalOptions(mOptions, options); 1839 } 1840 1841 @Override describeTo(Description description)1842 public void describeTo(Description description) { 1843 /* do nothing */ 1844 } 1845 } 1846 1847 private static final class WidgetIdsMatcher extends BaseMatcher<int[]> { 1848 private final int[] mWidgetIds; 1849 WidgetIdsMatcher(int[] widgetIds)1850 public WidgetIdsMatcher(int[] widgetIds) { 1851 mWidgetIds = widgetIds; 1852 } 1853 1854 @Override matches(Object item)1855 public boolean matches(Object item) { 1856 final int[] widgetIds = (int[]) item; 1857 return Arrays.equals(widgetIds, mWidgetIds); 1858 } 1859 1860 @Override describeTo(Description description)1861 public void describeTo(Description description) { 1862 /* do nothing */ 1863 } 1864 } 1865 1866 private static final class RemoteViewsMatcher extends BaseMatcher<RemoteViews> { 1867 private final int mLayoutId; 1868 private final String mPackageName; 1869 RemoteViewsMatcher(int layoutId, String packageName)1870 public RemoteViewsMatcher(int layoutId, String packageName) { 1871 mLayoutId = layoutId; 1872 mPackageName = packageName; 1873 } 1874 1875 @Override matches(Object item)1876 public boolean matches(Object item) { 1877 final RemoteViews remoteViews = (RemoteViews) item; 1878 return remoteViews != null && remoteViews.getLayoutId() == mLayoutId 1879 && remoteViews.getPackage().equals(mPackageName); 1880 } 1881 1882 @Override describeTo(Description description)1883 public void describeTo(Description description) { 1884 /* do nothing */ 1885 } 1886 } 1887 1888 private static class MyAppWidgetHostView extends AppWidgetHostView { 1889 private OnUpdateAppWidgetListener mOnUpdateAppWidgetListener; 1890 1891 public interface OnUpdateAppWidgetListener { onUpdateAppWidget(RemoteViews remoteViews)1892 public void onUpdateAppWidget(RemoteViews remoteViews); 1893 } 1894 MyAppWidgetHostView(Context context)1895 private MyAppWidgetHostView(Context context) { 1896 super(context); 1897 } 1898 setOnUpdateAppWidgetListener(OnUpdateAppWidgetListener listener)1899 public void setOnUpdateAppWidgetListener(OnUpdateAppWidgetListener listener) { 1900 mOnUpdateAppWidgetListener = listener; 1901 } 1902 1903 @Override updateAppWidget(RemoteViews remoteViews)1904 public void updateAppWidget(RemoteViews remoteViews) { 1905 super.updateAppWidget(remoteViews); 1906 if (mOnUpdateAppWidgetListener != null) { 1907 mOnUpdateAppWidgetListener.onUpdateAppWidget(remoteViews); 1908 } 1909 } 1910 } 1911 1912 private static class AppWidgetHostWithLatch extends AppWidgetHost { 1913 CountDownLatch latch = null; 1914 AppWidgetHostWithLatch(Context context, int hostId)1915 AppWidgetHostWithLatch(Context context, int hostId) { 1916 super(context, hostId); 1917 } 1918 1919 @Override onProvidersChanged()1920 protected void onProvidersChanged() { 1921 super.onProvidersChanged(); 1922 if (latch != null) { 1923 latch.countDown(); 1924 } 1925 } 1926 } 1927 } 1928