2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.connectivity;
19 import com.android.internal.annotations.VisibleForTesting;
20 import com.android.server.SystemService;
22 import android.app.PendingIntent;
23 import android.content.Context;
24 import android.content.pm.PackageManager;
25 import android.net.ConnectivityMetricsEvent;
26 import android.net.ConnectivityMetricsLogger;
27 import android.net.IConnectivityMetricsLogger;
28 import android.os.Binder;
29 import android.os.Parcel;
30 import android.text.format.DateUtils;
31 import android.util.Log;
33 import java.io.FileDescriptor;
34 import java.io.PrintWriter;
35 import java.util.ArrayDeque;
36 import java.util.ArrayList;
39 public class MetricsLoggerService extends SystemService {
40 private static String TAG = "ConnectivityMetricsLoggerService";
41 private static final boolean DBG = true;
42 private static final boolean VDBG = false;
44 public MetricsLoggerService(Context context) {
49 public void onStart() {
50 resetThrottlingCounters(System.currentTimeMillis());
54 public void onBootPhase(int phase) {
55 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
56 if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
57 publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE,
59 mDnsListener = new DnsEventListenerService(getContext());
60 publishBinderService(mDnsListener.SERVICE_NAME, mDnsListener);
64 // TODO: read these constants from system property
65 private final int EVENTS_NOTIFICATION_THRESHOLD = 300;
66 private final int MAX_NUMBER_OF_EVENTS = 1000;
67 private final int THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT = 1000;
68 private final long THROTTLING_TIME_INTERVAL_MILLIS = DateUtils.HOUR_IN_MILLIS;
70 private int mEventCounter = 0;
73 * Reference of the last event in the list of cached events.
75 * When client of this service retrieves events by calling getEvents, it is passing
76 * ConnectivityMetricsEvent.Reference object. After getEvents returns, that object will
77 * contain this reference. The client can save it and use next time it calls getEvents.
78 * This way only new events will be returned.
80 private long mLastEventReference = 0;
82 private final int mThrottlingCounters[] =
83 new int[ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS];
85 private long mThrottlingIntervalBoundaryMillis;
87 private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>();
89 private DnsEventListenerService mDnsListener;
91 private void enforceConnectivityInternalPermission() {
92 getContext().enforceCallingOrSelfPermission(
93 android.Manifest.permission.CONNECTIVITY_INTERNAL,
94 "MetricsLoggerService");
97 private void enforceDumpPermission() {
98 getContext().enforceCallingOrSelfPermission(
99 android.Manifest.permission.DUMP,
100 "MetricsLoggerService");
103 private void resetThrottlingCounters(long currentTimeMillis) {
104 synchronized (mThrottlingCounters) {
105 for (int i = 0; i < mThrottlingCounters.length; i++) {
106 mThrottlingCounters[i] = 0;
108 mThrottlingIntervalBoundaryMillis =
109 currentTimeMillis + THROTTLING_TIME_INTERVAL_MILLIS;
113 private void addEvent(ConnectivityMetricsEvent e) {
115 Log.v(TAG, "writeEvent(" + e.toString() + ")");
118 while (mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
119 mEvents.removeFirst();
126 final MetricsLoggerImpl mBinder = new MetricsLoggerImpl();
129 * Implementation of the IConnectivityMetricsLogger interface.
131 final class MetricsLoggerImpl extends IConnectivityMetricsLogger.Stub {
133 private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>();
136 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
137 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
138 != PackageManager.PERMISSION_GRANTED) {
139 pw.println("Permission Denial: can't dump ConnectivityMetricsLoggerService " +
140 "from from pid=" + Binder.getCallingPid() + ", uid=" +
141 Binder.getCallingUid());
145 boolean dumpSerializedSize = false;
146 boolean dumpEvents = false;
147 boolean dumpDebugInfo = false;
148 for (String arg : args) {
151 dumpDebugInfo = true;
159 dumpSerializedSize = true;
163 dumpDebugInfo = true;
165 dumpSerializedSize = true;
170 synchronized (mEvents) {
171 pw.println("Number of events: " + mEvents.size());
172 pw.println("Counter: " + mEventCounter);
173 if (mEvents.size() > 0) {
174 pw.println("Time span: " +
175 DateUtils.formatElapsedTime(
176 (System.currentTimeMillis() - mEvents.peekFirst().timestamp)
180 if (dumpSerializedSize) {
181 Parcel p = Parcel.obtain();
182 for (ConnectivityMetricsEvent e : mEvents) {
183 p.writeParcelable(e, 0);
185 pw.println("Serialized data size: " + p.dataSize());
191 pw.println("Events:");
192 for (ConnectivityMetricsEvent e : mEvents) {
193 pw.println(e.toString());
199 synchronized (mThrottlingCounters) {
201 for (int i = 0; i < ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS; i++) {
202 if (mThrottlingCounters[i] > 0) {
203 pw.println("Throttling Counter #" + i + ": " + mThrottlingCounters[i]);
206 pw.println("Throttling Time Remaining: " +
207 DateUtils.formatElapsedTime(
208 (mThrottlingIntervalBoundaryMillis - System.currentTimeMillis())
213 synchronized (mPendingIntents) {
214 if (!mPendingIntents.isEmpty()) {
216 pw.println("Pending intents:");
217 for (PendingIntent pi : mPendingIntents) {
218 pw.println(pi.toString());
224 if (mDnsListener != null) {
225 mDnsListener.dump(pw);
229 public long logEvent(ConnectivityMetricsEvent event) {
230 ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
231 return logEvents(events);
237 * Note: All events must belong to the same component.
239 * @return 0 on success
240 * <0 if error happened
241 * >0 timestamp after which new events will be accepted
243 public long logEvents(ConnectivityMetricsEvent[] events) {
244 enforceConnectivityInternalPermission();
246 if (events == null || events.length == 0) {
247 Log.wtf(TAG, "No events passed to logEvents()");
251 int componentTag = events[0].componentTag;
252 if (componentTag < 0 ||
253 componentTag >= ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS) {
254 Log.wtf(TAG, "Unexpected tag: " + componentTag);
258 synchronized (mThrottlingCounters) {
259 long currentTimeMillis = System.currentTimeMillis();
260 if (currentTimeMillis > mThrottlingIntervalBoundaryMillis) {
261 resetThrottlingCounters(currentTimeMillis);
264 mThrottlingCounters[componentTag] += events.length;
266 if (mThrottlingCounters[componentTag] >
267 THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT) {
268 Log.w(TAG, "Too many events from #" + componentTag +
269 ". Block until " + mThrottlingIntervalBoundaryMillis);
271 return mThrottlingIntervalBoundaryMillis;
275 boolean sendPendingIntents = false;
277 synchronized (mEvents) {
278 for (ConnectivityMetricsEvent e : events) {
279 if (e.componentTag != componentTag) {
280 Log.wtf(TAG, "Unexpected tag: " + e.componentTag);
287 mLastEventReference += events.length;
289 mEventCounter += events.length;
290 if (mEventCounter >= EVENTS_NOTIFICATION_THRESHOLD) {
292 sendPendingIntents = true;
296 if (sendPendingIntents) {
297 synchronized (mPendingIntents) {
298 for (PendingIntent pi : mPendingIntents) {
299 if (VDBG) Log.v(TAG, "Send pending intent");
301 pi.send(getContext(), 0, null, null, null);
302 } catch (PendingIntent.CanceledException e) {
303 Log.e(TAG, "Pending intent canceled: " + pi);
304 mPendingIntents.remove(pi);
316 * @param reference of the last event previously returned. The function will return
317 * events following it.
318 * If 0 then all events will be returned.
319 * After the function call it will contain reference of the
320 * last returned event.
323 public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
324 enforceDumpPermission();
325 long ref = reference.getValue();
326 if (VDBG) Log.v(TAG, "getEvents(" + ref + ")");
328 ConnectivityMetricsEvent[] result;
329 synchronized (mEvents) {
330 if (ref > mLastEventReference) {
331 Log.e(TAG, "Invalid reference");
332 reference.setValue(mLastEventReference);
335 if (ref < mLastEventReference - mEvents.size()) {
336 ref = mLastEventReference - mEvents.size();
339 int numEventsToSkip =
340 mEvents.size() // Total number of events
341 - (int)(mLastEventReference - ref); // Number of events to return
343 result = new ConnectivityMetricsEvent[mEvents.size() - numEventsToSkip];
345 for (ConnectivityMetricsEvent e : mEvents) {
346 if (numEventsToSkip > 0) {
353 reference.setValue(mLastEventReference);
359 public boolean register(PendingIntent newEventsIntent) {
360 enforceDumpPermission();
361 if (VDBG) Log.v(TAG, "register(" + newEventsIntent + ")");
363 synchronized (mPendingIntents) {
364 if (mPendingIntents.remove(newEventsIntent)) {
365 Log.w(TAG, "Replacing registered pending intent");
367 mPendingIntents.add(newEventsIntent);
373 public void unregister(PendingIntent newEventsIntent) {
374 enforceDumpPermission();
375 if (VDBG) Log.v(TAG, "unregister(" + newEventsIntent + ")");
377 synchronized (mPendingIntents) {
378 if (!mPendingIntents.remove(newEventsIntent)) {
379 Log.e(TAG, "Pending intent is not registered");