blob: 33f6ed5972042c87ba77e3c38429d1f9689138c6 [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;
paulhu59148b72019-08-12 16:25:11 +080023import android.net.NetworkStack;
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 Klinec172c7d2018-05-01 16:51:44 +0900273 // By default any other argument will fall into the default case which is the equivalent
274 // of calling both the "list" and "ipclient" commands. This includes most notably bug
275 // reports collected by dumpsys.cpp with the "-a" argument.
276 static final String CMD_DEFAULT = "";
Hugo Benichieab511b2016-09-09 09:23:47 +0900277
278 @Override
279 public int logEvent(ConnectivityMetricsEvent event) {
paulhu59148b72019-08-12 16:25:11 +0800280 NetworkStack.checkNetworkStackPermission(getContext());
Hugo Benichieab511b2016-09-09 09:23:47 +0900281 return append(event);
282 }
283
284 @Override
285 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
286 enforceDumpPermission();
287 if (DBG) Log.d(TAG, "dumpsys " + TextUtils.join(" ", args));
288 final String cmd = (args.length > 0) ? args[0] : CMD_DEFAULT;
289 switch (cmd) {
290 case CMD_FLUSH:
Hugo Benichi380a0632017-10-20 09:25:29 +0900291 cmdFlush(pw);
Hugo Benichieab511b2016-09-09 09:23:47 +0900292 return;
Hugo Benichi380a0632017-10-20 09:25:29 +0900293 case CMD_PROTO:
294 cmdListAsProto(pw);
Hugo Benichi1198ba12017-09-15 14:18:57 +0900295 return;
Erik Klinec172c7d2018-05-01 16:51:44 +0900296 case CMD_LIST:
Hugo Benichieab511b2016-09-09 09:23:47 +0900297 default:
Hugo Benichi380a0632017-10-20 09:25:29 +0900298 cmdList(pw);
299 return;
Hugo Benichieab511b2016-09-09 09:23:47 +0900300 }
301 }
302
Hugo Benichieab511b2016-09-09 09:23:47 +0900303 private void enforceDumpPermission() {
304 enforcePermission(android.Manifest.permission.DUMP);
305 }
306
307 private void enforcePermission(String what) {
308 getContext().enforceCallingOrSelfPermission(what, "IpConnectivityMetrics");
309 }
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100310
311 private void enforceNetdEventListeningPermission() {
312 final int uid = Binder.getCallingUid();
313 if (uid != Process.SYSTEM_UID) {
314 throw new SecurityException(String.format("Uid %d has no permission to listen for"
315 + " netd events.", uid));
316 }
317 }
318
319 @Override
Ricky Wai77518fb2017-10-27 14:46:01 +0100320 public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100321 enforceNetdEventListeningPermission();
322 if (mNetdListener == null) {
323 return false;
324 }
Ricky Wai77518fb2017-10-27 14:46:01 +0100325 return mNetdListener.addNetdEventCallback(callerType, callback);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100326 }
327
328 @Override
Ricky Wai77518fb2017-10-27 14:46:01 +0100329 public boolean removeNetdEventCallback(int callerType) {
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100330 enforceNetdEventListeningPermission();
331 if (mNetdListener == null) {
332 // if the service is null, we aren't registered anyway
333 return true;
334 }
Ricky Wai77518fb2017-10-27 14:46:01 +0100335 return mNetdListener.removeNetdEventCallback(callerType);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100336 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900337 };
Hugo Benichi05686db2016-10-19 11:17:28 +0900338
339 private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> {
340 int size = Settings.Global.getInt(ctx.getContentResolver(),
341 Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
342 if (size <= 0) {
343 return DEFAULT_BUFFER_SIZE;
344 }
345 return Math.min(size, MAXIMUM_BUFFER_SIZE);
346 };
Hugo Benichie1c173d2016-10-18 10:36:33 +0900347
348 private static ArrayMap<Class<?>, TokenBucket> makeRateLimitingBuckets() {
349 ArrayMap<Class<?>, TokenBucket> map = new ArrayMap<>();
350 // one token every minute, 50 tokens max: burst of ~50 events every hour.
351 map.put(ApfProgramEvent.class, new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50));
352 return map;
353 }
Hugo Benichi64901e52017-10-19 14:42:40 +0900354
355 /** Direct non-Binder interface for event producer clients within the system servers. */
356 public interface Logger {
357 DefaultNetworkMetrics defaultNetworkMetrics();
358 }
359
360 private class LoggerImpl implements Logger {
361 public DefaultNetworkMetrics defaultNetworkMetrics() {
362 return mDefaultNetworkMetrics;
363 }
364 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900365}