Use wificond to setup client interfaces
Bug: 30041062
Test: Can connect to open networks before and after restarting wificond.
unittests pass
Change-Id: I4f76cef3b4960a6c0601519019e1d3a4fa8acc4d
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index e4039dd..0a3c0ab 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -56,6 +56,7 @@
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.PasspointManagementObjectDefinition;
import android.net.wifi.RssiPacketCountInfo;
@@ -222,6 +223,8 @@
private String mLastBssid;
private int mLastNetworkId; // The network Id we successfully joined
private boolean mIsLinkDebouncing = false;
+ private final StateMachineDeathRecipient mDeathRecipient =
+ new StateMachineDeathRecipient(this, CMD_CLIENT_INTERFACE_BINDER_DEATH);
@Override
public void onRssiThresholdBreached(byte curRssi) {
@@ -772,6 +775,11 @@
/* Enable/disable Neighbor Discovery offload functionality. */
static final int CMD_CONFIG_ND_OFFLOAD = BASE + 204;
+ /**
+ * Signals that IClientInterface instance underpinning our state is dead.
+ */
+ private static final int CMD_CLIENT_INTERFACE_BINDER_DEATH = BASE + 250;
+
// For message logging.
private static final Class[] sMessageClasses = {
AsyncChannel.class, WifiStateMachine.class, DhcpClient.class };
@@ -3579,7 +3587,25 @@
mWifiNative.disconnect();
}
- /* Driver/firmware setup for soft AP. */
+ private IClientInterface setupDriverForClientMode() {
+ if (mWificond == null) {
+ Log.e(TAG, "Failed to get reference to wificond");
+ return null;
+ }
+
+ IClientInterface clientInterface = null;
+ try {
+ clientInterface = mWificond.createClientInterface();
+ } catch (RemoteException e1) { }
+
+ if (clientInterface == null) {
+ Log.e(TAG, "Could not get IClientInterface instance from wificond");
+ return null;
+ }
+
+ return clientInterface;
+ }
+
private IApInterface setupDriverForSoftAp() {
if (mWificond == null) {
Log.e(TAG, "Failed to get reference to wificond");
@@ -3597,8 +3623,8 @@
}
if (!mWifiNative.startHal()) {
- /* starting HAL is optional */
- Log.e(TAG, "Failed to start HAL");
+ // starting HAL is optional
+ Log.e(TAG, "Failed to start HAL for AP mode");
}
return apInterface;
}
@@ -3999,6 +4025,12 @@
mWifiNative.stopFilteringMulticastV4Packets();
}
break;
+ case CMD_CLIENT_INTERFACE_BINDER_DEATH:
+ // We have lost contact with a client interface, which means that we cannot
+ // trust that the driver is up or that the interface is ready. We are fit
+ // for no WiFi related work.
+ transitionTo(mInitialState);
+ break;
default:
loge("Error! unhandled message" + message);
break;
@@ -4008,8 +4040,15 @@
}
class InitialState extends State {
- @Override
- public void enter() {
+
+ private void cleanup() {
+ /* Stop a running supplicant after a runtime restart
+ * Avoids issues with drivers that do not handle interface down
+ * on a running supplicant properly.
+ */
+ mWifiMonitor.killSupplicant(mP2pSupported);
+
+ mDeathRecipient.unlinkToDeath();
if (mWificond != null) {
try {
mWificond.tearDownInterfaces();
@@ -4020,7 +4059,11 @@
mWificond = null;
}
mWifiNative.stopHal();
- mWifiNative.unloadDriver();
+ }
+
+ @Override
+ public void enter() {
+ cleanup();
if (mWifiP2pChannel == null && mWifiP2pServiceImpl != null) {
mWifiP2pChannel = new AsyncChannel();
mWifiP2pChannel.connect(mContext, getHandler(),
@@ -4037,63 +4080,55 @@
logStateAndMessage(message, this);
switch (message.what) {
case CMD_START_SUPPLICANT:
- if (mWifiNative.loadDriver()) {
- try {
- mNwService.wifiFirmwareReload(mInterfaceName, "STA");
- } catch (Exception e) {
- loge("Failed to reload STA firmware " + e);
- setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
- return HANDLED;
- }
-
- try {
- // A runtime crash can leave the interface up and
- // IP addresses configured, and this affects
- // connectivity when supplicant starts up.
- // Ensure interface is down and we have no IP
- // addresses before a supplicant start.
- mNwService.setInterfaceDown(mInterfaceName);
- mNwService.clearInterfaceAddresses(mInterfaceName);
-
- // Set privacy extensions
- mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
-
- // IPv6 is enabled only as long as access point is connected since:
- // - IPv6 addresses and routes stick around after disconnection
- // - kernel is unaware when connected and fails to start IPv6 negotiation
- // - kernel can start autoconfiguration when 802.1x is not complete
- mNwService.disableIpv6(mInterfaceName);
- } catch (RemoteException re) {
- loge("Unable to change interface settings: " + re);
- } catch (IllegalStateException ie) {
- loge("Unable to change interface settings: " + ie);
- }
-
- /* Stop a running supplicant after a runtime restart
- * Avoids issues with drivers that do not handle interface down
- * on a running supplicant properly.
- */
- mWifiMonitor.killSupplicant(mP2pSupported);
-
- if (mWifiNative.startHal() == false) {
- /* starting HAL is optional */
- loge("Failed to start HAL");
- }
-
- if (mWifiNative.startSupplicant(mP2pSupported)) {
- setSupplicantLogLevel();
- setWifiState(WIFI_STATE_ENABLING);
- if (mVerboseLoggingEnabled) log("Supplicant start successful");
- mWifiMonitor.startMonitoring(mInterfaceName);
- transitionTo(mSupplicantStartingState);
- } else {
- loge("Failed to start supplicant!");
- setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
- }
- } else {
- loge("Failed to load driver");
+ // Refresh our reference to wificond. This allows us to tolerate restarts.
+ mWificond = mWifiInjector.makeWificond();
+ IClientInterface clientInterface = setupDriverForClientMode();
+ if (clientInterface == null ||
+ !mDeathRecipient.linkToDeath(clientInterface.asBinder())) {
setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
+ cleanup();
+ break;
}
+
+ try {
+ // A runtime crash can leave the interface up and
+ // IP addresses configured, and this affects
+ // connectivity when supplicant starts up.
+ // Ensure interface is down and we have no IP
+ // addresses before a supplicant start.
+ mNwService.setInterfaceDown(mInterfaceName);
+ mNwService.clearInterfaceAddresses(mInterfaceName);
+
+ // Set privacy extensions
+ mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
+
+ // IPv6 is enabled only as long as access point is connected since:
+ // - IPv6 addresses and routes stick around after disconnection
+ // - kernel is unaware when connected and fails to start IPv6 negotiation
+ // - kernel can start autoconfiguration when 802.1x is not complete
+ mNwService.disableIpv6(mInterfaceName);
+ } catch (RemoteException re) {
+ loge("Unable to change interface settings: " + re);
+ } catch (IllegalStateException ie) {
+ loge("Unable to change interface settings: " + ie);
+ }
+
+ if (!mWifiNative.startHal()) {
+ // starting HAL is optional
+ Log.e(TAG, "Failed to start HAL for client mode");
+ }
+
+ if (!mWifiNative.startSupplicant(mP2pSupported)) {
+ loge("Failed to start supplicant!");
+ setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
+ cleanup();
+ break;
+ }
+ setSupplicantLogLevel();
+ setWifiState(WIFI_STATE_ENABLING);
+ if (mVerboseLoggingEnabled) log("Supplicant start successful");
+ mWifiMonitor.startMonitoring(mInterfaceName);
+ transitionTo(mSupplicantStartingState);
break;
case CMD_START_AP:
// Refresh our reference to wificond. This allows us to tolerate restarts.
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index 0c258e9..dbd8e32 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -34,6 +34,8 @@
import android.net.LinkProperties;
import android.net.dhcp.DhcpClient;
import android.net.ip.IpManager;
+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;
@@ -53,6 +55,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.test.TestLooper;
@@ -323,6 +326,9 @@
@Mock WifiLastResortWatchdog mWifiLastResortWatchdog;
@Mock PropertyService mPropertyService;
@Mock BuildProperties mBuildProperties;
+ @Mock IWificond mWificond;
+ @Mock IClientInterface mClientInterface;
+ @Mock IBinder mClientInterfaceBinder;
public WifiStateMachineTest() throws Exception {
}
@@ -350,6 +356,8 @@
when(mWifiInjector.getWifiBackupRestore()).thenReturn(mock(WifiBackupRestore.class));
when(mWifiInjector.makeWifiDiagnostics(anyObject())).thenReturn(
mock(BaseWifiDiagnostics.class));
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+
FrameworkFacade factory = getFrameworkFacade();
Context context = getContext();
@@ -374,6 +382,9 @@
new UserInfo(UserHandle.USER_SYSTEM, "owner", 0),
new UserInfo(11, "managed profile", 0)));
+ when(mWificond.createClientInterface()).thenReturn(mClientInterface);
+ when(mClientInterface.asBinder()).thenReturn(mClientInterfaceBinder);
+
mWsm = new WifiStateMachine(context, factory, mLooper.getLooper(),
mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode);
mWsmThread = getWsmHandlerThread(mWsm);
@@ -430,7 +441,6 @@
@Test
public void loadComponents() throws Exception {
- when(mWifiNative.loadDriver()).thenReturn(true);
when(mWifiNative.startHal()).thenReturn(true);
when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(true);
mWsm.setSupplicantRunning(true);
@@ -456,16 +466,50 @@
}
@Test
- public void loadComponentsFailure() throws Exception {
- when(mWifiNative.loadDriver()).thenReturn(false);
+ public void shouldRequireHalToLeaveInitialState() throws Exception {
when(mWifiNative.startHal()).thenReturn(false);
- when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(false);
+ mWsm.setSupplicantRunning(true);
+ mLooper.dispatchAll();
+ assertEquals("InitialState", getCurrentState().getName());
+ }
+ @Test
+ public void shouldRequireSupplicantStartupToLeaveInitialState() throws Exception {
+ when(mWifiNative.startSupplicant(true)).thenReturn(false);
+ mWsm.setSupplicantRunning(true);
+ mLooper.dispatchAll();
+ assertEquals("InitialState", getCurrentState().getName());
+ }
+
+ @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());
- when(mWifiNative.loadDriver()).thenReturn(true);
+ // 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.startHal()).thenReturn(false);
+ when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(false);
+
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
@@ -483,7 +527,7 @@
private void addNetworkAndVerifySuccess(boolean isHidden) throws Exception {
loadComponents();
- final HashMap<String, String> nameToValue = new HashMap<String, String>();
+ final HashMap<String, String> nameToValue = new HashMap<>();
when(mWifiNative.addNetwork()).thenReturn(0);
when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString()))