Check if a VPN connection is active in Android?
Asked Answered
S

9

32

I have a third party VPN app on my non-rooted Android 4.4 device, and want to write a background service to monitor the VPN connection and alert the user if the VPN connection has been broken.

Is there a way to do this? I couldn't find any way using the VPNService API.

Thanks -D

Schuyler answered 7/2, 2015 at 19:44 Comment(0)
W
9

Here is kotlin coroutines flow solution

val isVpnActiveFlow = callbackFlow {
    val connectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
    if (connectivityManager == null) {
        channel.close(IllegalStateException("connectivity manager is null"))
        return@callbackFlow
    } else {
        val callback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                channel.trySend(true)
            }
            override fun onLost(network: Network) {
               channel.trySend(false)
            }
        }
        connectivityManager.registerNetworkCallback(

            //I have to investigate on this builder!
            NetworkRequest.Builder()
                    .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
                    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
                    .build(),
            callback
        )
        awaitClose {
            connectivityManager.unregisterNetworkCallback(callback)
        }
    }
}
Willodeanwilloughby answered 14/8, 2022 at 22:52 Comment(1)
Answer needs supporting information Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Configuration
V
29

Using NetworkCapabilities worked for me. You have to loop over all existing networks and check which has VPN_TRANSPORT

ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
Network[] networks = cm.getAllNetworks();

Log.i(TAG, "Network count: " + networks.length);
for(int i = 0; i < networks.length; i++) {

  NetworkCapabilities caps = cm.getNetworkCapabilities(networks[i]);

  Log.i(TAG, "Network " + i + ": " + networks[i].toString());
  Log.i(TAG, "VPN transport is: " + caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN));
  Log.i(TAG, "NOT_VPN capability is: " + caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN));

}
Vaporization answered 8/2, 2016 at 16:46 Comment(4)
ConnectivityManager.getAllNetworks() is only available in API level 21 Lollipop. @Isaac Madwed Do you now a way to do this check on API Level 15 Ice Cream Sandwich and above ?Bedazzle
Unfortunately not @vovahost. My team has interest in supporting those versions of android as well, so I will edit my post if we figure something out.Vaporization
How do you know which one is active?Circumnutate
@Circumnutate You can use getActiveNetwork() on API 23+. On API 21 & 22, I consider a VPN connected if any network has a VPN.Privily
M
24

An alternative to check if the active Network is using VPN:

Network activeNetwork = connectivityManager.getActiveNetwork();
NetworkCapabilities caps = connectivityManager.getNetworkCapabilities(activeNetwork);
boolean vpnInUse = caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
Millionaire answered 10/4, 2019 at 9:22 Comment(2)
This is what actually answers the question. Instead of looping through all networks, the active network should be checked. The point is to know if the device is actually using a VPN connecting activelyVazquez
That's the right answer.Chryso
F
20

This is works for me: Tested from API 16 to 23:

List<String> networkList = new ArrayList<>();
try {
    for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
        if (networkInterface.isUp())
            networkList.add(networkInterface.getName());
    }
} catch (Exception ex) {
    Timber.d("isVpnUsing Network List didn't received");
}

return networkList.contains("tun0");

P.S. Also it can be another networkInterface with name "ppp0", but in my tests with different VPN apps it's always was "tun0"

Famous answered 1/8, 2016 at 13:34 Comment(1)
This changes. For instance, when using OpenVPN, it'll say tunX, where X is sometimes an incremented id. When using PPTP, the interface used is pptpX, where X, again, is an id starting from 0.Hein
Q
17

Late answer, tested on Android 16 <-> 29:

public boolean vpn() {
    String iface = "";
    try {
        for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
            if (networkInterface.isUp())
                iface = networkInterface.getName();
                Log.d("DEBUG", "IFACE NAME: " + iface);
            if ( iface.contains("tun") || iface.contains("ppp") || iface.contains("pptp")) {
                return true;
            }
        }
    } catch (SocketException e1) {
        e1.printStackTrace();
    }

    return false;
}

The above doesn't work with wireguard because the interface name can be anything (normally the connection's name).

Quaff answered 15/8, 2019 at 11:50 Comment(0)
W
9

Here is kotlin coroutines flow solution

val isVpnActiveFlow = callbackFlow {
    val connectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
    if (connectivityManager == null) {
        channel.close(IllegalStateException("connectivity manager is null"))
        return@callbackFlow
    } else {
        val callback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                channel.trySend(true)
            }
            override fun onLost(network: Network) {
               channel.trySend(false)
            }
        }
        connectivityManager.registerNetworkCallback(

            //I have to investigate on this builder!
            NetworkRequest.Builder()
                    .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
                    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
                    .build(),
            callback
        )
        awaitClose {
            connectivityManager.unregisterNetworkCallback(callback)
        }
    }
}
Willodeanwilloughby answered 14/8, 2022 at 22:52 Comment(1)
Answer needs supporting information Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Configuration
T
4

I wrote a helper function for myself that works API 21 and above.

public static boolean vpnActive(Context context){
        
    // This method doesn't work below API 21
    if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
        return false;

    boolean vpnInUse = false;

    ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
        Network activeNetwork = connectivityManager.getActiveNetwork();
        NetworkCapabilities caps = connectivityManager.getNetworkCapabilities(activeNetwork);

        return caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
    }

    Network[] networks = connectivityManager.getAllNetworks();

    for (int i = 0; i < networks.length; i++) {
        NetworkCapabilities caps = connectivityManager.getNetworkCapabilities(networks[i]);
        if (caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
            vpnInUse = true;
            break;
        }
    }

    return vpnInUse;
}
Tejada answered 16/11, 2019 at 15:26 Comment(1)
Code crashes with NullPointerException if there's no connection at all.Coraliecoraline
I
0

Write a little app that hits an internal page or pings an internal site...

HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(internal_url);
HttpResponse response = client.execute(request);

String html = "";
InputStream in = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder str = new StringBuilder();
String line = null;
while((line = reader.readLine()) != null)
{
    str.append(line);
}
in.close();
html = str.toString();

or

String str = "";
try 
{
    Process process = Runtime.getRuntime().exec(
            "/system/bin/ping -c 8 " + internal_url);
    BufferedReader reader = new BufferedReader(new InputStreamReader(
            process.getInputStream()));
    int i;
    char[] buffer = new char[4096];
    StringBuffer output = new StringBuffer();
    while ((i = reader.read(buffer)) > 0)
        output.append(buffer, 0, i);
    reader.close();


    str = output.toString();

} 
catch (IOException e)
{

    e.printStackTrace();
}
return str;
Iridic answered 7/2, 2015 at 19:49 Comment(2)
Thanks, Stuart. This will work, but I'm looking for something that doesn't require pinging a backend service. Are there any system wide broadcasts that Android sends based on VPN state?Schuyler
@user714122 the vpnService prepare() (developer.android.com/reference/android/net/VpnService.html) might be able to be used here, but the is more for establishing vpn connectivityIridic
B
0

How about checking the network infos? If one has a type of VPN, I would assume there's a VPN connection.

Requires the ACCESS_NETWORK_STATE permission.

            List<NetworkInfo> connectedNetworks    = new ArrayList<>();

        if (Build.VERSION.SDK_INT >= 21)
            {
            Network[] networks = m_ConnectivityManager.getAllNetworks();

            for (Network n : networks)
                {
                NetworkInfo ni = m_ConnectivityManager.getNetworkInfo(n);

                if (ni.isConnectedOrConnecting())
                    {
                    connectedNetworks.add(ni);
                    }
                }
            }
        else
            {
            NetworkInfo[] nis = m_ConnectivityManager.getAllNetworkInfo();

            for (NetworkInfo ni : nis)
                {
                if (ni.isConnectedOrConnecting())
                    {
                    connectedNetworks.add(ni);
                    }
                }
            }

        boolean bHasVPN = false;

        if (Build.VERSION.SDK_INT >= 21)
            {
            for (NetworkInfo ni : connectedNetworks)
                {
                bHasVPN |= (ni.getType() == ConnectivityManager.TYPE_VPN);
                }
            }
Biffin answered 3/11, 2017 at 11:11 Comment(0)
A
0

This works for API < 21 too :

ConnectivityManager manager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        NetworkInfo[] infos=manager.getAllNetworkInfo();
        boolean VPNConnected = false;
        for (NetworkInfo info : infos){
            if ("VPN".equalsIgnoreCase(info.getTypeName()){
                if (info.getState() == NetworkInfo.State.CONNECTED){
                    VPNConnected = true;
                    break;
                }
            }
        }
Advocate answered 14/11, 2022 at 8:47 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.