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