blob: a444e9334b2e72da5519dee49aab7d3fcf0f6d9b [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi;
import static android.net.wifi.WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_FAILURE_REASON;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import android.app.ActivityManager;
import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.app.test.TestAlarmManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
import android.net.LinkProperties;
import android.net.dhcp.DhcpClient;
import android.net.ip.IpManager;
import android.net.wifi.IApInterface;
import android.net.wifi.IClientInterface;
import android.net.wifi.IWificond;
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiSsid;
import android.net.wifi.WpsInfo;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.pps.HomeSp;
import android.net.wifi.p2p.IWifiP2pManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IInterface;
import android.os.INetworkManagementService;
import android.os.IPowerManager;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.test.TestLooper;
import android.provider.Settings;
import android.security.KeyStore;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IState;
import com.android.internal.util.StateMachine;
import com.android.server.wifi.hotspot2.NetworkDetail;
import com.android.server.wifi.hotspot2.PasspointManager;
import com.android.server.wifi.p2p.WifiP2pServiceImpl;
import com.android.server.wifi.util.WifiPermissionsUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
/**
* Unit tests for {@link com.android.server.wifi.WifiStateMachine}.
*/
@SmallTest
public class WifiStateMachineTest {
public static final String TAG = "WifiStateMachineTest";
private static final int MANAGED_PROFILE_UID = 1100000;
private static final int OTHER_USER_UID = 1200000;
private static final int LOG_REC_LIMIT_IN_VERBOSE_MODE =
(ActivityManager.isLowRamDeviceStatic()
? WifiStateMachine.NUM_LOG_RECS_VERBOSE_LOW_MEMORY
: WifiStateMachine.NUM_LOG_RECS_VERBOSE);
private static final int WPS_SUPPLICANT_NETWORK_ID = 5;
private static final int WPS_FRAMEWORK_NETWORK_ID = 10;
private static final String DEFAULT_TEST_SSID = "\"GoogleGuest\"";
private long mBinderToken;
private static <T> T mockWithInterfaces(Class<T> class1, Class<?>... interfaces) {
return mock(class1, withSettings().extraInterfaces(interfaces));
}
private static <T, I> IBinder mockService(Class<T> class1, Class<I> iface) {
T tImpl = mockWithInterfaces(class1, iface);
IBinder binder = mock(IBinder.class);
when(((IInterface) tImpl).asBinder()).thenReturn(binder);
when(binder.queryLocalInterface(iface.getCanonicalName()))
.thenReturn((IInterface) tImpl);
return binder;
}
private void enableDebugLogs() {
mWsm.enableVerboseLogging(1);
}
private FrameworkFacade getFrameworkFacade() throws Exception {
FrameworkFacade facade = mock(FrameworkFacade.class);
when(facade.getService(Context.NETWORKMANAGEMENT_SERVICE)).thenReturn(
mockWithInterfaces(IBinder.class, INetworkManagementService.class));
IBinder p2pBinder = mockService(WifiP2pServiceImpl.class, IWifiP2pManager.class);
when(facade.getService(Context.WIFI_P2P_SERVICE)).thenReturn(p2pBinder);
WifiP2pServiceImpl p2pm = (WifiP2pServiceImpl) p2pBinder.queryLocalInterface(
IWifiP2pManager.class.getCanonicalName());
final CountDownLatch untilDone = new CountDownLatch(1);
mP2pThread = new HandlerThread("WifiP2pMockThread") {
@Override
protected void onLooperPrepared() {
untilDone.countDown();
}
};
mP2pThread.start();
untilDone.await();
Handler handler = new Handler(mP2pThread.getLooper());
when(p2pm.getP2pStateMachineMessenger()).thenReturn(new Messenger(handler));
IBinder batteryStatsBinder = mockService(BatteryStats.class, IBatteryStats.class);
when(facade.getService(BatteryStats.SERVICE_NAME)).thenReturn(batteryStatsBinder);
when(facade.makeIpManager(any(Context.class), anyString(), any(IpManager.Callback.class)))
.then(new AnswerWithArguments() {
public IpManager answer(
Context context, String ifname, IpManager.Callback callback) {
mIpManagerCallback = callback;
return mIpManager;
}
});
return facade;
}
private Context getContext() throws Exception {
PackageManager pkgMgr = mock(PackageManager.class);
when(pkgMgr.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)).thenReturn(true);
Context context = mock(Context.class);
when(context.getPackageManager()).thenReturn(pkgMgr);
MockContentResolver mockContentResolver = new MockContentResolver();
mockContentResolver.addProvider(Settings.AUTHORITY,
new MockContentProvider(context) {
@Override
public Bundle call(String method, String arg, Bundle extras) {
return new Bundle();
}
});
when(context.getContentResolver()).thenReturn(mockContentResolver);
when(context.getSystemService(Context.POWER_SERVICE)).thenReturn(
new PowerManager(context, mock(IPowerManager.class), new Handler()));
mAlarmManager = new TestAlarmManager();
when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(
mAlarmManager.getAlarmManager());
when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
mock(ConnectivityManager.class));
return context;
}
private MockResources getMockResources() {
MockResources resources = new MockResources();
resources.setBoolean(R.bool.config_wifi_enable_wifi_firmware_debugging, false);
resources.setBoolean(
R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, false);
return resources;
}
private IState getCurrentState() throws
NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method = StateMachine.class.getDeclaredMethod("getCurrentState");
method.setAccessible(true);
return (IState) method.invoke(mWsm);
}
private static HandlerThread getWsmHandlerThread(WifiStateMachine wsm) throws
NoSuchFieldException, InvocationTargetException, IllegalAccessException {
Field field = StateMachine.class.getDeclaredField("mSmThread");
field.setAccessible(true);
return (HandlerThread) field.get(wsm);
}
private static void stopLooper(final Looper looper) throws Exception {
new Handler(looper).post(new Runnable() {
@Override
public void run() {
looper.quitSafely();
}
});
}
private void dumpState() {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(stream);
mWsm.dump(null, writer, null);
writer.flush();
Log.d(TAG, "WifiStateMachine state -" + stream.toString());
}
private static ScanDetail getGoogleGuestScanDetail(int rssi) {
ScanResult.InformationElement ie[] = new ScanResult.InformationElement[1];
ie[0] = ScanResults.generateSsidIe(sSSID);
NetworkDetail nd = new NetworkDetail(sBSSID, ie, new ArrayList<String>(), sFreq);
ScanDetail detail = new ScanDetail(nd, sWifiSsid, sBSSID, "", rssi, sFreq,
Long.MAX_VALUE, /* needed so that scan results aren't rejected because
there older than scan start */
ie, new ArrayList<String>());
return detail;
}
private ArrayList<ScanDetail> getMockScanResults() {
ScanResults sr = ScanResults.create(0, 2412, 2437, 2462, 5180, 5220, 5745, 5825);
ArrayList<ScanDetail> list = sr.getScanDetailArrayList();
int rssi = -65;
list.add(getGoogleGuestScanDetail(rssi));
return list;
}
private void injectDhcpSuccess(DhcpResults dhcpResults) {
mIpManagerCallback.onNewDhcpResults(dhcpResults);
mIpManagerCallback.onProvisioningSuccess(new LinkProperties());
}
private void injectDhcpFailure() {
mIpManagerCallback.onNewDhcpResults(null);
mIpManagerCallback.onProvisioningFailure(new LinkProperties());
}
static final String sSSID = "\"GoogleGuest\"";
static final WifiSsid sWifiSsid = WifiSsid.createFromAsciiEncoded(sSSID);
static final String sBSSID = "01:02:03:04:05:06";
static final int sFreq = 2437;
static final String WIFI_IFACE_NAME = "mockWlan";
WifiStateMachine mWsm;
HandlerThread mWsmThread;
HandlerThread mP2pThread;
HandlerThread mSyncThread;
AsyncChannel mWsmAsyncChannel;
TestAlarmManager mAlarmManager;
MockWifiMonitor mWifiMonitor;
TestLooper mLooper;
Context mContext;
MockResources mResources;
FrameworkFacade mFrameworkFacade;
IpManager.Callback mIpManagerCallback;
PhoneStateListener mPhoneStateListener;
final ArgumentCaptor<SoftApManager.Listener> mSoftApManagerListenerCaptor =
ArgumentCaptor.forClass(SoftApManager.Listener.class);
@Mock WifiScanner mWifiScanner;
@Mock SupplicantStateTracker mSupplicantStateTracker;
@Mock WifiMetrics mWifiMetrics;
@Mock UserManager mUserManager;
@Mock WifiApConfigStore mApConfigStore;
@Mock BackupManagerProxy mBackupManagerProxy;
@Mock WifiCountryCode mCountryCode;
@Mock WifiInjector mWifiInjector;
@Mock WifiLastResortWatchdog mWifiLastResortWatchdog;
@Mock PropertyService mPropertyService;
@Mock BuildProperties mBuildProperties;
@Mock IWificond mWificond;
@Mock IApInterface mApInterface;
@Mock IClientInterface mClientInterface;
@Mock IBinder mApInterfaceBinder;
@Mock IBinder mClientInterfaceBinder;
@Mock IBinder mPackageManagerBinder;
@Mock WifiConfigManager mWifiConfigManager;
@Mock WifiNative mWifiNative;
@Mock WifiConnectivityManager mWifiConnectivityManager;
@Mock SoftApManager mSoftApManager;
@Mock WifiStateTracker mWifiStateTracker;
@Mock PasspointManager mPasspointManager;
@Mock SelfRecovery mSelfRecovery;
@Mock WifiPermissionsUtil mWifiPermissionsUtil;
@Mock IpManager mIpManager;
@Mock TelephonyManager mTelephonyManager;
@Mock WrongPasswordNotifier mWrongPasswordNotifier;
@Mock Clock mClock;
public WifiStateMachineTest() throws Exception {
}
@Before
public void setUp() throws Exception {
Log.d(TAG, "Setting up ...");
// Ensure looper exists
mLooper = new TestLooper();
MockitoAnnotations.initMocks(this);
/** uncomment this to enable logs from WifiStateMachines */
// enableDebugLogs();
mWifiMonitor = new MockWifiMonitor();
when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
when(mWifiInjector.getClock()).thenReturn(new Clock());
when(mWifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog);
when(mWifiInjector.getPropertyService()).thenReturn(mPropertyService);
when(mWifiInjector.getBuildProperties()).thenReturn(mBuildProperties);
when(mWifiInjector.getKeyStore()).thenReturn(mock(KeyStore.class));
when(mWifiInjector.getWifiBackupRestore()).thenReturn(mock(WifiBackupRestore.class));
when(mWifiInjector.makeWifiDiagnostics(anyObject())).thenReturn(
mock(BaseWifiDiagnostics.class));
when(mWifiInjector.makeWificond()).thenReturn(mWificond);
when(mWifiInjector.getWifiConfigManager()).thenReturn(mWifiConfigManager);
when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
when(mWifiInjector.makeWifiConnectivityManager(any(WifiInfo.class), anyBoolean()))
.thenReturn(mWifiConnectivityManager);
when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class),
mSoftApManagerListenerCaptor.capture(), any(IApInterface.class),
any(WifiConfiguration.class)))
.thenReturn(mSoftApManager);
when(mWifiInjector.getPasspointManager()).thenReturn(mPasspointManager);
when(mWifiInjector.getWifiStateTracker()).thenReturn(mWifiStateTracker);
when(mWifiInjector.getWifiMonitor()).thenReturn(mWifiMonitor);
when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative);
when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery);
when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mWifiPermissionsUtil);
when(mWifiInjector.makeTelephonyManager()).thenReturn(mTelephonyManager);
when(mWifiInjector.getClock()).thenReturn(mClock);
when(mWifiNative.setupForClientMode())
.thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mClientInterface));
when(mWifiNative.setupForSoftApMode())
.thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
when(mWifiNative.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
when(mWifiNative.enableSupplicant()).thenReturn(true);
when(mWifiNative.disableSupplicant()).thenReturn(true);
when(mWifiNative.getFrameworkNetworkId(anyInt())).thenReturn(0);
mFrameworkFacade = getFrameworkFacade();
mContext = getContext();
mResources = getMockResources();
when(mContext.getResources()).thenReturn(mResources);
when(mFrameworkFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_FREQUENCY_BAND,
WifiManager.WIFI_FREQUENCY_BAND_AUTO)).thenReturn(
WifiManager.WIFI_FREQUENCY_BAND_AUTO);
when(mFrameworkFacade.makeApConfigStore(eq(mContext), eq(mBackupManagerProxy)))
.thenReturn(mApConfigStore);
when(mFrameworkFacade.makeSupplicantStateTracker(
any(Context.class), any(WifiConfigManager.class),
any(Handler.class))).thenReturn(mSupplicantStateTracker);
when(mUserManager.getProfileParent(11))
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "owner", 0));
when(mUserManager.getProfiles(UserHandle.USER_SYSTEM)).thenReturn(Arrays.asList(
new UserInfo(UserHandle.USER_SYSTEM, "owner", 0),
new UserInfo(11, "managed profile", 0)));
when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
when(mClientInterface.asBinder()).thenReturn(mClientInterfaceBinder);
doAnswer(new AnswerWithArguments() {
public void answer(PhoneStateListener phoneStateListener, int events)
throws Exception {
mPhoneStateListener = phoneStateListener;
}
}).when(mTelephonyManager).listen(any(PhoneStateListener.class), anyInt());
initializeWsm();
}
private void initializeWsm() throws Exception {
mWsm = new WifiStateMachine(mContext, mFrameworkFacade, mLooper.getLooper(),
mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative,
mWrongPasswordNotifier);
mWsmThread = getWsmHandlerThread(mWsm);
final AsyncChannel channel = new AsyncChannel();
Handler handler = new Handler(mLooper.getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
mWsmAsyncChannel = channel;
} else {
Log.d(TAG, "Failed to connect Command channel " + this);
}
break;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Log.d(TAG, "Command channel disconnected" + this);
break;
}
}
};
channel.connect(mContext, handler, mWsm.getMessenger());
mLooper.dispatchAll();
/* Now channel is supposed to be connected */
mBinderToken = Binder.clearCallingIdentity();
/* Send the BOOT_COMPLETED message to setup some WSM state. */
mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
mLooper.dispatchAll();
}
@After
public void cleanUp() throws Exception {
Binder.restoreCallingIdentity(mBinderToken);
if (mSyncThread != null) stopLooper(mSyncThread.getLooper());
if (mWsmThread != null) stopLooper(mWsmThread.getLooper());
if (mP2pThread != null) stopLooper(mP2pThread.getLooper());
mWsmThread = null;
mP2pThread = null;
mSyncThread = null;
mWsmAsyncChannel = null;
mWsm = null;
}
@Test
public void createNew() throws Exception {
assertEquals("InitialState", getCurrentState().getName());
mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
}
@Test
public void loadComponentsInStaMode() throws Exception {
startSupplicantAndDispatchMessages();
verify(mContext).sendStickyBroadcastAsUser(
(Intent) argThat(new WifiEnablingStateIntentMatcher()), eq(UserHandle.ALL));
assertEquals("DisconnectedState", getCurrentState().getName());
}
private void checkApStateChangedBroadcast(Intent intent, int expectedCurrentState,
int expectedPrevState, int expectedErrorCode, String expectedIfaceName,
int expectedMode) {
int currentState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
int prevState = intent.getIntExtra(EXTRA_PREVIOUS_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
int errorCode = intent.getIntExtra(EXTRA_WIFI_AP_FAILURE_REASON, HOTSPOT_NO_ERROR);
String ifaceName = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME);
int mode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
assertEquals(expectedCurrentState, currentState);
assertEquals(expectedPrevState, prevState);
assertEquals(expectedErrorCode, errorCode);
assertEquals(expectedIfaceName, ifaceName);
assertEquals(expectedMode, mode);
}
private void loadComponentsInApMode(int mode) throws Exception {
SoftApModeConfiguration config = new SoftApModeConfiguration(mode, new WifiConfiguration());
mWsm.setHostApRunning(config, true);
mLooper.dispatchAll();
assertEquals("SoftApState", getCurrentState().getName());
verify(mWifiNative).setupForSoftApMode();
verify(mSoftApManager).start();
// reset expectations for mContext due to previously sent AP broadcast
reset(mContext);
// get the SoftApManager.Listener and trigger some updates
SoftApManager.Listener listener = mSoftApManagerListenerCaptor.getValue();
listener.onStateChanged(WIFI_AP_STATE_ENABLING, 0);
listener.onStateChanged(WIFI_AP_STATE_ENABLED, 0);
listener.onStateChanged(WIFI_AP_STATE_DISABLING, 0);
// note, this will trigger a mode change when TestLooper is dispatched
listener.onStateChanged(WIFI_AP_STATE_DISABLED, 0);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(4))
.sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL));
List<Intent> capturedIntents = intentCaptor.getAllValues();
checkApStateChangedBroadcast(capturedIntents.get(0), WIFI_AP_STATE_ENABLING,
WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode);
checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_ENABLED,
WIFI_AP_STATE_ENABLING, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode);
checkApStateChangedBroadcast(capturedIntents.get(2), WIFI_AP_STATE_DISABLING,
WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode);
checkApStateChangedBroadcast(capturedIntents.get(3), WIFI_AP_STATE_DISABLED,
WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode);
}
@Test
public void loadComponentsInApModeForTethering() throws Exception {
loadComponentsInApMode(WifiManager.IFACE_IP_MODE_TETHERED);
}
@Test
public void loadComponentsInApModeForLOHS() throws Exception {
loadComponentsInApMode(WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
}
@Test
public void shouldRequireSupplicantStartupToLeaveInitialState() throws Exception {
when(mWifiNative.enableSupplicant()).thenReturn(false);
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
// we should not be sending a wifi enabling update
verify(mContext, never()).sendStickyBroadcastAsUser(
(Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
}
@Test
public void shouldRequireWificondToLeaveInitialState() throws Exception {
// We start out with valid default values, break them going backwards so that
// we test all the bailout cases.
// ClientInterface dies after creation.
doThrow(new RemoteException()).when(mClientInterfaceBinder).linkToDeath(any(), anyInt());
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
// Failed to even create the client interface.
when(mWificond.createClientInterface()).thenReturn(null);
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
// Failed to get wificond proxy.
when(mWifiInjector.makeWificond()).thenReturn(null);
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
}
@Test
public void loadComponentsFailure() throws Exception {
when(mWifiNative.enableSupplicant()).thenReturn(false);
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
}
@Test
public void checkInitialStateStickyWhenDisabledMode() throws Exception {
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
mWsm.setOperationalMode(WifiStateMachine.DISABLED_MODE);
mLooper.dispatchAll();
assertEquals(WifiStateMachine.DISABLED_MODE, mWsm.getOperationalModeForTest());
assertEquals("InitialState", getCurrentState().getName());
}
@Test
public void shouldStartSupplicantWhenConnectModeRequested() throws Exception {
// The first time we start out in InitialState, we sit around here.
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
// But if someone tells us to enter connect mode, we start up supplicant
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
assertEquals("SupplicantStartingState", getCurrentState().getName());
}
/**
* Test that mode changes accurately reflect the value for isWifiEnabled.
*/
@Test
public void checkIsWifiEnabledForModeChanges() throws Exception {
// Check initial state
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
startSupplicantAndDispatchMessages();
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest());
assertEquals("ScanModeState", getCurrentState().getName());
assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
verify(mContext, never()).sendStickyBroadcastAsUser(
(Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
// switch to connect mode and verify wifi is reported as enabled
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
assertEquals("DisconnectedState", getCurrentState().getName());
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
assertEquals(WifiManager.WIFI_STATE_ENABLED, mWsm.syncGetWifiState());
verify(mContext).sendStickyBroadcastAsUser(
(Intent) argThat(new WifiEnablingStateIntentMatcher()), eq(UserHandle.ALL));
// reset the expectations on mContext since we did get an expected broadcast, but we should
// not on the next transition
reset(mContext);
// now go back to scan mode with "wifi disabled" to verify the reported wifi state.
mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);
mLooper.dispatchAll();
assertEquals(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE,
mWsm.getOperationalModeForTest());
assertEquals("ScanModeState", getCurrentState().getName());
assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
verify(mContext, never()).sendStickyBroadcastAsUser(
(Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
// now go to AP mode
mWsm.setSupplicantRunning(false);
mWsm.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
mWsm.sendMessage(WifiMonitor.SUP_DISCONNECTION_EVENT);
SoftApModeConfiguration config = new SoftApModeConfiguration(
WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
mWsm.setHostApRunning(config, true);
mLooper.dispatchAll();
assertEquals("SoftApState", getCurrentState().getName());
assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
verify(mContext, never()).sendStickyBroadcastAsUser(
(Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
}
private class WifiEnablingStateIntentMatcher implements ArgumentMatcher<Intent> {
@Override
public boolean matches(Intent intent) {
if (WifiManager.WIFI_STATE_CHANGED_ACTION != intent.getAction()) {
// not the correct type
return false;
}
return WifiManager.WIFI_STATE_ENABLING
== intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_DISABLED);
}
}
/**
* Test that mode changes for WifiStateMachine in the InitialState are realized when supplicant
* is started.
*/
@Test
public void checkStartInCorrectStateAfterChangingInitialState() throws Exception {
// Check initial state
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
// Update the mode
mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
mLooper.dispatchAll();
assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest());
// Start supplicant so we move to the next state
startSupplicantAndDispatchMessages();
assertEquals("ScanModeState", getCurrentState().getName());
verify(mContext, never()).sendStickyBroadcastAsUser(
(Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
}
/**
* Verifies that configs can be removed when in client mode.
*/
@Test
public void canRemoveNetworkConfigInClientMode() throws Exception {
boolean result;
when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
initializeAndAddNetworkAndVerifySuccess();
mLooper.startAutoDispatch();
result = mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0);
mLooper.stopAutoDispatch();
assertTrue(result);
}
/**
* Verifies that configs can be removed when not in client mode.
*/
@Test
public void canRemoveNetworkConfigWhenWifiDisabled() {
boolean result;
when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
mLooper.startAutoDispatch();
result = mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0);
mLooper.stopAutoDispatch();
assertTrue(result);
verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
}
/**
* Verifies that configs can be forgotten when in client mode.
*/
@Test
public void canForgetNetworkConfigInClientMode() throws Exception {
when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
initializeAndAddNetworkAndVerifySuccess();
mWsm.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID);
mLooper.dispatchAll();
verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
}
/**
* Verifies that configs can be removed when not in client mode.
*/
@Test
public void canForgetNetworkConfigWhenWifiDisabled() throws Exception {
when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
mWsm.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID);
mLooper.dispatchAll();
verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
}
/**
* Helper method to move through SupplicantStarting and SupplicantStarted states.
*/
private void startSupplicantAndDispatchMessages() throws Exception {
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("SupplicantStartingState", getCurrentState().getName());
when(mWifiNative.setDeviceName(anyString())).thenReturn(true);
when(mWifiNative.setManufacturer(anyString())).thenReturn(true);
when(mWifiNative.setModelName(anyString())).thenReturn(true);
when(mWifiNative.setModelNumber(anyString())).thenReturn(true);
when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
when(mWifiNative.setConfigMethods(anyString())).thenReturn(true);
when(mWifiNative.setDeviceType(anyString())).thenReturn(true);
when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
when(mWifiNative.setScanningMacOui(any(byte[].class))).thenReturn(true);
mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
mLooper.dispatchAll();
verify(mWifiNative).setupForClientMode();
verify(mWifiLastResortWatchdog).clearAllFailureCounts();
}
private void addNetworkAndVerifySuccess(boolean isHidden) throws Exception {
WifiConfiguration config = new WifiConfiguration();
config.SSID = sSSID;
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.hiddenSSID = isHidden;
when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
.thenReturn(new NetworkUpdateResult(0));
when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(config));
when(mWifiConfigManager.getConfiguredNetwork(0)).thenReturn(config);
when(mWifiConfigManager.getConfiguredNetworkWithPassword(0)).thenReturn(config);
mLooper.startAutoDispatch();
mWsm.syncAddOrUpdateNetwork(mWsmAsyncChannel, config);
mLooper.stopAutoDispatch();
verify(mWifiConfigManager).addOrUpdateNetwork(eq(config), anyInt());
mLooper.startAutoDispatch();
List<WifiConfiguration> configs = mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel);
mLooper.stopAutoDispatch();
assertEquals(1, configs.size());
WifiConfiguration config2 = configs.get(0);
assertEquals("\"GoogleGuest\"", config2.SSID);
assertTrue(config2.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE));
}
private void initializeAndAddNetworkAndVerifySuccess() throws Exception {
initializeAndAddNetworkAndVerifySuccess(false);
}
private void initializeAndAddNetworkAndVerifySuccess(boolean isHidden) throws Exception {
loadComponentsInStaMode();
addNetworkAndVerifySuccess(isHidden);
}
/**
* Helper method to retrieve WifiConfiguration by SSID.
*
* Returns the associated WifiConfiguration if it is found, null otherwise.
*/
private WifiConfiguration getWifiConfigurationForNetwork(String ssid) {
mLooper.startAutoDispatch();
List<WifiConfiguration> configs = mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel);
mLooper.stopAutoDispatch();
for (WifiConfiguration checkConfig : configs) {
if (checkConfig.SSID.equals(ssid)) {
return checkConfig;
}
}
return null;
}
private void verifyScan(int band, int reportEvents, Set<String> hiddenNetworkSSIDSet) {
ArgumentCaptor<WifiScanner.ScanSettings> scanSettingsCaptor =
ArgumentCaptor.forClass(WifiScanner.ScanSettings.class);
ArgumentCaptor<WifiScanner.ScanListener> scanListenerCaptor =
ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
verify(mWifiScanner).startScan(scanSettingsCaptor.capture(), scanListenerCaptor.capture(),
eq(null));
WifiScanner.ScanSettings actualSettings = scanSettingsCaptor.getValue();
assertEquals("band", band, actualSettings.band);
assertEquals("reportEvents", reportEvents, actualSettings.reportEvents);
if (hiddenNetworkSSIDSet == null) {
hiddenNetworkSSIDSet = new HashSet<>();
}
Set<String> actualHiddenNetworkSSIDSet = new HashSet<>();
if (actualSettings.hiddenNetworks != null) {
for (int i = 0; i < actualSettings.hiddenNetworks.length; ++i) {
actualHiddenNetworkSSIDSet.add(actualSettings.hiddenNetworks[i].ssid);
}
}
assertEquals("hidden networks", hiddenNetworkSSIDSet, actualHiddenNetworkSSIDSet);
when(mWifiNative.getScanResults()).thenReturn(getMockScanResults());
mWsm.sendMessage(WifiMonitor.SCAN_RESULTS_EVENT);
mLooper.dispatchAll();
List<ScanResult> reportedResults = mWsm.syncGetScanResultsList();
assertEquals(8, reportedResults.size());
}
@Test
public void scan() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mWsm.startScan(-1, 0, null, null);
mLooper.dispatchAll();
verifyScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS,
WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
| WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT, null);
}
@Test
public void scanWithHiddenNetwork() throws Exception {
initializeAndAddNetworkAndVerifySuccess(true);
Set<String> hiddenNetworkSet = new HashSet<>();
hiddenNetworkSet.add(sSSID);
List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList = new ArrayList<>();
hiddenNetworkList.add(new WifiScanner.ScanSettings.HiddenNetwork(sSSID));
when(mWifiConfigManager.retrieveHiddenNetworkList()).thenReturn(hiddenNetworkList);
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mWsm.startScan(-1, 0, null, null);
mLooper.dispatchAll();
verifyScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS,
WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
| WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT,
hiddenNetworkSet);
}
@Test
public void connect() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
when(mWifiConfigManager.enableNetwork(eq(0), eq(true), anyInt())).thenReturn(true);
when(mWifiConfigManager.checkAndUpdateLastConnectUid(eq(0), anyInt())).thenReturn(true);
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
verify(mWifiNative).removeAllNetworks();
mLooper.startAutoDispatch();
assertTrue(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true));
mLooper.stopAutoDispatch();
verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
verify(mWifiConnectivityManager).setUserConnectChoice(eq(0));
mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
mLooper.dispatchAll();
mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
mLooper.dispatchAll();
assertEquals("ObtainingIpState", getCurrentState().getName());
DhcpResults dhcpResults = new DhcpResults();
dhcpResults.setGateway("1.2.3.4");
dhcpResults.setIpAddress("192.168.1.100", 0);
dhcpResults.addDns("8.8.8.8");
dhcpResults.setLeaseDuration(3600);
injectDhcpSuccess(dhcpResults);
mLooper.dispatchAll();
assertEquals("ConnectedState", getCurrentState().getName());
}
@Test
public void connectWithNoEnablePermission() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
when(mWifiConfigManager.enableNetwork(eq(0), eq(true), anyInt())).thenReturn(false);
when(mWifiConfigManager.checkAndUpdateLastConnectUid(eq(0), anyInt())).thenReturn(false);
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
verify(mWifiNative).removeAllNetworks();
mLooper.startAutoDispatch();
assertTrue(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true));
mLooper.stopAutoDispatch();
verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
verify(mWifiConnectivityManager, never()).setUserConnectChoice(eq(0));
mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
mLooper.dispatchAll();
mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
mLooper.dispatchAll();
assertEquals("ObtainingIpState", getCurrentState().getName());
DhcpResults dhcpResults = new DhcpResults();
dhcpResults.setGateway("1.2.3.4");
dhcpResults.setIpAddress("192.168.1.100", 0);
dhcpResults.addDns("8.8.8.8");
dhcpResults.setLeaseDuration(3600);
injectDhcpSuccess(dhcpResults);
mLooper.dispatchAll();
assertEquals("ConnectedState", getCurrentState().getName());
}
@Test
public void enableWithInvalidNetworkId() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
when(mWifiConfigManager.getConfiguredNetwork(eq(0))).thenReturn(null);
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
verify(mWifiNative).removeAllNetworks();
mLooper.startAutoDispatch();
assertFalse(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true));
mLooper.stopAutoDispatch();
verify(mWifiConfigManager, never()).enableNetwork(eq(0), eq(true), anyInt());
verify(mWifiConfigManager, never()).checkAndUpdateLastConnectUid(eq(0), anyInt());
}
/**
* If caller tries to connect to a network that is already connected, the connection request
* should succeed.
*
* Test: Create and connect to a network, then try to reconnect to the same network. Verify
* that connection request returns with CONNECT_NETWORK_SUCCEEDED.
*/
@Test
public void reconnectToConnectedNetwork() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
verify(mWifiNative).removeAllNetworks();
mLooper.startAutoDispatch();
mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
mLooper.stopAutoDispatch();
verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
mLooper.dispatchAll();
mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
mLooper.dispatchAll();
assertEquals("ObtainingIpState", getCurrentState().getName());
// try to reconnect
mLooper.startAutoDispatch();
Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.CONNECT_NETWORK, 0);
mLooper.stopAutoDispatch();
assertEquals(WifiManager.CONNECT_NETWORK_SUCCEEDED, reply.what);
}
@Test
public void testDhcpFailure() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
mLooper.startAutoDispatch();
mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
mLooper.stopAutoDispatch();
verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
mLooper.dispatchAll();
mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
mLooper.dispatchAll();
assertEquals("ObtainingIpState", getCurrentState().getName());
injectDhcpFailure();
mLooper.dispatchAll();
assertEquals("DisconnectingState", getCurrentState().getName());
}
/**
* Verify that the network selection status will be updated with DISABLED_AUTHENTICATION_FAILURE
* when wrong password authentication failure is detected and the network had been
* connected previously.
*/
@Test
public void testWrongPasswordWithPreviouslyConnected() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
mLooper.startAutoDispatch();
mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
mLooper.stopAutoDispatch();
verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
WifiConfiguration config = new WifiConfiguration();
config.getNetworkSelectionStatus().setHasEverConnected(true);
when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config);
mWsm.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0,
WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
mLooper.dispatchAll();
verify(mWrongPasswordNotifier, never()).onWrongPasswordError(anyString());
verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(),
eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE));
assertEquals("DisconnectedState", getCurrentState().getName());
}
/**
* Verify that the network selection status will be updated with DISABLED_BY_WRONG_PASSWORD
* when wrong password authentication failure is detected and the network has never been
* connected.
*/
@Test
public void testWrongPasswordWithNeverConnected() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
mLooper.startAutoDispatch();
mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
mLooper.stopAutoDispatch();
verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
WifiConfiguration config = new WifiConfiguration();
config.SSID = sSSID;
config.getNetworkSelectionStatus().setHasEverConnected(false);
when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config);
mWsm.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0,
WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
mLooper.dispatchAll();
verify(mWrongPasswordNotifier).onWrongPasswordError(eq(sSSID));
verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(),
eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD));
assertEquals("DisconnectedState", getCurrentState().getName());
}
/**
* Verify that the network selection status will be updated with DISABLED_BY_WRONG_PASSWORD
* when wrong password authentication failure is detected and the network is unknown.
*/
@Test
public void testWrongPasswordWithNullNetwork() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
mLooper.startAutoDispatch();
mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
mLooper.stopAutoDispatch();
verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null);
mWsm.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0,
WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
mLooper.dispatchAll();
verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(),
eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD));
assertEquals("DisconnectedState", getCurrentState().getName());
}
@Test
public void testBadNetworkEvent() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
mLooper.startAutoDispatch();
mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
mLooper.stopAutoDispatch();
verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 0, sBSSID);
mLooper.dispatchAll();
mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
mLooper.dispatchAll();
assertEquals("DisconnectedState", getCurrentState().getName());
}
@Test
public void smToString() throws Exception {
assertEquals("CMD_CHANNEL_HALF_CONNECTED", mWsm.smToString(
AsyncChannel.CMD_CHANNEL_HALF_CONNECTED));
assertEquals("CMD_PRE_DHCP_ACTION", mWsm.smToString(
DhcpClient.CMD_PRE_DHCP_ACTION));
assertEquals("CMD_IP_REACHABILITY_LOST", mWsm.smToString(
WifiStateMachine.CMD_IP_REACHABILITY_LOST));
}
@Test
public void disconnect() throws Exception {
connect();
mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, -1, 3, "01:02:03:04:05:06");
mLooper.dispatchAll();
mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED));
mLooper.dispatchAll();
assertEquals("DisconnectedState", getCurrentState().getName());
}
/**
* Successfully connecting to a network will set WifiConfiguration's value of HasEverConnected
* to true.
*
* Test: Successfully create and connect to a network. Check the config and verify
* WifiConfiguration.getHasEverConnected() is true.
*/
@Test
public void setHasEverConnectedTrueOnConnect() throws Exception {
connect();
verify(mWifiConfigManager, atLeastOnce()).updateNetworkAfterConnect(0);
}
/**
* Fail network connection attempt and verify HasEverConnected remains false.
*
* Test: Successfully create a network but fail when connecting. Check the config and verify
* WifiConfiguration.getHasEverConnected() is false.
*/
@Test
public void connectionFailureDoesNotSetHasEverConnectedTrue() throws Exception {
testDhcpFailure();
verify(mWifiConfigManager, never()).updateNetworkAfterConnect(0);
}
@Test
public void iconQueryTest() throws Exception {
// TODO(b/31065385): Passpoint config management.
}
@Test
public void verboseLogRecSizeIsGreaterThanNormalSize() {
assertTrue(LOG_REC_LIMIT_IN_VERBOSE_MODE > WifiStateMachine.NUM_LOG_RECS_NORMAL);
}
/**
* Verifies that, by default, we allow only the "normal" number of log records.
*/
@Test
public void normalLogRecSizeIsUsedByDefault() {
assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecMaxSize());
}
/**
* Verifies that, in verbose mode, we allow a larger number of log records.
*/
@Test
public void enablingVerboseLoggingUpdatesLogRecSize() {
mWsm.enableVerboseLogging(1);
assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mWsm.getLogRecMaxSize());
}
@Test
public void disablingVerboseLoggingClearsRecords() {
mWsm.sendMessage(WifiStateMachine.CMD_DISCONNECT);
mLooper.dispatchAll();
assertTrue(mWsm.getLogRecSize() >= 1);
mWsm.enableVerboseLogging(0);
assertEquals(0, mWsm.getLogRecSize());
}
@Test
public void disablingVerboseLoggingUpdatesLogRecSize() {
mWsm.enableVerboseLogging(1);
mWsm.enableVerboseLogging(0);
assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecMaxSize());
}
@Test
public void logRecsIncludeDisconnectCommand() {
// There's nothing special about the DISCONNECT command. It's just representative of
// "normal" commands.
mWsm.sendMessage(WifiStateMachine.CMD_DISCONNECT);
mLooper.dispatchAll();
assertEquals(1, mWsm.copyLogRecs()
.stream()
.filter(logRec -> logRec.getWhat() == WifiStateMachine.CMD_DISCONNECT)
.count());
}
@Test
public void logRecsExcludeRssiPollCommandByDefault() {
mWsm.sendMessage(WifiStateMachine.CMD_RSSI_POLL);
mLooper.dispatchAll();
assertEquals(0, mWsm.copyLogRecs()
.stream()
.filter(logRec -> logRec.getWhat() == WifiStateMachine.CMD_RSSI_POLL)
.count());
}
@Test
public void logRecsIncludeRssiPollCommandWhenVerboseLoggingIsEnabled() {
mWsm.enableVerboseLogging(1);
mWsm.sendMessage(WifiStateMachine.CMD_RSSI_POLL);
mLooper.dispatchAll();
assertEquals(1, mWsm.copyLogRecs()
.stream()
.filter(logRec -> logRec.getWhat() == WifiStateMachine.CMD_RSSI_POLL)
.count());
}
/** Verifies that enabling verbose logging sets the hal log property in eng builds. */
@Test
public void enablingVerboseLoggingSetsHalLogPropertyInEngBuilds() {
reset(mPropertyService); // Ignore calls made in setUp()
when(mBuildProperties.isEngBuild()).thenReturn(true);
when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
when(mBuildProperties.isUserBuild()).thenReturn(false);
mWsm.enableVerboseLogging(1);
verify(mPropertyService).set("log.tag.WifiHAL", "V");
}
/** Verifies that enabling verbose logging sets the hal log property in userdebug builds. */
@Test
public void enablingVerboseLoggingSetsHalLogPropertyInUserdebugBuilds() {
reset(mPropertyService); // Ignore calls made in setUp()
when(mBuildProperties.isUserdebugBuild()).thenReturn(true);
when(mBuildProperties.isEngBuild()).thenReturn(false);
when(mBuildProperties.isUserBuild()).thenReturn(false);
mWsm.enableVerboseLogging(1);
verify(mPropertyService).set("log.tag.WifiHAL", "V");
}
/** Verifies that enabling verbose logging does NOT set the hal log property in user builds. */
@Test
public void enablingVerboseLoggingDoeNotSetHalLogPropertyInUserBuilds() {
reset(mPropertyService); // Ignore calls made in setUp()
when(mBuildProperties.isUserBuild()).thenReturn(true);
when(mBuildProperties.isEngBuild()).thenReturn(false);
when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
mWsm.enableVerboseLogging(1);
verify(mPropertyService, never()).set(anyString(), anyString());
}
private int testGetSupportedFeaturesCase(int supportedFeatures, boolean rttConfigured) {
AsyncChannel channel = mock(AsyncChannel.class);
Message reply = Message.obtain();
reply.arg1 = supportedFeatures;
reset(mPropertyService); // Ignore calls made in setUp()
when(channel.sendMessageSynchronously(WifiStateMachine.CMD_GET_SUPPORTED_FEATURES))
.thenReturn(reply);
when(mPropertyService.getBoolean("config.disable_rtt", false))
.thenReturn(rttConfigured);
return mWsm.syncGetSupportedFeatures(channel);
}
/** Verifies that syncGetSupportedFeatures() masks out capabilities based on system flags. */
@Test
public void syncGetSupportedFeatures() {
final int featureAware = WifiManager.WIFI_FEATURE_AWARE;
final int featureInfra = WifiManager.WIFI_FEATURE_INFRA;
final int featureD2dRtt = WifiManager.WIFI_FEATURE_D2D_RTT;
final int featureD2apRtt = WifiManager.WIFI_FEATURE_D2AP_RTT;
assertEquals(0, testGetSupportedFeaturesCase(0, false));
assertEquals(0, testGetSupportedFeaturesCase(0, true));
assertEquals(featureAware | featureInfra,
testGetSupportedFeaturesCase(featureAware | featureInfra, false));
assertEquals(featureAware | featureInfra,
testGetSupportedFeaturesCase(featureAware | featureInfra, true));
assertEquals(featureInfra | featureD2dRtt,
testGetSupportedFeaturesCase(featureInfra | featureD2dRtt, false));
assertEquals(featureInfra,
testGetSupportedFeaturesCase(featureInfra | featureD2dRtt, true));
assertEquals(featureInfra | featureD2apRtt,
testGetSupportedFeaturesCase(featureInfra | featureD2apRtt, false));
assertEquals(featureInfra,
testGetSupportedFeaturesCase(featureInfra | featureD2apRtt, true));
assertEquals(featureInfra | featureD2dRtt | featureD2apRtt,
testGetSupportedFeaturesCase(featureInfra | featureD2dRtt | featureD2apRtt, false));
assertEquals(featureInfra,
testGetSupportedFeaturesCase(featureInfra | featureD2dRtt | featureD2apRtt, true));
}
/**
* Verify that syncAddOrUpdatePasspointConfig will redirect calls to {@link PasspointManager}
* and returning the result that's returned from {@link PasspointManager}.
*/
@Test
public void syncAddOrUpdatePasspointConfig() throws Exception {
PasspointConfiguration config = new PasspointConfiguration();
HomeSp homeSp = new HomeSp();
homeSp.setFqdn("test.com");
config.setHomeSp(homeSp);
when(mPasspointManager.addOrUpdateProvider(config, MANAGED_PROFILE_UID)).thenReturn(true);
mLooper.startAutoDispatch();
assertTrue(mWsm.syncAddOrUpdatePasspointConfig(
mWsmAsyncChannel, config, MANAGED_PROFILE_UID));
mLooper.stopAutoDispatch();
reset(mPasspointManager);
when(mPasspointManager.addOrUpdateProvider(config, MANAGED_PROFILE_UID)).thenReturn(false);
mLooper.startAutoDispatch();
assertFalse(mWsm.syncAddOrUpdatePasspointConfig(
mWsmAsyncChannel, config, MANAGED_PROFILE_UID));
mLooper.stopAutoDispatch();
}
/**
* Verify that syncAddOrUpdatePasspointConfig will redirect calls to {@link PasspointManager}
* and returning the result that's returned from {@link PasspointManager} when in client mode.
*/
@Test
public void syncAddOrUpdatePasspointConfigInClientMode() throws Exception {
loadComponentsInStaMode();
syncAddOrUpdatePasspointConfig();
}
/**
* Verify that syncRemovePasspointConfig will redirect calls to {@link PasspointManager}
* and returning the result that's returned from {@link PasspointManager}.
*/
@Test
public void syncRemovePasspointConfig() throws Exception {
String fqdn = "test.com";
when(mPasspointManager.removeProvider(fqdn)).thenReturn(true);
mLooper.startAutoDispatch();
assertTrue(mWsm.syncRemovePasspointConfig(mWsmAsyncChannel, fqdn));
mLooper.stopAutoDispatch();
reset(mPasspointManager);
when(mPasspointManager.removeProvider(fqdn)).thenReturn(false);
mLooper.startAutoDispatch();
assertFalse(mWsm.syncRemovePasspointConfig(mWsmAsyncChannel, fqdn));
mLooper.stopAutoDispatch();
}
/**
* Verify that syncRemovePasspointConfig will redirect calls to {@link PasspointManager}
* and returning the result that's returned from {@link PasspointManager} when in client mode.
*/
@Test
public void syncRemovePasspointConfigInClientMode() throws Exception {
loadComponentsInStaMode();
syncRemovePasspointConfig();
}
/**
* Verify that syncGetPasspointConfigs will redirect calls to {@link PasspointManager}
* and returning the result that's returned from {@link PasspointManager}.
*/
@Test
public void syncGetPasspointConfigs() throws Exception {
// Setup expected configs.
List<PasspointConfiguration> expectedConfigs = new ArrayList<>();
PasspointConfiguration config = new PasspointConfiguration();
HomeSp homeSp = new HomeSp();
homeSp.setFqdn("test.com");
config.setHomeSp(homeSp);
expectedConfigs.add(config);
when(mPasspointManager.getProviderConfigs()).thenReturn(expectedConfigs);
mLooper.startAutoDispatch();
assertEquals(expectedConfigs, mWsm.syncGetPasspointConfigs(mWsmAsyncChannel));
mLooper.stopAutoDispatch();
reset(mPasspointManager);
when(mPasspointManager.getProviderConfigs())
.thenReturn(new ArrayList<PasspointConfiguration>());
mLooper.startAutoDispatch();
assertTrue(mWsm.syncGetPasspointConfigs(mWsmAsyncChannel).isEmpty());
mLooper.stopAutoDispatch();
}
/**
* Verify that syncGetMatchingWifiConfig will redirect calls to {@link PasspointManager}
* with expected {@link WifiConfiguration} being returned when in client mode.
*
* @throws Exception
*/
@Test
public void syncGetMatchingWifiConfigInClientMode() throws Exception {
loadComponentsInStaMode();
when(mPasspointManager.getMatchingWifiConfig(any(ScanResult.class))).thenReturn(null);
mLooper.startAutoDispatch();
assertNull(mWsm.syncGetMatchingWifiConfig(new ScanResult(), mWsmAsyncChannel));
mLooper.stopAutoDispatch();
reset(mPasspointManager);
WifiConfiguration expectedConfig = new WifiConfiguration();
expectedConfig.SSID = "TestSSID";
when(mPasspointManager.getMatchingWifiConfig(any(ScanResult.class)))
.thenReturn(expectedConfig);
mLooper.startAutoDispatch();
WifiConfiguration actualConfig = mWsm.syncGetMatchingWifiConfig(new ScanResult(),
mWsmAsyncChannel);
mLooper.stopAutoDispatch();
assertEquals(expectedConfig.SSID, actualConfig.SSID);
}
/**
* Verify that syncGetMatchingWifiConfig will be a no-op and return {@code null} when not in
* client mode.
*
* @throws Exception
*/
@Test
public void syncGetMatchingWifiConfigInNonClientMode() throws Exception {
mLooper.startAutoDispatch();
assertNull(mWsm.syncGetMatchingWifiConfig(new ScanResult(), mWsmAsyncChannel));
mLooper.stopAutoDispatch();
verify(mPasspointManager, never()).getMatchingWifiConfig(any(ScanResult.class));
}
/**
* Verify successful Wps PBC network connection.
*/
@Test
public void wpsPbcConnectSuccess() throws Exception {
loadComponentsInStaMode();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
when(mWifiNative.startWpsPbc(eq(sBSSID))).thenReturn(true);
WpsInfo wpsInfo = new WpsInfo();
wpsInfo.setup = WpsInfo.PBC;
wpsInfo.BSSID = sBSSID;
mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo);
mLooper.dispatchAll();
verify(mWifiNative).startWpsPbc(eq(sBSSID));
assertEquals("WpsRunningState", getCurrentState().getName());
setupMocksForWpsNetworkMigration();
mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null);
mLooper.dispatchAll();
assertEquals("DisconnectedState", getCurrentState().getName());
verifyMocksForWpsNetworkMigration();
}
/**
* Verify failure in starting Wps PBC network connection.
*/
@Test
public void wpsPbcConnectFailure() throws Exception {
loadComponentsInStaMode();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
when(mWifiNative.startWpsPbc(eq(sBSSID))).thenReturn(false);
WpsInfo wpsInfo = new WpsInfo();
wpsInfo.setup = WpsInfo.PBC;
wpsInfo.BSSID = sBSSID;
mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo);
mLooper.dispatchAll();
verify(mWifiNative).startWpsPbc(eq(sBSSID));
assertFalse("WpsRunningState".equals(getCurrentState().getName()));
}
/**
* Verify successful Wps Pin Display network connection.
*/
@Test
public void wpsPinDisplayConnectSuccess() throws Exception {
loadComponentsInStaMode();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
when(mWifiNative.startWpsPinDisplay(eq(sBSSID))).thenReturn("34545434");
WpsInfo wpsInfo = new WpsInfo();
wpsInfo.setup = WpsInfo.DISPLAY;
wpsInfo.BSSID = sBSSID;
mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo);
mLooper.dispatchAll();
verify(mWifiNative).startWpsPinDisplay(eq(sBSSID));
assertEquals("WpsRunningState", getCurrentState().getName());
setupMocksForWpsNetworkMigration();
mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null);
mLooper.dispatchAll();
assertEquals("DisconnectedState", getCurrentState().getName());
verifyMocksForWpsNetworkMigration();
}
/**
* Verify failure in Wps Pin Display network connection.
*/
@Test
public void wpsPinDisplayConnectFailure() throws Exception {
loadComponentsInStaMode();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
when(mWifiNative.startWpsPinDisplay(eq(sBSSID))).thenReturn(null);
WpsInfo wpsInfo = new WpsInfo();
wpsInfo.setup = WpsInfo.DISPLAY;
wpsInfo.BSSID = sBSSID;
mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo);
mLooper.dispatchAll();
verify(mWifiNative).startWpsPinDisplay(eq(sBSSID));
assertFalse("WpsRunningState".equals(getCurrentState().getName()));
}
@Test
public void handleVendorHalDeath() throws Exception {
ArgumentCaptor<WifiNative.VendorHalDeathEventHandler> deathHandlerCapturer =
ArgumentCaptor.forClass(WifiNative.VendorHalDeathEventHandler.class);
when(mWifiNative.initializeVendorHal(deathHandlerCapturer.capture())).thenReturn(true);
// Trigger initialize to capture the death handler registration.
mLooper.startAutoDispatch();
assertTrue(mWsm.syncInitialize(mWsmAsyncChannel));
mLooper.stopAutoDispatch();
verify(mWifiNative).initializeVendorHal(any(WifiNative.VendorHalDeathEventHandler.class));
WifiNative.VendorHalDeathEventHandler deathHandler = deathHandlerCapturer.getValue();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
// Now trigger the death notification.
deathHandler.onDeath();
mLooper.dispatchAll();
verify(mWifiMetrics).incrementNumHalCrashes();
verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_HAL_CRASH));
}
@Test
public void handleWificondDeath() throws Exception {
ArgumentCaptor<StateMachineDeathRecipient> deathHandlerCapturer =
ArgumentCaptor.forClass(StateMachineDeathRecipient.class);
// Trigger initialize to capture the death handler registration.
loadComponentsInStaMode();
verify(mClientInterfaceBinder).linkToDeath(deathHandlerCapturer.capture(), anyInt());
StateMachineDeathRecipient deathHandler = deathHandlerCapturer.getValue();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
// Now trigger the death notification.
deathHandler.binderDied();
mLooper.dispatchAll();
verify(mWifiMetrics).incrementNumWificondCrashes();
verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_WIFICOND_CRASH));
}
private void setupMocksForWpsNetworkMigration() {
// Now trigger the network connection event for adding the WPS network.
doAnswer(new AnswerWithArguments() {
public boolean answer(Map<String, WifiConfiguration> configs,
SparseArray<Map<String, String>> networkExtras) throws Exception {
WifiConfiguration config = new WifiConfiguration();
config.networkId = WPS_SUPPLICANT_NETWORK_ID;
config.SSID = DEFAULT_TEST_SSID;
configs.put("dummy", config);
return true;
}
}).when(mWifiNative).migrateNetworksFromSupplicant(any(Map.class), any(SparseArray.class));
when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
.thenReturn(new NetworkUpdateResult(WPS_FRAMEWORK_NETWORK_ID));
when(mWifiConfigManager.enableNetwork(eq(WPS_FRAMEWORK_NETWORK_ID), anyBoolean(), anyInt()))
.thenReturn(true);
}
private void verifyMocksForWpsNetworkMigration() {
// Network Ids should be reset so that it is treated as addition.
ArgumentCaptor<WifiConfiguration> wifiConfigCaptor =
ArgumentCaptor.forClass(WifiConfiguration.class);
verify(mWifiConfigManager).addOrUpdateNetwork(wifiConfigCaptor.capture(), anyInt());
assertEquals(WifiConfiguration.INVALID_NETWORK_ID, wifiConfigCaptor.getValue().networkId);
assertEquals(DEFAULT_TEST_SSID, wifiConfigCaptor.getValue().SSID);
verify(mWifiConfigManager).enableNetwork(eq(WPS_FRAMEWORK_NETWORK_ID), anyBoolean(),
anyInt());
}
/**
* Verifies that WifiInfo is cleared upon exiting and entering WifiInfo, and that it is not
* updated by SUPPLICAN_STATE_CHANGE_EVENTs in ScanModeState.
* This protects WifiStateMachine from getting into a bad state where WifiInfo says wifi is
* already Connected or Connecting, (when it is in-fact Disconnected), so
* WifiConnectivityManager does not attempt any new Connections, freezing wifi.
*/
@Test
public void testWifiInfoCleanedUpEnteringExitingConnectModeState() throws Exception {
InOrder inOrder = inOrder(mWifiConnectivityManager);
Log.i(TAG, mWsm.getCurrentState().getName());
String initialBSSID = "aa:bb:cc:dd:ee:ff";
WifiInfo wifiInfo = mWsm.getWifiInfo();
wifiInfo.setBSSID(initialBSSID);
// Set WSM to CONNECT_MODE and verify state, and wifi enabled in ConnectivityManager
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
startSupplicantAndDispatchMessages();
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
assertEquals(WifiManager.WIFI_STATE_ENABLED, mWsm.syncGetWifiState());
inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(true));
assertNull(wifiInfo.getBSSID());
// Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is updated
mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
mLooper.dispatchAll();
assertEquals(sBSSID, wifiInfo.getBSSID());
assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState());
// Set WSM to SCAN_ONLY_MODE, verify state and wifi disabled in ConnectivityManager, and
// WifiInfo is reset() and state set to DISCONNECTED
mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
mLooper.dispatchAll();
assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest());
assertEquals("ScanModeState", getCurrentState().getName());
assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(false));
assertNull(wifiInfo.getBSSID());
assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState());
// Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is not updated
mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
mLooper.dispatchAll();
assertNull(wifiInfo.getBSSID());
assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState());
// Set the bssid to something, so we can verify it is cleared (just in case)
wifiInfo.setBSSID(initialBSSID);
// Set WSM to CONNECT_MODE and verify state, and wifi enabled in ConnectivityManager,
// and WifiInfo has been reset
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
assertEquals(WifiManager.WIFI_STATE_ENABLED, mWsm.syncGetWifiState());
inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(true));
assertEquals("DisconnectedState", getCurrentState().getName());
assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState());
assertNull(wifiInfo.getBSSID());
}
/**
* Test that the process uid has full wifiInfo access
*/
@Test
public void testConnectedIdsAreVisibleFromOwnUid() throws Exception {
assertEquals(Process.myUid(), Binder.getCallingUid());
WifiInfo wifiInfo = mWsm.getWifiInfo();
connect();
WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo();
assertEquals(wifiInfo, connectionInfo);
}
/**
* Test that connected SSID and BSSID are not exposed to an app that does not have the
* appropriate permissions.
*/
@Test
public void testConnectedIdsAreHiddenFromRandomApp() throws Exception {
int actualUid = Binder.getCallingUid();
int fakeUid = Process.myUid() + 100000;
assertNotEquals(actualUid, fakeUid);
BinderUtil.setUid(fakeUid);
try {
WifiInfo wifiInfo = mWsm.getWifiInfo();
// Get into a connected state, with known BSSID and SSID
connect();
assertEquals(sBSSID, wifiInfo.getBSSID());
assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(fakeUid), anyInt()))
.thenReturn(false);
WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo();
assertNotEquals(wifiInfo, connectionInfo);
assertEquals(WifiSsid.NONE, connectionInfo.getSSID());
assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID());
} finally {
BinderUtil.setUid(actualUid);
}
}
/**
* Test that connected SSID and BSSID are not exposed to an app that does not have the
* appropriate permissions, when canAccessScanResults raises a SecurityException.
*/
@Test
public void testConnectedIdsAreHiddenOnSecurityException() throws Exception {
int actualUid = Binder.getCallingUid();
int fakeUid = Process.myUid() + 100000;
assertNotEquals(actualUid, fakeUid);
BinderUtil.setUid(fakeUid);
try {
WifiInfo wifiInfo = mWsm.getWifiInfo();
// Get into a connected state, with known BSSID and SSID
connect();
assertEquals(sBSSID, wifiInfo.getBSSID());
assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(fakeUid), anyInt()))
.thenThrow(new SecurityException());
WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo();
assertNotEquals(wifiInfo, connectionInfo);
assertEquals(WifiSsid.NONE, connectionInfo.getSSID());
assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID());
} finally {
BinderUtil.setUid(actualUid);
}
}
/**
* Test that connected SSID and BSSID are exposed to an app that does have the
* appropriate permissions.
*/
@Test
public void testConnectedIdsAreVisibleFromPermittedApp() throws Exception {
int actualUid = Binder.getCallingUid();
int fakeUid = Process.myUid() + 100000;
BinderUtil.setUid(fakeUid);
try {
WifiInfo wifiInfo = mWsm.getWifiInfo();
// Get into a connected state, with known BSSID and SSID
connect();
assertEquals(sBSSID, wifiInfo.getBSSID());
assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(fakeUid), anyInt()))
.thenReturn(true);
WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo();
assertNotEquals(wifiInfo, connectionInfo);
assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID());
assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID());
// Access to our MAC address uses a different permission, make sure it is not granted
assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getMacAddress());
} finally {
BinderUtil.setUid(actualUid);
}
}
/**
* Adds the network without putting WifiStateMachine into ConnectMode.
*/
@Test
public void addNetworkInInitialState() throws Exception {
// We should not be in initial state now.
assertTrue("InitialState".equals(getCurrentState().getName()));
addNetworkAndVerifySuccess(false);
verify(mWifiConnectivityManager, never()).setUserConnectChoice(eq(0));
}
/**
* Test START_WPS with a null wpsInfo object fails gracefully (No NPE)
*/
@Test
public void testStartWps_nullWpsInfo() throws Exception {
loadComponentsInStaMode();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
assertEquals("DisconnectedState", getCurrentState().getName());
mLooper.startAutoDispatch();
Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.START_WPS, 0, 0,
null);
mLooper.stopAutoDispatch();
assertEquals(WifiManager.WPS_FAILED, reply.what);
}
/**
* Test that DISABLE_NETWORK returns failure to public API when WifiConfigManager returns
* failure.
*/
@Test
public void testSyncDisableNetwork_failure() throws Exception {
loadComponentsInStaMode();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
assertEquals("DisconnectedState", getCurrentState().getName());
when(mWifiConfigManager.disableNetwork(anyInt(), anyInt())).thenReturn(false);
mLooper.startAutoDispatch();
boolean succeeded = mWsm.syncDisableNetwork(mWsmAsyncChannel, 0);
mLooper.stopAutoDispatch();
assertFalse(succeeded);
}
/**
* Test that failure to start HAL in AP mode increments the corresponding metrics.
*/
@Test
public void testSetupForSoftApModeHalFailureIncrementsMetrics() throws Exception {
when(mWifiNative.setupForSoftApMode())
.thenReturn(Pair.create(WifiNative.SETUP_FAILURE_HAL, null));
SoftApModeConfiguration config = new SoftApModeConfiguration(
WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
mWsm.setHostApRunning(config, true);
mLooper.dispatchAll();
verify(mWifiNative).setupForSoftApMode();
verify(mWifiMetrics).incrementNumWifiOnFailureDueToHal();
}
/**
* Test that failure to start HAL in AP mode increments the corresponding metrics.
*/
@Test
public void testSetupForSoftApModeWificondFailureIncrementsMetrics() throws Exception {
when(mWifiNative.setupForSoftApMode())
.thenReturn(Pair.create(WifiNative.SETUP_FAILURE_WIFICOND, null));
SoftApModeConfiguration config = new SoftApModeConfiguration(
WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
mWsm.setHostApRunning(config, true);
mLooper.dispatchAll();
verify(mWifiNative).setupForSoftApMode();
verify(mWifiMetrics).incrementNumWifiOnFailureDueToWificond();
}
/**
* Test that failure to start HAL in client mode increments the corresponding metrics.
*/
@Test
public void testSetupForClientModeHalFailureIncrementsMetrics() throws Exception {
when(mWifiNative.setupForClientMode())
.thenReturn(Pair.create(WifiNative.SETUP_FAILURE_HAL, null));
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
mLooper.dispatchAll();
verify(mWifiNative).setupForClientMode();
verify(mWifiMetrics).incrementNumWifiOnFailureDueToHal();
}
/**
* Test that failure to start HAL in client mode increments the corresponding metrics.
*/
@Test
public void testSetupForClientModeWificondFailureIncrementsMetrics() throws Exception {
when(mWifiNative.setupForClientMode())
.thenReturn(Pair.create(WifiNative.SETUP_FAILURE_WIFICOND, null));
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
mLooper.dispatchAll();
verify(mWifiNative).setupForClientMode();
verify(mWifiMetrics).incrementNumWifiOnFailureDueToWificond();
}
/**
* Test that we don't register the telephony call state listener on devices which do not support
* setting/resetting Tx power limit.
*/
@Test
public void testVoiceCallSar_disabledTxPowerLimit_WifiOn() throws Exception {
loadComponentsInStaMode();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
assertEquals("DisconnectedState", getCurrentState().getName());
assertNull(mPhoneStateListener);
}
/**
* Test that we do register the telephony call state listener on devices which do support
* setting/resetting Tx power limit.
*/
@Test
public void testVoiceCallSar_enabledTxPowerLimit_WifiOn() throws Exception {
mResources.setBoolean(
R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
initializeWsm();
loadComponentsInStaMode();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
assertEquals("DisconnectedState", getCurrentState().getName());
assertNotNull(mPhoneStateListener);
}
/**
* Test that we do register the telephony call state listener on devices which do support
* setting/resetting Tx power limit and set the tx power level if we're in state
* {@link TelephonyManager#CALL_STATE_OFFHOOK}.
*/
@Test
public void testVoiceCallSar_enabledTxPowerLimitCallStateOffHook_WhenWifiTurnedOn()
throws Exception {
int powerLevelInDbm = -45;
mResources.setInteger(
R.integer.config_wifi_framework_voice_call_sar_tx_power_limit_in_dbm,
powerLevelInDbm);
mResources.setBoolean(
R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
initializeWsm();
when(mTelephonyManager.isOffhook()).thenReturn(true);
loadComponentsInStaMode();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
assertEquals("DisconnectedState", getCurrentState().getName());
assertNotNull(mPhoneStateListener);
verify(mWifiNative).setTxPowerLimit(eq(powerLevelInDbm));
}
/**
* Test that we do register the telephony call state listener on devices which do support
* setting/resetting Tx power limit and set the tx power level if we're in state
* {@link TelephonyManager#CALL_STATE_IDLE}.
*/
@Test
public void testVoiceCallSar_enabledTxPowerLimitCallStateIdle_WhenWifiTurnedOn()
throws Exception {
mResources.setBoolean(
R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
initializeWsm();
when(mTelephonyManager.isIdle()).thenReturn(true);
loadComponentsInStaMode();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
assertEquals("DisconnectedState", getCurrentState().getName());
assertNotNull(mPhoneStateListener);
verify(mWifiNative).resetTxPowerLimit();
}
/**
* Test that we invoke the corresponding WifiNative method when
* {@link PhoneStateListener#onCallStateChanged(int, String)} is invoked with state
* {@link TelephonyManager#CALL_STATE_OFFHOOK}.
*/
@Test
public void testVoiceCallSar_enabledTxPowerLimitCallStateOffHook_WhenWifiOn() throws Exception {
int powerLevelInDbm = -45;
mResources.setInteger(
R.integer.config_wifi_framework_voice_call_sar_tx_power_limit_in_dbm,
powerLevelInDbm);
testVoiceCallSar_enabledTxPowerLimit_WifiOn();
mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, "");
mLooper.dispatchAll();
verify(mWifiNative).setTxPowerLimit(eq(powerLevelInDbm));
}
/**
* Test that we invoke the corresponding WifiNative method when
* {@link PhoneStateListener#onCallStateChanged(int, String)} is invoked with state
* {@link TelephonyManager#CALL_STATE_IDLE}.
*/
@Test
public void testVoiceCallSar_enabledTxPowerLimitCallStateIdle_WhenWifiOn() throws Exception {
testVoiceCallSar_enabledTxPowerLimit_WifiOn();
mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE, "");
mLooper.dispatchAll();
verify(mWifiNative).resetTxPowerLimit();
}
/**
* Test that we don't invoke the corresponding WifiNative method when
* {@link PhoneStateListener#onCallStateChanged(int, String)} is invoked with state
* {@link TelephonyManager#CALL_STATE_IDLE} or {@link TelephonyManager#CALL_STATE_OFFHOOK} when
* wifi is off (state machine is not in SupplicantStarted state).
*/
@Test
public void testVoiceCallSar_enabledTxPowerLimitCallState_WhenWifiOff() throws Exception {
mResources.setBoolean(
R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
initializeWsm();
mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, "");
mLooper.dispatchAll();
verify(mWifiNative, never()).setTxPowerLimit(anyInt());
mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE, "");
mLooper.dispatchAll();
verify(mWifiNative, never()).resetTxPowerLimit();
}
/**
* Verifies that a network disconnection event will result in WifiStateMachine invoking
* {@link WifiConfigManager#removeAllEphemeralOrPasspointConfiguredNetworks()} to remove
* any ephemeral or passpoint networks from it's internal database.
*/
@Test
public void testDisconnectionRemovesEphemeralAndPasspointNetworks() throws Exception {
disconnect();
verify(mWifiConfigManager).removeAllEphemeralOrPasspointConfiguredNetworks();
}
/**
* Verifies that WifiStateMachine sets and unsets appropriate 'RecentFailureReason' values
* on a WifiConfiguration when it fails association, authentication, or successfully connects
*/
@Test
public void testExtraFailureReason_ApIsBusy() throws Exception {
// Setup CONNECT_MODE & a WifiConfiguration
initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
// Trigger a connection to this (CMD_START_CONNECT will actually fail, but it sets up
// targetNetworkId state)
mWsm.sendMessage(WifiStateMachine.CMD_START_CONNECT, 0, 0, sBSSID);
mLooper.dispatchAll();
// Simulate an ASSOCIATION_REJECTION_EVENT, due to the AP being busy
mWsm.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 0,
ISupplicantStaIfaceCallback.StatusCode.AP_UNABLE_TO_HANDLE_NEW_STA, sBSSID);
mLooper.dispatchAll();
verify(mWifiConfigManager).setRecentFailureAssociationStatus(eq(0),
eq(WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA));
assertEquals("DisconnectedState", getCurrentState().getName());
// Simulate an AUTHENTICATION_FAILURE_EVENT, which should clear the ExtraFailureReason
reset(mWifiConfigManager);
mWsm.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0, 0, null);
mLooper.dispatchAll();
verify(mWifiConfigManager).clearRecentFailureReason(eq(0));
verify(mWifiConfigManager, never()).setRecentFailureAssociationStatus(anyInt(), anyInt());
// Simulate a NETWORK_CONNECTION_EVENT which should clear the ExtraFailureReason
reset(mWifiConfigManager);
mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null);
mLooper.dispatchAll();
verify(mWifiConfigManager).clearRecentFailureReason(eq(0));
verify(mWifiConfigManager, never()).setRecentFailureAssociationStatus(anyInt(), anyInt());
}
/**
* Test that the helper method
* {@link WifiStateMachine#shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration)}
* returns true when we connect to the last selected network before expiration of
* {@link WifiStateMachine#LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS}.
*/
@Test
public void testShouldEvaluateWhetherToSendExplicitlySelected_SameNetworkNotExpired() {
long lastSelectedTimestamp = 45666743454L;
int lastSelectedNetworkId = 5;
when(mClock.getElapsedSinceBootMillis()).thenReturn(
lastSelectedTimestamp
+ WifiStateMachine.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1);
when(mWifiConfigManager.getLastSelectedTimeStamp()).thenReturn(lastSelectedTimestamp);
when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(lastSelectedNetworkId);
WifiConfiguration currentConfig = new WifiConfiguration();
currentConfig.networkId = lastSelectedNetworkId;
assertTrue(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig));
}
/**
* Test that the helper method
* {@link WifiStateMachine#shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration)}
* returns false when we connect to the last selected network after expiration of
* {@link WifiStateMachine#LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS}.
*/
@Test
public void testShouldEvaluateWhetherToSendExplicitlySelected_SameNetworkExpired() {
long lastSelectedTimestamp = 45666743454L;
int lastSelectedNetworkId = 5;
when(mClock.getElapsedSinceBootMillis()).thenReturn(
lastSelectedTimestamp
+ WifiStateMachine.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS + 1);
when(mWifiConfigManager.getLastSelectedTimeStamp()).thenReturn(lastSelectedTimestamp);
when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(lastSelectedNetworkId);
WifiConfiguration currentConfig = new WifiConfiguration();
currentConfig.networkId = lastSelectedNetworkId;
assertFalse(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig));
}
/**
* Test that the helper method
* {@link WifiStateMachine#shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration)}
* returns false when we connect to a different network to the last selected network.
*/
@Test
public void testShouldEvaluateWhetherToSendExplicitlySelected_DifferentNetwork() {
long lastSelectedTimestamp = 45666743454L;
int lastSelectedNetworkId = 5;
when(mClock.getElapsedSinceBootMillis()).thenReturn(
lastSelectedTimestamp
+ WifiStateMachine.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1);
when(mWifiConfigManager.getLastSelectedTimeStamp()).thenReturn(lastSelectedTimestamp);
when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(lastSelectedNetworkId);
WifiConfiguration currentConfig = new WifiConfiguration();
currentConfig.networkId = lastSelectedNetworkId - 1;
assertFalse(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig));
}
}