MetricsLoggerService.java revision 9b907e440cdff222481abf16def6e03870678c20
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 17package com.android.server.connectivity; 18 19import com.android.server.SystemService; 20 21import android.app.PendingIntent; 22import android.content.Context; 23import android.content.pm.PackageManager; 24import android.net.ConnectivityMetricsEvent; 25import android.net.ConnectivityMetricsLogger; 26import android.net.IConnectivityMetricsLogger; 27import android.os.Binder; 28import android.os.Parcel; 29import android.text.format.DateUtils; 30import android.util.Log; 31 32import java.io.FileDescriptor; 33import java.io.PrintWriter; 34import java.util.ArrayDeque; 35import java.util.ArrayList; 36 37/** {@hide} */ 38public class MetricsLoggerService extends SystemService { 39 private static String TAG = "ConnectivityMetricsLoggerService"; 40 private static final boolean DBG = true; 41 private static final boolean VDBG = false; 42 43 public MetricsLoggerService(Context context) { 44 super(context); 45 } 46 47 @Override 48 public void onStart() { 49 resetThrottlingCounters(System.currentTimeMillis()); 50 } 51 52 @Override 53 public void onBootPhase(int phase) { 54 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 55 if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY"); 56 publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE, 57 mBinder); 58 mDnsListener = new DnsEventListenerService(getContext()); 59 publishBinderService(mDnsListener.SERVICE_NAME, mDnsListener); 60 } 61 } 62 63 // TODO: read from system property 64 private final int MAX_NUMBER_OF_EVENTS = 1000; 65 66 // TODO: read from system property 67 private final int EVENTS_NOTIFICATION_THRESHOLD = 300; 68 69 // TODO: read from system property 70 private final int THROTTLING_TIME_INTERVAL_MILLIS = 60 * 60 * 1000; // 1 hour 71 72 // TODO: read from system property 73 private final int THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT = 1000; 74 75 private int mEventCounter = 0; 76 77 /** 78 * Reference of the last event in the list of cached events. 79 * 80 * When client of this service retrieves events by calling getEvents, it is passing 81 * ConnectivityMetricsEvent.Reference object. After getEvents returns, that object will 82 * contain this reference. The client can save it and use next time it calls getEvents. 83 * This way only new events will be returned. 84 */ 85 private long mLastEventReference = 0; 86 87 private final int mThrottlingCounters[] = 88 new int[ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS]; 89 90 private long mThrottlingIntervalBoundaryMillis; 91 92 private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>(); 93 94 private DnsEventListenerService mDnsListener; 95 96 private void enforceConnectivityInternalPermission() { 97 getContext().enforceCallingOrSelfPermission( 98 android.Manifest.permission.CONNECTIVITY_INTERNAL, 99 "MetricsLoggerService"); 100 } 101 102 private void enforceDumpPermission() { 103 getContext().enforceCallingOrSelfPermission( 104 android.Manifest.permission.DUMP, 105 "MetricsLoggerService"); 106 } 107 108 private void resetThrottlingCounters(long currentTimeMillis) { 109 for (int i = 0; i < mThrottlingCounters.length; i++) { 110 mThrottlingCounters[i] = 0; 111 } 112 mThrottlingIntervalBoundaryMillis = 113 currentTimeMillis + THROTTLING_TIME_INTERVAL_MILLIS; 114 } 115 116 private void addEvent(ConnectivityMetricsEvent e) { 117 if (VDBG) { 118 Log.v(TAG, "writeEvent(" + e.toString() + ")"); 119 } 120 121 while (mEvents.size() >= MAX_NUMBER_OF_EVENTS) { 122 mEvents.removeFirst(); 123 } 124 125 mEvents.addLast(e); 126 } 127 128 /** 129 * Implementation of the IConnectivityMetricsLogger interface. 130 */ 131 private final IConnectivityMetricsLogger.Stub mBinder = new 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 for (String arg : args) { 148 switch (arg) { 149 case "--events": 150 dumpEvents = true; 151 break; 152 153 case "--size": 154 dumpSerializedSize = true; 155 break; 156 157 case "--all": 158 dumpEvents = true; 159 dumpSerializedSize = true; 160 break; 161 } 162 } 163 164 synchronized (mEvents) { 165 pw.println("Number of events: " + mEvents.size()); 166 if (mEvents.size() > 0) { 167 pw.println("Time span: " + 168 DateUtils.formatElapsedTime( 169 (System.currentTimeMillis() - mEvents.peekFirst().timestamp) 170 / 1000)); 171 } 172 173 if (dumpSerializedSize) { 174 long dataSize = 0; 175 Parcel p = Parcel.obtain(); 176 for (ConnectivityMetricsEvent e : mEvents) { 177 dataSize += 16; // timestamp and 2 stamps 178 179 p.writeParcelable(e.data, 0); 180 } 181 dataSize += p.dataSize(); 182 p.recycle(); 183 pw.println("Serialized data size: " + dataSize); 184 } 185 186 if (dumpEvents) { 187 pw.println(); 188 pw.println("Events:"); 189 for (ConnectivityMetricsEvent e : mEvents) { 190 pw.println(e.toString()); 191 } 192 } 193 } 194 195 synchronized (mPendingIntents) { 196 if (!mPendingIntents.isEmpty()) { 197 pw.println(); 198 pw.println("Pending intents:"); 199 for (PendingIntent pi : mPendingIntents) { 200 pw.println(pi.toString()); 201 } 202 } 203 } 204 205 pw.println(); 206 mDnsListener.dump(pw); 207 } 208 209 public long logEvent(ConnectivityMetricsEvent event) { 210 ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event}; 211 return logEvents(events); 212 } 213 214 /** 215 * @param events 216 * 217 * Note: All events must belong to the same component. 218 * 219 * @return 0 on success 220 * <0 if error happened 221 * >0 timestamp after which new events will be accepted 222 */ 223 public long logEvents(ConnectivityMetricsEvent[] events) { 224 enforceConnectivityInternalPermission(); 225 226 if (events == null || events.length == 0) { 227 Log.wtf(TAG, "No events passed to logEvents()"); 228 return -1; 229 } 230 231 int componentTag = events[0].componentTag; 232 if (componentTag < 0 || 233 componentTag >= ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS) { 234 Log.wtf(TAG, "Unexpected tag: " + componentTag); 235 return -1; 236 } 237 238 synchronized (mThrottlingCounters) { 239 long currentTimeMillis = System.currentTimeMillis(); 240 if (currentTimeMillis > mThrottlingIntervalBoundaryMillis) { 241 resetThrottlingCounters(currentTimeMillis); 242 } 243 244 mThrottlingCounters[componentTag] += events.length; 245 246 if (mThrottlingCounters[componentTag] > 247 THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT) { 248 Log.w(TAG, "Too many events from #" + componentTag + 249 ". Block until " + mThrottlingIntervalBoundaryMillis); 250 251 return mThrottlingIntervalBoundaryMillis; 252 } 253 } 254 255 boolean sendPendingIntents = false; 256 257 synchronized (mEvents) { 258 for (ConnectivityMetricsEvent e : events) { 259 if (e.componentTag != componentTag) { 260 Log.wtf(TAG, "Unexpected tag: " + e.componentTag); 261 return -1; 262 } 263 264 addEvent(e); 265 } 266 267 mLastEventReference += events.length; 268 269 mEventCounter += events.length; 270 if (mEventCounter >= EVENTS_NOTIFICATION_THRESHOLD) { 271 mEventCounter = 0; 272 sendPendingIntents = true; 273 } 274 } 275 276 if (sendPendingIntents) { 277 synchronized (mPendingIntents) { 278 for (PendingIntent pi : mPendingIntents) { 279 if (VDBG) Log.v(TAG, "Send pending intent"); 280 try { 281 pi.send(getContext(), 0, null, null, null); 282 } catch (PendingIntent.CanceledException e) { 283 Log.e(TAG, "Pending intent canceled: " + pi); 284 mPendingIntents.remove(pi); 285 } 286 } 287 } 288 } 289 290 return 0; 291 } 292 293 /** 294 * Retrieve events 295 * 296 * @param reference of the last event previously returned. The function will return 297 * events following it. 298 * If 0 then all events will be returned. 299 * After the function call it will contain reference of the 300 * last returned event. 301 * @return events 302 */ 303 public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) { 304 enforceDumpPermission(); 305 long ref = reference.getValue(); 306 if (VDBG) Log.v(TAG, "getEvents(" + ref + ")"); 307 308 ConnectivityMetricsEvent[] result; 309 synchronized (mEvents) { 310 if (ref > mLastEventReference) { 311 Log.e(TAG, "Invalid reference"); 312 reference.setValue(mLastEventReference); 313 return null; 314 } 315 if (ref < mLastEventReference - mEvents.size()) { 316 ref = mLastEventReference - mEvents.size(); 317 } 318 319 int numEventsToSkip = 320 mEvents.size() // Total number of events 321 - (int)(mLastEventReference - ref); // Number of events to return 322 323 result = new ConnectivityMetricsEvent[mEvents.size() - numEventsToSkip]; 324 int i = 0; 325 for (ConnectivityMetricsEvent e : mEvents) { 326 if (numEventsToSkip > 0) { 327 numEventsToSkip--; 328 } else { 329 result[i++] = e; 330 } 331 } 332 333 reference.setValue(mLastEventReference); 334 } 335 336 return result; 337 } 338 339 public boolean register(PendingIntent newEventsIntent) { 340 enforceDumpPermission(); 341 if (VDBG) Log.v(TAG, "register(" + newEventsIntent + ")"); 342 343 synchronized (mPendingIntents) { 344 if (mPendingIntents.remove(newEventsIntent)) { 345 Log.w(TAG, "Replacing registered pending intent"); 346 } 347 mPendingIntents.add(newEventsIntent); 348 } 349 350 return true; 351 } 352 353 public void unregister(PendingIntent newEventsIntent) { 354 enforceDumpPermission(); 355 if (VDBG) Log.v(TAG, "unregister(" + newEventsIntent + ")"); 356 357 synchronized (mPendingIntents) { 358 if (!mPendingIntents.remove(newEventsIntent)) { 359 Log.e(TAG, "Pending intent is not registered"); 360 } 361 } 362 } 363 }; 364} 365