1 /*
2  * Copyright (C) 2016 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.connectivity;
18 
19 import static android.util.TimeUtils.NANOS_PER_MS;
20 
21 import android.annotation.Nullable;
22 import android.content.Context;
23 import android.net.ConnectivityManager;
24 import android.net.INetdEventCallback;
25 import android.net.MacAddress;
26 import android.net.Network;
27 import android.net.NetworkCapabilities;
28 import android.net.NetworkRequest;
29 import android.net.metrics.ConnectStats;
30 import android.net.metrics.DnsEvent;
31 import android.net.metrics.NetworkMetrics;
32 import android.net.metrics.WakeupEvent;
33 import android.net.metrics.WakeupStats;
34 import android.os.BatteryStatsInternal;
35 import android.os.RemoteException;
36 import android.os.SystemClock;
37 import android.text.format.DateUtils;
38 import android.util.ArrayMap;
39 import android.util.Log;
40 import android.util.SparseArray;
41 
42 import com.android.internal.annotations.GuardedBy;
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.util.ArrayUtils;
45 import com.android.internal.util.BitUtils;
46 import com.android.internal.util.FrameworkStatsLog;
47 import com.android.internal.util.RingBuffer;
48 import com.android.internal.util.TokenBucket;
49 import com.android.net.module.util.BaseNetdEventListener;
50 import com.android.server.LocalServices;
51 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
52 
53 import java.io.PrintWriter;
54 import java.util.ArrayList;
55 import java.util.List;
56 import java.util.StringJoiner;
57 
58 /**
59  * Implementation of the INetdEventListener interface.
60  */
61 public class NetdEventListenerService extends BaseNetdEventListener {
62 
63     public static final String SERVICE_NAME = "netd_listener";
64 
65     private static final String TAG = NetdEventListenerService.class.getSimpleName();
66     private static final boolean DBG = false;
67 
68     // Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum
69     // bursts of 5000 measurements.
70     private static final int CONNECT_LATENCY_BURST_LIMIT  = 5000;
71     private static final int CONNECT_LATENCY_FILL_RATE    = 15 * (int) DateUtils.SECOND_IN_MILLIS;
72 
73     private static final long METRICS_SNAPSHOT_SPAN_MS = 5 * DateUtils.MINUTE_IN_MILLIS;
74     private static final int METRICS_SNAPSHOT_BUFFER_SIZE = 48; // 4 hours
75 
76     @VisibleForTesting
77     static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;
78     // TODO: dedup this String constant with the one used in
79     // ConnectivityService#wakeupModifyInterface().
80     @VisibleForTesting
81     static final String WAKEUP_EVENT_PREFIX_DELIM = ":";
82 
83     // Array of aggregated DNS and connect events sent by netd, grouped by net id.
84     @GuardedBy("this")
85     private final SparseArray<NetworkMetrics> mNetworkMetrics = new SparseArray<>();
86 
87     @GuardedBy("this")
88     private final RingBuffer<NetworkMetricsSnapshot> mNetworkMetricsSnapshots =
89             new RingBuffer<>(NetworkMetricsSnapshot.class, METRICS_SNAPSHOT_BUFFER_SIZE);
90     @GuardedBy("this")
91     private long mLastSnapshot = 0;
92 
93     // Array of aggregated wakeup event stats, grouped by interface name.
94     @GuardedBy("this")
95     private final ArrayMap<String, WakeupStats> mWakeupStats = new ArrayMap<>();
96     // Ring buffer array for storing packet wake up events sent by Netd.
97     @GuardedBy("this")
98     private final RingBuffer<WakeupEvent> mWakeupEvents =
99             new RingBuffer<>(WakeupEvent.class, WAKEUP_EVENT_BUFFER_LENGTH);
100 
101     private final ConnectivityManager mCm;
102 
103     @GuardedBy("this")
104     private final TokenBucket mConnectTb =
105             new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT);
106 
107     final TransportForNetIdNetworkCallback mCallback = new TransportForNetIdNetworkCallback();
108 
109     /**
110      * There are only 3 possible callbacks.
111      *
112      * mNetdEventCallbackList[CALLBACK_CALLER_CONNECTIVITY_SERVICE]
113      * Callback registered/unregistered by ConnectivityService.
114      *
115      * mNetdEventCallbackList[CALLBACK_CALLER_DEVICE_POLICY]
116      * Callback registered/unregistered when logging is being enabled/disabled in DPM
117      * by the device owner. It's DevicePolicyManager's responsibility to ensure that.
118      *
119      * mNetdEventCallbackList[CALLBACK_CALLER_NETWORK_WATCHLIST]
120      * Callback registered/unregistered by NetworkWatchlistService.
121      */
122     @GuardedBy("this")
123     private static final int[] ALLOWED_CALLBACK_TYPES = {
124         INetdEventCallback.CALLBACK_CALLER_CONNECTIVITY_SERVICE,
125         INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY,
126         INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST
127     };
128 
129     @GuardedBy("this")
130     private INetdEventCallback[] mNetdEventCallbackList =
131             new INetdEventCallback[ALLOWED_CALLBACK_TYPES.length];
132 
addNetdEventCallback(int callerType, INetdEventCallback callback)133     public synchronized boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
134         if (!isValidCallerType(callerType)) {
135             Log.e(TAG, "Invalid caller type: " + callerType);
136             return false;
137         }
138         mNetdEventCallbackList[callerType] = callback;
139         return true;
140     }
141 
removeNetdEventCallback(int callerType)142     public synchronized boolean removeNetdEventCallback(int callerType) {
143         if (!isValidCallerType(callerType)) {
144             Log.e(TAG, "Invalid caller type: " + callerType);
145             return false;
146         }
147         mNetdEventCallbackList[callerType] = null;
148         return true;
149     }
150 
isValidCallerType(int callerType)151     private static boolean isValidCallerType(int callerType) {
152         for (int i = 0; i < ALLOWED_CALLBACK_TYPES.length; i++) {
153             if (callerType == ALLOWED_CALLBACK_TYPES[i]) {
154                 return true;
155             }
156         }
157         return false;
158     }
159 
NetdEventListenerService(Context context)160     public NetdEventListenerService(Context context) {
161         this(context.getSystemService(ConnectivityManager.class));
162     }
163 
164     @VisibleForTesting
NetdEventListenerService(ConnectivityManager cm)165     public NetdEventListenerService(ConnectivityManager cm) {
166         // We are started when boot is complete, so ConnectivityService should already be running.
167         mCm = cm;
168         // Clear all capabilities to listen all networks.
169         mCm.registerNetworkCallback(new NetworkRequest.Builder().clearCapabilities().build(),
170                 mCallback);
171     }
172 
projectSnapshotTime(long timeMs)173     private static long projectSnapshotTime(long timeMs) {
174         return (timeMs / METRICS_SNAPSHOT_SPAN_MS) * METRICS_SNAPSHOT_SPAN_MS;
175     }
176 
getMetricsForNetwork(long timeMs, int netId)177     private NetworkMetrics getMetricsForNetwork(long timeMs, int netId) {
178         NetworkMetrics metrics = mNetworkMetrics.get(netId);
179         final NetworkCapabilities nc = mCallback.getNetworkCapabilities(netId);
180         final long transports = (nc != null) ? BitUtils.packBits(nc.getTransportTypes()) : 0;
181         final boolean forceCollect =
182                 (metrics != null && nc != null && metrics.transports != transports);
183         collectPendingMetricsSnapshot(timeMs, forceCollect);
184         if (metrics == null || forceCollect) {
185             metrics = new NetworkMetrics(netId, transports, mConnectTb);
186             mNetworkMetrics.put(netId, metrics);
187         }
188         return metrics;
189     }
190 
getNetworkMetricsSnapshots()191     private NetworkMetricsSnapshot[] getNetworkMetricsSnapshots() {
192         collectPendingMetricsSnapshot(System.currentTimeMillis(), false /* forceCollect */);
193         return mNetworkMetricsSnapshots.toArray();
194     }
195 
collectPendingMetricsSnapshot(long timeMs, boolean forceCollect)196     private void collectPendingMetricsSnapshot(long timeMs, boolean forceCollect) {
197         // Detects time differences larger than the snapshot collection period.
198         // This is robust against clock jumps and long inactivity periods.
199         if (!forceCollect && Math.abs(timeMs - mLastSnapshot) <= METRICS_SNAPSHOT_SPAN_MS) {
200             return;
201         }
202         mLastSnapshot = projectSnapshotTime(timeMs);
203         NetworkMetricsSnapshot snapshot =
204                 NetworkMetricsSnapshot.collect(mLastSnapshot, mNetworkMetrics);
205         if (snapshot.stats.isEmpty()) {
206             return;
207         }
208         mNetworkMetricsSnapshots.append(snapshot);
209     }
210 
211     @Override
212     // Called concurrently by multiple binder threads.
213     // This method must not block or perform long-running operations.
onDnsEvent(int netId, int eventType, int returnCode, int latencyMs, String hostname, String[] ipAddresses, int ipAddressesCount, int uid)214     public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
215             String hostname, String[] ipAddresses, int ipAddressesCount, int uid) {
216         long timestamp = System.currentTimeMillis();
217         getMetricsForNetwork(timestamp, netId).addDnsResult(eventType, returnCode, latencyMs);
218 
219         for (INetdEventCallback callback : mNetdEventCallbackList) {
220             if (callback != null) {
221                 try {
222                     callback.onDnsEvent(netId, eventType, returnCode, hostname, ipAddresses,
223                             ipAddressesCount, timestamp, uid);
224                 } catch (RemoteException e) {
225                     throw e.rethrowFromSystemServer();
226                 }
227             }
228         }
229     }
230 
231     @Override
232     // Called concurrently by multiple binder threads.
233     // This method must not block or perform long-running operations.
onNat64PrefixEvent(int netId, boolean added, String prefixString, int prefixLength)234     public synchronized void onNat64PrefixEvent(int netId,
235             boolean added, String prefixString, int prefixLength) {
236         for (INetdEventCallback callback : mNetdEventCallbackList) {
237             if (callback != null) {
238                 try {
239                     callback.onNat64PrefixEvent(netId, added, prefixString, prefixLength);
240                 } catch (RemoteException e) {
241                     throw e.rethrowFromSystemServer();
242                 }
243             }
244         }
245     }
246 
247     @Override
248     // Called concurrently by multiple binder threads.
249     // This method must not block or perform long-running operations.
onPrivateDnsValidationEvent(int netId, String ipAddress, String hostname, boolean validated)250     public synchronized void onPrivateDnsValidationEvent(int netId,
251             String ipAddress, String hostname, boolean validated) {
252         for (INetdEventCallback callback : mNetdEventCallbackList) {
253             if (callback != null) {
254                 try {
255                     callback.onPrivateDnsValidationEvent(netId, ipAddress, hostname, validated);
256                 } catch (RemoteException e) {
257                     throw e.rethrowFromSystemServer();
258                 }
259             }
260         }
261     }
262 
263     @Override
264     // Called concurrently by multiple binder threads.
265     // This method must not block or perform long-running operations.
onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port, int uid)266     public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr,
267             int port, int uid) {
268         long timestamp = System.currentTimeMillis();
269         getMetricsForNetwork(timestamp, netId).addConnectResult(error, latencyMs, ipAddr);
270 
271         for (INetdEventCallback callback : mNetdEventCallbackList) {
272             if (callback != null) {
273                 try {
274                     callback.onConnectEvent(ipAddr, port, timestamp, uid);
275                 } catch (RemoteException e) {
276                     throw e.rethrowFromSystemServer();
277                 }
278             }
279         }
280     }
281 
hasWifiTransport(Network network)282     private boolean hasWifiTransport(Network network) {
283         final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
284         return nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
285     }
286 
287     @Override
onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader, byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort, long timestampNs)288     public synchronized void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader,
289             byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort, long timestampNs) {
290         final String[] prefixParts = prefix.split(WAKEUP_EVENT_PREFIX_DELIM);
291         if (prefixParts.length != 2) {
292             throw new IllegalArgumentException("Prefix " + prefix
293                     + " required in format <nethandle>:<interface>");
294         }
295         final long netHandle = Long.parseLong(prefixParts[0]);
296         final Network network = Network.fromNetworkHandle(netHandle);
297 
298         final WakeupEvent event = new WakeupEvent();
299         event.iface = prefixParts[1];
300         event.uid = uid;
301         event.ethertype = ethertype;
302         if (ArrayUtils.isEmpty(dstHw)) {
303             if (hasWifiTransport(network)) {
304                 Log.e(TAG, "Empty mac address on WiFi transport, network: " + network);
305             }
306             event.dstHwAddr = null;
307         } else {
308             event.dstHwAddr = MacAddress.fromBytes(dstHw);
309         }
310         event.srcIp = srcIp;
311         event.dstIp = dstIp;
312         event.ipNextHeader = ipNextHeader;
313         event.srcPort = srcPort;
314         event.dstPort = dstPort;
315         if (timestampNs > 0) {
316             event.timestampMs = timestampNs / NANOS_PER_MS;
317         } else {
318             event.timestampMs = System.currentTimeMillis();
319         }
320         addWakeupEvent(event);
321 
322         final BatteryStatsInternal bsi = LocalServices.getService(BatteryStatsInternal.class);
323         if (bsi != null) {
324             final long elapsedMs = SystemClock.elapsedRealtime() + event.timestampMs
325                     - System.currentTimeMillis();
326             bsi.noteCpuWakingNetworkPacket(network, elapsedMs, event.uid);
327         }
328 
329         final String dstMac = String.valueOf(event.dstHwAddr);
330         FrameworkStatsLog.write(FrameworkStatsLog.PACKET_WAKEUP_OCCURRED,
331                 uid, event.iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort);
332     }
333 
334     @Override
onTcpSocketStatsEvent(int[] networkIds, int[] sentPackets, int[] lostPackets, int[] rttsUs, int[] sentAckDiffsMs)335     public synchronized void onTcpSocketStatsEvent(int[] networkIds,
336             int[] sentPackets, int[] lostPackets, int[] rttsUs, int[] sentAckDiffsMs) {
337         if (networkIds.length != sentPackets.length
338                 || networkIds.length != lostPackets.length
339                 || networkIds.length != rttsUs.length
340                 || networkIds.length != sentAckDiffsMs.length) {
341             Log.e(TAG, "Mismatched lengths of TCP socket stats data arrays");
342             return;
343         }
344 
345         long timestamp = System.currentTimeMillis();
346         for (int i = 0; i < networkIds.length; i++) {
347             int netId = networkIds[i];
348             int sent = sentPackets[i];
349             int lost = lostPackets[i];
350             int rttUs = rttsUs[i];
351             int sentAckDiffMs = sentAckDiffsMs[i];
352             getMetricsForNetwork(timestamp, netId)
353                     .addTcpStatsResult(sent, lost, rttUs, sentAckDiffMs);
354         }
355     }
356 
357     @Override
getInterfaceVersion()358     public int getInterfaceVersion() {
359         return this.VERSION;
360     }
361 
362     @Override
getInterfaceHash()363     public String getInterfaceHash() {
364         return this.HASH;
365     }
366 
addWakeupEvent(WakeupEvent event)367     private void addWakeupEvent(WakeupEvent event) {
368         String iface = event.iface;
369         mWakeupEvents.append(event);
370         WakeupStats stats = mWakeupStats.get(iface);
371         if (stats == null) {
372             stats = new WakeupStats(iface);
373             mWakeupStats.put(iface, stats);
374         }
375         stats.countEvent(event);
376     }
377 
flushStatistics(List<IpConnectivityEvent> events)378     public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
379         for (int i = 0; i < mNetworkMetrics.size(); i++) {
380             ConnectStats stats = mNetworkMetrics.valueAt(i).connectMetrics;
381             if (stats.eventCount == 0) {
382                 continue;
383             }
384             events.add(IpConnectivityEventBuilder.toProto(stats));
385         }
386         for (int i = 0; i < mNetworkMetrics.size(); i++) {
387             DnsEvent ev = mNetworkMetrics.valueAt(i).dnsMetrics;
388             if (ev.eventCount == 0) {
389                 continue;
390             }
391             events.add(IpConnectivityEventBuilder.toProto(ev));
392         }
393         for (int i = 0; i < mWakeupStats.size(); i++) {
394             events.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
395         }
396         mNetworkMetrics.clear();
397         mWakeupStats.clear();
398     }
399 
list(PrintWriter pw)400     public synchronized void list(PrintWriter pw) {
401         pw.println("dns/connect events:");
402         for (int i = 0; i < mNetworkMetrics.size(); i++) {
403             pw.println(mNetworkMetrics.valueAt(i).connectMetrics);
404         }
405         for (int i = 0; i < mNetworkMetrics.size(); i++) {
406             pw.println(mNetworkMetrics.valueAt(i).dnsMetrics);
407         }
408         pw.println("");
409         pw.println("network statistics:");
410         for (NetworkMetricsSnapshot s : getNetworkMetricsSnapshots()) {
411             pw.println(s);
412         }
413         pw.println("");
414         pw.println("packet wakeup events:");
415         for (int i = 0; i < mWakeupStats.size(); i++) {
416             pw.println(mWakeupStats.valueAt(i));
417         }
418         for (WakeupEvent wakeup : mWakeupEvents.toArray()) {
419             pw.println(wakeup);
420         }
421     }
422 
423     /**
424      * Convert events in the buffer to a list of IpConnectivityEvent protos
425      */
listAsProtos()426     public synchronized List<IpConnectivityEvent> listAsProtos() {
427         List<IpConnectivityEvent> list = new ArrayList<>();
428         for (int i = 0; i < mNetworkMetrics.size(); i++) {
429             list.add(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).connectMetrics));
430         }
431         for (int i = 0; i < mNetworkMetrics.size(); i++) {
432             list.add(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).dnsMetrics));
433         }
434         for (int i = 0; i < mWakeupStats.size(); i++) {
435             list.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
436         }
437         return list;
438     }
439 
440     /** Helper class for buffering summaries of NetworkMetrics at regular time intervals */
441     static class NetworkMetricsSnapshot {
442 
443         public long timeMs;
444         public List<NetworkMetrics.Summary> stats = new ArrayList<>();
445 
collect(long timeMs, SparseArray<NetworkMetrics> networkMetrics)446         static NetworkMetricsSnapshot collect(long timeMs, SparseArray<NetworkMetrics> networkMetrics) {
447             NetworkMetricsSnapshot snapshot = new NetworkMetricsSnapshot();
448             snapshot.timeMs = timeMs;
449             for (int i = 0; i < networkMetrics.size(); i++) {
450                 NetworkMetrics.Summary s = networkMetrics.valueAt(i).getPendingStats();
451                 if (s != null) {
452                     snapshot.stats.add(s);
453                 }
454             }
455             return snapshot;
456         }
457 
458         @Override
toString()459         public String toString() {
460             StringJoiner j = new StringJoiner(", ");
461             for (NetworkMetrics.Summary s : stats) {
462                 j.add(s.toString());
463             }
464             return String.format("%tT.%tL: %s", timeMs, timeMs, j.toString());
465         }
466     }
467 
468     private class TransportForNetIdNetworkCallback extends ConnectivityManager.NetworkCallback {
469         private final SparseArray<NetworkCapabilities> mCapabilities = new SparseArray<>();
470 
471         @Override
onCapabilitiesChanged(Network network, NetworkCapabilities nc)472         public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
473             synchronized (mCapabilities) {
474                 mCapabilities.put(network.getNetId(), nc);
475             }
476         }
477 
478         @Override
onLost(Network network)479         public void onLost(Network network) {
480             synchronized (mCapabilities) {
481                 mCapabilities.remove(network.getNetId());
482             }
483         }
484 
485         @Nullable
getNetworkCapabilities(int netId)486         public NetworkCapabilities getNetworkCapabilities(int netId) {
487             synchronized (mCapabilities) {
488                 return mCapabilities.get(netId);
489             }
490         }
491     }
492 }
493