Foreground/background network stats using sets.

Teach NetworkStats about "counter sets" coming from kernel, and use
them to track usage in foreground/background.  Add AID_NET_BW_ACCT to
system_server so it can control counter sets.

Move to composite key of NetworkIdentitySet, UID, set, and tag when
recording historical usage.  Persisting still clusters by identity,
since that is heaviest object.

Request async stats poll during systemReady() to bootstrap later
delta calculations. Reset kernel counters when UID removed. Update
various tests.

Bug: 5105592, 5146067
Change-Id: Idabec9e3ffcaf212879821515602ecde0a03de8c
This commit is contained in:
Jeff Sharkey
2011-08-10 17:53:27 -07:00
parent 0c001477c2
commit 806d1fdf9a
5 changed files with 284 additions and 169 deletions

View File

@@ -26,7 +26,7 @@ interface INetworkStatsService {
/** Return historical network layer stats for traffic that matches template. */ /** Return historical network layer stats for traffic that matches template. */
NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields); NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields);
/** Return historical network layer stats for specific UID traffic that matches template. */ /** Return historical network layer stats for specific UID traffic that matches template. */
NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int tag, int fields); NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int set, int tag, int fields);
/** Return network layer usage summary for traffic that matches template. */ /** Return network layer usage summary for traffic that matches template. */
NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end); NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end);
@@ -38,6 +38,8 @@ interface INetworkStatsService {
/** Increment data layer count of operations performed for UID and tag. */ /** Increment data layer count of operations performed for UID and tag. */
void incrementOperationCount(int uid, int tag, int operationCount); void incrementOperationCount(int uid, int tag, int operationCount);
/** Mark given UID as being in foreground for stats purposes. */
void setUidForeground(int uid, boolean uidForeground);
/** Force update of statistics. */ /** Force update of statistics. */
void forceUpdate(); void forceUpdate();

View File

@@ -42,7 +42,13 @@ public class NetworkStats implements Parcelable {
public static final String IFACE_ALL = null; public static final String IFACE_ALL = null;
/** {@link #uid} value when UID details unavailable. */ /** {@link #uid} value when UID details unavailable. */
public static final int UID_ALL = -1; public static final int UID_ALL = -1;
/** {@link #tag} value for without tag. */ /** {@link #set} value when all sets combined. */
public static final int SET_ALL = -1;
/** {@link #set} value where background data is accounted. */
public static final int SET_DEFAULT = 0;
/** {@link #set} value where foreground data is accounted. */
public static final int SET_FOREGROUND = 1;
/** {@link #tag} value for total data across all tags. */
public static final int TAG_NONE = 0; public static final int TAG_NONE = 0;
/** /**
@@ -53,6 +59,7 @@ public class NetworkStats implements Parcelable {
private int size; private int size;
private String[] iface; private String[] iface;
private int[] uid; private int[] uid;
private int[] set;
private int[] tag; private int[] tag;
private long[] rxBytes; private long[] rxBytes;
private long[] rxPackets; private long[] rxPackets;
@@ -63,6 +70,7 @@ public class NetworkStats implements Parcelable {
public static class Entry { public static class Entry {
public String iface; public String iface;
public int uid; public int uid;
public int set;
public int tag; public int tag;
public long rxBytes; public long rxBytes;
public long rxPackets; public long rxPackets;
@@ -71,17 +79,19 @@ public class NetworkStats implements Parcelable {
public long operations; public long operations;
public Entry() { public Entry() {
this(IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0L); this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
} }
public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
this(IFACE_ALL, UID_ALL, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, operations); this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets,
operations);
} }
public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
long txPackets, long operations) { long txBytes, long txPackets, long operations) {
this.iface = iface; this.iface = iface;
this.uid = uid; this.uid = uid;
this.set = set;
this.tag = tag; this.tag = tag;
this.rxBytes = rxBytes; this.rxBytes = rxBytes;
this.rxPackets = rxPackets; this.rxPackets = rxPackets;
@@ -96,6 +106,7 @@ public class NetworkStats implements Parcelable {
this.size = 0; this.size = 0;
this.iface = new String[initialSize]; this.iface = new String[initialSize];
this.uid = new int[initialSize]; this.uid = new int[initialSize];
this.set = new int[initialSize];
this.tag = new int[initialSize]; this.tag = new int[initialSize];
this.rxBytes = new long[initialSize]; this.rxBytes = new long[initialSize];
this.rxPackets = new long[initialSize]; this.rxPackets = new long[initialSize];
@@ -109,6 +120,7 @@ public class NetworkStats implements Parcelable {
size = parcel.readInt(); size = parcel.readInt();
iface = parcel.createStringArray(); iface = parcel.createStringArray();
uid = parcel.createIntArray(); uid = parcel.createIntArray();
set = parcel.createIntArray();
tag = parcel.createIntArray(); tag = parcel.createIntArray();
rxBytes = parcel.createLongArray(); rxBytes = parcel.createLongArray();
rxPackets = parcel.createLongArray(); rxPackets = parcel.createLongArray();
@@ -123,6 +135,7 @@ public class NetworkStats implements Parcelable {
dest.writeInt(size); dest.writeInt(size);
dest.writeStringArray(iface); dest.writeStringArray(iface);
dest.writeIntArray(uid); dest.writeIntArray(uid);
dest.writeIntArray(set);
dest.writeIntArray(tag); dest.writeIntArray(tag);
dest.writeLongArray(rxBytes); dest.writeLongArray(rxBytes);
dest.writeLongArray(rxPackets); dest.writeLongArray(rxPackets);
@@ -131,15 +144,18 @@ public class NetworkStats implements Parcelable {
dest.writeLongArray(operations); dest.writeLongArray(operations);
} }
public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets, // @VisibleForTesting
long txBytes, long txPackets) { public NetworkStats addIfaceValues(
return addValues(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, 0L); String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
return addValues(
iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
} }
public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets, // @VisibleForTesting
long txBytes, long txPackets, long operations) { public NetworkStats addValues(String iface, int uid, int set, int tag, long rxBytes,
return addValues( long rxPackets, long txBytes, long txPackets, long operations) {
new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); return addValues(new Entry(
iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
} }
/** /**
@@ -151,6 +167,7 @@ public class NetworkStats implements Parcelable {
final int newLength = Math.max(iface.length, 10) * 3 / 2; final int newLength = Math.max(iface.length, 10) * 3 / 2;
iface = Arrays.copyOf(iface, newLength); iface = Arrays.copyOf(iface, newLength);
uid = Arrays.copyOf(uid, newLength); uid = Arrays.copyOf(uid, newLength);
set = Arrays.copyOf(set, newLength);
tag = Arrays.copyOf(tag, newLength); tag = Arrays.copyOf(tag, newLength);
rxBytes = Arrays.copyOf(rxBytes, newLength); rxBytes = Arrays.copyOf(rxBytes, newLength);
rxPackets = Arrays.copyOf(rxPackets, newLength); rxPackets = Arrays.copyOf(rxPackets, newLength);
@@ -161,6 +178,7 @@ public class NetworkStats implements Parcelable {
iface[size] = entry.iface; iface[size] = entry.iface;
uid[size] = entry.uid; uid[size] = entry.uid;
set[size] = entry.set;
tag[size] = entry.tag; tag[size] = entry.tag;
rxBytes[size] = entry.rxBytes; rxBytes[size] = entry.rxBytes;
rxPackets[size] = entry.rxPackets; rxPackets[size] = entry.rxPackets;
@@ -179,6 +197,7 @@ public class NetworkStats implements Parcelable {
final Entry entry = recycle != null ? recycle : new Entry(); final Entry entry = recycle != null ? recycle : new Entry();
entry.iface = iface[i]; entry.iface = iface[i];
entry.uid = uid[i]; entry.uid = uid[i];
entry.set = set[i];
entry.tag = tag[i]; entry.tag = tag[i];
entry.rxBytes = rxBytes[i]; entry.rxBytes = rxBytes[i];
entry.rxPackets = rxPackets[i]; entry.rxPackets = rxPackets[i];
@@ -201,19 +220,26 @@ public class NetworkStats implements Parcelable {
return iface.length; return iface.length;
} }
@Deprecated
public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
long txBytes, long txPackets, long operations) { long txBytes, long txPackets, long operations) {
return combineValues( return combineValues(
new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); iface, uid, SET_DEFAULT, tag, rxBytes, rxPackets, txBytes, txPackets, operations);
}
public NetworkStats combineValues(String iface, int uid, int set, int tag, long rxBytes,
long rxPackets, long txBytes, long txPackets, long operations) {
return combineValues(new Entry(
iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
} }
/** /**
* Combine given values with an existing row, or create a new row if * 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 * {@link #findIndex(String, int, int, int)} is unable to find match. Can
* used to subtract values from existing rows. * also be used to subtract values from existing rows.
*/ */
public NetworkStats combineValues(Entry entry) { public NetworkStats combineValues(Entry entry) {
final int i = findIndex(entry.iface, entry.uid, entry.tag); final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag);
if (i == -1) { if (i == -1) {
// only create new entry when positive contribution // only create new entry when positive contribution
addValues(entry); addValues(entry);
@@ -230,9 +256,10 @@ public class NetworkStats implements Parcelable {
/** /**
* Find first stats index that matches the requested parameters. * Find first stats index that matches the requested parameters.
*/ */
public int findIndex(String iface, int uid, int tag) { public int findIndex(String iface, int uid, int set, int tag) {
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
if (Objects.equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) { if (Objects.equal(iface, this.iface[i]) && uid == this.uid[i] && set == this.set[i]
&& tag == this.tag[i]) {
return i; return i;
} }
} }
@@ -246,7 +273,7 @@ public class NetworkStats implements Parcelable {
*/ */
public void spliceOperationsFrom(NetworkStats stats) { public void spliceOperationsFrom(NetworkStats stats) {
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
final int j = stats.findIndex(IFACE_ALL, uid[i], tag[i]); final int j = stats.findIndex(IFACE_ALL, uid[i], set[i], tag[i]);
if (j == -1) { if (j == -1) {
operations[i] = 0; operations[i] = 0;
} else { } else {
@@ -332,10 +359,11 @@ public class NetworkStats implements Parcelable {
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
entry.iface = iface[i]; entry.iface = iface[i];
entry.uid = uid[i]; entry.uid = uid[i];
entry.set = set[i];
entry.tag = tag[i]; entry.tag = tag[i];
// find remote row that matches, and subtract // find remote row that matches, and subtract
final int j = value.findIndex(entry.iface, entry.uid, entry.tag); final int j = value.findIndex(entry.iface, entry.uid, entry.set, entry.tag);
if (j == -1) { if (j == -1) {
// newly appearing row, return entire value // newly appearing row, return entire value
entry.rxBytes = rxBytes[i]; entry.rxBytes = rxBytes[i];
@@ -377,7 +405,8 @@ public class NetworkStats implements Parcelable {
pw.print(prefix); pw.print(prefix);
pw.print(" iface="); pw.print(iface[i]); pw.print(" iface="); pw.print(iface[i]);
pw.print(" uid="); pw.print(uid[i]); pw.print(" uid="); pw.print(uid[i]);
pw.print(" tag=0x"); pw.print(Integer.toHexString(tag[i])); pw.print(" set="); pw.print(setToString(set[i]));
pw.print(" tag="); pw.print(tagToString(tag[i]));
pw.print(" rxBytes="); pw.print(rxBytes[i]); pw.print(" rxBytes="); pw.print(rxBytes[i]);
pw.print(" rxPackets="); pw.print(rxPackets[i]); pw.print(" rxPackets="); pw.print(rxPackets[i]);
pw.print(" txBytes="); pw.print(txBytes[i]); pw.print(" txBytes="); pw.print(txBytes[i]);
@@ -386,6 +415,29 @@ public class NetworkStats implements Parcelable {
} }
} }
/**
* Return text description of {@link #set} value.
*/
public static String setToString(int set) {
switch (set) {
case SET_ALL:
return "ALL";
case SET_DEFAULT:
return "DEFAULT";
case SET_FOREGROUND:
return "FOREGROUND";
default:
return "UNKNOWN";
}
}
/**
* Return text description of {@link #tag} value.
*/
public static String tagToString(int tag) {
return "0x" + Integer.toHexString(tag);
}
@Override @Override
public String toString() { public String toString() {
final CharArrayWriter writer = new CharArrayWriter(); final CharArrayWriter writer = new CharArrayWriter();

View File

@@ -17,6 +17,7 @@
package android.net; package android.net;
import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.DataStreamUtils.readFullLongArray; import static android.net.NetworkStatsHistory.DataStreamUtils.readFullLongArray;
@@ -215,8 +216,8 @@ public class NetworkStatsHistory implements Parcelable {
*/ */
@Deprecated @Deprecated
public void recordData(long start, long end, long rxBytes, long txBytes) { public void recordData(long start, long end, long rxBytes, long txBytes) {
recordData(start, end, recordData(start, end, new NetworkStats.Entry(
new NetworkStats.Entry(IFACE_ALL, UID_ALL, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0L)); IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0L));
} }
/** /**
@@ -269,7 +270,7 @@ public class NetworkStatsHistory implements Parcelable {
*/ */
public void recordEntireHistory(NetworkStatsHistory input) { public void recordEntireHistory(NetworkStatsHistory input) {
final NetworkStats.Entry entry = new NetworkStats.Entry( final NetworkStats.Entry entry = new NetworkStats.Entry(
IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0L); IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
for (int i = 0; i < input.bucketCount; i++) { for (int i = 0; i < input.bucketCount; i++) {
final long start = input.bucketStart[i]; final long start = input.bucketStart[i];
final long end = start + input.bucketDuration; final long end = start + input.bucketDuration;
@@ -422,7 +423,7 @@ public class NetworkStatsHistory implements Parcelable {
ensureBuckets(start, end); ensureBuckets(start, end);
final NetworkStats.Entry entry = new NetworkStats.Entry( final NetworkStats.Entry entry = new NetworkStats.Entry(
IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0L); IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
final Random r = new Random(); final Random r = new Random();
while (rxBytes > 1024 || rxPackets > 128 || txBytes > 1024 || txPackets > 128 while (rxBytes > 1024 || rxPackets > 128 || txBytes > 1024 || txPackets > 128
|| operations > 32) { || operations > 32) {

View File

@@ -205,10 +205,6 @@ public class TrafficStats {
* @param operationCount Number of operations to increment count by. * @param operationCount Number of operations to increment count by.
*/ */
public static void incrementOperationCount(int tag, int operationCount) { public static void incrementOperationCount(int tag, int operationCount) {
if (operationCount < 0) {
throw new IllegalArgumentException("operation count can only be incremented");
}
final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
final int uid = android.os.Process.myUid(); final int uid = android.os.Process.myUid();

View File

@@ -26,6 +26,9 @@ import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID; import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED; import static android.net.TrafficStats.UID_REMOVED;
@@ -40,6 +43,8 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.IAlarmManager; import android.app.IAlarmManager;
@@ -63,17 +68,20 @@ import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.INetworkManagementService; import android.os.INetworkManagementService;
import android.os.Message;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.SystemClock; import android.os.SystemClock;
import android.provider.Settings; import android.provider.Settings;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.util.LongSparseArray;
import android.util.NtpTrustedTime; import android.util.NtpTrustedTime;
import android.util.Slog; import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TrustedTime; import android.util.TrustedTime;
import com.android.internal.os.AtomicFile; import com.android.internal.os.AtomicFile;
import com.android.internal.util.Objects;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps; import com.google.android.collect.Maps;
import com.google.android.collect.Sets; import com.google.android.collect.Sets;
@@ -88,6 +96,8 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.ProtocolException; import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -109,6 +119,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private static final int VERSION_UID_INIT = 1; private static final int VERSION_UID_INIT = 1;
private static final int VERSION_UID_WITH_IDENT = 2; private static final int VERSION_UID_WITH_IDENT = 2;
private static final int VERSION_UID_WITH_TAG = 3; private static final int VERSION_UID_WITH_TAG = 3;
private static final int VERSION_UID_WITH_SET = 4;
private static final int MSG_FORCE_UPDATE = 0x1;
private final Context mContext; private final Context mContext;
private final INetworkManagementService mNetworkManager; private final INetworkManagementService mNetworkManager;
@@ -156,8 +169,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
/** Set of historical network layer stats for known networks. */ /** Set of historical network layer stats for known networks. */
private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkStats = Maps.newHashMap(); private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkStats = Maps.newHashMap();
/** Set of historical network layer stats for known UIDs. */ /** Set of historical network layer stats for known UIDs. */
private HashMap<NetworkIdentitySet, LongSparseArray<NetworkStatsHistory>> mUidStats = private HashMap<UidStatsKey, NetworkStatsHistory> mUidStats = Maps.newHashMap();
Maps.newHashMap();
/** Flag if {@link #mUidStats} have been loaded from disk. */ /** Flag if {@link #mUidStats} have been loaded from disk. */
private boolean mUidStatsLoaded = false; private boolean mUidStatsLoaded = false;
@@ -167,6 +179,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private NetworkStats mLastUidSnapshot; private NetworkStats mLastUidSnapshot;
/** Current counter sets for each UID. */
private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
/** Data layer operation counters for splicing into other structures. */ /** Data layer operation counters for splicing into other structures. */
private NetworkStats mOperations = new NetworkStats(0L, 10); private NetworkStats mOperations = new NetworkStats(0L, 10);
private NetworkStats mLastOperationsSnapshot; private NetworkStats mLastOperationsSnapshot;
@@ -177,11 +192,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private final AtomicFile mNetworkFile; private final AtomicFile mNetworkFile;
private final AtomicFile mUidFile; private final AtomicFile mUidFile;
// TODO: collect detailed uid stats, storing tag-granularity data until next
// dropbox, and uid summary for a specific bucket count.
// TODO: periodically compile statistics and send to dropbox.
public NetworkStatsService( public NetworkStatsService(
Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) { Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) {
this(context, networkManager, alarmManager, NtpTrustedTime.getInstance(context), this(context, networkManager, alarmManager, NtpTrustedTime.getInstance(context),
@@ -207,7 +217,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mHandlerThread = new HandlerThread(TAG); mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start(); mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()); mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
mNetworkFile = new AtomicFile(new File(systemDir, "netstats.bin")); mNetworkFile = new AtomicFile(new File(systemDir, "netstats.bin"));
mUidFile = new AtomicFile(new File(systemDir, "netstats_uid.bin")); mUidFile = new AtomicFile(new File(systemDir, "netstats_uid.bin"));
@@ -246,6 +256,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "unable to register poll alarm"); Slog.w(TAG, "unable to register poll alarm");
} }
// kick off background poll to bootstrap deltas
mHandler.obtainMessage(MSG_FORCE_UPDATE).sendToTarget();
} }
private void shutdownLocked() { private void shutdownLocked() {
@@ -302,24 +315,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override @Override
public NetworkStatsHistory getHistoryForUid( public NetworkStatsHistory getHistoryForUid(
NetworkTemplate template, int uid, int tag, int fields) { NetworkTemplate template, int uid, int set, int tag, int fields) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
synchronized (mStatsLock) { synchronized (mStatsLock) {
ensureUidStatsLoadedLocked(); ensureUidStatsLoadedLocked();
final long packed = packUidAndTag(uid, tag);
// combine all interfaces that match template // combine all interfaces that match template
final NetworkStatsHistory combined = new NetworkStatsHistory( final NetworkStatsHistory combined = new NetworkStatsHistory(
mSettings.getUidBucketDuration(), estimateUidBuckets(), fields); mSettings.getUidBucketDuration(), estimateUidBuckets(), fields);
for (NetworkIdentitySet ident : mUidStats.keySet()) { for (UidStatsKey key : mUidStats.keySet()) {
if (templateMatches(template, ident)) { final boolean setMatches = set == SET_ALL || key.set == set;
final NetworkStatsHistory history = mUidStats.get(ident).get(packed); if (templateMatches(template, key.ident) && key.uid == uid && setMatches
if (history != null) { && key.tag == tag) {
combined.recordEntireHistory(history); final NetworkStatsHistory history = mUidStats.get(key);
} combined.recordEntireHistory(history);
} }
} }
return combined; return combined;
} }
} }
@@ -371,33 +384,27 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final NetworkStats.Entry entry = new NetworkStats.Entry(); final NetworkStats.Entry entry = new NetworkStats.Entry();
NetworkStatsHistory.Entry historyEntry = null; NetworkStatsHistory.Entry historyEntry = null;
for (NetworkIdentitySet ident : mUidStats.keySet()) { for (UidStatsKey key : mUidStats.keySet()) {
if (templateMatches(template, ident)) { if (templateMatches(template, key.ident)) {
final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident); // always include summary under TAG_NONE, and include
for (int i = 0; i < uidStats.size(); i++) { // other tags when requested.
final long packed = uidStats.keyAt(i); if (key.tag == TAG_NONE || includeTags) {
final int uid = unpackUid(packed); final NetworkStatsHistory history = mUidStats.get(key);
final int tag = unpackTag(packed); historyEntry = history.getValues(start, end, now, historyEntry);
// always include summary under TAG_NONE, and include entry.iface = IFACE_ALL;
// other tags when requested. entry.uid = key.uid;
if (tag == TAG_NONE || includeTags) { entry.set = key.set;
final NetworkStatsHistory history = uidStats.valueAt(i); entry.tag = key.tag;
historyEntry = history.getValues(start, end, now, historyEntry); entry.rxBytes = historyEntry.rxBytes;
entry.rxPackets = historyEntry.rxPackets;
entry.txBytes = historyEntry.txBytes;
entry.txPackets = historyEntry.txPackets;
entry.operations = historyEntry.operations;
entry.iface = IFACE_ALL; if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
entry.uid = uid; || entry.txPackets > 0 || entry.operations > 0) {
entry.tag = tag; stats.combineValues(entry);
entry.rxBytes = historyEntry.rxBytes;
entry.rxPackets = historyEntry.rxPackets;
entry.txBytes = historyEntry.txBytes;
entry.txPackets = historyEntry.txPackets;
entry.operations = historyEntry.operations;
if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
|| entry.txPackets > 0 || entry.operations > 0) {
stats.combineValues(entry);
}
} }
} }
} }
@@ -437,8 +444,31 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG); mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
} }
if (operationCount < 0) {
throw new IllegalArgumentException("operation count can only be incremented");
}
if (tag == TAG_NONE) {
throw new IllegalArgumentException("operation count must have specific tag");
}
synchronized (mStatsLock) { synchronized (mStatsLock) {
mOperations.combineValues(IFACE_ALL, uid, tag, 0L, 0L, 0L, 0L, operationCount); final int set = mActiveUidCounterSet.get(uid, SET_DEFAULT);
mOperations.combineValues(IFACE_ALL, uid, set, tag, 0L, 0L, 0L, 0L, operationCount);
mOperations.combineValues(IFACE_ALL, uid, set, TAG_NONE, 0L, 0L, 0L, 0L, operationCount);
}
}
@Override
public void setUidForeground(int uid, boolean uidForeground) {
mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
synchronized (mStatsLock) {
final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
final int oldSet = mActiveUidCounterSet.get(uid, SET_DEFAULT);
if (oldSet != set) {
mActiveUidCounterSet.put(uid, set);
setKernelCounterSet(uid, set);
}
} }
} }
@@ -601,7 +631,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
NetworkStats.Entry entry = null; NetworkStats.Entry entry = null;
for (String iface : persistDelta.getUniqueIfaces()) { for (String iface : persistDelta.getUniqueIfaces()) {
final int index = persistDelta.findIndex(iface, UID_ALL, TAG_NONE); final int index = persistDelta.findIndex(iface, UID_ALL, SET_DEFAULT, TAG_NONE);
entry = persistDelta.getValues(index, entry); entry = persistDelta.getValues(index, entry);
if (forcePersist || entry.rxBytes > persistThreshold if (forcePersist || entry.rxBytes > persistThreshold
|| entry.txBytes > persistThreshold) { || entry.txBytes > persistThreshold) {
@@ -676,31 +706,28 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
// splice in operation counts since last poll // splice in operation counts since last poll
final int j = operationsDelta.findIndex(IFACE_ALL, entry.uid, entry.tag); final int j = operationsDelta.findIndex(IFACE_ALL, entry.uid, entry.set, entry.tag);
if (j != -1) { if (j != -1) {
operationsEntry = operationsDelta.getValues(j, operationsEntry); operationsEntry = operationsDelta.getValues(j, operationsEntry);
entry.operations = operationsEntry.operations; entry.operations = operationsEntry.operations;
} }
final NetworkStatsHistory history = findOrCreateUidStatsLocked( final NetworkStatsHistory history = findOrCreateUidStatsLocked(
ident, entry.uid, entry.tag); ident, entry.uid, entry.set, entry.tag);
history.recordData(timeStart, currentTime, entry); history.recordData(timeStart, currentTime, entry);
} }
// trim any history beyond max // trim any history beyond max
final long maxUidHistory = mSettings.getUidMaxHistory(); final long maxUidHistory = mSettings.getUidMaxHistory();
final long maxTagHistory = mSettings.getTagMaxHistory(); final long maxTagHistory = mSettings.getTagMaxHistory();
for (LongSparseArray<NetworkStatsHistory> uidStats : mUidStats.values()) { for (UidStatsKey key : mUidStats.keySet()) {
for (int i = 0; i < uidStats.size(); i++) { final NetworkStatsHistory history = mUidStats.get(key);
final long packed = uidStats.keyAt(i);
final NetworkStatsHistory history = uidStats.valueAt(i);
// detailed tags are trimmed sooner than summary in TAG_NONE // detailed tags are trimmed sooner than summary in TAG_NONE
if (unpackTag(packed) == TAG_NONE) { if (key.tag == TAG_NONE) {
history.removeBucketsBefore(currentTime - maxUidHistory); history.removeBucketsBefore(currentTime - maxUidHistory);
} else { } else {
history.removeBucketsBefore(currentTime - maxTagHistory); history.removeBucketsBefore(currentTime - maxTagHistory);
}
} }
} }
@@ -715,26 +742,25 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private void removeUidLocked(int uid) { private void removeUidLocked(int uid) {
ensureUidStatsLoadedLocked(); ensureUidStatsLoadedLocked();
final ArrayList<UidStatsKey> knownKeys = Lists.newArrayList();
knownKeys.addAll(mUidStats.keySet());
// migrate all UID stats into special "removed" bucket // migrate all UID stats into special "removed" bucket
for (NetworkIdentitySet ident : mUidStats.keySet()) { for (UidStatsKey key : knownKeys) {
final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident); if (key.uid == uid) {
for (int i = 0; i < uidStats.size(); i++) { // only migrate combined TAG_NONE history
final long packed = uidStats.keyAt(i); if (key.tag == TAG_NONE) {
if (unpackUid(packed) == uid) { final NetworkStatsHistory uidHistory = mUidStats.get(key);
// only migrate combined TAG_NONE history final NetworkStatsHistory removedHistory = findOrCreateUidStatsLocked(
if (unpackTag(packed) == TAG_NONE) { key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
final NetworkStatsHistory uidHistory = uidStats.valueAt(i); removedHistory.recordEntireHistory(uidHistory);
final NetworkStatsHistory removedHistory = findOrCreateUidStatsLocked(
ident, UID_REMOVED, TAG_NONE);
removedHistory.recordEntireHistory(uidHistory);
}
uidStats.remove(packed);
} }
mUidStats.remove(key);
} }
} }
// TODO: push kernel event to wipe stats for UID, otherwise we risk // clear kernel stats associated with UID
// picking them up again during next poll. resetKernelUidStats(uid);
// since this was radical rewrite, push to disk // since this was radical rewrite, push to disk
writeUidStatsLocked(); writeUidStatsLocked();
@@ -763,17 +789,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
private NetworkStatsHistory findOrCreateUidStatsLocked( private NetworkStatsHistory findOrCreateUidStatsLocked(
NetworkIdentitySet ident, int uid, int tag) { NetworkIdentitySet ident, int uid, int set, int tag) {
ensureUidStatsLoadedLocked(); ensureUidStatsLoadedLocked();
LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident); final UidStatsKey key = new UidStatsKey(ident, uid, set, tag);
if (uidStats == null) { final NetworkStatsHistory existing = mUidStats.get(key);
uidStats = new LongSparseArray<NetworkStatsHistory>();
mUidStats.put(ident, uidStats);
}
final long packed = packUidAndTag(uid, tag);
final NetworkStatsHistory existing = uidStats.get(packed);
// update when no existing, or when bucket duration changed // update when no existing, or when bucket duration changed
final long bucketDuration = mSettings.getUidBucketDuration(); final long bucketDuration = mSettings.getUidBucketDuration();
@@ -787,7 +807,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
if (updated != null) { if (updated != null) {
uidStats.put(packed, updated); mUidStats.put(key, updated);
return updated; return updated;
} else { } else {
return existing; return existing;
@@ -874,25 +894,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// for a short time. // for a short time.
break; break;
} }
case VERSION_UID_WITH_TAG: { case VERSION_UID_WITH_TAG:
// uid := size *(NetworkIdentitySet size *(UID tag NetworkStatsHistory)) case VERSION_UID_WITH_SET: {
final int ifaceSize = in.readInt(); // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
for (int i = 0; i < ifaceSize; i++) { final int identSize = in.readInt();
for (int i = 0; i < identSize; i++) {
final NetworkIdentitySet ident = new NetworkIdentitySet(in); final NetworkIdentitySet ident = new NetworkIdentitySet(in);
final int childSize = in.readInt(); final int size = in.readInt();
final LongSparseArray<NetworkStatsHistory> uidStats = new LongSparseArray< for (int j = 0; j < size; j++) {
NetworkStatsHistory>(childSize);
for (int j = 0; j < childSize; j++) {
final int uid = in.readInt(); final int uid = in.readInt();
final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
: SET_DEFAULT;
final int tag = in.readInt(); final int tag = in.readInt();
final long packed = packUidAndTag(uid, tag);
final UidStatsKey key = new UidStatsKey(ident, uid, set, tag);
final NetworkStatsHistory history = new NetworkStatsHistory(in); final NetworkStatsHistory history = new NetworkStatsHistory(in);
uidStats.put(packed, history); mUidStats.put(key, history);
} }
mUidStats.put(ident, uidStats);
} }
break; break;
} }
@@ -949,29 +968,36 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// TODO: consider duplicating stats and releasing lock while writing // TODO: consider duplicating stats and releasing lock while writing
// build UidStatsKey lists grouped by ident
final HashMap<NetworkIdentitySet, ArrayList<UidStatsKey>> keysByIdent = Maps.newHashMap();
for (UidStatsKey key : mUidStats.keySet()) {
ArrayList<UidStatsKey> keys = keysByIdent.get(key.ident);
if (keys == null) {
keys = Lists.newArrayList();
keysByIdent.put(key.ident, keys);
}
keys.add(key);
}
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
fos = mUidFile.startWrite(); fos = mUidFile.startWrite();
final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos)); final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
out.writeInt(FILE_MAGIC); out.writeInt(FILE_MAGIC);
out.writeInt(VERSION_UID_WITH_TAG); out.writeInt(VERSION_UID_WITH_SET);
final int size = mUidStats.size(); out.writeInt(keysByIdent.size());
out.writeInt(size); for (NetworkIdentitySet ident : keysByIdent.keySet()) {
for (NetworkIdentitySet ident : mUidStats.keySet()) { final ArrayList<UidStatsKey> keys = keysByIdent.get(ident);
final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
ident.writeToStream(out); ident.writeToStream(out);
final int childSize = uidStats.size(); out.writeInt(keys.size());
out.writeInt(childSize); for (UidStatsKey key : keys) {
for (int i = 0; i < childSize; i++) { final NetworkStatsHistory history = mUidStats.get(key);
final long packed = uidStats.keyAt(i); out.writeInt(key.uid);
final int uid = unpackUid(packed); out.writeInt(key.set);
final int tag = unpackTag(packed); out.writeInt(key.tag);
final NetworkStatsHistory history = uidStats.valueAt(i);
out.writeInt(uid);
out.writeInt(tag);
history.writeToStream(out); history.writeToStream(out);
} }
} }
@@ -1030,20 +1056,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// from disk if not already in memory. // from disk if not already in memory.
ensureUidStatsLoadedLocked(); ensureUidStatsLoadedLocked();
pw.println("Detailed UID stats:"); final ArrayList<UidStatsKey> keys = Lists.newArrayList();
for (NetworkIdentitySet ident : mUidStats.keySet()) { keys.addAll(mUidStats.keySet());
pw.print(" ident="); pw.println(ident.toString()); Collections.sort(keys);
final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident); pw.println("Detailed UID stats:");
for (int i = 0; i < uidStats.size(); i++) { for (UidStatsKey key : keys) {
final long packed = uidStats.keyAt(i); pw.print(" ident="); pw.print(key.ident.toString());
final int uid = unpackUid(packed); pw.print(" uid="); pw.print(key.uid);
final int tag = unpackTag(packed); pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
final NetworkStatsHistory history = uidStats.valueAt(i); pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
pw.print(" UID="); pw.print(uid);
pw.print(" tag=0x"); pw.println(Integer.toHexString(tag)); final NetworkStatsHistory history = mUidStats.get(key);
history.dump(" ", pw, fullHistory); history.dump(" ", pw, fullHistory);
}
} }
} }
} }
@@ -1080,8 +1105,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
for (ApplicationInfo info : installedApps) { for (ApplicationInfo info : installedApps) {
final int uid = info.uid; final int uid = info.uid;
findOrCreateUidStatsLocked(ident, uid, TAG_NONE).generateRandom(UID_START, UID_END, findOrCreateUidStatsLocked(ident, uid, SET_DEFAULT, TAG_NONE).generateRandom(
UID_RX_BYTES, UID_RX_PACKETS, UID_TX_BYTES, UID_TX_PACKETS, UID_OPERATIONS); UID_START, UID_END, UID_RX_BYTES, UID_RX_PACKETS, UID_TX_BYTES,
UID_TX_PACKETS, UID_OPERATIONS);
findOrCreateUidStatsLocked(ident, uid, SET_FOREGROUND, TAG_NONE).generateRandom(
UID_START, UID_END, UID_RX_BYTES, UID_RX_PACKETS, UID_TX_BYTES,
UID_TX_PACKETS, UID_OPERATIONS);
} }
} }
} }
@@ -1116,23 +1145,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return (int) (existing.size() * existing.getBucketDuration() / newBucketDuration); return (int) (existing.size() * existing.getBucketDuration() / newBucketDuration);
} }
// @VisibleForTesting
public static long packUidAndTag(int uid, int tag) {
final long uidLong = uid;
final long tagLong = tag;
return (uidLong << 32) | (tagLong & 0xFFFFFFFFL);
}
// @VisibleForTesting
public static int unpackUid(long packed) {
return (int) (packed >> 32);
}
// @VisibleForTesting
public static int unpackTag(long packed) {
return (int) (packed & 0xFFFFFFFFL);
}
/** /**
* Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity} * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
* in the given {@link NetworkIdentitySet}. * in the given {@link NetworkIdentitySet}.
@@ -1146,6 +1158,58 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return false; return false;
} }
private Handler.Callback mHandlerCallback = new Handler.Callback() {
/** {@inheritDoc} */
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_FORCE_UPDATE: {
forceUpdate();
return true;
}
default: {
return false;
}
}
}
};
/**
* Key uniquely identifying a {@link NetworkStatsHistory} for a UID.
*/
private static class UidStatsKey implements Comparable<UidStatsKey> {
public final NetworkIdentitySet ident;
public final int uid;
public final int set;
public final int tag;
public UidStatsKey(NetworkIdentitySet ident, int uid, int set, int tag) {
this.ident = ident;
this.uid = uid;
this.set = set;
this.tag = tag;
}
@Override
public int hashCode() {
return Objects.hashCode(ident, uid, set, tag);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof UidStatsKey) {
final UidStatsKey key = (UidStatsKey) obj;
return Objects.equal(ident, key.ident) && uid == key.uid && set == key.set
&& tag == key.tag;
}
return false;
}
/** {@inheritDoc} */
public int compareTo(UidStatsKey another) {
return Integer.compare(uid, another.uid);
}
}
/** /**
* Default external settings that read from {@link Settings.Secure}. * Default external settings that read from {@link Settings.Secure}.
*/ */