Skip to content

Commit d1c1436

Browse files
committed
fix: connectivity service impl
1 parent 3a30726 commit d1c1436

File tree

1 file changed

+85
-89
lines changed

1 file changed

+85
-89
lines changed

app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java

Lines changed: 85 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import android.net.ConnectivityManager;
1313
import android.net.Network;
1414
import android.net.NetworkCapabilities;
15-
import android.net.NetworkInfo;
1615
import android.os.Build;
1716
import android.os.Handler;
1817
import android.os.Looper;
@@ -25,8 +24,10 @@
2524

2625
import org.apache.commons.httpclient.HttpStatus;
2726

27+
import java.util.concurrent.ExecutorService;
28+
import java.util.concurrent.Executors;
29+
2830
import androidx.annotation.NonNull;
29-
import androidx.core.net.ConnectivityManagerCompat;
3031
import kotlin.jvm.functions.Function1;
3132

3233
class 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

Comments
 (0)