Hack and ship: NetworkStats edition.

Some devices use clatd for catching raw IPv4 traffic when running on
a pure-IPv6 carrier network.  In those situations, the per-UID
stats are accounted against the clat iface, so framework users need
to combine both the "base" and "stacked" iface usage together.

This also means that policy rules (like restricting background data
or battery saver) need to apply to the stacked ifaces.

Finally, we need to massage stats data slightly:

-- Currently xt_qtaguid double-counts the clatd traffic *leaving*
the device; both against the original UID on the clat iface, and
against UID 0 on the final egress interface.

-- All clatd traffic *arriving* at the device is missing the extra
IPv6 packet header overhead when accounted against the final UID.

Bug: 12249687, 15459248, 16296564
Change-Id: I0ee59d96831f52782de7a980e4cce9b061902fff
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0da1fa4..ad2bb92 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -38,8 +38,8 @@
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
-import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE;
+import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
@@ -96,6 +96,7 @@
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
+import android.net.LinkProperties;
 import android.net.NetworkIdentity;
 import android.net.NetworkInfo;
 import android.net.NetworkPolicy;
@@ -127,6 +128,7 @@
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.NtpTrustedTime;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -142,6 +144,8 @@
 import com.android.server.SystemConfig;
 import com.google.android.collect.Lists;
 
+import libcore.io.IoUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -158,8 +162,6 @@
 import java.util.List;
 import java.util.Objects;
 
-import libcore.io.IoUtils;
-
 /**
  * Service that maintains low-level network policy rules, using
  * {@link NetworkStatsService} statistics to drive those rules.
@@ -1049,36 +1051,44 @@
         // use over those networks can have a cost associated with it).
         final boolean powerSave = mRestrictPower && !mRestrictBackground;
 
-        // first, derive identity for all connected networks, which can be used
-        // to match against templates.
-        final ArrayMap<NetworkIdentity, String> networks = new ArrayMap<NetworkIdentity,
-                String>(states.length);
+        // First, generate identities of all connected networks so we can
+        // quickly compare them against all defined policies below.
+        final ArrayList<Pair<String, NetworkIdentity>> connIdents = new ArrayList<>(states.length);
         final ArraySet<String> connIfaces = new ArraySet<String>(states.length);
         for (NetworkState state : states) {
-            // stash identity and iface away for later use
             if (state.networkInfo.isConnected()) {
-                final String iface = state.linkProperties.getInterfaceName();
                 final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
-                networks.put(ident, iface);
+
+                final String baseIface = state.linkProperties.getInterfaceName();
+                connIdents.add(Pair.create(baseIface, ident));
                 if (powerSave) {
-                    connIfaces.add(iface);
+                    connIfaces.add(baseIface);
+                }
+
+                // Stacked interfaces are considered to have same identity as
+                // their parent network.
+                final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
+                for (LinkProperties stackedLink : stackedLinks) {
+                    final String stackedIface = stackedLink.getInterfaceName();
+                    connIdents.add(Pair.create(stackedIface, ident));
+                    if (powerSave) {
+                        connIfaces.add(stackedIface);
+                    }
                 }
             }
         }
 
-        // build list of rules and ifaces to enforce them against
+        // Apply policies against all connected interfaces found above
         mNetworkRules.clear();
         final ArrayList<String> ifaceList = Lists.newArrayList();
-        for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+        for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
             final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
 
-            // collect all active ifaces that match this template
             ifaceList.clear();
-            for (int j = networks.size()-1; j >= 0; j--) {
-                final NetworkIdentity ident = networks.keyAt(j);
-                if (policy.template.matches(ident)) {
-                    final String iface = networks.valueAt(j);
-                    ifaceList.add(iface);
+            for (int j = connIdents.size() - 1; j >= 0; j--) {
+                final Pair<String, NetworkIdentity> ident = connIdents.get(j);
+                if (policy.template.matches(ident.second)) {
+                    ifaceList.add(ident.first);
                 }
             }
 
@@ -1787,6 +1797,8 @@
             }
             fout.decreaseIndent();
 
+            fout.print("Metered ifaces: "); fout.println(String.valueOf(mMeteredIfaces));
+
             fout.println("Policy for UIDs:");
             fout.increaseIndent();
             int size = mUidPolicy.size();