Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a97122e authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

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
parent 39ebc219
Loading
Loading
Loading
Loading
+33 −46
Original line number Diff line number Diff line
@@ -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++;
            return this;
        }
        this.iface[size] = iface;
        this.uid[size] = uid;
        this.rx[size] = rx;
        this.tx[size] = tx;
        size++;

        public NetworkStats build() {
            if (mIndex != mIface.length) {
                throw new IllegalArgumentException("unexpected number of entries");
            }
            return new NetworkStats(mElapsedRealtime, mIface, mUid, mRx, mTx);
        }
        return this;
    }

    @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);
+10 −6
Original line number Diff line number Diff line
@@ -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);
+42 −15
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package android.net;

import android.os.SystemClock;
import android.test.suitebuilder.annotation.SmallTest;

import junit.framework.TestCase;
@@ -25,12 +24,14 @@ import junit.framework.TestCase;
public class NetworkStatsTest extends TestCase {

    private static final String TEST_IFACE = "test0";
    private static final int TEST_UID = 1001;
    private static final long TEST_START = 1194220800000L;

    public void testFindIndex() throws Exception {
        final NetworkStats stats = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
        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).build();
                .addEntry(TEST_IFACE, 102, 1024, 1024);

        assertEquals(2, stats.findIndex(TEST_IFACE, 102));
        assertEquals(2, stats.findIndex(TEST_IFACE, 102));
@@ -38,14 +39,40 @@ public class NetworkStatsTest extends TestCase {
        assertEquals(-1, stats.findIndex(TEST_IFACE, 6));
    }

    public void testAddEntryGrow() throws Exception {
        final NetworkStats stats = new NetworkStats(TEST_START, 2);

        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);

        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);

        assertEquals(5, stats.size);
        assertTrue(stats.iface.length >= 5);

        assertEquals(1L, stats.rx[0]);
        assertEquals(2L, stats.rx[1]);
        assertEquals(3L, stats.rx[2]);
        assertEquals(4L, stats.rx[3]);
        assertEquals(5L, stats.rx[4]);
    }

    public void testSubtractIdenticalData() throws Exception {
        final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
        final NetworkStats before = new NetworkStats(TEST_START, 2)
                .addEntry(TEST_IFACE, 100, 1024, 0)
                .addEntry(TEST_IFACE, 101, 0, 1024).build();
                .addEntry(TEST_IFACE, 101, 0, 1024);

        final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
        final NetworkStats after = new NetworkStats(TEST_START, 2)
                .addEntry(TEST_IFACE, 100, 1024, 0)
                .addEntry(TEST_IFACE, 101, 0, 1024).build();
                .addEntry(TEST_IFACE, 101, 0, 1024);

        final NetworkStats result = after.subtract(before);

@@ -57,13 +84,13 @@ public class NetworkStatsTest extends TestCase {
    }

    public void testSubtractIdenticalRows() throws Exception {
        final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
        final NetworkStats before = new NetworkStats(TEST_START, 2)
                .addEntry(TEST_IFACE, 100, 1024, 0)
                .addEntry(TEST_IFACE, 101, 0, 1024).build();
                .addEntry(TEST_IFACE, 101, 0, 1024);

        final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
        final NetworkStats after = new NetworkStats(TEST_START, 2)
                .addEntry(TEST_IFACE, 100, 1025, 2)
                .addEntry(TEST_IFACE, 101, 3, 1028).build();
                .addEntry(TEST_IFACE, 101, 3, 1028);

        final NetworkStats result = after.subtract(before);

@@ -75,14 +102,14 @@ public class NetworkStatsTest extends TestCase {
    }

    public void testSubtractNewRows() throws Exception {
        final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
        final NetworkStats before = new NetworkStats(TEST_START, 2)
                .addEntry(TEST_IFACE, 100, 1024, 0)
                .addEntry(TEST_IFACE, 101, 0, 1024).build();
                .addEntry(TEST_IFACE, 101, 0, 1024);

        final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
        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).build();
                .addEntry(TEST_IFACE, 102, 1024, 1024);

        final NetworkStats result = after.subtract(before);

+7 −9
Original line number Diff line number Diff line
@@ -882,8 +882,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");

        final String[] ifaces = listInterfaces();
        final NetworkStats.Builder stats = new NetworkStats.Builder(
                SystemClock.elapsedRealtime(), ifaces.length);
        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), ifaces.length);

        for (String iface : ifaces) {
            final long rx = getInterfaceCounter(iface, true);
@@ -891,7 +890,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
            stats.addEntry(iface, NetworkStats.UID_ALL, rx, tx);
        }

        return stats.build();
        return stats;
    }

    @Override
@@ -900,7 +899,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");

        final String[] knownUids = PATH_PROC_UID_STAT.list();
        final NetworkStats.Builder stats = new NetworkStats.Builder(
        final NetworkStats stats = new NetworkStats(
                SystemClock.elapsedRealtime(), knownUids.length);

        for (String uid : knownUids) {
@@ -908,7 +907,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
            collectNetworkStatsDetail(stats, uidInt);
        }

        return stats.build();
        return stats;
    }

    @Override
@@ -918,13 +917,12 @@ class NetworkManagementService extends INetworkManagementService.Stub {
                    android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
        }

        final NetworkStats.Builder stats = new NetworkStats.Builder(
                SystemClock.elapsedRealtime(), 1);
        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
        collectNetworkStatsDetail(stats, uid);
        return stats.build();
        return stats;
    }

    private void collectNetworkStatsDetail(NetworkStats.Builder stats, int uid) {
    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.
+23 −9
Original line number Diff line number Diff line
@@ -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}.
     */
Loading