I'm curios if an Android device has both connections configured/available ConnectivityManager.TYPE_MOBILE & ConnectivityManager.TYPE_WIFI how it choose which connection to use for http requests?
Imagine I'm somewhere in WiFi zone without Internet access available (or requires login) but still i could connect to the Internet using GPRS/EDGE etc. (I mean MOBILE).
How Android handles such situations or how to handle it manually?
Making http requests is simple like:
HttpClient httpclient= new DefaultHttpClient();
HttpResponse response = httpclient.execute(mHttpRequest);
but there is nothing about which connection to use or force 2 use...
Check the answer here:
How to use 3G Connection in Android Application instead of Wi-fi?
Basically, you use ConnectivityManager's methods to route the connection. Try it with a test application that requests a GSM connection when WiFi is available and check whether it will do.
Related
My app connects to an external device using it's WiFi (the device works as a server). With introduction of Android 10 I needed to implement separate WiFi connectivity flow for different plaftorms (WifiNetworkSpecifier for Android 10+ and wifiManager.enableNetwork for < Android 10). The connectivity flow itself works fine, but I have some problems with stream communication.
In the app I have the ability to upload files to that external device. To do that I need to use HttpURLConnection. So I run:
val url = URL(UPDATE_FIRMWARE_URL)
val connection = (url.openConnection() as HttpURLConnection)
with(connection) {
doInput = true
doOutput = true
useCaches = false
requestMethod = METHOD_POST
//setRequestProperty(HEADER_CONNECTION, "Keep-Alive")
setRequestProperty("Connection", "close")
connectTimeout = 6000
setRequestProperty(HEADER_USER_AGENT, "Android Multipart HTTP Client 1.0")
setRequestProperty(HEADER_CONTENT_TYPE, "multipart/form-data; boundary=$boundary")
}
connection.connect()
val outputStream = connection.outputStream
DataOutputStream(outputStream).use { outputStream ->
// actual file upload
}
Now, the actual update consists of two files, and after first upload the device restarts, and I need to reconnect to it's wifi and upload the second file.
On Android < 9 the entire upload flow (with two files) works fine but on Android 10, after I send the first file and reconnect to the device's WiFi, when I call connection.connect() I get ConnectExcpetion with internal cause connect failed: ENETUNREACH (Network is unreachable) (which really makes no sense, cause I'm connected to that network...)
java.net.ConnectException: Failed to connect to (...)
at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:1409)
at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:1359)
at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:221)
at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:144)
at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:106)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:400)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:333)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:483)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:135)
Initially I had a problem also with connecting for the first time on Android 10, but I found this article, and adding the connectTimeout helped, but now the connection still fails when I try to connect for the second (and every next) time. The only thing that helps is restaring the entire app (which is no real solution).
What may be the problem, that the next connections fail despite I always execute the same code?
After a few days I finally found an answer to my question. It turns out that on Android 10 when you connect to the Access Point that does not offer the internet (eg. my external device) the standard API calls (using Retrofit) works fine, but when trying to use HttpURLConnection the system tries to use some network with internet connection, and as there is none, the connection fails.
The only way for the connection to work is to force the system to use our network by using ConnectivityManager.bindProcessToNetwork(network). This solution was proposed here and I've got no idea why someone downvoted that answer. It's correct.
What's interesting is that if we connect to the no-internet network via device settings, the connection works just fine even without binding.
I'm using Nearby Connections API in Android. It's working fine except cases where there is a sudden disconnections.
The client again succeed in finding the endpoint, using the discovery process, yet when he uses sendConnectionRequest() Connections.ConnectionResponseCallback never called no matter if I restart the app both on the client and on the endpoint. Only when I restart both devices the connection start to work again.
I have 20+ devices on the client side so there might be connection between the two things.
Any help on issue or where to start debugging the issue would be great.
For making a connection in Nearby Connection API, the client don't just sends connection request, but Host also has to accept it-
Nearby.Connections.acceptConnectionRequest(mGoogleApiClient, remoteEndpointId, myPayload, this);
or reject it-
Nearby.Connections.rejectConnectionRequest(mGoogleApiClient, remoteEndpointId);
try this, and in your connection response callback have conditions to do stuff
if(status.isSuccess()){
// Successful connection
} else {
// Failed connection
}
Hope it helped
You need to properly disconnect the connection when needed with;
Nearby.Connections.disconnectFromEndpoint(mGoogleApiClient, remoteEndpointId);
or;
Nearby.Connections.stopAllEndpoints(mGoogleApiClient);
https://developers.google.com/nearby/connections/android/manage-connections
Is there a way to access Android's broadcast that the WiFi connection is currently a captive portal (requires web login)? Android seems to do have this built in. If not a broadcast receiver, is there a way to check for the result of the captive portal check? I believe it's using this class, which is hidden from the API:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.L_preview/android/net/CaptivePortalTracker.java
Prior to 4.2, it was probably using:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.2_r1/android/net/wifi/WifiWatchdogStateMachine.java
Background:
I had been using my own method to detect whether WiFi likely required a login. I would wait for a WiFi connected state, then ping a site and make sure there was a response. This seemed to work great in most cases. Another strategy is to do a HttpRequest and check for a redirect or the response body you receive back, similar to Android's strategy in the classes listed above.
However, new to Lollipop is that the mobile data connection is used when WiFi does not have connectivity. This means my ping method will still return results, and that a redirect would not happen, as the request would be routed over the mobile data.
Is there a way to get Android's current status of a WiFi captive portal? If not, can we make sure a request goes over WiFi even when there's no connectivity as seen by Android?
You'd have to use the new ConnectivityManager.setProcessDefaultNetwork API to force your app to communicate over the captive portal. See https://github.com/pawitp/muwifi-autologin/commit/f045fe36f1fd98a106ea652e2d56f7ddfc871760 for an example.
Complete code added:
final ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
for (Network net : cm.getAllNetworks()) {
if (cm.getNetworkInfo(net).getType() == ConnectivityManager.TYPE_WIFI) {
Utils.logDebug(TAG, "Seting process network to " + net);
/*Since API 23 ConnectivityManager.setProcessDefaultNetwork(net);
is deprecated, use: */
cm.bindProcessToNetwork(net);
}
}
My application uses HttpURLConnection to connect to my REST services. I log errors and noticed that what happens occasionally is that user get's WiFi connection but it has proxy.
For example, those airport wifi's that redirect you to pay pages and then let you use internet. My code does not follow redirects.
What I really want is to ignore presence of WiFi and force communication over 3G/4G/E whatever. How can I do that on Android?
Switch to mobile network:
As soon as you detect a proxy, pop up a dialog telling the user that your app cannot use that network and hence you are switching to the mobile network. You can switch to a mobile network using ConnectivityManagerclass.
ConnectivityManager cm;
cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
cm.setNetworkPreference(ConnectivityManager.TYPE_MOBILE);
and switch back to the default when you are done:
cm.setNetworkPreference(ConnectivityManager.DEFAULT_NETWORK_PREFERENCE);
Detect a proxy:
Detect proxy using the following snippet
HttpURLConnection conn;
...
if (conn.getResponseCode() == HTTP_PROXY_AUTH){
// You got a '407: Proxy authentication required' response.
// Set the networkPreference() here and retry when
// network connection changes to TYPE_MOBILE.
}
You can check this post to know how to use a HttpURLConnection through a proxy : How do I make HttpURLConnection use a proxy?
Detect a 'network change':
To know how to detect 'network change' see this post :
Android, How to handle change in network (from GPRS to Wi-fi and vice-versa) while polling for data
Update:
If you cannot show a dialog, at least send a status bar Notification so that user knows about the network switch sometime later.
In your activities, Whenever you try to make call to your Web Services
Just Disable the WIFI if it's enabled. There will be many code snippets available on Internet for that like this
Now also Check that if Mobile data network is available or not, and If available make your call, otherwise show user a dialog that this app will require mobile data networks to do the tasks.
and as soon as you complete your HTTP Calls turn the WIFI ON again.
I perform some large downloads. I start a download being connected to 3G, all is fine. Then, I switch to WiFi connection, but the request returns a timeout exception. I have used HttpClient library. I have implemented a retry mechanism, so, when the request returns an exception, it sleeps for 0.5 seconds and tries to execute again and again. I would expect that, after connecting to a WiFi, the Http request could execute. But it seems that the Http execute method returns a null response, all the time after that. Very strange, if I commute again to 3G, the execute method returns again a good response. Can anyone help me please :) ?
First, it seems that it may be more convenient to use DownloadManager for large files - it handles retry and everything.
As for HttpClient - it's known to have some issues, but i'm not sure if you bumped into one of them or just overlooked something. It's been deprecated as of Gingerbread, you may want to try HttpUrlConnection instead, it's said to have less problems than HttpClient.
Also, when switching between WIFI and cell connections, HttpClient may need to be reinitialized completely, there's http range header to tell server which byte you want to continue downloading from. But again, I suggest you give DownloadManager a try, it may save you a lot of time.
This might be a routing problem:
when switching between different network types usually the local ip address and, more important, the local routing table changes, due to a different gateway being used. This means that packages that traveled fine between client and server wont reach any destination after a network change, if they are send the same route. Most likely your client implementation has to be notified of the change or even restartet completely, so that the routing strategy is reinitilized.
If the documentation of the implementation components you use dont reveal anything you could try to track this down using a package sniffer like wireshark. Typically packages running into nirwhana show up easily there.