1111import android .net .NetworkCapabilities ;
1212import android .net .NetworkInfo ;
1313import android .net .Uri ;
14+ import android .net .LinkProperties ;
15+ import android .net .LinkAddress ;
16+ import java .net .Inet4Address ;
1417import android .os .Build ;
1518import android .os .Bundle ;
1619import android .os .Environment ;
@@ -388,33 +391,43 @@ else if (this.options.fileCache)
388391
389392 // wifi only, need ACCESS_NETWORK_STATE permission
390393 // and API level >= 21
394+ boolean targetHostIpAvailable = (this .options .targetHostIp != null && !this .options .targetHostIp .isEmpty ());
391395 if (this .options .wifiOnly ) {
392-
393396 boolean found = false ;
394397
395398 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .LOLLIPOP ) {
396399 ConnectivityManager connectivityManager = (ConnectivityManager ) ReactNativeBlobUtilImpl .RCTContext .getSystemService (ReactNativeBlobUtilImpl .RCTContext .CONNECTIVITY_SERVICE );
397400 Network [] networks = connectivityManager .getAllNetworks ();
398-
401+ Network selectedNetwork ;
402+
399403 for (Network network : networks ) {
400404
401- NetworkInfo netInfo = connectivityManager .getNetworkInfo (network );
402- NetworkCapabilities caps = connectivityManager .getNetworkCapabilities (network );
403-
404- if (caps == null || netInfo == null ) {
405+ if (!isValidWifiNetwork (connectivityManager , network )) {
405406 continue ;
406407 }
407408
408- if (! netInfo . isConnected ()) {
409- continue ;
410- }
409+ // if targetHostIpAvailable does not match, fallback to any wifi
410+ if ( targetHostIpAvailable ) {
411+ String targetHostIp = this . options . targetHostIp ;
411412
412- if (caps .hasTransport (NetworkCapabilities .TRANSPORT_WIFI )) {
413+ if (networkMatchesTargetIp (connectivityManager , network , targetHostIp )) {
414+ clientBuilder .proxy (Proxy .NO_PROXY );
415+ clientBuilder .socketFactory (network .getSocketFactory ());
416+ found = true ;
417+ break ;
418+ }
419+ }
420+
421+ // wifiOnly. selects the first interface with wifi transport
422+ // if targetHostIp is available and it matches, this selection will be overriden
423+ if (!found ) {
413424 clientBuilder .proxy (Proxy .NO_PROXY );
414425 clientBuilder .socketFactory (network .getSocketFactory ());
415426 found = true ;
416- break ;
417427
428+ if (!targetHostIpAvailable ) {
429+ break ;
430+ }
418431 }
419432 }
420433
@@ -423,10 +436,12 @@ else if (this.options.fileCache)
423436 releaseTaskResource ();
424437 return ;
425438 }
439+
440+
426441 } else {
427- ReactNativeBlobUtilUtils .emitWarningEvent ("ReactNativeBlobUtil: wifiOnly was set, but SDK < 21. wifiOnly was ignored." );
442+ ReactNativeBlobUtilUtils .emitWarningEvent ("ReactNativeBlobUtil: wifiOnly or targetHostIp was set, but SDK < 21. wifiOnly was ignored." );
428443 }
429- }
444+ }
430445
431446 final Request .Builder builder = new Request .Builder ();
432447 try {
@@ -1011,4 +1026,50 @@ public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder
10111026
10121027 return client ;
10131028 }
1029+
1030+ /**
1031+ * Check if a network is a valid connected WiFi network
1032+ */
1033+ private boolean isValidWifiNetwork (ConnectivityManager cm , Network network ) {
1034+ NetworkInfo netInfo = cm .getNetworkInfo (network );
1035+ NetworkCapabilities caps = cm .getNetworkCapabilities (network );
1036+
1037+ if (caps == null || netInfo == null ) {
1038+ return false ;
1039+ }
1040+
1041+ if (!netInfo .isConnected ()) {
1042+ return false ;
1043+ }
1044+
1045+ return caps .hasTransport (NetworkCapabilities .TRANSPORT_WIFI );
1046+ }
1047+
1048+ /**
1049+ * Check if a network matches the target host IP address
1050+ */
1051+ private boolean networkMatchesTargetIp (ConnectivityManager cm , Network network , String targetHostIp ) {
1052+ LinkProperties lp = cm .getLinkProperties (network );
1053+ if (lp == null ) return false ;
1054+
1055+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
1056+ // For Android R and above, use DHCP server address
1057+ Inet4Address dhcpServer = lp .getDhcpServerAddress ();
1058+ if (dhcpServer != null && dhcpServer .getHostAddress ().equals (targetHostIp )) {
1059+ return true ;
1060+ }
1061+ }
1062+
1063+ // Always fall back to linkAddresses check
1064+ List <LinkAddress > linkAddresses = lp .getLinkAddresses ();
1065+ if (linkAddresses != null && !linkAddresses .isEmpty ()) {
1066+ for (LinkAddress la : linkAddresses ) {
1067+ if (la .getAddress ().getHostAddress ().equals (targetHostIp )) {
1068+ return true ;
1069+ }
1070+ }
1071+ }
1072+
1073+ return false ;
1074+ }
10141075}
0 commit comments