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()))