AppWidgetServiceImplTest.java revision 79255161042bf60bd890923104b3444a730295b5
1/*
2 * Copyright (C) 2017 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
17package com.android.server.appwidget;
18
19import static org.mockito.ArgumentMatchers.any;
20import static org.mockito.ArgumentMatchers.anyInt;
21import static org.mockito.ArgumentMatchers.anyString;
22import static org.mockito.ArgumentMatchers.eq;
23import static org.mockito.Mockito.mock;
24import static org.mockito.Mockito.reset;
25import static org.mockito.Mockito.times;
26import static org.mockito.Mockito.verify;
27import static org.mockito.Mockito.when;
28
29import android.app.admin.DevicePolicyManagerInternal;
30import android.appwidget.AppWidgetManager;
31import android.appwidget.AppWidgetProviderInfo;
32import android.appwidget.PendingHostUpdate;
33import android.content.BroadcastReceiver;
34import android.content.ComponentName;
35import android.content.ContextWrapper;
36import android.content.Intent;
37import android.content.IntentFilter;
38import android.content.pm.LauncherApps;
39import android.content.pm.ShortcutServiceInternal;
40import android.os.Handler;
41import android.os.UserHandle;
42import android.test.InstrumentationTestCase;
43import android.test.suitebuilder.annotation.SmallTest;
44import android.widget.RemoteViews;
45
46import com.android.internal.appwidget.IAppWidgetHost;
47import com.android.server.LocalServices;
48
49import org.mockito.ArgumentCaptor;
50
51import java.util.Iterator;
52import java.util.List;
53import java.util.Random;
54import java.util.concurrent.CountDownLatch;
55
56
57/**
58 * Tests for {@link AppWidgetManager} and {@link AppWidgetServiceImpl}.
59 *
60 m FrameworksServicesTests &&
61 adb install \
62 -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
63 adb shell am instrument -e class com.android.server.appwidget.AppWidgetServiceImplTest \
64 -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
65 */
66@SmallTest
67public class AppWidgetServiceImplTest extends InstrumentationTestCase {
68
69    private static final int HOST_ID = 42;
70
71    private TestContext mTestContext;
72    private String mPkgName;
73    private AppWidgetServiceImpl mService;
74    private AppWidgetManager mManager;
75
76    private ShortcutServiceInternal mMockShortcutService;
77    private IAppWidgetHost mMockHost;
78
79    @Override
80    protected void setUp() throws Exception {
81        super.setUp();
82        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
83        LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
84
85        mTestContext = new TestContext();
86        mPkgName = mTestContext.getOpPackageName();
87        mService = new AppWidgetServiceImpl(mTestContext);
88        mManager = new AppWidgetManager(mTestContext, mService);
89
90        mMockShortcutService = mock(ShortcutServiceInternal.class);
91        mMockHost = mock(IAppWidgetHost.class);
92        LocalServices.addService(ShortcutServiceInternal.class, mMockShortcutService);
93        mService.onStart();
94    }
95
96    public void testRequestPinAppWidget_otherProvider() {
97        ComponentName otherProvider = null;
98        for (AppWidgetProviderInfo provider : mManager.getInstalledProviders()) {
99            if (!provider.provider.getPackageName().equals(mTestContext.getPackageName())) {
100                otherProvider = provider.provider;
101                break;
102            }
103        }
104        if (otherProvider == null) {
105            // No other provider found. Ignore this test.
106        }
107        assertFalse(mManager.requestPinAppWidget(otherProvider, null, null));
108    }
109
110    public void testRequestPinAppWidget() {
111        ComponentName provider = new ComponentName(mTestContext, DummyAppWidget.class);
112        // Set up users.
113        when(mMockShortcutService.requestPinAppWidget(anyString(),
114                any(AppWidgetProviderInfo.class), eq(null), eq(null), anyInt()))
115                .thenReturn(true);
116        assertTrue(mManager.requestPinAppWidget(provider, null, null));
117
118        final ArgumentCaptor<AppWidgetProviderInfo> providerCaptor =
119                ArgumentCaptor.forClass(AppWidgetProviderInfo.class);
120        verify(mMockShortcutService, times(1)).requestPinAppWidget(anyString(),
121                providerCaptor.capture(), eq(null), eq(null), anyInt());
122        assertEquals(provider, providerCaptor.getValue().provider);
123    }
124
125    public void testIsRequestPinAppWidgetSupported() {
126        // Set up users.
127        when(mMockShortcutService.isRequestPinItemSupported(anyInt(), anyInt()))
128                .thenReturn(true, false);
129        assertTrue(mManager.isRequestPinAppWidgetSupported());
130        assertFalse(mManager.isRequestPinAppWidgetSupported());
131
132        verify(mMockShortcutService, times(2)).isRequestPinItemSupported(anyInt(),
133                eq(LauncherApps.PinItemRequest.REQUEST_TYPE_APPWIDGET));
134    }
135
136    public void testProviderUpdatesReceived() throws Exception {
137        int widgetId = setupHostAndWidget();
138        RemoteViews view = new RemoteViews(mPkgName, android.R.layout.simple_list_item_1);
139        mManager.updateAppWidget(widgetId, view);
140        mManager.updateAppWidget(widgetId, view);
141        mManager.updateAppWidget(widgetId, view);
142        mManager.updateAppWidget(widgetId, view);
143
144        flushMainThread();
145        verify(mMockHost, times(4)).updateAppWidget(eq(widgetId), any(RemoteViews.class));
146
147        reset(mMockHost);
148        mManager.notifyAppWidgetViewDataChanged(widgetId, 22);
149        flushMainThread();
150        verify(mMockHost, times(1)).viewDataChanged(eq(widgetId), eq(22));
151    }
152
153    public void testProviderUpdatesNotReceived() throws Exception {
154        int widgetId = setupHostAndWidget();
155        mService.stopListening(mPkgName, HOST_ID);
156        RemoteViews view = new RemoteViews(mPkgName, android.R.layout.simple_list_item_1);
157        mManager.updateAppWidget(widgetId, view);
158        mManager.notifyAppWidgetViewDataChanged(widgetId, 22);
159
160        flushMainThread();
161        verify(mMockHost, times(0)).updateAppWidget(anyInt(), any(RemoteViews.class));
162        verify(mMockHost, times(0)).viewDataChanged(anyInt(), eq(22));
163    }
164
165    public void testNoUpdatesReceived_queueEmpty() {
166        int widgetId = setupHostAndWidget();
167        RemoteViews view = new RemoteViews(mPkgName, android.R.layout.simple_list_item_1);
168        mManager.updateAppWidget(widgetId, view);
169        mManager.notifyAppWidgetViewDataChanged(widgetId, 22);
170        mService.stopListening(mPkgName, HOST_ID);
171
172        List<PendingHostUpdate> updates = mService.startListening(
173                mMockHost, mPkgName, HOST_ID, new int[0]).getList();
174        assertTrue(updates.isEmpty());
175    }
176
177    /**
178     * Sends dummy widget updates to {@link #mManager}.
179     * @param widgetId widget to update
180     * @param viewIds a list of view ids for which
181     *                {@link AppWidgetManager#notifyAppWidgetViewDataChanged} will be called
182     */
183    private void sendDummyUpdates(int widgetId, int... viewIds) {
184        Random r = new Random();
185        RemoteViews view = new RemoteViews(mPkgName, android.R.layout.simple_list_item_1);
186        for (int i = r.nextInt(10) + 2; i >= 0; i--) {
187            mManager.updateAppWidget(widgetId, view);
188        }
189
190        for (int viewId : viewIds) {
191            mManager.notifyAppWidgetViewDataChanged(widgetId, viewId);
192            for (int i = r.nextInt(3); i >= 0; i--) {
193                mManager.updateAppWidget(widgetId, view);
194            }
195        }
196    }
197
198    public void testNoUpdatesReceived_queueNonEmpty_noWidgetId() {
199        int widgetId = setupHostAndWidget();
200        mService.stopListening(mPkgName, HOST_ID);
201
202        sendDummyUpdates(widgetId, 22, 23);
203        List<PendingHostUpdate> updates = mService.startListening(
204                mMockHost, mPkgName, HOST_ID, new int[0]).getList();
205        assertTrue(updates.isEmpty());
206    }
207
208    public void testUpdatesReceived_queueNotEmpty_widgetIdProvided() {
209        int widgetId = setupHostAndWidget();
210        int widgetId2 = bindNewWidget();
211        mService.stopListening(mPkgName, HOST_ID);
212
213        sendDummyUpdates(widgetId, 22, 23);
214        sendDummyUpdates(widgetId2, 100, 101, 102);
215
216        List<PendingHostUpdate> updates = mService.startListening(
217                mMockHost, mPkgName, HOST_ID, new int[]{widgetId}).getList();
218        // 3 updates corresponding to the first widget
219        assertEquals(3, updates.size());
220    }
221
222    public void testUpdatesReceived_queueNotEmpty_widgetIdProvided2() {
223        int widgetId = setupHostAndWidget();
224        int widgetId2 = bindNewWidget();
225        mService.stopListening(mPkgName, HOST_ID);
226
227        sendDummyUpdates(widgetId, 22, 23);
228        sendDummyUpdates(widgetId2, 100, 101, 102);
229
230        List<PendingHostUpdate> updates = mService.startListening(
231                mMockHost, mPkgName, HOST_ID, new int[]{widgetId2}).getList();
232        // 4 updates corresponding to the second widget
233        assertEquals(4, updates.size());
234    }
235
236    public void testUpdatesReceived_queueNotEmpty_multipleWidgetIdProvided() {
237        int widgetId = setupHostAndWidget();
238        int widgetId2 = bindNewWidget();
239        mService.stopListening(mPkgName, HOST_ID);
240
241        sendDummyUpdates(widgetId, 22, 23);
242        sendDummyUpdates(widgetId2, 100, 101, 102);
243
244        List<PendingHostUpdate> updates = mService.startListening(
245                mMockHost, mPkgName, HOST_ID, new int[]{widgetId, widgetId2}).getList();
246        // 3 updates for first widget and 4 for second
247        assertEquals(7, updates.size());
248    }
249
250    public void testGetInstalledProvidersForPackage() {
251        List<AppWidgetProviderInfo> allProviders = mManager.getInstalledProviders();
252        assertTrue(!allProviders.isEmpty());
253        String packageName = allProviders.get(0).provider.getPackageName();
254        List<AppWidgetProviderInfo> providersForPackage = mManager.getInstalledProvidersForPackage(
255                packageName, null);
256        // Remove providers from allProviders that don't have the given package name.
257        Iterator<AppWidgetProviderInfo> iter = allProviders.iterator();
258        while (iter.hasNext()) {
259            if (!iter.next().provider.getPackageName().equals(packageName)) {
260                iter.remove();
261            }
262        }
263        assertEquals(allProviders.size(), providersForPackage.size());
264        for (int i = 0; i < allProviders.size(); i++) {
265            assertEquals(allProviders.get(i).provider, providersForPackage.get(i).provider);
266        }
267    }
268
269    private int setupHostAndWidget() {
270        List<PendingHostUpdate> updates = mService.startListening(
271                mMockHost, mPkgName, HOST_ID, new int[0]).getList();
272        assertTrue(updates.isEmpty());
273        return bindNewWidget();
274    }
275
276    private int bindNewWidget() {
277        ComponentName provider = new ComponentName(mTestContext, DummyAppWidget.class);
278        int widgetId = mService.allocateAppWidgetId(mPkgName, HOST_ID);
279        assertTrue(mManager.bindAppWidgetIdIfAllowed(widgetId, provider));
280        assertEquals(provider, mManager.getAppWidgetInfo(widgetId).provider);
281
282        return widgetId;
283    }
284
285    private void flushMainThread() throws Exception {
286        CountDownLatch latch = new CountDownLatch(1);
287        new Handler(mTestContext.getMainLooper()).post(latch::countDown);
288        latch.await();
289    }
290
291    private class TestContext extends ContextWrapper {
292
293        public TestContext() {
294            super(getInstrumentation().getContext());
295        }
296
297        @Override
298        public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
299                IntentFilter filter, String broadcastPermission, Handler scheduler) {
300            // ignore.
301            return null;
302        }
303
304        @Override
305        public void unregisterReceiver(BroadcastReceiver receiver) {
306            // ignore.
307        }
308
309        @Override
310        public void enforceCallingOrSelfPermission(String permission, String message) {
311            // ignore.
312        }
313
314        @Override
315        public void sendBroadcastAsUser(Intent intent, UserHandle user) {
316            // ignore.
317        }
318    }
319}
320