blob: 6206dfcd6622bb817f427188ba20f19c867b0a47 [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 Benichif562ac32017-09-04 13:24:43 +090030import android.net.metrics.WakeupEvent;
Hugo Benichi60c9f632017-09-05 13:34:48 +090031import android.net.metrics.WakeupStats;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010032import android.os.RemoteException;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090033import android.text.format.DateUtils;
Lorenzo Colitti43724732016-04-12 23:29:19 +090034import android.util.Log;
Hugo Benichi60c9f632017-09-05 13:34:48 +090035import android.util.ArrayMap;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090036import android.util.SparseArray;
Lorenzo Colitti43724732016-04-12 23:29:19 +090037import com.android.internal.annotations.GuardedBy;
Hugo Benichi5e055182016-06-01 08:50:38 +090038import com.android.internal.annotations.VisibleForTesting;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090039import com.android.internal.util.BitUtils;
Lorenzo Colitti43724732016-04-12 23:29:19 +090040import com.android.internal.util.IndentingPrintWriter;
Hugo Benichi67c5e032017-09-14 16:31:38 +090041import com.android.internal.util.RingBuffer;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090042import com.android.internal.util.TokenBucket;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090043import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
Lorenzo Colitti43724732016-04-12 23:29:19 +090044import java.io.PrintWriter;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090045import java.util.List;
Hugo Benichi5eb90532017-03-23 18:38:22 +090046import java.util.function.Function;
47import java.util.function.IntFunction;
Lorenzo Colitti43724732016-04-12 23:29:19 +090048
Lorenzo Colitti43724732016-04-12 23:29:19 +090049/**
Michal Karpinskid11d18c2016-09-15 17:07:08 +090050 * Implementation of the INetdEventListener interface.
Lorenzo Colitti43724732016-04-12 23:29:19 +090051 */
Michal Karpinskid11d18c2016-09-15 17:07:08 +090052public class NetdEventListenerService extends INetdEventListener.Stub {
Lorenzo Colitti43724732016-04-12 23:29:19 +090053
Michal Karpinskid11d18c2016-09-15 17:07:08 +090054 public static final String SERVICE_NAME = "netd_listener";
Lorenzo Colitti43724732016-04-12 23:29:19 +090055
Michal Karpinskid11d18c2016-09-15 17:07:08 +090056 private static final String TAG = NetdEventListenerService.class.getSimpleName();
Hugo Benichi8b06bcd2016-10-31 15:04:37 +090057 private static final boolean DBG = false;
Lorenzo Colitti43724732016-04-12 23:29:19 +090058 private static final boolean VDBG = false;
59
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090060 private static final int INITIAL_DNS_BATCH_SIZE = 100;
Lorenzo Colitti43724732016-04-12 23:29:19 +090061
Hugo Benichi0d4a3982016-11-25 11:24:22 +090062 // Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum
63 // bursts of 5000 measurements.
64 private static final int CONNECT_LATENCY_BURST_LIMIT = 5000;
65 private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS;
66 private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;
67
Hugo Benichif562ac32017-09-04 13:24:43 +090068 @VisibleForTesting
69 static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;
Hugo Benichi60c9f632017-09-05 13:34:48 +090070 // TODO: dedup this String constant with the one used in
71 // ConnectivityService#wakeupModifyInterface().
72 @VisibleForTesting
73 static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:";
Hugo Benichif562ac32017-09-04 13:24:43 +090074
Hugo Benichi5eb90532017-03-23 18:38:22 +090075 // Sparse arrays of DNS and connect events, grouped by net id.
Lorenzo Colitti43724732016-04-12 23:29:19 +090076 @GuardedBy("this")
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090077 private final SparseArray<DnsEvent> mDnsEvents = new SparseArray<>();
Hugo Benichi5eb90532017-03-23 18:38:22 +090078 @GuardedBy("this")
79 private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>();
Lorenzo Colitti43724732016-04-12 23:29:19 +090080
Hugo Benichi60c9f632017-09-05 13:34:48 +090081 // Array of aggregated wakeup event stats, grouped by interface name.
82 @GuardedBy("this")
83 private final ArrayMap<String, WakeupStats> mWakeupStats = new ArrayMap<>();
Hugo Benichif562ac32017-09-04 13:24:43 +090084 // Ring buffer array for storing packet wake up events sent by Netd.
85 @GuardedBy("this")
Hugo Benichi67c5e032017-09-14 16:31:38 +090086 private final RingBuffer<WakeupEvent> mWakeupEvents =
87 new RingBuffer(WakeupEvent.class, WAKEUP_EVENT_BUFFER_LENGTH);
Hugo Benichif562ac32017-09-04 13:24:43 +090088
Lorenzo Colitti43724732016-04-12 23:29:19 +090089 private final ConnectivityManager mCm;
Lorenzo Colitti43724732016-04-12 23:29:19 +090090
Hugo Benichi0d4a3982016-11-25 11:24:22 +090091 @GuardedBy("this")
92 private final TokenBucket mConnectTb =
93 new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010094 // Callback should only be registered/unregistered when logging is being enabled/disabled in DPM
95 // by the device owner. It's DevicePolicyManager's responsibility to ensure that.
96 @GuardedBy("this")
97 private INetdEventCallback mNetdEventCallback;
98
99 public synchronized boolean registerNetdEventCallback(INetdEventCallback callback) {
100 mNetdEventCallback = callback;
101 return true;
102 }
103
104 public synchronized boolean unregisterNetdEventCallback() {
105 mNetdEventCallback = null;
106 return true;
107 }
108
Michal Karpinskid11d18c2016-09-15 17:07:08 +0900109 public NetdEventListenerService(Context context) {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900110 this(context.getSystemService(ConnectivityManager.class));
Hugo Benichi5e055182016-06-01 08:50:38 +0900111 }
112
113 @VisibleForTesting
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900114 public NetdEventListenerService(ConnectivityManager cm) {
Lorenzo Colitti43724732016-04-12 23:29:19 +0900115 // We are started when boot is complete, so ConnectivityService should already be running.
Hugo Benichi5e055182016-06-01 08:50:38 +0900116 mCm = cm;
Lorenzo Colitti43724732016-04-12 23:29:19 +0900117 }
118
119 @Override
120 // Called concurrently by multiple binder threads.
Michal Karpinski14c9d2d2016-09-27 17:13:57 +0100121 // This method must not block or perform long-running operations.
122 public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100123 String hostname, String[] ipAddresses, int ipAddressesCount, int uid)
124 throws RemoteException {
Hugo Benichi8b06bcd2016-10-31 15:04:37 +0900125 maybeVerboseLog("onDnsEvent(%d, %d, %d, %dms)", netId, eventType, returnCode, latencyMs);
Lorenzo Colitti43724732016-04-12 23:29:19 +0900126
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900127 DnsEvent dnsEvent = mDnsEvents.get(netId);
128 if (dnsEvent == null) {
129 dnsEvent = makeDnsEvent(netId);
130 mDnsEvents.put(netId, dnsEvent);
Lorenzo Colitti43724732016-04-12 23:29:19 +0900131 }
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900132 dnsEvent.addResult((byte) eventType, (byte) returnCode, latencyMs);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100133
134 if (mNetdEventCallback != null) {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900135 long timestamp = System.currentTimeMillis();
136 mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100137 }
Lorenzo Colitti43724732016-04-12 23:29:19 +0900138 }
139
Michal Karpinski965894e2016-09-28 16:06:16 +0100140 @Override
141 // Called concurrently by multiple binder threads.
142 // This method must not block or perform long-running operations.
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900143 public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr,
144 int port, int uid) throws RemoteException {
Hugo Benichi8b06bcd2016-10-31 15:04:37 +0900145 maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100146
Hugo Benichi5eb90532017-03-23 18:38:22 +0900147 ConnectStats connectStats = mConnectEvents.get(netId);
148 if (connectStats == null) {
149 connectStats = makeConnectStats(netId);
150 mConnectEvents.put(netId, connectStats);
151 }
152 connectStats.addEvent(error, latencyMs, ipAddr);
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900153
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100154 if (mNetdEventCallback != null) {
155 mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid);
156 }
Michal Karpinski965894e2016-09-28 16:06:16 +0100157 }
158
Joel Scherpelzb369bf52017-05-22 13:47:41 +0900159 @Override
160 public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
Hugo Benichif562ac32017-09-04 13:24:43 +0900161 maybeVerboseLog("onWakeupEvent(%s, %d, %d, %sns)", prefix, uid, gid, timestampNs);
162
163 // TODO: add ip protocol and port
164
165 String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
Hugo Benichi60c9f632017-09-05 13:34:48 +0900166 final long timestampMs;
167 if (timestampNs > 0) {
168 timestampMs = timestampNs / NANOS_PER_MS;
169 } else {
170 timestampMs = System.currentTimeMillis();
171 }
Hugo Benichif562ac32017-09-04 13:24:43 +0900172
Hugo Benichi175b5742017-09-19 13:15:26 +0900173 addWakeupEvent(iface, timestampMs, uid);
Hugo Benichif562ac32017-09-04 13:24:43 +0900174 }
175
176 @GuardedBy("this")
Hugo Benichi175b5742017-09-19 13:15:26 +0900177 private void addWakeupEvent(String iface, long timestampMs, int uid) {
Hugo Benichif562ac32017-09-04 13:24:43 +0900178 WakeupEvent event = new WakeupEvent();
179 event.iface = iface;
180 event.timestampMs = timestampMs;
181 event.uid = uid;
Hugo Benichi67c5e032017-09-14 16:31:38 +0900182 mWakeupEvents.append(event);
Hugo Benichi60c9f632017-09-05 13:34:48 +0900183 WakeupStats stats = mWakeupStats.get(iface);
184 if (stats == null) {
185 stats = new WakeupStats(iface);
186 mWakeupStats.put(iface, stats);
187 }
188 stats.countEvent(event);
Hugo Benichif562ac32017-09-04 13:24:43 +0900189 }
190
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900191 public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
Hugo Benichi5eb90532017-03-23 18:38:22 +0900192 flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
193 flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
Hugo Benichi60c9f632017-09-05 13:34:48 +0900194 for (int i = 0; i < mWakeupStats.size(); i++) {
195 events.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
196 }
197 mWakeupStats.clear();
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900198 }
199
Lorenzo Colitti43724732016-04-12 23:29:19 +0900200 public synchronized void dump(PrintWriter writer) {
201 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
202 pw.println(TAG + ":");
203 pw.increaseIndent();
Hugo Benichia2decca2017-02-22 14:32:27 +0900204 list(pw);
205 pw.decreaseIndent();
206 }
207
208 public synchronized void list(PrintWriter pw) {
Hugo Benichi60c9f632017-09-05 13:34:48 +0900209 listEvents(pw, mConnectEvents, (x) -> x, "\n");
210 listEvents(pw, mDnsEvents, (x) -> x, "\n");
211 for (int i = 0; i < mWakeupStats.size(); i++) {
212 pw.println(mWakeupStats.valueAt(i));
213 }
Hugo Benichi67c5e032017-09-14 16:31:38 +0900214 for (WakeupEvent wakeup : mWakeupEvents.toArray()) {
Hugo Benichi60c9f632017-09-05 13:34:48 +0900215 pw.println(wakeup);
216 }
Hugo Benichia2decca2017-02-22 14:32:27 +0900217 }
218
219 public synchronized void listAsProtos(PrintWriter pw) {
Hugo Benichi60c9f632017-09-05 13:34:48 +0900220 listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto, "");
221 listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto, "");
222 for (int i = 0; i < mWakeupStats.size(); i++) {
223 pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
224 }
Lorenzo Colitti43724732016-04-12 23:29:19 +0900225 }
226
Hugo Benichi5eb90532017-03-23 18:38:22 +0900227 private static <T> void flushProtos(List<IpConnectivityEvent> out, SparseArray<T> in,
228 Function<T, IpConnectivityEvent> mapper) {
229 for (int i = 0; i < in.size(); i++) {
230 out.add(mapper.apply(in.valueAt(i)));
231 }
232 in.clear();
233 }
234
Hugo Benichif562ac32017-09-04 13:24:43 +0900235 private static <T> void listEvents(
Hugo Benichi60c9f632017-09-05 13:34:48 +0900236 PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper, String separator) {
237 // Proto derived Classes have toString method that adds a \n at the end.
238 // Let the caller control that by passing in the line separator explicitly.
Hugo Benichi5eb90532017-03-23 18:38:22 +0900239 for (int i = 0; i < events.size(); i++) {
Hugo Benichi60c9f632017-09-05 13:34:48 +0900240 pw.print(mapper.apply(events.valueAt(i)));
241 pw.print(separator);
Hugo Benichif562ac32017-09-04 13:24:43 +0900242 }
243 }
244
Hugo Benichi5eb90532017-03-23 18:38:22 +0900245 private ConnectStats makeConnectStats(int netId) {
246 long transports = getTransports(netId);
247 return new ConnectStats(netId, transports, mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS);
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900248 }
249
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900250 private DnsEvent makeDnsEvent(int netId) {
251 long transports = getTransports(netId);
252 return new DnsEvent(netId, transports, INITIAL_DNS_BATCH_SIZE);
253 }
254
255 private long getTransports(int netId) {
256 // TODO: directly query ConnectivityService instead of going through Binder interface.
257 NetworkCapabilities nc = mCm.getNetworkCapabilities(new Network(netId));
258 if (nc == null) {
259 return 0;
260 }
261 return BitUtils.packBits(nc.getTransportTypes());
262 }
263
Hugo Benichi8b06bcd2016-10-31 15:04:37 +0900264 private static void maybeLog(String s, Object... args) {
265 if (DBG) Log.d(TAG, String.format(s, args));
Lorenzo Colitti43724732016-04-12 23:29:19 +0900266 }
267
Hugo Benichi8b06bcd2016-10-31 15:04:37 +0900268 private static void maybeVerboseLog(String s, Object... args) {
269 if (VDBG) Log.d(TAG, String.format(s, args));
Lorenzo Colitti43724732016-04-12 23:29:19 +0900270 }
271}