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