From 3bd888f411149bb781c5a8da8b1b8b63fe8353ba Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 4 Apr 2012 20:40:58 -0700 Subject: [PATCH] Support metered Wi-Fi NetworkPolicy. Add networkId field to NetworkIdentity to identify Wi-Fi networks by SSID. Add support for policies without usage cycles. Only apply mobile policies when SIM state is ready, which is cleaner than just checking for airplane mode. Also avoids creating no-op default policies when subscriberId is null. Bug: 3001465, 3291052 Change-Id: I1f8aaa49a5db306df022c402ea7f3f5d4bc0cfc7 --- core/java/android/net/NetworkIdentity.java | 74 ++++++++++----- core/java/android/net/NetworkTemplate.java | 95 ++++++++++++------- .../internal/net/NetworkStatsFactory.java | 8 +- .../server/net/NetworkIdentitySet.java | 47 +++++---- .../server/net/NetworkStatsService.java | 4 +- 5 files changed, 139 insertions(+), 89 deletions(-) diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index ee12989a00..4ac5e76472 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -16,9 +16,13 @@ package android.net; +import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeMobile; import android.content.Context; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.os.Build; import android.telephony.TelephonyManager; @@ -42,18 +46,21 @@ public class NetworkIdentity { final int mType; final int mSubType; final String mSubscriberId; + final String mNetworkId; final boolean mRoaming; - public NetworkIdentity(int type, int subType, String subscriberId, boolean roaming) { - this.mType = type; - this.mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType; - this.mSubscriberId = subscriberId; - this.mRoaming = roaming; + public NetworkIdentity( + int type, int subType, String subscriberId, String networkId, boolean roaming) { + mType = type; + mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType; + mSubscriberId = subscriberId; + mNetworkId = networkId; + mRoaming = roaming; } @Override public int hashCode() { - return Objects.hashCode(mType, mSubType, mSubscriberId, mRoaming); + return Objects.hashCode(mType, mSubType, mSubscriberId, mNetworkId, mRoaming); } @Override @@ -61,27 +68,34 @@ public class NetworkIdentity { if (obj instanceof NetworkIdentity) { final NetworkIdentity ident = (NetworkIdentity) obj; return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming - && Objects.equal(mSubscriberId, ident.mSubscriberId); + && Objects.equal(mSubscriberId, ident.mSubscriberId) + && Objects.equal(mNetworkId, ident.mNetworkId); } return false; } @Override public String toString() { - final String typeName = ConnectivityManager.getNetworkTypeName(mType); - final String subTypeName; + final StringBuilder builder = new StringBuilder("["); + builder.append("type=").append(getNetworkTypeName(mType)); + builder.append(", subType="); if (COMBINE_SUBTYPE_ENABLED) { - subTypeName = "COMBINED"; + builder.append("COMBINED"); } else if (ConnectivityManager.isNetworkTypeMobile(mType)) { - subTypeName = TelephonyManager.getNetworkTypeName(mSubType); + builder.append(TelephonyManager.getNetworkTypeName(mSubType)); } else { - subTypeName = Integer.toString(mSubType); + builder.append(mSubType); } - - final String scrubSubscriberId = scrubSubscriberId(mSubscriberId); - final String roaming = mRoaming ? ", ROAMING" : ""; - return "[type=" + typeName + ", subType=" + subTypeName + ", subscriberId=" - + scrubSubscriberId + roaming + "]"; + if (mSubscriberId != null) { + builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId)); + } + if (mNetworkId != null) { + builder.append(", networkId=").append(mNetworkId); + } + if (mRoaming) { + builder.append(", ROAMING"); + } + return builder.append("]").toString(); } public int getType() { @@ -96,6 +110,10 @@ public class NetworkIdentity { return mSubscriberId; } + public String getNetworkId() { + return mNetworkId; + } + public boolean getRoaming() { return mRoaming; } @@ -106,8 +124,11 @@ public class NetworkIdentity { public static String scrubSubscriberId(String subscriberId) { if ("eng".equals(Build.TYPE)) { return subscriberId; + } else if (subscriberId != null) { + // TODO: parse this as MCC+MNC instead of hard-coding + return subscriberId.substring(0, Math.min(6, subscriberId.length())) + "..."; } else { - return subscriberId != null ? "valid" : "null"; + return "null"; } } @@ -122,8 +143,10 @@ public class NetworkIdentity { // TODO: consider moving subscriberId over to LinkCapabilities, so it // comes from an authoritative source. - final String subscriberId; - final boolean roaming; + String subscriberId = null; + String networkId = null; + boolean roaming = false; + if (isNetworkTypeMobile(type)) { final TelephonyManager telephony = (TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE); @@ -133,10 +156,13 @@ public class NetworkIdentity { } else { subscriberId = telephony.getSubscriberId(); } - } else { - subscriberId = null; - roaming = false; + + } else if (type == TYPE_WIFI) { + final WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + final WifiInfo info = wifi.getConnectionInfo(); + networkId = info != null ? info.getSSID() : null; } - return new NetworkIdentity(type, subType, subscriberId, roaming); + + return new NetworkIdentity(type, subType, subscriberId, networkId, roaming); } } diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index e1fbdcc705..50432a14d0 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -43,15 +43,10 @@ import com.android.internal.util.Objects; */ public class NetworkTemplate implements Parcelable { - /** {@hide} */ public static final int MATCH_MOBILE_ALL = 1; - /** {@hide} */ public static final int MATCH_MOBILE_3G_LOWER = 2; - /** {@hide} */ public static final int MATCH_MOBILE_4G = 3; - /** {@hide} */ public static final int MATCH_WIFI = 4; - /** {@hide} */ public static final int MATCH_ETHERNET = 5; /** @@ -65,37 +60,50 @@ public class NetworkTemplate implements Parcelable { } /** - * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style - * networks together. Only uses statistics for requested IMSI. + * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with + * the given IMSI. */ public static NetworkTemplate buildTemplateMobileAll(String subscriberId) { - return new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId); + return new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId, null); } /** - * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style - * networks together that roughly meet a "3G" definition, or lower. Only - * uses statistics for requested IMSI. + * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with + * the given IMSI that roughly meet a "3G" definition, or lower. */ + @Deprecated public static NetworkTemplate buildTemplateMobile3gLower(String subscriberId) { - return new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId); + return new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId, null); } /** - * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style - * networks together that meet a "4G" definition. Only uses statistics for - * requested IMSI. + * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with + * the given IMSI that roughly meet a "4G" definition. */ + @Deprecated public static NetworkTemplate buildTemplateMobile4g(String subscriberId) { - return new NetworkTemplate(MATCH_MOBILE_4G, subscriberId); + return new NetworkTemplate(MATCH_MOBILE_4G, subscriberId, null); } /** - * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style - * networks together. + * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks, + * regardless of SSID. */ + public static NetworkTemplate buildTemplateWifiWildcard() { + return new NetworkTemplate(MATCH_WIFI, null, null); + } + + @Deprecated public static NetworkTemplate buildTemplateWifi() { - return new NetworkTemplate(MATCH_WIFI, null); + return buildTemplateWifiWildcard(); + } + + /** + * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the + * given SSID. + */ + public static NetworkTemplate buildTemplateWifi(String networkId) { + return new NetworkTemplate(MATCH_WIFI, null, networkId); } /** @@ -103,44 +111,53 @@ public class NetworkTemplate implements Parcelable { * networks together. */ public static NetworkTemplate buildTemplateEthernet() { - return new NetworkTemplate(MATCH_ETHERNET, null); + return new NetworkTemplate(MATCH_ETHERNET, null, null); } private final int mMatchRule; private final String mSubscriberId; + private final String mNetworkId; - /** {@hide} */ - public NetworkTemplate(int matchRule, String subscriberId) { - this.mMatchRule = matchRule; - this.mSubscriberId = subscriberId; + public NetworkTemplate(int matchRule, String subscriberId, String networkId) { + mMatchRule = matchRule; + mSubscriberId = subscriberId; + mNetworkId = networkId; } private NetworkTemplate(Parcel in) { mMatchRule = in.readInt(); mSubscriberId = in.readString(); + mNetworkId = in.readString(); } - /** {@inheritDoc} */ + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mMatchRule); dest.writeString(mSubscriberId); + dest.writeString(mNetworkId); } - /** {@inheritDoc} */ + @Override public int describeContents() { return 0; } @Override public String toString() { - final String scrubSubscriberId = scrubSubscriberId(mSubscriberId); - return "NetworkTemplate: matchRule=" + getMatchRuleName(mMatchRule) + ", subscriberId=" - + scrubSubscriberId; + final StringBuilder builder = new StringBuilder("NetworkTemplate: "); + builder.append("matchRule=").append(getMatchRuleName(mMatchRule)); + if (mSubscriberId != null) { + builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId)); + } + if (mNetworkId != null) { + builder.append(", networkId=").append(mNetworkId); + } + return builder.toString(); } @Override public int hashCode() { - return Objects.hashCode(mMatchRule, mSubscriberId); + return Objects.hashCode(mMatchRule, mSubscriberId, mNetworkId); } @Override @@ -148,21 +165,24 @@ public class NetworkTemplate implements Parcelable { if (obj instanceof NetworkTemplate) { final NetworkTemplate other = (NetworkTemplate) obj; return mMatchRule == other.mMatchRule - && Objects.equal(mSubscriberId, other.mSubscriberId); + && Objects.equal(mSubscriberId, other.mSubscriberId) + && Objects.equal(mNetworkId, other.mNetworkId); } return false; } - /** {@hide} */ public int getMatchRule() { return mMatchRule; } - /** {@hide} */ public String getSubscriberId() { return mSubscriberId; } + public String getNetworkId() { + return mNetworkId; + } + /** * Test if given {@link NetworkIdentity} matches this template. */ @@ -237,8 +257,13 @@ public class NetworkTemplate implements Parcelable { private boolean matchesWifi(NetworkIdentity ident) { switch (ident.mType) { case TYPE_WIFI: + if (mNetworkId == null) { + return true; + } else { + return Objects.equal(mNetworkId, ident.mNetworkId); + } case TYPE_WIFI_P2P: - return true; + return mNetworkId == null; default: return false; } @@ -279,10 +304,12 @@ public class NetworkTemplate implements Parcelable { } public static final Creator CREATOR = new Creator() { + @Override public NetworkTemplate createFromParcel(Parcel in) { return new NetworkTemplate(in); } + @Override public NetworkTemplate[] newArray(int size) { return new NetworkTemplate[size]; } diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java index ccd27632b9..d59585f2fe 100644 --- a/core/java/com/android/internal/net/NetworkStatsFactory.java +++ b/core/java/com/android/internal/net/NetworkStatsFactory.java @@ -16,7 +16,7 @@ package com.android.internal.net; -import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static com.android.server.NetworkManagementSocketTagger.kernelToTag; @@ -101,7 +101,7 @@ public class NetworkStatsFactory { while (reader.hasMoreData()) { entry.iface = reader.nextString(); entry.uid = UID_ALL; - entry.set = SET_DEFAULT; + entry.set = SET_ALL; entry.tag = TAG_NONE; final boolean active = reader.nextInt() != 0; @@ -165,7 +165,7 @@ public class NetworkStatsFactory { entry.iface = iface; entry.uid = UID_ALL; - entry.set = SET_DEFAULT; + entry.set = SET_ALL; entry.tag = TAG_NONE; entry.rxBytes = readSingleLongFromFile(new File(ifacePath, "rx_bytes")); entry.rxPackets = readSingleLongFromFile(new File(ifacePath, "rx_packets")); @@ -193,7 +193,7 @@ public class NetworkStatsFactory { try { entry.iface = values.get(0); entry.uid = UID_ALL; - entry.set = SET_DEFAULT; + entry.set = SET_ALL; entry.tag = TAG_NONE; entry.rxBytes = Long.parseLong(values.get(1)); entry.rxPackets = Long.parseLong(values.get(2)); diff --git a/services/java/com/android/server/net/NetworkIdentitySet.java b/services/java/com/android/server/net/NetworkIdentitySet.java index af03fb3182..397f9f41d8 100644 --- a/services/java/com/android/server/net/NetworkIdentitySet.java +++ b/services/java/com/android/server/net/NetworkIdentitySet.java @@ -21,7 +21,6 @@ import android.net.NetworkIdentity; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.net.ProtocolException; import java.util.HashSet; /** @@ -33,48 +32,46 @@ import java.util.HashSet; public class NetworkIdentitySet extends HashSet { private static final int VERSION_INIT = 1; private static final int VERSION_ADD_ROAMING = 2; + private static final int VERSION_ADD_NETWORK_ID = 3; public NetworkIdentitySet() { } public NetworkIdentitySet(DataInputStream in) throws IOException { final int version = in.readInt(); - switch (version) { - case VERSION_INIT: { - final int size = in.readInt(); - for (int i = 0; i < size; i++) { - final int ignoredVersion = in.readInt(); - final int type = in.readInt(); - final int subType = in.readInt(); - final String subscriberId = readOptionalString(in); - add(new NetworkIdentity(type, subType, subscriberId, false)); - } - break; + final int size = in.readInt(); + for (int i = 0; i < size; i++) { + if (version <= VERSION_INIT) { + final int ignored = in.readInt(); } - case VERSION_ADD_ROAMING: { - final int size = in.readInt(); - for (int i = 0; i < size; i++) { - final int type = in.readInt(); - final int subType = in.readInt(); - final String subscriberId = readOptionalString(in); - final boolean roaming = in.readBoolean(); - add(new NetworkIdentity(type, subType, subscriberId, roaming)); - } - break; + final int type = in.readInt(); + final int subType = in.readInt(); + final String subscriberId = readOptionalString(in); + final String networkId; + if (version >= VERSION_ADD_NETWORK_ID) { + networkId = readOptionalString(in); + } else { + networkId = null; } - default: { - throw new ProtocolException("unexpected version: " + version); + final boolean roaming; + if (version >= VERSION_ADD_ROAMING) { + roaming = in.readBoolean(); + } else { + roaming = false; } + + add(new NetworkIdentity(type, subType, subscriberId, networkId, false)); } } public void writeToStream(DataOutputStream out) throws IOException { - out.writeInt(VERSION_ADD_ROAMING); + out.writeInt(VERSION_ADD_NETWORK_ID); out.writeInt(size()); for (NetworkIdentity ident : this) { out.writeInt(ident.getType()); out.writeInt(ident.getSubType()); writeOptionalString(out, ident.getSubscriberId()); + writeOptionalString(out, ident.getNetworkId()); out.writeBoolean(ident.getRoaming()); } } diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 8796afc06a..b847673d4e 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -34,7 +34,7 @@ import static android.net.NetworkStats.SET_FOREGROUND; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkTemplate.buildTemplateMobileAll; -import static android.net.NetworkTemplate.buildTemplateWifi; +import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.TrafficStats.MB_IN_BYTES; import static android.provider.Settings.Secure.NETSTATS_DEV_BUCKET_DURATION; import static android.provider.Settings.Secure.NETSTATS_DEV_DELETE_AGE; @@ -836,7 +836,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { trustedTime); // collect wifi sample - template = buildTemplateWifi(); + template = buildTemplateWifiWildcard(); devTotal = mDevRecorder.getTotalSinceBootLocked(template); xtTotal = new NetworkStats.Entry(); uidTotal = mUidRecorder.getTotalSinceBootLocked(template);