blob: 673dd8fbbd66475859bd94340b81f60dcf2e3a2a [file] [log] [blame]
Jeff Sharkey63abc372012-01-11 18:38:16 -08001/*
2 * Copyright (C) 2012 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.net;
18
19import static android.net.NetworkStats.IFACE_ALL;
Jeff Davidson1f7e05e2016-03-10 13:21:38 -080020import static android.net.NetworkStats.ROAMING_NO;
21import static android.net.NetworkStats.ROAMING_YES;
Jeff Sharkey63abc372012-01-11 18:38:16 -080022import static android.net.NetworkStats.SET_ALL;
23import static android.net.NetworkStats.SET_DEFAULT;
24import static android.net.NetworkStats.TAG_NONE;
25import static android.net.NetworkStats.UID_ALL;
26import static android.net.TrafficStats.UID_REMOVED;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080027import static android.text.format.DateUtils.WEEK_IN_MILLIS;
Jeff Sharkey63abc372012-01-11 18:38:16 -080028
29import android.net.NetworkIdentity;
30import android.net.NetworkStats;
31import android.net.NetworkStatsHistory;
32import android.net.NetworkTemplate;
33import android.net.TrafficStats;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000034import android.os.Binder;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080035import android.util.ArrayMap;
Dianne Hackborn39606a02012-07-31 17:54:35 -070036import android.util.AtomicFile;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000037import android.util.IntArray;
Jeff Sharkey63abc372012-01-11 18:38:16 -080038
Jeff Sharkeydaa57e82012-09-19 14:10:39 -070039import com.android.internal.util.ArrayUtils;
Jeff Sharkey63abc372012-01-11 18:38:16 -080040import com.android.internal.util.FileRotator;
41import com.android.internal.util.IndentingPrintWriter;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000042
Jeff Sharkey63abc372012-01-11 18:38:16 -080043import com.google.android.collect.Lists;
44import com.google.android.collect.Maps;
45
Jeff Davidson1efb1332015-12-09 18:04:50 -080046import libcore.io.IoUtils;
47
Jeff Sharkey63abc372012-01-11 18:38:16 -080048import java.io.BufferedInputStream;
49import java.io.DataInputStream;
50import java.io.DataOutputStream;
51import java.io.File;
52import java.io.FileNotFoundException;
53import java.io.IOException;
54import java.io.InputStream;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080055import java.io.PrintWriter;
Jeff Sharkey63abc372012-01-11 18:38:16 -080056import java.net.ProtocolException;
57import java.util.ArrayList;
58import java.util.Collections;
59import java.util.HashMap;
Kenny Roote6585b32013-12-13 12:00:26 -080060import java.util.Objects;
Jeff Sharkey63abc372012-01-11 18:38:16 -080061
Jeff Sharkey63abc372012-01-11 18:38:16 -080062/**
63 * Collection of {@link NetworkStatsHistory}, stored based on combined key of
64 * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
65 */
66public class NetworkStatsCollection implements FileRotator.Reader {
Jeff Sharkey63abc372012-01-11 18:38:16 -080067 /** File header magic number: "ANET" */
68 private static final int FILE_MAGIC = 0x414E4554;
69
70 private static final int VERSION_NETWORK_INIT = 1;
71
72 private static final int VERSION_UID_INIT = 1;
73 private static final int VERSION_UID_WITH_IDENT = 2;
74 private static final int VERSION_UID_WITH_TAG = 3;
75 private static final int VERSION_UID_WITH_SET = 4;
76
77 private static final int VERSION_UNIFIED_INIT = 16;
78
Jeff Sharkey55a442e2014-11-18 18:22:21 -080079 private ArrayMap<Key, NetworkStatsHistory> mStats = new ArrayMap<>();
Jeff Sharkey63abc372012-01-11 18:38:16 -080080
Jeff Sharkey70c70532012-05-16 14:51:19 -070081 private final long mBucketDuration;
Jeff Sharkey63abc372012-01-11 18:38:16 -080082
83 private long mStartMillis;
84 private long mEndMillis;
85 private long mTotalBytes;
86 private boolean mDirty;
87
88 public NetworkStatsCollection(long bucketDuration) {
89 mBucketDuration = bucketDuration;
90 reset();
91 }
92
93 public void reset() {
94 mStats.clear();
95 mStartMillis = Long.MAX_VALUE;
96 mEndMillis = Long.MIN_VALUE;
97 mTotalBytes = 0;
98 mDirty = false;
99 }
100
101 public long getStartMillis() {
102 return mStartMillis;
103 }
104
Jeff Sharkey70c70532012-05-16 14:51:19 -0700105 /**
106 * Return first atomic bucket in this collection, which is more conservative
107 * than {@link #mStartMillis}.
108 */
109 public long getFirstAtomicBucketMillis() {
110 if (mStartMillis == Long.MAX_VALUE) {
111 return Long.MAX_VALUE;
112 } else {
113 return mStartMillis + mBucketDuration;
114 }
115 }
116
Jeff Sharkey63abc372012-01-11 18:38:16 -0800117 public long getEndMillis() {
118 return mEndMillis;
119 }
120
121 public long getTotalBytes() {
122 return mTotalBytes;
123 }
124
125 public boolean isDirty() {
126 return mDirty;
127 }
128
129 public void clearDirty() {
130 mDirty = false;
131 }
132
133 public boolean isEmpty() {
134 return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
135 }
136
Jeff Davidson1efb1332015-12-09 18:04:50 -0800137 public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
Antonio Cansadocd42acd2016-02-17 13:03:38 -0800138 return getRelevantUids(accessLevel, Binder.getCallingUid());
139 }
140
141 public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
142 final int callerUid) {
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000143 IntArray uids = new IntArray();
144 for (int i = 0; i < mStats.size(); i++) {
145 final Key key = mStats.keyAt(i);
Jeff Davidson1efb1332015-12-09 18:04:50 -0800146 if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) {
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000147 int j = uids.binarySearch(key.uid);
148
149 if (j < 0) {
150 j = ~j;
151 uids.add(j, key.uid);
152 }
153 }
154 }
155 return uids.toArray();
156 }
157
Jeff Sharkey63abc372012-01-11 18:38:16 -0800158 /**
159 * Combine all {@link NetworkStatsHistory} in this collection which match
160 * the requested parameters.
161 */
162 public NetworkStatsHistory getHistory(
Jeff Davidson1efb1332015-12-09 18:04:50 -0800163 NetworkTemplate template, int uid, int set, int tag, int fields,
164 @NetworkStatsAccess.Level int accessLevel) {
165 return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE,
166 accessLevel);
Jeff Sharkey70c70532012-05-16 14:51:19 -0700167 }
168
169 /**
170 * Combine all {@link NetworkStatsHistory} in this collection which match
171 * the requested parameters.
172 */
173 public NetworkStatsHistory getHistory(
Jeff Davidson1efb1332015-12-09 18:04:50 -0800174 NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
175 @NetworkStatsAccess.Level int accessLevel) {
Antonio Cansadocd42acd2016-02-17 13:03:38 -0800176 return getHistory(template, uid, set, tag, fields, start, end, accessLevel,
177 Binder.getCallingUid());
178 }
179
180 /**
181 * Combine all {@link NetworkStatsHistory} in this collection which match
182 * the requested parameters.
183 */
184 public NetworkStatsHistory getHistory(
185 NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
186 @NetworkStatsAccess.Level int accessLevel, int callerUid) {
Jeff Davidson1efb1332015-12-09 18:04:50 -0800187 if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) {
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000188 throw new SecurityException("Network stats history of uid " + uid
189 + " is forbidden for caller " + callerUid);
190 }
191
Jeff Sharkey63abc372012-01-11 18:38:16 -0800192 final NetworkStatsHistory combined = new NetworkStatsHistory(
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000193 mBucketDuration, start == end ? 1 : estimateBuckets(), fields);
194
195 // shortcut when we know stats will be empty
196 if (start == end) return combined;
197
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800198 for (int i = 0; i < mStats.size(); i++) {
199 final Key key = mStats.keyAt(i);
Wenchao Tong98170b02015-03-17 16:14:23 -0700200 if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag
Jeff Sharkey63abc372012-01-11 18:38:16 -0800201 && templateMatches(template, key.ident)) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800202 final NetworkStatsHistory value = mStats.valueAt(i);
203 combined.recordHistory(value, start, end);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800204 }
205 }
206 return combined;
207 }
208
209 /**
210 * Summarize all {@link NetworkStatsHistory} in this collection which match
211 * the requested parameters.
212 */
Jeff Davidson1efb1332015-12-09 18:04:50 -0800213 public NetworkStats getSummary(NetworkTemplate template, long start, long end,
214 @NetworkStatsAccess.Level int accessLevel) {
Antonio Cansadocd42acd2016-02-17 13:03:38 -0800215 return getSummary(template, start, end, accessLevel, Binder.getCallingUid());
216 }
217
218 /**
219 * Summarize all {@link NetworkStatsHistory} in this collection which match
220 * the requested parameters.
221 */
222 public NetworkStats getSummary(NetworkTemplate template, long start, long end,
223 @NetworkStatsAccess.Level int accessLevel, int callerUid) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800224 final long now = System.currentTimeMillis();
225
226 final NetworkStats stats = new NetworkStats(end - start, 24);
Jeff Sharkey70c70532012-05-16 14:51:19 -0700227 // shortcut when we know stats will be empty
228 if (start == end) return stats;
229
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000230 final NetworkStats.Entry entry = new NetworkStats.Entry();
231 NetworkStatsHistory.Entry historyEntry = null;
232
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800233 for (int i = 0; i < mStats.size(); i++) {
234 final Key key = mStats.keyAt(i);
Jeff Davidson1efb1332015-12-09 18:04:50 -0800235 if (templateMatches(template, key.ident)
236 && NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)
Wenchao Tong98170b02015-03-17 16:14:23 -0700237 && key.set < NetworkStats.SET_DEBUG_START) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800238 final NetworkStatsHistory value = mStats.valueAt(i);
239 historyEntry = value.getValues(start, end, now, historyEntry);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800240
241 entry.iface = IFACE_ALL;
242 entry.uid = key.uid;
243 entry.set = key.set;
244 entry.tag = key.tag;
Jeff Davidson1f7e05e2016-03-10 13:21:38 -0800245 entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800246 entry.rxBytes = historyEntry.rxBytes;
247 entry.rxPackets = historyEntry.rxPackets;
248 entry.txBytes = historyEntry.txBytes;
249 entry.txPackets = historyEntry.txPackets;
250 entry.operations = historyEntry.operations;
251
252 if (!entry.isEmpty()) {
253 stats.combineValues(entry);
254 }
255 }
256 }
257
258 return stats;
259 }
260
261 /**
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700262 * Record given {@link android.net.NetworkStats.Entry} into this collection.
Jeff Sharkey63abc372012-01-11 18:38:16 -0800263 */
264 public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
265 long end, NetworkStats.Entry entry) {
Jeff Sharkey70c70532012-05-16 14:51:19 -0700266 final NetworkStatsHistory history = findOrCreateHistory(ident, uid, set, tag);
267 history.recordData(start, end, entry);
268 noteRecordedHistory(history.getStart(), history.getEnd(), entry.rxBytes + entry.txBytes);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800269 }
270
271 /**
272 * Record given {@link NetworkStatsHistory} into this collection.
273 */
274 private void recordHistory(Key key, NetworkStatsHistory history) {
275 if (history.size() == 0) return;
276 noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
277
Jeff Sharkeyac3fcb12012-05-02 18:11:52 -0700278 NetworkStatsHistory target = mStats.get(key);
279 if (target == null) {
280 target = new NetworkStatsHistory(history.getBucketDuration());
281 mStats.put(key, target);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800282 }
Jeff Sharkeyac3fcb12012-05-02 18:11:52 -0700283 target.recordEntireHistory(history);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800284 }
285
286 /**
287 * Record all {@link NetworkStatsHistory} contained in the given collection
288 * into this collection.
289 */
290 public void recordCollection(NetworkStatsCollection another) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800291 for (int i = 0; i < another.mStats.size(); i++) {
292 final Key key = another.mStats.keyAt(i);
293 final NetworkStatsHistory value = another.mStats.valueAt(i);
294 recordHistory(key, value);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800295 }
296 }
297
298 private NetworkStatsHistory findOrCreateHistory(
299 NetworkIdentitySet ident, int uid, int set, int tag) {
300 final Key key = new Key(ident, uid, set, tag);
301 final NetworkStatsHistory existing = mStats.get(key);
302
303 // update when no existing, or when bucket duration changed
304 NetworkStatsHistory updated = null;
305 if (existing == null) {
306 updated = new NetworkStatsHistory(mBucketDuration, 10);
307 } else if (existing.getBucketDuration() != mBucketDuration) {
308 updated = new NetworkStatsHistory(existing, mBucketDuration);
309 }
310
311 if (updated != null) {
312 mStats.put(key, updated);
313 return updated;
314 } else {
315 return existing;
316 }
317 }
318
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700319 @Override
Jeff Sharkey63abc372012-01-11 18:38:16 -0800320 public void read(InputStream in) throws IOException {
321 read(new DataInputStream(in));
322 }
323
324 public void read(DataInputStream in) throws IOException {
325 // verify file magic header intact
326 final int magic = in.readInt();
327 if (magic != FILE_MAGIC) {
328 throw new ProtocolException("unexpected magic: " + magic);
329 }
330
331 final int version = in.readInt();
332 switch (version) {
333 case VERSION_UNIFIED_INIT: {
334 // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
335 final int identSize = in.readInt();
336 for (int i = 0; i < identSize; i++) {
337 final NetworkIdentitySet ident = new NetworkIdentitySet(in);
338
339 final int size = in.readInt();
340 for (int j = 0; j < size; j++) {
341 final int uid = in.readInt();
342 final int set = in.readInt();
343 final int tag = in.readInt();
344
345 final Key key = new Key(ident, uid, set, tag);
346 final NetworkStatsHistory history = new NetworkStatsHistory(in);
347 recordHistory(key, history);
348 }
349 }
350 break;
351 }
352 default: {
353 throw new ProtocolException("unexpected version: " + version);
354 }
355 }
356 }
357
358 public void write(DataOutputStream out) throws IOException {
359 // cluster key lists grouped by ident
360 final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
361 for (Key key : mStats.keySet()) {
362 ArrayList<Key> keys = keysByIdent.get(key.ident);
363 if (keys == null) {
364 keys = Lists.newArrayList();
365 keysByIdent.put(key.ident, keys);
366 }
367 keys.add(key);
368 }
369
370 out.writeInt(FILE_MAGIC);
371 out.writeInt(VERSION_UNIFIED_INIT);
372
373 out.writeInt(keysByIdent.size());
374 for (NetworkIdentitySet ident : keysByIdent.keySet()) {
375 final ArrayList<Key> keys = keysByIdent.get(ident);
376 ident.writeToStream(out);
377
378 out.writeInt(keys.size());
379 for (Key key : keys) {
380 final NetworkStatsHistory history = mStats.get(key);
381 out.writeInt(key.uid);
382 out.writeInt(key.set);
383 out.writeInt(key.tag);
384 history.writeToStream(out);
385 }
386 }
387
388 out.flush();
389 }
390
391 @Deprecated
392 public void readLegacyNetwork(File file) throws IOException {
393 final AtomicFile inputFile = new AtomicFile(file);
394
395 DataInputStream in = null;
396 try {
397 in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
398
399 // verify file magic header intact
400 final int magic = in.readInt();
401 if (magic != FILE_MAGIC) {
402 throw new ProtocolException("unexpected magic: " + magic);
403 }
404
405 final int version = in.readInt();
406 switch (version) {
407 case VERSION_NETWORK_INIT: {
408 // network := size *(NetworkIdentitySet NetworkStatsHistory)
409 final int size = in.readInt();
410 for (int i = 0; i < size; i++) {
411 final NetworkIdentitySet ident = new NetworkIdentitySet(in);
412 final NetworkStatsHistory history = new NetworkStatsHistory(in);
413
414 final Key key = new Key(ident, UID_ALL, SET_ALL, TAG_NONE);
415 recordHistory(key, history);
416 }
417 break;
418 }
419 default: {
420 throw new ProtocolException("unexpected version: " + version);
421 }
422 }
423 } catch (FileNotFoundException e) {
424 // missing stats is okay, probably first boot
425 } finally {
426 IoUtils.closeQuietly(in);
427 }
428 }
429
430 @Deprecated
431 public void readLegacyUid(File file, boolean onlyTags) throws IOException {
432 final AtomicFile inputFile = new AtomicFile(file);
433
434 DataInputStream in = null;
435 try {
436 in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
437
438 // verify file magic header intact
439 final int magic = in.readInt();
440 if (magic != FILE_MAGIC) {
441 throw new ProtocolException("unexpected magic: " + magic);
442 }
443
444 final int version = in.readInt();
445 switch (version) {
446 case VERSION_UID_INIT: {
447 // uid := size *(UID NetworkStatsHistory)
448
449 // drop this data version, since we don't have a good
450 // mapping into NetworkIdentitySet.
451 break;
452 }
453 case VERSION_UID_WITH_IDENT: {
454 // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
455
456 // drop this data version, since this version only existed
457 // for a short time.
458 break;
459 }
460 case VERSION_UID_WITH_TAG:
461 case VERSION_UID_WITH_SET: {
462 // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
463 final int identSize = in.readInt();
464 for (int i = 0; i < identSize; i++) {
465 final NetworkIdentitySet ident = new NetworkIdentitySet(in);
466
467 final int size = in.readInt();
468 for (int j = 0; j < size; j++) {
469 final int uid = in.readInt();
470 final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
471 : SET_DEFAULT;
472 final int tag = in.readInt();
473
474 final Key key = new Key(ident, uid, set, tag);
475 final NetworkStatsHistory history = new NetworkStatsHistory(in);
476
477 if ((tag == TAG_NONE) != onlyTags) {
478 recordHistory(key, history);
479 }
480 }
481 }
482 break;
483 }
484 default: {
485 throw new ProtocolException("unexpected version: " + version);
486 }
487 }
488 } catch (FileNotFoundException e) {
489 // missing stats is okay, probably first boot
490 } finally {
491 IoUtils.closeQuietly(in);
492 }
493 }
494
495 /**
496 * Remove any {@link NetworkStatsHistory} attributed to the requested UID,
497 * moving any {@link NetworkStats#TAG_NONE} series to
498 * {@link TrafficStats#UID_REMOVED}.
499 */
Jeff Sharkeydaa57e82012-09-19 14:10:39 -0700500 public void removeUids(int[] uids) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800501 final ArrayList<Key> knownKeys = Lists.newArrayList();
502 knownKeys.addAll(mStats.keySet());
503
504 // migrate all UID stats into special "removed" bucket
505 for (Key key : knownKeys) {
Jeff Sharkeydaa57e82012-09-19 14:10:39 -0700506 if (ArrayUtils.contains(uids, key.uid)) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800507 // only migrate combined TAG_NONE history
508 if (key.tag == TAG_NONE) {
509 final NetworkStatsHistory uidHistory = mStats.get(key);
510 final NetworkStatsHistory removedHistory = findOrCreateHistory(
511 key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
512 removedHistory.recordEntireHistory(uidHistory);
513 }
514 mStats.remove(key);
515 mDirty = true;
516 }
517 }
518 }
519
520 private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
521 if (startMillis < mStartMillis) mStartMillis = startMillis;
522 if (endMillis > mEndMillis) mEndMillis = endMillis;
523 mTotalBytes += totalBytes;
524 mDirty = true;
525 }
526
527 private int estimateBuckets() {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800528 return (int) (Math.min(mEndMillis - mStartMillis, WEEK_IN_MILLIS * 5)
Jeff Sharkey63abc372012-01-11 18:38:16 -0800529 / mBucketDuration);
530 }
531
532 public void dump(IndentingPrintWriter pw) {
533 final ArrayList<Key> keys = Lists.newArrayList();
534 keys.addAll(mStats.keySet());
535 Collections.sort(keys);
536
537 for (Key key : keys) {
538 pw.print("ident="); pw.print(key.ident.toString());
539 pw.print(" uid="); pw.print(key.uid);
540 pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
541 pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
542
543 final NetworkStatsHistory history = mStats.get(key);
544 pw.increaseIndent();
545 history.dump(pw, true);
546 pw.decreaseIndent();
547 }
548 }
549
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800550 public void dumpCheckin(PrintWriter pw, long start, long end) {
551 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
552 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
553 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateEthernet(), "eth");
554 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateBluetooth(), "bt");
555 }
556
557 /**
558 * Dump all contained stats that match requested parameters, but group
559 * together all matching {@link NetworkTemplate} under a single prefix.
560 */
561 private void dumpCheckin(PrintWriter pw, long start, long end, NetworkTemplate groupTemplate,
562 String groupPrefix) {
563 final ArrayMap<Key, NetworkStatsHistory> grouped = new ArrayMap<>();
564
565 // Walk through all history, grouping by matching network templates
566 for (int i = 0; i < mStats.size(); i++) {
567 final Key key = mStats.keyAt(i);
568 final NetworkStatsHistory value = mStats.valueAt(i);
569
570 if (!templateMatches(groupTemplate, key.ident)) continue;
Wenchao Tong98170b02015-03-17 16:14:23 -0700571 if (key.set >= NetworkStats.SET_DEBUG_START) continue;
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800572
573 final Key groupKey = new Key(null, key.uid, key.set, key.tag);
574 NetworkStatsHistory groupHistory = grouped.get(groupKey);
575 if (groupHistory == null) {
576 groupHistory = new NetworkStatsHistory(value.getBucketDuration());
577 grouped.put(groupKey, groupHistory);
578 }
579 groupHistory.recordHistory(value, start, end);
580 }
581
582 for (int i = 0; i < grouped.size(); i++) {
583 final Key key = grouped.keyAt(i);
584 final NetworkStatsHistory value = grouped.valueAt(i);
585
586 if (value.size() == 0) continue;
587
588 pw.print("c,");
589 pw.print(groupPrefix); pw.print(',');
590 pw.print(key.uid); pw.print(',');
591 pw.print(NetworkStats.setToCheckinString(key.set)); pw.print(',');
592 pw.print(key.tag);
593 pw.println();
594
595 value.dumpCheckin(pw);
596 }
597 }
598
Jeff Sharkey63abc372012-01-11 18:38:16 -0800599 /**
600 * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
601 * in the given {@link NetworkIdentitySet}.
602 */
603 private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
604 for (NetworkIdentity ident : identSet) {
605 if (template.matches(ident)) {
606 return true;
607 }
608 }
609 return false;
610 }
611
612 private static class Key implements Comparable<Key> {
613 public final NetworkIdentitySet ident;
614 public final int uid;
615 public final int set;
616 public final int tag;
617
618 private final int hashCode;
619
620 public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
621 this.ident = ident;
622 this.uid = uid;
623 this.set = set;
624 this.tag = tag;
Kenny Roote6585b32013-12-13 12:00:26 -0800625 hashCode = Objects.hash(ident, uid, set, tag);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800626 }
627
628 @Override
629 public int hashCode() {
630 return hashCode;
631 }
632
633 @Override
634 public boolean equals(Object obj) {
635 if (obj instanceof Key) {
636 final Key key = (Key) obj;
637 return uid == key.uid && set == key.set && tag == key.tag
Kenny Roote6585b32013-12-13 12:00:26 -0800638 && Objects.equals(ident, key.ident);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800639 }
640 return false;
641 }
642
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700643 @Override
Jeff Sharkey63abc372012-01-11 18:38:16 -0800644 public int compareTo(Key another) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800645 int res = 0;
646 if (ident != null && another.ident != null) {
647 res = ident.compareTo(another.ident);
648 }
649 if (res == 0) {
650 res = Integer.compare(uid, another.uid);
651 }
652 if (res == 0) {
653 res = Integer.compare(set, another.set);
654 }
655 if (res == 0) {
656 res = Integer.compare(tag, another.tag);
657 }
658 return res;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800659 }
660 }
661}