JuanSeBestia / react-native-wifi-reborn

A react-native implementation for viewing and connecting to Wifi networks on Android and iOS devices.
ISC License
340 stars 117 forks source link

Wifi connection to a network without internet access #391

Open esleybonomo opened 2 weeks ago

esleybonomo commented 2 weeks ago

I have a project that connects WiFi to an IoT device, generating the APK in --dev-client mode, everything works perfectly, however when I generate the APK for testing in QA, it doesn't work, it seems to create a second network and it doesn't connect to the available.

image

useEffect(() => {
    (async () => {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
        {
          title: 'A permissão de localização é necessária para conexões WiFi',
          message:
            'Este aplicativo precisa de permissão de localização, ' + 
            'pois isso é necessário para digitalizar redes Wi-Fi.',
          buttonNegative: 'DENY',
          buttonPositive: 'ALLOW',
        },
      );
      const { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== 'granted') {
        return;
      }
      const location = await Location.getCurrentPositionAsync({});

      enableWifiUsage().then(() => {
        console.debug("WiFi local ativado. Conectando...");
        connectToWifi(macAddress, setIsLoading, wifiGatewayPassword, setNewMacAddress).then(() => {
          console.debug("Conectado!");
          getDeviceConfiguration().then(() => {
            console.debug("Carregou os dados de rede do equipamento.");
          },
          (e) => {
            let msgError = "Erro ao carregar dados de rede do equipamento. " + e.error;
            Toast.show({
              type: 'error',
              text1: msgError
            });
            throw new Error(msgError);
          });
        });
      });
    })();
  }, []);

  const wifiGatewayPassword = async () => {
    const response = await services.gatewayWifiPassword(macAddress);
    const wifiPassword = response.data;
    setGatewayPassword(wifiPassword);
    return Promise.resolve(wifiPassword)
  };

  const getDeviceConfiguration = ( async () => {
    const responseData = await services.GetDeviceConfiguration();
    const response = responseData.data
    setSsid(response.net_ssid);
    setPassword(response.net_pass);
  });

I created WIfiManager.js

import WifiManager from 'react-native-wifi-reborn';
import Toast from 'react-native-toast-message';

export const enableWifiUsage = async () => {
  try {
    await WifiManager.forceWifiUsageWithOptions(true, { noInternet: true }).then(() => {
      console.debug("Todas as solicitações de rede serão roteadas para a rede WiFi sem Internet.");
    });
  } catch (error) {
    console.error("Falhou em permitir o uso de Wi-Fi sem internet: " + error.message);
  }
};

export const disableWifiUsage = async () => {
  try {
    await WifiManager.forceWifiUsageWithOptions(false, { noInternet: true }).then(() => {
      console.debug("As solicitações de rede serão roteadas para a rede padrão.");
    });
  } catch (error) {
    console.error("Falha ao desativar o uso de WiFi: " + error.message);
  }
};

export const connectToWifi = async (macAddress, setIsLoading, wifiGatewayPassword, setNewMacAddress) => {
  const newMacAddress = macAddress.replace(/:/g, '-');
  setNewMacAddress(newMacAddress);
  const ssid = 'Connect Lite ' + newMacAddress;
  setIsLoading(true);

  const password = await wifiGatewayPassword();
  try {
    WifiManager.isEnabled().then((enabled) => {
      if (!enabled) {
        WifiManager.setEnabled(true);
        console.debug("Wifi Enabled");
      }
    })

    await WifiManager.connectToProtectedSSID(ssid, password, false, false).then(() => {
      Toast.show({
        type: 'success',
        text1: 'Conectado à rede Wi-Fi',
        text2: ssid
      });
    });
  } catch (error) {
    Toast.show({
      type: 'error',
      text1: 'Erro ao conectar à rede Wi-Fi do gateway',
      text2: error.message
    });
  } finally {
    setIsLoading(false);
  }
};

Can anyone help me identify where my error is?

esleybonomo commented 1 week ago

I found solution.

My suggestion is to include this in the lib in some way, so you don't need to create a plugin to change the AndroidManifest and do the prebuild generating the android and ios folders.

File plugins/network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">192.168.0.1</domain>
    </domain-config>
</network-security-config>

File plugins/network_security_config.js

const {AndroidConfig, withAndroidManifest } = require('@expo/config-plugins');
const {Paths} = require('@expo/config-plugins/build/android');
const path = require('path');
const fs = require('fs');
const fsPromises = fs.promises;

const { getMainApplicationOrThrow} = AndroidConfig.Manifest

const networkConfig = config => {
    return withAndroidManifest(config, async config => {
        config.modResults = await setCustomConfigAsync(config, config.modResults);
        return config;
    });
}

async function setCustomConfigAsync(
    config,
    androidManifest
) {

    const src_file_pat = path.join(__dirname, "network_security_config.xml");
    const res_file_path = path.join(await Paths.getResourceFolderAsync(config.modRequest.projectRoot),
        "xml", "network_security_config.xml");

    const res_dir = path.resolve(res_file_path, "..");

    if (!fs.existsSync(res_dir)) {
        await fsPromises.mkdir(res_dir);
    }

    try {
        await fsPromises.copyFile(src_file_pat, res_file_path);
    } catch (e) {
        throw e;
    }

    const mainApplication = getMainApplicationOrThrow(androidManifest);
    mainApplication.$["android:networkSecurityConfig"] = "@xml/network_security_config";

    return androidManifest;
}

module.exports = networkConfig;

And in app.json include it:

    "plugins": [
      "./plugins/network_security_config.js"
    ],

And it's important run npx expo prebuild --no-install

refs:

  1. https://stackoverflow.com/a/70775576
  2. https://freakycoder.com/react-native-notes-21-android-http-network-security-problem-fix-eeac4e1ea58b