NetworkStats.java revision 75279904202357565cf5a1cb11148d01f42b4569
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;
22
23import java.io.CharArrayWriter;
24import java.io.PrintWriter;
25import java.util.HashSet;
26
27/**
28 * Collection of active network statistics. Can contain summary details across
29 * all interfaces, or details with per-UID granularity. Internally stores data
30 * as a large table, closely matching {@code /proc/} data format. This structure
31 * optimizes for rapid in-memory comparison, but consider using
32 * {@link NetworkStatsHistory} when persisting.
33 *
34 * @hide
35 */
36public class NetworkStats implements Parcelable {
37    /** {@link #iface} value when interface details unavailable. */
38    public static final String IFACE_ALL = null;
39    /** {@link #uid} value when UID details unavailable. */
40    public static final int UID_ALL = -1;
41
42    // NOTE: data should only be accounted for once in this structure; if data
43    // is broken out, the summarized version should not be included.
44
45    /**
46     * {@link SystemClock#elapsedRealtime()} timestamp when this data was
47     * generated.
48     */
49    public final long elapsedRealtime;
50    public final String[] iface;
51    public final int[] uid;
52    public final long[] rx;
53    public final long[] tx;
54
55    // TODO: add fg/bg stats once reported by kernel
56
57    private NetworkStats(long elapsedRealtime, String[] iface, int[] uid, long[] rx, long[] tx) {
58        this.elapsedRealtime = elapsedRealtime;
59        this.iface = iface;
60        this.uid = uid;
61        this.rx = rx;
62        this.tx = tx;
63    }
64
65    public NetworkStats(Parcel parcel) {
66        elapsedRealtime = parcel.readLong();
67        iface = parcel.createStringArray();
68        uid = parcel.createIntArray();
69        rx = parcel.createLongArray();
70        tx = parcel.createLongArray();
71    }
72
73    public static class Builder {
74        private long mElapsedRealtime;
75        private final String[] mIface;
76        private final int[] mUid;
77        private final long[] mRx;
78        private final long[] mTx;
79
80        private int mIndex = 0;
81
82        public Builder(long elapsedRealtime, int size) {
83            mElapsedRealtime = elapsedRealtime;
84            mIface = new String[size];
85            mUid = new int[size];
86            mRx = new long[size];
87            mTx = new long[size];
88        }
89
90        public Builder addEntry(String iface, int uid, long rx, long tx) {
91            mIface[mIndex] = iface;
92            mUid[mIndex] = uid;
93            mRx[mIndex] = rx;
94            mTx[mIndex] = tx;
95            mIndex++;
96            return this;
97        }
98
99        public NetworkStats build() {
100            if (mIndex != mIface.length) {
101                throw new IllegalArgumentException("unexpected number of entries");
102            }
103            return new NetworkStats(mElapsedRealtime, mIface, mUid, mRx, mTx);
104        }
105    }
106
107    public int length() {
108        // length is identical for all fields
109        return iface.length;
110    }
111
112    /**
113     * Find first stats index that matches the requested parameters.
114     */
115    public int findIndex(String iface, int uid) {
116        final int length = length();
117        for (int i = 0; i < length; i++) {
118            if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
119                return i;
120            }
121        }
122        return -1;
123    }
124
125    /**
126     * Return list of unique interfaces known by this data structure.
127     */
128    public String[] getKnownIfaces() {
129        final HashSet<String> ifaces = new HashSet<String>();
130        for (String iface : this.iface) {
131            if (iface != IFACE_ALL) {
132                ifaces.add(iface);
133            }
134        }
135        return ifaces.toArray(new String[ifaces.size()]);
136    }
137
138    /**
139     * Subtract the given {@link NetworkStats}, effectively leaving the delta
140     * between two snapshots in time. Assumes that statistics rows collect over
141     * time, and that none of them have disappeared.
142     *
143     * @param enforceMonotonic Validate that incoming value is strictly
144     *            monotonic compared to this object.
145     */
146    public NetworkStats subtract(NetworkStats value, boolean enforceMonotonic) {
147        final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
148        if (enforceMonotonic && deltaRealtime < 0) {
149            throw new IllegalArgumentException("found non-monotonic realtime");
150        }
151
152        // result will have our rows, and elapsed time between snapshots
153        final int length = length();
154        final NetworkStats.Builder result = new NetworkStats.Builder(deltaRealtime, length);
155        for (int i = 0; i < length; i++) {
156            final String iface = this.iface[i];
157            final int uid = this.uid[i];
158
159            // find remote row that matches, and subtract
160            final int j = value.findIndex(iface, uid);
161            if (j == -1) {
162                // newly appearing row, return entire value
163                result.addEntry(iface, uid, this.rx[i], this.tx[i]);
164            } else {
165                // existing row, subtract remote value
166                final long rx = this.rx[i] - value.rx[j];
167                final long tx = this.tx[i] - value.tx[j];
168                if (enforceMonotonic && (rx < 0 || tx < 0)) {
169                    throw new IllegalArgumentException("found non-monotonic values");
170                }
171                result.addEntry(iface, uid, rx, tx);
172            }
173        }
174
175        return result.build();
176    }
177
178    private static boolean equal(Object a, Object b) {
179        return a == b || (a != null && a.equals(b));
180    }
181
182    public void dump(String prefix, PrintWriter pw) {
183        pw.print(prefix);
184        pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
185        for (int i = 0; i < iface.length; i++) {
186            pw.print(prefix);
187            pw.print("  iface="); pw.print(iface[i]);
188            pw.print(" uid="); pw.print(uid[i]);
189            pw.print(" rx="); pw.print(rx[i]);
190            pw.print(" tx="); pw.println(tx[i]);
191        }
192    }
193
194    @Override
195    public String toString() {
196        final CharArrayWriter writer = new CharArrayWriter();
197        dump("", new PrintWriter(writer));
198        return writer.toString();
199    }
200
201    /** {@inheritDoc} */
202    public int describeContents() {
203        return 0;
204    }
205
206    /** {@inheritDoc} */
207    public void writeToParcel(Parcel dest, int flags) {
208        dest.writeLong(elapsedRealtime);
209        dest.writeStringArray(iface);
210        dest.writeIntArray(uid);
211        dest.writeLongArray(rx);
212        dest.writeLongArray(tx);
213    }
214
215    public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
216        public NetworkStats createFromParcel(Parcel in) {
217            return new NetworkStats(in);
218        }
219
220        public NetworkStats[] newArray(int size) {
221            return new NetworkStats[size];
222        }
223    };
224}
225