I created a function that returns a boolean based on the presence of an internet connection, this function it is called different times from different java classes.
The only way that I find to use it is to use the StrictMode.setThreadPolicy (i know that it's not a good practice).
How I can solve my problem ?
public boolean checkConnection() {
boolean response = false;
try {
//StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager != null) {
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
URL url = new URL(databaseManagement.getSettingValue("urlCheckConnection", context));
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(3000);
httpURLConnection.setReadTimeout(3000);
httpURLConnection.connect();
response = httpURLConnection.getResponseCode() == 200;
httpURLConnection.disconnect();
httpURLConnection.getInputStream().close();
}
}
} catch (Exception e) {
response = false;
wil.WriteFile("checkConnection - Exception: " + e.getMessage(), context);
}
return response;
}
It is possible that this method will block the calling thread for up to 3 seconds. Generally speaking you should not do network or file I/O on the main (UI) thread because that can cause the app to appear non-responsive (and Android will generate an ANR exception in that case). There are various alternatives you could use, depending on your situation. Here are two:
Don't ever call this method on the main (UI) thread. Always perform your Internet connectivity checks on background threads
Make the method asynchronous and provide a callback interface. The caller would then call the method (which will return immediately after launching the connectivity check on a background thread) and then the callback would be triggered when the connectivity check is completed. If this must be done on the main (UI) thread, you should show a progress dialog or similar while you are executing the connectivity check so that the user doesn't think the app is stuck.
Your question is a bit unclear.
Here is how internet connection is checked in my app:
private fun isConnected(): Boolean {
val connMgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkInfo: NetworkInfo? = connMgr.activeNetworkInfo
return networkInfo?.isConnected == true
}
It still should be tested on the latest Android APIs as there are known changes, e.g. lack of options to emulate network on\off programmatically from tests since some specific version of Android API.
On another hand StrictMode is used to force you and all the rest software developers to write correct programs. Your code which operates with network and data storages should not be executed in the main thread (which is done by default), it should be run in the separate thread. StrictMode tracks this and notify you about violation of this practice either by warning message in logs or by crashing your app (I prefer second one as it is more obvious).
However sometimes you depend on 3rd party library which violates this good practices and keeping yourStrictMode enabled prevents you from using this library.
In any cases StrictMode is usually enabled only for development stage like this:
if (BuildConfig.DEBUG) {
// TODO enable StrictMode policies
}
Background
I've noticed that in WifiManager class there is a function called addNetwork, that might be useful if I want to restore or save networks information (network name AKA SSID, together with the password and the type), so that I could also connect to it.
The problem
I can't find much information about how to do such a thing. I've seen various examples on StackOverflow, and if I target Android API 28 (or below), I indeed succeed to make it add a network and even connect to it.
When targeting Android 29 (Android Q), however, it fails to add the network.
What I've found
Since I'm trying on Pixel 2 with Android Q beta 4, I think that maybe it's because addNetwork is deprecated, so the docs even say so, and that if I target Android Q, it won't work, and indeed it doesn't work:
Compatibility Note: For applications targeting Build.VERSION_CODES.Q
or above, this API will always return -1.
The way it seems it should work up till Android Q (excluding), is by preparing WifiConfiguration and adding it. Later I can also connect to it if I wish. On Android Q, it seems it was replaced by WifiNetworkSuggestion, but it doesn't seem like it's about adding a network at all:
The Network Suggestion object is used to provide a Wi-Fi network for
consideration when auto-connecting to networks. Apps cannot directly
create this object, they must use
WifiNetworkSuggestion.Builder#build() to obtain an instance of this
object.
Apps can provide a list of such networks to the platform using
WifiManager#addNetworkSuggestions(List).
Here's my current code, for pre-Android-Q
#WorkerThread
fun addNetwork(context: Context, networkName: String, networkPassword: String? = null, keyMgmt: Int = WifiConfiguration.KeyMgmt.NONE) {
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
val conf = WifiConfiguration()
conf.SSID = "\"$networkName\""
conf.preSharedKey = if (networkPassword.isNullOrEmpty()) "" else "\"$networkPassword\""
conf.allowedKeyManagement.set(keyMgmt)
when (keyMgmt) {
WifiConfiguration.KeyMgmt.WPA_PSK -> {
//WPA/WPA2
}
WifiConfiguration.KeyMgmt.IEEE8021X -> {
}
WifiConfiguration.KeyMgmt.WPA_EAP -> {
}
WifiConfiguration.KeyMgmt.NONE -> {
if (networkPassword.isNullOrEmpty()) {
//open network
conf.wepKeys[0] = "\"\""
} else {
//wep
conf.wepKeys[0] = "\"" + networkPassword + "\""
conf.wepTxKeyIndex = 0
conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40)
}
}
}
if (networkPassword.isNullOrEmpty()) {
//open network
conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
} else {
}
wifiManager.isWifiEnabled = true
while (!wifiManager.pingSupplicant()) {
Log.d("AppLog", "waiting to be able to add network")
}
val networkId = wifiManager.addNetwork(conf)
if (networkId == -1)
Log.d("AppLog", "failed to add network")
else {
wifiManager.enableNetwork(networkId, false)
Log.d("AppLog", "success to add network")
}
}
Seems it requires only these permissions:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
But in any case, this works as long as you don't target Android Q (API 29) and above. When you target it, I indeed always get "-1" as a result, meaning it fails.
I've also found an issue on the issue tracker (here and I wrote about it here), telling about someone that needs the API back, but I'm not sure it's about adding a network.
Looking at WifiNetworkSuggestion, I don't see that it has as many things to set as WifiConfiguration via its builder, so this is another reason for why I suspect it's not about adding a network.
But I tried anyway. Here's the code I've tried, for example, to add a normal WPA network:
#WorkerThread
fun addNetworkAndroidQ(context: Context, networkName: String, networkPassword: String? = null) {
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
val list = ArrayList<WifiNetworkSuggestion>()
val builder = WifiNetworkSuggestion.Builder().setSsid(networkName)
if (!networkPassword.isNullOrEmpty())
builder.setWpa2Passphrase(networkPassword)
list.add(builder.build())
val result = wifiManager.addNetworkSuggestions(list)
if (result == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS)
Log.d("AppLog", "success")
else Log.d("AppLog", "failed")
}
When running (I gave it my Wifi network details, after making the OS forget about it), it says it succeeded, but nothing occurred on the OS's Wifi settings. The network doesn't exist there with the password I've added. So I really don't get what it did...
After a few long seconds, I've noticed a notification asking me if it's ok to connect to the suggested networks made by the app:
But still when I chose that I accept, it didn't do anything, as before.
I tried to make another POC, thinking I might have done it incorrectly, but then it didn't even show the notification. Since I think this whole behavior is a bug, I've reported about it here.
Not only that, but I've found out that if indeed it is supposed to add a network one way or another, it still has some serious restrictions, such as max added networks (here) and being removed upon uninstall of the app (here)
The questions
How should Android Q be handled exactly? Is there really no API anymore to add a network?
If WifiNetworkSuggestion is not about adding a network, what is it really used for exactly?
Since I'm not familiar enough with the tidbits of adding a network, is my code correct about all possible ways to add a network? I ask this because someone wrote here that people should enable Wifi and make sure pingSupplicant returns true. Is it true? Or would it be enough to just call addNetwork ?
If it's now impossible to add a network using the normal API, is there maybe a solution by using a rooted device instead? Maybe some adb command?
EDIT: Not sure how to do it officially, but using adb, you might be able to add Wifi-networks on Android 11 . Need to check adb shell cmd wifi help .
I stuck with same issue, but somehow I reached a reproducible state for connecting a desired network and I want to share my findings it may helps.
As a summary:
You have to disable all auto connection before applying WifiNetworkSuggestion logic
For more details, Please read the following:
I used the following code (Similar to what you use):
private fun connectUsingNetworkSuggestion(ssid: String, password: String) {
val wifiNetworkSuggestion = WifiNetworkSuggestion.Builder()
.setSsid(ssid)
.setWpa2Passphrase(password)
.build()
// Optional (Wait for post connection broadcast to one of your suggestions)
val intentFilter =
IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);
val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (!intent.action.equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
return
}
showToast("Connection Suggestion Succeeded")
// do post connect processing here
}
}
registerReceiver(broadcastReceiver, intentFilter)
lastSuggestedNetwork?.let {
val status = wifiManager.removeNetworkSuggestions(listOf(it))
Log.i("WifiNetworkSuggestion", "Removing Network suggestions status is $status")
}
val suggestionsList = listOf(wifiNetworkSuggestion)
var status = wifiManager.addNetworkSuggestions(suggestionsList)
Log.i("WifiNetworkSuggestion", "Adding Network suggestions status is $status")
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE) {
showToast("Suggestion Update Needed")
status = wifiManager.removeNetworkSuggestions(suggestionsList)
Log.i("WifiNetworkSuggestion", "Removing Network suggestions status is $status")
status = wifiManager.addNetworkSuggestions(suggestionsList)
}
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
lastSuggestedNetwork = wifiNetworkSuggestion
lastSuggestedNetworkSSID = ssid
showToast("Suggestion Added")
}
}
So here are the steps:
Install fresh version / Or remove all suggestion you added before
Make sure that you forgot all surrounding networks so your device won't auto-connect
Add wifi network suggestions list
Go to Wifi Settings to scan networks Or wait until next scan is running
A notification prompt will appear :
6. When you Press "Yes" the system will auto-connect with it via your app and internet will work normally. See the following:
Please note the following:
If you disconnect the network from Wifi Settings (i.e press disconnect bin icon in the following image) your network will be blocked for 24 hours from auto-connect even if you removed the suggested network using wifiManager.removeNetworkSuggestions(listOf(it)) and add it again. And even if you uninstall and install your app again
Unfortunately, this is limitation added by Android System as described here:
If the user uses the Wi-Fi picker to explicitly disconnect from one of the network suggestions when connected to it, then that network is blacklisted for 24 hours. During the blacklist period, that network will not be considered for auto-connection, even if the app removes and re-adds the network suggestion corresponding to the network.
If you uninstall the application while connected to suggested WiFi, the system will close the connection automatically.
In case you have multiple suggestion you can priorities them by using WifiNetworkSuggestion.Builder().setPriority(<Priority Integer>) as mentioned here:
Specify the priority of this network among other network suggestions provided by the same app (priorities have no impact on suggestions by different apps). The higher the number, the higher the priority (i.e value of 0 = lowest priority).
In case you pressed "No" in notification prompt, you can change it from (Settings > Apps & notifications > Special App access > Wi-Fi Control > App name) as described here:
A user declining the network suggestion notification removes the CHANGE_WIFI_STATE permission from the app. The user can grant this approval later by going into the Wi-Fi control menu (Settings > Apps & notifications > Special App access > Wi-Fi Control > App name).
I wish I had answers to all of your questions because I'm currently struggling with similar issues.
After many hours I was finally able to connect to the desired network using this approach:
val wifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
.setSsid(ssid)
.setWpa2Passphrase(passphrase)
.setBssid(mac)
.build()
val networkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setNetworkSpecifier(wifiNetworkSpecifier)
.build()
val connectivityManager = applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
connectivityManager?.requestNetwork(networkRequest, ConnectivityManager.NetworkCallback())
You can receive a whole host of events through the ConnectivityManager.NetworkCallback().
Looks like they've added support in Android 11(API 30) for adding network configuration that persists outside of the application scope and is saved as a system network configuration just like it was done with the deprecated WiFiManager method addNetwork. All you need to do is to use ACTION_WIFI_ADD_NETWORKS to show a system dialog that asks a user if he wants to proceed with adding a new Wifi suggestion to the system. This is how we start that dialog:
// used imports
import android.provider.Settings.ACTION_WIFI_ADD_NETWORKS
import android.provider.Settings.EXTRA_WIFI_NETWORK_LIST
import android.app.Activity
import android.content.Intent
import android.net.wifi.WifiNetworkSuggestion
// show system dialog for adding new network configuration
val wifiSuggestionBuilder = WifiNetworkSuggestion.Builder()
.setSsid("network SSID")
.build()
val suggestionsList = arraylistOf(wifiSuggestionBuilder)
val intent = new Intent(ACTION_WIFI_ADD_NETWORKS)
intent.putParcelableArrayListExtra(EXTRA_WIFI_NETWORK_LIST, suggestionsList);
activity.startActivityForResult(intent, 1000)
The dialog looks like this:
And then we just need to handle a result in onActivityResult method like this:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == 1000) {
if (resultCode == Activity.RESULT_OK) {
// network succesfully added - User pressed Save
} else if (resultCode == Activity.RESULT_CANCELED) {
// failed attempt of adding network to system - User pressed Cancel
}
}
}
But as I've tested this code on Android devices that have older Android versions(lower then API30) installed I've got a crash every time I want it to show that dialog for adding a new network configuration. This is the crash:
java.lang.RuntimeException: Unable to start activity: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.settings.WIFI_ADD_NETWORKS (has extras) }
Looks like the new way is not back-supported out of the box. So, for API30 we can use a new Intent action, for API 28 and below we can still use the old way of adding Networks, but for API29 we have some kind of gray area where I was not able to find a good solution yet. If anyone has an idea what else to do please share it with me. ;)
#Sebastian Helzer's answer works for me. I use java in my application. This may help java users...
WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier.Builder()
.setSsid(ssid)
.setWpa2Passphrase(password)
.build();
NetworkRequest networkRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setNetworkSpecifier(wifiNetworkSpecifier)
.build();
ConnectivityManager connectivityManager = (ConnectivityManager)this.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback());
Ok, so my question is may be off topic but i really did not found any useful content to use both network interface simulnasily in my application is simple image uplaod to server using both open network for better speed.here can we use both network by programing in java?
i found this code snippet but its return only connection status.
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
Network etherNetwork = null;
for (Network network : connectivityManager.getAllNetworks()) {
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);
if (networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
etherNetwork = network;
}
}
Network boundNetwork = connectivityManager.getBoundNetworkForProcess();
if (boundNetwork != null) {
NetworkInfo boundNetworkInfo = connectivityManager.getNetworkInfo(boundNetwork);
if (boundNetworkInfo.getType() != ConnectivityManager.TYPE_ETHERNET) {
if (etherNetwork != null) {
connectivityManager.bindProcessToNetwork(etherNetwork);
}
}
}
As far as I know it is not possible.
Nevertheless:
MPTCP exists, and you may find roms that support it, but it is not out of the box.
Speedify claims to be able to do it, but since it doesn't require root, I assume it's just a clever use of a VPN connection and a sort of load balancing trick between connection types.
Basically, in order to really have the 2 connection types active, you would need to modify the kernel so that both network interfaces can be used at the same time.
You can follow the approach I'm using in this app if it helps
https://github.com/yschimke/OkHttpAndroidApp/
You can bind each socket yourself to a specific network interface before you connect. Each individual socket needs to be on a single network, but you can use both.
https://github.com/yschimke/OkHttpAndroidApp/blob/master/android/app/src/main/java/com/okhttpandroidapp/factory/AndroidNetworkManager.kt#L123
How to detect programatically if traffic is going through VPN without using intent to connect to VPNService. Is there some system call?
This works :
private boolean checkVPN() {
ConnectivityManager cm = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getNetworkInfo(ConnectivityManager.TYPE_VPN).isConnectedOrConnecting();
}
It is possible to check it in Android API 21 an higher, but it seems doesn't work (on nexus 5 # 5.0.1, nexus 7 # 5.0.2). Why its not working?
Snippet of new API for check it (and all traffic is routed by vpn if connected):
#Inject
boolean checkVPN(ConnectivityManager connMgr) {
//don't know why always returns null:
NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_VPN);
boolean isVpnConn = networkInfo == null ? false : networkInfo.isConnected();
return isVpnConn;
}
The Android OS is aware of when a VPN connection is active (as it shows an icon in the status bar for the duration of a VPN connection) but there is no public API method (which would appear on the VpnService) to check for an active connection.
Two solutions occur. If you are dealing with a specific situation where you know the network topology, run 'ping' to see if a particular IP address is available - use System.exec. the ping command is present on Android devices with stock ROMs, I've used it as part of a solution to to provide a more granular view of the state of a data connection (ip address not allocated, ip address allocated but can't access the internet, can access the internet)
A better solution would be to run a traceroute instead and analyse the output. I've not checked if traceroute is present on Android devices with stock ROMs, I'll edit this answer later with more complete information.
You can detect VPN with following code in android. Code will work in new and older version of android. Below code is in Kotlin.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if(checkVPNStatus()) {
Log.d("VPN-RAJ", "is VPN Connected YES")
} else {
Log.d("VPN-RAJ", "is VPN Connected NO")
}
}
private fun checkVPNStatus(): Boolean {
val connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
val network = connectivityManager.activeNetwork
val capabilities = connectivityManager.getNetworkCapabilities(network)
Log.d("VPN-RAJ", "in New Android Version")
capabilities!= null && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)
} else {
Log.d("VPN-RAJ", "in Old Android Version")
connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_VPN)!!.isConnectedOrConnecting
}
}
}
I guess for most use-cases you want to check if the actively used network is using VPN instead of if just any network available is using VPN.
connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_VPN)
... is returning a NetworkInfo for an available VPN Network. This might not be the active Network! Instead you could use the following snippet to test if the active Network is using VPN:
Network activeNetwork = connectivityManager.getActiveNetwork();
NetworkCapabilities caps = connectivityManager.getNetworkCapabilities(activeNetwork);
boolean vpnInUse = caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
To detect whether internet is connected or not I am using the following code:
public boolean netConnect(Context ctx)
{
ConnectivityManager cm;
NetworkInfo info = null;
try
{
cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
info = cm.getActiveNetworkInfo();
}
catch (Exception e)
{
e.printStackTrace();
}
if (info != null)
{
return true;
}
else
{
return false;
}
}
Which is working perfectly fine .
But I want to know if the internet connection goes off at the time when app is downloading some files from server(I mean to say in between the process) is there anyway to detect that and display that internet connection lost or weak?
You will have to use a BroadcastReceiver in all you Activities. I eventually ended up writing a super class for this.
Now, whenever I write an App I do not extend Activity, I always extends a dummy class that I create say MyActivity. That way whenever a requirement changes I only change MyActivity and all my classes are updated.
For your particular case, I wrote a superclass that is available at : https://github.com/sfarooq/A-droid-lib/
Look at the ConnectedActivity class in the network package. It shows a dialog whenever connection is lost and dismisses it when connection is restored. To use it just extend ConnectedActivity in the Activities you want. To use it throughout the app, I would recommend making your own superclass as mentioned above and have that extend ConnectedActivity so you are able to change stuff later (also helpful if you want to add custom dialog and menus later, you'll only have to add it to your super class).
There is special system action, that notify apps about changing of state of internet access. For getting it you need create BroadcastReceiver.
See this question for more info.