diff options
19 files changed, 858 insertions, 576 deletions
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 288112a..6371500 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -18,18 +18,19 @@ package android.net; import android.net.NetworkStats; import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; /** {@hide} */ interface INetworkStatsService { /** Return historical stats for traffic that matches template. */ - NetworkStatsHistory getHistoryForNetwork(int networkTemplate); + NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template); /** Return historical stats for specific UID traffic that matches template. */ - NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate); + NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid); /** Return usage summary for traffic that matches template. */ - NetworkStats getSummaryForNetwork(long start, long end, int networkTemplate, String subscriberId); + NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end); /** Return usage summary per UID for traffic that matches template. */ - NetworkStats getSummaryForAllUid(long start, long end, int networkTemplate); + NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end); } diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java new file mode 100644 index 0000000..f82d922 --- /dev/null +++ b/core/java/android/net/NetworkIdentity.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static android.net.ConnectivityManager.isNetworkTypeMobile; + +import android.content.Context; +import android.telephony.TelephonyManager; + +import com.android.internal.util.Objects; + +/** + * Network definition that includes strong identity. Analogous to combining + * {@link NetworkInfo} and an IMSI. + * + * @hide + */ +public class NetworkIdentity { + final int mType; + final int mSubType; + final String mSubscriberId; + + public NetworkIdentity(int type, int subType, String subscriberId) { + this.mType = type; + this.mSubType = subType; + this.mSubscriberId = subscriberId; + } + + @Override + public int hashCode() { + return Objects.hashCode(mType, mSubType, mSubscriberId); + } + + @Override + 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); + } + return false; + } + + @Override + public String toString() { + final String typeName = ConnectivityManager.getNetworkTypeName(mType); + final String subTypeName; + if (ConnectivityManager.isNetworkTypeMobile(mType)) { + subTypeName = TelephonyManager.getNetworkTypeName(mSubType); + } else { + subTypeName = Integer.toString(mSubType); + } + + final String scrubSubscriberId = mSubscriberId != null ? "valid" : "null"; + return "[type=" + typeName + ", subType=" + subTypeName + ", subscriberId=" + + scrubSubscriberId + "]"; + } + + public int getType() { + return mType; + } + + public int getSubType() { + return mSubType; + } + + public String getSubscriberId() { + return mSubscriberId; + } + + /** + * Build a {@link NetworkIdentity} from the given {@link NetworkState}, + * assuming that any mobile networks are using the current IMSI. + */ + public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) { + final int type = state.networkInfo.getType(); + final int subType = state.networkInfo.getSubtype(); + + // TODO: consider moving subscriberId over to LinkCapabilities, so it + // comes from an authoritative source. + + final String subscriberId; + if (isNetworkTypeMobile(type)) { + final TelephonyManager telephony = (TelephonyManager) context.getSystemService( + Context.TELEPHONY_SERVICE); + subscriberId = telephony.getSubscriberId(); + } else { + subscriberId = null; + } + return new NetworkIdentity(type, subType, subscriberId); + } + +} diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index 1899281..52cab30 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -16,37 +16,38 @@ package android.net; +import static com.android.internal.util.Preconditions.checkNotNull; + import android.os.Parcel; import android.os.Parcelable; /** - * Policy for a specific network, including usage cycle and limits to be - * enforced. + * Policy for networks matching a {@link NetworkTemplate}, including usage cycle + * and limits to be enforced. * * @hide */ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { - public final int networkTemplate; - public final String subscriberId; + public static final long WARNING_DISABLED = -1; + public static final long LIMIT_DISABLED = -1; + + public final NetworkTemplate template; public int cycleDay; public long warningBytes; public long limitBytes; - public static final long WARNING_DISABLED = -1; - public static final long LIMIT_DISABLED = -1; + // TODO: teach how to snooze limit for current cycle - public NetworkPolicy(int networkTemplate, String subscriberId, int cycleDay, long warningBytes, - long limitBytes) { - this.networkTemplate = networkTemplate; - this.subscriberId = subscriberId; + public NetworkPolicy( + NetworkTemplate template, int cycleDay, long warningBytes, long limitBytes) { + this.template = checkNotNull(template, "missing NetworkTemplate"); this.cycleDay = cycleDay; this.warningBytes = warningBytes; this.limitBytes = limitBytes; } public NetworkPolicy(Parcel in) { - networkTemplate = in.readInt(); - subscriberId = in.readString(); + template = in.readParcelable(null); cycleDay = in.readInt(); warningBytes = in.readLong(); limitBytes = in.readLong(); @@ -54,8 +55,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { /** {@inheritDoc} */ public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(networkTemplate); - dest.writeString(subscriberId); + dest.writeParcelable(template, flags); dest.writeInt(cycleDay); dest.writeLong(warningBytes); dest.writeLong(limitBytes); @@ -81,8 +81,8 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { @Override public String toString() { - return "NetworkPolicy: networkTemplate=" + networkTemplate + ", cycleDay=" + cycleDay - + ", warningBytes=" + warningBytes + ", limitBytes=" + limitBytes; + return "NetworkPolicy[" + template + "]: cycleDay=" + cycleDay + ", warningBytes=" + + warningBytes + ", limitBytes=" + limitBytes; } public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() { diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index bfea168..91af16d 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -59,7 +59,7 @@ public class NetworkPolicyManager { /** * {@link Intent} extra included in {@link #ACTION_DATA_USAGE_WARNING} and * {@link #ACTION_DATA_USAGE_LIMIT} to indicate which - * {@link NetworkPolicy#networkTemplate} it applies to. + * {@link NetworkTemplate} rule it applies to. */ public static final String EXTRA_NETWORK_TEMPLATE = "android.intent.extra.NETWORK_TEMPLATE"; diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 60f740e..9d40c42 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -40,9 +40,8 @@ public class NetworkStats implements Parcelable { public static final String IFACE_ALL = null; /** {@link #uid} value when UID details unavailable. */ public static final int UID_ALL = -1; - - // NOTE: data should only be accounted for once in this structure; if data - // is broken out, the summarized version should not be included. + /** {@link #tag} value for without tag. */ + public static final int TAG_NONE = 0; /** * {@link SystemClock#elapsedRealtime()} timestamp when this data was @@ -52,16 +51,16 @@ public class NetworkStats implements Parcelable { public int size; public String[] iface; public int[] uid; + public int[] tag; public long[] rx; public long[] tx; - // TODO: add fg/bg stats once reported by kernel - public NetworkStats(long elapsedRealtime, int initialSize) { this.elapsedRealtime = elapsedRealtime; this.size = 0; this.iface = new String[initialSize]; this.uid = new int[initialSize]; + this.tag = new int[initialSize]; this.rx = new long[initialSize]; this.tx = new long[initialSize]; } @@ -71,21 +70,27 @@ public class NetworkStats implements Parcelable { size = parcel.readInt(); iface = parcel.createStringArray(); uid = parcel.createIntArray(); + tag = parcel.createIntArray(); rx = parcel.createLongArray(); tx = parcel.createLongArray(); } - public NetworkStats addEntry(String iface, int uid, long rx, long tx) { + /** + * Add new stats entry with given values. + */ + public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) { if (size >= this.iface.length) { final int newLength = Math.max(this.iface.length, 10) * 3 / 2; this.iface = Arrays.copyOf(this.iface, newLength); this.uid = Arrays.copyOf(this.uid, newLength); + this.tag = Arrays.copyOf(this.tag, newLength); this.rx = Arrays.copyOf(this.rx, newLength); this.tx = Arrays.copyOf(this.tx, newLength); } this.iface[size] = iface; this.uid[size] = uid; + this.tag[size] = tag; this.rx[size] = rx; this.tx[size] = tx; size++; @@ -93,17 +98,29 @@ public class NetworkStats implements Parcelable { return this; } - @Deprecated - public int length() { - return size; + /** + * Combine given values with an existing row, or create a new row if + * {@link #findIndex(String, int, int)} is unable to find match. Can also be + * used to subtract values from existing rows. + */ + public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) { + final int i = findIndex(iface, uid, tag); + if (i == -1) { + // only create new entry when positive contribution + addEntry(iface, uid, tag, rx, tx); + } else { + this.rx[i] += rx; + this.tx[i] += tx; + } + return this; } /** * Find first stats index that matches the requested parameters. */ - public int findIndex(String iface, int uid) { + public int findIndex(String iface, int uid, int tag) { for (int i = 0; i < size; i++) { - if (equal(iface, this.iface[i]) && uid == this.uid[i]) { + if (equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) { return i; } } @@ -186,12 +203,13 @@ public class NetworkStats implements Parcelable { for (int i = 0; i < size; i++) { final String iface = this.iface[i]; final int uid = this.uid[i]; + final int tag = this.tag[i]; // find remote row that matches, and subtract - final int j = value.findIndex(iface, uid); + final int j = value.findIndex(iface, uid, tag); if (j == -1) { // newly appearing row, return entire value - result.addEntry(iface, uid, this.rx[i], this.tx[i]); + result.addEntry(iface, uid, tag, this.rx[i], this.tx[i]); } else { // existing row, subtract remote value long rx = this.rx[i] - value.rx[j]; @@ -203,7 +221,7 @@ public class NetworkStats implements Parcelable { rx = Math.max(0, rx); tx = Math.max(0, tx); } - result.addEntry(iface, uid, rx, tx); + result.addEntry(iface, uid, tag, rx, tx); } } @@ -221,6 +239,7 @@ public class NetworkStats implements Parcelable { pw.print(prefix); pw.print(" iface="); pw.print(iface[i]); pw.print(" uid="); pw.print(uid[i]); + pw.print(" tag="); pw.print(tag[i]); pw.print(" rx="); pw.print(rx[i]); pw.print(" tx="); pw.println(tx[i]); } @@ -244,6 +263,7 @@ public class NetworkStats implements Parcelable { dest.writeInt(size); dest.writeStringArray(iface); dest.writeIntArray(uid); + dest.writeIntArray(tag); dest.writeLongArray(rx); dest.writeLongArray(tx); } diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index 5fa8e21..ff6e220 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -40,10 +40,9 @@ import java.util.Random; * @hide */ public class NetworkStatsHistory implements Parcelable { - private static final int VERSION_CURRENT = 1; + private static final int VERSION_INIT = 1; - // TODO: teach about zigzag encoding to use less disk space - // TODO: teach how to convert between bucket sizes + // TODO: teach about varint encoding to use less disk space public final long bucketDuration; @@ -83,7 +82,7 @@ public class NetworkStatsHistory implements Parcelable { public NetworkStatsHistory(DataInputStream in) throws IOException { final int version = in.readInt(); switch (version) { - case VERSION_CURRENT: { + case VERSION_INIT: { bucketDuration = in.readLong(); bucketStart = readLongArray(in); rx = readLongArray(in); @@ -98,7 +97,7 @@ public class NetworkStatsHistory implements Parcelable { } public void writeToStream(DataOutputStream out) throws IOException { - out.writeInt(VERSION_CURRENT); + out.writeInt(VERSION_INIT); out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); writeLongArray(out, rx, bucketCount); @@ -115,6 +114,11 @@ public class NetworkStatsHistory implements Parcelable { * distribute across internal buckets, creating new buckets as needed. */ public void recordData(long start, long end, long rx, long tx) { + if (rx < 0 || tx < 0) { + throw new IllegalArgumentException( + "tried recording negative data: rx=" + rx + ", tx=" + tx); + } + // create any buckets needed by this range ensureBuckets(start, end); diff --git a/core/java/android/net/NetworkTemplate.aidl b/core/java/android/net/NetworkTemplate.aidl new file mode 100644 index 0000000..3d37488 --- /dev/null +++ b/core/java/android/net/NetworkTemplate.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +parcelable NetworkTemplate; diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java new file mode 100644 index 0000000..9381f1d --- /dev/null +++ b/core/java/android/net/NetworkTemplate.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.ConnectivityManager.TYPE_WIMAX; +import static android.net.ConnectivityManager.isNetworkTypeMobile; +import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G; +import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G; +import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G; +import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN; +import static android.telephony.TelephonyManager.getNetworkClass; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Objects; + +/** + * Template definition used to generically match {@link NetworkIdentity}, + * usually when collecting statistics. + * + * @hide + */ +public class NetworkTemplate implements Parcelable { + + /** + * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style + * networks together. Only uses statistics for requested IMSI. + */ + public static final int MATCH_MOBILE_ALL = 1; + + /** + * 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. + */ + public static final int MATCH_MOBILE_3G_LOWER = 2; + + /** + * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style + * networks together that meet a "4G" definition. Only uses statistics for + * requested IMSI. + */ + public static final int MATCH_MOBILE_4G = 3; + + /** + * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style + * networks together. + */ + public static final int MATCH_WIFI = 4; + + final int mMatchRule; + final String mSubscriberId; + + public NetworkTemplate(int matchRule, String subscriberId) { + this.mMatchRule = matchRule; + this.mSubscriberId = subscriberId; + } + + public NetworkTemplate(Parcel in) { + mMatchRule = in.readInt(); + mSubscriberId = in.readString(); + } + + /** {@inheritDoc} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mMatchRule); + dest.writeString(mSubscriberId); + } + + /** {@inheritDoc} */ + public int describeContents() { + return 0; + } + + @Override + public String toString() { + final String scrubSubscriberId = mSubscriberId != null ? "valid" : "null"; + return "NetworkTemplate: matchRule=" + getMatchRuleName(mMatchRule) + ", subscriberId=" + + scrubSubscriberId; + } + + @Override + public int hashCode() { + return Objects.hashCode(mMatchRule, mSubscriberId); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NetworkTemplate) { + final NetworkTemplate other = (NetworkTemplate) obj; + return mMatchRule == other.mMatchRule + && Objects.equal(mSubscriberId, other.mSubscriberId); + } + return false; + } + + public int getMatchRule() { + return mMatchRule; + } + + public String getSubscriberId() { + return mSubscriberId; + } + + /** + * Test if this network matches the given template and IMEI. + */ + public boolean matches(NetworkIdentity ident) { + switch (mMatchRule) { + case MATCH_MOBILE_ALL: + return matchesMobile(ident); + case MATCH_MOBILE_3G_LOWER: + return matchesMobile3gLower(ident); + case MATCH_MOBILE_4G: + return matchesMobile4g(ident); + case MATCH_WIFI: + return matchesWifi(ident); + default: + throw new IllegalArgumentException("unknown network template"); + } + } + + /** + * Check if mobile network with matching IMEI. Also matches + * {@link #TYPE_WIMAX}. + */ + private boolean matchesMobile(NetworkIdentity ident) { + if (isNetworkTypeMobile(ident.mType) && Objects.equal(mSubscriberId, ident.mSubscriberId)) { + return true; + } else if (ident.mType == TYPE_WIMAX) { + return true; + } + return false; + } + + /** + * Check if mobile network classified 3G or lower with matching IMEI. + */ + private boolean matchesMobile3gLower(NetworkIdentity ident) { + if (isNetworkTypeMobile(ident.mType) && Objects.equal(mSubscriberId, ident.mSubscriberId)) { + switch (getNetworkClass(ident.mSubType)) { + case NETWORK_CLASS_UNKNOWN: + case NETWORK_CLASS_2_G: + case NETWORK_CLASS_3_G: + return true; + } + } + return false; + } + + /** + * Check if mobile network classified 4G with matching IMEI. Also matches + * {@link #TYPE_WIMAX}. + */ + private boolean matchesMobile4g(NetworkIdentity ident) { + if (isNetworkTypeMobile(ident.mType) && Objects.equal(mSubscriberId, ident.mSubscriberId)) { + switch (getNetworkClass(ident.mSubType)) { + case NETWORK_CLASS_4_G: + return true; + } + } else if (ident.mType == TYPE_WIMAX) { + return true; + } + return false; + } + + /** + * Check if matches Wi-Fi network template. + */ + private boolean matchesWifi(NetworkIdentity ident) { + if (ident.mType == TYPE_WIFI) { + return true; + } + return false; + } + + public static String getMatchRuleName(int matchRule) { + switch (matchRule) { + case MATCH_MOBILE_3G_LOWER: + return "MOBILE_3G_LOWER"; + case MATCH_MOBILE_4G: + return "MOBILE_4G"; + case MATCH_MOBILE_ALL: + return "MOBILE_ALL"; + case MATCH_WIFI: + return "WIFI"; + default: + return "UNKNOWN"; + } + } + + public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() { + public NetworkTemplate createFromParcel(Parcel in) { + return new NetworkTemplate(in); + } + + public NetworkTemplate[] newArray(int size) { + return new NetworkTemplate[size]; + } + }; +} diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 3725fa6..e163abf 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -42,40 +42,6 @@ public class TrafficStats { public final static int UNSUPPORTED = -1; /** - * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style - * networks together. Only uses statistics for requested IMSI. - * - * @hide - */ - public static final int TEMPLATE_MOBILE_ALL = 1; - - /** - * 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. - * - * @hide - */ - public static final int TEMPLATE_MOBILE_3G_LOWER = 2; - - /** - * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style - * networks together that meet a "4G" definition. Only uses statistics for - * requested IMSI. - * - * @hide - */ - public static final int TEMPLATE_MOBILE_4G = 3; - - /** - * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style - * networks together. - * - * @hide - */ - public static final int TEMPLATE_WIFI = 4; - - /** * Snapshot of {@link NetworkStats} when the currently active profiling * session started, or {@code null} if no session active. * @@ -182,17 +148,6 @@ public class TrafficStats { } } - /** {@hide} */ - public static boolean isNetworkTemplateMobile(int networkTemplate) { - switch (networkTemplate) { - case TEMPLATE_MOBILE_3G_LOWER: - case TEMPLATE_MOBILE_4G: - case TEMPLATE_MOBILE_ALL: - return true; - } - return false; - } - /** * Get the total number of packets transmitted through the mobile interface. * diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java index 5250a7c..3cb64c7 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java @@ -16,6 +16,8 @@ package android.net; +import static android.net.NetworkStats.TAG_NONE; + import android.test.suitebuilder.annotation.SmallTest; import junit.framework.TestCase; @@ -29,14 +31,14 @@ public class NetworkStatsTest extends TestCase { public void testFindIndex() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 3) - .addEntry(TEST_IFACE, 100, 1024, 0) - .addEntry(TEST_IFACE, 101, 0, 1024) - .addEntry(TEST_IFACE, 102, 1024, 1024); - - assertEquals(2, stats.findIndex(TEST_IFACE, 102)); - assertEquals(2, stats.findIndex(TEST_IFACE, 102)); - assertEquals(0, stats.findIndex(TEST_IFACE, 100)); - assertEquals(-1, stats.findIndex(TEST_IFACE, 6)); + .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) + .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L) + .addEntry(TEST_IFACE, 102, TAG_NONE, 1024L, 1024L); + + assertEquals(2, stats.findIndex(TEST_IFACE, 102, TAG_NONE)); + assertEquals(2, stats.findIndex(TEST_IFACE, 102, TAG_NONE)); + assertEquals(0, stats.findIndex(TEST_IFACE, 100, TAG_NONE)); + assertEquals(-1, stats.findIndex(TEST_IFACE, 6, TAG_NONE)); } public void testAddEntryGrow() throws Exception { @@ -45,15 +47,15 @@ public class NetworkStatsTest extends TestCase { assertEquals(0, stats.size); assertEquals(2, stats.iface.length); - stats.addEntry(TEST_IFACE, TEST_UID, 1L, 2L); - stats.addEntry(TEST_IFACE, TEST_UID, 2L, 2L); + stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 2L); + stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L); assertEquals(2, stats.size); assertEquals(2, stats.iface.length); - stats.addEntry(TEST_IFACE, TEST_UID, 3L, 4L); - stats.addEntry(TEST_IFACE, TEST_UID, 4L, 4L); - stats.addEntry(TEST_IFACE, TEST_UID, 5L, 5L); + stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 4L); + stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 4L); + stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 5L); assertEquals(5, stats.size); assertTrue(stats.iface.length >= 5); @@ -65,14 +67,31 @@ public class NetworkStatsTest extends TestCase { assertEquals(5L, stats.rx[4]); } + public void testCombineExisting() throws Exception { + final NetworkStats stats = new NetworkStats(TEST_START, 10); + + stats.addEntry(TEST_IFACE, 1001, TAG_NONE, 512L, 256L); + stats.addEntry(TEST_IFACE, 1001, 0xff, 128L, 128L); + stats.combineEntry(TEST_IFACE, 1001, TAG_NONE, -128L, -128L); + + assertStatsEntry(stats, 0, TEST_IFACE, 1001, TAG_NONE, 384L, 128L); + assertStatsEntry(stats, 1, TEST_IFACE, 1001, 0xff, 128L, 128L); + + // now try combining that should create row + stats.combineEntry(TEST_IFACE, 5005, TAG_NONE, 128L, 128L); + assertStatsEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 128L, 128L); + stats.combineEntry(TEST_IFACE, 5005, TAG_NONE, 128L, 128L); + assertStatsEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 256L, 256L); + } + public void testSubtractIdenticalData() throws Exception { final NetworkStats before = new NetworkStats(TEST_START, 2) - .addEntry(TEST_IFACE, 100, 1024, 0) - .addEntry(TEST_IFACE, 101, 0, 1024); + .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) + .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L); final NetworkStats after = new NetworkStats(TEST_START, 2) - .addEntry(TEST_IFACE, 100, 1024, 0) - .addEntry(TEST_IFACE, 101, 0, 1024); + .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) + .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L); final NetworkStats result = after.subtract(before); @@ -85,12 +104,12 @@ public class NetworkStatsTest extends TestCase { public void testSubtractIdenticalRows() throws Exception { final NetworkStats before = new NetworkStats(TEST_START, 2) - .addEntry(TEST_IFACE, 100, 1024, 0) - .addEntry(TEST_IFACE, 101, 0, 1024); + .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) + .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L); final NetworkStats after = new NetworkStats(TEST_START, 2) - .addEntry(TEST_IFACE, 100, 1025, 2) - .addEntry(TEST_IFACE, 101, 3, 1028); + .addEntry(TEST_IFACE, 100, TAG_NONE, 1025L, 2L) + .addEntry(TEST_IFACE, 101, TAG_NONE, 3L, 1028L); final NetworkStats result = after.subtract(before); @@ -103,13 +122,13 @@ public class NetworkStatsTest extends TestCase { public void testSubtractNewRows() throws Exception { final NetworkStats before = new NetworkStats(TEST_START, 2) - .addEntry(TEST_IFACE, 100, 1024, 0) - .addEntry(TEST_IFACE, 101, 0, 1024); + .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) + .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L); final NetworkStats after = new NetworkStats(TEST_START, 3) - .addEntry(TEST_IFACE, 100, 1024, 0) - .addEntry(TEST_IFACE, 101, 0, 1024) - .addEntry(TEST_IFACE, 102, 1024, 1024); + .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) + .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L) + .addEntry(TEST_IFACE, 102, TAG_NONE, 1024L, 1024L); final NetworkStats result = after.subtract(before); @@ -122,4 +141,13 @@ public class NetworkStatsTest extends TestCase { assertEquals(1024, result.tx[2]); } + private static void assertStatsEntry( + NetworkStats stats, int i, String iface, int uid, int tag, long rx, long tx) { + assertEquals(iface, stats.iface[i]); + assertEquals(uid, stats.uid[i]); + assertEquals(tag, stats.tag[i]); + assertEquals(rx, stats.rx[i]); + assertEquals(tx, stats.tx[i]); + } + } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index bb0c671..d5bdd21 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -16,6 +16,10 @@ package com.android.server; +import static android.net.NetworkStats.IFACE_ALL; +import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkStats.UID_ALL; + import android.content.Context; import android.content.pm.PackageManager; import android.net.INetworkManagementEventObserver; @@ -37,6 +41,7 @@ import java.io.BufferedReader; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Inet4Address; @@ -59,8 +64,9 @@ class NetworkManagementService extends INetworkManagementService.Stub { private static final int ADD = 1; private static final int REMOVE = 2; - /** Base path to UID-granularity network statistics. */ - private static final File PATH_PROC_UID_STAT = new File("/proc/uid_stat"); + @Deprecated + private static final File STATS_UIDSTAT = new File("/proc/uid_stat"); + private static final File STATS_NETFILTER = new File("/proc/net/xt_qtaguid/stats"); class NetdResponseCode { public static final int InterfaceListResult = 110; @@ -899,7 +905,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { for (String iface : ifaces) { final long rx = getInterfaceCounter(iface, true); final long tx = getInterfaceCounter(iface, false); - stats.addEntry(iface, NetworkStats.UID_ALL, rx, tx); + stats.addEntry(iface, UID_ALL, TAG_NONE, rx, tx); } return stats; @@ -910,16 +916,11 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - final String[] knownUids = PATH_PROC_UID_STAT.list(); - final NetworkStats stats = new NetworkStats( - SystemClock.elapsedRealtime(), knownUids.length); - - for (String uid : knownUids) { - final int uidInt = Integer.parseInt(uid); - collectNetworkStatsDetail(stats, uidInt); + if (STATS_NETFILTER.exists()) { + return getNetworkStatsDetailNetfilter(UID_ALL); + } else { + return getNetworkStatsDetailUidstat(UID_ALL); } - - return stats; } @Override @@ -929,19 +930,89 @@ class NetworkManagementService extends INetworkManagementService.Stub { android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); } - final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); - collectNetworkStatsDetail(stats, uid); + if (STATS_NETFILTER.exists()) { + return getNetworkStatsDetailNetfilter(uid); + } else { + return getNetworkStatsDetailUidstat(uid); + } + } + + /** + * Build {@link NetworkStats} with detailed UID statistics. + */ + private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) { + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24); + + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(STATS_NETFILTER)); + + // assumes format from kernel: + // idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes + + // skip first line, which is legend + String line = reader.readLine(); + while ((line = reader.readLine()) != null) { + final StringTokenizer t = new StringTokenizer(line); + + final String idx = t.nextToken(); + final String iface = t.nextToken(); + + try { + // TODO: kernel currently emits tag in upper half of long; + // eventually switch to directly using int. + final int tag = (int) (Long.parseLong(t.nextToken().substring(2), 16) >> 32); + final int uid = Integer.parseInt(t.nextToken()); + final long rx = Long.parseLong(t.nextToken()); + final long tx = Long.parseLong(t.nextToken()); + + if (limitUid == UID_ALL || limitUid == uid) { + stats.addEntry(iface, uid, tag, rx, tx); + if (tag != TAG_NONE) { + // proc also counts tagged data in generic tag, so + // we subtract it here to avoid double-counting. + stats.combineEntry(iface, uid, TAG_NONE, -rx, -tx); + } + } + } catch (NumberFormatException e) { + Slog.w(TAG, "problem parsing stats for idx " + idx + ": " + e); + } + } + } catch (IOException e) { + Slog.w(TAG, "problem parsing stats: " + e); + } finally { + IoUtils.closeQuietly(reader); + } + return stats; } - private void collectNetworkStatsDetail(NetworkStats stats, int uid) { - // TODO: kernel module will provide interface-level stats in future - // TODO: migrate these stats to come across netd in bulk, instead of all - // these individual file reads. - final File uidPath = new File(PATH_PROC_UID_STAT, Integer.toString(uid)); - final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); - final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd")); - stats.addEntry(NetworkStats.IFACE_ALL, uid, rx, tx); + /** + * Build {@link NetworkStats} with detailed UID statistics. + * + * @deprecated since this uses older "uid_stat" data, and doesn't provide + * tag-level granularity or additional variables. + */ + @Deprecated + private NetworkStats getNetworkStatsDetailUidstat(int limitUid) { + final String[] knownUids; + if (limitUid == UID_ALL) { + knownUids = STATS_UIDSTAT.list(); + } else { + knownUids = new String[] { String.valueOf(limitUid) }; + } + + final NetworkStats stats = new NetworkStats( + SystemClock.elapsedRealtime(), knownUids.length); + for (String uid : knownUids) { + final int uidInt = Integer.parseInt(uid); + final File uidPath = new File(STATS_UIDSTAT, uid); + final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); + final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd")); + stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx); + } + + return stats; } public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) { diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java index 510ff62..7266d7d 100644 --- a/services/java/com/android/server/ThrottleService.java +++ b/services/java/com/android/server/ThrottleService.java @@ -533,7 +533,8 @@ public class ThrottleService extends IThrottleManager.Stub { long incWrite = 0; try { final NetworkStats stats = mNMService.getNetworkStatsSummary(); - final int index = stats.findIndex(mIface, NetworkStats.UID_ALL); + final int index = stats.findIndex( + mIface, NetworkStats.UID_ALL, NetworkStats.TAG_NONE); if (index != -1) { incRead = stats.rx[index] - mLastRead; diff --git a/services/java/com/android/server/net/NetworkIdentity.java b/services/java/com/android/server/net/NetworkIdentity.java deleted file mode 100644 index 4a207f7..0000000 --- a/services/java/com/android/server/net/NetworkIdentity.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.net; - -import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.ConnectivityManager.TYPE_WIMAX; -import static android.net.ConnectivityManager.isNetworkTypeMobile; -import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER; -import static android.net.TrafficStats.TEMPLATE_MOBILE_4G; -import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL; -import static android.net.TrafficStats.TEMPLATE_WIFI; -import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G; -import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G; -import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G; -import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN; -import static android.telephony.TelephonyManager.getNetworkClass; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.NetworkState; -import android.telephony.TelephonyManager; - -import com.android.internal.util.Objects; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.ProtocolException; - -/** - * Identity of a {@link NetworkInfo}, defined by network type and billing - * relationship (such as IMSI). - * - * @hide - */ -public class NetworkIdentity { - private static final int VERSION_CURRENT = 1; - - public final int type; - public final int subType; - public final String subscriberId; - - public NetworkIdentity(int type, int subType, String subscriberId) { - this.type = type; - this.subType = subType; - this.subscriberId = subscriberId; - } - - public NetworkIdentity(DataInputStream in) throws IOException { - final int version = in.readInt(); - switch (version) { - case VERSION_CURRENT: { - type = in.readInt(); - subType = in.readInt(); - subscriberId = readOptionalString(in); - break; - } - default: { - throw new ProtocolException("unexpected version: " + version); - } - } - } - - public void writeToStream(DataOutputStream out) throws IOException { - out.writeInt(VERSION_CURRENT); - out.writeInt(type); - out.writeInt(subType); - writeOptionalString(out, subscriberId); - } - - @Override - public int hashCode() { - return Objects.hashCode(type, subType, subscriberId); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof NetworkIdentity) { - final NetworkIdentity ident = (NetworkIdentity) obj; - return type == ident.type && subType == ident.subType - && Objects.equal(subscriberId, ident.subscriberId); - } - return false; - } - - @Override - public String toString() { - final String typeName = ConnectivityManager.getNetworkTypeName(type); - final String subTypeName; - if (ConnectivityManager.isNetworkTypeMobile(type)) { - subTypeName = TelephonyManager.getNetworkTypeName(subType); - } else { - subTypeName = Integer.toString(subType); - } - - return "[type=" + typeName + ", subType=" + subTypeName + ", subId=" + subscriberId + "]"; - } - - /** - * Test if this network matches the given template and IMEI. - */ - public boolean matchesTemplate(int networkTemplate, String subscriberId) { - switch (networkTemplate) { - case TEMPLATE_MOBILE_ALL: - return matchesMobile(subscriberId); - case TEMPLATE_MOBILE_3G_LOWER: - return matchesMobile3gLower(subscriberId); - case TEMPLATE_MOBILE_4G: - return matchesMobile4g(subscriberId); - case TEMPLATE_WIFI: - return matchesWifi(); - default: - throw new IllegalArgumentException("unknown network template"); - } - } - - /** - * Check if mobile network with matching IMEI. Also matches - * {@link #TYPE_WIMAX}. - */ - private boolean matchesMobile(String subscriberId) { - if (isNetworkTypeMobile(type) && Objects.equal(this.subscriberId, subscriberId)) { - return true; - } else if (type == TYPE_WIMAX) { - return true; - } - return false; - } - - /** - * Check if mobile network classified 3G or lower with matching IMEI. - */ - private boolean matchesMobile3gLower(String subscriberId) { - if (isNetworkTypeMobile(type) - && Objects.equal(this.subscriberId, subscriberId)) { - switch (getNetworkClass(subType)) { - case NETWORK_CLASS_UNKNOWN: - case NETWORK_CLASS_2_G: - case NETWORK_CLASS_3_G: - return true; - } - } - return false; - } - - /** - * Check if mobile network classified 4G with matching IMEI. Also matches - * {@link #TYPE_WIMAX}. - */ - private boolean matchesMobile4g(String subscriberId) { - if (isNetworkTypeMobile(type) - && Objects.equal(this.subscriberId, subscriberId)) { - switch (getNetworkClass(subType)) { - case NETWORK_CLASS_4_G: - return true; - } - } else if (type == TYPE_WIMAX) { - return true; - } - return false; - } - - /** - * Check if matches Wi-Fi network template. - */ - private boolean matchesWifi() { - if (type == TYPE_WIFI) { - return true; - } - return false; - } - - /** - * Build a {@link NetworkIdentity} from the given {@link NetworkState}, - * assuming that any mobile networks are using the current IMSI. - */ - public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) { - final int type = state.networkInfo.getType(); - final int subType = state.networkInfo.getSubtype(); - - // TODO: consider moving subscriberId over to LinkCapabilities, so it - // comes from an authoritative source. - - final String subscriberId; - if (isNetworkTypeMobile(type)) { - final TelephonyManager telephony = (TelephonyManager) context.getSystemService( - Context.TELEPHONY_SERVICE); - subscriberId = telephony.getSubscriberId(); - } else { - subscriberId = null; - } - return new NetworkIdentity(type, subType, subscriberId); - } - - private static void writeOptionalString(DataOutputStream out, String value) throws IOException { - if (value != null) { - out.writeByte(1); - out.writeUTF(value); - } else { - out.writeByte(0); - } - } - - private static String readOptionalString(DataInputStream in) throws IOException { - if (in.readByte() != 0) { - return in.readUTF(); - } else { - return null; - } - } - -} diff --git a/services/java/com/android/server/net/InterfaceIdentity.java b/services/java/com/android/server/net/NetworkIdentitySet.java index ff86581..757d3bc 100644 --- a/services/java/com/android/server/net/InterfaceIdentity.java +++ b/services/java/com/android/server/net/NetworkIdentitySet.java @@ -16,6 +16,8 @@ package com.android.server.net; +import android.net.NetworkIdentity; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @@ -28,19 +30,23 @@ import java.util.HashSet; * * @hide */ -public class InterfaceIdentity extends HashSet<NetworkIdentity> { - private static final int VERSION_CURRENT = 1; +public class NetworkIdentitySet extends HashSet<NetworkIdentity> { + private static final int VERSION_INIT = 1; - public InterfaceIdentity() { + public NetworkIdentitySet() { } - public InterfaceIdentity(DataInputStream in) throws IOException { + public NetworkIdentitySet(DataInputStream in) throws IOException { final int version = in.readInt(); switch (version) { - case VERSION_CURRENT: { + case VERSION_INIT: { final int size = in.readInt(); for (int i = 0; i < size; i++) { - add(new NetworkIdentity(in)); + 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)); } break; } @@ -51,23 +57,30 @@ public class InterfaceIdentity extends HashSet<NetworkIdentity> { } public void writeToStream(DataOutputStream out) throws IOException { - out.writeInt(VERSION_CURRENT); + out.writeInt(VERSION_INIT); out.writeInt(size()); for (NetworkIdentity ident : this) { - ident.writeToStream(out); + out.writeInt(VERSION_INIT); + out.writeInt(ident.getType()); + out.writeInt(ident.getSubType()); + writeOptionalString(out, ident.getSubscriberId()); } } - /** - * Test if any {@link NetworkIdentity} on this interface matches the given - * template and IMEI. - */ - public boolean matchesTemplate(int networkTemplate, String subscriberId) { - for (NetworkIdentity ident : this) { - if (ident.matchesTemplate(networkTemplate, subscriberId)) { - return true; - } + private static void writeOptionalString(DataOutputStream out, String value) throws IOException { + if (value != null) { + out.writeByte(1); + out.writeUTF(value); + } else { + out.writeByte(0); + } + } + + private static String readOptionalString(DataInputStream in) throws IOException { + if (in.readByte() != 0) { + return in.readUTF(); + } else { + return null; } - return false; } } diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 43f3c63..ada9ba4 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -23,6 +23,7 @@ import static android.Manifest.permission.MANAGE_NETWORK_POLICY; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.Manifest.permission.READ_PHONE_STATE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_LIMIT; @@ -36,10 +37,9 @@ import static android.net.NetworkPolicyManager.computeLastCycleBoundary; import static android.net.NetworkPolicyManager.dumpPolicy; import static android.net.NetworkPolicyManager.dumpRules; import static android.net.NetworkPolicyManager.isUidValidForPolicy; -import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER; -import static android.net.TrafficStats.TEMPLATE_MOBILE_4G; -import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL; -import static android.net.TrafficStats.isNetworkTemplateMobile; +import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER; +import static android.net.NetworkTemplate.MATCH_MOBILE_4G; +import static android.net.NetworkTemplate.MATCH_MOBILE_ALL; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; @@ -61,9 +61,11 @@ import android.net.IConnectivityManager; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; +import android.net.NetworkIdentity; import android.net.NetworkPolicy; import android.net.NetworkState; import android.net.NetworkStats; +import android.net.NetworkTemplate; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; @@ -84,7 +86,6 @@ import android.util.Xml; import com.android.internal.R; import com.android.internal.os.AtomicFile; import com.android.internal.util.FastXmlSerializer; -import com.android.internal.util.Objects; import com.google.android.collect.Lists; import com.google.android.collect.Maps; import com.google.android.collect.Sets; @@ -353,10 +354,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long total; try { final NetworkStats stats = mNetworkStats.getSummaryForNetwork( - start, end, policy.networkTemplate, policy.subscriberId); + policy.template, start, end); total = stats.rx[0] + stats.tx[0]; } catch (RemoteException e) { - Slog.w(TAG, "problem reading summary for template " + policy.networkTemplate); + Slog.w(TAG, "problem reading summary for template " + policy.template); continue; } @@ -380,8 +381,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * notification of a specific type, like {@link #TYPE_LIMIT}. */ private String buildNotificationTag(NetworkPolicy policy, int type) { - // TODO: consider splicing subscriberId hash into mix - return TAG + ":" + policy.networkTemplate + ":" + type; + return TAG + ":" + policy.template.hashCode() + ":" + type; } /** @@ -408,7 +408,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final Intent intent = new Intent(ACTION_DATA_USAGE_WARNING); intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.putExtra(EXTRA_NETWORK_TEMPLATE, policy.networkTemplate); + intent.putExtra(EXTRA_NETWORK_TEMPLATE, policy.template.getMatchRule()); builder.setContentIntent(PendingIntent.getActivity( mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); break; @@ -416,11 +416,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { case TYPE_LIMIT: { final String title; final String body = res.getString(R.string.data_usage_limit_body); - switch (policy.networkTemplate) { - case TEMPLATE_MOBILE_3G_LOWER: + switch (policy.template.getMatchRule()) { + case MATCH_MOBILE_3G_LOWER: title = res.getString(R.string.data_usage_3g_limit_title); break; - case TEMPLATE_MOBILE_4G: + case MATCH_MOBILE_4G: title = res.getString(R.string.data_usage_4g_limit_title); break; default: @@ -435,7 +435,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final Intent intent = new Intent(ACTION_DATA_USAGE_LIMIT); intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.putExtra(EXTRA_NETWORK_TEMPLATE, policy.networkTemplate); + intent.putExtra(EXTRA_NETWORK_TEMPLATE, policy.template.getMatchRule()); builder.setContentIntent(PendingIntent.getActivity( mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); break; @@ -521,7 +521,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // collect all active ifaces that match this template ifaceList.clear(); for (NetworkIdentity ident : networks.keySet()) { - if (ident.matchesTemplate(policy.networkTemplate, policy.subscriberId)) { + if (policy.template.matches(ident)) { final String iface = networks.get(ident); ifaceList.add(iface); } @@ -554,11 +554,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final NetworkStats stats; final long total; try { - stats = mNetworkStats.getSummaryForNetwork( - start, end, policy.networkTemplate, policy.subscriberId); + stats = mNetworkStats.getSummaryForNetwork(policy.template, start, end); total = stats.rx[0] + stats.tx[0]; } catch (RemoteException e) { - Slog.w(TAG, "problem reading summary for template " + policy.networkTemplate); + Slog.w(TAG, "problem reading summary for template " + policy.template); continue; } @@ -603,12 +602,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private void ensureActiveMobilePolicyLocked() { if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyLocked()"); final String subscriberId = getActiveSubscriberId(); + final NetworkIdentity probeIdent = new NetworkIdentity( + TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId); // examine to see if any policy is defined for active mobile boolean mobileDefined = false; for (NetworkPolicy policy : mNetworkPolicy) { - if (isNetworkTemplateMobile(policy.networkTemplate) - && Objects.equal(subscriberId, policy.subscriberId)) { + if (policy.template.matches(probeIdent)) { mobileDefined = true; } } @@ -624,8 +624,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { time.setToNow(); final int cycleDay = time.monthDay; - mNetworkPolicy.add(new NetworkPolicy( - TEMPLATE_MOBILE_ALL, subscriberId, cycleDay, 4 * GB_IN_BYTES, LIMIT_DISABLED)); + final NetworkTemplate template = new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId); + mNetworkPolicy.add( + new NetworkPolicy(template, cycleDay, 4 * GB_IN_BYTES, LIMIT_DISABLED)); writePolicyLocked(); } } @@ -658,8 +659,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long warningBytes = readLongAttribute(in, ATTR_WARNING_BYTES); final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES); - mNetworkPolicy.add(new NetworkPolicy( - networkTemplate, subscriberId, cycleDay, warningBytes, limitBytes)); + final NetworkTemplate template = new NetworkTemplate( + networkTemplate, subscriberId); + mNetworkPolicy.add( + new NetworkPolicy(template, cycleDay, warningBytes, limitBytes)); } else if (TAG_UID_POLICY.equals(tag)) { final int uid = readIntAttribute(in, ATTR_UID); @@ -701,10 +704,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // write all known network policies for (NetworkPolicy policy : mNetworkPolicy) { + final NetworkTemplate template = policy.template; + out.startTag(null, TAG_NETWORK_POLICY); - writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, policy.networkTemplate); - if (policy.subscriberId != null) { - out.attribute(null, ATTR_SUBSCRIBER_ID, policy.subscriberId); + writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.getMatchRule()); + final String subscriberId = template.getSubscriberId(); + if (subscriberId != null) { + out.attribute(null, ATTR_SUBSCRIBER_ID, subscriberId); } writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay); writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes); diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 0a84bc7..54a806a 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.Manifest.permission.SHUTDOWN; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.NetworkStats.IFACE_ALL; +import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION; import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY; @@ -45,10 +46,12 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.net.IConnectivityManager; import android.net.INetworkStatsService; +import android.net.NetworkIdentity; import android.net.NetworkInfo; import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; @@ -63,8 +66,8 @@ import android.util.SparseArray; import android.util.TrustedTime; import com.android.internal.os.AtomicFile; -import com.google.android.collect.Lists; import com.google.android.collect.Maps; +import com.google.android.collect.Sets; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -76,9 +79,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.net.ProtocolException; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import libcore.io.IoUtils; @@ -93,7 +96,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** File header magic number: "ANET" */ private static final int FILE_MAGIC = 0x414E4554; - private static final int VERSION_CURRENT = 1; + private static final int VERSION_NETWORK_INIT = 1; + private static final int VERSION_UID_INIT = 1; + private static final int VERSION_UID_WITH_IDENT = 2; private final Context mContext; private final INetworkManagementService mNetworkManager; @@ -112,6 +117,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private PendingIntent mPollIntent; // TODO: listen for kernel push events through netd instead of polling + // TODO: watch for UID uninstall, and transfer stats into single bucket private static final long KB_IN_BYTES = 1024; private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES; @@ -132,13 +138,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final Object mStatsLock = new Object(); - /** Set of active ifaces during this boot. */ - private HashMap<String, InterfaceIdentity> mActiveIface = Maps.newHashMap(); - - /** Set of historical stats for known ifaces. */ - private HashMap<InterfaceIdentity, NetworkStatsHistory> mNetworkStats = Maps.newHashMap(); + /** Set of currently active ifaces. */ + private HashMap<String, NetworkIdentitySet> mActiveIfaces = Maps.newHashMap(); + /** Set of historical stats for known networks. */ + private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkStats = Maps.newHashMap(); /** Set of historical stats for known UIDs. */ - private SparseArray<NetworkStatsHistory> mUidStats = new SparseArray<NetworkStatsHistory>(); + private HashMap<NetworkIdentitySet, SparseArray<NetworkStatsHistory>> mUidStats = + Maps.newHashMap(); /** Flag if {@link #mUidStats} have been loaded from disk. */ private boolean mUidStatsLoaded = false; @@ -251,17 +257,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override - public NetworkStatsHistory getHistoryForNetwork(int networkTemplate) { + public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); synchronized (mStatsLock) { // combine all interfaces that match template - final String subscriberId = getActiveSubscriberId(); final NetworkStatsHistory combined = new NetworkStatsHistory( mSettings.getNetworkBucketDuration(), estimateNetworkBuckets()); - for (InterfaceIdentity ident : mNetworkStats.keySet()) { - final NetworkStatsHistory history = mNetworkStats.get(ident); - if (ident.matchesTemplate(networkTemplate, subscriberId)) { + for (NetworkIdentitySet ident : mNetworkStats.keySet()) { + if (templateMatches(template, ident)) { + final NetworkStatsHistory history = mNetworkStats.get(ident); combined.recordEntireHistory(history); } } @@ -270,19 +275,29 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override - public NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate) { + public NetworkStatsHistory getHistoryForUid(NetworkTemplate template, int uid) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); synchronized (mStatsLock) { - // TODO: combine based on template, if we store that granularity ensureUidStatsLoadedLocked(); - return mUidStats.get(uid); + + // combine all interfaces that match template + final NetworkStatsHistory combined = new NetworkStatsHistory( + mSettings.getUidBucketDuration(), estimateUidBuckets()); + for (NetworkIdentitySet ident : mUidStats.keySet()) { + if (templateMatches(template, ident)) { + final NetworkStatsHistory history = mUidStats.get(ident).get(uid); + if (history != null) { + combined.recordEntireHistory(history); + } + } + } + return combined; } } @Override - public NetworkStats getSummaryForNetwork( - long start, long end, int networkTemplate, String subscriberId) { + public NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); synchronized (mStatsLock) { @@ -291,9 +306,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { long[] networkTotal = new long[2]; // combine total from all interfaces that match template - for (InterfaceIdentity ident : mNetworkStats.keySet()) { - final NetworkStatsHistory history = mNetworkStats.get(ident); - if (ident.matchesTemplate(networkTemplate, subscriberId)) { + for (NetworkIdentitySet ident : mNetworkStats.keySet()) { + if (templateMatches(template, ident)) { + final NetworkStatsHistory history = mNetworkStats.get(ident); networkTotal = history.getTotalData(start, end, networkTotal); rx += networkTotal[0]; tx += networkTotal[1]; @@ -301,30 +316,33 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } final NetworkStats stats = new NetworkStats(end - start, 1); - stats.addEntry(IFACE_ALL, UID_ALL, rx, tx); + stats.addEntry(IFACE_ALL, UID_ALL, TAG_NONE, rx, tx); return stats; } } @Override - public NetworkStats getSummaryForAllUid(long start, long end, int networkTemplate) { + public NetworkStats getSummaryForAllUid(NetworkTemplate template, long start, long end) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); - // TODO: apply networktemplate once granular uid stats are stored. - synchronized (mStatsLock) { ensureUidStatsLoadedLocked(); - final int size = mUidStats.size(); - final NetworkStats stats = new NetworkStats(end - start, size); - + final NetworkStats stats = new NetworkStats(end - start, 24); long[] total = new long[2]; - for (int i = 0; i < size; i++) { - final int uid = mUidStats.keyAt(i); - final NetworkStatsHistory history = mUidStats.valueAt(i); - total = history.getTotalData(start, end, total); - stats.addEntry(IFACE_ALL, uid, total[0], total[1]); + + for (NetworkIdentitySet ident : mUidStats.keySet()) { + if (templateMatches(template, ident)) { + final SparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident); + for (int i = 0; i < uidStats.size(); i++) { + final int uid = uidStats.keyAt(i); + final NetworkStatsHistory history = uidStats.valueAt(i); + total = history.getTotalData(start, end, total); + stats.combineEntry(IFACE_ALL, uid, TAG_NONE, total[0], total[1]); + } + } } + return stats; } } @@ -352,7 +370,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // permission above. synchronized (mStatsLock) { // TODO: acquire wakelock while performing poll - performPollLocked(true); + performPollLocked(true, false); } } }; @@ -371,7 +389,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * Inspect all current {@link NetworkState} to derive mapping from {@code * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo} * are active on a single {@code iface}, they are combined under a single - * {@link InterfaceIdentity}. + * {@link NetworkIdentitySet}. */ private void updateIfacesLocked() { if (LOGV) Slog.v(TAG, "updateIfacesLocked()"); @@ -379,7 +397,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // take one last stats snapshot before updating iface mapping. this // isn't perfect, since the kernel may already be counting traffic from // the updated network. - performPollLocked(false); + performPollLocked(false, false); final NetworkState[] states; try { @@ -390,13 +408,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } // rebuild active interfaces based on connected networks - mActiveIface.clear(); + mActiveIfaces.clear(); for (NetworkState state : states) { if (state.networkInfo.isConnected()) { // collect networks under their parent interfaces final String iface = state.linkProperties.getInterfaceName(); - final InterfaceIdentity ident = findOrCreateInterfaceLocked(iface); + + NetworkIdentitySet ident = mActiveIfaces.get(iface); + if (ident == null) { + ident = new NetworkIdentitySet(); + mActiveIfaces.put(iface, ident); + } + ident.add(NetworkIdentity.buildNetworkIdentity(mContext, state)); } } @@ -409,7 +433,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * @param detailedPoll Indicate if detailed UID stats should be collected * during this poll operation. */ - private void performPollLocked(boolean detailedPoll) { + private void performPollLocked(boolean detailedPoll, boolean forcePersist) { if (LOGV) Slog.v(TAG, "performPollLocked()"); // try refreshing time source when stale @@ -421,33 +445,33 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); - final NetworkStats networkStats; + final NetworkStats ifaceStats; final NetworkStats uidStats; try { - networkStats = mNetworkManager.getNetworkStatsSummary(); + ifaceStats = mNetworkManager.getNetworkStatsSummary(); uidStats = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null; } catch (RemoteException e) { Slog.w(TAG, "problem reading network stats"); return; } - performNetworkPollLocked(networkStats, currentTime); + performNetworkPollLocked(ifaceStats, currentTime); if (detailedPoll) { performUidPollLocked(uidStats, currentTime); } // decide if enough has changed to trigger persist - final NetworkStats persistDelta = computeStatsDelta(mLastNetworkPersist, networkStats); + final NetworkStats persistDelta = computeStatsDelta(mLastNetworkPersist, ifaceStats); final long persistThreshold = mSettings.getPersistThreshold(); for (String iface : persistDelta.getUniqueIfaces()) { - final int index = persistDelta.findIndex(iface, UID_ALL); - if (persistDelta.rx[index] > persistThreshold + final int index = persistDelta.findIndex(iface, UID_ALL, TAG_NONE); + if (forcePersist || persistDelta.rx[index] > persistThreshold || persistDelta.tx[index] > persistThreshold) { writeNetworkStatsLocked(); if (mUidStatsLoaded) { writeUidStatsLocked(); } - mLastNetworkPersist = networkStats; + mLastNetworkPersist = ifaceStats; break; } } @@ -462,23 +486,23 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * Update {@link #mNetworkStats} historical usage. */ private void performNetworkPollLocked(NetworkStats networkStats, long currentTime) { - final ArrayList<String> unknownIface = Lists.newArrayList(); + final HashSet<String> unknownIface = Sets.newHashSet(); final NetworkStats delta = computeStatsDelta(mLastNetworkPoll, networkStats); final long timeStart = currentTime - delta.elapsedRealtime; final long maxHistory = mSettings.getNetworkMaxHistory(); - for (String iface : delta.getUniqueIfaces()) { - final InterfaceIdentity ident = mActiveIface.get(iface); + for (int i = 0; i < delta.size; i++) { + final String iface = delta.iface[i]; + final NetworkIdentitySet ident = mActiveIfaces.get(iface); if (ident == null) { unknownIface.add(iface); continue; } - final int index = delta.findIndex(iface, UID_ALL); - final long rx = delta.rx[index]; - final long tx = delta.tx[index]; + final long rx = delta.rx[i]; + final long tx = delta.tx[i]; - final NetworkStatsHistory history = findOrCreateNetworkLocked(ident); + final NetworkStatsHistory history = findOrCreateNetworkStatsLocked(ident); history.recordData(timeStart, currentTime, rx, tx); history.removeBucketsBefore(currentTime - maxHistory); } @@ -498,22 +522,30 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStats delta = computeStatsDelta(mLastUidPoll, uidStats); final long timeStart = currentTime - delta.elapsedRealtime; final long maxHistory = mSettings.getUidMaxHistory(); - for (int uid : delta.getUniqueUids()) { - // TODO: traverse all ifaces once surfaced in stats - final int index = delta.findIndex(IFACE_ALL, uid); - if (index != -1) { - final long rx = delta.rx[index]; - final long tx = delta.tx[index]; - - final NetworkStatsHistory history = findOrCreateUidLocked(uid); - history.recordData(timeStart, currentTime, rx, tx); - history.removeBucketsBefore(currentTime - maxHistory); + + // NOTE: historical UID stats ignore tags, and simply records all stats + // entries into a single UID bucket. + + for (int i = 0; i < delta.size; i++) { + final String iface = delta.iface[i]; + final NetworkIdentitySet ident = mActiveIfaces.get(iface); + if (ident == null) { + continue; } + + final int uid = delta.uid[i]; + final long rx = delta.rx[i]; + final long tx = delta.tx[i]; + + final NetworkStatsHistory history = findOrCreateUidStatsLocked(ident, uid); + history.recordData(timeStart, currentTime, rx, tx); + history.removeBucketsBefore(currentTime - maxHistory); } + mLastUidPoll = uidStats; } - private NetworkStatsHistory findOrCreateNetworkLocked(InterfaceIdentity ident) { + private NetworkStatsHistory findOrCreateNetworkStatsLocked(NetworkIdentitySet ident) { final long bucketDuration = mSettings.getNetworkBucketDuration(); final NetworkStatsHistory existing = mNetworkStats.get(ident); @@ -535,9 +567,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private NetworkStatsHistory findOrCreateUidLocked(int uid) { + private NetworkStatsHistory findOrCreateUidStatsLocked(NetworkIdentitySet ident, int uid) { + // find bucket for identity first, then find uid + SparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident); + if (uidStats == null) { + uidStats = new SparseArray<NetworkStatsHistory>(); + mUidStats.put(ident, uidStats); + } + final long bucketDuration = mSettings.getUidBucketDuration(); - final NetworkStatsHistory existing = mUidStats.get(uid); + final NetworkStatsHistory existing = uidStats.get(uid); // update when no existing, or when bucket duration changed NetworkStatsHistory updated = null; @@ -550,22 +589,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } if (updated != null) { - mUidStats.put(uid, updated); + uidStats.put(uid, updated); return updated; } else { return existing; } } - private InterfaceIdentity findOrCreateInterfaceLocked(String iface) { - InterfaceIdentity ident = mActiveIface.get(iface); - if (ident == null) { - ident = new InterfaceIdentity(); - mActiveIface.put(iface, ident); - } - return ident; - } - private void readNetworkStatsLocked() { if (LOGV) Slog.v(TAG, "readNetworkStatsLocked()"); @@ -585,15 +615,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final int version = in.readInt(); switch (version) { - case VERSION_CURRENT: { - // file format is pairs of interfaces and stats: - // network := size *(InterfaceIdentity NetworkStatsHistory) - + case VERSION_NETWORK_INIT: { + // network := size *(NetworkIdentitySet NetworkStatsHistory) final int size = in.readInt(); for (int i = 0; i < size; i++) { - final InterfaceIdentity ident = new InterfaceIdentity(in); + final NetworkIdentitySet ident = new NetworkIdentitySet(in); final NetworkStatsHistory history = new NetworkStatsHistory(in); - mNetworkStats.put(ident, history); } break; @@ -637,16 +664,29 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final int version = in.readInt(); switch (version) { - case VERSION_CURRENT: { - // file format is pairs of UIDs and stats: + case VERSION_UID_INIT: { // uid := size *(UID NetworkStatsHistory) - final int size = in.readInt(); - for (int i = 0; i < size; i++) { - final int uid = in.readInt(); - final NetworkStatsHistory history = new NetworkStatsHistory(in); - - mUidStats.put(uid, history); + // drop this data version, since we don't have a good + // mapping into NetworkIdentitySet. + break; + } + case VERSION_UID_WITH_IDENT: { + // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory)) + final int ifaceSize = in.readInt(); + for (int i = 0; i < ifaceSize; i++) { + final NetworkIdentitySet ident = new NetworkIdentitySet(in); + + final int uidSize = in.readInt(); + final SparseArray<NetworkStatsHistory> uidStats = new SparseArray< + NetworkStatsHistory>(uidSize); + for (int j = 0; j < uidSize; j++) { + final int uid = in.readInt(); + final NetworkStatsHistory history = new NetworkStatsHistory(in); + uidStats.put(uid, history); + } + + mUidStats.put(ident, uidStats); } break; } @@ -674,10 +714,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final DataOutputStream out = new DataOutputStream(fos); out.writeInt(FILE_MAGIC); - out.writeInt(VERSION_CURRENT); + out.writeInt(VERSION_NETWORK_INIT); out.writeInt(mNetworkStats.size()); - for (InterfaceIdentity ident : mNetworkStats.keySet()) { + for (NetworkIdentitySet ident : mNetworkStats.keySet()) { final NetworkStatsHistory history = mNetworkStats.get(ident); ident.writeToStream(out); history.writeToStream(out); @@ -702,16 +742,21 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final DataOutputStream out = new DataOutputStream(fos); out.writeInt(FILE_MAGIC); - out.writeInt(VERSION_CURRENT); + out.writeInt(VERSION_UID_WITH_IDENT); - final int size = mUidStats.size(); + out.writeInt(mUidStats.size()); + for (NetworkIdentitySet ident : mUidStats.keySet()) { + final SparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident); + ident.writeToStream(out); - out.writeInt(size); - for (int i = 0; i < size; i++) { - final int uid = mUidStats.keyAt(i); - final NetworkStatsHistory history = mUidStats.valueAt(i); - out.writeInt(uid); - history.writeToStream(out); + final int size = uidStats.size(); + out.writeInt(size); + for (int i = 0; i < size; i++) { + final int uid = uidStats.keyAt(i); + final NetworkStatsHistory history = uidStats.valueAt(i); + out.writeInt(uid); + history.writeToStream(out); + } } mUidFile.finishWrite(fos); @@ -740,35 +785,41 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } if (argSet.contains("poll")) { - performPollLocked(true); + performPollLocked(true, true); pw.println("Forced poll"); return; } pw.println("Active interfaces:"); - for (String iface : mActiveIface.keySet()) { - final InterfaceIdentity ident = mActiveIface.get(iface); + for (String iface : mActiveIfaces.keySet()) { + final NetworkIdentitySet ident = mActiveIfaces.get(iface); pw.print(" iface="); pw.print(iface); pw.print(" ident="); pw.println(ident.toString()); } pw.println("Known historical stats:"); - for (InterfaceIdentity ident : mNetworkStats.keySet()) { - final NetworkStatsHistory stats = mNetworkStats.get(ident); + for (NetworkIdentitySet ident : mNetworkStats.keySet()) { + final NetworkStatsHistory history = mNetworkStats.get(ident); pw.print(" ident="); pw.println(ident.toString()); - stats.dump(" ", pw); + history.dump(" ", pw); } if (argSet.contains("detail")) { // since explicitly requested with argument, we're okay to load // from disk if not already in memory. ensureUidStatsLoadedLocked(); - pw.println("Known UID stats:"); - for (int i = 0; i < mUidStats.size(); i++) { - final int uid = mUidStats.keyAt(i); - final NetworkStatsHistory stats = mUidStats.valueAt(i); - pw.print(" UID="); pw.println(uid); - stats.dump(" ", pw); + + pw.println("Detailed UID stats:"); + for (NetworkIdentitySet ident : mUidStats.keySet()) { + pw.print(" ident="); pw.println(ident.toString()); + + final SparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident); + for (int i = 0; i < uidStats.size(); i++) { + final int uid = uidStats.keyAt(i); + final NetworkStatsHistory history = uidStats.valueAt(i); + pw.print(" UID="); pw.println(uid); + history.dump(" ", pw); + } } } } @@ -779,27 +830,30 @@ public class NetworkStatsService extends INetworkStatsService.Stub { */ @Deprecated private void generateRandomLocked() { - long end = System.currentTimeMillis(); - long start = end - mSettings.getNetworkMaxHistory(); - long rx = 3 * GB_IN_BYTES; - long tx = 2 * GB_IN_BYTES; + long networkEnd = System.currentTimeMillis(); + long networkStart = networkEnd - mSettings.getNetworkMaxHistory(); + long networkRx = 3 * GB_IN_BYTES; + long networkTx = 2 * GB_IN_BYTES; - mNetworkStats.clear(); - for (InterfaceIdentity ident : mActiveIface.values()) { - final NetworkStatsHistory stats = findOrCreateNetworkLocked(ident); - stats.generateRandom(start, end, rx, tx); - } + long uidEnd = System.currentTimeMillis(); + long uidStart = uidEnd - mSettings.getUidMaxHistory(); + long uidRx = 500 * MB_IN_BYTES; + long uidTx = 100 * MB_IN_BYTES; - end = System.currentTimeMillis(); - start = end - mSettings.getUidMaxHistory(); - rx = 500 * MB_IN_BYTES; - tx = 100 * MB_IN_BYTES; + final List<ApplicationInfo> installedApps = mContext + .getPackageManager().getInstalledApplications(0); + mNetworkStats.clear(); mUidStats.clear(); - for (ApplicationInfo info : mContext.getPackageManager().getInstalledApplications(0)) { - final int uid = info.uid; - final NetworkStatsHistory stats = findOrCreateUidLocked(uid); - stats.generateRandom(start, end, rx, tx); + for (NetworkIdentitySet ident : mActiveIfaces.values()) { + findOrCreateNetworkStatsLocked(ident).generateRandom( + networkStart, networkEnd, networkRx, networkTx); + + for (ApplicationInfo info : installedApps) { + final int uid = info.uid; + findOrCreateUidStatsLocked(ident, uid).generateRandom( + uidStart, uidEnd, uidRx, uidTx); + } } } @@ -815,12 +869,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private String getActiveSubscriberId() { - final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService( - Context.TELEPHONY_SERVICE); - return telephony.getSubscriberId(); - } - private int estimateNetworkBuckets() { return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration()); } @@ -834,6 +882,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } /** + * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity} + * in the given {@link NetworkIdentitySet}. + */ + private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) { + for (NetworkIdentity ident : identSet) { + if (template.matches(ident)) { + return true; + } + } + return false; + } + + /** * Default external settings that read from {@link Settings.Secure}. */ private static class DefaultNetworkStatsSettings implements NetworkStatsSettings { diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 5cb1763..b62687a 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -23,8 +23,9 @@ 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; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; +import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static android.net.TrafficStats.TEMPLATE_WIFI; +import static android.net.NetworkTemplate.MATCH_WIFI; import static org.easymock.EasyMock.anyInt; import static org.easymock.EasyMock.aryEq; import static org.easymock.EasyMock.capture; @@ -49,6 +50,7 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkPolicy; import android.net.NetworkState; import android.net.NetworkStats; +import android.net.NetworkTemplate; import android.os.Binder; import android.os.IPowerManager; import android.test.AndroidTestCase; @@ -76,6 +78,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { private static final long TEST_START = 1194220800000L; private static final String TEST_IFACE = "test0"; + private static NetworkTemplate sTemplateWifi = new NetworkTemplate(MATCH_WIFI, null); + private BroadcastInterceptingContext mServiceContext; private File mPolicyDir; @@ -305,7 +309,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { final long currentTime = parseTime("2007-11-14T00:00:00.000Z"); final long expectedCycle = parseTime("2007-11-05T00:00:00.000Z"); - final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 5, 1024L, 1024L); + final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 5, 1024L, 1024L); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -315,7 +319,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { final long currentTime = parseTime("2007-11-14T00:00:00.000Z"); final long expectedCycle = parseTime("2007-10-20T00:00:00.000Z"); - final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 20, 1024L, 1024L); + final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 20, 1024L, 1024L); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -325,7 +329,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { final long currentTime = parseTime("2007-02-14T00:00:00.000Z"); final long expectedCycle = parseTime("2007-01-30T00:00:00.000Z"); - final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 30, 1024L, 1024L); + final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 30, 1024L, 1024L); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -335,7 +339,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { final long currentTime = parseTime("2007-03-14T00:00:00.000Z"); final long expectedCycle = parseTime("2007-03-01T00:00:00.000Z"); - final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 30, 1024L, 1024L); + final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 30, 1024L, 1024L); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -367,8 +371,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { // pretend that 512 bytes total have happened stats = new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, 256L, 256L); - expect(mStatsService.getSummaryForNetwork(TIME_FEB_15, TIME_MAR_10, TEMPLATE_WIFI, null)) + .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 256L, 256L); + expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10)) .andReturn(stats).atLeastOnce(); // expect that quota remaining should be 1536 bytes @@ -378,7 +382,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expectMeteredIfacesChanged(TEST_IFACE); replay(); - setNetworkPolicies(new NetworkPolicy(TEMPLATE_WIFI, null, CYCLE_DAY, 1024L, 2048L)); + setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L)); verifyAndReset(); } diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index 2457ff3..1d2634c 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -18,9 +18,9 @@ package com.android.server; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.NetworkStats.IFACE_ALL; +import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static android.net.TrafficStats.TEMPLATE_WIFI; +import static android.net.NetworkTemplate.MATCH_WIFI; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; @@ -44,6 +44,7 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; import android.os.INetworkManagementService; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; @@ -66,6 +67,8 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private static final String TEST_IFACE = "test0"; private static final long TEST_START = 1194220800000L; + private static NetworkTemplate sTemplateWifi = new NetworkTemplate(MATCH_WIFI, null); + private static final int TEST_UID_1 = 1001; private static final int TEST_UID_2 = 1002; @@ -138,7 +141,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); // verify service has empty history for wifi - assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L); + assertNetworkTotal(sTemplateWifi, 0L, 0L); verifyAndReset(); // modify some number on wifi, and trigger poll event @@ -146,14 +149,14 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, 1024L, 2048L)); + .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 2048L)); expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); // verify service recorded history - assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L); + assertNetworkTotal(sTemplateWifi, 1024L, 2048L); verifyAndReset(); // and bump forward again, with counters going higher. this is @@ -162,14 +165,14 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, 4096L, 8192L)); + .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 4096L, 8192L)); expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); // verify service recorded history - assertNetworkTotal(TEMPLATE_WIFI, 4096L, 8192L); + assertNetworkTotal(sTemplateWifi, 4096L, 8192L); verifyAndReset(); } @@ -189,7 +192,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); // verify service has empty history for wifi - assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L); + assertNetworkTotal(sTemplateWifi, 0L, 0L); verifyAndReset(); // modify some number on wifi, and trigger poll event @@ -197,19 +200,18 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, 1024L, 2048L)); - // TODO: switch these stats to specific iface + .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 2048L)); expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 2) - .addEntry(IFACE_ALL, TEST_UID_1, 512L, 256L) - .addEntry(IFACE_ALL, TEST_UID_2, 128L, 128L)); + .addEntry(TEST_IFACE, TEST_UID_1, TAG_NONE, 512L, 256L) + .addEntry(TEST_IFACE, TEST_UID_2, TAG_NONE, 128L, 128L)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); // verify service recorded history - assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L); - assertUidTotal(TEST_UID_1, TEMPLATE_WIFI, 512L, 256L); - assertUidTotal(TEST_UID_2, TEMPLATE_WIFI, 128L, 128L); + assertNetworkTotal(sTemplateWifi, 1024L, 2048L); + assertUidTotal(sTemplateWifi, TEST_UID_1, 512L, 256L); + assertUidTotal(sTemplateWifi, TEST_UID_2, 128L, 128L); verifyAndReset(); // graceful shutdown system, which should trigger persist of stats, and @@ -220,7 +222,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { // we persisted them to file. expectDefaultSettings(); replay(); - assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L); + assertNetworkTotal(sTemplateWifi, 0L, 0L); verifyAndReset(); assertStatsFilesExist(true); @@ -233,9 +235,9 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mService.systemReady(); // after systemReady(), we should have historical stats loaded again - assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L); - assertUidTotal(TEST_UID_1, TEMPLATE_WIFI, 512L, 256L); - assertUidTotal(TEST_UID_2, TEMPLATE_WIFI, 128L, 128L); + assertNetworkTotal(sTemplateWifi, 1024L, 2048L); + assertUidTotal(sTemplateWifi, TEST_UID_1, 512L, 256L); + assertUidTotal(sTemplateWifi, TEST_UID_2, 128L, 128L); verifyAndReset(); } @@ -263,14 +265,14 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, 512L, 512L)); + .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 512L, 512L)); expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); // verify service recorded history - history = mService.getHistoryForNetwork(TEMPLATE_WIFI); + history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null)); total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); assertEquals(512L, total[0]); assertEquals(512L, total[1]); @@ -289,7 +291,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); // verify identical stats, but spread across 4 buckets now - history = mService.getHistoryForNetwork(TEMPLATE_WIFI); + history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null)); total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); assertEquals(512L, total[0]); assertEquals(512L, total[1]); @@ -299,15 +301,15 @@ public class NetworkStatsServiceTest extends AndroidTestCase { } - private void assertNetworkTotal(int template, long rx, long tx) { + private void assertNetworkTotal(NetworkTemplate template, long rx, long tx) { final NetworkStatsHistory history = mService.getHistoryForNetwork(template); final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); assertEquals(rx, total[0]); assertEquals(tx, total[1]); } - private void assertUidTotal(int uid, int template, long rx, long tx) { - final NetworkStatsHistory history = mService.getHistoryForUid(uid, template); + private void assertUidTotal(NetworkTemplate template, int uid, long rx, long tx) { + final NetworkStatsHistory history = mService.getHistoryForUid(template, uid); final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); assertEquals(rx, total[0]); assertEquals(tx, total[1]); diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java index 30afdd8..2f275c3 100644 --- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java @@ -289,7 +289,7 @@ public class ThrottleServiceTest extends AndroidTestCase { public void expectGetInterfaceCounter(long rx, long tx) throws Exception { // TODO: provide elapsedRealtime mock to match TimeAuthority final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); - stats.addEntry(TEST_IFACE, NetworkStats.UID_ALL, rx, tx); + stats.addEntry(TEST_IFACE, NetworkStats.UID_ALL, NetworkStats.TAG_NONE, rx, tx); expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats).atLeastOnce(); } |