2626import java .net .InetSocketAddress ;
2727import java .util .concurrent .ConcurrentHashMap ;
2828import java .util .concurrent .CountDownLatch ;
29+ import java .util .concurrent .ScheduledThreadPoolExecutor ;
30+ import java .util .concurrent .TimeUnit ;
2931
3032import androidx .annotation .NonNull ;
3133import androidx .annotation .Nullable ;
3234
3335public class TcpSocketModule extends ReactContextBaseJavaModule implements TcpReceiverTask .OnDataReceivedListener {
34-
36+ private static final String TAG = "TcpSockets" ;
3537 private final ReactApplicationContext mReactContext ;
3638 private final ConcurrentHashMap <Integer , TcpSocketClient > socketClients = new ConcurrentHashMap <>();
3739 private final ConcurrentHashMap <String , Network > mNetworkMap = new ConcurrentHashMap <>();
38- @ Nullable
39- private Network mSelectedNetwork ;
40-
41- private static final String TAG = "TcpSockets" ;
40+ private final CurrentNetwork currentNetwork = new CurrentNetwork ();
4241
4342 public TcpSocketModule (ReactApplicationContext reactContext ) {
4443 super (reactContext );
@@ -57,53 +56,6 @@ private void sendEvent(String eventName, WritableMap params) {
5756 .emit (eventName , params );
5857 }
5958
60- /**
61- * Returns a network given its interface name:
62- * "wifi" -> WIFI
63- * "cellular" -> Cellular
64- * etc...
65- */
66- private void selectNetwork (@ Nullable final String iface , @ Nullable final String ipAddress ) throws InterruptedException {
67- if (iface == null ) return ;
68- mSelectedNetwork = null ;
69- if (ipAddress != null ) {
70- Network cachedNetwork = mNetworkMap .get (ipAddress );
71- if (cachedNetwork != null ) {
72- mSelectedNetwork = cachedNetwork ;
73- return ;
74- }
75- }
76- final CountDownLatch awaitingNetwork = new CountDownLatch (1 ); // only needs to be counted down once to release waiting threads
77- final ConnectivityManager cm = (ConnectivityManager ) mReactContext .getSystemService (Context .CONNECTIVITY_SERVICE );
78- NetworkRequest .Builder requestBuilder = new NetworkRequest .Builder ();
79- switch (iface ) {
80- case "wifi" :
81- requestBuilder .addTransportType (NetworkCapabilities .TRANSPORT_WIFI );
82- cm .requestNetwork (requestBuilder .build (), new ConnectivityManager .NetworkCallback () {
83- @ Override
84- public void onAvailable (Network network ) {
85- mSelectedNetwork = network ;
86- if (ipAddress != null && !ipAddress .equals ("0.0.0.0" ))
87- mNetworkMap .put (ipAddress , mSelectedNetwork );
88- awaitingNetwork .countDown (); // Stop waiting
89- }
90-
91- @ Override
92- public void onUnavailable () {
93- awaitingNetwork .countDown (); // Stop waiting
94- }
95- });
96- awaitingNetwork .await ();
97- break ;
98- case "cellular" : // TODO
99- default :
100- mSelectedNetwork = null ;
101- break ;
102- }
103- if (mSelectedNetwork != null && ipAddress != null && !ipAddress .equals ("0.0.0.0" ))
104- mNetworkMap .put (ipAddress , mSelectedNetwork );
105- }
106-
10759 /**
10860 * Creates a TCP Socket and establish a connection with the given host
10961 *
@@ -126,12 +78,12 @@ protected void doInBackgroundGuarded(Void... params) {
12678 }
12779 try {
12880 // Get the network interface
129- String localAddress = options .hasKey ("localAddress" ) ? options .getString ("localAddress" ) : null ;
130- String iface = options .hasKey ("interface" ) ? options .getString ("interface" ) : null ;
81+ final String localAddress = options .hasKey ("localAddress" ) ? options .getString ("localAddress" ) : null ;
82+ final String iface = options .hasKey ("interface" ) ? options .getString ("interface" ) : null ;
13183 selectNetwork (iface , localAddress );
13284 client = new TcpSocketClient (TcpSocketModule .this , cId , null );
13385 socketClients .put (cId , client );
134- client .connect (host , port , options , mSelectedNetwork );
86+ client .connect (host , port , options , currentNetwork . getNetwork () );
13587 onConnect (cId , host , port );
13688 } catch (Exception e ) {
13789 onError (cId , e .getMessage ());
@@ -209,6 +161,68 @@ protected void doInBackgroundGuarded(Void... params) {
209161 }.executeOnExecutor (AsyncTask .THREAD_POOL_EXECUTOR );
210162 }
211163
164+ private void requestNetwork (final int transportType ) throws InterruptedException {
165+ final NetworkRequest .Builder requestBuilder = new NetworkRequest .Builder ();
166+ requestBuilder .addTransportType (transportType );
167+ final CountDownLatch awaitingNetwork = new CountDownLatch (1 ); // only needs to be counted down once to release waiting threads
168+ final ConnectivityManager cm = (ConnectivityManager ) mReactContext .getSystemService (Context .CONNECTIVITY_SERVICE );
169+ cm .requestNetwork (requestBuilder .build (), new ConnectivityManager .NetworkCallback () {
170+ @ Override
171+ public void onAvailable (Network network ) {
172+ currentNetwork .setNetwork (network );
173+ awaitingNetwork .countDown (); // Stop waiting
174+ }
175+
176+ @ Override
177+ public void onUnavailable () {
178+ awaitingNetwork .countDown (); // Stop waiting
179+ }
180+ });
181+ // Timeout if there the network is unreachable
182+ ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor (1 );
183+ exec .schedule (new Runnable () {
184+ public void run () {
185+ awaitingNetwork .countDown (); // Stop waiting
186+ }
187+ }, 5 , TimeUnit .SECONDS );
188+ awaitingNetwork .await ();
189+ }
190+
191+ // REQUEST NETWORK
192+
193+ /**
194+ * Returns a network given its interface name:
195+ * "wifi" -> WIFI
196+ * "cellular" -> Cellular
197+ * etc...
198+ */
199+ private void selectNetwork (@ Nullable final String iface , @ Nullable final String ipAddress ) throws InterruptedException , IOException {
200+ currentNetwork .setNetwork (null );
201+ if (iface == null ) return ;
202+ if (ipAddress != null ) {
203+ final Network cachedNetwork = mNetworkMap .get (iface + ipAddress );
204+ if (cachedNetwork != null ) {
205+ currentNetwork .setNetwork (cachedNetwork );
206+ return ;
207+ }
208+ }
209+ switch (iface ) {
210+ case "wifi" :
211+ requestNetwork (NetworkCapabilities .TRANSPORT_WIFI );
212+ break ;
213+ case "cellular" :
214+ requestNetwork (NetworkCapabilities .TRANSPORT_CELLULAR );
215+ break ;
216+ case "ethernet" :
217+ requestNetwork (NetworkCapabilities .TRANSPORT_ETHERNET );
218+ break ;
219+ }
220+ if (currentNetwork .getNetwork () == null ) {
221+ throw new IOException ("Interface " + iface + " unreachable" );
222+ } else if (ipAddress != null && !ipAddress .equals ("0.0.0.0" ))
223+ mNetworkMap .put (iface + ipAddress , currentNetwork .getNetwork ());
224+ }
225+
212226 // TcpReceiverTask.OnDataReceivedListener
213227
214228 @ Override
@@ -273,4 +287,21 @@ public void onConnection(Integer serverId, Integer clientId, InetSocketAddress s
273287
274288 sendEvent ("connection" , eventParams );
275289 }
290+
291+ private class CurrentNetwork {
292+ @ Nullable
293+ Network network = null ;
294+
295+ private CurrentNetwork () {
296+ }
297+
298+ @ Nullable
299+ private Network getNetwork () {
300+ return network ;
301+ }
302+
303+ private void setNetwork (@ Nullable final Network network ) {
304+ this .network = network ;
305+ }
306+ }
276307}
0 commit comments