From cb3b895f1d3ec9d9ee1631b363487104f728f57e Mon Sep 17 00:00:00 2001 From: Pavel Grafov Date: Fri, 14 Dec 2018 13:51:07 +0000 Subject: [PATCH] Add API for VPN apps to query always-on and lockdown. Test: atest MixedDeviceOwnerTest#testAlwaysOnVpn Bug: 72628179 Change-Id: I73cb0888f7049b12ab0cdfa62678c3846e074d3b --- api/current.txt | 2 + .../android/net/IConnectivityManager.aidl | 2 + core/java/android/net/VpnService.java | 23 +++++++++++ .../android/server/ConnectivityService.java | 40 +++++++++++++++---- .../com/android/server/connectivity/Vpn.java | 15 ++++--- .../android/server/connectivity/VpnTest.java | 26 +++++++++++- 6 files changed, 94 insertions(+), 14 deletions(-) diff --git a/api/current.txt b/api/current.txt index 7c7429ffedafe..5cea771175f47 100755 --- a/api/current.txt +++ b/api/current.txt @@ -27897,6 +27897,8 @@ package android.net { public class VpnService extends android.app.Service { ctor public VpnService(); + method public final boolean isAlwaysOn(); + method public final boolean isLockdownEnabled(); method public android.os.IBinder onBind(android.content.Intent); method public void onRevoke(); method public static android.content.Intent prepare(android.content.Context); diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index da5d96e49d024..3d34574440dde 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -187,4 +187,6 @@ interface IConnectivityManager byte[] getNetworkWatchlistConfigHash(); int getConnectionOwnerUid(in ConnectionInfo connectionInfo); + boolean isCallerCurrentAlwaysOnVpnApp(); + boolean isCallerCurrentAlwaysOnVpnLockdownApp(); } diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index f0c0462cec186..37bf3a71ce62c 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -367,6 +367,29 @@ public class VpnService extends Service { } } + /** + * Returns whether the service is running in always-on VPN mode. + */ + public final boolean isAlwaysOn() { + try { + return getService().isCallerCurrentAlwaysOnVpnApp(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns whether the service is running in always-on VPN mode blocking connections without + * VPN. + */ + public final boolean isLockdownEnabled() { + try { + return getService().isCallerCurrentAlwaysOnVpnLockdownApp(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Return the communication interface to the service. This method returns * {@code null} on {@link Intent}s other than {@link #SERVICE_INTERFACE} diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d6f3e2ba48352..5220bdc103119 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -6342,6 +6342,20 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + @GuardedBy("mVpns") + private Vpn getVpnIfOwner() { + final int uid = Binder.getCallingUid(); + final int user = UserHandle.getUserId(uid); + + final Vpn vpn = mVpns.get(user); + if (vpn == null) { + return null; + } else { + final VpnInfo info = vpn.getVpnInfo(); + return (info == null || info.ownerUid != uid) ? null : vpn; + } + } + /** * Caller either needs to be an active VPN, or hold the NETWORK_STACK permission * for testing. @@ -6350,14 +6364,10 @@ public class ConnectivityService extends IConnectivityManager.Stub if (checkNetworkStackPermission()) { return null; } - final int uid = Binder.getCallingUid(); - final int user = UserHandle.getUserId(uid); synchronized (mVpns) { - Vpn vpn = mVpns.get(user); - try { - if (vpn.getVpnInfo().ownerUid == uid) return vpn; - } catch (NullPointerException e) { - /* vpn is null, or VPN is not connected and getVpnInfo() is null. */ + Vpn vpn = getVpnIfOwner(); + if (vpn != null) { + return vpn; } } throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK " @@ -6386,4 +6396,20 @@ public class ConnectivityService extends IConnectivityManager.Stub return uid; } + + @Override + public boolean isCallerCurrentAlwaysOnVpnApp() { + synchronized (mVpns) { + Vpn vpn = getVpnIfOwner(); + return vpn != null && vpn.getAlwaysOn(); + } + } + + @Override + public boolean isCallerCurrentAlwaysOnVpnLockdownApp() { + synchronized (mVpns) { + Vpn vpn = getVpnIfOwner(); + return vpn != null && vpn.getLockdown(); + } + } } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 602aedbc2d00e..c72c9ddf3f7a6 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -60,7 +60,6 @@ import android.net.NetworkMisc; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.UidRange; -import android.net.Uri; import android.net.VpnService; import android.os.Binder; import android.os.Build.VERSION_CODES; @@ -71,7 +70,6 @@ import android.os.INetworkManagementService; import android.os.Looper; import android.os.Parcel; import android.os.ParcelFileDescriptor; -import android.os.PatternMatcher; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; @@ -100,6 +98,8 @@ import com.android.server.DeviceIdleController; import com.android.server.LocalServices; import com.android.server.net.BaseNetworkObserver; +import libcore.io.IoUtils; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -121,8 +121,6 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; -import libcore.io.IoUtils; - /** * @hide */ @@ -346,10 +344,17 @@ public class Vpn { * * @return {@code true} if VPN lockdown is enabled. */ - public boolean getLockdown() { + public synchronized boolean getLockdown() { return mLockdown; } + /** + * Returns whether VPN is configured as always-on. + */ + public synchronized boolean getAlwaysOn() { + return mAlwaysOn; + } + /** * Checks if a VPN app supports always-on mode. * diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 9bf758797ed21..0b74d878f0695 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -57,7 +57,6 @@ import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.content.res.Resources; import android.net.ConnectivityManager; -import android.net.IConnectivityManager; import android.net.IpPrefix; import android.net.LinkProperties; import android.net.Network; @@ -97,7 +96,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -239,6 +237,30 @@ public class VpnTest { })), disallow); } + @Test + public void testGetAlwaysAndOnGetLockDown() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + + // Default state. + assertFalse(vpn.getAlwaysOn()); + assertFalse(vpn.getLockdown()); + + // Set always-on without lockdown. + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false)); + assertTrue(vpn.getAlwaysOn()); + assertFalse(vpn.getLockdown()); + + // Set always-on with lockdown. + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true)); + assertTrue(vpn.getAlwaysOn()); + assertTrue(vpn.getLockdown()); + + // Remove always-on configuration. + assertTrue(vpn.setAlwaysOnPackage(null, false)); + assertFalse(vpn.getAlwaysOn()); + assertFalse(vpn.getLockdown()); + } + @Test public void testLockdownChangingPackage() throws Exception { final Vpn vpn = createVpn(primaryUser.id);