blob: 7687718b0693ff1f6c87e47e87d71880a61dc223 [file] [log] [blame]
Jeff Sharkey1059c3c2011-10-04 16:54:49 -07001/*
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
Chenbo Feng061cec72019-03-01 15:07:24 -080017package com.android.server.net;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070018
Benedict Wong5cc3e6a2019-06-11 18:21:30 -070019import static android.net.NetworkStats.INTERFACES_ALL;
Jeff Sharkey8fc27e82012-04-04 20:40:58 -070020import static android.net.NetworkStats.SET_ALL;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -080021import static android.net.NetworkStats.TAG_ALL;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070022import static android.net.NetworkStats.TAG_NONE;
23import static android.net.NetworkStats.UID_ALL;
Chenbo Feng6880d632018-12-22 13:20:31 -080024
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070025import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
26
Remi NGUYEN VAN088ff682018-03-06 12:36:54 +090027import android.annotation.Nullable;
Chenbo Fengdb910392019-02-27 19:07:39 -080028import android.net.INetd;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070029import android.net.NetworkStats;
Chenbo Fengdb910392019-02-27 19:07:39 -080030import android.net.util.NetdService;
31import android.os.RemoteException;
Jeff Sharkey453dafa2012-02-27 17:42:34 -080032import android.os.StrictMode;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070033import android.os.SystemClock;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070034
Chenbo Fengbce73642019-04-11 18:44:45 -070035import com.android.internal.annotations.GuardedBy;
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080036import com.android.internal.annotations.VisibleForTesting;
Benedict Wongc9511e72019-06-12 17:46:31 +000037import com.android.internal.net.VpnInfo;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -080038import com.android.internal.util.ArrayUtils;
Jeff Sharkey163e6442011-10-31 16:37:52 -070039import com.android.internal.util.ProcFileReader;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070040
Jeff Sharkeyeb2c2c72014-08-11 15:22:51 -070041import libcore.io.IoUtils;
42
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070043import java.io.File;
Jeff Sharkey163e6442011-10-31 16:37:52 -070044import java.io.FileInputStream;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070045import java.io.IOException;
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -080046import java.net.ProtocolException;
Remi NGUYEN VAN088ff682018-03-06 12:36:54 +090047import java.util.Arrays;
48import java.util.HashSet;
49import java.util.Map;
50import java.util.concurrent.ConcurrentHashMap;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070051
52/**
53 * Creates {@link NetworkStats} instances by parsing various {@code /proc/}
54 * files as needed.
Ricky Wai0aa3aef2017-11-09 15:22:59 +000055 *
56 * @hide
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070057 */
58public class NetworkStatsFactory {
59 private static final String TAG = "NetworkStatsFactory";
60
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -080061 private static final boolean USE_NATIVE_PARSING = true;
62 private static final boolean SANITY_CHECK_NATIVE = false;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070063
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070064 /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
65 private final File mStatsXtIfaceAll;
Jeff Sharkeye8914c32012-05-01 16:26:09 -070066 /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
67 private final File mStatsXtIfaceFmt;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -070068 /** Path to {@code /proc/net/xt_qtaguid/stats}. */
69 private final File mStatsXtUid;
70
Benedict Wong51a770b2019-06-20 14:46:35 -070071 private final boolean mUseBpfStats;
Chenbo Feng828f1b42017-11-20 17:03:59 -080072
Chenbo Fengdb910392019-02-27 19:07:39 -080073 private INetd mNetdService;
74
Benedict Wongc9511e72019-06-12 17:46:31 +000075 /**
76 * Guards persistent data access in this class
77 *
78 * <p>In order to prevent deadlocks, critical sections protected by this lock SHALL NOT call out
79 * to other code that will acquire other locks within the system server. See b/134244752.
80 */
81 private static final Object sPersistentDataLock = new Object();
Chenbo Fengb29a6162019-01-10 14:48:54 -080082
Benedict Wongc9511e72019-06-12 17:46:31 +000083 /** Set containing info about active VPNs and their underlying networks. */
84 private static volatile VpnInfo[] sVpnInfos = new VpnInfo[0];
85
86 // A persistent snapshot of cumulative stats since device start
87 @GuardedBy("sPersistentDataLock")
88 private NetworkStats mPersistSnapshot;
89
90 // The persistent snapshot of tun and 464xlat adjusted stats since device start
91 @GuardedBy("sPersistentDataLock")
92 private NetworkStats mTunAnd464xlatAdjustedStats;
93
Remi NGUYEN VAN088ff682018-03-06 12:36:54 +090094 /**
95 * (Stacked interface) -> (base interface) association for all connected ifaces since boot.
96 *
97 * Because counters must never roll backwards, once a given interface is stacked on top of an
98 * underlying interface, the stacked interface can never be stacked on top of
99 * another interface. */
100 private static final ConcurrentHashMap<String, String> sStackedIfaces
101 = new ConcurrentHashMap<>();
Jeff Sharkeyeb2c2c72014-08-11 15:22:51 -0700102
103 public static void noteStackedIface(String stackedIface, String baseIface) {
Remi NGUYEN VAN088ff682018-03-06 12:36:54 +0900104 if (stackedIface != null && baseIface != null) {
105 sStackedIfaces.put(stackedIface, baseIface);
106 }
107 }
108
109 /**
Benedict Wongc9511e72019-06-12 17:46:31 +0000110 * Set active VPN information for data usage migration purposes
111 *
112 * <p>Traffic on TUN-based VPNs inherently all appear to be originated from the VPN providing
113 * app's UID. This method is used to support migration of VPN data usage, ensuring data is
114 * accurately billed to the real owner of the traffic.
115 *
116 * @param vpnArray The snapshot of the currently-running VPNs.
117 */
118 public static void updateVpnInfos(VpnInfo[] vpnArray) {
119 sVpnInfos = vpnArray.clone();
120 }
121
122 @VisibleForTesting
123 public static VpnInfo[] getVpnInfos() {
124 return sVpnInfos.clone();
125 }
126
127 /**
Remi NGUYEN VAN088ff682018-03-06 12:36:54 +0900128 * Get a set of interfaces containing specified ifaces and stacked interfaces.
129 *
130 * <p>The added stacked interfaces are ifaces stacked on top of the specified ones, or ifaces
131 * on which the specified ones are stacked. Stacked interfaces are those noted with
132 * {@link #noteStackedIface(String, String)}, but only interfaces noted before this method
133 * is called are guaranteed to be included.
134 */
Remi NGUYEN VAN9fb55e42018-02-27 16:47:22 +0900135 public static String[] augmentWithStackedInterfaces(@Nullable String[] requiredIfaces) {
Remi NGUYEN VAN088ff682018-03-06 12:36:54 +0900136 if (requiredIfaces == NetworkStats.INTERFACES_ALL) {
137 return null;
138 }
139
140 HashSet<String> relatedIfaces = new HashSet<>(Arrays.asList(requiredIfaces));
141 // ConcurrentHashMap's EntrySet iterators are "guaranteed to traverse
142 // elements as they existed upon construction exactly once, and may
143 // (but are not guaranteed to) reflect any modifications subsequent to construction".
144 // This is enough here.
145 for (Map.Entry<String, String> entry : sStackedIfaces.entrySet()) {
146 if (relatedIfaces.contains(entry.getKey())) {
147 relatedIfaces.add(entry.getValue());
148 } else if (relatedIfaces.contains(entry.getValue())) {
149 relatedIfaces.add(entry.getKey());
Jeff Sharkeyeb2c2c72014-08-11 15:22:51 -0700150 }
151 }
Remi NGUYEN VAN088ff682018-03-06 12:36:54 +0900152
153 String[] outArray = new String[relatedIfaces.size()];
154 return relatedIfaces.toArray(outArray);
155 }
156
Remi NGUYEN VAN9fb55e42018-02-27 16:47:22 +0900157 /**
158 * Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
junyulaic33ac0d2018-10-19 21:14:30 +0800159 * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map, boolean)
Remi NGUYEN VAN9fb55e42018-02-27 16:47:22 +0900160 */
161 public static void apply464xlatAdjustments(NetworkStats baseTraffic,
junyulaic33ac0d2018-10-19 21:14:30 +0800162 NetworkStats stackedTraffic, boolean useBpfStats) {
163 NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces,
164 useBpfStats);
Remi NGUYEN VAN9fb55e42018-02-27 16:47:22 +0900165 }
166
Remi NGUYEN VAN088ff682018-03-06 12:36:54 +0900167 @VisibleForTesting
168 public static void clearStackedIfaces() {
169 sStackedIfaces.clear();
Jeff Sharkeyeb2c2c72014-08-11 15:22:51 -0700170 }
171
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700172 public NetworkStatsFactory() {
Chenbo Feng6880d632018-12-22 13:20:31 -0800173 this(new File("/proc/"), new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists());
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700174 }
175
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800176 @VisibleForTesting
Chenbo Feng828f1b42017-11-20 17:03:59 -0800177 public NetworkStatsFactory(File procRoot, boolean useBpfStats) {
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700178 mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
Jeff Sharkeye8914c32012-05-01 16:26:09 -0700179 mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
180 mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
Chenbo Feng828f1b42017-11-20 17:03:59 -0800181 mUseBpfStats = useBpfStats;
Chenbo Fengb29a6162019-01-10 14:48:54 -0800182 mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
Benedict Wongc9511e72019-06-12 17:46:31 +0000183 mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700184 }
185
Chenbo Fengdcc56782018-04-18 15:44:46 -0700186 public NetworkStats readBpfNetworkStatsDev() throws IOException {
187 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
188 if (nativeReadNetworkStatsDev(stats) != 0) {
189 throw new IOException("Failed to parse bpf iface stats");
190 }
191 return stats;
192 }
193
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700194 /**
Jeff Sharkeye8914c32012-05-01 16:26:09 -0700195 * Parse and return interface-level summary {@link NetworkStats} measured
196 * using {@code /proc/net/dev} style hooks, which may include non IP layer
197 * traffic. Values monotonically increase since device boot, and may include
198 * details about inactive interfaces.
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700199 *
200 * @throws IllegalStateException when problem parsing stats.
201 */
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800202 public NetworkStats readNetworkStatsSummaryDev() throws IOException {
Chenbo Feng93afbf22018-01-18 19:48:52 -0800203
Chenbo Fengdcc56782018-04-18 15:44:46 -0700204 // Return xt_bpf stats if switched to bpf module.
Chenbo Feng93afbf22018-01-18 19:48:52 -0800205 if (mUseBpfStats)
Chenbo Fengdcc56782018-04-18 15:44:46 -0700206 return readBpfNetworkStatsDev();
Chenbo Feng93afbf22018-01-18 19:48:52 -0800207
Jeff Sharkey453dafa2012-02-27 17:42:34 -0800208 final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
209
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700210 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
211 final NetworkStats.Entry entry = new NetworkStats.Entry();
212
Jeff Sharkey1d29a302012-02-27 18:08:01 -0800213 ProcFileReader reader = null;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700214 try {
Jeff Sharkey1d29a302012-02-27 18:08:01 -0800215 reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceAll));
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700216
Jeff Sharkey1d29a302012-02-27 18:08:01 -0800217 while (reader.hasMoreData()) {
218 entry.iface = reader.nextString();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700219 entry.uid = UID_ALL;
Jeff Sharkey8fc27e82012-04-04 20:40:58 -0700220 entry.set = SET_ALL;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700221 entry.tag = TAG_NONE;
222
Jeff Sharkey1d29a302012-02-27 18:08:01 -0800223 final boolean active = reader.nextInt() != 0;
224
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700225 // always include snapshot values
Jeff Sharkey1d29a302012-02-27 18:08:01 -0800226 entry.rxBytes = reader.nextLong();
227 entry.rxPackets = reader.nextLong();
228 entry.txBytes = reader.nextLong();
229 entry.txPackets = reader.nextLong();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700230
231 // fold in active numbers, but only when active
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700232 if (active) {
Jeff Sharkey1d29a302012-02-27 18:08:01 -0800233 entry.rxBytes += reader.nextLong();
234 entry.rxPackets += reader.nextLong();
235 entry.txBytes += reader.nextLong();
236 entry.txPackets += reader.nextLong();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700237 }
238
239 stats.addValues(entry);
Jeff Sharkey1d29a302012-02-27 18:08:01 -0800240 reader.finishLine();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700241 }
Hugo Benichie1bb3a12017-04-07 15:20:56 +0900242 } catch (NullPointerException|NumberFormatException e) {
Neil Fuller038b20c2018-07-03 19:22:45 +0100243 throw protocolExceptionWithCause("problem parsing stats", e);
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700244 } finally {
245 IoUtils.closeQuietly(reader);
Jeff Sharkey453dafa2012-02-27 17:42:34 -0800246 StrictMode.setThreadPolicy(savedPolicy);
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700247 }
248 return stats;
249 }
250
251 /**
Jeff Sharkeye8914c32012-05-01 16:26:09 -0700252 * Parse and return interface-level summary {@link NetworkStats}. Designed
253 * to return only IP layer traffic. Values monotonically increase since
254 * device boot, and may include details about inactive interfaces.
255 *
256 * @throws IllegalStateException when problem parsing stats.
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700257 */
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800258 public NetworkStats readNetworkStatsSummaryXt() throws IOException {
Chenbo Feng93afbf22018-01-18 19:48:52 -0800259
Chenbo Fengdcc56782018-04-18 15:44:46 -0700260 // Return xt_bpf stats if qtaguid module is replaced.
Chenbo Feng93afbf22018-01-18 19:48:52 -0800261 if (mUseBpfStats)
Chenbo Fengdcc56782018-04-18 15:44:46 -0700262 return readBpfNetworkStatsDev();
Chenbo Feng93afbf22018-01-18 19:48:52 -0800263
Jeff Sharkey453dafa2012-02-27 17:42:34 -0800264 final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
265
Jeff Sharkeye8914c32012-05-01 16:26:09 -0700266 // return null when kernel doesn't support
267 if (!mStatsXtIfaceFmt.exists()) return null;
268
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700269 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
270 final NetworkStats.Entry entry = new NetworkStats.Entry();
271
Jeff Sharkeye8914c32012-05-01 16:26:09 -0700272 ProcFileReader reader = null;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700273 try {
Jeff Sharkeye8914c32012-05-01 16:26:09 -0700274 // open and consume header line
275 reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceFmt));
276 reader.finishLine();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700277
Jeff Sharkeye8914c32012-05-01 16:26:09 -0700278 while (reader.hasMoreData()) {
279 entry.iface = reader.nextString();
280 entry.uid = UID_ALL;
281 entry.set = SET_ALL;
282 entry.tag = TAG_NONE;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700283
Jeff Sharkeye8914c32012-05-01 16:26:09 -0700284 entry.rxBytes = reader.nextLong();
285 entry.rxPackets = reader.nextLong();
286 entry.txBytes = reader.nextLong();
287 entry.txPackets = reader.nextLong();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700288
Jeff Sharkeye8914c32012-05-01 16:26:09 -0700289 stats.addValues(entry);
290 reader.finishLine();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700291 }
Hugo Benichie1bb3a12017-04-07 15:20:56 +0900292 } catch (NullPointerException|NumberFormatException e) {
Neil Fuller038b20c2018-07-03 19:22:45 +0100293 throw protocolExceptionWithCause("problem parsing stats", e);
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700294 } finally {
295 IoUtils.closeQuietly(reader);
Jeff Sharkey453dafa2012-02-27 17:42:34 -0800296 StrictMode.setThreadPolicy(savedPolicy);
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700297 }
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700298 return stats;
299 }
300
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800301 public NetworkStats readNetworkStatsDetail() throws IOException {
Benedict Wong5cc3e6a2019-06-11 18:21:30 -0700302 return readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700303 }
304
Benedict Wong51a770b2019-06-20 14:46:35 -0700305 @GuardedBy("sPersistentDataLock")
306 private void requestSwapActiveStatsMapLocked() throws RemoteException {
307 // Ask netd to do a active map stats swap. When the binder call successfully returns,
308 // the system server should be able to safely read and clean the inactive map
309 // without race problem.
310 if (mNetdService == null) {
311 mNetdService = NetdService.getInstance();
312 }
313 mNetdService.trafficSwapActiveStatsMap();
314 }
315
Benedict Wong5cc3e6a2019-06-11 18:21:30 -0700316 /**
317 * Reads the detailed UID stats based on the provided parameters
318 *
319 * @param limitUid the UID to limit this query to
320 * @param limitIfaces the interfaces to limit this query to. Use {@link
321 * NetworkStats.INTERFACES_ALL} to select all interfaces
322 * @param limitTag the tags to limit this query to
323 * @return the NetworkStats instance containing network statistics at the present time.
324 */
325 public NetworkStats readNetworkStatsDetail(
Benedict Wong5cc3e6a2019-06-11 18:21:30 -0700326 int limitUid, String[] limitIfaces, int limitTag) throws IOException {
Benedict Wongc9511e72019-06-12 17:46:31 +0000327 // In order to prevent deadlocks, anything protected by this lock MUST NOT call out to other
328 // code that will acquire other locks within the system server. See b/134244752.
329 synchronized (sPersistentDataLock) {
330 // Take a reference. If this gets swapped out, we still have the old reference.
331 final VpnInfo[] vpnArray = sVpnInfos;
332 // Take a defensive copy. mPersistSnapshot is mutated in some cases below
333 final NetworkStats prev = mPersistSnapshot.clone();
334
335 if (USE_NATIVE_PARSING) {
336 final NetworkStats stats =
337 new NetworkStats(SystemClock.elapsedRealtime(), 0 /* initialSize */);
338 if (mUseBpfStats) {
Chenbo Fengdb910392019-02-27 19:07:39 -0800339 try {
340 requestSwapActiveStatsMapLocked();
341 } catch (RemoteException e) {
342 throw new IOException(e);
343 }
344 // Stats are always read from the inactive map, so they must be read after the
345 // swap
Chenbo Fengbce73642019-04-11 18:44:45 -0700346 if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
Benedict Wongc9511e72019-06-12 17:46:31 +0000347 INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
Chenbo Fengbce73642019-04-11 18:44:45 -0700348 throw new IOException("Failed to parse network stats");
349 }
Benedict Wongc9511e72019-06-12 17:46:31 +0000350
351 // BPF stats are incremental; fold into mPersistSnapshot.
Chenbo Fengbce73642019-04-11 18:44:45 -0700352 mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
353 mPersistSnapshot.combineAllValues(stats);
Benedict Wongc9511e72019-06-12 17:46:31 +0000354 } else {
355 if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
356 INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
357 throw new IOException("Failed to parse network stats");
358 }
359 if (SANITY_CHECK_NATIVE) {
360 final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid,
361 UID_ALL, INTERFACES_ALL, TAG_ALL);
362 assertEquals(javaStats, stats);
363 }
364
365 mPersistSnapshot = stats;
Chenbo Fengb29a6162019-01-10 14:48:54 -0800366 }
Chenbo Fengb29a6162019-01-10 14:48:54 -0800367 } else {
Benedict Wongc9511e72019-06-12 17:46:31 +0000368 mPersistSnapshot = javaReadNetworkStatsDetail(mStatsXtUid, UID_ALL, INTERFACES_ALL,
369 TAG_ALL);
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800370 }
Benedict Wongc9511e72019-06-12 17:46:31 +0000371
372 NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray);
373
374 // Filter return values
375 adjustedStats.filter(limitUid, limitIfaces, limitTag);
376 return adjustedStats;
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800377 }
378 }
379
Benedict Wongc9511e72019-06-12 17:46:31 +0000380 @GuardedBy("sPersistentDataLock")
381 private NetworkStats adjustForTunAnd464Xlat(
382 NetworkStats uidDetailStats, NetworkStats previousStats, VpnInfo[] vpnArray) {
383 // Calculate delta from last snapshot
384 final NetworkStats delta = uidDetailStats.subtract(previousStats);
385
386 // Apply 464xlat adjustments before VPN adjustments. If VPNs are using v4 on a v6 only
387 // network, the overhead is their fault.
388 // No locking here: apply464xlatAdjustments behaves fine with an add-only
389 // ConcurrentHashMap.
390 delta.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats);
391
392 // Migrate data usage over a VPN to the TUN network.
393 for (VpnInfo info : vpnArray) {
394 delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces);
395 }
396
397 // Filter out debug entries as that may lead to over counting.
398 delta.filterDebugEntries();
399
400 // Update mTunAnd464xlatAdjustedStats with migrated delta.
401 mTunAnd464xlatAdjustedStats.combineAllValues(delta);
402 mTunAnd464xlatAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime());
403
404 return mTunAnd464xlatAdjustedStats.clone();
405 }
406
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700407 /**
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800408 * Parse and return {@link NetworkStats} with UID-level details. Values are
409 * expected to monotonically increase since device boot.
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700410 */
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800411 @VisibleForTesting
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800412 public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid,
413 String[] limitIfaces, int limitTag)
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800414 throws IOException {
Jeff Sharkey453dafa2012-02-27 17:42:34 -0800415 final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
416
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700417 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
418 final NetworkStats.Entry entry = new NetworkStats.Entry();
419
Jeff Sharkey163e6442011-10-31 16:37:52 -0700420 int idx = 1;
421 int lastIdx = 1;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700422
Jeff Sharkey163e6442011-10-31 16:37:52 -0700423 ProcFileReader reader = null;
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700424 try {
Jeff Sharkey163e6442011-10-31 16:37:52 -0700425 // open and consume header line
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800426 reader = new ProcFileReader(new FileInputStream(detailPath));
Jeff Sharkey163e6442011-10-31 16:37:52 -0700427 reader.finishLine();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700428
Jeff Sharkey163e6442011-10-31 16:37:52 -0700429 while (reader.hasMoreData()) {
430 idx = reader.nextInt();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700431 if (idx != lastIdx + 1) {
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800432 throw new ProtocolException(
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700433 "inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
434 }
435 lastIdx = idx;
436
Jeff Sharkey163e6442011-10-31 16:37:52 -0700437 entry.iface = reader.nextString();
438 entry.tag = kernelToTag(reader.nextString());
439 entry.uid = reader.nextInt();
440 entry.set = reader.nextInt();
441 entry.rxBytes = reader.nextLong();
442 entry.rxPackets = reader.nextLong();
443 entry.txBytes = reader.nextLong();
444 entry.txPackets = reader.nextLong();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700445
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800446 if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
447 && (limitUid == UID_ALL || limitUid == entry.uid)
448 && (limitTag == TAG_ALL || limitTag == entry.tag)) {
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700449 stats.addValues(entry);
450 }
Jeff Sharkey163e6442011-10-31 16:37:52 -0700451
452 reader.finishLine();
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700453 }
Hugo Benichie1bb3a12017-04-07 15:20:56 +0900454 } catch (NullPointerException|NumberFormatException e) {
Neil Fuller038b20c2018-07-03 19:22:45 +0100455 throw protocolExceptionWithCause("problem parsing idx " + idx, e);
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700456 } finally {
457 IoUtils.closeQuietly(reader);
Jeff Sharkey453dafa2012-02-27 17:42:34 -0800458 StrictMode.setThreadPolicy(savedPolicy);
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700459 }
Jeff Sharkey163e6442011-10-31 16:37:52 -0700460
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700461 return stats;
462 }
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800463
464 public void assertEquals(NetworkStats expected, NetworkStats actual) {
465 if (expected.size() != actual.size()) {
466 throw new AssertionError(
467 "Expected size " + expected.size() + ", actual size " + actual.size());
468 }
469
470 NetworkStats.Entry expectedRow = null;
471 NetworkStats.Entry actualRow = null;
472 for (int i = 0; i < expected.size(); i++) {
473 expectedRow = expected.getValues(i, expectedRow);
474 actualRow = actual.getValues(i, actualRow);
475 if (!expectedRow.equals(actualRow)) {
476 throw new AssertionError(
477 "Expected row " + i + ": " + expectedRow + ", actual row " + actualRow);
478 }
479 }
480 }
481
482 /**
483 * Parse statistics from file into given {@link NetworkStats} object. Values
484 * are expected to monotonically increase since device boot.
485 */
486 @VisibleForTesting
Chenbo Feng828f1b42017-11-20 17:03:59 -0800487 public static native int nativeReadNetworkStatsDetail(NetworkStats stats, String path,
488 int limitUid, String[] limitIfaces, int limitTag, boolean useBpfStats);
Chenbo Fengdcc56782018-04-18 15:44:46 -0700489
490 @VisibleForTesting
491 public static native int nativeReadNetworkStatsDev(NetworkStats stats);
Neil Fuller038b20c2018-07-03 19:22:45 +0100492
493 private static ProtocolException protocolExceptionWithCause(String message, Throwable cause) {
494 ProtocolException pe = new ProtocolException(message);
495 pe.initCause(cause);
496 return pe;
497 }
Jeff Sharkey1059c3c2011-10-04 16:54:49 -0700498}