blob: 5cc390a02b865c4075fe097ae2599721af69bd3b [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;
Hugo Benichie1c173d2016-10-18 10:36:33 +090023import android.net.metrics.ApfProgramEvent;
Hugo Benichieab511b2016-09-09 09:23:47 +090024import android.net.metrics.IpConnectivityLog;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010025import android.os.Binder;
Hugo Benichieab511b2016-09-09 09:23:47 +090026import android.os.IBinder;
27import android.os.Parcelable;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010028import android.os.Process;
Hugo Benichi05686db2016-10-19 11:17:28 +090029import android.provider.Settings;
Hugo Benichieab511b2016-09-09 09:23:47 +090030import android.text.TextUtils;
Hugo Benichie1c173d2016-10-18 10:36:33 +090031import android.text.format.DateUtils;
32import android.util.ArrayMap;
Hugo Benichieab511b2016-09-09 09:23:47 +090033import android.util.Base64;
34import android.util.Log;
Hugo Benichi64901e52017-10-19 14:42:40 +090035
Hugo Benichieab511b2016-09-09 09:23:47 +090036import com.android.internal.annotations.GuardedBy;
37import com.android.internal.annotations.VisibleForTesting;
Hugo Benichi1198ba12017-09-15 14:18:57 +090038import com.android.internal.util.RingBuffer;
Hugo Benichie1c173d2016-10-18 10:36:33 +090039import com.android.internal.util.TokenBucket;
Hugo Benichi64901e52017-10-19 14:42:40 +090040import com.android.server.LocalServices;
Hugo Benichieab511b2016-09-09 09:23:47 +090041import com.android.server.SystemService;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090042import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
Hugo Benichi64901e52017-10-19 14:42:40 +090043
Hugo Benichieab511b2016-09-09 09:23:47 +090044import java.io.FileDescriptor;
45import java.io.IOException;
46import java.io.PrintWriter;
47import java.util.ArrayList;
Hugo Benichi380a0632017-10-20 09:25:29 +090048import java.util.Arrays;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090049import java.util.List;
Hugo Benichi05686db2016-10-19 11:17:28 +090050import java.util.function.ToIntFunction;
Hugo Benichieab511b2016-09-09 09:23:47 +090051
Hugo Benichi67c5e032017-09-14 16:31:38 +090052/**
53 * Event buffering service for core networking and connectivity metrics.
54 *
55 * {@hide}
56 */
Hugo Benichieab511b2016-09-09 09:23:47 +090057final public class IpConnectivityMetrics extends SystemService {
58 private static final String TAG = IpConnectivityMetrics.class.getSimpleName();
59 private static final boolean DBG = false;
60
Hugo Benichid680d4c2016-10-13 13:16:16 +090061 // The logical version numbers of ipconnectivity.proto, corresponding to the
62 // "version" field of IpConnectivityLog.
63 private static final int NYC = 0;
64 private static final int NYC_MR1 = 1;
65 private static final int NYC_MR2 = 2;
66 public static final int VERSION = NYC_MR2;
67
Hugo Benichieab511b2016-09-09 09:23:47 +090068 private static final String SERVICE_NAME = IpConnectivityLog.SERVICE_NAME;
69
Hugo Benichi1198ba12017-09-15 14:18:57 +090070 // Default size of the event rolling log for bug report dumps.
71 private static final int DEFAULT_LOG_SIZE = 500;
72 // Default size of the event buffer for metrics reporting.
73 // Once the buffer is full, incoming events are dropped.
Hugo Benichieab511b2016-09-09 09:23:47 +090074 private static final int DEFAULT_BUFFER_SIZE = 2000;
Hugo Benichi05686db2016-10-19 11:17:28 +090075 // Maximum size of the event buffer.
76 private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
Hugo Benichieab511b2016-09-09 09:23:47 +090077
Hugo Benichi0d4a3982016-11-25 11:24:22 +090078 private static final int MAXIMUM_CONNECT_LATENCY_RECORDS = 20000;
79
Hugo Benichie1c173d2016-10-18 10:36:33 +090080 private static final int ERROR_RATE_LIMITED = -1;
Hugo Benichieab511b2016-09-09 09:23:47 +090081
Hugo Benichi1198ba12017-09-15 14:18:57 +090082 // Lock ensuring that concurrent manipulations of the event buffers are correct.
Hugo Benichieab511b2016-09-09 09:23:47 +090083 // There are three concurrent operations to synchronize:
84 // - appending events to the buffer.
85 // - iterating throught the buffer.
86 // - flushing the buffer content and replacing it by a new buffer.
87 private final Object mLock = new Object();
88
Hugo Benichi1198ba12017-09-15 14:18:57 +090089 // Implementation instance of IIpConnectivityMetrics.aidl.
Hugo Benichieab511b2016-09-09 09:23:47 +090090 @VisibleForTesting
91 public final Impl impl = new Impl();
Hugo Benichi1198ba12017-09-15 14:18:57 +090092 // Subservice listening to Netd events via INetdEventListener.aidl.
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090093 @VisibleForTesting
94 NetdEventListenerService mNetdListener;
Hugo Benichi00a42d42016-09-13 15:55:09 +090095
Hugo Benichi1198ba12017-09-15 14:18:57 +090096 // Rolling log of the most recent events. This log is used for dumping
97 // connectivity events in bug reports.
98 @GuardedBy("mLock")
99 private final RingBuffer<ConnectivityMetricsEvent> mEventLog =
100 new RingBuffer(ConnectivityMetricsEvent.class, DEFAULT_LOG_SIZE);
101 // Buffer of connectivity events used for metrics reporting. This buffer
102 // does not rotate automatically and instead saturates when it becomes full.
103 // It is flushed at metrics reporting.
Hugo Benichieab511b2016-09-09 09:23:47 +0900104 @GuardedBy("mLock")
105 private ArrayList<ConnectivityMetricsEvent> mBuffer;
Hugo Benichi1198ba12017-09-15 14:18:57 +0900106 // Total number of events dropped from mBuffer since last metrics reporting.
Hugo Benichieab511b2016-09-09 09:23:47 +0900107 @GuardedBy("mLock")
108 private int mDropped;
Hugo Benichi1198ba12017-09-15 14:18:57 +0900109 // Capacity of mBuffer
Hugo Benichieab511b2016-09-09 09:23:47 +0900110 @GuardedBy("mLock")
111 private int mCapacity;
Hugo Benichi1198ba12017-09-15 14:18:57 +0900112 // A list of rate limiting counters keyed by connectivity event types for
113 // metrics reporting mBuffer.
Hugo Benichie1c173d2016-10-18 10:36:33 +0900114 @GuardedBy("mLock")
115 private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets();
Hugo Benichieab511b2016-09-09 09:23:47 +0900116
Hugo Benichi05686db2016-10-19 11:17:28 +0900117 private final ToIntFunction<Context> mCapacityGetter;
118
Hugo Benichi64901e52017-10-19 14:42:40 +0900119 @VisibleForTesting
120 final DefaultNetworkMetrics mDefaultNetworkMetrics = new DefaultNetworkMetrics();
121
Hugo Benichi05686db2016-10-19 11:17:28 +0900122 public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) {
Hugo Benichieab511b2016-09-09 09:23:47 +0900123 super(ctx);
Hugo Benichi05686db2016-10-19 11:17:28 +0900124 mCapacityGetter = capacityGetter;
Hugo Benichieab511b2016-09-09 09:23:47 +0900125 initBuffer();
126 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900127
128 public IpConnectivityMetrics(Context ctx) {
Hugo Benichi05686db2016-10-19 11:17:28 +0900129 this(ctx, READ_BUFFER_SIZE);
Hugo Benichieab511b2016-09-09 09:23:47 +0900130 }
131
132 @Override
133 public void onStart() {
134 if (DBG) Log.d(TAG, "onStart");
135 }
136
137 @Override
138 public void onBootPhase(int phase) {
139 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
140 if (DBG) Log.d(TAG, "onBootPhase");
Hugo Benichie69608f2016-09-23 14:48:01 +0900141 mNetdListener = new NetdEventListenerService(getContext());
Hugo Benichieab511b2016-09-09 09:23:47 +0900142
143 publishBinderService(SERVICE_NAME, impl);
Hugo Benichie69608f2016-09-23 14:48:01 +0900144 publishBinderService(mNetdListener.SERVICE_NAME, mNetdListener);
Hugo Benichi64901e52017-10-19 14:42:40 +0900145
146 LocalServices.addService(Logger.class, new LoggerImpl());
Hugo Benichieab511b2016-09-09 09:23:47 +0900147 }
148 }
149
150 @VisibleForTesting
151 public int bufferCapacity() {
Hugo Benichi05686db2016-10-19 11:17:28 +0900152 return mCapacityGetter.applyAsInt(getContext());
Hugo Benichieab511b2016-09-09 09:23:47 +0900153 }
154
155 private void initBuffer() {
156 synchronized (mLock) {
157 mDropped = 0;
158 mCapacity = bufferCapacity();
159 mBuffer = new ArrayList<>(mCapacity);
160 }
161 }
162
163 private int append(ConnectivityMetricsEvent event) {
164 if (DBG) Log.d(TAG, "logEvent: " + event);
165 synchronized (mLock) {
Hugo Benichi1198ba12017-09-15 14:18:57 +0900166 mEventLog.append(event);
Hugo Benichieab511b2016-09-09 09:23:47 +0900167 final int left = mCapacity - mBuffer.size();
168 if (event == null) {
169 return left;
170 }
Hugo Benichie1c173d2016-10-18 10:36:33 +0900171 if (isRateLimited(event)) {
172 // Do not count as a dropped event. TODO: consider adding separate counter
173 return ERROR_RATE_LIMITED;
174 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900175 if (left == 0) {
176 mDropped++;
177 return 0;
178 }
179 mBuffer.add(event);
180 return left - 1;
181 }
182 }
183
Hugo Benichie1c173d2016-10-18 10:36:33 +0900184 private boolean isRateLimited(ConnectivityMetricsEvent event) {
185 TokenBucket tb = mBuckets.get(event.data.getClass());
186 return (tb != null) && !tb.get();
187 }
188
Hugo Benichieab511b2016-09-09 09:23:47 +0900189 private String flushEncodedOutput() {
190 final ArrayList<ConnectivityMetricsEvent> events;
191 final int dropped;
192 synchronized (mLock) {
193 events = mBuffer;
194 dropped = mDropped;
195 initBuffer();
196 }
197
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900198 final List<IpConnectivityEvent> protoEvents = IpConnectivityEventBuilder.toProto(events);
199
Hugo Benichi1193a9c2017-10-19 14:58:15 +0900200 mDefaultNetworkMetrics.flushEvents(protoEvents);
201
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900202 if (mNetdListener != null) {
203 mNetdListener.flushStatistics(protoEvents);
204 }
205
Hugo Benichieab511b2016-09-09 09:23:47 +0900206 final byte[] data;
207 try {
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900208 data = IpConnectivityEventBuilder.serialize(dropped, protoEvents);
Hugo Benichieab511b2016-09-09 09:23:47 +0900209 } catch (IOException e) {
210 Log.e(TAG, "could not serialize events", e);
211 return "";
212 }
213
214 return Base64.encodeToString(data, Base64.DEFAULT);
215 }
216
217 /**
Hugo Benichi380a0632017-10-20 09:25:29 +0900218 * Clear the event buffer and prints its content as a protobuf serialized byte array
Hugo Benichieab511b2016-09-09 09:23:47 +0900219 * inside a base64 encoded string.
220 */
Hugo Benichi380a0632017-10-20 09:25:29 +0900221 private void cmdFlush(PrintWriter pw) {
Hugo Benichieab511b2016-09-09 09:23:47 +0900222 pw.print(flushEncodedOutput());
223 }
224
225 /**
Hugo Benichi380a0632017-10-20 09:25:29 +0900226 * Print the content of the rolling event buffer in human readable format.
227 * Also print network dns/connect statistics and recent default network events.
Hugo Benichieab511b2016-09-09 09:23:47 +0900228 */
Hugo Benichi380a0632017-10-20 09:25:29 +0900229 private void cmdList(PrintWriter pw) {
230 pw.println("metrics events:");
231 final List<ConnectivityMetricsEvent> events = getEvents();
Hugo Benichieab511b2016-09-09 09:23:47 +0900232 for (ConnectivityMetricsEvent ev : events) {
233 pw.println(ev.toString());
234 }
Hugo Benichi380a0632017-10-20 09:25:29 +0900235 pw.println("");
Hugo Benichia2decca2017-02-22 14:32:27 +0900236 if (mNetdListener != null) {
237 mNetdListener.list(pw);
238 }
Hugo Benichi380a0632017-10-20 09:25:29 +0900239 pw.println("");
Hugo Benichi1193a9c2017-10-19 14:58:15 +0900240 mDefaultNetworkMetrics.listEvents(pw);
Hugo Benichieab511b2016-09-09 09:23:47 +0900241 }
242
Hugo Benichi380a0632017-10-20 09:25:29 +0900243 /*
244 * Print the content of the rolling event buffer in text proto format.
Hugo Benichi1198ba12017-09-15 14:18:57 +0900245 */
Hugo Benichi380a0632017-10-20 09:25:29 +0900246 private void cmdListAsProto(PrintWriter pw) {
247 final List<ConnectivityMetricsEvent> events = getEvents();
248 for (IpConnectivityEvent ev : IpConnectivityEventBuilder.toProto(events)) {
249 pw.print(ev.toString());
Hugo Benichi1198ba12017-09-15 14:18:57 +0900250 }
251 if (mNetdListener != null) {
Hugo Benichi380a0632017-10-20 09:25:29 +0900252 mNetdListener.listAsProtos(pw);
Hugo Benichi1198ba12017-09-15 14:18:57 +0900253 }
Hugo Benichi380a0632017-10-20 09:25:29 +0900254 mDefaultNetworkMetrics.listEventsAsProto(pw);
Hugo Benichi1198ba12017-09-15 14:18:57 +0900255 }
256
Hugo Benichi380a0632017-10-20 09:25:29 +0900257 /*
258 * Return a copy of metrics events stored in buffer for metrics uploading.
259 */
260 private List<ConnectivityMetricsEvent> getEvents() {
Hugo Benichieab511b2016-09-09 09:23:47 +0900261 synchronized (mLock) {
Hugo Benichi380a0632017-10-20 09:25:29 +0900262 return Arrays.asList(mEventLog.toArray());
Hugo Benichieab511b2016-09-09 09:23:47 +0900263 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900264 }
265
266 public final class Impl extends IIpConnectivityMetrics.Stub {
Hugo Benichi380a0632017-10-20 09:25:29 +0900267 // Dump and flushes the metrics event buffer in base64 encoded serialized proto output.
268 static final String CMD_FLUSH = "flush";
269 // Dump the rolling buffer of metrics event in human readable proto text format.
270 static final String CMD_PROTO = "proto";
271 // Dump the rolling buffer of metrics event and pretty print events using a human readable
272 // format. Also print network dns/connect statistics and default network event time series.
273 static final String CMD_LIST = "list";
274 // By default any other argument will fall into the default case which is remapped to the
275 // "list" command. This includes most notably bug reports collected by dumpsys.cpp with
276 // the "-a" argument.
277 static final String CMD_DEFAULT = CMD_LIST;
Hugo Benichieab511b2016-09-09 09:23:47 +0900278
279 @Override
280 public int logEvent(ConnectivityMetricsEvent event) {
281 enforceConnectivityInternalPermission();
282 return append(event);
283 }
284
285 @Override
286 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
287 enforceDumpPermission();
288 if (DBG) Log.d(TAG, "dumpsys " + TextUtils.join(" ", args));
289 final String cmd = (args.length > 0) ? args[0] : CMD_DEFAULT;
290 switch (cmd) {
291 case CMD_FLUSH:
Hugo Benichi380a0632017-10-20 09:25:29 +0900292 cmdFlush(pw);
Hugo Benichieab511b2016-09-09 09:23:47 +0900293 return;
Hugo Benichi380a0632017-10-20 09:25:29 +0900294 case CMD_PROTO:
295 cmdListAsProto(pw);
Hugo Benichi1198ba12017-09-15 14:18:57 +0900296 return;
Hugo Benichi380a0632017-10-20 09:25:29 +0900297 case CMD_LIST: // fallthrough
Hugo Benichieab511b2016-09-09 09:23:47 +0900298 default:
Hugo Benichi380a0632017-10-20 09:25:29 +0900299 cmdList(pw);
300 return;
Hugo Benichieab511b2016-09-09 09:23:47 +0900301 }
302 }
303
304 private void enforceConnectivityInternalPermission() {
305 enforcePermission(android.Manifest.permission.CONNECTIVITY_INTERNAL);
306 }
307
308 private void enforceDumpPermission() {
309 enforcePermission(android.Manifest.permission.DUMP);
310 }
311
312 private void enforcePermission(String what) {
313 getContext().enforceCallingOrSelfPermission(what, "IpConnectivityMetrics");
314 }
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100315
316 private void enforceNetdEventListeningPermission() {
317 final int uid = Binder.getCallingUid();
318 if (uid != Process.SYSTEM_UID) {
319 throw new SecurityException(String.format("Uid %d has no permission to listen for"
320 + " netd events.", uid));
321 }
322 }
323
324 @Override
325 public boolean registerNetdEventCallback(INetdEventCallback callback) {
326 enforceNetdEventListeningPermission();
327 if (mNetdListener == null) {
328 return false;
329 }
330 return mNetdListener.registerNetdEventCallback(callback);
331 }
332
333 @Override
334 public boolean unregisterNetdEventCallback() {
335 enforceNetdEventListeningPermission();
336 if (mNetdListener == null) {
337 // if the service is null, we aren't registered anyway
338 return true;
339 }
340 return mNetdListener.unregisterNetdEventCallback();
341 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900342 };
Hugo Benichi05686db2016-10-19 11:17:28 +0900343
344 private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> {
345 int size = Settings.Global.getInt(ctx.getContentResolver(),
346 Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
347 if (size <= 0) {
348 return DEFAULT_BUFFER_SIZE;
349 }
350 return Math.min(size, MAXIMUM_BUFFER_SIZE);
351 };
Hugo Benichie1c173d2016-10-18 10:36:33 +0900352
353 private static ArrayMap<Class<?>, TokenBucket> makeRateLimitingBuckets() {
354 ArrayMap<Class<?>, TokenBucket> map = new ArrayMap<>();
355 // one token every minute, 50 tokens max: burst of ~50 events every hour.
356 map.put(ApfProgramEvent.class, new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50));
357 return map;
358 }
Hugo Benichi64901e52017-10-19 14:42:40 +0900359
360 /** Direct non-Binder interface for event producer clients within the system servers. */
361 public interface Logger {
362 DefaultNetworkMetrics defaultNetworkMetrics();
363 }
364
365 private class LoggerImpl implements Logger {
366 public DefaultNetworkMetrics defaultNetworkMetrics() {
367 return mDefaultNetworkMetrics;
368 }
369 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900370}