Implement WifiManager.connect/reconnect

PiperOrigin-RevId: 195159416
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowWifiManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowWifiManagerTest.java
index 70f5423..14725f7 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowWifiManagerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowWifiManagerTest.java
@@ -6,7 +6,9 @@
 import static org.robolectric.Shadows.shadowOf;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
+import android.net.NetworkInfo;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
@@ -319,4 +321,69 @@
   public void startScan_shouldNotThrowException() {
     assertThat(wifiManager.startScan()).isTrue();
   }
+
+  @Test
+  public void reconnect_shouldNotThrowException() {
+    assertThat(wifiManager.reconnect()).isFalse();
+  }
+
+  @Test
+  public void reconnect_setsConnectionInfo() {
+    // GIVEN
+    WifiConfiguration wifiConfiguration = new WifiConfiguration();
+    wifiConfiguration.SSID = "foo";
+    int netId = wifiManager.addNetwork(wifiConfiguration);
+    wifiManager.enableNetwork(netId, false);
+
+    // WHEN
+    wifiManager.reconnect();
+
+    // THEN
+    assertThat(wifiManager.getConnectionInfo().getSSID()).isEqualTo("foo");
+  }
+
+  @Test
+  public void reconnect_shouldEnableDhcp() {
+    // GIVEN
+    int netId = wifiManager.addNetwork(new WifiConfiguration());
+    wifiManager.enableNetwork(netId, false);
+
+    // WHEN
+    wifiManager.reconnect();
+
+    // THEN
+    assertThat(wifiManager.getDhcpInfo()).isNotNull();
+  }
+
+  @Test
+  public void reconnect_updatesConnectivityManager() {
+    // GIVEN
+    int netId = wifiManager.addNetwork(new WifiConfiguration());
+    wifiManager.enableNetwork(netId, false);
+
+    // WHEN
+    wifiManager.reconnect();
+
+    // THEN
+    NetworkInfo networkInfo =
+        ((ConnectivityManager)
+                RuntimeEnvironment.application.getSystemService(Context.CONNECTIVITY_SERVICE))
+            .getActiveNetworkInfo();
+    assertThat(networkInfo.getType()).isEqualTo(ConnectivityManager.TYPE_WIFI);
+    assertThat(networkInfo.isConnected()).isTrue();
+  }
+
+  @Test
+  @Config(minSdk = Build.VERSION_CODES.KITKAT)
+  public void connect_setsConnectionInfo() throws Exception {
+    // GIVEN
+    WifiConfiguration wifiConfiguration = new WifiConfiguration();
+    wifiConfiguration.SSID = "foo";
+
+    // WHEN
+    wifiManager.connect(wifiConfiguration, null);
+
+    // THEN
+    assertThat(wifiManager.getConnectionInfo().getSSID()).isEqualTo("foo");
+  }
 }
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWifiManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWifiManager.java
index aeb96a5..c38fc4b 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWifiManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWifiManager.java
@@ -1,9 +1,14 @@
 package org.robolectric.shadows;
 
 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
+import static android.os.Build.VERSION_CODES.KITKAT;
 import static android.os.Build.VERSION_CODES.LOLLIPOP;
+import static org.robolectric.Shadows.shadowOf;
 
+import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
+import android.net.NetworkInfo;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
@@ -14,6 +19,8 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.HiddenApi;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 import org.robolectric.shadow.api.Shadow;
@@ -24,6 +31,8 @@
  */
 @Implements(WifiManager.class)
 public class ShadowWifiManager {
+  private static final int LOCAL_HOST = 2130706433;
+
   private static float sSignalLevelInPercent = 1f;
   private boolean accessWifiStatePermission = true;
   private boolean wifiEnabled = true;
@@ -157,6 +166,60 @@
     return isScanAlwaysAvailable;
   }
 
+  @HiddenApi
+  @Implementation(minSdk = KITKAT)
+  protected void connect(WifiConfiguration wifiConfiguration, WifiManager.ActionListener listener) {
+    WifiInfo wifiInfo = getConnectionInfo();
+    shadowOf(wifiInfo).setSSID(wifiConfiguration.SSID);
+    shadowOf(wifiInfo).setBSSID(wifiConfiguration.BSSID);
+    shadowOf(wifiInfo).setNetworkId(wifiConfiguration.networkId);
+    setConnectionInfo(wifiInfo);
+
+    // Now that we're "connected" to wifi, update Dhcp and point it to localhost.
+    DhcpInfo dhcpInfo = new DhcpInfo();
+    dhcpInfo.gateway = LOCAL_HOST;
+    dhcpInfo.ipAddress = LOCAL_HOST;
+    setDhcpInfo(dhcpInfo);
+
+    // Now add the network to ConnectivityManager.
+    NetworkInfo networkInfo =
+        ShadowNetworkInfo.newInstance(
+            NetworkInfo.DetailedState.CONNECTED,
+            ConnectivityManager.TYPE_WIFI,
+            0 /* subType */,
+            true /* isAvailable */,
+            true /* isConnected */);
+    ShadowConnectivityManager connectivityManager =
+        (ShadowConnectivityManager)
+            shadowOf(
+                (ConnectivityManager)
+                    RuntimeEnvironment.application.getSystemService(Context.CONNECTIVITY_SERVICE));
+    connectivityManager.setActiveNetworkInfo(networkInfo);
+
+    if (listener != null) {
+      listener.onSuccess();
+    }
+  }
+
+  @Implementation
+  protected boolean reconnect() {
+    WifiConfiguration wifiConfiguration = getMostRecentNetwork();
+    if (wifiConfiguration == null) {
+      return false;
+    }
+
+    connect(wifiConfiguration, null);
+    return true;
+  }
+
+  private WifiConfiguration getMostRecentNetwork() {
+    if (getLastEnabledNetwork() == null) {
+      return null;
+    }
+
+    return getWifiConfiguration(getLastEnabledNetwork().first);
+  }
+
   public static void setSignalLevelInPercent(float level) {
     if (level < 0 || level > 1) {
       throw new IllegalArgumentException("level needs to be between 0 and 1");
@@ -201,6 +264,10 @@
     return copy;
   }
 
+  public WifiConfiguration getWifiConfiguration(int netId) {
+    return networkIdToConfiguredNetworks.get(netId);
+  }
+
   @Implements(WifiManager.WifiLock.class)
   public static class ShadowWifiLock {
     private int refCount;