Merge "Allow VPNs to specify their underlying networks." into lmp-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
45e2732717
@@ -17446,6 +17446,7 @@ package android.net {
|
|||||||
method public boolean protect(int);
|
method public boolean protect(int);
|
||||||
method public boolean protect(java.net.Socket);
|
method public boolean protect(java.net.Socket);
|
||||||
method public boolean protect(java.net.DatagramSocket);
|
method public boolean protect(java.net.DatagramSocket);
|
||||||
|
method public boolean setUnderlyingNetworks(android.net.Network[]);
|
||||||
field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService";
|
field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17467,6 +17468,7 @@ package android.net {
|
|||||||
method public android.net.VpnService.Builder setConfigureIntent(android.app.PendingIntent);
|
method public android.net.VpnService.Builder setConfigureIntent(android.app.PendingIntent);
|
||||||
method public android.net.VpnService.Builder setMtu(int);
|
method public android.net.VpnService.Builder setMtu(int);
|
||||||
method public android.net.VpnService.Builder setSession(java.lang.String);
|
method public android.net.VpnService.Builder setSession(java.lang.String);
|
||||||
|
method public android.net.VpnService.Builder setUnderlyingNetworks(android.net.Network[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,4 +170,5 @@ interface IConnectivityManager
|
|||||||
|
|
||||||
boolean addVpnAddress(String address, int prefixLength);
|
boolean addVpnAddress(String address, int prefixLength);
|
||||||
boolean removeVpnAddress(String address, int prefixLength);
|
boolean removeVpnAddress(String address, int prefixLength);
|
||||||
|
boolean setUnderlyingNetworksForVpn(in Network[] networks);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.IPackageManager;
|
import android.content.pm.IPackageManager;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Network;
|
||||||
import android.net.NetworkUtils;
|
import android.net.NetworkUtils;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -287,6 +288,46 @@ public class VpnService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the underlying networks used by the VPN for its upstream connections.
|
||||||
|
*
|
||||||
|
* Used by the system to know the actual networks that carry traffic for apps affected by this
|
||||||
|
* VPN in order to present this information to the user (e.g., via status bar icons).
|
||||||
|
*
|
||||||
|
* This method only needs to be called if the VPN has explicitly bound its underlying
|
||||||
|
* communications channels — such as the socket(s) passed to {@link #protect(int)} —
|
||||||
|
* to a {@code Network} using APIs such as {@link Network#bindSocket} or {@link
|
||||||
|
* Network#bindDatagramSocket}. The VPN should call this method every time the set of {@code
|
||||||
|
* Network}s it is using changes.
|
||||||
|
*
|
||||||
|
* {@code networks} is one of the following:
|
||||||
|
* <ul>
|
||||||
|
* <li><strong>a non-empty array</strong>: an array of one or more {@link Network}s, in
|
||||||
|
* decreasing preference order. For example, if this VPN uses both wifi and mobile (cellular)
|
||||||
|
* networks to carry app traffic, but prefers or uses wifi more than mobile, wifi should appear
|
||||||
|
* first in the array.</li>
|
||||||
|
* <li><strong>an empty array</strong>: a zero-element array, meaning that the VPN has no
|
||||||
|
* underlying network connection, and thus, app traffic will not be sent or received.</li>
|
||||||
|
* <li><strong>null</strong>: (default) signifies that the VPN uses whatever is the system's
|
||||||
|
* default network. I.e., it doesn't use the {@code bindSocket} or {@code bindDatagramSocket}
|
||||||
|
* APIs mentioned above to send traffic over specific channels.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* This call will succeed only if the VPN is currently established. For setting this value when
|
||||||
|
* the VPN has not yet been established, see {@link Builder#setUnderlyingNetworks}.
|
||||||
|
*
|
||||||
|
* @param networks An array of networks the VPN uses to tunnel traffic to/from its servers.
|
||||||
|
*
|
||||||
|
* @return {@code true} on success.
|
||||||
|
*/
|
||||||
|
public boolean setUnderlyingNetworks(Network[] networks) {
|
||||||
|
try {
|
||||||
|
return getService().setUnderlyingNetworksForVpn(networks);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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}
|
||||||
@@ -662,6 +703,20 @@ public class VpnService extends Service {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the underlying networks used by the VPN for its upstream connections.
|
||||||
|
*
|
||||||
|
* @see VpnService#setUnderlyingNetworks
|
||||||
|
*
|
||||||
|
* @param networks An array of networks the VPN uses to tunnel traffic to/from its servers.
|
||||||
|
*
|
||||||
|
* @return this {@link Builder} object to facilitate chaining method calls.
|
||||||
|
*/
|
||||||
|
public Builder setUnderlyingNetworks(Network[] networks) {
|
||||||
|
mConfig.underlyingNetworks = networks != null ? networks.clone() : null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a VPN interface using the parameters supplied to this
|
* Create a VPN interface using the parameters supplied to this
|
||||||
* builder. The interface works on IP packets, and a file descriptor
|
* builder. The interface works on IP packets, and a file descriptor
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
|||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.net.LinkAddress;
|
import android.net.LinkAddress;
|
||||||
|
import android.net.Network;
|
||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
@@ -99,6 +100,7 @@ public class VpnConfig implements Parcelable {
|
|||||||
public boolean allowBypass;
|
public boolean allowBypass;
|
||||||
public boolean allowIPv4;
|
public boolean allowIPv4;
|
||||||
public boolean allowIPv6;
|
public boolean allowIPv6;
|
||||||
|
public Network[] underlyingNetworks;
|
||||||
|
|
||||||
public void updateAllowedFamilies(InetAddress address) {
|
public void updateAllowedFamilies(InetAddress address) {
|
||||||
if (address instanceof Inet4Address) {
|
if (address instanceof Inet4Address) {
|
||||||
@@ -162,6 +164,7 @@ public class VpnConfig implements Parcelable {
|
|||||||
out.writeInt(allowBypass ? 1 : 0);
|
out.writeInt(allowBypass ? 1 : 0);
|
||||||
out.writeInt(allowIPv4 ? 1 : 0);
|
out.writeInt(allowIPv4 ? 1 : 0);
|
||||||
out.writeInt(allowIPv6 ? 1 : 0);
|
out.writeInt(allowIPv6 ? 1 : 0);
|
||||||
|
out.writeTypedArray(underlyingNetworks, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Parcelable.Creator<VpnConfig> CREATOR =
|
public static final Parcelable.Creator<VpnConfig> CREATOR =
|
||||||
@@ -186,6 +189,7 @@ public class VpnConfig implements Parcelable {
|
|||||||
config.allowBypass = in.readInt() != 0;
|
config.allowBypass = in.readInt() != 0;
|
||||||
config.allowIPv4 = in.readInt() != 0;
|
config.allowIPv4 = in.readInt() != 0;
|
||||||
config.allowIPv6 = in.readInt() != 0;
|
config.allowIPv6 = in.readInt() != 0;
|
||||||
|
config.underlyingNetworks = in.createTypedArray(Network.CREATOR);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -865,6 +865,29 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
Network network = null;
|
Network network = null;
|
||||||
|
|
||||||
NetworkAgentInfo nai = mNetworkForRequestId.get(mDefaultRequest.requestId);
|
NetworkAgentInfo nai = mNetworkForRequestId.get(mDefaultRequest.requestId);
|
||||||
|
|
||||||
|
if (!mLockdownEnabled) {
|
||||||
|
int user = UserHandle.getUserId(uid);
|
||||||
|
synchronized (mVpns) {
|
||||||
|
Vpn vpn = mVpns.get(user);
|
||||||
|
if (vpn != null && vpn.appliesToUid(uid)) {
|
||||||
|
// getUnderlyingNetworks() returns:
|
||||||
|
// null => the VPN didn't specify anything, so we use the default.
|
||||||
|
// empty array => the VPN explicitly said "no default network".
|
||||||
|
// non-empty array => the VPN specified one or more default networks; we use the
|
||||||
|
// first one.
|
||||||
|
Network[] networks = vpn.getUnderlyingNetworks();
|
||||||
|
if (networks != null) {
|
||||||
|
if (networks.length > 0) {
|
||||||
|
nai = getNetworkAgentInfoForNetwork(networks[0]);
|
||||||
|
} else {
|
||||||
|
nai = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (nai != null) {
|
if (nai != null) {
|
||||||
synchronized (nai) {
|
synchronized (nai) {
|
||||||
info = new NetworkInfo(nai.networkInfo);
|
info = new NetworkInfo(nai.networkInfo);
|
||||||
@@ -4376,4 +4399,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
return mVpns.get(user).removeAddress(address, prefixLength);
|
return mVpns.get(user).removeAddress(address, prefixLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setUnderlyingNetworksForVpn(Network[] networks) {
|
||||||
|
throwIfLockdownEnabled();
|
||||||
|
int user = UserHandle.getUserId(Binder.getCallingUid());
|
||||||
|
synchronized (mVpns) {
|
||||||
|
return mVpns.get(user).setUnderlyingNetworks(networks);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import android.net.LinkAddress;
|
|||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.LocalSocket;
|
import android.net.LocalSocket;
|
||||||
import android.net.LocalSocketAddress;
|
import android.net.LocalSocketAddress;
|
||||||
|
import android.net.Network;
|
||||||
import android.net.NetworkAgent;
|
import android.net.NetworkAgent;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
@@ -580,7 +581,13 @@ public class Vpn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isRunningLocked() {
|
private boolean isRunningLocked() {
|
||||||
return mVpnUsers != null;
|
return mNetworkAgent != null && mInterface != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the VPN has been established and the calling UID is its owner. Used to check
|
||||||
|
// that a call to mutate VPN state is admissible.
|
||||||
|
private boolean isCallerEstablishedOwnerLocked() {
|
||||||
|
return isRunningLocked() && Binder.getCallingUid() == mOwnerUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Return type guarantees results are deduped and sorted, which callers require.
|
// Note: Return type guarantees results are deduped and sorted, which callers require.
|
||||||
@@ -595,7 +602,7 @@ public class Vpn {
|
|||||||
|
|
||||||
// Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
|
// Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
|
||||||
private void addVpnUserLocked(int userHandle) {
|
private void addVpnUserLocked(int userHandle) {
|
||||||
if (!isRunningLocked()) {
|
if (mVpnUsers == null) {
|
||||||
throw new IllegalStateException("VPN is not active");
|
throw new IllegalStateException("VPN is not active");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,7 +654,7 @@ public class Vpn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void removeVpnUserLocked(int userHandle) {
|
private void removeVpnUserLocked(int userHandle) {
|
||||||
if (!isRunningLocked()) {
|
if (mVpnUsers == null) {
|
||||||
throw new IllegalStateException("VPN is not active");
|
throw new IllegalStateException("VPN is not active");
|
||||||
}
|
}
|
||||||
final List<UidRange> ranges = uidRangesForUser(userHandle);
|
final List<UidRange> ranges = uidRangesForUser(userHandle);
|
||||||
@@ -767,27 +774,61 @@ public class Vpn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean addAddress(String address, int prefixLength) {
|
public synchronized boolean addAddress(String address, int prefixLength) {
|
||||||
if (Binder.getCallingUid() != mOwnerUID || mInterface == null || mNetworkAgent == null) {
|
if (!isCallerEstablishedOwnerLocked()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean success = jniAddAddress(mInterface, address, prefixLength);
|
boolean success = jniAddAddress(mInterface, address, prefixLength);
|
||||||
if (mNetworkAgent != null) {
|
mNetworkAgent.sendLinkProperties(makeLinkProperties());
|
||||||
mNetworkAgent.sendLinkProperties(makeLinkProperties());
|
|
||||||
}
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean removeAddress(String address, int prefixLength) {
|
public synchronized boolean removeAddress(String address, int prefixLength) {
|
||||||
if (Binder.getCallingUid() != mOwnerUID || mInterface == null || mNetworkAgent == null) {
|
if (!isCallerEstablishedOwnerLocked()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean success = jniDelAddress(mInterface, address, prefixLength);
|
boolean success = jniDelAddress(mInterface, address, prefixLength);
|
||||||
if (mNetworkAgent != null) {
|
mNetworkAgent.sendLinkProperties(makeLinkProperties());
|
||||||
mNetworkAgent.sendLinkProperties(makeLinkProperties());
|
|
||||||
}
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized boolean setUnderlyingNetworks(Network[] networks) {
|
||||||
|
if (!isCallerEstablishedOwnerLocked()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (networks == null) {
|
||||||
|
mConfig.underlyingNetworks = null;
|
||||||
|
} else {
|
||||||
|
mConfig.underlyingNetworks = new Network[networks.length];
|
||||||
|
for (int i = 0; i < networks.length; ++i) {
|
||||||
|
if (networks[i] == null) {
|
||||||
|
mConfig.underlyingNetworks[i] = null;
|
||||||
|
} else {
|
||||||
|
mConfig.underlyingNetworks[i] = new Network(networks[i].netId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized Network[] getUnderlyingNetworks() {
|
||||||
|
if (!isRunningLocked()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return mConfig.underlyingNetworks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean appliesToUid(int uid) {
|
||||||
|
if (!isRunningLocked()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (UidRange uidRange : mVpnUsers) {
|
||||||
|
if (uidRange.start <= uid && uid <= uidRange.stop) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private native int jniCreate(int mtu);
|
private native int jniCreate(int mtu);
|
||||||
private native String jniGetName(int tun);
|
private native String jniGetName(int tun);
|
||||||
private native int jniSetAddresses(String interfaze, String addresses);
|
private native int jniSetAddresses(String interfaze, String addresses);
|
||||||
|
|||||||
Reference in New Issue
Block a user