blob: c45b4169f575b7430745c7799175be55b5030e25 [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;
Stephen Chen25147872016-10-21 12:44:26 -070020import static android.net.NetworkStats.METERED_NO;
21import static android.net.NetworkStats.METERED_YES;
Jeff Davidson1f7e05e2016-03-10 13:21:38 -080022import static android.net.NetworkStats.ROAMING_NO;
23import static android.net.NetworkStats.ROAMING_YES;
Jeff Sharkey63abc372012-01-11 18:38:16 -080024import static android.net.NetworkStats.SET_ALL;
25import static android.net.NetworkStats.SET_DEFAULT;
26import static android.net.NetworkStats.TAG_NONE;
27import static android.net.NetworkStats.UID_ALL;
28import static android.net.TrafficStats.UID_REMOVED;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080029import static android.text.format.DateUtils.WEEK_IN_MILLIS;
Jeff Sharkey63abc372012-01-11 18:38:16 -080030
31import android.net.NetworkIdentity;
32import android.net.NetworkStats;
33import android.net.NetworkStatsHistory;
34import android.net.NetworkTemplate;
35import android.net.TrafficStats;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000036import android.os.Binder;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080037import android.util.ArrayMap;
Dianne Hackborn39606a02012-07-31 17:54:35 -070038import android.util.AtomicFile;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000039import android.util.IntArray;
Jeff Sharkey63abc372012-01-11 18:38:16 -080040
Jeff Sharkeydaa57e82012-09-19 14:10:39 -070041import com.android.internal.util.ArrayUtils;
Jeff Sharkey63abc372012-01-11 18:38:16 -080042import com.android.internal.util.FileRotator;
43import com.android.internal.util.IndentingPrintWriter;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000044
Jeff Sharkey63abc372012-01-11 18:38:16 -080045import com.google.android.collect.Lists;
46import com.google.android.collect.Maps;
47
Jeff Davidson1efb1332015-12-09 18:04:50 -080048import libcore.io.IoUtils;
49
Jeff Sharkey63abc372012-01-11 18:38:16 -080050import java.io.BufferedInputStream;
51import java.io.DataInputStream;
52import java.io.DataOutputStream;
53import java.io.File;
54import java.io.FileNotFoundException;
55import java.io.IOException;
56import java.io.InputStream;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080057import java.io.PrintWriter;
Jeff Sharkey63abc372012-01-11 18:38:16 -080058import java.net.ProtocolException;
59import java.util.ArrayList;
60import java.util.Collections;
61import java.util.HashMap;
Kenny Roote6585b32013-12-13 12:00:26 -080062import java.util.Objects;
Jeff Sharkey63abc372012-01-11 18:38:16 -080063
Jeff Sharkey63abc372012-01-11 18:38:16 -080064/**
65 * Collection of {@link NetworkStatsHistory}, stored based on combined key of
66 * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
67 */
68public class NetworkStatsCollection implements FileRotator.Reader {
Jeff Sharkey63abc372012-01-11 18:38:16 -080069 /** File header magic number: "ANET" */
70 private static final int FILE_MAGIC = 0x414E4554;
71
72 private static final int VERSION_NETWORK_INIT = 1;
73
74 private static final int VERSION_UID_INIT = 1;
75 private static final int VERSION_UID_WITH_IDENT = 2;
76 private static final int VERSION_UID_WITH_TAG = 3;
77 private static final int VERSION_UID_WITH_SET = 4;
78
79 private static final int VERSION_UNIFIED_INIT = 16;
80
Jeff Sharkey55a442e2014-11-18 18:22:21 -080081 private ArrayMap<Key, NetworkStatsHistory> mStats = new ArrayMap<>();
Jeff Sharkey63abc372012-01-11 18:38:16 -080082
Jeff Sharkey70c70532012-05-16 14:51:19 -070083 private final long mBucketDuration;
Jeff Sharkey63abc372012-01-11 18:38:16 -080084
85 private long mStartMillis;
86 private long mEndMillis;
87 private long mTotalBytes;
88 private boolean mDirty;
89
90 public NetworkStatsCollection(long bucketDuration) {
91 mBucketDuration = bucketDuration;
92 reset();
93 }
94
95 public void reset() {
96 mStats.clear();
97 mStartMillis = Long.MAX_VALUE;
98 mEndMillis = Long.MIN_VALUE;
99 mTotalBytes = 0;
100 mDirty = false;
101 }
102
103 public long getStartMillis() {
104 return mStartMillis;
105 }
106
Jeff Sharkey70c70532012-05-16 14:51:19 -0700107 /**
108 * Return first atomic bucket in this collection, which is more conservative
109 * than {@link #mStartMillis}.
110 */
111 public long getFirstAtomicBucketMillis() {
112 if (mStartMillis == Long.MAX_VALUE) {
113 return Long.MAX_VALUE;
114 } else {
115 return mStartMillis + mBucketDuration;
116 }
117 }
118
Jeff Sharkey63abc372012-01-11 18:38:16 -0800119 public long getEndMillis() {
120 return mEndMillis;
121 }
122
123 public long getTotalBytes() {
124 return mTotalBytes;
125 }
126
127 public boolean isDirty() {
128 return mDirty;
129 }
130
131 public void clearDirty() {
132 mDirty = false;
133 }
134
135 public boolean isEmpty() {
136 return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
137 }
138
Jeff Davidson1efb1332015-12-09 18:04:50 -0800139 public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
Antonio Cansadocd42acd2016-02-17 13:03:38 -0800140 return getRelevantUids(accessLevel, Binder.getCallingUid());
141 }
142
143 public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
144 final int callerUid) {
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000145 IntArray uids = new IntArray();
146 for (int i = 0; i < mStats.size(); i++) {
147 final Key key = mStats.keyAt(i);
Jeff Davidson1efb1332015-12-09 18:04:50 -0800148 if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) {
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000149 int j = uids.binarySearch(key.uid);
150
151 if (j < 0) {
152 j = ~j;
153 uids.add(j, key.uid);
154 }
155 }
156 }
157 return uids.toArray();
158 }
159
Jeff Sharkey63abc372012-01-11 18:38:16 -0800160 /**
161 * Combine all {@link NetworkStatsHistory} in this collection which match
162 * the requested parameters.
163 */
164 public NetworkStatsHistory getHistory(
Jeff Davidson1efb1332015-12-09 18:04:50 -0800165 NetworkTemplate template, int uid, int set, int tag, int fields,
166 @NetworkStatsAccess.Level int accessLevel) {
167 return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE,
168 accessLevel);
Jeff Sharkey70c70532012-05-16 14:51:19 -0700169 }
170
171 /**
172 * Combine all {@link NetworkStatsHistory} in this collection which match
173 * the requested parameters.
174 */
175 public NetworkStatsHistory getHistory(
Jeff Davidson1efb1332015-12-09 18:04:50 -0800176 NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
177 @NetworkStatsAccess.Level int accessLevel) {
Antonio Cansadocd42acd2016-02-17 13:03:38 -0800178 return getHistory(template, uid, set, tag, fields, start, end, accessLevel,
179 Binder.getCallingUid());
180 }
181
182 /**
183 * Combine all {@link NetworkStatsHistory} in this collection which match
184 * the requested parameters.
185 */
186 public NetworkStatsHistory getHistory(
187 NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
188 @NetworkStatsAccess.Level int accessLevel, int callerUid) {
Jeff Davidson1efb1332015-12-09 18:04:50 -0800189 if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) {
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000190 throw new SecurityException("Network stats history of uid " + uid
191 + " is forbidden for caller " + callerUid);
192 }
193
Jeff Sharkey63abc372012-01-11 18:38:16 -0800194 final NetworkStatsHistory combined = new NetworkStatsHistory(
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000195 mBucketDuration, start == end ? 1 : estimateBuckets(), fields);
196
197 // shortcut when we know stats will be empty
198 if (start == end) return combined;
199
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800200 for (int i = 0; i < mStats.size(); i++) {
201 final Key key = mStats.keyAt(i);
Wenchao Tong98170b02015-03-17 16:14:23 -0700202 if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag
Jeff Sharkey63abc372012-01-11 18:38:16 -0800203 && templateMatches(template, key.ident)) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800204 final NetworkStatsHistory value = mStats.valueAt(i);
205 combined.recordHistory(value, start, end);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800206 }
207 }
208 return combined;
209 }
210
211 /**
212 * Summarize all {@link NetworkStatsHistory} in this collection which match
213 * the requested parameters.
214 */
Jeff Davidson1efb1332015-12-09 18:04:50 -0800215 public NetworkStats getSummary(NetworkTemplate template, long start, long end,
216 @NetworkStatsAccess.Level int accessLevel) {
Antonio Cansadocd42acd2016-02-17 13:03:38 -0800217 return getSummary(template, start, end, accessLevel, Binder.getCallingUid());
218 }
219
220 /**
221 * Summarize all {@link NetworkStatsHistory} in this collection which match
222 * the requested parameters.
223 */
224 public NetworkStats getSummary(NetworkTemplate template, long start, long end,
225 @NetworkStatsAccess.Level int accessLevel, int callerUid) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800226 final long now = System.currentTimeMillis();
227
228 final NetworkStats stats = new NetworkStats(end - start, 24);
Jeff Sharkey70c70532012-05-16 14:51:19 -0700229 // shortcut when we know stats will be empty
230 if (start == end) return stats;
231
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000232 final NetworkStats.Entry entry = new NetworkStats.Entry();
233 NetworkStatsHistory.Entry historyEntry = null;
234
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800235 for (int i = 0; i < mStats.size(); i++) {
236 final Key key = mStats.keyAt(i);
Jeff Davidson1efb1332015-12-09 18:04:50 -0800237 if (templateMatches(template, key.ident)
238 && NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)
Wenchao Tong98170b02015-03-17 16:14:23 -0700239 && key.set < NetworkStats.SET_DEBUG_START) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800240 final NetworkStatsHistory value = mStats.valueAt(i);
241 historyEntry = value.getValues(start, end, now, historyEntry);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800242
243 entry.iface = IFACE_ALL;
244 entry.uid = key.uid;
245 entry.set = key.set;
246 entry.tag = key.tag;
Stephen Chen25147872016-10-21 12:44:26 -0700247 entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO;
Jeff Davidson1f7e05e2016-03-10 13:21:38 -0800248 entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800249 entry.rxBytes = historyEntry.rxBytes;
250 entry.rxPackets = historyEntry.rxPackets;
251 entry.txBytes = historyEntry.txBytes;
252 entry.txPackets = historyEntry.txPackets;
253 entry.operations = historyEntry.operations;
254
255 if (!entry.isEmpty()) {
256 stats.combineValues(entry);
257 }
258 }
259 }
260
261 return stats;
262 }
263
264 /**
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700265 * Record given {@link android.net.NetworkStats.Entry} into this collection.
Jeff Sharkey63abc372012-01-11 18:38:16 -0800266 */
267 public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
268 long end, NetworkStats.Entry entry) {
Jeff Sharkey70c70532012-05-16 14:51:19 -0700269 final NetworkStatsHistory history = findOrCreateHistory(ident, uid, set, tag);
270 history.recordData(start, end, entry);
271 noteRecordedHistory(history.getStart(), history.getEnd(), entry.rxBytes + entry.txBytes);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800272 }
273
274 /**
275 * Record given {@link NetworkStatsHistory} into this collection.
276 */
277 private void recordHistory(Key key, NetworkStatsHistory history) {
278 if (history.size() == 0) return;
279 noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
280
Jeff Sharkeyac3fcb12012-05-02 18:11:52 -0700281 NetworkStatsHistory target = mStats.get(key);
282 if (target == null) {
283 target = new NetworkStatsHistory(history.getBucketDuration());
284 mStats.put(key, target);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800285 }
Jeff Sharkeyac3fcb12012-05-02 18:11:52 -0700286 target.recordEntireHistory(history);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800287 }
288
289 /**
290 * Record all {@link NetworkStatsHistory} contained in the given collection
291 * into this collection.
292 */
293 public void recordCollection(NetworkStatsCollection another) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800294 for (int i = 0; i < another.mStats.size(); i++) {
295 final Key key = another.mStats.keyAt(i);
296 final NetworkStatsHistory value = another.mStats.valueAt(i);
297 recordHistory(key, value);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800298 }
299 }
300
301 private NetworkStatsHistory findOrCreateHistory(
302 NetworkIdentitySet ident, int uid, int set, int tag) {
303 final Key key = new Key(ident, uid, set, tag);
304 final NetworkStatsHistory existing = mStats.get(key);
305
306 // update when no existing, or when bucket duration changed
307 NetworkStatsHistory updated = null;
308 if (existing == null) {
309 updated = new NetworkStatsHistory(mBucketDuration, 10);
310 } else if (existing.getBucketDuration() != mBucketDuration) {
311 updated = new NetworkStatsHistory(existing, mBucketDuration);
312 }
313
314 if (updated != null) {
315 mStats.put(key, updated);
316 return updated;
317 } else {
318 return existing;
319 }
320 }
321
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700322 @Override
Jeff Sharkey63abc372012-01-11 18:38:16 -0800323 public void read(InputStream in) throws IOException {
324 read(new DataInputStream(in));
325 }
326
327 public void read(DataInputStream in) throws IOException {
328 // verify file magic header intact
329 final int magic = in.readInt();
330 if (magic != FILE_MAGIC) {
331 throw new ProtocolException("unexpected magic: " + magic);
332 }
333
334 final int version = in.readInt();
335 switch (version) {
336 case VERSION_UNIFIED_INIT: {
337 // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
338 final int identSize = in.readInt();
339 for (int i = 0; i < identSize; i++) {
340 final NetworkIdentitySet ident = new NetworkIdentitySet(in);
341
342 final int size = in.readInt();
343 for (int j = 0; j < size; j++) {
344 final int uid = in.readInt();
345 final int set = in.readInt();
346 final int tag = in.readInt();
347
348 final Key key = new Key(ident, uid, set, tag);
349 final NetworkStatsHistory history = new NetworkStatsHistory(in);
350 recordHistory(key, history);
351 }
352 }
353 break;
354 }
355 default: {
356 throw new ProtocolException("unexpected version: " + version);
357 }
358 }
359 }
360
361 public void write(DataOutputStream out) throws IOException {
362 // cluster key lists grouped by ident
363 final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
364 for (Key key : mStats.keySet()) {
365 ArrayList<Key> keys = keysByIdent.get(key.ident);
366 if (keys == null) {
367 keys = Lists.newArrayList();
368 keysByIdent.put(key.ident, keys);
369 }
370 keys.add(key);
371 }
372
373 out.writeInt(FILE_MAGIC);
374 out.writeInt(VERSION_UNIFIED_INIT);
375
376 out.writeInt(keysByIdent.size());
377 for (NetworkIdentitySet ident : keysByIdent.keySet()) {
378 final ArrayList<Key> keys = keysByIdent.get(ident);
379 ident.writeToStream(out);
380
381 out.writeInt(keys.size());
382 for (Key key : keys) {
383 final NetworkStatsHistory history = mStats.get(key);
384 out.writeInt(key.uid);
385 out.writeInt(key.set);
386 out.writeInt(key.tag);
387 history.writeToStream(out);
388 }
389 }
390
391 out.flush();
392 }
393
394 @Deprecated
395 public void readLegacyNetwork(File file) throws IOException {
396 final AtomicFile inputFile = new AtomicFile(file);
397
398 DataInputStream in = null;
399 try {
400 in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
401
402 // verify file magic header intact
403 final int magic = in.readInt();
404 if (magic != FILE_MAGIC) {
405 throw new ProtocolException("unexpected magic: " + magic);
406 }
407
408 final int version = in.readInt();
409 switch (version) {
410 case VERSION_NETWORK_INIT: {
411 // network := size *(NetworkIdentitySet NetworkStatsHistory)
412 final int size = in.readInt();
413 for (int i = 0; i < size; i++) {
414 final NetworkIdentitySet ident = new NetworkIdentitySet(in);
415 final NetworkStatsHistory history = new NetworkStatsHistory(in);
416
417 final Key key = new Key(ident, UID_ALL, SET_ALL, TAG_NONE);
418 recordHistory(key, history);
419 }
420 break;
421 }
422 default: {
423 throw new ProtocolException("unexpected version: " + version);
424 }
425 }
426 } catch (FileNotFoundException e) {
427 // missing stats is okay, probably first boot
428 } finally {
429 IoUtils.closeQuietly(in);
430 }
431 }
432
433 @Deprecated
434 public void readLegacyUid(File file, boolean onlyTags) throws IOException {
435 final AtomicFile inputFile = new AtomicFile(file);
436
437 DataInputStream in = null;
438 try {
439 in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
440
441 // verify file magic header intact
442 final int magic = in.readInt();
443 if (magic != FILE_MAGIC) {
444 throw new ProtocolException("unexpected magic: " + magic);
445 }
446
447 final int version = in.readInt();
448 switch (version) {
449 case VERSION_UID_INIT: {
450 // uid := size *(UID NetworkStatsHistory)
451
452 // drop this data version, since we don't have a good
453 // mapping into NetworkIdentitySet.
454 break;
455 }
456 case VERSION_UID_WITH_IDENT: {
457 // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
458
459 // drop this data version, since this version only existed
460 // for a short time.
461 break;
462 }
463 case VERSION_UID_WITH_TAG:
464 case VERSION_UID_WITH_SET: {
465 // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
466 final int identSize = in.readInt();
467 for (int i = 0; i < identSize; i++) {
468 final NetworkIdentitySet ident = new NetworkIdentitySet(in);
469
470 final int size = in.readInt();
471 for (int j = 0; j < size; j++) {
472 final int uid = in.readInt();
473 final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
474 : SET_DEFAULT;
475 final int tag = in.readInt();
476
477 final Key key = new Key(ident, uid, set, tag);
478 final NetworkStatsHistory history = new NetworkStatsHistory(in);
479
480 if ((tag == TAG_NONE) != onlyTags) {
481 recordHistory(key, history);
482 }
483 }
484 }
485 break;
486 }
487 default: {
488 throw new ProtocolException("unexpected version: " + version);
489 }
490 }
491 } catch (FileNotFoundException e) {
492 // missing stats is okay, probably first boot
493 } finally {
494 IoUtils.closeQuietly(in);
495 }
496 }
497
498 /**
499 * Remove any {@link NetworkStatsHistory} attributed to the requested UID,
500 * moving any {@link NetworkStats#TAG_NONE} series to
501 * {@link TrafficStats#UID_REMOVED}.
502 */
Jeff Sharkeydaa57e82012-09-19 14:10:39 -0700503 public void removeUids(int[] uids) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800504 final ArrayList<Key> knownKeys = Lists.newArrayList();
505 knownKeys.addAll(mStats.keySet());
506
507 // migrate all UID stats into special "removed" bucket
508 for (Key key : knownKeys) {
Jeff Sharkeydaa57e82012-09-19 14:10:39 -0700509 if (ArrayUtils.contains(uids, key.uid)) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800510 // only migrate combined TAG_NONE history
511 if (key.tag == TAG_NONE) {
512 final NetworkStatsHistory uidHistory = mStats.get(key);
513 final NetworkStatsHistory removedHistory = findOrCreateHistory(
514 key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
515 removedHistory.recordEntireHistory(uidHistory);
516 }
517 mStats.remove(key);
518 mDirty = true;
519 }
520 }
521 }
522
523 private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
524 if (startMillis < mStartMillis) mStartMillis = startMillis;
525 if (endMillis > mEndMillis) mEndMillis = endMillis;
526 mTotalBytes += totalBytes;
527 mDirty = true;
528 }
529
530 private int estimateBuckets() {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800531 return (int) (Math.min(mEndMillis - mStartMillis, WEEK_IN_MILLIS * 5)
Jeff Sharkey63abc372012-01-11 18:38:16 -0800532 / mBucketDuration);
533 }
534
535 public void dump(IndentingPrintWriter pw) {
536 final ArrayList<Key> keys = Lists.newArrayList();
537 keys.addAll(mStats.keySet());
538 Collections.sort(keys);
539
540 for (Key key : keys) {
541 pw.print("ident="); pw.print(key.ident.toString());
542 pw.print(" uid="); pw.print(key.uid);
543 pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
544 pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
545
546 final NetworkStatsHistory history = mStats.get(key);
547 pw.increaseIndent();
548 history.dump(pw, true);
549 pw.decreaseIndent();
550 }
551 }
552
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800553 public void dumpCheckin(PrintWriter pw, long start, long end) {
554 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
555 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
556 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateEthernet(), "eth");
557 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateBluetooth(), "bt");
558 }
559
560 /**
561 * Dump all contained stats that match requested parameters, but group
562 * together all matching {@link NetworkTemplate} under a single prefix.
563 */
564 private void dumpCheckin(PrintWriter pw, long start, long end, NetworkTemplate groupTemplate,
565 String groupPrefix) {
566 final ArrayMap<Key, NetworkStatsHistory> grouped = new ArrayMap<>();
567
568 // Walk through all history, grouping by matching network templates
569 for (int i = 0; i < mStats.size(); i++) {
570 final Key key = mStats.keyAt(i);
571 final NetworkStatsHistory value = mStats.valueAt(i);
572
573 if (!templateMatches(groupTemplate, key.ident)) continue;
Wenchao Tong98170b02015-03-17 16:14:23 -0700574 if (key.set >= NetworkStats.SET_DEBUG_START) continue;
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800575
576 final Key groupKey = new Key(null, key.uid, key.set, key.tag);
577 NetworkStatsHistory groupHistory = grouped.get(groupKey);
578 if (groupHistory == null) {
579 groupHistory = new NetworkStatsHistory(value.getBucketDuration());
580 grouped.put(groupKey, groupHistory);
581 }
582 groupHistory.recordHistory(value, start, end);
583 }
584
585 for (int i = 0; i < grouped.size(); i++) {
586 final Key key = grouped.keyAt(i);
587 final NetworkStatsHistory value = grouped.valueAt(i);
588
589 if (value.size() == 0) continue;
590
591 pw.print("c,");
592 pw.print(groupPrefix); pw.print(',');
593 pw.print(key.uid); pw.print(',');
594 pw.print(NetworkStats.setToCheckinString(key.set)); pw.print(',');
595 pw.print(key.tag);
596 pw.println();
597
598 value.dumpCheckin(pw);
599 }
600 }
601
Jeff Sharkey63abc372012-01-11 18:38:16 -0800602 /**
603 * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
604 * in the given {@link NetworkIdentitySet}.
605 */
606 private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
607 for (NetworkIdentity ident : identSet) {
608 if (template.matches(ident)) {
609 return true;
610 }
611 }
612 return false;
613 }
614
615 private static class Key implements Comparable<Key> {
616 public final NetworkIdentitySet ident;
617 public final int uid;
618 public final int set;
619 public final int tag;
620
621 private final int hashCode;
622
623 public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
624 this.ident = ident;
625 this.uid = uid;
626 this.set = set;
627 this.tag = tag;
Kenny Roote6585b32013-12-13 12:00:26 -0800628 hashCode = Objects.hash(ident, uid, set, tag);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800629 }
630
631 @Override
632 public int hashCode() {
633 return hashCode;
634 }
635
636 @Override
637 public boolean equals(Object obj) {
638 if (obj instanceof Key) {
639 final Key key = (Key) obj;
640 return uid == key.uid && set == key.set && tag == key.tag
Kenny Roote6585b32013-12-13 12:00:26 -0800641 && Objects.equals(ident, key.ident);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800642 }
643 return false;
644 }
645
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700646 @Override
Jeff Sharkey63abc372012-01-11 18:38:16 -0800647 public int compareTo(Key another) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800648 int res = 0;
649 if (ident != null && another.ident != null) {
650 res = ident.compareTo(another.ident);
651 }
652 if (res == 0) {
653 res = Integer.compare(uid, another.uid);
654 }
655 if (res == 0) {
656 res = Integer.compare(set, another.set);
657 }
658 if (res == 0) {
659 res = Integer.compare(tag, another.tag);
660 }
661 return res;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800662 }
663 }
664}