flutternetwork / WiFiFlutter

Flutter plugin suite for various WiFi services.
https://wifi.flutternetwork.dev
285 stars 177 forks source link

Could not get gateway ip after connecting to iot device and setting forceWifiUsage(true); #346

Open ctwdtw opened 1 year ago

ctwdtw commented 1 year ago

Android Studio: Giraffe | 2022.3.1 Flutter SDK: 3.10.6 wifi_iot: ^0.3.18+1 network_info_plus: ^4.0.2 Android Phone: Pixel 3a, Android 12 android/app/src/main/AndroidManifes.xml

<uses-permission android:name="android.permission.INTERNET" />

    <uses-permission android:name="android.permission.WRITE_SETTINGS" />

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

code snippet:

const ssid = 'IGD-MY-IOT-SSID';
WiFiForIoTPlugin.connect(ssid,
            withInternet: false, security: NetworkSecurity.NONE)
        .then((value) async {

      String currentSsid = await WiFiForIoTPlugin.getSSID() ?? '';
      String ip = await WiFiForIoTPlugin.getIP() ?? '';

      if (Platform.isAndroid) {
        if (value) {
          WiFiForIoTPlugin.forceWifiUsage(true);
          dprint('success to connect with Andorid');
          final gatewayIP = await NetworkInfo().getWifiGatewayIP(); // null
          debugPrint('Adnroid, current gateway ip: $gatewayIP');
          debugPrint('Android, current_ssid: $currentSsid, current_ip: $ip'); // work as expected
        } else {
          dprint('failure to connect with Andorid');
          throw CustomError('Android Wifi Failure');
        }
      }
})

I am using wifi_iot on my Android Phone to connect to my IoT device which act as an wifi-spot without Internet connection. After connecting successfully, I was able to send an HTTP request to my IoT device with a successful response JSON.

However, the IP address of my IoT device (used for sending http request from my Android Phone to it) is hardcoded in my code.

Therefore, I used network_info_plus 4.0.2 to obtain the gateway IP of my Android Phone (which happens to be the ip address of my iot device) so that I can remove the hardcoded IP address from my code.

I have tested that if I connect to my IoT device through the settings page, network_info_plus 4.0.2 is able to retrieve the gateway IP. However, if I connect to my IoT device using the above code, network_info_plus 4.0.2 will return null for the gateway IP address.

I am aware that this is similar to this issue: https://github.com/flutternetwork/WiFiFlutter/issues/181, I read the instruction there but still can not solve my problem.

By the way, on my iPhone, there is no problem, wifi_iot and network_info_plus works seamlessly.

Please let me know if I need to provide further information or if I am missing something. Thank you very much.

ctwdtw commented 1 year ago

After investigating the source code of network_info_plus 4.0.2 and wifi_iot, I found the following:

  1. After wifi_iot.selectNetwork get called, the network is not an active network.
  2. The Implementation of network_info_plus.getGatewayIPAddress use an active network to retrieve gatewayIP, therefore it returns null.
fun getGatewayIPAddress(): String? {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        val linkAddresses = connectivityManager?.getLinkProperties(connectivityManager.activeNetwork)
        val dhcpServer = linkAddresses?.dhcpServerAddress?.hostAddress
        dhcpServer
    } else {
        @Suppress("DEPRECATION")
        val dhcpInfo = wifiManager.dhcpInfo
        val gatewayIPInt = dhcpInfo?.gateway
        Log.w("my_log_tag", "depercate, gatewayIPInt: $gatewayIPInt");
        gatewayIPInt?.let { formatIPAddress(it) }
    }
}
  1. Insdie the implementation of wifi_iot.selectNetwrok, if I do the following, I can get get getwayIp I needed:

    private boolean selectNetwork(final Network network, final ConnectivityManager manager) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        boolean isBinded = manager.bindProcessToNetwork(network);
    
        Log.w("my_log_tag", String.format("bindProcessToNetwork: %b, network: %s", isBinded, network.toString()));
    
        LinkProperties linkAddresses = manager.getLinkProperties(network);
    
        if (linkAddresses == null) {
            Log.w("my_log_tag", "!!! null linkAddress");
            return isBinded;
        }
    
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R) {
            Log.w("my_log_tag", "!!! SDK version < R");
            return isBinded;
        }
    
        Inet4Address dhcpServerAddress = linkAddresses.getDhcpServerAddress();
    
        if (dhcpServerAddress == null) {
            Log.w("my_log_tag", "!!! null dhcpServerAddress");
            return isBinded;
        }
    
        String hostAddress = dhcpServerAddress.getHostAddress();
    
        if (hostAddress == null) {
            Log.w("my_log_tag", "!!! null hostAddress");
            return isBinded;
        }
    
        /// code successfully ran through here, the host address is 192.168.xxx.xxx         
        Log.w("my_log_tag", String.format("get getway hostAddress: %s", hostAddress));
    
        return isBinded;
    
    } else {
        return ConnectivityManager.setProcessDefaultNetwork(network);
    }
    }

Now I know more about why did I got null gatewayIp in my original code snippet. However, I have limited knowledge about networking and Android, I am not sure what's the proper way to fix it. It seems to me that in my use case, it is the implementaion of network_info_plus.getGatewayIPAddress needs to be change as something like this:

fun getGatewayIPAddress(): String? {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        val network = connectivityManager?.allNetworks?.first() // but allNetworks is deprecated on API level 31
        val linkAddresses = connectivityManager?.getLinkProperties(network);
        val dhcpServer = linkAddresses?.dhcpServerAddress?.hostAddress
        dhcpServer
    } else {
        @Suppress("DEPRECATION")
        val dhcpInfo = wifiManager.dhcpInfo
        val gatewayIPInt = dhcpInfo?.gateway
        Log.w("my_log_tag", "depercate, gatewayIPInt: $gatewayIPInt");
        gatewayIPInt?.let { formatIPAddress(it) }
    }
}

If so, please close this issue or you may give me some further suggestion, thank you.

kodejack commented 1 year ago

I was having similar issue with this and I tried using "findAndConnect" and then forceWifi for Android and this then connected properly and allow me to hit the server on that network. I'll need to do dome more digging but his allow me to continue using the plugin. Also not that for iOS I had use "connect" as 'findAndConnect' did not work.

szgus commented 12 months ago

Hi ctwdtw You said:

I am using wifi_iot on my Android Phone to connect to my IoT device which act as an wifi-spot without Internet connection. After connecting successfully, I was able to send an HTTP request to my IoT device with a successful response JSON.

I would like to know if, you Android Phone could use internet over mobile data, while you app is sending HTTP requests and receiving responses from you IOT device. Are you connecting to your iot device through standard wifi or wifi-direct?