Combine network subtypes by default.

Subtype controls (3G-vs-4G) aren't exposed in the UI, so tracking
data with that granularity creates unnecessary overhead. For example,
some GSM networks can regularly flap between two subtypes.

Bug: 6118868
Change-Id: Id098891dba52336d00d0f96632a7924e228b4713
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 1a74abf..ee12989 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -31,6 +31,14 @@
  * @hide
  */
 public class NetworkIdentity {
+    /**
+     * When enabled, combine all {@link #mSubType} together under
+     * {@link #SUBTYPE_COMBINED}.
+     */
+    public static final boolean COMBINE_SUBTYPE_ENABLED = true;
+
+    public static final int SUBTYPE_COMBINED = -1;
+
     final int mType;
     final int mSubType;
     final String mSubscriberId;
@@ -38,7 +46,7 @@
 
     public NetworkIdentity(int type, int subType, String subscriberId, boolean roaming) {
         this.mType = type;
-        this.mSubType = subType;
+        this.mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType;
         this.mSubscriberId = subscriberId;
         this.mRoaming = roaming;
     }
@@ -52,9 +60,8 @@
     public boolean equals(Object obj) {
         if (obj instanceof NetworkIdentity) {
             final NetworkIdentity ident = (NetworkIdentity) obj;
-            return mType == ident.mType && mSubType == ident.mSubType
-                    && Objects.equal(mSubscriberId, ident.mSubscriberId)
-                    && mRoaming == ident.mRoaming;
+            return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
+                    && Objects.equal(mSubscriberId, ident.mSubscriberId);
         }
         return false;
     }
@@ -63,7 +70,9 @@
     public String toString() {
         final String typeName = ConnectivityManager.getNetworkTypeName(mType);
         final String subTypeName;
-        if (ConnectivityManager.isNetworkTypeMobile(mType)) {
+        if (COMBINE_SUBTYPE_ENABLED) {
+            subTypeName = "COMBINED";
+        } else if (ConnectivityManager.isNetworkTypeMobile(mType)) {
             subTypeName = TelephonyManager.getNetworkTypeName(mSubType);
         } else {
             subTypeName = Integer.toString(mSubType);
@@ -130,5 +139,4 @@
         }
         return new NetworkIdentity(type, subType, subscriberId, roaming);
     }
-
 }
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 8ebfd8d..204ef76 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -20,6 +20,7 @@
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
 import static android.net.NetworkIdentity.scrubSubscriberId;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
@@ -77,6 +78,7 @@
      * uses statistics for requested IMSI.
      */
     public static NetworkTemplate buildTemplateMobile3gLower(String subscriberId) {
+        ensureSubtypeAvailable();
         return new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId);
     }
 
@@ -86,6 +88,7 @@
      * requested IMSI.
      */
     public static NetworkTemplate buildTemplateMobile4g(String subscriberId) {
+        ensureSubtypeAvailable();
         return new NetworkTemplate(MATCH_MOBILE_4G, subscriberId);
     }
 
@@ -199,6 +202,7 @@
      * Check if mobile network classified 3G or lower with matching IMSI.
      */
     private boolean matchesMobile3gLower(NetworkIdentity ident) {
+        ensureSubtypeAvailable();
         if (ident.mType == TYPE_WIMAX) {
             return false;
         } else if (matchesMobile(ident)) {
@@ -216,6 +220,7 @@
      * Check if mobile network classified 4G with matching IMSI.
      */
     private boolean matchesMobile4g(NetworkIdentity ident) {
+        ensureSubtypeAvailable();
         if (ident.mType == TYPE_WIMAX) {
             // TODO: consider matching against WiMAX subscriber identity
             return true;
@@ -268,6 +273,13 @@
         }
     }
 
+    private static void ensureSubtypeAvailable() {
+        if (COMBINE_SUBTYPE_ENABLED) {
+            throw new IllegalArgumentException(
+                    "Unable to enforce 3G_LOWER template on combined data.");
+        }
+    }
+
     public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() {
         public NetworkTemplate createFromParcel(Parcel in) {
             return new NetworkTemplate(in);
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index fd8d411..8796afc 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -26,6 +26,7 @@
 import static android.content.Intent.EXTRA_UID;
 import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.SET_DEFAULT;
@@ -304,7 +305,9 @@
 
         // watch for networkType changes that aren't broadcast through
         // CONNECTIVITY_ACTION_IMMEDIATE above.
-        mTeleManager.listen(mPhoneListener, LISTEN_DATA_CONNECTION_STATE);
+        if (!COMBINE_SUBTYPE_ENABLED) {
+            mTeleManager.listen(mPhoneListener, LISTEN_DATA_CONNECTION_STATE);
+        }
 
         registerPollAlarmLocked();
         registerGlobalAlert();
@@ -325,7 +328,9 @@
         mContext.unregisterReceiver(mRemovedReceiver);
         mContext.unregisterReceiver(mShutdownReceiver);
 
-        mTeleManager.listen(mPhoneListener, LISTEN_NONE);
+        if (!COMBINE_SUBTYPE_ENABLED) {
+            mTeleManager.listen(mPhoneListener, LISTEN_NONE);
+        }
 
         final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
                 : System.currentTimeMillis();