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 
17 package com.android.server.net;
18 
19 import static android.net.NetworkStats.INTERFACES_ALL;
20 import static android.net.NetworkStats.TAG_ALL;
21 import static android.net.NetworkStats.UID_ALL;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.Context;
26 import android.net.NetworkStats;
27 import android.net.UnderlyingNetworkInfo;
28 import android.os.ServiceSpecificException;
29 import android.os.SystemClock;
30 
31 import com.android.internal.annotations.GuardedBy;
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.server.BpfNetMaps;
34 
35 import java.io.IOException;
36 import java.net.ProtocolException;
37 import java.util.Map;
38 import java.util.concurrent.ConcurrentHashMap;
39 
40 /**
41  * Creates {@link NetworkStats} instances by parsing various {@code /proc/}
42  * files as needed.
43  *
44  * @hide
45  */
46 public class NetworkStatsFactory {
47     static {
48         System.loadLibrary("service-connectivity");
49     }
50 
51     private static final String TAG = "NetworkStatsFactory";
52 
53     private final Context mContext;
54 
55     private final BpfNetMaps mBpfNetMaps;
56 
57     /**
58      * Guards persistent data access in this class
59      *
60      * <p>In order to prevent deadlocks, critical sections protected by this lock SHALL NOT call out
61      * to other code that will acquire other locks within the system server. See b/134244752.
62      */
63     private final Object mPersistentDataLock = new Object();
64 
65     /** Set containing info about active VPNs and their underlying networks. */
66     private volatile UnderlyingNetworkInfo[] mUnderlyingNetworkInfos = new UnderlyingNetworkInfo[0];
67 
68     // A persistent snapshot of cumulative stats since device start
69     @GuardedBy("mPersistentDataLock")
70     private NetworkStats mPersistSnapshot;
71 
72     // The persistent snapshot of tun and 464xlat adjusted stats since device start
73     @GuardedBy("mPersistentDataLock")
74     private NetworkStats mTunAnd464xlatAdjustedStats;
75 
76     private final Dependencies mDeps;
77     /**
78      * Dependencies of NetworkStatsFactory, for injection in tests.
79      */
80     @VisibleForTesting
81     public static class Dependencies {
82         /**
83          * Parse detailed statistics from bpf into given {@link NetworkStats} object. Values
84          * are expected to monotonically increase since device boot.
85          */
86         @NonNull
getNetworkStatsDetail()87         public NetworkStats getNetworkStatsDetail() throws IOException {
88             final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
89             final int ret = nativeReadNetworkStatsDetail(stats);
90             if (ret != 0) {
91                 throw new IOException("Failed to parse network stats");
92             }
93             return stats;
94         }
95         /**
96          * Parse device summary statistics from bpf into given {@link NetworkStats} object. Values
97          * are expected to monotonically increase since device boot.
98          */
99         @NonNull
getNetworkStatsDev()100         public NetworkStats getNetworkStatsDev() throws IOException {
101             final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
102             final int ret = nativeReadNetworkStatsDev(stats);
103             if (ret != 0) {
104                 throw new IOException("Failed to parse bpf iface stats");
105             }
106             return stats;
107         }
108 
109         /** Create a new {@link BpfNetMaps}. */
createBpfNetMaps(@onNull Context ctx)110         public BpfNetMaps createBpfNetMaps(@NonNull Context ctx) {
111             return new BpfNetMaps(ctx);
112         }
113     }
114 
115     /**
116      * (Stacked interface) -> (base interface) association for all connected ifaces since boot.
117      *
118      * Because counters must never roll backwards, once a given interface is stacked on top of an
119      * underlying interface, the stacked interface can never be stacked on top of
120      * another interface. */
121     private final ConcurrentHashMap<String, String> mStackedIfaces
122             = new ConcurrentHashMap<>();
123 
124     /** Informs the factory of a new stacked interface. */
noteStackedIface(String stackedIface, String baseIface)125     public void noteStackedIface(String stackedIface, String baseIface) {
126         if (stackedIface != null && baseIface != null) {
127             mStackedIfaces.put(stackedIface, baseIface);
128         }
129     }
130 
131     /**
132      * Set active VPN information for data usage migration purposes
133      *
134      * <p>Traffic on TUN-based VPNs inherently all appear to be originated from the VPN providing
135      * app's UID. This method is used to support migration of VPN data usage, ensuring data is
136      * accurately billed to the real owner of the traffic.
137      *
138      * @param vpnArray The snapshot of the currently-running VPNs.
139      */
updateUnderlyingNetworkInfos(UnderlyingNetworkInfo[] vpnArray)140     public void updateUnderlyingNetworkInfos(UnderlyingNetworkInfo[] vpnArray) {
141         mUnderlyingNetworkInfos = vpnArray.clone();
142     }
143 
144     /**
145      * Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
146      * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
147      */
apply464xlatAdjustments(NetworkStats baseTraffic, NetworkStats stackedTraffic)148     public void apply464xlatAdjustments(NetworkStats baseTraffic, NetworkStats stackedTraffic) {
149         NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces);
150     }
151 
NetworkStatsFactory(@onNull Context ctx)152     public NetworkStatsFactory(@NonNull Context ctx) {
153         this(ctx, new Dependencies());
154     }
155 
156     @VisibleForTesting
NetworkStatsFactory(@onNull Context ctx, Dependencies deps)157     public NetworkStatsFactory(@NonNull Context ctx, Dependencies deps) {
158         mBpfNetMaps = deps.createBpfNetMaps(ctx);
159         synchronized (mPersistentDataLock) {
160             mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
161             mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
162         }
163         mContext = ctx;
164         mDeps = deps;
165     }
166 
167     /**
168      * Parse and return interface-level summary {@link NetworkStats}. Designed
169      * to return only IP layer traffic. Values monotonically increase since
170      * device boot, and may include details about inactive interfaces.
171      */
readNetworkStatsSummaryXt()172     public NetworkStats readNetworkStatsSummaryXt() throws IOException {
173         return mDeps.getNetworkStatsDev();
174     }
175 
readNetworkStatsDetail()176     public NetworkStats readNetworkStatsDetail() throws IOException {
177         return readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
178     }
179 
180     @GuardedBy("mPersistentDataLock")
requestSwapActiveStatsMapLocked()181     private void requestSwapActiveStatsMapLocked() throws IOException {
182         try {
183             // Do a active map stats swap. Once the swap completes, this code
184             // can read and clean the inactive map without races.
185             mBpfNetMaps.swapActiveStatsMap();
186         } catch (ServiceSpecificException e) {
187             throw new IOException(e);
188         }
189     }
190 
191     /**
192      * Reads the detailed UID stats based on the provided parameters
193      *
194      * @param limitUid the UID to limit this query to
195      * @param limitIfaces the interfaces to limit this query to. Use {@link
196      *     NetworkStats.INTERFACES_ALL} to select all interfaces
197      * @param limitTag the tags to limit this query to
198      * @return the NetworkStats instance containing network statistics at the present time.
199      */
readNetworkStatsDetail( int limitUid, String[] limitIfaces, int limitTag)200     public NetworkStats readNetworkStatsDetail(
201             int limitUid, String[] limitIfaces, int limitTag) throws IOException {
202         // In order to prevent deadlocks, anything protected by this lock MUST NOT call out to other
203         // code that will acquire other locks within the system server. See b/134244752.
204         synchronized (mPersistentDataLock) {
205             // Take a reference. If this gets swapped out, we still have the old reference.
206             final UnderlyingNetworkInfo[] vpnArray = mUnderlyingNetworkInfos;
207             // Take a defensive copy. mPersistSnapshot is mutated in some cases below
208             final NetworkStats prev = mPersistSnapshot.clone();
209 
210             requestSwapActiveStatsMapLocked();
211             // Stats are always read from the inactive map, so they must be read after the
212             // swap
213             final NetworkStats stats = mDeps.getNetworkStatsDetail();
214             // BPF stats are incremental; fold into mPersistSnapshot.
215             mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
216             mPersistSnapshot.combineAllValues(stats);
217 
218             NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray);
219 
220             // Filter return values
221             adjustedStats.filter(limitUid, limitIfaces, limitTag);
222             return adjustedStats;
223         }
224     }
225 
226     @GuardedBy("mPersistentDataLock")
adjustForTunAnd464Xlat(NetworkStats uidDetailStats, NetworkStats previousStats, UnderlyingNetworkInfo[] vpnArray)227     private NetworkStats adjustForTunAnd464Xlat(NetworkStats uidDetailStats,
228             NetworkStats previousStats, UnderlyingNetworkInfo[] vpnArray) {
229         // Calculate delta from last snapshot
230         final NetworkStats delta = uidDetailStats.subtract(previousStats);
231 
232         // Apply 464xlat adjustments before VPN adjustments. If VPNs are using v4 on a v6 only
233         // network, the overhead is their fault.
234         // No locking here: apply464xlatAdjustments behaves fine with an add-only
235         // ConcurrentHashMap.
236         delta.apply464xlatAdjustments(mStackedIfaces);
237 
238         // Migrate data usage over a VPN to the TUN network.
239         for (UnderlyingNetworkInfo info : vpnArray) {
240             delta.migrateTun(info.getOwnerUid(), info.getInterface(),
241                     info.getUnderlyingInterfaces());
242             // Filter out debug entries as that may lead to over counting.
243             delta.filterDebugEntries();
244         }
245 
246         // Update mTunAnd464xlatAdjustedStats with migrated delta.
247         mTunAnd464xlatAdjustedStats.combineAllValues(delta);
248         mTunAnd464xlatAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime());
249 
250         return mTunAnd464xlatAdjustedStats.clone();
251     }
252 
253     /**
254      * Remove stats from {@code mPersistSnapshot} and {@code mTunAnd464xlatAdjustedStats} for the
255      * given uids.
256      */
removeUidsLocked(int[] uids)257     public void removeUidsLocked(int[] uids) {
258         synchronized (mPersistentDataLock) {
259             mPersistSnapshot.removeUids(uids);
260             mTunAnd464xlatAdjustedStats.removeUids(uids);
261         }
262     }
263 
assertEquals(NetworkStats expected, NetworkStats actual)264     public void assertEquals(NetworkStats expected, NetworkStats actual) {
265         if (expected.size() != actual.size()) {
266             throw new AssertionError(
267                     "Expected size " + expected.size() + ", actual size " + actual.size());
268         }
269 
270         NetworkStats.Entry expectedRow = null;
271         NetworkStats.Entry actualRow = null;
272         for (int i = 0; i < expected.size(); i++) {
273             expectedRow = expected.getValues(i, expectedRow);
274             actualRow = actual.getValues(i, actualRow);
275             if (!expectedRow.equals(actualRow)) {
276                 throw new AssertionError(
277                         "Expected row " + i + ": " + expectedRow + ", actual row " + actualRow);
278             }
279         }
280     }
281 
282     /**
283      * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
284      * format like {@code 0x7fffffff00000000}.
285      */
kernelToTag(String string)286     public static int kernelToTag(String string) {
287         int length = string.length();
288         if (length > 10) {
289             return Long.decode(string.substring(0, length - 8)).intValue();
290         } else {
291             return 0;
292         }
293     }
294 
295     /**
296      * Parse statistics from file into given {@link NetworkStats} object. Values
297      * are expected to monotonically increase since device boot.
298      */
299     @VisibleForTesting
nativeReadNetworkStatsDetail(NetworkStats stats)300     public static native int nativeReadNetworkStatsDetail(NetworkStats stats);
301 
302     @VisibleForTesting
nativeReadNetworkStatsDev(NetworkStats stats)303     public static native int nativeReadNetworkStatsDev(NetworkStats stats);
304 
protocolExceptionWithCause(String message, Throwable cause)305     private static ProtocolException protocolExceptionWithCause(String message, Throwable cause) {
306         ProtocolException pe = new ProtocolException(message);
307         pe.initCause(cause);
308         return pe;
309     }
310 }
311