1

I have an Android VPN application. When I fire the intent to start the VPN (via VPNService.prepare), it fails immediately if there's an always-on VPN already configured on the device. That seems reasonable, but I'd like to be able to easily detect that case, so I can show a helpful message to the user.

By 'always on' I mean the specific VPN always-on Android VPN flag: https://developer.android.com/guide/topics/connectivity/vpn#always-on

I can't seem to find a way to access that info, even though it is used internally in Android (e.g. here but that getAlwaysOnVpnPackage doesn't seem to be available publicly AFAICT).

The best option I've seen is Check if a VPN connection is active in Android?, which will tell you if any VPN connection is currently active, but that's not enough, because:

  • I don't want to know about temporary VPN connections: I'm only interested if it's an always-on VPN connection.
  • Sometimes 'always-on' connections aren't actually always on. If you have a disconnected connection and set it as 'always-on', it's configured as such, and blocks all other VPN installs, but there's no network connection created (Android shows a persistent warning instead, which takes you to the other app to activate the connection). Because there's no connection, the above technique doesn't work. I still need to detect this case, since it still blocks my VPN setup.

Is there any way to check whether the device currently has a VPN configured as 'always-on'?

Tim Perry
  • 11,766
  • 1
  • 57
  • 85
  • This was closed for covering more than one problem, which I think is a misunderstanding of the question. There's a couple of examples here, but they're examples of the same single problem: I want to access info about the current VPN configurations, not the active VPN network connections. I've added more detail to try & clarify that. – Tim Perry Jan 29 '21 at 10:42
  • AFAIK, you need to be a device owner/manager to get this info. – Always Learning Jan 30 '21 at 23:50

2 Answers2

5

You can use this method

private fun isVpnAlwaysOn(): Boolean {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
        val alwaysOn = Settings.Secure.getString(contentResolver, "always_on_vpn_app")
        return !alwaysOn.isNullOrEmpty()
    } else false
}

'alwaysOn' contains the package name of the app for which always-on is configured.

Anas Tariq
  • 51
  • 1
  • 5
  • 3
    This will no longer work with Android 12 and above. If you use this, you will get `java.lang.SecurityException: Settings key: is not readable. From S+, settings keys annotated with @hide are restricted to system_server and system apps only, unless they are annotated with @Readable.` – hiddeneyes02 Mar 18 '22 at 23:39
1

In the end, it seems this isn't possible on a normal device any way that I can find. I think is possible if you're a device admin, but that requires managed enterprise devices etc.

For now, I've handled this by watching for near-instant (less than 200ms) VPN setup failures (between running startActivityForResult(vpnIntent) and receiving onActivityResult with RESULT_CANCELED) and then showing a helpful message in that case.

Full implementation is in https://github.com/httptoolkit/httptoolkit-android/commit/928fbf92a4f868042789471be0d42800a226194b in case you're trying to do the same.

Tim Perry
  • 11,766
  • 1
  • 57
  • 85