diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index d414bb7..f5ce4b2 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + diff --git a/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketModule.java b/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketModule.java index 6d3e7cb..3944542 100644 --- a/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketModule.java +++ b/android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketModule.java @@ -4,6 +4,8 @@ import android.annotation.SuppressLint; import android.content.Context; import android.net.ConnectivityManager; +import android.net.LinkAddress; +import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; @@ -18,6 +20,10 @@ import com.facebook.react.bridge.ReadableMap; import java.io.IOException; +import java.net.Inet4Address; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -76,7 +82,9 @@ public void run() { // Get the network interface final String localAddress = options.hasKey("localAddress") ? options.getString("localAddress") : null; final String iface = options.hasKey("interface") ? options.getString("interface") : null; - selectNetwork(iface, localAddress); + // Get ioT device host to retreive correct network in android concurrent connections + final String iotDeviceHost = options.hasKey("host") ? options.getString("host") : null; + selectNetwork(iface, localAddress, iotDeviceHost); TcpSocketClient client = new TcpSocketClient(tcpEvtListener, cId, null); socketMap.put(cId, client); ReadableMap tlsOptions = pendingTLS.get(cId); @@ -213,23 +221,99 @@ public void removeListeners(Integer count) { // Keep: Required for RN built in Event Emitter Calls. } - private void requestNetwork(final int transportType) throws InterruptedException { + private void requestNetwork(final int transportType, @Nullable final String iotDeviceHost) throws InterruptedException { final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder(); requestBuilder.addTransportType(transportType); final CountDownLatch awaitingNetwork = new CountDownLatch(1); // only needs to be counted down once to release waiting threads final ConnectivityManager cm = (ConnectivityManager) mReactContext.getSystemService(Context.CONNECTIVITY_SERVICE); - cm.requestNetwork(requestBuilder.build(), new ConnectivityManager.NetworkCallback() { - @Override - public void onAvailable(Network network) { - currentNetwork.setNetwork(network); - awaitingNetwork.countDown(); // Stop waiting - } - @Override - public void onUnavailable() { + if(iotDeviceHost==null || Objects.equals(iotDeviceHost, "localhost")) { + // Use old behavior if "host" param not specified on configuration array - default value "localhost" used + cm.requestNetwork(requestBuilder.build(), new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + currentNetwork.setNetwork(network); + awaitingNetwork.countDown(); // Stop waiting + } + + @Override + public void onUnavailable() { + awaitingNetwork.countDown(); // Stop waiting + } + }); + } else { + // smartmedev - add support for for concurrent-connections: + // Route all data to the ioT device network interface if exist more than one concurrent network + // See: https://developer.android.com/about/versions/12/behavior-changes-12#concurrent-connections + if (cm != null) { + // Get all connected networks + Network[] allNetworks = cm.getAllNetworks(); + List wifiNetworks = new ArrayList<>(); + + // Check exist at least one newtwork + if (allNetworks != null && allNetworks.length > 0) { + // Filter for retreive only networks based on selected transport type + for (Network network : allNetworks) { + NetworkCapabilities nc = cm.getNetworkCapabilities(network); + if (nc != null && nc.hasTransport(transportType)) { + wifiNetworks.add(network); + } + } + + // Check exist at least one newtwork based on selected transport type + if (!wifiNetworks.isEmpty()) { + boolean networkFound = false; + for (Network network : wifiNetworks) { + LinkProperties linkProperties = cm.getLinkProperties(network); + // Ensure linkProperties is not null + if (linkProperties == null) + continue; + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + Inet4Address foundServerAddress = linkProperties.getDhcpServerAddress(); + if(iotDeviceHost.equals(foundServerAddress.getHostAddress())) { + // found ioT device network + currentNetwork.setNetwork(network); + cm.bindProcessToNetwork(network); + networkFound = true; + awaitingNetwork.countDown(); // Stop waiting + break; + } + } else { + List linkAddressList = linkProperties.getLinkAddresses(); + if(linkAddressList != null && !linkAddressList.isEmpty()) { + for (LinkAddress address : linkAddressList) { + int lastDotIndex = iotDeviceHost.lastIndexOf('.'); + String iotSubnetAddress = iotDeviceHost; + if(lastDotIndex>=0) + iotSubnetAddress = iotDeviceHost.substring(0, lastDotIndex); + if(address.getAddress().getHostAddress().startsWith(iotSubnetAddress)) { + // found ioT device network + currentNetwork.setNetwork(network); + cm.bindProcessToNetwork(network); + networkFound = true; + awaitingNetwork.countDown(); // Stop waiting + break; + } + } + } + } + } + if (!networkFound) { + awaitingNetwork.countDown(); // Stop waiting if no network was found + } + } else { + awaitingNetwork.countDown(); // Stop waiting + } + } else { + awaitingNetwork.countDown(); // Stop waiting + } + } else { awaitingNetwork.countDown(); // Stop waiting } - }); + // smartmedev - end + } + // Timeout if there the network is unreachable ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1); exec.schedule(new Runnable() { @@ -248,7 +332,7 @@ public void run() { * "cellular" -> Cellular * etc... */ - private void selectNetwork(@Nullable final String iface, @Nullable final String ipAddress) throws InterruptedException, IOException { + private void selectNetwork(@Nullable final String iface, @Nullable final String ipAddress, @Nullable final String iotDeviceHost) throws InterruptedException, IOException { currentNetwork.setNetwork(null); if (iface == null) return; if (ipAddress != null) { @@ -260,13 +344,13 @@ private void selectNetwork(@Nullable final String iface, @Nullable final String } switch (iface) { case "wifi": - requestNetwork(NetworkCapabilities.TRANSPORT_WIFI); + requestNetwork(NetworkCapabilities.TRANSPORT_WIFI, iotDeviceHost); break; case "cellular": - requestNetwork(NetworkCapabilities.TRANSPORT_CELLULAR); + requestNetwork(NetworkCapabilities.TRANSPORT_CELLULAR, iotDeviceHost); break; case "ethernet": - requestNetwork(NetworkCapabilities.TRANSPORT_ETHERNET); + requestNetwork(NetworkCapabilities.TRANSPORT_ETHERNET, iotDeviceHost); break; } if (currentNetwork.getNetwork() == null) {