1212import android .net .ConnectivityManager ;
1313import android .net .Network ;
1414import android .net .NetworkCapabilities ;
15- import android .net .NetworkInfo ;
1615import android .os .Build ;
1716import android .os .Handler ;
1817import android .os .Looper ;
2524
2625import org .apache .commons .httpclient .HttpStatus ;
2726
27+ import java .util .concurrent .ExecutorService ;
28+ import java .util .concurrent .Executors ;
29+
2830import androidx .annotation .NonNull ;
29- import androidx .core .net .ConnectivityManagerCompat ;
3031import kotlin .jvm .functions .Function1 ;
3132
3233class ConnectivityServiceImpl implements ConnectivityService {
@@ -39,6 +40,7 @@ class ConnectivityServiceImpl implements ConnectivityService {
3940 private final ClientFactory clientFactory ;
4041 private final GetRequestBuilder requestBuilder ;
4142 private final WalledCheckCache walledCheckCache ;
43+ private final ExecutorService executor = Executors .newSingleThreadExecutor ();
4244 private final Handler mainThreadHandler = new Handler (Looper .getMainLooper ());
4345
4446 static class GetRequestBuilder implements Function1 <String , GetMethod > {
@@ -62,43 +64,59 @@ public GetMethod invoke(String url) {
6264
6365 @ Override
6466 public void isNetworkAndServerAvailable (@ NonNull GenericCallback <Boolean > callback ) {
65- new Thread (() -> {
66- Network activeNetwork = platformConnectivityManager .getActiveNetwork ();
67- NetworkCapabilities networkCapabilities = platformConnectivityManager .getNetworkCapabilities (activeNetwork );
68- boolean hasInternet = networkCapabilities != null && networkCapabilities .hasCapability (NetworkCapabilities .NET_CAPABILITY_INTERNET );
69-
70- boolean result ;
71- if (hasInternet ) {
72- result = !isInternetWalled ();
73- } else {
74- result = false ;
75- }
76-
77- mainThreadHandler .post (() -> callback .onComplete (result ));
78- }).start ();
67+ executor .submit (() -> {
68+ boolean isAvailable = !isInternetWalled ();
69+ mainThreadHandler .post (() -> callback .onComplete (isAvailable ));
70+ });
7971 }
8072
73+ /**
74+ * Checks whether the device is currently connected to a network
75+ * that has verified Internet access.
76+ *
77+ * <p>This method performs multiple levels of validation:
78+ * <ul>
79+ * <li>Ensures there is an active network connection.</li>
80+ * <li>Retrieves and checks network capabilities.</li>
81+ * <li>Verifies that the active network provides and has validated Internet access.</li>
82+ * <li>Confirms that the network uses a supported transport type
83+ * (Wi-Fi, Cellular, Ethernet, VPN, etc.).</li>
84+ * </ul>
85+ *
86+ * @return {@code true} if the device is connected to the Internet via a valid transport type;
87+ * {@code false} otherwise.
88+ */
8189 @ Override
8290 public boolean isConnected () {
8391 Network nw = platformConnectivityManager .getActiveNetwork ();
84- NetworkCapabilities actNw = platformConnectivityManager .getNetworkCapabilities (nw );
92+ if (nw == null ) {
93+ return false ;
94+ }
8595
96+ NetworkCapabilities actNw = platformConnectivityManager .getNetworkCapabilities (nw );
8697 if (actNw == null ) {
8798 return false ;
8899 }
89100
90- if (actNw .hasTransport (NetworkCapabilities .TRANSPORT_WIFI ) ||
91- actNw .hasTransport (NetworkCapabilities .TRANSPORT_CELLULAR ) ||
92- actNw .hasTransport (NetworkCapabilities .TRANSPORT_ETHERNET ) ||
93- actNw .hasTransport (NetworkCapabilities .TRANSPORT_VPN ) ||
94- actNw .hasTransport (NetworkCapabilities .TRANSPORT_BLUETOOTH ) ||
95- actNw .hasTransport (NetworkCapabilities .TRANSPORT_WIFI_AWARE )) {
96- return true ;
97- }
101+ // Verify that the network both claims to provide Internet
102+ // and has been validated (i.e., Internet is actually reachable).
103+ if (actNw .hasCapability (NetworkCapabilities .NET_CAPABILITY_INTERNET ) && actNw .hasCapability (NetworkCapabilities .NET_CAPABILITY_VALIDATED )) {
98104
99- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S &&
100- actNw .hasTransport (NetworkCapabilities .TRANSPORT_USB )) {
101- return true ;
105+ // Check if the active network uses one of the recognized transport types.
106+ if (actNw .hasTransport (NetworkCapabilities .TRANSPORT_WIFI ) ||
107+ actNw .hasTransport (NetworkCapabilities .TRANSPORT_CELLULAR ) ||
108+ actNw .hasTransport (NetworkCapabilities .TRANSPORT_ETHERNET ) ||
109+ actNw .hasTransport (NetworkCapabilities .TRANSPORT_VPN ) ||
110+ actNw .hasTransport (NetworkCapabilities .TRANSPORT_BLUETOOTH ) ||
111+ actNw .hasTransport (NetworkCapabilities .TRANSPORT_WIFI_AWARE )) {
112+
113+ // Connected through a valid, verified network transport.
114+ return true ;
115+ }
116+
117+ // If still nothing matched check Android 12 (API 31, "S") and above via USB network transport.
118+ return Build .VERSION .SDK_INT >= Build .VERSION_CODES .S &&
119+ actNw .hasTransport (NetworkCapabilities .TRANSPORT_USB );
102120 }
103121
104122 return false ;
@@ -109,77 +127,55 @@ public boolean isInternetWalled() {
109127 final Boolean cachedValue = walledCheckCache .getValue ();
110128 if (cachedValue != null ) {
111129 return cachedValue ;
112- } else {
113- Server server = accountManager .getUser ().getServer ();
114- String baseServerAddress = server .getUri ().toString ();
115-
116- boolean result ;
117- Connectivity c = getConnectivity ();
118- if (c != null && c .isConnected () && c .isWifi () && !c .isMetered () && !baseServerAddress .isEmpty ()) {
119- GetMethod get = requestBuilder .invoke (baseServerAddress + CONNECTIVITY_CHECK_ROUTE );
120- PlainClient client = clientFactory .createPlainClient ();
121-
122- int status = get .execute (client );
123-
124- // Content-Length is not available when using chunked transfer encoding, so check for -1 as well
125- result = !(status == HttpStatus .SC_NO_CONTENT && get .getResponseContentLength () <= 0 );
126- get .releaseConnection ();
127- if (result ) {
128- Log_OC .w (TAG , "isInternetWalled(): Failed to GET " + CONNECTIVITY_CHECK_ROUTE + "," +
129- " assuming connectivity is impaired" );
130- }
131- } else {
132- result = (c != null && !c .isConnected ());
133- }
134-
135- walledCheckCache .setValue (result );
136- return result ;
137130 }
138- }
139131
140- @ Override
141- public Connectivity getConnectivity () {
142- NetworkInfo networkInfo ;
143- try {
144- networkInfo = platformConnectivityManager .getActiveNetworkInfo ();
145- } catch (Throwable t ) {
146- networkInfo = null ; // no network available or no information (permission denied?)
147- }
132+ final Server server = accountManager .getUser ().getServer ();
133+ final String baseServerAddress = server .getUri ().toString ();
148134
149- if (networkInfo != null ) {
150- boolean isConnected = networkInfo .isConnectedOrConnecting ();
151- // more detailed check
152- boolean isMetered ;
153- isMetered = isNetworkMetered ();
154- boolean isWifi = networkInfo .getType () == ConnectivityManager .TYPE_WIFI || hasNonCellularConnectivity ();
155- return new Connectivity (isConnected , isMetered , isWifi , null );
156- } else {
157- return Connectivity .DISCONNECTED ;
135+ if (!isConnected () || baseServerAddress .isEmpty ()) {
136+ walledCheckCache .setValue (true );
137+ return true ;
158138 }
159- }
160139
161- private boolean isNetworkMetered () {
162- final Network network = platformConnectivityManager .getActiveNetwork ();
140+ final GetMethod get = requestBuilder .invoke (baseServerAddress + CONNECTIVITY_CHECK_ROUTE );
163141 try {
164- NetworkCapabilities networkCapabilities = platformConnectivityManager .getNetworkCapabilities (network );
165- if (networkCapabilities != null ) {
166- return !networkCapabilities .hasCapability (NetworkCapabilities .NET_CAPABILITY_NOT_RESTRICTED );
167- } else {
168- return ConnectivityManagerCompat .isActiveNetworkMetered (platformConnectivityManager );
142+ final PlainClient client = clientFactory .createPlainClient ();
143+ int status = get .execute (client );
144+
145+ boolean isWalled = !(status == HttpStatus .SC_NO_CONTENT && get .getResponseContentLength () <= 0 );
146+
147+ if (isWalled ) {
148+ Log_OC .w (TAG , "isInternetWalled(): Failed to GET " + CONNECTIVITY_CHECK_ROUTE +
149+ ", assuming connectivity is impaired" );
169150 }
170- } catch (RuntimeException e ) {
171- Log_OC .e (TAG , "Exception when checking network capabilities" , e );
172- return false ;
151+
152+ // Cache and return result
153+ walledCheckCache .setValue (isWalled );
154+ return isWalled ;
155+ } catch (Exception e ) {
156+ Log_OC .e (TAG , "Exception while checking internet walled state" , e );
157+ walledCheckCache .setValue (true );
158+ return true ;
159+ } finally {
160+ get .releaseConnection ();
173161 }
174162 }
175163
176- private boolean hasNonCellularConnectivity () {
177- for (NetworkInfo networkInfo : platformConnectivityManager .getAllNetworkInfo ()) {
178- if (networkInfo .isConnectedOrConnecting () && (networkInfo .getType () == ConnectivityManager .TYPE_WIFI ||
179- networkInfo .getType () == ConnectivityManager .TYPE_ETHERNET )) {
180- return true ;
181- }
164+ @ Override
165+ public Connectivity getConnectivity () {
166+ Network nw = platformConnectivityManager .getActiveNetwork ();
167+ if (nw == null ) {
168+ return Connectivity .DISCONNECTED ;
182169 }
183- return false ;
170+
171+ NetworkCapabilities nc = platformConnectivityManager .getNetworkCapabilities (nw );
172+ boolean isConnected = isConnected ();
173+ boolean isMetered = (nc != null ) && !nc .hasCapability (NetworkCapabilities .NET_CAPABILITY_NOT_METERED );
174+ boolean isWifi = (nc != null ) &&
175+ (nc .hasTransport (NetworkCapabilities .TRANSPORT_WIFI ) ||
176+ nc .hasTransport (NetworkCapabilities .TRANSPORT_ETHERNET ) ||
177+ nc .hasTransport (NetworkCapabilities .TRANSPORT_WIFI_AWARE ));
178+
179+ return new Connectivity (isConnected , isMetered , isWifi , null );
184180 }
185181}
0 commit comments