1/*
2 * Copyright (C) 2018 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.wifi;
18
19import static com.android.server.wifi.WakeupNotificationFactory.ACTION_DISMISS_NOTIFICATION;
20import static com.android.server.wifi.WakeupNotificationFactory.ACTION_OPEN_WIFI_PREFERENCES;
21import static com.android.server.wifi.WakeupNotificationFactory.ACTION_TURN_OFF_WIFI_WAKE;
22
23import static org.junit.Assert.assertFalse;
24import static org.junit.Assert.assertTrue;
25import static org.mockito.ArgumentMatchers.any;
26import static org.mockito.ArgumentMatchers.eq;
27import static org.mockito.Mockito.never;
28import static org.mockito.Mockito.times;
29import static org.mockito.Mockito.verify;
30import static org.mockito.Mockito.when;
31
32import android.app.NotificationManager;
33import android.content.BroadcastReceiver;
34import android.content.Context;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.os.Handler;
38import android.os.test.TestLooper;
39import android.provider.Settings;
40
41import org.junit.Before;
42import org.junit.Test;
43import org.mockito.ArgumentCaptor;
44import org.mockito.InOrder;
45import org.mockito.Mock;
46import org.mockito.Mockito;
47import org.mockito.MockitoAnnotations;
48
49/** Unit tests for {@link com.android.server.wifi.WakeupOnboarding} */
50public class WakeupOnboardingTest {
51
52    @Mock private Context mContext;
53    @Mock private WifiConfigManager mWifiConfigManager;
54    @Mock private FrameworkFacade mFrameworkFacade;
55    @Mock private WakeupNotificationFactory mWakeupNotificationFactory;
56    @Mock private NotificationManager mNotificationManager;
57
58    private TestLooper mLooper;
59    private WakeupOnboarding mWakeupOnboarding;
60
61    // convenience method for resetting onboarded status
62    private void setOnboardedStatus(boolean isOnboarded) {
63        mWakeupOnboarding.getIsOnboadedDataSource().setData(isOnboarded);
64    }
65
66    private void setNotificationsShown(int numNotifications) {
67        mWakeupOnboarding.getNotificationsDataSource().setData(numNotifications);
68    }
69
70    @Before
71    public void setUp() {
72        MockitoAnnotations.initMocks(this);
73
74        when(mContext.getSystemService(Context.NOTIFICATION_SERVICE))
75                .thenReturn(mNotificationManager);
76
77        mLooper = new TestLooper();
78        mWakeupOnboarding = new WakeupOnboarding(mContext, mWifiConfigManager, mLooper.getLooper(),
79                mFrameworkFacade, mWakeupNotificationFactory);
80    }
81
82    /**
83     * Verify that the notification shows if the user isn't onboarded.
84     */
85    @Test
86    public void showsNotificationIfNotOnboarded() {
87        setOnboardedStatus(false);
88        mWakeupOnboarding.maybeShowNotification();
89
90        verify(mNotificationManager).notify(eq(WakeupNotificationFactory.ONBOARD_ID), any());
91    }
92
93    /**
94     * Verify that the notification does not show if the user is onboarded.
95     */
96    @Test
97    public void doesNotShowNotificationIfAlreadyOnboarded() {
98        setOnboardedStatus(true);
99        mWakeupOnboarding.maybeShowNotification();
100
101        verify(mNotificationManager, never())
102                .notify(eq(WakeupNotificationFactory.ONBOARD_ID), any());
103    }
104
105    /**
106     * Verify that the notification does not relaunch if it's already showing.
107     */
108    @Test
109    public void doesNotShowNotificationIfAlreadyShowing() {
110        setOnboardedStatus(false);
111        mWakeupOnboarding.maybeShowNotification();
112        mWakeupOnboarding.maybeShowNotification();
113
114        InOrder inOrder = Mockito.inOrder(mNotificationManager);
115        inOrder.verify(mNotificationManager)
116                .notify(eq(WakeupNotificationFactory.ONBOARD_ID), any());
117        inOrder.verifyNoMoreInteractions();
118    }
119
120    /**
121     * Verify that the user is onboarded when the notification is dismissed.
122     */
123    @Test
124    public void dismissNotificationAction_setsOnboarded() {
125        setOnboardedStatus(false);
126        assertFalse(mWakeupOnboarding.isOnboarded());
127
128        mWakeupOnboarding.maybeShowNotification();
129        ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
130        verify(mContext).registerReceiver(captor.capture(), any(IntentFilter.class), any(),
131                any(Handler.class));
132        BroadcastReceiver broadcastReceiver = captor.getValue();
133
134        broadcastReceiver.onReceive(mContext, new Intent(ACTION_DISMISS_NOTIFICATION));
135
136        verify(mNotificationManager).cancel(WakeupNotificationFactory.ONBOARD_ID);
137        assertTrue(mWakeupOnboarding.isOnboarded());
138    }
139
140    /**
141     * Verify that the user is onboarded and Wifi Wake is turned off when the user selects the
142     * ACTION_TURN_OFF_WIFI_WAKE action.
143     */
144    @Test
145    public void turnOffWifiWakeAction_setsOnboardedAndTurnsOffWifiWake() {
146        setOnboardedStatus(false);
147        assertFalse(mWakeupOnboarding.isOnboarded());
148
149        mWakeupOnboarding.maybeShowNotification();
150        ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
151        verify(mContext).registerReceiver(captor.capture(), any(IntentFilter.class), any(),
152                any(Handler.class));
153        BroadcastReceiver broadcastReceiver = captor.getValue();
154
155        broadcastReceiver.onReceive(mContext, new Intent(ACTION_TURN_OFF_WIFI_WAKE));
156
157        verify(mFrameworkFacade).setIntegerSetting(mContext,
158                Settings.Global.WIFI_WAKEUP_ENABLED, 0);
159
160        verify(mNotificationManager).cancel(WakeupNotificationFactory.ONBOARD_ID);
161        assertTrue(mWakeupOnboarding.isOnboarded());
162    }
163
164    /**
165     * Verify that the user is onboarded and sent to WifiSettings when the user selects the
166     * ACTION_OPEN_WIFI_SETTINGS action.
167     */
168    @Test
169    public void openWifiSettingsAction_setsOnboardedAndOpensWifiSettings() {
170        setOnboardedStatus(false);
171        assertFalse(mWakeupOnboarding.isOnboarded());
172
173        mWakeupOnboarding.maybeShowNotification();
174        ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
175        verify(mContext).registerReceiver(captor.capture(), any(IntentFilter.class), any(),
176                any(Handler.class));
177        BroadcastReceiver broadcastReceiver = captor.getValue();
178
179        broadcastReceiver.onReceive(mContext, new Intent(ACTION_OPEN_WIFI_PREFERENCES));
180
181        verify(mContext).startActivity(any());
182
183        verify(mNotificationManager).cancel(WakeupNotificationFactory.ONBOARD_ID);
184        assertTrue(mWakeupOnboarding.isOnboarded());
185    }
186
187    /**
188     * Verify that onStop() doesn't onboard the user.
189     */
190    @Test
191    public void onStopDismissesNotificationWithoutOnboarding() {
192        setOnboardedStatus(false);
193        assertFalse(mWakeupOnboarding.isOnboarded());
194
195        mWakeupOnboarding.maybeShowNotification();
196        mWakeupOnboarding.onStop();
197
198        verify(mNotificationManager).cancel(WakeupNotificationFactory.ONBOARD_ID);
199        assertFalse(mWakeupOnboarding.isOnboarded());
200    }
201
202    /**
203     * Verify that incrementing the notification count saves to store.
204     */
205    @Test
206    public void setOnboardedSavesToStore() {
207        setOnboardedStatus(false);
208        mWakeupOnboarding.setOnboarded();
209        verify(mWifiConfigManager).saveToStore(false /* forceWrite */);
210        assertTrue(mWakeupOnboarding.isOnboarded());
211    }
212
213    /**
214     * Verify that incrementing the notification count saves to store.
215     */
216    @Test
217    public void incrementingNotificationCountSavesToStore() {
218        setOnboardedStatus(false);
219        setNotificationsShown(0);
220        mWakeupOnboarding.maybeShowNotification();
221        verify(mWifiConfigManager).saveToStore(false /* forceWrite */);
222    }
223
224    /**
225     * Verify that the notification does not show multiple times within 24 hours.
226     */
227    @Test
228    public void doesNotShowMultipleNotificationsWithin24Hours() {
229        setOnboardedStatus(false);
230        setNotificationsShown(0);
231
232        mWakeupOnboarding.maybeShowNotification(0 /* timestamp */);
233        mWakeupOnboarding.onStop();
234        mWakeupOnboarding.maybeShowNotification(0 /* timestamp */);
235
236        InOrder inOrder = Mockito.inOrder(mNotificationManager);
237        inOrder.verify(mNotificationManager)
238                .notify(eq(WakeupNotificationFactory.ONBOARD_ID), any());
239        inOrder.verify(mNotificationManager).cancel(WakeupNotificationFactory.ONBOARD_ID);
240        inOrder.verifyNoMoreInteractions();
241    }
242
243    /**
244     * Verify that notification reappears after 24 hours if not onboarded.
245     */
246    @Test
247    public void showsNotificationsOutsideOf24Hours() {
248        setOnboardedStatus(false);
249        setNotificationsShown(0);
250
251        mWakeupOnboarding.maybeShowNotification(0 /* timestamp */);
252        assertFalse(mWakeupOnboarding.isOnboarded());
253
254        mWakeupOnboarding.onStop();
255        mWakeupOnboarding.maybeShowNotification(WakeupOnboarding.REQUIRED_NOTIFICATION_DELAY + 1);
256
257        verify(mNotificationManager, times(2))
258                .notify(eq(WakeupNotificationFactory.ONBOARD_ID), any());
259    }
260
261    /**
262     * Verify that the user is onboarded after
263     * {@link WakeupOnboarding#NOTIFICATIONS_UNTIL_ONBOARDED} notifications are shown.
264     */
265    @Test
266    public void onboardsUserAfterThreeNotifications() {
267        setOnboardedStatus(false);
268        setNotificationsShown(WakeupOnboarding.NOTIFICATIONS_UNTIL_ONBOARDED - 1);
269
270        mWakeupOnboarding.maybeShowNotification(0 /* timestamp */);
271        assertTrue(mWakeupOnboarding.isOnboarded());
272    }
273}
274