blob: 79b56c6027f8b66c4f556adda5079f9ab702d20f [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;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010026import android.os.Process;
Hugo Benichi05686db2016-10-19 11:17:28 +090027import android.provider.Settings;
Hugo Benichieab511b2016-09-09 09:23:47 +090028import android.text.TextUtils;
Hugo Benichie1c173d2016-10-18 10:36:33 +090029import android.text.format.DateUtils;
30import android.util.ArrayMap;
Hugo Benichieab511b2016-09-09 09:23:47 +090031import android.util.Base64;
32import android.util.Log;
Hugo Benichi64901e52017-10-19 14:42:40 +090033
Hugo Benichieab511b2016-09-09 09:23:47 +090034import com.android.internal.annotations.GuardedBy;
35import com.android.internal.annotations.VisibleForTesting;
Hugo Benichi1198ba12017-09-15 14:18:57 +090036import com.android.internal.util.RingBuffer;
Hugo Benichie1c173d2016-10-18 10:36:33 +090037import com.android.internal.util.TokenBucket;
Hugo Benichi64901e52017-10-19 14:42:40 +090038import com.android.server.LocalServices;
Hugo Benichieab511b2016-09-09 09:23:47 +090039import com.android.server.SystemService;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090040import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
Hugo Benichi64901e52017-10-19 14:42:40 +090041
Hugo Benichieab511b2016-09-09 09:23:47 +090042import java.io.FileDescriptor;
43import java.io.IOException;
44import java.io.PrintWriter;
45import java.util.ArrayList;
Hugo Benichi380a0632017-10-20 09:25:29 +090046import java.util.Arrays;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090047import java.util.List;
Hugo Benichi05686db2016-10-19 11:17:28 +090048import java.util.function.ToIntFunction;
Hugo Benichieab511b2016-09-09 09:23:47 +090049
Hugo Benichi67c5e032017-09-14 16:31:38 +090050/**
51 * Event buffering service for core networking and connectivity metrics.
52 *
53 * {@hide}
54 */
Hugo Benichieab511b2016-09-09 09:23:47 +090055final public class IpConnectivityMetrics extends SystemService {
56 private static final String TAG = IpConnectivityMetrics.class.getSimpleName();
57 private static final boolean DBG = false;
58
Hugo Benichid680d4c2016-10-13 13:16:16 +090059 // The logical version numbers of ipconnectivity.proto, corresponding to the
60 // "version" field of IpConnectivityLog.
61 private static final int NYC = 0;
62 private static final int NYC_MR1 = 1;
63 private static final int NYC_MR2 = 2;
64 public static final int VERSION = NYC_MR2;
65
Hugo Benichieab511b2016-09-09 09:23:47 +090066 private static final String SERVICE_NAME = IpConnectivityLog.SERVICE_NAME;
67
Hugo Benichi1198ba12017-09-15 14:18:57 +090068 // Default size of the event rolling log for bug report dumps.
69 private static final int DEFAULT_LOG_SIZE = 500;
70 // Default size of the event buffer for metrics reporting.
71 // Once the buffer is full, incoming events are dropped.
Hugo Benichieab511b2016-09-09 09:23:47 +090072 private static final int DEFAULT_BUFFER_SIZE = 2000;
Hugo Benichi05686db2016-10-19 11:17:28 +090073 // Maximum size of the event buffer.
74 private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
Hugo Benichieab511b2016-09-09 09:23:47 +090075
Hugo Benichi0d4a3982016-11-25 11:24:22 +090076 private static final int MAXIMUM_CONNECT_LATENCY_RECORDS = 20000;
77
Hugo Benichie1c173d2016-10-18 10:36:33 +090078 private static final int ERROR_RATE_LIMITED = -1;
Hugo Benichieab511b2016-09-09 09:23:47 +090079
Hugo Benichi1198ba12017-09-15 14:18:57 +090080 // Lock ensuring that concurrent manipulations of the event buffers are correct.
Hugo Benichieab511b2016-09-09 09:23:47 +090081 // There are three concurrent operations to synchronize:
82 // - appending events to the buffer.
83 // - iterating throught the buffer.
84 // - flushing the buffer content and replacing it by a new buffer.
85 private final Object mLock = new Object();
86
Hugo Benichi1198ba12017-09-15 14:18:57 +090087 // Implementation instance of IIpConnectivityMetrics.aidl.
Hugo Benichieab511b2016-09-09 09:23:47 +090088 @VisibleForTesting
89 public final Impl impl = new Impl();
Hugo Benichi1198ba12017-09-15 14:18:57 +090090 // Subservice listening to Netd events via INetdEventListener.aidl.
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090091 @VisibleForTesting
92 NetdEventListenerService mNetdListener;
Hugo Benichi00a42d42016-09-13 15:55:09 +090093
Hugo Benichi1198ba12017-09-15 14:18:57 +090094 // Rolling log of the most recent events. This log is used for dumping
95 // connectivity events in bug reports.
96 @GuardedBy("mLock")
97 private final RingBuffer<ConnectivityMetricsEvent> mEventLog =
98 new RingBuffer(ConnectivityMetricsEvent.class, DEFAULT_LOG_SIZE);
99 // Buffer of connectivity events used for metrics reporting. This buffer
100 // does not rotate automatically and instead saturates when it becomes full.
101 // It is flushed at metrics reporting.
Hugo Benichieab511b2016-09-09 09:23:47 +0900102 @GuardedBy("mLock")
103 private ArrayList<ConnectivityMetricsEvent> mBuffer;
Hugo Benichi1198ba12017-09-15 14:18:57 +0900104 // Total number of events dropped from mBuffer since last metrics reporting.
Hugo Benichieab511b2016-09-09 09:23:47 +0900105 @GuardedBy("mLock")
106 private int mDropped;
Hugo Benichi1198ba12017-09-15 14:18:57 +0900107 // Capacity of mBuffer
Hugo Benichieab511b2016-09-09 09:23:47 +0900108 @GuardedBy("mLock")
109 private int mCapacity;
Hugo Benichi1198ba12017-09-15 14:18:57 +0900110 // A list of rate limiting counters keyed by connectivity event types for
111 // metrics reporting mBuffer.
Hugo Benichie1c173d2016-10-18 10:36:33 +0900112 @GuardedBy("mLock")
113 private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets();
Hugo Benichieab511b2016-09-09 09:23:47 +0900114
Hugo Benichi05686db2016-10-19 11:17:28 +0900115 private final ToIntFunction<Context> mCapacityGetter;
116
Hugo Benichi64901e52017-10-19 14:42:40 +0900117 @VisibleForTesting
118 final DefaultNetworkMetrics mDefaultNetworkMetrics = new DefaultNetworkMetrics();
119
Hugo Benichi05686db2016-10-19 11:17:28 +0900120 public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) {
Hugo Benichieab511b2016-09-09 09:23:47 +0900121 super(ctx);
Hugo Benichi05686db2016-10-19 11:17:28 +0900122 mCapacityGetter = capacityGetter;
Hugo Benichieab511b2016-09-09 09:23:47 +0900123 initBuffer();
124 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900125
126 public IpConnectivityMetrics(Context ctx) {
Hugo Benichi05686db2016-10-19 11:17:28 +0900127 this(ctx, READ_BUFFER_SIZE);
Hugo Benichieab511b2016-09-09 09:23:47 +0900128 }
129
130 @Override
131 public void onStart() {
132 if (DBG) Log.d(TAG, "onStart");
133 }
134
135 @Override
136 public void onBootPhase(int phase) {
137 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
138 if (DBG) Log.d(TAG, "onBootPhase");
Hugo Benichie69608f2016-09-23 14:48:01 +0900139 mNetdListener = new NetdEventListenerService(getContext());
Hugo Benichieab511b2016-09-09 09:23:47 +0900140
141 publishBinderService(SERVICE_NAME, impl);
Hugo Benichie69608f2016-09-23 14:48:01 +0900142 publishBinderService(mNetdListener.SERVICE_NAME, mNetdListener);
Hugo Benichi64901e52017-10-19 14:42:40 +0900143
144 LocalServices.addService(Logger.class, new LoggerImpl());
Hugo Benichieab511b2016-09-09 09:23:47 +0900145 }
146 }
147
148 @VisibleForTesting
149 public int bufferCapacity() {
Hugo Benichi05686db2016-10-19 11:17:28 +0900150 return mCapacityGetter.applyAsInt(getContext());
Hugo Benichieab511b2016-09-09 09:23:47 +0900151 }
152
153 private void initBuffer() {
154 synchronized (mLock) {
155 mDropped = 0;
156 mCapacity = bufferCapacity();
157 mBuffer = new ArrayList<>(mCapacity);
158 }
159 }
160
161 private int append(ConnectivityMetricsEvent event) {
162 if (DBG) Log.d(TAG, "logEvent: " + event);
163 synchronized (mLock) {
Hugo Benichi1198ba12017-09-15 14:18:57 +0900164 mEventLog.append(event);
Hugo Benichieab511b2016-09-09 09:23:47 +0900165 final int left = mCapacity - mBuffer.size();
166 if (event == null) {
167 return left;
168 }
Hugo Benichie1c173d2016-10-18 10:36:33 +0900169 if (isRateLimited(event)) {
170 // Do not count as a dropped event. TODO: consider adding separate counter
171 return ERROR_RATE_LIMITED;
172 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900173 if (left == 0) {
174 mDropped++;
175 return 0;
176 }
177 mBuffer.add(event);
178 return left - 1;
179 }
180 }
181
Hugo Benichie1c173d2016-10-18 10:36:33 +0900182 private boolean isRateLimited(ConnectivityMetricsEvent event) {
183 TokenBucket tb = mBuckets.get(event.data.getClass());
184 return (tb != null) && !tb.get();
185 }
186
Hugo Benichieab511b2016-09-09 09:23:47 +0900187 private String flushEncodedOutput() {
188 final ArrayList<ConnectivityMetricsEvent> events;
189 final int dropped;
190 synchronized (mLock) {
191 events = mBuffer;
192 dropped = mDropped;
193 initBuffer();
194 }
195
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900196 final List<IpConnectivityEvent> protoEvents = IpConnectivityEventBuilder.toProto(events);
197
Hugo Benichi1193a9c2017-10-19 14:58:15 +0900198 mDefaultNetworkMetrics.flushEvents(protoEvents);
199
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900200 if (mNetdListener != null) {
201 mNetdListener.flushStatistics(protoEvents);
202 }
203
Hugo Benichieab511b2016-09-09 09:23:47 +0900204 final byte[] data;
205 try {
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900206 data = IpConnectivityEventBuilder.serialize(dropped, protoEvents);
Hugo Benichieab511b2016-09-09 09:23:47 +0900207 } catch (IOException e) {
208 Log.e(TAG, "could not serialize events", e);
209 return "";
210 }
211
212 return Base64.encodeToString(data, Base64.DEFAULT);
213 }
214
215 /**
Hugo Benichi380a0632017-10-20 09:25:29 +0900216 * Clear the event buffer and prints its content as a protobuf serialized byte array
Hugo Benichieab511b2016-09-09 09:23:47 +0900217 * inside a base64 encoded string.
218 */
Hugo Benichi380a0632017-10-20 09:25:29 +0900219 private void cmdFlush(PrintWriter pw) {
Hugo Benichieab511b2016-09-09 09:23:47 +0900220 pw.print(flushEncodedOutput());
221 }
222
223 /**
Hugo Benichi380a0632017-10-20 09:25:29 +0900224 * Print the content of the rolling event buffer in human readable format.
225 * Also print network dns/connect statistics and recent default network events.
Hugo Benichieab511b2016-09-09 09:23:47 +0900226 */
Hugo Benichi380a0632017-10-20 09:25:29 +0900227 private void cmdList(PrintWriter pw) {
228 pw.println("metrics events:");
229 final List<ConnectivityMetricsEvent> events = getEvents();
Hugo Benichieab511b2016-09-09 09:23:47 +0900230 for (ConnectivityMetricsEvent ev : events) {
231 pw.println(ev.toString());
232 }
Hugo Benichi380a0632017-10-20 09:25:29 +0900233 pw.println("");
Hugo Benichia2decca2017-02-22 14:32:27 +0900234 if (mNetdListener != null) {
235 mNetdListener.list(pw);
236 }
Hugo Benichi380a0632017-10-20 09:25:29 +0900237 pw.println("");
Hugo Benichi1193a9c2017-10-19 14:58:15 +0900238 mDefaultNetworkMetrics.listEvents(pw);
Hugo Benichieab511b2016-09-09 09:23:47 +0900239 }
240
Hugo Benichi380a0632017-10-20 09:25:29 +0900241 /*
242 * Print the content of the rolling event buffer in text proto format.
Hugo Benichi1198ba12017-09-15 14:18:57 +0900243 */
Hugo Benichi380a0632017-10-20 09:25:29 +0900244 private void cmdListAsProto(PrintWriter pw) {
245 final List<ConnectivityMetricsEvent> events = getEvents();
246 for (IpConnectivityEvent ev : IpConnectivityEventBuilder.toProto(events)) {
247 pw.print(ev.toString());
Hugo Benichi1198ba12017-09-15 14:18:57 +0900248 }
249 if (mNetdListener != null) {
Hugo Benichi380a0632017-10-20 09:25:29 +0900250 mNetdListener.listAsProtos(pw);
Hugo Benichi1198ba12017-09-15 14:18:57 +0900251 }
Hugo Benichi380a0632017-10-20 09:25:29 +0900252 mDefaultNetworkMetrics.listEventsAsProto(pw);
Hugo Benichi1198ba12017-09-15 14:18:57 +0900253 }
254
Hugo Benichi380a0632017-10-20 09:25:29 +0900255 /*
256 * Return a copy of metrics events stored in buffer for metrics uploading.
257 */
258 private List<ConnectivityMetricsEvent> getEvents() {
Hugo Benichieab511b2016-09-09 09:23:47 +0900259 synchronized (mLock) {
Hugo Benichi380a0632017-10-20 09:25:29 +0900260 return Arrays.asList(mEventLog.toArray());
Hugo Benichieab511b2016-09-09 09:23:47 +0900261 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900262 }
263
264 public final class Impl extends IIpConnectivityMetrics.Stub {
Hugo Benichi380a0632017-10-20 09:25:29 +0900265 // Dump and flushes the metrics event buffer in base64 encoded serialized proto output.
266 static final String CMD_FLUSH = "flush";
267 // Dump the rolling buffer of metrics event in human readable proto text format.
268 static final String CMD_PROTO = "proto";
269 // Dump the rolling buffer of metrics event and pretty print events using a human readable
270 // format. Also print network dns/connect statistics and default network event time series.
271 static final String CMD_LIST = "list";
Erik Klinec172c7d2018-05-01 16:51:44 +0900272 // By default any other argument will fall into the default case which is the equivalent
273 // of calling both the "list" and "ipclient" commands. This includes most notably bug
274 // reports collected by dumpsys.cpp with the "-a" argument.
275 static final String CMD_DEFAULT = "";
Hugo Benichieab511b2016-09-09 09:23:47 +0900276
277 @Override
278 public int logEvent(ConnectivityMetricsEvent event) {
279 enforceConnectivityInternalPermission();
280 return append(event);
281 }
282
283 @Override
284 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
285 enforceDumpPermission();
286 if (DBG) Log.d(TAG, "dumpsys " + TextUtils.join(" ", args));
287 final String cmd = (args.length > 0) ? args[0] : CMD_DEFAULT;
288 switch (cmd) {
289 case CMD_FLUSH:
Hugo Benichi380a0632017-10-20 09:25:29 +0900290 cmdFlush(pw);
Hugo Benichieab511b2016-09-09 09:23:47 +0900291 return;
Hugo Benichi380a0632017-10-20 09:25:29 +0900292 case CMD_PROTO:
293 cmdListAsProto(pw);
Hugo Benichi1198ba12017-09-15 14:18:57 +0900294 return;
Erik Klinec172c7d2018-05-01 16:51:44 +0900295 case CMD_LIST:
Hugo Benichieab511b2016-09-09 09:23:47 +0900296 default:
Hugo Benichi380a0632017-10-20 09:25:29 +0900297 cmdList(pw);
298 return;
Hugo Benichieab511b2016-09-09 09:23:47 +0900299 }
300 }
301
302 private void enforceConnectivityInternalPermission() {
303 enforcePermission(android.Manifest.permission.CONNECTIVITY_INTERNAL);
304 }
305
306 private void enforceDumpPermission() {
307 enforcePermission(android.Manifest.permission.DUMP);
308 }
309
310 private void enforcePermission(String what) {
311 getContext().enforceCallingOrSelfPermission(what, "IpConnectivityMetrics");
312 }
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100313
314 private void enforceNetdEventListeningPermission() {
315 final int uid = Binder.getCallingUid();
316 if (uid != Process.SYSTEM_UID) {
317 throw new SecurityException(String.format("Uid %d has no permission to listen for"
318 + " netd events.", uid));
319 }
320 }
321
322 @Override
Ricky Wai77518fb2017-10-27 14:46:01 +0100323 public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100324 enforceNetdEventListeningPermission();
325 if (mNetdListener == null) {
326 return false;
327 }
Ricky Wai77518fb2017-10-27 14:46:01 +0100328 return mNetdListener.addNetdEventCallback(callerType, callback);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100329 }
330
331 @Override
Ricky Wai77518fb2017-10-27 14:46:01 +0100332 public boolean removeNetdEventCallback(int callerType) {
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100333 enforceNetdEventListeningPermission();
334 if (mNetdListener == null) {
335 // if the service is null, we aren't registered anyway
336 return true;
337 }
Ricky Wai77518fb2017-10-27 14:46:01 +0100338 return mNetdListener.removeNetdEventCallback(callerType);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100339 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900340 };
Hugo Benichi05686db2016-10-19 11:17:28 +0900341
342 private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> {
343 int size = Settings.Global.getInt(ctx.getContentResolver(),
344 Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
345 if (size <= 0) {
346 return DEFAULT_BUFFER_SIZE;
347 }
348 return Math.min(size, MAXIMUM_BUFFER_SIZE);
349 };
Hugo Benichie1c173d2016-10-18 10:36:33 +0900350
351 private static ArrayMap<Class<?>, TokenBucket> makeRateLimitingBuckets() {
352 ArrayMap<Class<?>, TokenBucket> map = new ArrayMap<>();
353 // one token every minute, 50 tokens max: burst of ~50 events every hour.
354 map.put(ApfProgramEvent.class, new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50));
355 return map;
356 }
Hugo Benichi64901e52017-10-19 14:42:40 +0900357
358 /** Direct non-Binder interface for event producer clients within the system servers. */
359 public interface Logger {
360 DefaultNetworkMetrics defaultNetworkMetrics();
361 }
362
363 private class LoggerImpl implements Logger {
364 public DefaultNetworkMetrics defaultNetworkMetrics() {
365 return mDefaultNetworkMetrics;
366 }
367 }
Hugo Benichieab511b2016-09-09 09:23:47 +0900368}