blob: 05c6e69c18952e6497ac0628974863b8c714ea11 [file] [log] [blame]
Lorenzo Colitti43724732016-04-12 23:29:19 +09001/*
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
17package com.android.server.connectivity;
18
Hugo Benichi60c9f632017-09-05 13:34:48 +090019import static android.util.TimeUtils.NANOS_PER_MS;
20
Lorenzo Colitti43724732016-04-12 23:29:19 +090021import android.content.Context;
Lorenzo Colitti43724732016-04-12 23:29:19 +090022import android.net.ConnectivityManager;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010023import android.net.INetdEventCallback;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090024import android.net.Network;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090025import android.net.NetworkCapabilities;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090026import android.net.metrics.ConnectStats;
Hugo Benichi5e055182016-06-01 08:50:38 +090027import android.net.metrics.DnsEvent;
Michal Karpinskid11d18c2016-09-15 17:07:08 +090028import android.net.metrics.INetdEventListener;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090029import android.net.metrics.IpConnectivityLog;
Hugo Benichiab209752017-09-27 23:28:59 +090030import android.net.metrics.NetworkMetrics;
Hugo Benichif562ac32017-09-04 13:24:43 +090031import android.net.metrics.WakeupEvent;
Hugo Benichi60c9f632017-09-05 13:34:48 +090032import android.net.metrics.WakeupStats;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010033import android.os.RemoteException;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090034import android.text.format.DateUtils;
Lorenzo Colitti43724732016-04-12 23:29:19 +090035import android.util.Log;
Hugo Benichi60c9f632017-09-05 13:34:48 +090036import android.util.ArrayMap;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090037import android.util.SparseArray;
Hugo Benichiab209752017-09-27 23:28:59 +090038
Lorenzo Colitti43724732016-04-12 23:29:19 +090039import com.android.internal.annotations.GuardedBy;
Hugo Benichi5e055182016-06-01 08:50:38 +090040import com.android.internal.annotations.VisibleForTesting;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090041import com.android.internal.util.BitUtils;
Lorenzo Colitti43724732016-04-12 23:29:19 +090042import com.android.internal.util.IndentingPrintWriter;
Hugo Benichi67c5e032017-09-14 16:31:38 +090043import com.android.internal.util.RingBuffer;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090044import com.android.internal.util.TokenBucket;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090045import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
Hugo Benichiab209752017-09-27 23:28:59 +090046
Lorenzo Colitti43724732016-04-12 23:29:19 +090047import java.io.PrintWriter;
Hugo Benichiab209752017-09-27 23:28:59 +090048import java.util.ArrayList;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090049import java.util.List;
Hugo Benichiab209752017-09-27 23:28:59 +090050import java.util.StringJoiner;
Lorenzo Colitti43724732016-04-12 23:29:19 +090051
Lorenzo Colitti43724732016-04-12 23:29:19 +090052/**
Michal Karpinskid11d18c2016-09-15 17:07:08 +090053 * Implementation of the INetdEventListener interface.
Lorenzo Colitti43724732016-04-12 23:29:19 +090054 */
Michal Karpinskid11d18c2016-09-15 17:07:08 +090055public class NetdEventListenerService extends INetdEventListener.Stub {
Lorenzo Colitti43724732016-04-12 23:29:19 +090056
Michal Karpinskid11d18c2016-09-15 17:07:08 +090057 public static final String SERVICE_NAME = "netd_listener";
Lorenzo Colitti43724732016-04-12 23:29:19 +090058
Michal Karpinskid11d18c2016-09-15 17:07:08 +090059 private static final String TAG = NetdEventListenerService.class.getSimpleName();
Hugo Benichi8b06bcd2016-10-31 15:04:37 +090060 private static final boolean DBG = false;
Lorenzo Colitti43724732016-04-12 23:29:19 +090061 private static final boolean VDBG = false;
62
Hugo Benichi0d4a3982016-11-25 11:24:22 +090063 // Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum
64 // bursts of 5000 measurements.
65 private static final int CONNECT_LATENCY_BURST_LIMIT = 5000;
66 private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS;
Hugo Benichiab209752017-09-27 23:28:59 +090067
68 private static final long METRICS_SNAPSHOT_SPAN_MS = 5 * DateUtils.MINUTE_IN_MILLIS;
69 private static final int METRICS_SNAPSHOT_BUFFER_SIZE = 48; // 4 hours
Hugo Benichi0d4a3982016-11-25 11:24:22 +090070
Hugo Benichif562ac32017-09-04 13:24:43 +090071 @VisibleForTesting
72 static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;
Hugo Benichi60c9f632017-09-05 13:34:48 +090073 // TODO: dedup this String constant with the one used in
74 // ConnectivityService#wakeupModifyInterface().
75 @VisibleForTesting
76 static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:";
Hugo Benichif562ac32017-09-04 13:24:43 +090077
Hugo Benichiab209752017-09-27 23:28:59 +090078 // Array of aggregated DNS and connect events sent by netd, grouped by net id.
Lorenzo Colitti43724732016-04-12 23:29:19 +090079 @GuardedBy("this")
Hugo Benichiab209752017-09-27 23:28:59 +090080 private final SparseArray<NetworkMetrics> mNetworkMetrics = new SparseArray<>();
81
Hugo Benichi5eb90532017-03-23 18:38:22 +090082 @GuardedBy("this")
Hugo Benichiab209752017-09-27 23:28:59 +090083 private final RingBuffer<NetworkMetricsSnapshot> mNetworkMetricsSnapshots =
84 new RingBuffer<>(NetworkMetricsSnapshot.class, METRICS_SNAPSHOT_BUFFER_SIZE);
85 @GuardedBy("this")
86 private long mLastSnapshot = 0;
Lorenzo Colitti43724732016-04-12 23:29:19 +090087
Hugo Benichi60c9f632017-09-05 13:34:48 +090088 // Array of aggregated wakeup event stats, grouped by interface name.
89 @GuardedBy("this")
90 private final ArrayMap<String, WakeupStats> mWakeupStats = new ArrayMap<>();
Hugo Benichif562ac32017-09-04 13:24:43 +090091 // Ring buffer array for storing packet wake up events sent by Netd.
92 @GuardedBy("this")
Hugo Benichi67c5e032017-09-14 16:31:38 +090093 private final RingBuffer<WakeupEvent> mWakeupEvents =
Hugo Benichiab209752017-09-27 23:28:59 +090094 new RingBuffer<>(WakeupEvent.class, WAKEUP_EVENT_BUFFER_LENGTH);
Hugo Benichif562ac32017-09-04 13:24:43 +090095
Lorenzo Colitti43724732016-04-12 23:29:19 +090096 private final ConnectivityManager mCm;
Lorenzo Colitti43724732016-04-12 23:29:19 +090097
Hugo Benichi0d4a3982016-11-25 11:24:22 +090098 @GuardedBy("this")
99 private final TokenBucket mConnectTb =
100 new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100101 // Callback should only be registered/unregistered when logging is being enabled/disabled in DPM
102 // by the device owner. It's DevicePolicyManager's responsibility to ensure that.
103 @GuardedBy("this")
104 private INetdEventCallback mNetdEventCallback;
105
106 public synchronized boolean registerNetdEventCallback(INetdEventCallback callback) {
107 mNetdEventCallback = callback;
108 return true;
109 }
110
111 public synchronized boolean unregisterNetdEventCallback() {
112 mNetdEventCallback = null;
113 return true;
114 }
115
Michal Karpinskid11d18c2016-09-15 17:07:08 +0900116 public NetdEventListenerService(Context context) {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900117 this(context.getSystemService(ConnectivityManager.class));
Hugo Benichi5e055182016-06-01 08:50:38 +0900118 }
119
120 @VisibleForTesting
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900121 public NetdEventListenerService(ConnectivityManager cm) {
Lorenzo Colitti43724732016-04-12 23:29:19 +0900122 // We are started when boot is complete, so ConnectivityService should already be running.
Hugo Benichi5e055182016-06-01 08:50:38 +0900123 mCm = cm;
Lorenzo Colitti43724732016-04-12 23:29:19 +0900124 }
125
Hugo Benichiab209752017-09-27 23:28:59 +0900126 private static long projectSnapshotTime(long timeMs) {
127 return (timeMs / METRICS_SNAPSHOT_SPAN_MS) * METRICS_SNAPSHOT_SPAN_MS;
128 }
129
130 private NetworkMetrics getMetricsForNetwork(long timeMs, int netId) {
131 collectPendingMetricsSnapshot(timeMs);
132 NetworkMetrics metrics = mNetworkMetrics.get(netId);
133 if (metrics == null) {
134 // TODO: allow to change transport for a given netid.
135 metrics = new NetworkMetrics(netId, getTransports(netId), mConnectTb);
136 mNetworkMetrics.put(netId, metrics);
137 }
138 return metrics;
139 }
140
141 private NetworkMetricsSnapshot[] getNetworkMetricsSnapshots() {
142 collectPendingMetricsSnapshot(System.currentTimeMillis());
143 return mNetworkMetricsSnapshots.toArray();
144 }
145
146 private void collectPendingMetricsSnapshot(long timeMs) {
147 // Detects time differences larger than the snapshot collection period.
148 // This is robust against clock jumps and long inactivity periods.
149 if (Math.abs(timeMs - mLastSnapshot) <= METRICS_SNAPSHOT_SPAN_MS) {
150 return;
151 }
152 mLastSnapshot = projectSnapshotTime(timeMs);
153 NetworkMetricsSnapshot snapshot =
154 NetworkMetricsSnapshot.collect(mLastSnapshot, mNetworkMetrics);
155 if (snapshot.stats.isEmpty()) {
156 return;
157 }
158 mNetworkMetricsSnapshots.append(snapshot);
159 }
160
Lorenzo Colitti43724732016-04-12 23:29:19 +0900161 @Override
162 // Called concurrently by multiple binder threads.
Michal Karpinski14c9d2d2016-09-27 17:13:57 +0100163 // This method must not block or perform long-running operations.
164 public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100165 String hostname, String[] ipAddresses, int ipAddressesCount, int uid)
166 throws RemoteException {
Hugo Benichi8b06bcd2016-10-31 15:04:37 +0900167 maybeVerboseLog("onDnsEvent(%d, %d, %d, %dms)", netId, eventType, returnCode, latencyMs);
Lorenzo Colitti43724732016-04-12 23:29:19 +0900168
Hugo Benichiab209752017-09-27 23:28:59 +0900169 long timestamp = System.currentTimeMillis();
170 getMetricsForNetwork(timestamp, netId).addDnsResult(eventType, returnCode, latencyMs);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100171
172 if (mNetdEventCallback != null) {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900173 mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100174 }
Lorenzo Colitti43724732016-04-12 23:29:19 +0900175 }
176
Michal Karpinski965894e2016-09-28 16:06:16 +0100177 @Override
178 // Called concurrently by multiple binder threads.
179 // This method must not block or perform long-running operations.
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900180 public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr,
181 int port, int uid) throws RemoteException {
Hugo Benichi8b06bcd2016-10-31 15:04:37 +0900182 maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100183
Hugo Benichiab209752017-09-27 23:28:59 +0900184 long timestamp = System.currentTimeMillis();
185 getMetricsForNetwork(timestamp, netId).addConnectResult(error, latencyMs, ipAddr);
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900186
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100187 if (mNetdEventCallback != null) {
Hugo Benichiab209752017-09-27 23:28:59 +0900188 mNetdEventCallback.onConnectEvent(ipAddr, port, timestamp, uid);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100189 }
Michal Karpinski965894e2016-09-28 16:06:16 +0100190 }
191
Joel Scherpelzb369bf52017-05-22 13:47:41 +0900192 @Override
193 public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
Hugo Benichif562ac32017-09-04 13:24:43 +0900194 maybeVerboseLog("onWakeupEvent(%s, %d, %d, %sns)", prefix, uid, gid, timestampNs);
195
196 // TODO: add ip protocol and port
197
198 String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
Hugo Benichi60c9f632017-09-05 13:34:48 +0900199 final long timestampMs;
200 if (timestampNs > 0) {
201 timestampMs = timestampNs / NANOS_PER_MS;
202 } else {
203 timestampMs = System.currentTimeMillis();
204 }
Hugo Benichif562ac32017-09-04 13:24:43 +0900205
Hugo Benichi175b5742017-09-19 13:15:26 +0900206 addWakeupEvent(iface, timestampMs, uid);
Hugo Benichif562ac32017-09-04 13:24:43 +0900207 }
208
209 @GuardedBy("this")
Hugo Benichi175b5742017-09-19 13:15:26 +0900210 private void addWakeupEvent(String iface, long timestampMs, int uid) {
Hugo Benichif562ac32017-09-04 13:24:43 +0900211 WakeupEvent event = new WakeupEvent();
212 event.iface = iface;
213 event.timestampMs = timestampMs;
214 event.uid = uid;
Hugo Benichi67c5e032017-09-14 16:31:38 +0900215 mWakeupEvents.append(event);
Hugo Benichi60c9f632017-09-05 13:34:48 +0900216 WakeupStats stats = mWakeupStats.get(iface);
217 if (stats == null) {
218 stats = new WakeupStats(iface);
219 mWakeupStats.put(iface, stats);
220 }
221 stats.countEvent(event);
Hugo Benichif562ac32017-09-04 13:24:43 +0900222 }
223
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900224 public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
Hugo Benichiab209752017-09-27 23:28:59 +0900225 for (int i = 0; i < mNetworkMetrics.size(); i++) {
226 ConnectStats stats = mNetworkMetrics.valueAt(i).connectMetrics;
227 if (stats.eventCount == 0) {
228 continue;
229 }
230 events.add(IpConnectivityEventBuilder.toProto(stats));
231 }
232 for (int i = 0; i < mNetworkMetrics.size(); i++) {
233 DnsEvent ev = mNetworkMetrics.valueAt(i).dnsMetrics;
234 if (ev.eventCount == 0) {
235 continue;
236 }
237 events.add(IpConnectivityEventBuilder.toProto(ev));
238 }
Hugo Benichi60c9f632017-09-05 13:34:48 +0900239 for (int i = 0; i < mWakeupStats.size(); i++) {
240 events.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
241 }
Hugo Benichiab209752017-09-27 23:28:59 +0900242 mNetworkMetrics.clear();
Hugo Benichi60c9f632017-09-05 13:34:48 +0900243 mWakeupStats.clear();
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900244 }
245
Lorenzo Colitti43724732016-04-12 23:29:19 +0900246 public synchronized void dump(PrintWriter writer) {
247 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
248 pw.println(TAG + ":");
249 pw.increaseIndent();
Hugo Benichia2decca2017-02-22 14:32:27 +0900250 list(pw);
251 pw.decreaseIndent();
252 }
253
254 public synchronized void list(PrintWriter pw) {
Hugo Benichiab209752017-09-27 23:28:59 +0900255 for (int i = 0; i < mNetworkMetrics.size(); i++) {
256 pw.println(mNetworkMetrics.valueAt(i).connectMetrics);
257 }
258 for (int i = 0; i < mNetworkMetrics.size(); i++) {
259 pw.println(mNetworkMetrics.valueAt(i).dnsMetrics);
260 }
261 for (NetworkMetricsSnapshot s : getNetworkMetricsSnapshots()) {
262 pw.println(s);
263 }
Hugo Benichi60c9f632017-09-05 13:34:48 +0900264 for (int i = 0; i < mWakeupStats.size(); i++) {
265 pw.println(mWakeupStats.valueAt(i));
266 }
Hugo Benichi67c5e032017-09-14 16:31:38 +0900267 for (WakeupEvent wakeup : mWakeupEvents.toArray()) {
Hugo Benichi60c9f632017-09-05 13:34:48 +0900268 pw.println(wakeup);
269 }
Hugo Benichia2decca2017-02-22 14:32:27 +0900270 }
271
272 public synchronized void listAsProtos(PrintWriter pw) {
Hugo Benichiab209752017-09-27 23:28:59 +0900273 for (int i = 0; i < mNetworkMetrics.size(); i++) {
274 pw.print(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).connectMetrics));
275 }
276 for (int i = 0; i < mNetworkMetrics.size(); i++) {
277 pw.print(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).dnsMetrics));
278 }
Hugo Benichi60c9f632017-09-05 13:34:48 +0900279 for (int i = 0; i < mWakeupStats.size(); i++) {
280 pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
281 }
Lorenzo Colitti43724732016-04-12 23:29:19 +0900282 }
283
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900284 private long getTransports(int netId) {
285 // TODO: directly query ConnectivityService instead of going through Binder interface.
286 NetworkCapabilities nc = mCm.getNetworkCapabilities(new Network(netId));
287 if (nc == null) {
288 return 0;
289 }
290 return BitUtils.packBits(nc.getTransportTypes());
291 }
292
Hugo Benichi8b06bcd2016-10-31 15:04:37 +0900293 private static void maybeLog(String s, Object... args) {
294 if (DBG) Log.d(TAG, String.format(s, args));
Lorenzo Colitti43724732016-04-12 23:29:19 +0900295 }
296
Hugo Benichi8b06bcd2016-10-31 15:04:37 +0900297 private static void maybeVerboseLog(String s, Object... args) {
298 if (VDBG) Log.d(TAG, String.format(s, args));
Lorenzo Colitti43724732016-04-12 23:29:19 +0900299 }
Hugo Benichiab209752017-09-27 23:28:59 +0900300
301 /** Helper class for buffering summaries of NetworkMetrics at regular time intervals */
302 static class NetworkMetricsSnapshot {
303
304 public long timeMs;
305 public List<NetworkMetrics.Summary> stats = new ArrayList<>();
306
307 static NetworkMetricsSnapshot collect(long timeMs, SparseArray<NetworkMetrics> networkMetrics) {
308 NetworkMetricsSnapshot snapshot = new NetworkMetricsSnapshot();
309 snapshot.timeMs = timeMs;
310 for (int i = 0; i < networkMetrics.size(); i++) {
311 NetworkMetrics.Summary s = networkMetrics.valueAt(i).getPendingStats();
312 if (s != null) {
313 snapshot.stats.add(s);
314 }
315 }
316 return snapshot;
317 }
318
319 @Override
320 public String toString() {
321 StringJoiner j = new StringJoiner(", ");
322 for (NetworkMetrics.Summary s : stats) {
323 j.add(s.toString());
324 }
325 return String.format("%tT.%tL: %s", timeMs, timeMs, j.toString());
326 }
327 }
Lorenzo Colitti43724732016-04-12 23:29:19 +0900328}