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