Allow VPNs to specify their underlying networks.

These are used when responding to getActiveNetworkInfo() (and cousins)
when an app is subject to the VPN.

Bug: 17460017
Change-Id: Ief7a840c760777a41d3358aa6b8e4cdd99c29f24
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 03c05ec..6da186f 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -46,6 +46,7 @@
 import android.net.LinkProperties;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
+import android.net.Network;
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
@@ -580,7 +581,13 @@
     }
 
     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.
@@ -595,7 +602,7 @@
 
     // Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
     private void addVpnUserLocked(int userHandle) {
-        if (!isRunningLocked()) {
+        if (mVpnUsers == null) {
             throw new IllegalStateException("VPN is not active");
         }
 
@@ -647,7 +654,7 @@
     }
 
     private void removeVpnUserLocked(int userHandle) {
-        if (!isRunningLocked()) {
+        if (mVpnUsers == null) {
             throw new IllegalStateException("VPN is not active");
         }
         final List<UidRange> ranges = uidRangesForUser(userHandle);
@@ -767,27 +774,61 @@
     }
 
     public synchronized boolean addAddress(String address, int prefixLength) {
-        if (Binder.getCallingUid() != mOwnerUID || mInterface == null || mNetworkAgent == null) {
+        if (!isCallerEstablishedOwnerLocked()) {
             return false;
         }
         boolean success = jniAddAddress(mInterface, address, prefixLength);
-        if (mNetworkAgent != null) {
-            mNetworkAgent.sendLinkProperties(makeLinkProperties());
-        }
+        mNetworkAgent.sendLinkProperties(makeLinkProperties());
         return success;
     }
 
     public synchronized boolean removeAddress(String address, int prefixLength) {
-        if (Binder.getCallingUid() != mOwnerUID || mInterface == null || mNetworkAgent == null) {
+        if (!isCallerEstablishedOwnerLocked()) {
             return false;
         }
         boolean success = jniDelAddress(mInterface, address, prefixLength);
-        if (mNetworkAgent != null) {
-            mNetworkAgent.sendLinkProperties(makeLinkProperties());
-        }
+        mNetworkAgent.sendLinkProperties(makeLinkProperties());
         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 String jniGetName(int tun);
     private native int jniSetAddresses(String interfaze, String addresses);