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 org.mockito.Matchers.any;
20 import static org.mockito.Matchers.argThat;
21 import static org.mockito.Matchers.eq;
22 import static org.mockito.Matchers.same;
23 import static org.mockito.Mockito.atLeastOnce;
24 import static org.mockito.Mockito.doAnswer;
25 import static org.mockito.Mockito.inOrder;
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.spy;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 
31 import android.appwidget.AppWidgetHost;
32 import android.appwidget.AppWidgetHostView;
33 import android.appwidget.AppWidgetManager;
34 import android.appwidget.AppWidgetProviderInfo;
35 import android.appwidget.cts.provider.AppWidgetProviderCallbacks;
36 import android.appwidget.cts.provider.FirstAppWidgetProvider;
37 import android.appwidget.cts.provider.SecondAppWidgetProvider;
38 import android.appwidget.cts.service.MyAppWidgetService;
39 import android.content.ComponentName;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.pm.PackageManager;
43 import android.cts.appwidget.R;
44 import android.graphics.drawable.Drawable;
45 import android.net.Uri;
46 import android.os.Bundle;
47 import android.os.ParcelFileDescriptor;
48 import android.os.Process;
49 import android.os.SystemClock;
50 import android.os.UserHandle;
51 import android.os.UserManager;
52 import android.test.InstrumentationTestCase;
53 import android.text.TextUtils;
54 import android.util.DisplayMetrics;
55 import android.widget.RemoteViews;
56 import android.widget.RemoteViewsService.RemoteViewsFactory;
57 import libcore.io.IoUtils;
58 import libcore.io.Streams;
59 import org.hamcrest.BaseMatcher;
60 import org.hamcrest.Description;
61 import org.mockito.InOrder;
62 import org.mockito.invocation.InvocationOnMock;
63 import org.mockito.stubbing.Answer;
64 
65 import java.io.FileInputStream;
66 import java.io.IOException;
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.List;
70 import java.util.concurrent.atomic.AtomicInteger;
71 
72 public class AppWidgetTest extends InstrumentationTestCase {
73 
74     private static final long OPERATION_TIMEOUT = 20 * 1000; // 20 sec
75 
76     private static final String FIRST_APP_WIDGET_CONFIGURE_ACTIVITY =
77             "android.appwidget.cts.provider.FirstAppWidgetConfigureActivity";
78 
79     private static final String SECOND_APP_WIDGET_CONFIGURE_ACTIVITY =
80             "android.appwidget.cts.provider.SecondAppWidgetConfigureActivity";
81 
82     private final Object mLock = new Object();
83 
84     @Override
setUp()85     public void setUp() throws Exception {
86         // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
87         // Dexmaker is used by mockito.
88         System.setProperty("dexmaker.dexcache", getInstrumentation()
89                 .getTargetContext().getCacheDir().getPath());
90     }
91 
92     private static final String GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND =
93             "appwidget grantbind --package android.cts.appwidget --user 0";
94 
95     private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
96             "appwidget revokebind --package android.cts.appwidget --user 0";
97 
98 
hasAppWidgets()99     private boolean hasAppWidgets() {
100         return getInstrumentation().getTargetContext().getPackageManager()
101             .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
102     }
103 
testGetAppInstalledProvidersForCurrentUserLegacy()104     public void testGetAppInstalledProvidersForCurrentUserLegacy() throws Exception {
105         if (!hasAppWidgets()) {
106             return;
107         }
108 
109         // By default we should get only providers for the current user.
110         List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
111 
112         // Make sure we have our two providers in the list.
113         assertExpectedInstalledProviders(providers);
114     }
115 
testGetAppInstalledProvidersForCurrentUserNewCurrentProfile()116     public void testGetAppInstalledProvidersForCurrentUserNewCurrentProfile() throws Exception {
117         if (!hasAppWidgets()) {
118             return;
119         }
120 
121         // We ask only for providers for the current user.
122         List<AppWidgetProviderInfo> providers = getAppWidgetManager()
123                 .getInstalledProvidersForProfile(Process.myUserHandle());
124 
125         // Make sure we have our two providers in the list.
126         assertExpectedInstalledProviders(providers);
127     }
128 
testGetAppInstalledProvidersForCurrentUserNewAllProfiles()129     public void testGetAppInstalledProvidersForCurrentUserNewAllProfiles() throws Exception {
130         if (!hasAppWidgets()) {
131             return;
132         }
133 
134         // We ask only for providers for all current user's profiles
135         UserManager userManager = (UserManager) getInstrumentation()
136                 .getTargetContext().getSystemService(Context.USER_SERVICE);
137 
138         List<AppWidgetProviderInfo> allProviders = new ArrayList<>();
139 
140         List<UserHandle> profiles = userManager.getUserProfiles();
141         final int profileCount = profiles.size();
142         for (int i = 0; i < profileCount; i++) {
143             UserHandle profile = profiles.get(i);
144             List<AppWidgetProviderInfo> providers = getAppWidgetManager()
145                     .getInstalledProvidersForProfile(profile);
146             allProviders.addAll(providers);
147         }
148 
149         // Make sure we have our two providers in the list.
150         assertExpectedInstalledProviders(allProviders);
151     }
152 
testBindAppWidget()153     public void testBindAppWidget() throws Exception {
154         if (!hasAppWidgets()) {
155             return;
156         }
157 
158         // Create a host and start listening.
159         AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0);
160         host.deleteHost();
161         host.startListening();
162 
163         // Allocate an app widget id to bind.
164         final int appWidgetId = host.allocateAppWidgetId();
165 
166         // Grab a provider we defined to be bound.
167         AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
168 
169         // Bind the widget.
170         boolean widgetBound = getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
171                 provider.getProfile(), provider.provider, null);
172         assertFalse(widgetBound);
173 
174         // Well, app do not have this permission unless explicitly granted
175         // by the user. Now we will pretend for the user and grant it.
176         grantBindAppWidgetPermission();
177 
178         try {
179             // Bind the widget as we have a permission for that.
180             widgetBound = getAppWidgetManager().bindAppWidgetIdIfAllowed(
181                     appWidgetId, provider.getProfile(), provider.provider, null);
182             assertTrue(widgetBound);
183 
184             // Deallocate the app widget id.
185             host.deleteAppWidgetId(appWidgetId);
186         } finally {
187             // Clean up.
188             host.deleteAppWidgetId(appWidgetId);
189             host.deleteHost();
190             revokeBindAppWidgetPermission();
191         }
192     }
193 
testAppWidgetProviderCallbacks()194     public void testAppWidgetProviderCallbacks() throws Exception {
195         if (!hasAppWidgets()) {
196             return;
197         }
198 
199         AtomicInteger invocationCounter = new AtomicInteger();
200 
201         // Set a mock to intercept provider callbacks.
202         AppWidgetProviderCallbacks callbacks = createAppWidgetProviderCallbacks(invocationCounter);
203         FirstAppWidgetProvider.setCallbacks(callbacks);
204 
205         int firstAppWidgetId = 0;
206         int secondAppWidgetId = 0;
207 
208         final Bundle firstOptions;
209         final Bundle secondOptions;
210 
211         // Create a host and start listening.
212         AppWidgetHost host = spy(new AppWidgetHost(getInstrumentation().getTargetContext(), 0));
213         host.deleteHost();
214         host.startListening();
215 
216         // We want to bind a widget.
217         grantBindAppWidgetPermission();
218         try {
219             // Allocate the first widget id to bind.
220             firstAppWidgetId = host.allocateAppWidgetId();
221 
222             // Grab a provider we defined to be bound.
223             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
224 
225             // Bind the first widget.
226             getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
227                     provider.getProfile(), provider.provider, null);
228 
229             // Wait for onEnabled and onUpdate
230             waitForCallCount(invocationCounter, 2);
231 
232             // Update the first widget options.
233             firstOptions = new Bundle();
234             firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 1);
235             firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 2);
236             firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 3);
237             firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 4);
238             getAppWidgetManager().updateAppWidgetOptions(firstAppWidgetId, firstOptions);
239 
240             // Wait for onAppWidgetOptionsChanged
241             waitForCallCount(invocationCounter, 3);
242 
243             // Allocate the second app widget id to bind.
244             secondAppWidgetId = host.allocateAppWidgetId();
245 
246             // Bind the second widget.
247             getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
248                     provider.getProfile(), provider.provider, null);
249 
250             // Wait for onUpdate
251             waitForCallCount(invocationCounter, 4);
252 
253             // Update the second widget options.
254             secondOptions = new Bundle();
255             secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 5);
256             secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 6);
257             secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 7);
258             secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 8);
259             getAppWidgetManager().updateAppWidgetOptions(secondAppWidgetId, secondOptions);
260 
261             // Wait for onAppWidgetOptionsChanged
262             waitForCallCount(invocationCounter, 5);
263 
264             // Delete the first widget.
265             host.deleteAppWidgetId(firstAppWidgetId);
266 
267             // Wait for onDeleted
268             waitForCallCount(invocationCounter, 6);
269 
270             // Delete the second widget.
271             host.deleteAppWidgetId(secondAppWidgetId);
272 
273             // Wait for onDeleted and onDisabled
274             waitForCallCount(invocationCounter, 8);
275 
276             // Make sure the provider callbacks are correct.
277             InOrder inOrder = inOrder(callbacks);
278 
279             inOrder.verify(callbacks).onEnabled(any(Context.class));
280             inOrder.verify(callbacks).onUpdate(any(Context.class),
281                     any(AppWidgetManager.class), eq(new int[] {firstAppWidgetId}));
282             inOrder.verify(callbacks).onAppWidgetOptionsChanged(any(Context.class),
283                     any(AppWidgetManager.class), same(firstAppWidgetId), argThat(
284                             new OptionsMatcher(firstOptions)));
285             inOrder.verify(callbacks).onUpdate(any(Context.class),
286                     any(AppWidgetManager.class), eq(new int[] {secondAppWidgetId}));
287             inOrder.verify(callbacks).onAppWidgetOptionsChanged(any(Context.class),
288                     any(AppWidgetManager.class), same(secondAppWidgetId), argThat(
289                             new OptionsMatcher(secondOptions)));
290             inOrder.verify(callbacks).onDeleted(any(Context.class),
291                     argThat(new WidgetIdsMatcher(new int[]{firstAppWidgetId})));
292             inOrder.verify(callbacks).onDeleted(any(Context.class),
293                     argThat(new WidgetIdsMatcher(new int[]{secondAppWidgetId})));
294             inOrder.verify(callbacks).onDisabled(any(Context.class));
295         } finally {
296             // Clean up.
297             host.deleteAppWidgetId(firstAppWidgetId);
298             host.deleteAppWidgetId(secondAppWidgetId);
299             host.deleteHost();
300             FirstAppWidgetProvider.setCallbacks(null);
301             revokeBindAppWidgetPermission();
302         }
303     }
304 
testTwoAppWidgetProviderCallbacks()305     public void testTwoAppWidgetProviderCallbacks() throws Exception {
306         if (!hasAppWidgets()) {
307             return;
308         }
309 
310         AtomicInteger invocationCounter = new AtomicInteger();
311 
312         // Set a mock to intercept first provider callbacks.
313         AppWidgetProviderCallbacks firstCallbacks = createAppWidgetProviderCallbacks(
314                 invocationCounter);
315         FirstAppWidgetProvider.setCallbacks(firstCallbacks);
316 
317         // Set a mock to intercept second provider callbacks.
318         AppWidgetProviderCallbacks secondCallbacks = createAppWidgetProviderCallbacks(
319                 invocationCounter);
320         SecondAppWidgetProvider.setCallbacks(secondCallbacks);
321 
322         int firstAppWidgetId = 0;
323         int secondAppWidgetId = 0;
324 
325         // Create a host and start listening.
326         AppWidgetHost host = spy(new AppWidgetHost(
327                 getInstrumentation().getTargetContext(), 0));
328         host.deleteHost();
329         host.startListening();
330 
331         // We want to bind widgets.
332         grantBindAppWidgetPermission();
333         try {
334             // Allocate the first widget id to bind.
335             firstAppWidgetId = host.allocateAppWidgetId();
336 
337             // Allocate the second widget id to bind.
338             secondAppWidgetId = host.allocateAppWidgetId();
339 
340             // Grab the first provider we defined to be bound.
341             AppWidgetProviderInfo firstProvider = getFirstAppWidgetProviderInfo();
342 
343             // Bind the first widget.
344             getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
345                     firstProvider.getProfile(), firstProvider.provider, null);
346 
347             // Wait for onEnabled and onUpdate
348             waitForCallCount(invocationCounter, 2);
349 
350             // Grab the second provider we defined to be bound.
351             AppWidgetProviderInfo secondProvider = getSecondAppWidgetProviderInfo();
352 
353             // Bind the second widget.
354             getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
355                     secondProvider.getProfile(), secondProvider.provider, null);
356 
357             // Wait for onEnabled and onUpdate
358             waitForCallCount(invocationCounter, 4);
359 
360             // Delete the first widget.
361             host.deleteAppWidgetId(firstAppWidgetId);
362 
363             // Wait for onDeleted and onDisabled
364             waitForCallCount(invocationCounter, 6);
365 
366             // Delete the second widget.
367             host.deleteAppWidgetId(secondAppWidgetId);
368 
369             // Wait for onDeleted and onDisabled
370             waitForCallCount(invocationCounter, 8);
371 
372             // Make sure the first provider callbacks are correct.
373             InOrder firstInOrder = inOrder(firstCallbacks);
374             firstInOrder.verify(firstCallbacks).onEnabled(any(Context.class));
375             firstInOrder.verify(firstCallbacks).onUpdate(any(Context.class),
376                     any(AppWidgetManager.class), eq(new int[]{firstAppWidgetId}));
377             firstInOrder.verify(firstCallbacks).onDeleted(any(Context.class),
378                     argThat(new WidgetIdsMatcher(new int[]{firstAppWidgetId})));
379             firstInOrder.verify(firstCallbacks).onDisabled(any(Context.class));
380 
381             // Make sure the second provider callbacks are correct.
382             InOrder secondInOrder = inOrder(secondCallbacks);
383             secondInOrder.verify(secondCallbacks).onEnabled(any(Context.class));
384             secondInOrder.verify(secondCallbacks).onUpdate(any(Context.class),
385                     any(AppWidgetManager.class), eq(new int[]{secondAppWidgetId}));
386             secondInOrder.verify(secondCallbacks).onDeleted(any(Context.class),
387                     argThat(new WidgetIdsMatcher(new int[] {secondAppWidgetId})));
388             secondInOrder.verify(secondCallbacks).onDisabled(any(Context.class));
389         } finally {
390             // Clean up.
391             host.deleteAppWidgetId(firstAppWidgetId);
392             host.deleteAppWidgetId(secondAppWidgetId);
393             host.deleteHost();
394             FirstAppWidgetProvider.setCallbacks(null);
395             SecondAppWidgetProvider.setCallbacks(null);
396             revokeBindAppWidgetPermission();
397         }
398     }
399 
testGetAppWidgetIds()400     public void testGetAppWidgetIds() throws Exception {
401         if (!hasAppWidgets()) {
402             return;
403         }
404 
405         // We want to bind widgets.
406         grantBindAppWidgetPermission();
407 
408         // Create a host and start listening.
409         AppWidgetHost host = new AppWidgetHost(
410                 getInstrumentation().getTargetContext(), 0);
411         host.deleteHost();
412         host.startListening();
413 
414         int firstAppWidgetId = 0;
415         int secondAppWidgetId = 0;
416 
417         try {
418             // Grab the provider we defined to be bound.
419             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
420 
421             // Initially we have no widgets.
422             int[] widgetsIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
423             assertTrue(widgetsIds.length == 0);
424 
425             // Allocate the first widget id to bind.
426             firstAppWidgetId = host.allocateAppWidgetId();
427 
428             // Bind the first widget.
429             getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
430                     provider.getProfile(), provider.provider, null);
431 
432             // Allocate the second widget id to bind.
433             secondAppWidgetId = host.allocateAppWidgetId();
434 
435             // Bind the second widget.
436             getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
437                     provider.getProfile(), provider.provider, null);
438 
439             // Now we have two widgets,
440             widgetsIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
441             assertTrue(Arrays.equals(widgetsIds, new int[]{firstAppWidgetId, secondAppWidgetId}));
442         } finally {
443             // Clean up.
444             host.deleteAppWidgetId(firstAppWidgetId);
445             host.deleteAppWidgetId(secondAppWidgetId);
446             host.deleteHost();
447             revokeBindAppWidgetPermission();
448         }
449     }
450 
testGetAppWidgetInfo()451     public void testGetAppWidgetInfo() throws Exception {
452         if (!hasAppWidgets()) {
453             return;
454         }
455 
456         // We want to bind widgets.
457         grantBindAppWidgetPermission();
458 
459         // Create a host and start listening.
460         AppWidgetHost host = new AppWidgetHost(
461                 getInstrumentation().getTargetContext(), 0);
462         host.deleteHost();
463         host.startListening();
464 
465         int appWidgetId = 0;
466 
467         try {
468             // Allocate an widget id to bind.
469             appWidgetId = host.allocateAppWidgetId();
470 
471             // The widget is not bound, so no info.
472             AppWidgetProviderInfo foundProvider = getAppWidgetManager()
473                     .getAppWidgetInfo(appWidgetId);
474             assertNull(foundProvider);
475 
476             // Grab the provider we defined to be bound.
477             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
478 
479             // Bind the app widget.
480             getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
481                     provider.getProfile(), provider.provider, null);
482 
483             // The widget is bound, so the provider info should be there.
484             foundProvider = getAppWidgetManager().getAppWidgetInfo(appWidgetId);
485             assertEquals(provider.provider, foundProvider.provider);
486             assertEquals(provider.getProfile(), foundProvider.getProfile());
487 
488             Context context = getInstrumentation().getTargetContext();
489 
490             // Let us make sure the provider info is sane.
491             String label = foundProvider.loadLabel(context.getPackageManager());
492             assertTrue(!TextUtils.isEmpty(label));
493 
494             Drawable icon = foundProvider.loadIcon(context, DisplayMetrics.DENSITY_DEFAULT);
495             assertNotNull(icon);
496 
497             Drawable previewImage = foundProvider.loadPreviewImage(context, 0);
498             assertNotNull(previewImage);
499         } finally {
500             // Clean up.
501             host.deleteAppWidgetId(appWidgetId);
502             host.deleteHost();
503             revokeBindAppWidgetPermission();
504         }
505     }
506 
testGetAppWidgetOptions()507     public void testGetAppWidgetOptions() throws Exception {
508         if (!hasAppWidgets()) {
509             return;
510         }
511 
512         // We want to bind widgets.
513         grantBindAppWidgetPermission();
514 
515         // Create a host and start listening.
516         AppWidgetHost host = new AppWidgetHost(
517                 getInstrumentation().getTargetContext(), 0);
518         host.deleteHost();
519         host.startListening();
520 
521         int appWidgetId = 0;
522 
523         try {
524             // Grab the provider we defined to be bound.
525             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
526 
527             // Allocate an widget id to bind.
528             appWidgetId = host.allocateAppWidgetId();
529 
530             // Initially we have no options.
531             Bundle foundOptions = getAppWidgetManager().getAppWidgetOptions(appWidgetId);
532             assertTrue(foundOptions.isEmpty());
533 
534             // We want to set the options when binding.
535             Bundle setOptions = new Bundle();
536             setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 1);
537             setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 2);
538             setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 3);
539             setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 4);
540 
541             // Bind the app widget.
542             getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
543                     provider.getProfile(), provider.provider, setOptions);
544 
545             // Make sure we get the options used when binding.
546             foundOptions = getAppWidgetManager().getAppWidgetOptions(appWidgetId);
547             assertTrue(equalOptions(setOptions, foundOptions));
548         } finally {
549             // Clean up.
550             host.deleteAppWidgetId(appWidgetId);
551             host.deleteHost();
552             revokeBindAppWidgetPermission();
553         }
554     }
555 
testDeleteHost()556     public void testDeleteHost() throws Exception {
557         if (!hasAppWidgets()) {
558             return;
559         }
560 
561         // We want to bind widgets.
562         grantBindAppWidgetPermission();
563 
564         // Create a host and start listening.
565         AppWidgetHost host = new AppWidgetHost(
566                 getInstrumentation().getTargetContext(), 0);
567         host.deleteHost();
568         host.startListening();
569 
570         int appWidgetId = 0;
571 
572         try {
573             // Allocate an widget id to bind.
574             appWidgetId = host.allocateAppWidgetId();
575 
576             // Grab the provider we defined to be bound.
577             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
578 
579             // Bind the app widget.
580             getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
581                     provider.getProfile(), provider.provider, null);
582 
583             // The widget should be there.
584             int[] widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
585             assertTrue(Arrays.equals(widgetIds, new int[]{appWidgetId}));
586 
587             // Delete the host.
588             host.deleteHost();
589 
590             // The host is gone and with it the widgets.
591             widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
592             assertTrue(widgetIds.length == 0);
593         } finally {
594             // Clean up.
595             host.deleteAppWidgetId(appWidgetId);
596             host.deleteHost();
597             revokeBindAppWidgetPermission();
598         }
599     }
600 
testDeleteHosts()601     public void testDeleteHosts() throws Exception {
602         if (!hasAppWidgets()) {
603             return;
604         }
605 
606         // We want to bind widgets.
607         grantBindAppWidgetPermission();
608 
609         // Create the first host and start listening.
610         AppWidgetHost firstHost = new AppWidgetHost(
611                 getInstrumentation().getTargetContext(), 0);
612         firstHost.deleteHost();
613         firstHost.startListening();
614 
615         // Create the second host and start listening.
616         AppWidgetHost secondHost = new AppWidgetHost(
617                 getInstrumentation().getTargetContext(), 1);
618         secondHost.deleteHost();
619         secondHost.startListening();
620 
621         int firstAppWidgetId = 0;
622         int secondAppWidgetId = 0;
623 
624         try {
625             // Grab the provider we defined to be bound.
626             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
627 
628             // Allocate the first widget id to bind.
629             firstAppWidgetId = firstHost.allocateAppWidgetId();
630 
631             // Bind the first app widget.
632             getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
633                     provider.getProfile(), provider.provider, null);
634 
635             // Allocate the second widget id to bind.
636             secondAppWidgetId = secondHost.allocateAppWidgetId();
637 
638             // Bind the second app widget.
639             getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
640                     provider.getProfile(), provider.provider, null);
641 
642             // The widgets should be there.
643             int[] widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
644             assertTrue(Arrays.equals(widgetIds, new int[]{firstAppWidgetId, secondAppWidgetId}));
645 
646             // Delete all hosts.
647             AppWidgetHost.deleteAllHosts();
648 
649             // The hosts are gone and with it the widgets.
650             widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
651             assertTrue(widgetIds.length == 0);
652         } finally {
653             // Clean up.
654             firstHost.deleteAppWidgetId(firstAppWidgetId);
655             secondHost.deleteAppWidgetId(secondAppWidgetId);
656             AppWidgetHost.deleteAllHosts();
657             revokeBindAppWidgetPermission();
658         }
659     }
660 
testOnProvidersChanged()661     public void testOnProvidersChanged() throws Exception {
662         if (!hasAppWidgets()) {
663             return;
664         }
665 
666         // We want to bind widgets.
667         grantBindAppWidgetPermission();
668 
669         final AtomicInteger onProvidersChangedCallCounter = new AtomicInteger();
670 
671         // Create a host and start listening.
672         AppWidgetHost host = new AppWidgetHost(
673                 getInstrumentation().getTargetContext(), 0) {
674             @Override
675             public void onProvidersChanged() {
676                 synchronized (mLock) {
677                     onProvidersChangedCallCounter.incrementAndGet();
678                     mLock.notifyAll();
679                 }
680             }
681         };
682         host.deleteHost();
683         host.startListening();
684 
685         int appWidgetId = 0;
686 
687         try {
688             // Grab the provider we defined to be bound.
689             AppWidgetProviderInfo firstLookupProvider = getFirstAppWidgetProviderInfo();
690 
691             // Allocate a widget id to bind.
692             appWidgetId = host.allocateAppWidgetId();
693 
694             // Bind the first app widget.
695             getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
696                     firstLookupProvider.getProfile(), firstLookupProvider.provider, null);
697 
698             // Disable the provider we just bound to.
699             PackageManager packageManager = getInstrumentation().getTargetContext()
700                     .getApplicationContext().getPackageManager();
701             packageManager.setComponentEnabledSetting(firstLookupProvider.provider,
702                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
703                     PackageManager.DONT_KILL_APP);
704 
705             // Wait for the package change to propagate.
706             waitForCallCount(onProvidersChangedCallCounter, 1);
707 
708             // The provider should not be present anymore.
709             AppWidgetProviderInfo secondLookupProvider = getFirstAppWidgetProviderInfo();
710             assertNull(secondLookupProvider);
711 
712             // Enable the provider we disabled.
713             packageManager.setComponentEnabledSetting(firstLookupProvider.provider,
714                     PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
715                     PackageManager.DONT_KILL_APP);
716 
717             // Wait for the package change to propagate.
718             waitForCallCount(onProvidersChangedCallCounter, 2);
719         } finally {
720             // Clean up.
721             host.deleteAppWidgetId(appWidgetId);
722             host.deleteHost();
723             revokeBindAppWidgetPermission();
724         }
725     }
726 
testUpdateAppWidgetViaComponentName()727     public void testUpdateAppWidgetViaComponentName() throws Exception {
728         if (!hasAppWidgets()) {
729             return;
730         }
731 
732         // We want to bind widgets.
733         grantBindAppWidgetPermission();
734 
735         final AtomicInteger updateAppWidgetCallCount = new AtomicInteger();
736 
737         // Create a host and start listening.
738         AppWidgetHost host = new AppWidgetHost(
739                 getInstrumentation().getTargetContext(), 0) {
740             @Override
741             protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
742                     AppWidgetProviderInfo appWidget) {
743                 return new MyAppWidgetHostView(context);
744             }
745         };
746         host.deleteHost();
747         host.startListening();
748 
749         int firstAppWidgetId = 0;
750         int secondAppWidgetId = 0;
751 
752         try {
753             // Grab the provider to be bound.
754             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
755 
756             // Allocate the first widget id to bind.
757             firstAppWidgetId = host.allocateAppWidgetId();
758 
759             // Bind the first app widget.
760             getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
761                     provider.getProfile(), provider.provider, null);
762 
763             // Create the first host view.
764             MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView(
765                     getInstrumentation().getContext(), firstAppWidgetId, provider);
766             MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener =
767                     mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
768             firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener);
769 
770             // Allocate the second widget id to bind.
771             secondAppWidgetId = host.allocateAppWidgetId();
772 
773             // Bind the second app widget.
774             getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
775                     provider.getProfile(), provider.provider, null);
776 
777             // Create the second host view.
778             MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView(
779                     getInstrumentation().getContext(), secondAppWidgetId, provider);
780             MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener =
781                     mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
782             doAnswer(new Answer<Void>() {
783                 @Override
784                 public Void answer(InvocationOnMock invocation) throws Throwable {
785                     synchronized (mLock) {
786                         updateAppWidgetCallCount.incrementAndGet();
787                         mLock.notifyAll();
788                     }
789                     return null;
790                 }
791             }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
792             secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener);
793 
794             // Update all app widgets.
795             final RemoteViews content = new RemoteViews(
796                     getInstrumentation().getContext().getPackageName(),
797                     R.layout.second_initial_layout);
798             getAppWidgetManager().updateAppWidget(provider.provider, content);
799 
800             waitForCallCount(updateAppWidgetCallCount, 1);
801 
802             // Verify the expected callbacks.
803             InOrder firstInOrder = inOrder(firstAppHostViewListener);
804             firstInOrder.verify(firstAppHostViewListener).onUpdateAppWidget(argThat(
805                     new RemoteViewsMatcher(content.getLayoutId(),
806                             provider.provider.getPackageName())));
807 
808             InOrder secondInOrder = inOrder(secondAppHostViewListener);
809             secondInOrder.verify(secondAppHostViewListener).onUpdateAppWidget(argThat(
810                     new RemoteViewsMatcher(content.getLayoutId(),
811                             provider.provider.getPackageName())));
812         } finally {
813             // Clean up.
814             host.deleteAppWidgetId(firstAppWidgetId);
815             host.deleteAppWidgetId(secondAppWidgetId);
816             host.deleteHost();
817             revokeBindAppWidgetPermission();
818         }
819     }
820 
testUpdateAppWidgetViaWidgetId()821     public void testUpdateAppWidgetViaWidgetId() throws Exception {
822         if (!hasAppWidgets()) {
823             return;
824         }
825 
826         // We want to bind widgets.
827         grantBindAppWidgetPermission();
828 
829         final AtomicInteger updateAppWidgetCallCount = new AtomicInteger();
830 
831         // Create a host and start listening.
832         AppWidgetHost host = new AppWidgetHost(
833                 getInstrumentation().getTargetContext(), 0) {
834             @Override
835             protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
836                     AppWidgetProviderInfo appWidget) {
837                 return new MyAppWidgetHostView(context);
838             }
839         };
840         host.deleteHost();
841         host.startListening();
842 
843         int firstAppWidgetId = 0;
844 
845         try {
846             // Grab the provider to be bound.
847             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
848 
849             // Allocate the first widget id to bind.
850             firstAppWidgetId = host.allocateAppWidgetId();
851 
852             // Bind the first app widget.
853             getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
854                     provider.getProfile(), provider.provider, null);
855 
856             // Create the first host view.
857             MyAppWidgetHostView hostView = (MyAppWidgetHostView) host.createView(
858                     getInstrumentation().getContext(), firstAppWidgetId, provider);
859             MyAppWidgetHostView.OnUpdateAppWidgetListener appHostViewListener =
860                     mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
861             doAnswer(new Answer<Void>() {
862                 @Override
863                 public Void answer(InvocationOnMock invocation) throws Throwable {
864                     synchronized (mLock) {
865                         updateAppWidgetCallCount.incrementAndGet();
866                         mLock.notifyAll();
867                     }
868                     return null;
869                 }
870             }).when(appHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
871             hostView.setOnUpdateAppWidgetListener(appHostViewListener);
872 
873             // Update all app widgets.
874             RemoteViews content = new RemoteViews(
875                     getInstrumentation().getContext().getPackageName(),
876                     R.layout.second_initial_layout);
877             getAppWidgetManager().updateAppWidget(firstAppWidgetId, content);
878 
879             waitForCallCount(updateAppWidgetCallCount, 1);
880 
881             // Verify the expected callbacks.
882             InOrder inOrder = inOrder(appHostViewListener);
883             inOrder.verify(appHostViewListener).onUpdateAppWidget(argThat(
884                     new RemoteViewsMatcher(content.getLayoutId(),
885                             provider.provider.getPackageName())
886             ));
887         } finally {
888             // Clean up.
889             host.deleteAppWidgetId(firstAppWidgetId);
890             host.deleteHost();
891             revokeBindAppWidgetPermission();
892         }
893     }
894 
testUpdateAppWidgetViaWidgetIds()895     public void testUpdateAppWidgetViaWidgetIds() throws Exception {
896         if (!hasAppWidgets()) {
897             return;
898         }
899 
900         // We want to bind widgets.
901         grantBindAppWidgetPermission();
902 
903         final AtomicInteger onUpdateAppWidgetCallCount = new AtomicInteger();
904 
905         // Create a host and start listening.
906         AppWidgetHost host = new AppWidgetHost(
907                 getInstrumentation().getTargetContext(), 0) {
908             @Override
909             protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
910                     AppWidgetProviderInfo appWidget) {
911                 return new MyAppWidgetHostView(context);
912             }
913         };
914         host.deleteHost();
915         host.startListening();
916 
917         int firstAppWidgetId = 0;
918         int secondAppWidgetId = 0;
919 
920         try {
921             // Grab the provider to be bound.
922             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
923 
924             // Allocate the first widget id to bind.
925             firstAppWidgetId = host.allocateAppWidgetId();
926 
927             // Bind the first app widget.
928             getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
929                     provider.getProfile(), provider.provider, null);
930 
931             // Create the first host view.
932             MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView(
933                     getInstrumentation().getContext(), firstAppWidgetId, provider);
934             MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener =
935                     mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
936             firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener);
937 
938             // Allocate the second widget id to bind.
939             secondAppWidgetId = host.allocateAppWidgetId();
940 
941             // Bind the second app widget.
942             getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
943                     provider.getProfile(), provider.provider, null);
944 
945             // Create the second host view.
946             MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView(
947                     getInstrumentation().getContext(), secondAppWidgetId, provider);
948             MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener =
949                     mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
950             doAnswer(new Answer<Void>() {
951                 @Override
952                 public Void answer(InvocationOnMock invocation) throws Throwable {
953                     synchronized (mLock) {
954                         onUpdateAppWidgetCallCount.incrementAndGet();
955                         mLock.notifyAll();
956                     }
957                     return null;
958                 }
959             }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
960             secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener);
961 
962             // Update all app widgets.
963             RemoteViews content = new RemoteViews(
964                     getInstrumentation().getContext().getPackageName(),
965                     R.layout.second_initial_layout);
966             getAppWidgetManager().updateAppWidget(new int[] {firstAppWidgetId,
967                     secondAppWidgetId}, content);
968 
969             waitForCallCount(onUpdateAppWidgetCallCount, 1);
970 
971             // Verify the expected callbacks.
972             InOrder firstInOrder = inOrder(firstAppHostViewListener);
973             firstInOrder.verify(firstAppHostViewListener).onUpdateAppWidget(
974                     argThat(new RemoteViewsMatcher(content.getLayoutId(),
975                             provider.provider.getPackageName())));
976 
977             InOrder secondInOrder = inOrder(secondAppHostViewListener);
978             secondInOrder.verify(secondAppHostViewListener).onUpdateAppWidget(
979                     argThat(new RemoteViewsMatcher(content.getLayoutId(),
980                             provider.provider.getPackageName()))
981             );
982         } finally {
983             // Clean up.
984             host.deleteAppWidgetId(firstAppWidgetId);
985             host.deleteAppWidgetId(secondAppWidgetId);
986             host.deleteHost();
987             revokeBindAppWidgetPermission();
988         }
989     }
990 
testPartiallyUpdateAppWidgetViaWidgetId()991     public void testPartiallyUpdateAppWidgetViaWidgetId() throws Exception {
992         if (!hasAppWidgets()) {
993             return;
994         }
995 
996         // We want to bind widgets.
997         grantBindAppWidgetPermission();
998 
999         final AtomicInteger updateAppWidgetCallCount = new AtomicInteger();
1000 
1001         // Create a host and start listening.
1002         AppWidgetHost host = new AppWidgetHost(
1003                 getInstrumentation().getTargetContext(), 0) {
1004             @Override
1005             protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
1006                     AppWidgetProviderInfo appWidget) {
1007                 return new MyAppWidgetHostView(context);
1008             }
1009         };
1010         host.deleteHost();
1011         host.startListening();
1012 
1013         int firstAppWidgetId = 0;
1014 
1015         try {
1016             // Grab the provider to be bound.
1017             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
1018 
1019             // Allocate the first widget id to bind.
1020             firstAppWidgetId = host.allocateAppWidgetId();
1021 
1022             // Bind the first app widget.
1023             getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
1024                     provider.getProfile(), provider.provider, null);
1025 
1026             // Create the first host view.
1027             MyAppWidgetHostView hostView = (MyAppWidgetHostView) host.createView(
1028                     getInstrumentation().getContext(), firstAppWidgetId, provider);
1029             MyAppWidgetHostView.OnUpdateAppWidgetListener appHostViewListener =
1030                     mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
1031             doAnswer(new Answer<Void>() {
1032                 @Override
1033                 public Void answer(InvocationOnMock invocation) throws Throwable {
1034                     synchronized (mLock) {
1035                         updateAppWidgetCallCount.incrementAndGet();
1036                         mLock.notifyAll();
1037                     }
1038                     return null;
1039                 }
1040             }).when(appHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
1041             hostView.setOnUpdateAppWidgetListener(appHostViewListener);
1042 
1043             // Set the content for all app widgets.
1044             RemoteViews content = new RemoteViews(
1045                     getInstrumentation().getContext().getPackageName(),
1046                     R.layout.first_initial_layout);
1047             getAppWidgetManager().updateAppWidget(firstAppWidgetId, content);
1048 
1049             waitForCallCount(updateAppWidgetCallCount, 1);
1050 
1051             // Partially update the content for all app widgets (pretend we changed something).
1052             getAppWidgetManager().partiallyUpdateAppWidget(firstAppWidgetId, content);
1053 
1054             waitForCallCount(updateAppWidgetCallCount, 2);
1055 
1056             // Verify the expected callbacks.
1057             InOrder inOrder = inOrder(appHostViewListener);
1058             inOrder.verify(appHostViewListener, times(2)).onUpdateAppWidget(
1059                     argThat(new RemoteViewsMatcher(content.getLayoutId(),
1060                             provider.provider.getPackageName())));
1061         } finally {
1062             // Clean up.
1063             host.deleteAppWidgetId(firstAppWidgetId);
1064             host.deleteHost();
1065             revokeBindAppWidgetPermission();
1066         }
1067     }
1068 
testPartiallyUpdateAppWidgetViaWidgetIds()1069     public void testPartiallyUpdateAppWidgetViaWidgetIds() throws Exception {
1070         if (!hasAppWidgets()) {
1071             return;
1072         }
1073 
1074         // We want to bind widgets.
1075         grantBindAppWidgetPermission();
1076 
1077         final AtomicInteger firstAppWidgetCallCounter = new AtomicInteger();
1078         final AtomicInteger secondAppWidgetCallCounter = new AtomicInteger();
1079 
1080         // Create a host and start listening.
1081         AppWidgetHost host = new AppWidgetHost(
1082                 getInstrumentation().getTargetContext(), 0) {
1083             @Override
1084             protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
1085                     AppWidgetProviderInfo appWidget) {
1086                 return new MyAppWidgetHostView(context);
1087             }
1088         };
1089         host.deleteHost();
1090         host.startListening();
1091 
1092         int firstAppWidgetId = 0;
1093         int secondAppWidgetId = 0;
1094 
1095         try {
1096             // Grab the provider to be bound.
1097             AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
1098 
1099             // Allocate the first widget id to bind.
1100             firstAppWidgetId = host.allocateAppWidgetId();
1101 
1102             // Bind the first app widget.
1103             getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
1104                     provider.getProfile(), provider.provider, null);
1105 
1106             // Create the first host view.
1107             MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView(
1108                     getInstrumentation().getContext(), firstAppWidgetId, provider);
1109             MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener =
1110                     mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
1111             doAnswer(new Answer<Void>() {
1112                 @Override
1113                 public Void answer(InvocationOnMock invocation) throws Throwable {
1114                     synchronized (mLock) {
1115                         firstAppWidgetCallCounter.incrementAndGet();
1116                         mLock.notifyAll();
1117                     }
1118                     return null;
1119                 }
1120             }).when(firstAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
1121             firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener);
1122 
1123             // Allocate the second widget id to bind.
1124             secondAppWidgetId = host.allocateAppWidgetId();
1125 
1126             // Bind the second app widget.
1127             getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
1128                     provider.getProfile(), provider.provider, null);
1129 
1130             // Create the second host view.
1131             MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView(
1132                     getInstrumentation().getContext(), secondAppWidgetId, provider);
1133             MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener =
1134                     mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
1135             doAnswer(new Answer<Void>() {
1136                 @Override
1137                 public Void answer(InvocationOnMock invocation) throws Throwable {
1138                     synchronized (mLock) {
1139                         secondAppWidgetCallCounter.incrementAndGet();
1140                         mLock.notifyAll();
1141                     }
1142                     return null;
1143                 }
1144             }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
1145             secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener);
1146 
1147             // Set the content for all app widgets.
1148             RemoteViews content = new RemoteViews(
1149                     getInstrumentation().getContext().getPackageName(),
1150                     R.layout.first_initial_layout);
1151             getAppWidgetManager().updateAppWidget(new int[]{firstAppWidgetId,
1152                     secondAppWidgetId}, content);
1153 
1154             waitForCallCount(firstAppWidgetCallCounter, 1);
1155             waitForCallCount(secondAppWidgetCallCounter, 1);
1156 
1157             // Partially update the content for all app widgets (pretend we changed something).
1158             getAppWidgetManager().partiallyUpdateAppWidget(new int[] {firstAppWidgetId,
1159                     secondAppWidgetId}, content);
1160 
1161             waitForCallCount(firstAppWidgetCallCounter, 2);
1162             waitForCallCount(secondAppWidgetCallCounter, 2);
1163 
1164             // Verify the expected callbacks.
1165             InOrder firstInOrder = inOrder(firstAppHostViewListener);
1166             firstInOrder.verify(firstAppHostViewListener, times(2)).onUpdateAppWidget(
1167                     argThat(new RemoteViewsMatcher(content.getLayoutId(),
1168                             provider.provider.getPackageName())));
1169 
1170             InOrder secondInOrder = inOrder(secondAppHostViewListener);
1171             secondInOrder.verify(secondAppHostViewListener, times(2)).onUpdateAppWidget(
1172                     argThat(new RemoteViewsMatcher(content.getLayoutId(),
1173                             provider.provider.getPackageName())));
1174         } finally {
1175             // Clean up.
1176             host.deleteAppWidgetId(firstAppWidgetId);
1177             host.deleteAppWidgetId(secondAppWidgetId);
1178             host.deleteHost();
1179             revokeBindAppWidgetPermission();
1180         }
1181     }
1182 
testCollectionWidgets()1183     public void testCollectionWidgets() throws Exception {
1184         if (!hasAppWidgets()) {
1185             return;
1186         }
1187 
1188         // We want to bind widgets.
1189         grantBindAppWidgetPermission();
1190 
1191         final AtomicInteger invocationCounter = new AtomicInteger();
1192         final Context context = getInstrumentation().getTargetContext();
1193 
1194         // Create a host and start listening.
1195         final AppWidgetHost host = new AppWidgetHost(context, 0);
1196         host.deleteHost();
1197         host.startListening();
1198 
1199         final int appWidgetId;
1200 
1201         try {
1202             // Configure the provider behavior.
1203             AppWidgetProviderCallbacks callbacks = createAppWidgetProviderCallbacks(
1204                     invocationCounter);
1205             doAnswer(new Answer<Void>() {
1206                 @Override
1207                 public Void answer(InvocationOnMock invocation) throws Throwable {
1208                     final int appWidgetId = ((int[]) invocation.getArguments()[2])[0];
1209 
1210                     Intent intent = new Intent(context, MyAppWidgetService.class);
1211                     intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1212                     intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
1213 
1214                     RemoteViews removeViews = new RemoteViews(context.getPackageName(),
1215                             R.layout.collection_widget_layout);
1216                     removeViews.setRemoteAdapter(R.id.stack_view, intent);
1217 
1218                     getAppWidgetManager().updateAppWidget(appWidgetId, removeViews);
1219 
1220                     synchronized (mLock) {
1221                         invocationCounter.incrementAndGet();
1222                         mLock.notifyAll();
1223                     }
1224 
1225                     return null;
1226                 }
1227             }).when(callbacks).onUpdate(any(Context.class), any(AppWidgetManager.class),
1228                     any(int[].class));
1229             FirstAppWidgetProvider.setCallbacks(callbacks);
1230 
1231             // Grab the provider to be bound.
1232             final AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
1233 
1234             // Allocate a widget id to bind.
1235             appWidgetId = host.allocateAppWidgetId();
1236 
1237             // Bind the app widget.
1238             getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
1239                     provider.getProfile(), provider.provider, null);
1240 
1241             // Wait for onEnabled and onUpdate
1242             waitForCallCount(invocationCounter, 2);
1243 
1244             // Configure the app widget service behavior.
1245             RemoteViewsFactory factory = mock(RemoteViewsFactory.class);
1246             doAnswer(new Answer<Integer>() {
1247                 @Override
1248                 public Integer answer(InvocationOnMock invocation) throws Throwable {
1249                     return 1;
1250                 }
1251             }).when(factory).getCount();
1252             doAnswer(new Answer<RemoteViews>() {
1253                 @Override
1254                 public RemoteViews answer(InvocationOnMock invocation) throws Throwable {
1255                     RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
1256                             R.layout.collection_widget_item_layout);
1257                     remoteViews.setTextViewText(R.id.text_view, context.getText(R.string.foo));
1258                     synchronized (mLock) {
1259                         invocationCounter.incrementAndGet();
1260                     }
1261                     return remoteViews;
1262                 }
1263             }).when(factory).getViewAt(any(int.class));
1264             doAnswer(new Answer<Integer>() {
1265                 @Override
1266                 public Integer answer(InvocationOnMock invocation) throws Throwable {
1267                     return 1;
1268                 }
1269             }).when(factory).getViewTypeCount();
1270             MyAppWidgetService.setFactory(factory);
1271 
1272             getInstrumentation().runOnMainSync(new Runnable() {
1273                 @Override
1274                 public void run() {
1275                     host.createView(context, appWidgetId, provider);
1276                 }
1277             });
1278 
1279             // Wait for the interactions to occur.
1280             waitForCallCount(invocationCounter, 3);
1281 
1282             // Verify the interactions.
1283             verify(factory, atLeastOnce()).hasStableIds();
1284             verify(factory, atLeastOnce()).getViewTypeCount();
1285             verify(factory, atLeastOnce()).getCount();
1286             verify(factory, atLeastOnce()).getLoadingView();
1287             verify(factory, atLeastOnce()).getViewAt(same(0));
1288         } finally {
1289             // Clean up.
1290             FirstAppWidgetProvider.setCallbacks(null);
1291             host.deleteHost();
1292             revokeBindAppWidgetPermission();
1293         }
1294     }
1295 
waitForCallCount(AtomicInteger counter, int expectedCount)1296     private void waitForCallCount(AtomicInteger counter, int expectedCount) {
1297         synchronized (mLock) {
1298             final long startTimeMillis = SystemClock.uptimeMillis();
1299             while (counter.get() < expectedCount) {
1300                 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
1301                 final long remainingTimeMillis = OPERATION_TIMEOUT - elapsedTimeMillis;
1302                 if (remainingTimeMillis <= 0) {
1303                     fail("Did not get expected call");
1304                 }
1305                 try {
1306                     mLock.wait(remainingTimeMillis);
1307                 } catch (InterruptedException ie) {
1308                         /* ignore */
1309                 }
1310             }
1311         }
1312     }
1313 
1314     @SuppressWarnings("deprecation")
assertExpectedInstalledProviders(List<AppWidgetProviderInfo> providers)1315     private void assertExpectedInstalledProviders(List<AppWidgetProviderInfo> providers) {
1316         boolean firstProviderVerified = false;
1317         boolean secondProviderVerified = false;
1318 
1319         ComponentName firstComponentName = new ComponentName(
1320                 getInstrumentation().getTargetContext().getPackageName(),
1321                 FirstAppWidgetProvider.class.getName());
1322 
1323         ComponentName secondComponentName = new ComponentName(
1324                 getInstrumentation().getTargetContext().getPackageName(),
1325                 SecondAppWidgetProvider.class.getName());
1326 
1327         final int providerCount = providers.size();
1328         for (int i = 0; i < providerCount; i++) {
1329             AppWidgetProviderInfo provider = providers.get(i);
1330 
1331             if (firstComponentName.equals(provider.provider)
1332                     && android.os.Process.myUserHandle().equals(provider.getProfile())) {
1333                 assertEquals(getNormalizedDimensionResource(R.dimen.first_min_appwidget_size),
1334                         provider.minWidth);
1335                 assertEquals(getNormalizedDimensionResource(R.dimen.first_min_appwidget_size),
1336                         provider.minHeight);
1337                 assertEquals(getNormalizedDimensionResource(
1338                         R.dimen.first_min_resize_appwidget_size), provider.minResizeWidth);
1339                 assertEquals(getNormalizedDimensionResource(
1340                         R.dimen.first_min_resize_appwidget_size), provider.minResizeHeight);
1341                 assertEquals(getIntResource(R.integer.first_update_period_millis),
1342                         provider.updatePeriodMillis);
1343                 assertEquals(getInstrumentation().getTargetContext().getPackageName(),
1344                         provider.configure.getPackageName());
1345                 assertEquals(FIRST_APP_WIDGET_CONFIGURE_ACTIVITY,
1346                         provider.configure.getClassName());
1347                 assertEquals(getIntResource(R.integer.first_resize_mode),
1348                         provider.resizeMode);
1349                 assertEquals(getIntResource(R.integer.first_widget_category),
1350                         provider.widgetCategory);
1351                 assertEquals(R.layout.first_initial_layout,
1352                         provider.initialLayout);
1353                 assertEquals(R.layout.first_initial_keyguard_layout,
1354                         provider.initialKeyguardLayout);
1355                 assertEquals(R.drawable.first_android_icon,
1356                         provider.previewImage);
1357                 assertEquals(R.id.first_auto_advance_view_id,
1358                         provider.autoAdvanceViewId);
1359                 firstProviderVerified = true;
1360             } else if (secondComponentName.equals(provider.provider)
1361                     && android.os.Process.myUserHandle().equals(provider.getProfile())) {
1362                 assertEquals(getNormalizedDimensionResource(R.dimen.second_min_appwidget_size),
1363                         provider.minWidth);
1364                 assertEquals(getNormalizedDimensionResource(R.dimen.second_min_appwidget_size),
1365                         provider.minHeight);
1366                 assertEquals(getNormalizedDimensionResource(
1367                         R.dimen.second_min_resize_appwidget_size), provider.minResizeWidth);
1368                 assertEquals(getNormalizedDimensionResource(
1369                         R.dimen.second_min_resize_appwidget_size), provider.minResizeHeight);
1370                 assertEquals(getIntResource(R.integer.second_update_period_millis),
1371                         provider.updatePeriodMillis);
1372                 assertEquals(getInstrumentation().getTargetContext().getPackageName(),
1373                         provider.configure.getPackageName());
1374                 assertEquals(SECOND_APP_WIDGET_CONFIGURE_ACTIVITY,
1375                         provider.configure.getClassName());
1376                 assertEquals(getIntResource(R.integer.second_resize_mode),
1377                         provider.resizeMode);
1378                 assertEquals(getIntResource(R.integer.second_widget_category),
1379                         provider.widgetCategory);
1380                 assertEquals(R.layout.second_initial_layout,
1381                         provider.initialLayout);
1382                 assertEquals(R.layout.second_initial_keyguard_layout,
1383                         provider.initialKeyguardLayout);
1384                 assertEquals(R.drawable.second_android_icon,
1385                         provider.previewImage);
1386                 assertEquals(R.id.second_auto_advance_view_id,
1387                         provider.autoAdvanceViewId);
1388                 secondProviderVerified = true;
1389             }
1390         }
1391 
1392         assertTrue(firstProviderVerified && secondProviderVerified);
1393     }
1394 
grantBindAppWidgetPermission()1395     private void grantBindAppWidgetPermission() {
1396         executeShellCommandIgnoreOutput(GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND);
1397     }
1398 
revokeBindAppWidgetPermission()1399     private void revokeBindAppWidgetPermission() {
1400         executeShellCommandIgnoreOutput(REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND);
1401     }
1402 
executeShellCommandIgnoreOutput(String command)1403     private void executeShellCommandIgnoreOutput(String command) {
1404         ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
1405                 .executeShellCommand(command);
1406         try {
1407             Streams.readFully(new FileInputStream(pfd.getFileDescriptor()));
1408         } catch (IOException ioe) {
1409             IoUtils.closeQuietly(pfd);
1410         }
1411     }
1412 
getFirstAppWidgetProviderInfo()1413     private AppWidgetProviderInfo getFirstAppWidgetProviderInfo() {
1414         ComponentName firstComponentName = new ComponentName(
1415                 getInstrumentation().getTargetContext().getPackageName(),
1416                 FirstAppWidgetProvider.class.getName());
1417 
1418         return getProviderInfo(firstComponentName);
1419     }
1420 
getSecondAppWidgetProviderInfo()1421     private AppWidgetProviderInfo getSecondAppWidgetProviderInfo() {
1422         ComponentName secondComponentName = new ComponentName(
1423                 getInstrumentation().getTargetContext().getPackageName(),
1424                 SecondAppWidgetProvider.class.getName());
1425 
1426         return getProviderInfo(secondComponentName);
1427     }
1428 
getProviderInfo(ComponentName componentName)1429     private AppWidgetProviderInfo getProviderInfo(ComponentName componentName) {
1430         List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
1431 
1432         final int providerCount = providers.size();
1433         for (int i = 0; i < providerCount; i++) {
1434             AppWidgetProviderInfo provider = providers.get(i);
1435             if (componentName.equals(provider.provider)
1436                     && Process.myUserHandle().equals(provider.getProfile())) {
1437                 return provider;
1438 
1439             }
1440         }
1441 
1442         return null;
1443     }
1444 
getNormalizedDimensionResource(int resId)1445     private int getNormalizedDimensionResource(int resId) {
1446         return getInstrumentation().getTargetContext().getResources()
1447                 .getDimensionPixelSize(resId);
1448     }
1449 
getIntResource(int resId)1450     private int getIntResource(int resId) {
1451         return getInstrumentation().getTargetContext().getResources().getInteger(resId);
1452     }
1453 
getAppWidgetManager()1454     private AppWidgetManager getAppWidgetManager() {
1455         return (AppWidgetManager) getInstrumentation().getTargetContext()
1456                 .getSystemService(Context.APPWIDGET_SERVICE);
1457     }
1458 
createAppWidgetProviderCallbacks( final AtomicInteger callCounter)1459     private AppWidgetProviderCallbacks createAppWidgetProviderCallbacks(
1460             final AtomicInteger callCounter) {
1461         // Set a mock to intercept provider callbacks.
1462         AppWidgetProviderCallbacks callbacks = mock(AppWidgetProviderCallbacks.class);
1463 
1464         // onEnabled
1465         doAnswer(new Answer<Void>() {
1466             @Override
1467             public Void answer(InvocationOnMock invocation) throws Throwable {
1468                 synchronized (mLock) {
1469                     callCounter.incrementAndGet();
1470                     mLock.notifyAll();
1471                 }
1472                 return null;
1473             }
1474         }).when(callbacks).onEnabled(any(Context.class));
1475 
1476         // onUpdate
1477         doAnswer(new Answer<Void>() {
1478             @Override
1479             public Void answer(InvocationOnMock invocation) throws Throwable {
1480                 synchronized (mLock) {
1481                     callCounter.incrementAndGet();
1482                     mLock.notifyAll();
1483                 }
1484                 return null;
1485             }
1486         }).when(callbacks).onUpdate(any(Context.class), any(AppWidgetManager.class),
1487                 any(int[].class));
1488 
1489         // onAppWidgetOptionsChanged
1490         doAnswer(new Answer<Void>() {
1491             @Override
1492             public Void answer(InvocationOnMock invocation) throws Throwable {
1493                 synchronized (mLock) {
1494                     callCounter.incrementAndGet();
1495                     mLock.notifyAll();
1496                 }
1497                 return null;
1498             }
1499         }).when(callbacks).onAppWidgetOptionsChanged(any(Context.class),
1500                 any(AppWidgetManager.class), any(int.class), any(Bundle.class));
1501 
1502         // onDeleted
1503         doAnswer(new Answer<Void>() {
1504             @Override
1505             public Void answer(InvocationOnMock invocation) throws Throwable {
1506                 synchronized (mLock) {
1507                     callCounter.incrementAndGet();
1508                     mLock.notifyAll();
1509                 }
1510                 return null;
1511             }
1512         }).when(callbacks).onDeleted(any(Context.class), any(int[].class));
1513 
1514         // onDisabled
1515         doAnswer(new Answer<Void>() {
1516             @Override
1517             public Void answer(InvocationOnMock invocation) throws Throwable {
1518                 synchronized (mLock) {
1519                     callCounter.incrementAndGet();
1520                     mLock.notifyAll();
1521                 }
1522                 return null;
1523             }
1524         }).when(callbacks).onDisabled(any(Context.class));
1525 
1526         // onRestored
1527         doAnswer(new Answer<Void>() {
1528             @Override
1529             public Void answer(InvocationOnMock invocation) throws Throwable {
1530                 synchronized (mLock) {
1531                     callCounter.incrementAndGet();
1532                     mLock.notifyAll();
1533                 }
1534                 return null;
1535             }
1536         }).when(callbacks).onRestored(any(Context.class), any(int[].class),
1537                 any(int[].class));
1538 
1539         return callbacks;
1540     }
1541 
equalOptions(Bundle first, Bundle second)1542     private static boolean equalOptions(Bundle first, Bundle second) {
1543         return first.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
1544                        == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
1545                 && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
1546                        == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
1547                 && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
1548                        == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
1549                 && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
1550                        == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
1551     }
1552 
1553     private static final class OptionsMatcher extends BaseMatcher<Bundle> {
1554         private Bundle mOptions;
1555 
OptionsMatcher(Bundle options)1556         public OptionsMatcher(Bundle options) {
1557             mOptions = options;
1558         }
1559 
1560         @Override
matches(Object item)1561         public boolean matches(Object item) {
1562             Bundle options = (Bundle) item;
1563             return equalOptions(mOptions, options);
1564         }
1565 
1566         @Override
describeTo(Description description)1567         public void describeTo(Description description) {
1568             /* do nothing */
1569         }
1570     }
1571 
1572     private static final class WidgetIdsMatcher extends BaseMatcher<int[]> {
1573         private final int[] mWidgetIds;
1574 
WidgetIdsMatcher(int[] widgetIds)1575         public WidgetIdsMatcher(int[] widgetIds) {
1576             mWidgetIds = widgetIds;
1577         }
1578 
1579         @Override
matches(Object item)1580         public boolean matches(Object item) {
1581             final int[] widgetIds = (int[]) item;
1582             return Arrays.equals(widgetIds, mWidgetIds);
1583         }
1584 
1585         @Override
describeTo(Description description)1586         public void describeTo(Description description) {
1587             /* do nothing */
1588         }
1589     }
1590 
1591     private static final class RemoteViewsMatcher extends BaseMatcher<RemoteViews> {
1592         private final int mLayoutId;
1593         private final String mPackageName;
1594 
RemoteViewsMatcher(int layoutId, String packageName)1595         public RemoteViewsMatcher(int layoutId, String packageName) {
1596             mLayoutId = layoutId;
1597             mPackageName = packageName;
1598         }
1599 
1600         @Override
matches(Object item)1601         public boolean matches(Object item) {
1602             final RemoteViews remoteViews = (RemoteViews) item;
1603             return remoteViews != null && remoteViews.getLayoutId() == mLayoutId
1604                     && remoteViews.getPackage().equals(mPackageName);
1605         }
1606 
1607         @Override
describeTo(Description description)1608         public void describeTo(Description description) {
1609             /* do nothing */
1610         }
1611     }
1612 
1613     private static class MyAppWidgetHostView extends AppWidgetHostView {
1614         private OnUpdateAppWidgetListener mOnUpdateAppWidgetListener;
1615 
1616 
1617         public interface OnUpdateAppWidgetListener {
onUpdateAppWidget(RemoteViews remoteViews)1618             public void onUpdateAppWidget(RemoteViews remoteViews);
1619         }
1620 
MyAppWidgetHostView(Context context)1621         private MyAppWidgetHostView(Context context) {
1622             super(context);
1623         }
1624 
setOnUpdateAppWidgetListener(OnUpdateAppWidgetListener listener)1625         public void setOnUpdateAppWidgetListener(OnUpdateAppWidgetListener listener) {
1626             mOnUpdateAppWidgetListener = listener;
1627         }
1628 
1629         @Override
updateAppWidget(RemoteViews remoteViews)1630         public void updateAppWidget(RemoteViews remoteViews) {
1631             super.updateAppWidget(remoteViews);
1632             if (mOnUpdateAppWidgetListener != null) {
1633                 mOnUpdateAppWidgetListener.onUpdateAppWidget(remoteViews);
1634             }
1635         }
1636     }
1637 }
1638