Merge "Add API for VPN apps to query always-on and lockdown."

This commit is contained in:
Pavel Grafov
2019-01-22 14:43:06 +00:00
committed by Gerrit Code Review
6 changed files with 94 additions and 14 deletions

View File

@@ -27766,6 +27766,8 @@ package android.net {
public class VpnService extends android.app.Service { public class VpnService extends android.app.Service {
ctor public VpnService(); ctor public VpnService();
method public final boolean isAlwaysOn();
method public final boolean isLockdownEnabled();
method public android.os.IBinder onBind(android.content.Intent); method public android.os.IBinder onBind(android.content.Intent);
method public void onRevoke(); method public void onRevoke();
method public static android.content.Intent prepare(android.content.Context); method public static android.content.Intent prepare(android.content.Context);

View File

@@ -187,4 +187,6 @@ interface IConnectivityManager
byte[] getNetworkWatchlistConfigHash(); byte[] getNetworkWatchlistConfigHash();
int getConnectionOwnerUid(in ConnectionInfo connectionInfo); int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
boolean isCallerCurrentAlwaysOnVpnApp();
boolean isCallerCurrentAlwaysOnVpnLockdownApp();
} }

View File

@@ -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 * Return the communication interface to the service. This method returns
* {@code null} on {@link Intent}s other than {@link #SERVICE_INTERFACE} * {@code null} on {@link Intent}s other than {@link #SERVICE_INTERFACE}

View File

@@ -6346,6 +6346,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 * Caller either needs to be an active VPN, or hold the NETWORK_STACK permission
* for testing. * for testing.
@@ -6354,14 +6368,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (checkNetworkStackPermission()) { if (checkNetworkStackPermission()) {
return null; return null;
} }
final int uid = Binder.getCallingUid();
final int user = UserHandle.getUserId(uid);
synchronized (mVpns) { synchronized (mVpns) {
Vpn vpn = mVpns.get(user); Vpn vpn = getVpnIfOwner();
try { if (vpn != null) {
if (vpn.getVpnInfo().ownerUid == uid) return vpn; return vpn;
} catch (NullPointerException e) {
/* vpn is null, or VPN is not connected and getVpnInfo() is null. */
} }
} }
throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK " throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK "
@@ -6390,4 +6400,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
return uid; 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();
}
}
} }

View File

@@ -60,7 +60,6 @@ import android.net.NetworkMisc;
import android.net.NetworkUtils; import android.net.NetworkUtils;
import android.net.RouteInfo; import android.net.RouteInfo;
import android.net.UidRange; import android.net.UidRange;
import android.net.Uri;
import android.net.VpnService; import android.net.VpnService;
import android.os.Binder; import android.os.Binder;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
@@ -71,7 +70,6 @@ import android.os.INetworkManagementService;
import android.os.Looper; import android.os.Looper;
import android.os.Parcel; import android.os.Parcel;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.PatternMatcher;
import android.os.Process; import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.SystemClock; import android.os.SystemClock;
@@ -100,6 +98,8 @@ import com.android.server.DeviceIdleController;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver; import com.android.server.net.BaseNetworkObserver;
import libcore.io.IoUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -121,8 +121,6 @@ import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import libcore.io.IoUtils;
/** /**
* @hide * @hide
*/ */
@@ -346,10 +344,17 @@ public class Vpn {
* *
* @return {@code true} if VPN lockdown is enabled. * @return {@code true} if VPN lockdown is enabled.
*/ */
public boolean getLockdown() { public synchronized boolean getLockdown() {
return mLockdown; 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. * Checks if a VPN app supports always-on mode.
* *

View File

@@ -57,7 +57,6 @@ import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.IpPrefix; import android.net.IpPrefix;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.Network; import android.net.Network;
@@ -97,7 +96,6 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@@ -239,6 +237,30 @@ public class VpnTest {
})), disallow); })), 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 @Test
public void testLockdownChangingPackage() throws Exception { public void testLockdownChangingPackage() throws Exception {
final Vpn vpn = createVpn(primaryUser.id); final Vpn vpn = createVpn(primaryUser.id);