【问题标题】:Check if a VPN connection is active in Android?检查 Android 中的 VPN 连接是否处于活动状态?
【发布时间】:2015-04-07 19:43:48
【问题描述】:

我在我的无根 Android 4.4 设备上安装了第三方 VPN 应用,并且想编写一个后台服务来监控 VPN 连接并在 VPN 连接中断时提醒用户。

有没有办法做到这一点?我找不到任何使用 VPNService API 的方法。

谢谢-D

【问题讨论】:

    标签: android monitoring vpn


    【解决方案1】:

    迟到的答案,在 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;
    }
    

    上述内容不适用于wireguard,因为接口名称可以是任何名称(通常是连接名称)。

    【讨论】:

      【解决方案2】:

      我为自己编写了一个支持 API 21 及更高版本的辅助函数。

      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;
      }
      

      【讨论】:

        【解决方案3】:

        这对我有用: 从 API 16 到 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");
        

        附:它也可以是另一个名为“ppp0”的网络接口,但在我对不同 VPN 应用程序的测试中,它始终是“tun0”

        【讨论】:

        • 这种变化。例如,在使用 OpenVPN 时,它会显示 tunX,其中 X 有时是递增的 id。使用 PPTP 时,使用的接口是 pptpX,其中 X 也是一个从 0 开始的 id。
        【解决方案4】:

        检查活动网络是否正在使用 VPN 的替代方法:

        Network activeNetwork = connectivityManager.getActiveNetwork();
        NetworkCapabilities caps = connectivityManager.getNetworkCapabilities(activeNetwork);
        boolean vpnInUse = caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
        

        【讨论】:

        • 这是真正回答问题的内容。应该检查活动网络,而不是遍历所有网络。关键是要知道设备是否真的在使用 VPN 主动连接
        【解决方案5】:

        查看网络信息怎么样?如果一个人有一种 VPN,我会假设有一个 VPN 连接。

        需要 ACCESS_NETWORK_STATE 权限。

                    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);
                        }
                    }
        

        【讨论】:

          【解决方案6】:

          使用NetworkCapabilities 对我有用。您必须遍历所有现有网络并检查哪个有 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));
          
          }
          

          【讨论】:

          • ConnectivityManager.getAllNetworks() 仅在 API 级别 21 Lollipop 中可用。 @Isaac Madwed 您现在有办法对 API 15 级冰淇淋三明治 及以上级别进行检查吗?
          • 不幸的是不是@vovahost。我的团队也有兴趣支持这些版本的 android,所以如果我们解决了问题,我会编辑我的帖子。
          • 你怎么知道哪个是活跃的?
          • @NickF 您可以在 API 23+ 上使用 getActiveNetwork()。在 API 21 和 22 上,如果任何网络有 VPN,我认为 VPN 已连接。
          【解决方案7】:

          编写一个点击内部页面或 ping 内部站点的小应用程序...

          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();
          

          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;
          

          【讨论】:

          • 谢谢,斯图尔特。这会起作用,但我正在寻找不需要 ping 后端服务的东西。 Android 是否会根据 VPN 状态发送任何系统范围的广播?
          • @user714122 vpnService prepare() (developer.android.com/reference/android/net/VpnService.html) 可能可以在这里使用,但更多的是用于建立 vpn 连接
          猜你喜欢
          • 2019-01-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-01-11
          • 2019-12-10
          • 2010-10-16
          • 1970-01-01
          相关资源
          最近更新 更多