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 org.mockito.Mockito.any; 20import static org.mockito.Mockito.anyBoolean; 21import static org.mockito.Mockito.anyInt; 22import static org.mockito.Mockito.eq; 23import static org.mockito.Mockito.inOrder; 24import static org.mockito.Mockito.never; 25import static org.mockito.Mockito.verify; 26import static org.mockito.Mockito.when; 27 28import android.net.InterfaceConfiguration; 29import android.net.wifi.IApInterface; 30import android.net.wifi.WifiConfiguration; 31import android.net.wifi.WifiManager; 32import android.os.IBinder; 33import android.os.IBinder.DeathRecipient; 34import android.os.INetworkManagementService; 35import android.os.test.TestLooper; 36import android.test.suitebuilder.annotation.SmallTest; 37 38import com.android.server.net.BaseNetworkObserver; 39 40import org.junit.Before; 41import org.junit.Test; 42import org.mockito.ArgumentCaptor; 43import org.mockito.InOrder; 44import org.mockito.Mock; 45import org.mockito.MockitoAnnotations; 46 47import java.nio.charset.StandardCharsets; 48import java.util.ArrayList; 49import java.util.Arrays; 50import java.util.Locale; 51 52/** Unit tests for {@link SoftApManager}. */ 53@SmallTest 54public class SoftApManagerTest { 55 56 private static final String TAG = "SoftApManagerTest"; 57 58 private static final String DEFAULT_SSID = "DefaultTestSSID"; 59 private static final String TEST_SSID = "TestSSID"; 60 private static final String TEST_COUNTRY_CODE = "TestCountry"; 61 private static final Integer[] ALLOWED_2G_CHANNELS = {1, 2, 3, 4}; 62 private static final String TEST_INTERFACE_NAME = "testif0"; 63 64 private final ArrayList<Integer> mAllowed2GChannels = 65 new ArrayList<>(Arrays.asList(ALLOWED_2G_CHANNELS)); 66 67 private final WifiConfiguration mDefaultApConfig = createDefaultApConfig(); 68 69 TestLooper mLooper; 70 @Mock WifiNative mWifiNative; 71 @Mock SoftApManager.Listener mListener; 72 @Mock InterfaceConfiguration mInterfaceConfiguration; 73 @Mock IBinder mApInterfaceBinder; 74 @Mock IApInterface mApInterface; 75 @Mock INetworkManagementService mNmService; 76 @Mock WifiApConfigStore mWifiApConfigStore; 77 @Mock WifiMetrics mWifiMetrics; 78 final ArgumentCaptor<DeathRecipient> mDeathListenerCaptor = 79 ArgumentCaptor.forClass(DeathRecipient.class); 80 final ArgumentCaptor<BaseNetworkObserver> mNetworkObserverCaptor = 81 ArgumentCaptor.forClass(BaseNetworkObserver.class); 82 83 SoftApManager mSoftApManager; 84 85 /** Sets up test. */ 86 @Before 87 public void setUp() throws Exception { 88 MockitoAnnotations.initMocks(this); 89 mLooper = new TestLooper(); 90 91 when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder); 92 when(mApInterface.startHostapd()).thenReturn(true); 93 when(mApInterface.stopHostapd()).thenReturn(true); 94 when(mApInterface.writeHostapdConfig( 95 any(), anyBoolean(), anyInt(), anyInt(), any())).thenReturn(true); 96 when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME); 97 } 98 99 private WifiConfiguration createDefaultApConfig() { 100 WifiConfiguration defaultConfig = new WifiConfiguration(); 101 defaultConfig.SSID = DEFAULT_SSID; 102 return defaultConfig; 103 } 104 105 private SoftApManager createSoftApManager(WifiConfiguration config) throws Exception { 106 when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder); 107 when(mApInterface.startHostapd()).thenReturn(true); 108 when(mApInterface.stopHostapd()).thenReturn(true); 109 if (config == null) { 110 when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig); 111 } 112 SoftApManager newSoftApManager = new SoftApManager(mLooper.getLooper(), 113 mWifiNative, 114 TEST_COUNTRY_CODE, 115 mListener, 116 mApInterface, 117 mNmService, 118 mWifiApConfigStore, 119 config, 120 mWifiMetrics); 121 mLooper.dispatchAll(); 122 return newSoftApManager; 123 } 124 125 /** Verifies startSoftAp will use default config if AP configuration is not provided. */ 126 @Test 127 public void startSoftApWithoutConfig() throws Exception { 128 startSoftApAndVerifyEnabled(null); 129 } 130 131 /** Verifies startSoftAp will use provided config and start AP. */ 132 @Test 133 public void startSoftApWithConfig() throws Exception { 134 WifiConfiguration config = new WifiConfiguration(); 135 config.apBand = WifiConfiguration.AP_BAND_2GHZ; 136 config.SSID = TEST_SSID; 137 startSoftApAndVerifyEnabled(config); 138 } 139 140 141 /** 142 * Verifies startSoftAp will start with the hiddenSSID param set when it is set to true in the 143 * supplied config. 144 */ 145 @Test 146 public void startSoftApWithHiddenSsidTrueInConfig() throws Exception { 147 WifiConfiguration config = new WifiConfiguration(); 148 config.apBand = WifiConfiguration.AP_BAND_2GHZ; 149 config.SSID = TEST_SSID; 150 config.hiddenSSID = true; 151 startSoftApAndVerifyEnabled(config); 152 } 153 154 /** Tests softap startup if default config fails to load. **/ 155 @Test 156 public void startSoftApDefaultConfigFailedToLoad() throws Exception { 157 when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder); 158 when(mApInterface.startHostapd()).thenReturn(true); 159 when(mApInterface.stopHostapd()).thenReturn(true); 160 when(mWifiApConfigStore.getApConfiguration()).thenReturn(null); 161 SoftApManager newSoftApManager = new SoftApManager(mLooper.getLooper(), 162 mWifiNative, 163 TEST_COUNTRY_CODE, 164 mListener, 165 mApInterface, 166 mNmService, 167 mWifiApConfigStore, 168 null, 169 mWifiMetrics); 170 mLooper.dispatchAll(); 171 newSoftApManager.start(); 172 mLooper.dispatchAll(); 173 verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 174 WifiManager.SAP_START_FAILURE_GENERAL); 175 } 176 177 /** Tests the handling of stop command when soft AP is not started. */ 178 @Test 179 public void stopWhenNotStarted() throws Exception { 180 mSoftApManager = createSoftApManager(null); 181 mSoftApManager.stop(); 182 mLooper.dispatchAll(); 183 /* Verify no state changes. */ 184 verify(mListener, never()).onStateChanged(anyInt(), anyInt()); 185 } 186 187 /** Tests the handling of stop command when soft AP is started. */ 188 @Test 189 public void stopWhenStarted() throws Exception { 190 startSoftApAndVerifyEnabled(null); 191 192 InOrder order = inOrder(mListener); 193 194 mSoftApManager.stop(); 195 mLooper.dispatchAll(); 196 197 verify(mApInterface).stopHostapd(); 198 order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0); 199 order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0); 200 } 201 202 @Test 203 public void handlesWificondInterfaceDeath() throws Exception { 204 startSoftApAndVerifyEnabled(null); 205 206 mDeathListenerCaptor.getValue().binderDied(); 207 mLooper.dispatchAll(); 208 InOrder order = inOrder(mListener); 209 order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0); 210 order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 211 WifiManager.SAP_START_FAILURE_GENERAL); 212 } 213 214 /** Starts soft AP and verifies that it is enabled successfully. */ 215 protected void startSoftApAndVerifyEnabled(WifiConfiguration config) throws Exception { 216 String expectedSSID; 217 boolean expectedHiddenSsid; 218 InOrder order = inOrder(mListener, mApInterfaceBinder, mApInterface, mNmService); 219 220 when(mWifiNative.isHalStarted()).thenReturn(false); 221 when(mWifiNative.setCountryCodeHal(TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT))) 222 .thenReturn(true); 223 224 mSoftApManager = createSoftApManager(config); 225 if (config == null) { 226 when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig); 227 expectedSSID = mDefaultApConfig.SSID; 228 expectedHiddenSsid = mDefaultApConfig.hiddenSSID; 229 } else { 230 expectedSSID = config.SSID; 231 expectedHiddenSsid = config.hiddenSSID; 232 } 233 234 mSoftApManager.start(); 235 mLooper.dispatchAll(); 236 order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0); 237 order.verify(mApInterfaceBinder).linkToDeath(mDeathListenerCaptor.capture(), eq(0)); 238 order.verify(mNmService).registerObserver(mNetworkObserverCaptor.capture()); 239 order.verify(mApInterface).writeHostapdConfig( 240 eq(expectedSSID.getBytes(StandardCharsets.UTF_8)), eq(expectedHiddenSsid), 241 anyInt(), anyInt(), any()); 242 order.verify(mApInterface).startHostapd(); 243 mNetworkObserverCaptor.getValue().interfaceLinkStateChanged(TEST_INTERFACE_NAME, true); 244 mLooper.dispatchAll(); 245 order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0); 246 } 247 248 /** Verifies that soft AP was not disabled. */ 249 protected void verifySoftApNotDisabled() throws Exception { 250 verify(mListener, never()).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0); 251 verify(mListener, never()).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0); 252 } 253} 254