xref: /aosp_15_r20/cts/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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