NetworkStats.java revision 61ee0bbb5b87fb5c4c3dc219868d52743def3d2b
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.os.SystemClock;
22import android.util.SparseBooleanArray;
23
24import java.io.CharArrayWriter;
25import java.io.PrintWriter;
26import java.util.HashSet;
27
28/**
29 * Collection of active network statistics. Can contain summary details across
30 * all interfaces, or details with per-UID granularity. Internally stores data
31 * as a large table, closely matching {@code /proc/} data format. This structure
32 * optimizes for rapid in-memory comparison, but consider using
33 * {@link NetworkStatsHistory} when persisting.
34 *
35 * @hide
36 */
37public class NetworkStats implements Parcelable {
38    /** {@link #iface} value when interface details unavailable. */
39    public static final String IFACE_ALL = null;
40    /** {@link #uid} value when UID details unavailable. */
41    public static final int UID_ALL = -1;
42
43    // NOTE: data should only be accounted for once in this structure; if data
44    // is broken out, the summarized version should not be included.
45
46    /**
47     * {@link SystemClock#elapsedRealtime()} timestamp when this data was
48     * generated.
49     */
50    public final long elapsedRealtime;
51    public final String[] iface;
52    public final int[] uid;
53    public final long[] rx;
54    public final long[] tx;
55
56    // TODO: add fg/bg stats once reported by kernel
57
58    private NetworkStats(long elapsedRealtime, String[] iface, int[] uid, long[] rx, long[] tx) {
59        this.elapsedRealtime = elapsedRealtime;
60        this.iface = iface;
61        this.uid = uid;
62        this.rx = rx;
63        this.tx = tx;
64    }
65
66    public NetworkStats(Parcel parcel) {
67        elapsedRealtime = parcel.readLong();
68        iface = parcel.createStringArray();
69        uid = parcel.createIntArray();
70        rx = parcel.createLongArray();
71        tx = parcel.createLongArray();
72    }
73
74    public static class Builder {
75        private long mElapsedRealtime;
76        private final String[] mIface;
77        private final int[] mUid;
78        private final long[] mRx;
79        private final long[] mTx;
80
81        private int mIndex = 0;
82
83        public Builder(long elapsedRealtime, int size) {
84            mElapsedRealtime = elapsedRealtime;
85            mIface = new String[size];
86            mUid = new int[size];
87            mRx = new long[size];
88            mTx = new long[size];
89        }
90
91        public Builder addEntry(String iface, int uid, long rx, long tx) {
92            mIface[mIndex] = iface;
93            mUid[mIndex] = uid;
94            mRx[mIndex] = rx;
95            mTx[mIndex] = tx;
96            mIndex++;
97            return this;
98        }
99
100        public NetworkStats build() {
101            if (mIndex != mIface.length) {
102                throw new IllegalArgumentException("unexpected number of entries");
103            }
104            return new NetworkStats(mElapsedRealtime, mIface, mUid, mRx, mTx);
105        }
106    }
107
108    public int length() {
109        // length is identical for all fields
110        return iface.length;
111    }
112
113    /**
114     * Find first stats index that matches the requested parameters.
115     */
116    public int findIndex(String iface, int uid) {
117        final int length = length();
118        for (int i = 0; i < length; i++) {
119            if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
120                return i;
121            }
122        }
123        return -1;
124    }
125
126    /**
127     * Return list of unique interfaces known by this data structure.
128     */
129    public String[] getUniqueIfaces() {
130        final HashSet<String> ifaces = new HashSet<String>();
131        for (String iface : this.iface) {
132            if (iface != IFACE_ALL) {
133                ifaces.add(iface);
134            }
135        }
136        return ifaces.toArray(new String[ifaces.size()]);
137    }
138
139    /**
140     * Return list of unique UIDs known by this data structure.
141     */
142    public int[] getUniqueUids() {
143        final SparseBooleanArray uids = new SparseBooleanArray();
144        for (int uid : this.uid) {
145            uids.put(uid, true);
146        }
147
148        final int size = uids.size();
149        final int[] result = new int[size];
150        for (int i = 0; i < size; i++) {
151            result[i] = uids.keyAt(i);
152        }
153        return result;
154    }
155
156    /**
157     * Subtract the given {@link NetworkStats}, effectively leaving the delta
158     * between two snapshots in time. Assumes that statistics rows collect over
159     * time, and that none of them have disappeared.
160     *
161     * @param enforceMonotonic Validate that incoming value is strictly
162     *            monotonic compared to this object.
163     */
164    public NetworkStats subtract(NetworkStats value, boolean enforceMonotonic) {
165        final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
166        if (enforceMonotonic && deltaRealtime < 0) {
167            throw new IllegalArgumentException("found non-monotonic realtime");
168        }
169
170        // result will have our rows, and elapsed time between snapshots
171        final int length = length();
172        final NetworkStats.Builder result = new NetworkStats.Builder(deltaRealtime, length);
173        for (int i = 0; i < length; i++) {
174            final String iface = this.iface[i];
175            final int uid = this.uid[i];
176
177            // find remote row that matches, and subtract
178            final int j = value.findIndex(iface, uid);
179            if (j == -1) {
180                // newly appearing row, return entire value
181                result.addEntry(iface, uid, this.rx[i], this.tx[i]);
182            } else {
183                // existing row, subtract remote value
184                final long rx = this.rx[i] - value.rx[j];
185                final long tx = this.tx[i] - value.tx[j];
186                if (enforceMonotonic && (rx < 0 || tx < 0)) {
187                    throw new IllegalArgumentException("found non-monotonic values");
188                }
189                result.addEntry(iface, uid, rx, tx);
190            }
191        }
192
193        return result.build();
194    }
195
196    private static boolean equal(Object a, Object b) {
197        return a == b || (a != null && a.equals(b));
198    }
199
200    public void dump(String prefix, PrintWriter pw) {
201        pw.print(prefix);
202        pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
203        for (int i = 0; i < iface.length; i++) {
204            pw.print(prefix);
205            pw.print("  iface="); pw.print(iface[i]);
206            pw.print(" uid="); pw.print(uid[i]);
207            pw.print(" rx="); pw.print(rx[i]);
208            pw.print(" tx="); pw.println(tx[i]);
209        }
210    }
211
212    @Override
213    public String toString() {
214        final CharArrayWriter writer = new CharArrayWriter();
215        dump("", new PrintWriter(writer));
216        return writer.toString();
217    }
218
219    /** {@inheritDoc} */
220    public int describeContents() {
221        return 0;
222    }
223
224    /** {@inheritDoc} */
225    public void writeToParcel(Parcel dest, int flags) {
226        dest.writeLong(elapsedRealtime);
227        dest.writeStringArray(iface);
228        dest.writeIntArray(uid);
229        dest.writeLongArray(rx);
230        dest.writeLongArray(tx);
231    }
232
233    public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
234        public NetworkStats createFromParcel(Parcel in) {
235            return new NetworkStats(in);
236        }
237
238        public NetworkStats[] newArray(int size) {
239            return new NetworkStats[size];
240        }
241    };
242}
243