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 com.android.internal.net;
18
19import static android.net.NetworkStats.SET_ALL;
20import static android.net.NetworkStats.TAG_NONE;
21import static android.net.NetworkStats.UID_ALL;
22import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
23
24import android.net.NetworkStats;
25import android.os.StrictMode;
26import android.os.SystemClock;
27
28import com.android.internal.util.ProcFileReader;
29
30import java.io.File;
31import java.io.FileInputStream;
32import java.io.IOException;
33
34import libcore.io.IoUtils;
35
36/**
37 * Creates {@link NetworkStats} instances by parsing various {@code /proc/}
38 * files as needed.
39 */
40public class NetworkStatsFactory {
41    private static final String TAG = "NetworkStatsFactory";
42
43    // TODO: consider moving parsing to native code
44
45    /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
46    private final File mStatsXtIfaceAll;
47    /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
48    private final File mStatsXtIfaceFmt;
49    /** Path to {@code /proc/net/xt_qtaguid/stats}. */
50    private final File mStatsXtUid;
51
52    public NetworkStatsFactory() {
53        this(new File("/proc/"));
54    }
55
56    // @VisibleForTesting
57    public NetworkStatsFactory(File procRoot) {
58        mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
59        mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
60        mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
61    }
62
63    /**
64     * Parse and return interface-level summary {@link NetworkStats} measured
65     * using {@code /proc/net/dev} style hooks, which may include non IP layer
66     * traffic. Values monotonically increase since device boot, and may include
67     * details about inactive interfaces.
68     *
69     * @throws IllegalStateException when problem parsing stats.
70     */
71    public NetworkStats readNetworkStatsSummaryDev() throws IllegalStateException {
72        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
73
74        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
75        final NetworkStats.Entry entry = new NetworkStats.Entry();
76
77        ProcFileReader reader = null;
78        try {
79            reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceAll));
80
81            while (reader.hasMoreData()) {
82                entry.iface = reader.nextString();
83                entry.uid = UID_ALL;
84                entry.set = SET_ALL;
85                entry.tag = TAG_NONE;
86
87                final boolean active = reader.nextInt() != 0;
88
89                // always include snapshot values
90                entry.rxBytes = reader.nextLong();
91                entry.rxPackets = reader.nextLong();
92                entry.txBytes = reader.nextLong();
93                entry.txPackets = reader.nextLong();
94
95                // fold in active numbers, but only when active
96                if (active) {
97                    entry.rxBytes += reader.nextLong();
98                    entry.rxPackets += reader.nextLong();
99                    entry.txBytes += reader.nextLong();
100                    entry.txPackets += reader.nextLong();
101                }
102
103                stats.addValues(entry);
104                reader.finishLine();
105            }
106        } catch (NullPointerException e) {
107            throw new IllegalStateException("problem parsing stats: " + e);
108        } catch (NumberFormatException e) {
109            throw new IllegalStateException("problem parsing stats: " + e);
110        } catch (IOException e) {
111            throw new IllegalStateException("problem parsing stats: " + e);
112        } finally {
113            IoUtils.closeQuietly(reader);
114            StrictMode.setThreadPolicy(savedPolicy);
115        }
116        return stats;
117    }
118
119    /**
120     * Parse and return interface-level summary {@link NetworkStats}. Designed
121     * to return only IP layer traffic. Values monotonically increase since
122     * device boot, and may include details about inactive interfaces.
123     *
124     * @throws IllegalStateException when problem parsing stats.
125     */
126    public NetworkStats readNetworkStatsSummaryXt() throws IllegalStateException {
127        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
128
129        // return null when kernel doesn't support
130        if (!mStatsXtIfaceFmt.exists()) return null;
131
132        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
133        final NetworkStats.Entry entry = new NetworkStats.Entry();
134
135        ProcFileReader reader = null;
136        try {
137            // open and consume header line
138            reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceFmt));
139            reader.finishLine();
140
141            while (reader.hasMoreData()) {
142                entry.iface = reader.nextString();
143                entry.uid = UID_ALL;
144                entry.set = SET_ALL;
145                entry.tag = TAG_NONE;
146
147                entry.rxBytes = reader.nextLong();
148                entry.rxPackets = reader.nextLong();
149                entry.txBytes = reader.nextLong();
150                entry.txPackets = reader.nextLong();
151
152                stats.addValues(entry);
153                reader.finishLine();
154            }
155        } catch (NullPointerException e) {
156            throw new IllegalStateException("problem parsing stats: " + e);
157        } catch (NumberFormatException e) {
158            throw new IllegalStateException("problem parsing stats: " + e);
159        } catch (IOException e) {
160            throw new IllegalStateException("problem parsing stats: " + e);
161        } finally {
162            IoUtils.closeQuietly(reader);
163            StrictMode.setThreadPolicy(savedPolicy);
164        }
165        return stats;
166    }
167
168    public NetworkStats readNetworkStatsDetail() {
169        return readNetworkStatsDetail(UID_ALL);
170    }
171
172    /**
173     * Parse and return {@link NetworkStats} with UID-level details. Values
174     * monotonically increase since device boot.
175     *
176     * @throws IllegalStateException when problem parsing stats.
177     */
178    public NetworkStats readNetworkStatsDetail(int limitUid) throws IllegalStateException {
179        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
180
181        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
182        final NetworkStats.Entry entry = new NetworkStats.Entry();
183
184        int idx = 1;
185        int lastIdx = 1;
186
187        ProcFileReader reader = null;
188        try {
189            // open and consume header line
190            reader = new ProcFileReader(new FileInputStream(mStatsXtUid));
191            reader.finishLine();
192
193            while (reader.hasMoreData()) {
194                idx = reader.nextInt();
195                if (idx != lastIdx + 1) {
196                    throw new IllegalStateException(
197                            "inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
198                }
199                lastIdx = idx;
200
201                entry.iface = reader.nextString();
202                entry.tag = kernelToTag(reader.nextString());
203                entry.uid = reader.nextInt();
204                entry.set = reader.nextInt();
205                entry.rxBytes = reader.nextLong();
206                entry.rxPackets = reader.nextLong();
207                entry.txBytes = reader.nextLong();
208                entry.txPackets = reader.nextLong();
209
210                if (limitUid == UID_ALL || limitUid == entry.uid) {
211                    stats.addValues(entry);
212                }
213
214                reader.finishLine();
215            }
216        } catch (NullPointerException e) {
217            throw new IllegalStateException("problem parsing idx " + idx, e);
218        } catch (NumberFormatException e) {
219            throw new IllegalStateException("problem parsing idx " + idx, e);
220        } catch (IOException e) {
221            throw new IllegalStateException("problem parsing idx " + idx, e);
222        } finally {
223            IoUtils.closeQuietly(reader);
224            StrictMode.setThreadPolicy(savedPolicy);
225        }
226
227        return stats;
228    }
229}
230