WifiControllerTest.java revision 24750cd1f8148d6a935bda96f3b17f22b0c1d8bd
1/*
2 * Copyright (C) 2016 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 android.net.wifi.WifiManager.WIFI_MODE_FULL;
20
21import static com.android.server.wifi.WifiController.CMD_AP_STOPPED;
22import static com.android.server.wifi.WifiController.CMD_DEVICE_IDLE;
23import static com.android.server.wifi.WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED;
24import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED;
25import static com.android.server.wifi.WifiController.CMD_RESTART_WIFI;
26import static com.android.server.wifi.WifiController.CMD_SET_AP;
27import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
28
29import static org.junit.Assert.assertEquals;
30import static org.mockito.Matchers.*;
31import static org.mockito.Mockito.*;
32
33import android.content.ContentResolver;
34import android.content.Context;
35import android.os.WorkSource;
36import android.test.suitebuilder.annotation.SmallTest;
37import android.util.Log;
38
39import com.android.internal.util.IState;
40import com.android.internal.util.StateMachine;
41
42import org.junit.After;
43import org.junit.Before;
44import org.junit.Test;
45import org.mockito.InOrder;
46import org.mockito.Mock;
47import org.mockito.MockitoAnnotations;
48
49import java.io.ByteArrayOutputStream;
50import java.io.PrintWriter;
51import java.lang.reflect.Method;
52
53/**
54 * Test WifiController for changes in and out of ECM and SoftAP modes.
55 */
56@SmallTest
57public class WifiControllerTest {
58
59    private static final String TAG = "WifiControllerTest";
60
61    private void dumpState() {
62        ByteArrayOutputStream stream = new ByteArrayOutputStream();
63        PrintWriter writer = new PrintWriter(stream);
64        mWifiController.dump(null, writer, null);
65        writer.flush();
66        Log.d(TAG, "WifiStateMachine state -" + stream.toString());
67    }
68
69    private IState getCurrentState() throws Exception {
70        Method method = StateMachine.class.getDeclaredMethod("getCurrentState");
71        method.setAccessible(true);
72        return (IState) method.invoke(mWifiController);
73    }
74
75    private void initializeSettingsStore() throws Exception {
76        when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
77        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
78        when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true);
79    }
80
81    MockLooper mLooper;
82    @Mock Context mContext;
83    @Mock WifiServiceImpl mService;
84    @Mock FrameworkFacade mFacade;
85    @Mock WifiSettingsStore mSettingsStore;
86    @Mock WifiStateMachine mWifiStateMachine;
87    @Mock WifiLockManager mWifiLockManager;
88
89    WifiController mWifiController;
90
91    @Before
92    public void setUp() throws Exception {
93        MockitoAnnotations.initMocks(this);
94
95        mLooper = new MockLooper();
96
97        initializeSettingsStore();
98
99        when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
100
101        mWifiController = new WifiController(mContext, mWifiStateMachine,
102                mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade);
103
104        mWifiController.start();
105        mLooper.dispatchAll();
106    }
107
108    @After
109    public void cleanUp() {
110        mLooper.dispatchAll();
111    }
112
113    @Test
114    public void enableWifi() throws Exception {
115        assertEquals("StaDisabledWithScanState", getCurrentState().getName());
116
117        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true);
118        mWifiController.sendMessage(CMD_WIFI_TOGGLED);
119        mLooper.dispatchAll();
120        assertEquals("DeviceActiveState", getCurrentState().getName());
121
122        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
123        mWifiController.sendMessage(CMD_WIFI_TOGGLED);
124        mLooper.dispatchAll();
125        assertEquals("StaDisabledWithScanState", getCurrentState().getName());
126
127        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true);
128        mWifiController.sendMessage(CMD_WIFI_TOGGLED);
129        mLooper.dispatchAll();
130        assertEquals("DeviceActiveState", getCurrentState().getName());
131    }
132
133    @Test
134    public void testEcmOn() throws Exception {
135        enableWifi();
136
137        // Test with WifiDisableInECBM turned on:
138        when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true);
139        doTestEcm(true);
140    }
141
142    @Test
143    public void testEcmOff() throws Exception {
144        enableWifi();
145
146        // Test with WifiDisableInECBM turned off
147        when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(false);
148        doTestEcm(false);
149    }
150
151    private void assertInEcm(boolean ecmEnabled) throws Exception {
152        if (ecmEnabled) {
153            assertEquals("EcmState", getCurrentState().getName());
154        } else {
155            assertEquals("DeviceActiveState", getCurrentState().getName());
156        }
157    }
158
159
160    private void doTestEcm(boolean ecmEnabled) throws Exception {
161
162        // test ecm changed
163        mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1);
164        mLooper.dispatchAll();
165        assertInEcm(ecmEnabled);
166
167        mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0);
168        mLooper.dispatchAll();
169        assertEquals("DeviceActiveState", getCurrentState().getName());
170
171        // test call state changed
172        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1);
173        mLooper.dispatchAll();
174        assertInEcm(ecmEnabled);
175
176        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0);
177        mLooper.dispatchAll();
178        assertEquals("DeviceActiveState", getCurrentState().getName());
179
180
181        // test both changed (variation 1 - the good case)
182        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1);
183        mLooper.dispatchAll();
184        assertInEcm(ecmEnabled);
185
186        mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1);
187        mLooper.dispatchAll();
188        assertInEcm(ecmEnabled);
189
190        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0);
191        mLooper.dispatchAll();
192        assertInEcm(ecmEnabled);
193
194        mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0);
195        mLooper.dispatchAll();
196        assertEquals("DeviceActiveState", getCurrentState().getName());
197
198        // test both changed (variation 2 - emergency call in ecm)
199        mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1);
200        mLooper.dispatchAll();
201        assertInEcm(ecmEnabled);
202
203        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1);
204        mLooper.dispatchAll();
205        assertInEcm(ecmEnabled);
206
207        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0);
208        mLooper.dispatchAll();
209        assertInEcm(ecmEnabled);
210
211        mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0);
212        mLooper.dispatchAll();
213        assertEquals("DeviceActiveState", getCurrentState().getName());
214
215        // test both changed (variation 3 - not so good order of events)
216        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1);
217        mLooper.dispatchAll();
218        assertInEcm(ecmEnabled);
219
220        mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1);
221        mLooper.dispatchAll();
222        assertInEcm(ecmEnabled);
223
224        mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0);
225        mLooper.dispatchAll();
226        assertInEcm(ecmEnabled);
227
228        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0);
229        mLooper.dispatchAll();
230        assertEquals("DeviceActiveState", getCurrentState().getName());
231
232        // test that Wifi toggle doesn't exit Ecm
233        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1);
234        mLooper.dispatchAll();
235        assertInEcm(ecmEnabled);
236
237        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true);
238        mWifiController.sendMessage(CMD_WIFI_TOGGLED);
239        mLooper.dispatchAll();
240        assertInEcm(ecmEnabled);
241
242        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0);
243        mLooper.dispatchAll();
244        assertEquals("DeviceActiveState", getCurrentState().getName());
245    }
246
247    /**
248     * When AP mode is enabled and wifi was previously in AP mode, we should return to
249     * DeviceActiveState after the AP is disabled.
250     * Enter DeviceActiveState, activate AP mode, disable AP mode.
251     * <p>
252     * Expected: AP should successfully start and exit, then return to DeviceActiveState.
253     */
254    @Test
255    public void testReturnToDeviceActiveStateAfterAPModeShutdown() throws Exception {
256        enableWifi();
257        assertEquals("DeviceActiveState", getCurrentState().getName());
258
259        mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget();
260        mLooper.dispatchAll();
261        assertEquals("ApEnabledState", getCurrentState().getName());
262
263        when(mSettingsStore.getWifiSavedState()).thenReturn(1);
264        mWifiController.obtainMessage(CMD_AP_STOPPED).sendToTarget();
265        mLooper.dispatchAll();
266
267        InOrder inOrder = inOrder(mWifiStateMachine);
268        inOrder.verify(mWifiStateMachine).setSupplicantRunning(true);
269        inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE);
270        inOrder.verify(mWifiStateMachine).setDriverStart(true);
271        assertEquals("DeviceActiveState", getCurrentState().getName());
272    }
273
274    /**
275     * When AP mode is enabled and wifi is toggled on, we should transition to
276     * DeviceActiveState after the AP is disabled.
277     * Enter DeviceActiveState, activate AP mode, toggle WiFi.
278     * <p>
279     * Expected: AP should successfully start and exit, then return to DeviceActiveState.
280     */
281    @Test
282    public void testReturnToDeviceActiveStateAfterWifiEnabledShutdown() throws Exception {
283        enableWifi();
284        assertEquals("DeviceActiveState", getCurrentState().getName());
285
286        mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget();
287        mLooper.dispatchAll();
288        assertEquals("ApEnabledState", getCurrentState().getName());
289
290        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true);
291        mWifiController.obtainMessage(CMD_WIFI_TOGGLED).sendToTarget();
292        mWifiController.obtainMessage(CMD_AP_STOPPED).sendToTarget();
293        mLooper.dispatchAll();
294
295        InOrder inOrder = inOrder(mWifiStateMachine);
296        inOrder.verify(mWifiStateMachine).setSupplicantRunning(true);
297        inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE);
298        inOrder.verify(mWifiStateMachine).setDriverStart(true);
299        assertEquals("DeviceActiveState", getCurrentState().getName());
300    }
301
302    /**
303     * When the wifi device is idle, AP mode is enabled and disabled
304     * we should return to the appropriate Idle state.
305     * Enter DeviceActiveState, indicate idle device, activate AP mode, disable AP mode.
306     * <p>
307     * Expected: AP should successfully start and exit, then return to a device idle state.
308     */
309    @Test
310    public void testReturnToDeviceIdleStateAfterAPModeShutdown() throws Exception {
311        enableWifi();
312        assertEquals("DeviceActiveState", getCurrentState().getName());
313
314        // make sure mDeviceIdle is set to true
315        when(mWifiLockManager.getStrongestLockMode()).thenReturn(WIFI_MODE_FULL);
316        when(mWifiLockManager.createMergedWorkSource()).thenReturn(new WorkSource());
317        mWifiController.sendMessage(CMD_DEVICE_IDLE);
318        mLooper.dispatchAll();
319        assertEquals("FullLockHeldState", getCurrentState().getName());
320
321        mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget();
322        mLooper.dispatchAll();
323        assertEquals("ApEnabledState", getCurrentState().getName());
324
325        when(mSettingsStore.getWifiSavedState()).thenReturn(1);
326        mWifiController.obtainMessage(CMD_AP_STOPPED).sendToTarget();
327        mLooper.dispatchAll();
328
329        InOrder inOrder = inOrder(mWifiStateMachine);
330        inOrder.verify(mWifiStateMachine).setSupplicantRunning(true);
331        inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE);
332        inOrder.verify(mWifiStateMachine).setDriverStart(true);
333        assertEquals("FullLockHeldState", getCurrentState().getName());
334    }
335
336    /**
337     * The command to trigger a WiFi reset should not trigger any action by WifiController if we
338     * are not in STA mode.
339     * WiFi is not in connect mode, so any calls to reset the wifi stack due to connection failures
340     * should be ignored.
341     * Create and start WifiController in ApStaDisabledState, send command to restart WiFi
342     * <p>
343     * Expected: WiFiController should not call WifiStateMachine.setSupplicantRunning(false)
344     */
345    @Test
346    public void testRestartWifiStackInApStaDisabledState() throws Exception {
347        // Start a new WifiController with wifi disabled
348        when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
349        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
350        when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false);
351
352        when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
353
354        mWifiController = new WifiController(mContext, mWifiStateMachine,
355                mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade);
356
357        mWifiController.start();
358        mLooper.dispatchAll();
359
360        reset(mWifiStateMachine);
361        assertEquals("ApStaDisabledState", getCurrentState().getName());
362        mWifiController.sendMessage(CMD_RESTART_WIFI);
363        mLooper.dispatchAll();
364        verifyZeroInteractions(mWifiStateMachine);
365    }
366
367    /**
368     * The command to trigger a WiFi reset should not trigger any action by WifiController if we
369     * are not in STA mode, even if scans are allowed.
370     * WiFi is not in connect mode, so any calls to reset the wifi stack due to connection failures
371     * should be ignored.
372     * Create and start WifiController in StaDisablediWithScanState, send command to restart WiFi
373     * <p>
374     * Expected: WiFiController should not call WifiStateMachine.setSupplicantRunning(false)
375     */
376    @Test
377    public void testRestartWifiStackInStaDisabledWithScanState() throws Exception {
378        reset(mWifiStateMachine);
379        assertEquals("StaDisabledWithScanState", getCurrentState().getName());
380        mWifiController.sendMessage(CMD_RESTART_WIFI);
381        mLooper.dispatchAll();
382        verifyZeroInteractions(mWifiStateMachine);
383    }
384
385    /**
386     * The command to trigger a WiFi reset should trigger a wifi reset in WifiStateMachine through
387     * the WifiStateMachine.setSupplicantRunning(false) call when in STA mode.
388     * WiFi is in connect mode, calls to reset the wifi stack due to connection failures
389     * should trigger a supplicant stop, and subsequently, a driver reload.
390     * Create and start WifiController in DeviceActiveState, send command to restart WiFi
391     * <p>
392     * Expected: WiFiController should call WifiStateMachine.setSupplicantRunning(false),
393     * WifiStateMachine should enter CONNECT_MODE and the wifi driver should be started.
394     */
395    @Test
396    public void testRestartWifiStackInStaEnabledState() throws Exception {
397        enableWifi();
398
399        reset(mWifiStateMachine);
400        assertEquals("DeviceActiveState", getCurrentState().getName());
401        mWifiController.sendMessage(CMD_RESTART_WIFI);
402        mLooper.dispatchAll();
403        InOrder inOrder = inOrder(mWifiStateMachine);
404        inOrder.verify(mWifiStateMachine).setSupplicantRunning(false);
405        inOrder.verify(mWifiStateMachine).setSupplicantRunning(true);
406        inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE);
407        inOrder.verify(mWifiStateMachine).setDriverStart(true);
408        assertEquals("DeviceActiveState", getCurrentState().getName());
409    }
410
411    /**
412     * The command to trigger a WiFi reset should not trigger a reset when in ECM mode.
413     * Enable wifi and enter ECM state, send command to restart wifi.
414     * <p>
415     * Expected: The command to trigger a wifi reset should be ignored and we should remain in ECM
416     * mode.
417     */
418    @Test
419    public void testRestartWifiStackDoesNotExitECMMode() throws Exception {
420        enableWifi();
421        assertEquals("DeviceActiveState", getCurrentState().getName());
422        when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true);
423
424        mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1);
425        mLooper.dispatchAll();
426        assertInEcm(true);
427
428        reset(mWifiStateMachine);
429        mWifiController.sendMessage(CMD_RESTART_WIFI);
430        mLooper.dispatchAll();
431        assertInEcm(true);
432        verifyZeroInteractions(mWifiStateMachine);
433    }
434
435    /**
436     * The command to trigger a WiFi reset should not trigger a reset when in AP mode.
437     * Enter AP mode, send command to restart wifi.
438     * <p>
439     * Expected: The command to trigger a wifi reset should be ignored and we should remain in AP
440     * mode.
441     */
442    @Test
443    public void testRestartWifiStackDoesNotExitAPMode() throws Exception {
444        mWifiController.obtainMessage(CMD_SET_AP, 1).sendToTarget();
445        mLooper.dispatchAll();
446        assertEquals("ApEnabledState", getCurrentState().getName());
447
448        reset(mWifiStateMachine);
449        mWifiController.sendMessage(CMD_RESTART_WIFI);
450        mLooper.dispatchAll();
451        verifyZeroInteractions(mWifiStateMachine);
452    }
453}
454