Skip to content

Commit a2d1a79

Browse files
smartmedevnicpaola
andauthored
feat(Android): Add support for Android concurrent connections (multiple networks) (Rapsssito#193)
--------- Co-authored-by: Nicola Paola <nicolapaola.np@gmail.com>
1 parent cfe6a9c commit a2d1a79

File tree

2 files changed

+100
-15
lines changed

2 files changed

+100
-15
lines changed

android/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
package="com.asterinet.react.tcpsocket">
3+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
34
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
45
</manifest>

android/src/main/java/com/asterinet/react/tcpsocket/TcpSocketModule.java

Lines changed: 99 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import android.annotation.SuppressLint;
55
import android.content.Context;
66
import android.net.ConnectivityManager;
7+
import android.net.LinkAddress;
8+
import android.net.LinkProperties;
79
import android.net.Network;
810
import android.net.NetworkCapabilities;
911
import android.net.NetworkRequest;
@@ -18,6 +20,10 @@
1820
import com.facebook.react.bridge.ReadableMap;
1921

2022
import java.io.IOException;
23+
import java.net.Inet4Address;
24+
import java.util.ArrayList;
25+
import java.util.List;
26+
import java.util.Objects;
2127
import java.util.concurrent.ConcurrentHashMap;
2228
import java.util.concurrent.CountDownLatch;
2329
import java.util.concurrent.ExecutorService;
@@ -76,7 +82,9 @@ public void run() {
7682
// Get the network interface
7783
final String localAddress = options.hasKey("localAddress") ? options.getString("localAddress") : null;
7884
final String iface = options.hasKey("interface") ? options.getString("interface") : null;
79-
selectNetwork(iface, localAddress);
85+
// Get ioT device host to retreive correct network in android concurrent connections
86+
final String iotDeviceHost = options.hasKey("host") ? options.getString("host") : null;
87+
selectNetwork(iface, localAddress, iotDeviceHost);
8088
TcpSocketClient client = new TcpSocketClient(tcpEvtListener, cId, null);
8189
socketMap.put(cId, client);
8290
ReadableMap tlsOptions = pendingTLS.get(cId);
@@ -213,23 +221,99 @@ public void removeListeners(Integer count) {
213221
// Keep: Required for RN built in Event Emitter Calls.
214222
}
215223

216-
private void requestNetwork(final int transportType) throws InterruptedException {
224+
private void requestNetwork(final int transportType, @Nullable final String iotDeviceHost) throws InterruptedException {
217225
final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
218226
requestBuilder.addTransportType(transportType);
219227
final CountDownLatch awaitingNetwork = new CountDownLatch(1); // only needs to be counted down once to release waiting threads
220228
final ConnectivityManager cm = (ConnectivityManager) mReactContext.getSystemService(Context.CONNECTIVITY_SERVICE);
221-
cm.requestNetwork(requestBuilder.build(), new ConnectivityManager.NetworkCallback() {
222-
@Override
223-
public void onAvailable(Network network) {
224-
currentNetwork.setNetwork(network);
225-
awaitingNetwork.countDown(); // Stop waiting
226-
}
227229

228-
@Override
229-
public void onUnavailable() {
230+
if(iotDeviceHost==null || Objects.equals(iotDeviceHost, "localhost")) {
231+
// Use old behavior if "host" param not specified on configuration array - default value "localhost" used
232+
cm.requestNetwork(requestBuilder.build(), new ConnectivityManager.NetworkCallback() {
233+
@Override
234+
public void onAvailable(Network network) {
235+
currentNetwork.setNetwork(network);
236+
awaitingNetwork.countDown(); // Stop waiting
237+
}
238+
239+
@Override
240+
public void onUnavailable() {
241+
awaitingNetwork.countDown(); // Stop waiting
242+
}
243+
});
244+
} else {
245+
// smartmedev - add support for for concurrent-connections:
246+
// Route all data to the ioT device network interface if exist more than one concurrent network
247+
// See: https://developer.android.com/about/versions/12/behavior-changes-12#concurrent-connections
248+
if (cm != null) {
249+
// Get all connected networks
250+
Network[] allNetworks = cm.getAllNetworks();
251+
List<Network> wifiNetworks = new ArrayList<>();
252+
253+
// Check exist at least one newtwork
254+
if (allNetworks != null && allNetworks.length > 0) {
255+
// Filter for retreive only networks based on selected transport type
256+
for (Network network : allNetworks) {
257+
NetworkCapabilities nc = cm.getNetworkCapabilities(network);
258+
if (nc != null && nc.hasTransport(transportType)) {
259+
wifiNetworks.add(network);
260+
}
261+
}
262+
263+
// Check exist at least one newtwork based on selected transport type
264+
if (!wifiNetworks.isEmpty()) {
265+
boolean networkFound = false;
266+
for (Network network : wifiNetworks) {
267+
LinkProperties linkProperties = cm.getLinkProperties(network);
268+
// Ensure linkProperties is not null
269+
if (linkProperties == null)
270+
continue;
271+
272+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
273+
Inet4Address foundServerAddress = linkProperties.getDhcpServerAddress();
274+
if(iotDeviceHost.equals(foundServerAddress.getHostAddress())) {
275+
// found ioT device network
276+
currentNetwork.setNetwork(network);
277+
cm.bindProcessToNetwork(network);
278+
networkFound = true;
279+
awaitingNetwork.countDown(); // Stop waiting
280+
break;
281+
}
282+
} else {
283+
List<LinkAddress> linkAddressList = linkProperties.getLinkAddresses();
284+
if(linkAddressList != null && !linkAddressList.isEmpty()) {
285+
for (LinkAddress address : linkAddressList) {
286+
int lastDotIndex = iotDeviceHost.lastIndexOf('.');
287+
String iotSubnetAddress = iotDeviceHost;
288+
if(lastDotIndex>=0)
289+
iotSubnetAddress = iotDeviceHost.substring(0, lastDotIndex);
290+
if(address.getAddress().getHostAddress().startsWith(iotSubnetAddress)) {
291+
// found ioT device network
292+
currentNetwork.setNetwork(network);
293+
cm.bindProcessToNetwork(network);
294+
networkFound = true;
295+
awaitingNetwork.countDown(); // Stop waiting
296+
break;
297+
}
298+
}
299+
}
300+
}
301+
}
302+
if (!networkFound) {
303+
awaitingNetwork.countDown(); // Stop waiting if no network was found
304+
}
305+
} else {
306+
awaitingNetwork.countDown(); // Stop waiting
307+
}
308+
} else {
309+
awaitingNetwork.countDown(); // Stop waiting
310+
}
311+
} else {
230312
awaitingNetwork.countDown(); // Stop waiting
231313
}
232-
});
314+
// smartmedev - end
315+
}
316+
233317
// Timeout if there the network is unreachable
234318
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
235319
exec.schedule(new Runnable() {
@@ -248,7 +332,7 @@ public void run() {
248332
* "cellular" -> Cellular
249333
* etc...
250334
*/
251-
private void selectNetwork(@Nullable final String iface, @Nullable final String ipAddress) throws InterruptedException, IOException {
335+
private void selectNetwork(@Nullable final String iface, @Nullable final String ipAddress, @Nullable final String iotDeviceHost) throws InterruptedException, IOException {
252336
currentNetwork.setNetwork(null);
253337
if (iface == null) return;
254338
if (ipAddress != null) {
@@ -260,13 +344,13 @@ private void selectNetwork(@Nullable final String iface, @Nullable final String
260344
}
261345
switch (iface) {
262346
case "wifi":
263-
requestNetwork(NetworkCapabilities.TRANSPORT_WIFI);
347+
requestNetwork(NetworkCapabilities.TRANSPORT_WIFI, iotDeviceHost);
264348
break;
265349
case "cellular":
266-
requestNetwork(NetworkCapabilities.TRANSPORT_CELLULAR);
350+
requestNetwork(NetworkCapabilities.TRANSPORT_CELLULAR, iotDeviceHost);
267351
break;
268352
case "ethernet":
269-
requestNetwork(NetworkCapabilities.TRANSPORT_ETHERNET);
353+
requestNetwork(NetworkCapabilities.TRANSPORT_ETHERNET, iotDeviceHost);
270354
break;
271355
}
272356
if (currentNetwork.getNetwork() == null) {

0 commit comments

Comments
 (0)