Growable NetworkStats object instead of builder.

NetworkStats now grows in place with arraycopy() instead of callers
needing to know record count a priori.  Better growth calculation for
both NetworkStats and NetworkStatsHistory; 50% each time.  Better
estimates of buckets needed in calling services.

Change-Id: I3adbffa0b7407612cc6349d9135a8b4eb63cd440
This commit is contained in:
Jeff Sharkey
2011-06-11 22:16:55 -07:00
parent 6fc33809a7
commit 755000588f
3 changed files with 66 additions and 61 deletions

View File

@@ -23,6 +23,7 @@ import android.util.SparseBooleanArray;
import java.io.CharArrayWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
/**
@@ -48,74 +49,60 @@ public class NetworkStats implements Parcelable {
* generated.
*/
public final long elapsedRealtime;
public final String[] iface;
public final int[] uid;
public final long[] rx;
public final long[] tx;
public int size;
public String[] iface;
public int[] uid;
public long[] rx;
public long[] tx;
// TODO: add fg/bg stats once reported by kernel
private NetworkStats(long elapsedRealtime, String[] iface, int[] uid, long[] rx, long[] tx) {
public NetworkStats(long elapsedRealtime, int initialSize) {
this.elapsedRealtime = elapsedRealtime;
this.iface = iface;
this.uid = uid;
this.rx = rx;
this.tx = tx;
this.size = 0;
this.iface = new String[initialSize];
this.uid = new int[initialSize];
this.rx = new long[initialSize];
this.tx = new long[initialSize];
}
public NetworkStats(Parcel parcel) {
elapsedRealtime = parcel.readLong();
size = parcel.readInt();
iface = parcel.createStringArray();
uid = parcel.createIntArray();
rx = parcel.createLongArray();
tx = parcel.createLongArray();
}
public static class Builder {
private long mElapsedRealtime;
private final String[] mIface;
private final int[] mUid;
private final long[] mRx;
private final long[] mTx;
private int mIndex = 0;
public Builder(long elapsedRealtime, int size) {
mElapsedRealtime = elapsedRealtime;
mIface = new String[size];
mUid = new int[size];
mRx = new long[size];
mTx = new long[size];
public NetworkStats addEntry(String iface, int uid, 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.rx = Arrays.copyOf(this.rx, newLength);
this.tx = Arrays.copyOf(this.tx, newLength);
}
public Builder addEntry(String iface, int uid, long rx, long tx) {
mIface[mIndex] = iface;
mUid[mIndex] = uid;
mRx[mIndex] = rx;
mTx[mIndex] = tx;
mIndex++;
this.iface[size] = iface;
this.uid[size] = uid;
this.rx[size] = rx;
this.tx[size] = tx;
size++;
return this;
}
public NetworkStats build() {
if (mIndex != mIface.length) {
throw new IllegalArgumentException("unexpected number of entries");
}
return new NetworkStats(mElapsedRealtime, mIface, mUid, mRx, mTx);
}
}
@Deprecated
public int length() {
// length is identical for all fields
return iface.length;
return size;
}
/**
* Find first stats index that matches the requested parameters.
*/
public int findIndex(String iface, int uid) {
final int length = length();
for (int i = 0; i < length; i++) {
for (int i = 0; i < size; i++) {
if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
return i;
}
@@ -195,9 +182,8 @@ public class NetworkStats implements Parcelable {
}
// result will have our rows, and elapsed time between snapshots
final int length = length();
final NetworkStats.Builder result = new NetworkStats.Builder(deltaRealtime, length);
for (int i = 0; i < length; i++) {
final NetworkStats result = new NetworkStats(deltaRealtime, size);
for (int i = 0; i < size; i++) {
final String iface = this.iface[i];
final int uid = this.uid[i];
@@ -221,7 +207,7 @@ public class NetworkStats implements Parcelable {
}
}
return result.build();
return result;
}
private static boolean equal(Object a, Object b) {
@@ -255,6 +241,7 @@ public class NetworkStats implements Parcelable {
/** {@inheritDoc} */
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(elapsedRealtime);
dest.writeInt(size);
dest.writeStringArray(iface);
dest.writeIntArray(uid);
dest.writeLongArray(rx);

View File

@@ -53,11 +53,15 @@ public class NetworkStatsHistory implements Parcelable {
public long[] tx;
public NetworkStatsHistory(long bucketDuration) {
this(bucketDuration, 10);
}
public NetworkStatsHistory(long bucketDuration, int initialSize) {
this.bucketDuration = bucketDuration;
bucketStart = new long[0];
rx = new long[0];
tx = new long[0];
bucketCount = bucketStart.length;
bucketStart = new long[initialSize];
rx = new long[initialSize];
tx = new long[initialSize];
bucketCount = 0;
}
public NetworkStatsHistory(Parcel in) {
@@ -168,8 +172,8 @@ public class NetworkStatsHistory implements Parcelable {
*/
private void insertBucket(int index, long start) {
// create more buckets when needed
if (bucketCount + 1 > bucketStart.length) {
final int newLength = bucketStart.length + 10;
if (bucketCount >= bucketStart.length) {
final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
bucketStart = Arrays.copyOf(bucketStart, newLength);
rx = Arrays.copyOf(rx, newLength);
tx = Arrays.copyOf(tx, newLength);

View File

@@ -255,7 +255,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// combine all interfaces that match template
final String subscriberId = getActiveSubscriberId();
final NetworkStatsHistory combined = new NetworkStatsHistory(
mSettings.getNetworkBucketDuration());
mSettings.getNetworkBucketDuration(), estimateNetworkBuckets());
for (InterfaceIdentity ident : mNetworkStats.keySet()) {
final NetworkStatsHistory history = mNetworkStats.get(ident);
if (ident.matchesTemplate(networkTemplate, subscriberId)) {
@@ -297,9 +297,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, 1);
final NetworkStats stats = new NetworkStats(end - start, 1);
stats.addEntry(IFACE_ALL, UID_ALL, tx, tx);
return stats.build();
return stats;
}
}
@@ -313,7 +313,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
ensureUidStatsLoadedLocked();
final int size = mUidStats.size();
final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, size);
final NetworkStats stats = new NetworkStats(end - start, size);
long[] total = new long[2];
for (int i = 0; i < size; i++) {
@@ -322,7 +322,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
total = history.getTotalData(start, end, total);
stats.addEntry(IFACE_ALL, uid, total[0], total[1]);
}
return stats.build();
return stats;
}
}
@@ -510,9 +510,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// update when no existing, or when bucket duration changed
NetworkStatsHistory updated = null;
if (existing == null) {
updated = new NetworkStatsHistory(bucketDuration);
updated = new NetworkStatsHistory(bucketDuration, 10);
} else if (existing.bucketDuration != bucketDuration) {
updated = new NetworkStatsHistory(bucketDuration);
updated = new NetworkStatsHistory(
bucketDuration, estimateResizeBuckets(existing, bucketDuration));
updated.recordEntireHistory(existing);
}
@@ -531,9 +532,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// update when no existing, or when bucket duration changed
NetworkStatsHistory updated = null;
if (existing == null) {
updated = new NetworkStatsHistory(bucketDuration);
updated = new NetworkStatsHistory(bucketDuration, 10);
} else if (existing.bucketDuration != bucketDuration) {
updated = new NetworkStatsHistory(bucketDuration);
updated = new NetworkStatsHistory(
bucketDuration, estimateResizeBuckets(existing, bucketDuration));
updated.recordEntireHistory(existing);
}
@@ -809,6 +811,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return telephony.getSubscriberId();
}
private int estimateNetworkBuckets() {
return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration());
}
private int estimateUidBuckets() {
return (int) (mSettings.getUidMaxHistory() / mSettings.getUidBucketDuration());
}
private static int estimateResizeBuckets(NetworkStatsHistory existing, long newBucketDuration) {
return (int) (existing.bucketCount * existing.bucketDuration / newBucketDuration);
}
/**
* Default external settings that read from {@link Settings.Secure}.
*/