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