OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / services / core / java / com / android / server / connectivity / MetricsLoggerService.java
1 /*
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
17 package com.android.server.connectivity;
18
19 import com.android.internal.annotations.VisibleForTesting;
20 import com.android.server.SystemService;
21
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;
32
33 import java.io.FileDescriptor;
34 import java.io.PrintWriter;
35 import java.util.ArrayDeque;
36 import java.util.ArrayList;
37
38 /** {@hide} */
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;
43
44     public MetricsLoggerService(Context context) {
45         super(context);
46     }
47
48     @Override
49     public void onStart() {
50         resetThrottlingCounters(System.currentTimeMillis());
51     }
52
53     @Override
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,
58                     mBinder);
59             mDnsListener = new DnsEventListenerService(getContext());
60             publishBinderService(mDnsListener.SERVICE_NAME, mDnsListener);
61         }
62     }
63
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;
69
70     private int mEventCounter = 0;
71
72     /**
73      * Reference of the last event in the list of cached events.
74      *
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.
79      */
80     private long mLastEventReference = 0;
81
82     private final int mThrottlingCounters[] =
83             new int[ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS];
84
85     private long mThrottlingIntervalBoundaryMillis;
86
87     private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>();
88
89     private DnsEventListenerService mDnsListener;
90
91     private void enforceConnectivityInternalPermission() {
92         getContext().enforceCallingOrSelfPermission(
93                 android.Manifest.permission.CONNECTIVITY_INTERNAL,
94                 "MetricsLoggerService");
95     }
96
97     private void enforceDumpPermission() {
98         getContext().enforceCallingOrSelfPermission(
99                 android.Manifest.permission.DUMP,
100                 "MetricsLoggerService");
101     }
102
103     private void resetThrottlingCounters(long currentTimeMillis) {
104         synchronized (mThrottlingCounters) {
105             for (int i = 0; i < mThrottlingCounters.length; i++) {
106                 mThrottlingCounters[i] = 0;
107             }
108             mThrottlingIntervalBoundaryMillis =
109                     currentTimeMillis + THROTTLING_TIME_INTERVAL_MILLIS;
110         }
111     }
112
113     private void addEvent(ConnectivityMetricsEvent e) {
114         if (VDBG) {
115             Log.v(TAG, "writeEvent(" + e.toString() + ")");
116         }
117
118         while (mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
119             mEvents.removeFirst();
120         }
121
122         mEvents.addLast(e);
123     }
124
125     @VisibleForTesting
126     final MetricsLoggerImpl mBinder = new MetricsLoggerImpl();
127
128     /**
129      * Implementation of the IConnectivityMetricsLogger interface.
130      */
131     final class MetricsLoggerImpl extends IConnectivityMetricsLogger.Stub {
132
133         private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>();
134
135         @Override
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());
142                 return;
143             }
144
145             boolean dumpSerializedSize = false;
146             boolean dumpEvents = false;
147             boolean dumpDebugInfo = false;
148             for (String arg : args) {
149                 switch (arg) {
150                     case "--debug":
151                         dumpDebugInfo = true;
152                         break;
153
154                     case "--events":
155                         dumpEvents = true;
156                         break;
157
158                     case "--size":
159                         dumpSerializedSize = true;
160                         break;
161
162                     case "--all":
163                         dumpDebugInfo = true;
164                         dumpEvents = true;
165                         dumpSerializedSize = true;
166                         break;
167                 }
168             }
169
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)
177                                             / 1000));
178                 }
179
180                 if (dumpSerializedSize) {
181                     Parcel p = Parcel.obtain();
182                     for (ConnectivityMetricsEvent e : mEvents) {
183                         p.writeParcelable(e, 0);
184                     }
185                     pw.println("Serialized data size: " + p.dataSize());
186                     p.recycle();
187                 }
188
189                 if (dumpEvents) {
190                     pw.println();
191                     pw.println("Events:");
192                     for (ConnectivityMetricsEvent e : mEvents) {
193                         pw.println(e.toString());
194                     }
195                 }
196             }
197
198             if (dumpDebugInfo) {
199                 synchronized (mThrottlingCounters) {
200                     pw.println();
201                     for (int i = 0; i < ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS; i++) {
202                         if (mThrottlingCounters[i] > 0) {
203                             pw.println("Throttling Counter #" + i + ": " + mThrottlingCounters[i]);
204                         }
205                     }
206                     pw.println("Throttling Time Remaining: " +
207                             DateUtils.formatElapsedTime(
208                                     (mThrottlingIntervalBoundaryMillis - System.currentTimeMillis())
209                                             / 1000));
210                 }
211             }
212
213             synchronized (mPendingIntents) {
214                 if (!mPendingIntents.isEmpty()) {
215                     pw.println();
216                     pw.println("Pending intents:");
217                     for (PendingIntent pi : mPendingIntents) {
218                         pw.println(pi.toString());
219                     }
220                 }
221             }
222
223             pw.println();
224             if (mDnsListener != null) {
225                 mDnsListener.dump(pw);
226             }
227         }
228
229         public long logEvent(ConnectivityMetricsEvent event) {
230             ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
231             return logEvents(events);
232         }
233
234         /**
235          * @param events
236          *
237          * Note: All events must belong to the same component.
238          *
239          * @return 0 on success
240          *        <0 if error happened
241          *        >0 timestamp after which new events will be accepted
242          */
243         public long logEvents(ConnectivityMetricsEvent[] events) {
244             enforceConnectivityInternalPermission();
245
246             if (events == null || events.length == 0) {
247                 Log.wtf(TAG, "No events passed to logEvents()");
248                 return -1;
249             }
250
251             int componentTag = events[0].componentTag;
252             if (componentTag < 0 ||
253                     componentTag >= ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS) {
254                 Log.wtf(TAG, "Unexpected tag: " + componentTag);
255                 return -1;
256             }
257
258             synchronized (mThrottlingCounters) {
259                 long currentTimeMillis = System.currentTimeMillis();
260                 if (currentTimeMillis > mThrottlingIntervalBoundaryMillis) {
261                     resetThrottlingCounters(currentTimeMillis);
262                 }
263
264                 mThrottlingCounters[componentTag] += events.length;
265
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);
270
271                     return mThrottlingIntervalBoundaryMillis;
272                 }
273             }
274
275             boolean sendPendingIntents = false;
276
277             synchronized (mEvents) {
278                 for (ConnectivityMetricsEvent e : events) {
279                     if (e.componentTag != componentTag) {
280                         Log.wtf(TAG, "Unexpected tag: " + e.componentTag);
281                         return -1;
282                     }
283
284                     addEvent(e);
285                 }
286
287                 mLastEventReference += events.length;
288
289                 mEventCounter += events.length;
290                 if (mEventCounter >= EVENTS_NOTIFICATION_THRESHOLD) {
291                     mEventCounter = 0;
292                     sendPendingIntents = true;
293                 }
294             }
295
296             if (sendPendingIntents) {
297                 synchronized (mPendingIntents) {
298                     for (PendingIntent pi : mPendingIntents) {
299                         if (VDBG) Log.v(TAG, "Send pending intent");
300                         try {
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);
305                         }
306                     }
307                 }
308             }
309
310             return 0;
311         }
312
313         /**
314          * Retrieve events
315          *
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.
321          * @return events
322          */
323         public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
324             enforceDumpPermission();
325             long ref = reference.getValue();
326             if (VDBG) Log.v(TAG, "getEvents(" + ref + ")");
327
328             ConnectivityMetricsEvent[] result;
329             synchronized (mEvents) {
330                 if (ref > mLastEventReference) {
331                     Log.e(TAG, "Invalid reference");
332                     reference.setValue(mLastEventReference);
333                     return null;
334                 }
335                 if (ref < mLastEventReference - mEvents.size()) {
336                     ref = mLastEventReference - mEvents.size();
337                 }
338
339                 int numEventsToSkip =
340                         mEvents.size() // Total number of events
341                         - (int)(mLastEventReference - ref); // Number of events to return
342
343                 result = new ConnectivityMetricsEvent[mEvents.size() - numEventsToSkip];
344                 int i = 0;
345                 for (ConnectivityMetricsEvent e : mEvents) {
346                     if (numEventsToSkip > 0) {
347                         numEventsToSkip--;
348                     } else {
349                         result[i++] = e;
350                     }
351                 }
352
353                 reference.setValue(mLastEventReference);
354             }
355
356             return result;
357         }
358
359         public boolean register(PendingIntent newEventsIntent) {
360             enforceDumpPermission();
361             if (VDBG) Log.v(TAG, "register(" + newEventsIntent + ")");
362
363             synchronized (mPendingIntents) {
364                 if (mPendingIntents.remove(newEventsIntent)) {
365                     Log.w(TAG, "Replacing registered pending intent");
366                 }
367                 mPendingIntents.add(newEventsIntent);
368             }
369
370             return true;
371         }
372
373         public void unregister(PendingIntent newEventsIntent) {
374             enforceDumpPermission();
375             if (VDBG) Log.v(TAG, "unregister(" + newEventsIntent + ")");
376
377             synchronized (mPendingIntents) {
378                 if (!mPendingIntents.remove(newEventsIntent)) {
379                     Log.e(TAG, "Pending intent is not registered");
380                 }
381             }
382         }
383     };
384 }