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 } 385