Tethering and Data Saver: There Can Be Only One!

If tethering is on when Data Saver mode is turned on, tethering should
be disabled, and vice-versa.

BUG: 28313291
Change-Id: I7f3dadbd97ea1492ab76b41043dea7487c1db02d
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index a57fac3..2b8b28d 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -52,6 +52,9 @@
     void setRestrictBackground(boolean restrictBackground);
     boolean getRestrictBackground();
 
+    /** Callback used to change internal state on tethering */
+    void onTetheringChanged(String iface, boolean tethering);
+
     /** Control which applications can be exempt from background data restrictions */
     void addRestrictBackgroundWhitelistedUid(int uid);
     void removeRestrictBackgroundWhitelistedUid(int uid);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f06583b..0287332 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -31,7 +31,6 @@
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
-
 import android.annotation.Nullable;
 import android.app.BroadcastOptions;
 import android.app.Notification;
@@ -1382,6 +1381,10 @@
             if (LOGD_RULES) {
                 log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
             }
+            if (restrictBackground) {
+                log("onRestrictBackgroundChanged(true): disabling tethering");
+                mTethering.untetherAll();
+            }
         }
 
         @Override
@@ -1813,6 +1816,14 @@
         pw.decreaseIndent();
         pw.println();
 
+        pw.println("Metered Interfaces:");
+        pw.increaseIndent();
+        for (String value : mMeteredIfaces) {
+            pw.println(value);
+        }
+        pw.decreaseIndent();
+        pw.println();
+
         pw.println("Network Requests:");
         pw.increaseIndent();
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
@@ -2568,7 +2579,14 @@
     public int tether(String iface) {
         ConnectivityManager.enforceTetherChangePermission(mContext);
         if (isTetheringSupported()) {
-            return mTethering.tether(iface);
+            final int status = mTethering.tether(iface);
+            if (status == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                try {
+                    mPolicyManager.onTetheringChanged(iface, true);
+                } catch (RemoteException e) {
+                }
+            }
+            return status;
         } else {
             return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
         }
@@ -2579,7 +2597,14 @@
         ConnectivityManager.enforceTetherChangePermission(mContext);
 
         if (isTetheringSupported()) {
-            return mTethering.untether(iface);
+            final int status = mTethering.untether(iface);
+            if (status == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                try {
+                    mPolicyManager.onTetheringChanged(iface, false);
+                } catch (RemoteException e) {
+                }
+            }
+            return status;
         } else {
             return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
         }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index c2022d5..1012f9a 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -610,6 +610,13 @@
         return ConnectivityManager.TETHER_ERROR_NO_ERROR;
     }
 
+    public void untetherAll() {
+        if (DBG) Log.d(TAG, "Untethering " + mIfaces);
+        for (String iface : mIfaces.keySet()) {
+            untether(iface);
+        }
+    }
+
     public int getLastTetherError(String iface) {
         TetherInterfaceSM sm = null;
         synchronized (mPublicSync) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 43f47fa..0b1ece5 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1899,6 +1899,18 @@
     }
 
     @Override
+    public void onTetheringChanged(String iface, boolean tethering) {
+        // No need to enforce permission because setRestrictBackground() will do it.
+        if (LOGD) Log.d(TAG, "onTetherStateChanged(" + iface + ", " + tethering + ")");
+        synchronized (mRulesLock) {
+            if (mRestrictBackground && tethering) {
+                Log.d(TAG, "Tethering on (" + iface +"); disable Data Saver");
+                setRestrictBackground(false);
+            }
+        }
+    }
+
+    @Override
     public void setRestrictBackground(boolean restrictBackground) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
         final long token = Binder.clearCallingIdentity();