blob: ac745985417e15be008f54e5a6bc8d6f73714c23 [file] [log] [blame]
Hugo Benichieab511b2016-09-09 09:23:47 +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
19import android.content.Context;
20import android.net.ConnectivityMetricsEvent;
21import android.net.IIpConnectivityMetrics;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010022import android.net.INetdEventCallback;
Erik Kline3f8306b2018-05-01 16:51:44 +090023import android.net.ip.IpClient;
Hugo Benichie1c173d2016-10-18 10:36:33 +090024import android.net.metrics.ApfProgramEvent;
Hugo Benichieab511b2016-09-09 09:23:47 +090025import android.net.metrics.IpConnectivityLog;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010026import android.os.Binder;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010027import android.os.Process;
Hugo Benichi05686db2016-10-19 11:17:28 +090028import android.provider.Settings;
Hugo Benichieab511b2016-09-09 09:23:47 +090029import android.text.TextUtils;
Hugo Benichie1c173d2016-10-18 10:36:33 +090030import android.text.format.DateUtils;
31import android.util.ArrayMap;
Hugo Benichieab511b2016-09-09 09:23:47 +090032import android.util.Base64;
33import android.util.Log;
Hugo Benichi64901e52017-10-19 14:42:40 +090034
Hugo Benichieab511b2016-09-09 09:23:47 +090035import com.android.internal.annotations.GuardedBy;
36import com.android.internal.annotations.VisibleForTesting;
Hugo Benichi1198ba12017-09-15 14:18:57 +090037import com.android.internal.util.RingBuffer;
Hugo Benichie1c173d2016-10-18 10:36:33 +090038import com.android.internal.util.TokenBucket;
Hugo Benichi64901e52017-10-19 14:42:40 +090039import com.android.server.LocalServices;
Hugo Benichieab511b2016-09-09 09:23:47 +090040import com.android.server.SystemService;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090041import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
Hugo Benichi64901e52017-10-19 14:42:40 +090042
Hugo Benichieab511b2016-09-09 09:23:47 +090043import java.io.FileDescriptor;
44import java.io.IOException;
45import java.io.PrintWriter;
46import java.util.ArrayList;
Hugo Benichi380a0632017-10-20 09:25:29 +090047import java.util.Arrays;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090048import java.util.List;
Hugo Benichi05686db2016-10-19 11:17:28 +090049import java.util.function.ToIntFunction;
Hugo Benichieab511b2016-09-09 09:23:47 +090050
Hugo Benichi67c5e032017-09-14 16:31:38 +090051/**
52 * Event buffering service for core networking and connectivity metrics.
53 *
54 * {@hide}
55 */
Hugo Benichieab511b2016-09-09 09:23:47 +090056final public class IpConnectivityMetrics extends SystemService {
57 private static final String TAG = IpConnectivityMetrics.class.getSimpleName();
58 private static final boolean DBG = false;
59
Hugo Benichid680d4c2016-10-13 13:16:16 +090060 // The logical version numbers of ipconnectivity.proto, corresponding to the
61 // "version" field of IpConnectivityLog.
62 private static final int NYC = 0;
63 private static final int NYC_MR1 = 1;
64 private static final int NYC_MR2 = 2;
65 public static final int VERSION = NYC_MR2;
66
Hugo Benichieab511b2016-09-09 09:23:47 +090067 private static final String SERVICE_NAME = IpConnectivityLog.SERVICE_NAME;
68
Hugo Benichi1198ba12017-09-15 14:18:57 +090069 // Default size of the event rolling log for bug report dumps.
70 private static final int DEFAULT_LOG_SIZE = 500;
71 // Default size of the event buffer for metrics reporting.
72 // Once the buffer is full, incoming events are dropped.
Hugo Benichieab511b2016-09-09 09:23:47 +090073 private static final int DEFAULT_BUFFER_SIZE = 2000;
Hugo Benichi05686db2016-10-19 11:17:28 +090074 // Maximum size of the event buffer.
75 private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
Hugo Benichieab511b2016-09-09 09:23:47 +090076
Hugo Benichi0d4a3982016-11-25 11:24:22 +090077 private static final int MAXIMUM_CONNECT_LATENCY_RECORDS = 20000;
78
Hugo Benichie1c173d2016-10-18 10:36:33 +090079 private static final int ERROR_RATE_LIMITED = -1;
Hugo Benichieab511b2016-09-09 09:23:47 +090080
Hugo Benichi1198ba12017-09-15 14:18:57 +090081 // Lock ensuring that concurrent manipulations of the event buffers are correct.
Hugo Benichieab511b2016-09-09 09:23:47 +090082 // There are three concurrent operations to synchronize:
83 // - appending events to the buffer.
84 // - iterating throught the buffer.
85 // - flushing the buffer content and replacing it by a new buffer.
86 private final Object mLock = new Object();
87
Hugo Benichi1198ba12017-09-15 14:18:57 +090088 // Implementation instance of IIpConnectivityMetrics.aidl.
Hugo Benichieab511b2016-09-09 09:23:47 +090089 @VisibleForTesting
90 public final Impl impl = new Impl();
Hugo Benichi1198ba12017-09-15 14:18:57 +090091 // Subservice listening to Netd events via INetdEventListener.aidl.
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090092 @VisibleForTesting
93 NetdEventListenerService mNetdListener;
Hugo Benichi00a42d42016-09-13 15:55:09 +090094
Hugo Benichi1198ba12017-09-15 14:18:57 +090095 // Rolling log of the most recent events. This log is used for dumping
96 // connectivity events in bug reports.
97 @GuardedBy("mLock")
98 private final RingBuffer<ConnectivityMetricsEvent> mEventLog =
99 new RingBuffer(ConnectivityMetricsEvent.class, DEFAULT_LOG_SIZE);
100 // Buffer of connectivity events used for metrics reporting. This buffer
101 // does not rotate automatically and instead saturates when it becomes full.
102 // It is flushed at metrics reporting.
Hugo Benichieab511b2016-09-09 09:23:47 +0900103 @GuardedBy("mLock")
104 private ArrayList<ConnectivityMetricsEvent> mBuffer;
Hugo Benichi1198ba12017-09-15 14:18:57 +0900105 // Total number of events dropped from mBuffer since last metrics reporting.
Hugo Benichieab511b2016-09-09 09:23:47 +0900106 @GuardedBy("mLock")
107 private int mDropped;
Hugo Benichi1198ba12017-09-15 14:18:57 +0900108 // Capacity of mBuffer
Hugo Benichieab511b2016-09-09 09:23:47 +0900109 @GuardedBy("mLock")
110 private int mCapacity;
Hugo Benichi1198ba12017-09-15 14:18:57 +0900111 // A list of rate limiting counters keyed by connectivity event types for
112 // metrics reporting mBuffer.
Hugo Benichie1c173d2016-10-18 10:36:33 +0900113 @GuardedBy("mLock")
114 private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets();
Hugo Benichieab511b2016-09-09 09:23:47 +0900115
Hugo Benichi05686db2016-10-19 11:17:28 +0900116 private final ToIntFunction<Context> mCapacityGetter;
117
Hugo Benichi64901e52017-10-19 14:42:40 +0900118 @VisibleForTesting
119 final DefaultNetworkMetrics mDefaultNetworkMetrics = new DefaultNetworkMetrics();
120
Hugo Benichi05686db2016-10-19 11:17:28 +0900121 public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) {
Hugo Benichieab511b2016-09-09 09:23:47 +0900122 super(ctx);
Hugo Benichi05686db2016-10-19 11:17:28 +0900123 mCapacityGetter = capacityGetter;
Hugo Benichieab511b2016-09-09 09:23:47 +0900124 initBuffer();
125 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900126
127 public IpConnectivityMetrics(Context ctx) {
Hugo Benichi05686db2016-10-19 11:17:28 +0900128 this(ctx, READ_BUFFER_SIZE);
Hugo Benichieab511b2016-09-09 09:23:47 +0900129 }
130
131 @Override
132 public void onStart() {
133 if (DBG) Log.d(TAG, "onStart");
134 }
135
136 @Override
137 public void onBootPhase(int phase) {
138 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
139 if (DBG) Log.d(TAG, "onBootPhase");
Hugo Benichie69608f2016-09-23 14:48:01 +0900140 mNetdListener = new NetdEventListenerService(getContext());
Hugo Benichieab511b2016-09-09 09:23:47 +0900141
142 publishBinderService(SERVICE_NAME, impl);
Hugo Benichie69608f2016-09-23 14:48:01 +0900143 publishBinderService(mNetdListener.SERVICE_NAME, mNetdListener);
Hugo Benichi64901e52017-10-19 14:42:40 +0900144
145 LocalServices.addService(Logger.class, new LoggerImpl());
Hugo Benichieab511b2016-09-09 09:23:47 +0900146 }
147 }
148
149 @VisibleForTesting
150 public int bufferCapacity() {
Hugo Benichi05686db2016-10-19 11:17:28 +0900151 return mCapacityGetter.applyAsInt(getContext());
Hugo Benichieab511b2016-09-09 09:23:47 +0900152 }
153
154 private void initBuffer() {
155 synchronized (mLock) {
156 mDropped = 0;
157 mCapacity = bufferCapacity();
158 mBuffer = new ArrayList<>(mCapacity);
159 }
160 }
161
162 private int append(ConnectivityMetricsEvent event) {
163 if (DBG) Log.d(TAG, "logEvent: " + event);
164 synchronized (mLock) {
Hugo Benichi1198ba12017-09-15 14:18:57 +0900165 mEventLog.append(event);
Hugo Benichieab511b2016-09-09 09:23:47 +0900166 final int left = mCapacity - mBuffer.size();
167 if (event == null) {
168 return left;
169 }
Hugo Benichie1c173d2016-10-18 10:36:33 +0900170 if (isRateLimited(event)) {
171 // Do not count as a dropped event. TODO: consider adding separate counter
172 return ERROR_RATE_LIMITED;
173 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900174 if (left == 0) {
175 mDropped++;
176 return 0;
177 }
178 mBuffer.add(event);
179 return left - 1;
180 }
181 }
182
Hugo Benichie1c173d2016-10-18 10:36:33 +0900183 private boolean isRateLimited(ConnectivityMetricsEvent event) {
184 TokenBucket tb = mBuckets.get(event.data.getClass());
185 return (tb != null) && !tb.get();
186 }
187
Hugo Benichieab511b2016-09-09 09:23:47 +0900188 private String flushEncodedOutput() {
189 final ArrayList<ConnectivityMetricsEvent> events;
190 final int dropped;
191 synchronized (mLock) {
192 events = mBuffer;
193 dropped = mDropped;
194 initBuffer();
195 }
196
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900197 final List<IpConnectivityEvent> protoEvents = IpConnectivityEventBuilder.toProto(events);
198
Hugo Benichi1193a9c2017-10-19 14:58:15 +0900199 mDefaultNetworkMetrics.flushEvents(protoEvents);
200
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900201 if (mNetdListener != null) {
202 mNetdListener.flushStatistics(protoEvents);
203 }
204
Hugo Benichieab511b2016-09-09 09:23:47 +0900205 final byte[] data;
206 try {
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900207 data = IpConnectivityEventBuilder.serialize(dropped, protoEvents);
Hugo Benichieab511b2016-09-09 09:23:47 +0900208 } catch (IOException e) {
209 Log.e(TAG, "could not serialize events", e);
210 return "";
211 }
212
213 return Base64.encodeToString(data, Base64.DEFAULT);
214 }
215
216 /**
Hugo Benichi380a0632017-10-20 09:25:29 +0900217 * Clear the event buffer and prints its content as a protobuf serialized byte array
Hugo Benichieab511b2016-09-09 09:23:47 +0900218 * inside a base64 encoded string.
219 */
Hugo Benichi380a0632017-10-20 09:25:29 +0900220 private void cmdFlush(PrintWriter pw) {
Hugo Benichieab511b2016-09-09 09:23:47 +0900221 pw.print(flushEncodedOutput());
222 }
223
224 /**
Hugo Benichi380a0632017-10-20 09:25:29 +0900225 * Print the content of the rolling event buffer in human readable format.
226 * Also print network dns/connect statistics and recent default network events.
Hugo Benichieab511b2016-09-09 09:23:47 +0900227 */
Hugo Benichi380a0632017-10-20 09:25:29 +0900228 private void cmdList(PrintWriter pw) {
229 pw.println("metrics events:");
230 final List<ConnectivityMetricsEvent> events = getEvents();
Hugo Benichieab511b2016-09-09 09:23:47 +0900231 for (ConnectivityMetricsEvent ev : events) {
232 pw.println(ev.toString());
233 }
Hugo Benichi380a0632017-10-20 09:25:29 +0900234 pw.println("");
Hugo Benichia2decca2017-02-22 14:32:27 +0900235 if (mNetdListener != null) {
236 mNetdListener.list(pw);
237 }
Hugo Benichi380a0632017-10-20 09:25:29 +0900238 pw.println("");
Hugo Benichi1193a9c2017-10-19 14:58:15 +0900239 mDefaultNetworkMetrics.listEvents(pw);
Hugo Benichieab511b2016-09-09 09:23:47 +0900240 }
241
Hugo Benichi380a0632017-10-20 09:25:29 +0900242 /*
243 * Print the content of the rolling event buffer in text proto format.
Hugo Benichi1198ba12017-09-15 14:18:57 +0900244 */
Hugo Benichi380a0632017-10-20 09:25:29 +0900245 private void cmdListAsProto(PrintWriter pw) {
246 final List<ConnectivityMetricsEvent> events = getEvents();
247 for (IpConnectivityEvent ev : IpConnectivityEventBuilder.toProto(events)) {
248 pw.print(ev.toString());
Hugo Benichi1198ba12017-09-15 14:18:57 +0900249 }
250 if (mNetdListener != null) {
Hugo Benichi380a0632017-10-20 09:25:29 +0900251 mNetdListener.listAsProtos(pw);
Hugo Benichi1198ba12017-09-15 14:18:57 +0900252 }
Hugo Benichi380a0632017-10-20 09:25:29 +0900253 mDefaultNetworkMetrics.listEventsAsProto(pw);
Hugo Benichi1198ba12017-09-15 14:18:57 +0900254 }
255
Hugo Benichi380a0632017-10-20 09:25:29 +0900256 /*
257 * Return a copy of metrics events stored in buffer for metrics uploading.
258 */
259 private List<ConnectivityMetricsEvent> getEvents() {
Hugo Benichieab511b2016-09-09 09:23:47 +0900260 synchronized (mLock) {
Hugo Benichi380a0632017-10-20 09:25:29 +0900261 return Arrays.asList(mEventLog.toArray());
Hugo Benichieab511b2016-09-09 09:23:47 +0900262 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900263 }
264
265 public final class Impl extends IIpConnectivityMetrics.Stub {
Hugo Benichi380a0632017-10-20 09:25:29 +0900266 // Dump and flushes the metrics event buffer in base64 encoded serialized proto output.
267 static final String CMD_FLUSH = "flush";
268 // Dump the rolling buffer of metrics event in human readable proto text format.
269 static final String CMD_PROTO = "proto";
270 // Dump the rolling buffer of metrics event and pretty print events using a human readable
271 // format. Also print network dns/connect statistics and default network event time series.
272 static final String CMD_LIST = "list";
Erik Kline3f8306b2018-05-01 16:51:44 +0900273 // Dump all IpClient logs ("ipclient").
274 static final String CMD_IPCLIENT = IpClient.DUMP_ARG;
275 // By default any other argument will fall into the default case which is the equivalent
276 // of calling both the "list" and "ipclient" commands. This includes most notably bug
277 // reports collected by dumpsys.cpp with the "-a" argument.
278 static final String CMD_DEFAULT = "";
Hugo Benichieab511b2016-09-09 09:23:47 +0900279
280 @Override
281 public int logEvent(ConnectivityMetricsEvent event) {
282 enforceConnectivityInternalPermission();
283 return append(event);
284 }
285
286 @Override
287 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
288 enforceDumpPermission();
289 if (DBG) Log.d(TAG, "dumpsys " + TextUtils.join(" ", args));
290 final String cmd = (args.length > 0) ? args[0] : CMD_DEFAULT;
291 switch (cmd) {
292 case CMD_FLUSH:
Hugo Benichi380a0632017-10-20 09:25:29 +0900293 cmdFlush(pw);
Hugo Benichieab511b2016-09-09 09:23:47 +0900294 return;
Hugo Benichi380a0632017-10-20 09:25:29 +0900295 case CMD_PROTO:
296 cmdListAsProto(pw);
Hugo Benichi1198ba12017-09-15 14:18:57 +0900297 return;
Erik Kline3f8306b2018-05-01 16:51:44 +0900298 case CMD_IPCLIENT: {
299 final String[] ipclientArgs = ((args != null) && (args.length > 1))
300 ? Arrays.copyOfRange(args, 1, args.length)
301 : null;
302 IpClient.dumpAllLogs(pw, ipclientArgs);
303 return;
304 }
305 case CMD_LIST:
306 cmdList(pw);
307 return;
Hugo Benichieab511b2016-09-09 09:23:47 +0900308 default:
Hugo Benichi380a0632017-10-20 09:25:29 +0900309 cmdList(pw);
Erik Kline3f8306b2018-05-01 16:51:44 +0900310 pw.println("");
311 IpClient.dumpAllLogs(pw, null);
Hugo Benichi380a0632017-10-20 09:25:29 +0900312 return;
Hugo Benichieab511b2016-09-09 09:23:47 +0900313 }
314 }
315
316 private void enforceConnectivityInternalPermission() {
317 enforcePermission(android.Manifest.permission.CONNECTIVITY_INTERNAL);
318 }
319
320 private void enforceDumpPermission() {
321 enforcePermission(android.Manifest.permission.DUMP);
322 }
323
324 private void enforcePermission(String what) {
325 getContext().enforceCallingOrSelfPermission(what, "IpConnectivityMetrics");
326 }
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100327
328 private void enforceNetdEventListeningPermission() {
329 final int uid = Binder.getCallingUid();
330 if (uid != Process.SYSTEM_UID) {
331 throw new SecurityException(String.format("Uid %d has no permission to listen for"
332 + " netd events.", uid));
333 }
334 }
335
336 @Override
Ricky Wai1a6e6672017-10-27 14:46:01 +0100337 public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100338 enforceNetdEventListeningPermission();
339 if (mNetdListener == null) {
340 return false;
341 }
Ricky Wai1a6e6672017-10-27 14:46:01 +0100342 return mNetdListener.addNetdEventCallback(callerType, callback);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100343 }
344
345 @Override
Ricky Wai1a6e6672017-10-27 14:46:01 +0100346 public boolean removeNetdEventCallback(int callerType) {
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100347 enforceNetdEventListeningPermission();
348 if (mNetdListener == null) {
349 // if the service is null, we aren't registered anyway
350 return true;
351 }
Ricky Wai1a6e6672017-10-27 14:46:01 +0100352 return mNetdListener.removeNetdEventCallback(callerType);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100353 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900354 };
Hugo Benichi05686db2016-10-19 11:17:28 +0900355
356 private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> {
357 int size = Settings.Global.getInt(ctx.getContentResolver(),
358 Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
359 if (size <= 0) {
360 return DEFAULT_BUFFER_SIZE;
361 }
362 return Math.min(size, MAXIMUM_BUFFER_SIZE);
363 };
Hugo Benichie1c173d2016-10-18 10:36:33 +0900364
365 private static ArrayMap<Class<?>, TokenBucket> makeRateLimitingBuckets() {
366 ArrayMap<Class<?>, TokenBucket> map = new ArrayMap<>();
367 // one token every minute, 50 tokens max: burst of ~50 events every hour.
368 map.put(ApfProgramEvent.class, new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50));
369 return map;
370 }
Hugo Benichi64901e52017-10-19 14:42:40 +0900371
372 /** Direct non-Binder interface for event producer clients within the system servers. */
373 public interface Logger {
374 DefaultNetworkMetrics defaultNetworkMetrics();
375 }
376
377 private class LoggerImpl implements Logger {
378 public DefaultNetworkMetrics defaultNetworkMetrics() {
379 return mDefaultNetworkMetrics;
380 }
381 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900382}