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