Connectivity: Add capability to allow tethering to use VPN upstreams [3/3]
Squash of:
Author: Sam Mortimer <sam@mortimer.me.uk>
Date: Thu Aug 15 19:40:55 2019 -0700
fw/b: Add capability to allow tethering to use VPN upstreams
* Toggled on/off at runtime via a new hotspot lineage setting.
* Dynamically updates the tethering upstream for existing hotspot
clients as VPNs are brought up / down or the hotspot setting
is changed.
* This implementation depends on fw/b config_tether_upstream_automatic
being set to true.
Change-Id: I2ac0b4acc0ea686dfdf54561cb3428808e337160
Change-Id: I2363e9baa007eac25be04ce23f0de41b80c5da42
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 5022b40..8ee28bd 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -26,6 +26,7 @@
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
@@ -502,6 +503,17 @@
}
startTrackDefaultNetwork();
+
+ // Listen for allowing tethering upstream via VPN settings changes
+ final ContentObserver vpnSettingObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean self) {
+ // Reconsider tethering upstream
+ mTetherMainSM.sendMessage(TetherMainSM.CMD_UPSTREAM_CHANGED);
+ }
+ };
+ mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.TETHERING_ALLOW_VPN_UPSTREAMS), false, vpnSettingObserver);
}
private class TetheringThreadExecutor implements Executor {
@@ -2306,6 +2318,12 @@
}
public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
+ // Disable hw offload on vpn upstream interfaces.
+ // setUpstreamLinkProperties() interprets null as disable.
+ if (ns != null && ns.networkCapabilities != null
+ && !ns.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_VPN)) {
+ ns = null;
+ }
mOffloadController.setUpstreamLinkProperties(
(ns != null) ? ns.linkProperties : null);
}
diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 7a05d74..68e1352 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -37,6 +37,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Handler;
+import android.provider.Settings;
import android.util.Log;
import android.util.SparseIntArray;
@@ -132,6 +133,8 @@
// The current system default network (not really used yet).
private Network mDefaultInternetNetwork;
private boolean mPreferTestNetworks;
+ // Set if the Internet is considered reachable via a VPN network
+ private Network mVpnInternetNetwork;
public UpstreamNetworkMonitor(Context ctx, Handler h, SharedLog log, EventListener listener) {
mContext = ctx;
@@ -186,6 +189,7 @@
releaseCallback(mListenAllCallback);
mListenAllCallback = null;
+ mVpnInternetNetwork = null;
mNetworkMap.clear();
}
@@ -317,6 +321,12 @@
* Returns null if no current upstream is available.
*/
public UpstreamNetworkState getCurrentPreferredUpstream() {
+ // Use VPN upstreams if hotspot settings allow.
+ if (mVpnInternetNetwork != null &&
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.TETHERING_ALLOW_VPN_UPSTREAMS, 0) == 1) {
+ return mNetworkMap.get(mVpnInternetNetwork);
+ }
final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null)
? mNetworkMap.get(mDefaultInternetNetwork)
: null;
@@ -358,6 +368,7 @@
}
private void handleNetCap(Network network, NetworkCapabilities newNc) {
+ if (isVpnInternetNetwork(newNc)) mVpnInternetNetwork = network;
final UpstreamNetworkState prev = mNetworkMap.get(network);
if (prev == null || newNc.equals(prev.networkCapabilities)) {
// Ignore notifications about networks for which we have not yet
@@ -423,6 +434,10 @@
// - deletes the entry from the map only when the LISTEN_ALL
// callback gets notified.
+ if (network.equals(mVpnInternetNetwork)) {
+ mVpnInternetNetwork = null;
+ }
+
if (!mNetworkMap.containsKey(network)) {
// Ignore loss of networks about which we had not previously
// learned any information or for which we have already processed
@@ -636,6 +651,11 @@
&& !isCellular(ns.networkCapabilities);
}
+ private static boolean isVpnInternetNetwork(NetworkCapabilities nc) {
+ return (nc != null) && !nc.hasCapability(NET_CAPABILITY_NOT_VPN) &&
+ nc.hasCapability(NET_CAPABILITY_INTERNET);
+ }
+
private static UpstreamNetworkState findFirstDunNetwork(
Iterable<UpstreamNetworkState> netStates) {
for (UpstreamNetworkState ns : netStates) {