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