MetricsLoggerService.java revision 4b4c31de80aa3c518669db4b2cad43b5677a9bdb
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 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 mDnsListener.dump(pw); 225 } 226 227 public long logEvent(ConnectivityMetricsEvent event) { 228 ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event}; 229 return logEvents(events); 230 } 231 232 /** 233 * @param events 234 * 235 * Note: All events must belong to the same component. 236 * 237 * @return 0 on success 238 * <0 if error happened 239 * >0 timestamp after which new events will be accepted 240 */ 241 public long logEvents(ConnectivityMetricsEvent[] events) { 242 enforceConnectivityInternalPermission(); 243 244 if (events == null || events.length == 0) { 245 Log.wtf(TAG, "No events passed to logEvents()"); 246 return -1; 247 } 248 249 int componentTag = events[0].componentTag; 250 if (componentTag < 0 || 251 componentTag >= ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS) { 252 Log.wtf(TAG, "Unexpected tag: " + componentTag); 253 return -1; 254 } 255 256 synchronized (mThrottlingCounters) { 257 long currentTimeMillis = System.currentTimeMillis(); 258 if (currentTimeMillis > mThrottlingIntervalBoundaryMillis) { 259 resetThrottlingCounters(currentTimeMillis); 260 } 261 262 mThrottlingCounters[componentTag] += events.length; 263 264 if (mThrottlingCounters[componentTag] > 265 THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT) { 266 Log.w(TAG, "Too many events from #" + componentTag + 267 ". Block until " + mThrottlingIntervalBoundaryMillis); 268 269 return mThrottlingIntervalBoundaryMillis; 270 } 271 } 272 273 boolean sendPendingIntents = false; 274 275 synchronized (mEvents) { 276 for (ConnectivityMetricsEvent e : events) { 277 if (e.componentTag != componentTag) { 278 Log.wtf(TAG, "Unexpected tag: " + e.componentTag); 279 return -1; 280 } 281 282 addEvent(e); 283 } 284 285 mLastEventReference += events.length; 286 287 mEventCounter += events.length; 288 if (mEventCounter >= EVENTS_NOTIFICATION_THRESHOLD) { 289 mEventCounter = 0; 290 sendPendingIntents = true; 291 } 292 } 293 294 if (sendPendingIntents) { 295 synchronized (mPendingIntents) { 296 for (PendingIntent pi : mPendingIntents) { 297 if (VDBG) Log.v(TAG, "Send pending intent"); 298 try { 299 pi.send(getContext(), 0, null, null, null); 300 } catch (PendingIntent.CanceledException e) { 301 Log.e(TAG, "Pending intent canceled: " + pi); 302 mPendingIntents.remove(pi); 303 } 304 } 305 } 306 } 307 308 return 0; 309 } 310 311 /** 312 * Retrieve events 313 * 314 * @param reference of the last event previously returned. The function will return 315 * events following it. 316 * If 0 then all events will be returned. 317 * After the function call it will contain reference of the 318 * last returned event. 319 * @return events 320 */ 321 public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) { 322 enforceDumpPermission(); 323 long ref = reference.getValue(); 324 if (VDBG) Log.v(TAG, "getEvents(" + ref + ")"); 325 326 ConnectivityMetricsEvent[] result; 327 synchronized (mEvents) { 328 if (ref > mLastEventReference) { 329 Log.e(TAG, "Invalid reference"); 330 reference.setValue(mLastEventReference); 331 return null; 332 } 333 if (ref < mLastEventReference - mEvents.size()) { 334 ref = mLastEventReference - mEvents.size(); 335 } 336 337 int numEventsToSkip = 338 mEvents.size() // Total number of events 339 - (int)(mLastEventReference - ref); // Number of events to return 340 341 result = new ConnectivityMetricsEvent[mEvents.size() - numEventsToSkip]; 342 int i = 0; 343 for (ConnectivityMetricsEvent e : mEvents) { 344 if (numEventsToSkip > 0) { 345 numEventsToSkip--; 346 } else { 347 result[i++] = e; 348 } 349 } 350 351 reference.setValue(mLastEventReference); 352 } 353 354 return result; 355 } 356 357 public boolean register(PendingIntent newEventsIntent) { 358 enforceDumpPermission(); 359 if (VDBG) Log.v(TAG, "register(" + newEventsIntent + ")"); 360 361 synchronized (mPendingIntents) { 362 if (mPendingIntents.remove(newEventsIntent)) { 363 Log.w(TAG, "Replacing registered pending intent"); 364 } 365 mPendingIntents.add(newEventsIntent); 366 } 367 368 return true; 369 } 370 371 public void unregister(PendingIntent newEventsIntent) { 372 enforceDumpPermission(); 373 if (VDBG) Log.v(TAG, "unregister(" + newEventsIntent + ")"); 374 375 synchronized (mPendingIntents) { 376 if (!mPendingIntents.remove(newEventsIntent)) { 377 Log.e(TAG, "Pending intent is not registered"); 378 } 379 } 380 } 381 }; 382} 383