JuanSeBestia/react-native-wifi-reborn

Wifi connection to a network without internet access

esleybonomo opened this issue · 3 comments

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?

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

### [NOT WORK / NO SOLUTION YET]

I think it is the problem. The differences of connections.

image
Look the description of TiqueTaque_180... : "Disponível via Setup TiqueTaque" (Available via TiqueTaque)

if connect manually in network settings of device appear a different description:
Captura de Tela 2024-08-06 às 14 28 51
Look the description of TiqueTaque_180... : "Conectado ao dispositivo. Sem acesso à internet." (Connected to device. Can't provide internet.)

--

(NOT WORK) Thanks. In my case, I am not using EXPO. So I tried this:

PROJECT/android/app/src/main/res/xml/network_security_config.xml:

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

PROJECT/android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
      <!-- your existing permissions ... -->
      
      <uses-permission android:name="android.permission.INTERNET" />
            <application
                  <!-- your existing options ... -->
                  android:usesCleartextTraffic="true"
                  android:networkSecurityConfig="@xml/network_security_config"
            >
            </application>
</manifest>

Some solution?

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community attention? This issue may be closed if no further activity occurs.