blob: 03543007c80ed435887b88b05a64bda5ce2fda97 [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;
Makoto Onukida65a522017-01-13 10:23:30 -080037import android.service.NetworkStatsCollectionKeyProto;
38import android.service.NetworkStatsCollectionProto;
39import android.service.NetworkStatsCollectionStatsProto;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080040import android.util.ArrayMap;
Dianne Hackborn39606a02012-07-31 17:54:35 -070041import android.util.AtomicFile;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000042import android.util.IntArray;
Makoto Onukida65a522017-01-13 10:23:30 -080043import android.util.proto.ProtoOutputStream;
Jeff Sharkey63abc372012-01-11 18:38:16 -080044
Jeff Sharkeydaa57e82012-09-19 14:10:39 -070045import com.android.internal.util.ArrayUtils;
Jeff Sharkey63abc372012-01-11 18:38:16 -080046import com.android.internal.util.FileRotator;
47import com.android.internal.util.IndentingPrintWriter;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000048
Jeff Sharkey63abc372012-01-11 18:38:16 -080049import com.google.android.collect.Lists;
50import com.google.android.collect.Maps;
51
Jeff Davidson1efb1332015-12-09 18:04:50 -080052import libcore.io.IoUtils;
53
Jeff Sharkey63abc372012-01-11 18:38:16 -080054import java.io.BufferedInputStream;
55import java.io.DataInputStream;
56import java.io.DataOutputStream;
57import java.io.File;
58import java.io.FileNotFoundException;
59import java.io.IOException;
60import java.io.InputStream;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080061import java.io.PrintWriter;
Jeff Sharkey63abc372012-01-11 18:38:16 -080062import java.net.ProtocolException;
63import java.util.ArrayList;
64import java.util.Collections;
65import java.util.HashMap;
Kenny Roote6585b32013-12-13 12:00:26 -080066import java.util.Objects;
Jeff Sharkey63abc372012-01-11 18:38:16 -080067
Jeff Sharkey63abc372012-01-11 18:38:16 -080068/**
69 * Collection of {@link NetworkStatsHistory}, stored based on combined key of
70 * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
71 */
72public class NetworkStatsCollection implements FileRotator.Reader {
Jeff Sharkey63abc372012-01-11 18:38:16 -080073 /** File header magic number: "ANET" */
74 private static final int FILE_MAGIC = 0x414E4554;
75
76 private static final int VERSION_NETWORK_INIT = 1;
77
78 private static final int VERSION_UID_INIT = 1;
79 private static final int VERSION_UID_WITH_IDENT = 2;
80 private static final int VERSION_UID_WITH_TAG = 3;
81 private static final int VERSION_UID_WITH_SET = 4;
82
83 private static final int VERSION_UNIFIED_INIT = 16;
84
Jeff Sharkey55a442e2014-11-18 18:22:21 -080085 private ArrayMap<Key, NetworkStatsHistory> mStats = new ArrayMap<>();
Jeff Sharkey63abc372012-01-11 18:38:16 -080086
Jeff Sharkey70c70532012-05-16 14:51:19 -070087 private final long mBucketDuration;
Jeff Sharkey63abc372012-01-11 18:38:16 -080088
89 private long mStartMillis;
90 private long mEndMillis;
91 private long mTotalBytes;
92 private boolean mDirty;
93
94 public NetworkStatsCollection(long bucketDuration) {
95 mBucketDuration = bucketDuration;
96 reset();
97 }
98
99 public void reset() {
100 mStats.clear();
101 mStartMillis = Long.MAX_VALUE;
102 mEndMillis = Long.MIN_VALUE;
103 mTotalBytes = 0;
104 mDirty = false;
105 }
106
107 public long getStartMillis() {
108 return mStartMillis;
109 }
110
Jeff Sharkey70c70532012-05-16 14:51:19 -0700111 /**
112 * Return first atomic bucket in this collection, which is more conservative
113 * than {@link #mStartMillis}.
114 */
115 public long getFirstAtomicBucketMillis() {
116 if (mStartMillis == Long.MAX_VALUE) {
117 return Long.MAX_VALUE;
118 } else {
119 return mStartMillis + mBucketDuration;
120 }
121 }
122
Jeff Sharkey63abc372012-01-11 18:38:16 -0800123 public long getEndMillis() {
124 return mEndMillis;
125 }
126
127 public long getTotalBytes() {
128 return mTotalBytes;
129 }
130
131 public boolean isDirty() {
132 return mDirty;
133 }
134
135 public void clearDirty() {
136 mDirty = false;
137 }
138
139 public boolean isEmpty() {
140 return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
141 }
142
Jeff Davidson1efb1332015-12-09 18:04:50 -0800143 public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
Antonio Cansadocd42acd2016-02-17 13:03:38 -0800144 return getRelevantUids(accessLevel, Binder.getCallingUid());
145 }
146
147 public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
148 final int callerUid) {
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000149 IntArray uids = new IntArray();
150 for (int i = 0; i < mStats.size(); i++) {
151 final Key key = mStats.keyAt(i);
Jeff Davidson1efb1332015-12-09 18:04:50 -0800152 if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) {
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000153 int j = uids.binarySearch(key.uid);
154
155 if (j < 0) {
156 j = ~j;
157 uids.add(j, key.uid);
158 }
159 }
160 }
161 return uids.toArray();
162 }
163
Jeff Sharkey63abc372012-01-11 18:38:16 -0800164 /**
165 * Combine all {@link NetworkStatsHistory} in this collection which match
166 * the requested parameters.
167 */
168 public NetworkStatsHistory getHistory(
Jeff Davidson1efb1332015-12-09 18:04:50 -0800169 NetworkTemplate template, int uid, int set, int tag, int fields,
170 @NetworkStatsAccess.Level int accessLevel) {
171 return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE,
172 accessLevel);
Jeff Sharkey70c70532012-05-16 14:51:19 -0700173 }
174
175 /**
176 * Combine all {@link NetworkStatsHistory} in this collection which match
177 * the requested parameters.
178 */
179 public NetworkStatsHistory getHistory(
Jeff Davidson1efb1332015-12-09 18:04:50 -0800180 NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
181 @NetworkStatsAccess.Level int accessLevel) {
Antonio Cansadocd42acd2016-02-17 13:03:38 -0800182 return getHistory(template, uid, set, tag, fields, start, end, accessLevel,
183 Binder.getCallingUid());
184 }
185
186 /**
187 * Combine all {@link NetworkStatsHistory} in this collection which match
188 * the requested parameters.
189 */
190 public NetworkStatsHistory getHistory(
191 NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
192 @NetworkStatsAccess.Level int accessLevel, int callerUid) {
Jeff Davidson1efb1332015-12-09 18:04:50 -0800193 if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) {
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000194 throw new SecurityException("Network stats history of uid " + uid
195 + " is forbidden for caller " + callerUid);
196 }
197
Jeff Sharkey63abc372012-01-11 18:38:16 -0800198 final NetworkStatsHistory combined = new NetworkStatsHistory(
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000199 mBucketDuration, start == end ? 1 : estimateBuckets(), fields);
200
201 // shortcut when we know stats will be empty
202 if (start == end) return combined;
203
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800204 for (int i = 0; i < mStats.size(); i++) {
205 final Key key = mStats.keyAt(i);
Wenchao Tong98170b02015-03-17 16:14:23 -0700206 if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag
Jeff Sharkey63abc372012-01-11 18:38:16 -0800207 && templateMatches(template, key.ident)) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800208 final NetworkStatsHistory value = mStats.valueAt(i);
209 combined.recordHistory(value, start, end);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800210 }
211 }
212 return combined;
213 }
214
215 /**
216 * Summarize all {@link NetworkStatsHistory} in this collection which match
217 * the requested parameters.
218 */
Jeff Davidson1efb1332015-12-09 18:04:50 -0800219 public NetworkStats getSummary(NetworkTemplate template, long start, long end,
220 @NetworkStatsAccess.Level int accessLevel) {
Antonio Cansadocd42acd2016-02-17 13:03:38 -0800221 return getSummary(template, start, end, accessLevel, Binder.getCallingUid());
222 }
223
224 /**
225 * Summarize all {@link NetworkStatsHistory} in this collection which match
226 * the requested parameters.
227 */
228 public NetworkStats getSummary(NetworkTemplate template, long start, long end,
229 @NetworkStatsAccess.Level int accessLevel, int callerUid) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800230 final long now = System.currentTimeMillis();
231
232 final NetworkStats stats = new NetworkStats(end - start, 24);
Jeff Sharkey70c70532012-05-16 14:51:19 -0700233 // shortcut when we know stats will be empty
234 if (start == end) return stats;
235
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000236 final NetworkStats.Entry entry = new NetworkStats.Entry();
237 NetworkStatsHistory.Entry historyEntry = null;
238
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800239 for (int i = 0; i < mStats.size(); i++) {
240 final Key key = mStats.keyAt(i);
Jeff Davidson1efb1332015-12-09 18:04:50 -0800241 if (templateMatches(template, key.ident)
242 && NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)
Wenchao Tong98170b02015-03-17 16:14:23 -0700243 && key.set < NetworkStats.SET_DEBUG_START) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800244 final NetworkStatsHistory value = mStats.valueAt(i);
245 historyEntry = value.getValues(start, end, now, historyEntry);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800246
247 entry.iface = IFACE_ALL;
248 entry.uid = key.uid;
249 entry.set = key.set;
250 entry.tag = key.tag;
Stephen Chen25147872016-10-21 12:44:26 -0700251 entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO;
Jeff Davidson1f7e05e2016-03-10 13:21:38 -0800252 entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800253 entry.rxBytes = historyEntry.rxBytes;
254 entry.rxPackets = historyEntry.rxPackets;
255 entry.txBytes = historyEntry.txBytes;
256 entry.txPackets = historyEntry.txPackets;
257 entry.operations = historyEntry.operations;
258
259 if (!entry.isEmpty()) {
260 stats.combineValues(entry);
261 }
262 }
263 }
264
265 return stats;
266 }
267
268 /**
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700269 * Record given {@link android.net.NetworkStats.Entry} into this collection.
Jeff Sharkey63abc372012-01-11 18:38:16 -0800270 */
271 public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
272 long end, NetworkStats.Entry entry) {
Jeff Sharkey70c70532012-05-16 14:51:19 -0700273 final NetworkStatsHistory history = findOrCreateHistory(ident, uid, set, tag);
274 history.recordData(start, end, entry);
275 noteRecordedHistory(history.getStart(), history.getEnd(), entry.rxBytes + entry.txBytes);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800276 }
277
278 /**
279 * Record given {@link NetworkStatsHistory} into this collection.
280 */
281 private void recordHistory(Key key, NetworkStatsHistory history) {
282 if (history.size() == 0) return;
283 noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
284
Jeff Sharkeyac3fcb12012-05-02 18:11:52 -0700285 NetworkStatsHistory target = mStats.get(key);
286 if (target == null) {
287 target = new NetworkStatsHistory(history.getBucketDuration());
288 mStats.put(key, target);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800289 }
Jeff Sharkeyac3fcb12012-05-02 18:11:52 -0700290 target.recordEntireHistory(history);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800291 }
292
293 /**
294 * Record all {@link NetworkStatsHistory} contained in the given collection
295 * into this collection.
296 */
297 public void recordCollection(NetworkStatsCollection another) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800298 for (int i = 0; i < another.mStats.size(); i++) {
299 final Key key = another.mStats.keyAt(i);
300 final NetworkStatsHistory value = another.mStats.valueAt(i);
301 recordHistory(key, value);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800302 }
303 }
304
305 private NetworkStatsHistory findOrCreateHistory(
306 NetworkIdentitySet ident, int uid, int set, int tag) {
307 final Key key = new Key(ident, uid, set, tag);
308 final NetworkStatsHistory existing = mStats.get(key);
309
310 // update when no existing, or when bucket duration changed
311 NetworkStatsHistory updated = null;
312 if (existing == null) {
313 updated = new NetworkStatsHistory(mBucketDuration, 10);
314 } else if (existing.getBucketDuration() != mBucketDuration) {
315 updated = new NetworkStatsHistory(existing, mBucketDuration);
316 }
317
318 if (updated != null) {
319 mStats.put(key, updated);
320 return updated;
321 } else {
322 return existing;
323 }
324 }
325
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700326 @Override
Jeff Sharkey63abc372012-01-11 18:38:16 -0800327 public void read(InputStream in) throws IOException {
328 read(new DataInputStream(in));
329 }
330
331 public void read(DataInputStream in) throws IOException {
332 // verify file magic header intact
333 final int magic = in.readInt();
334 if (magic != FILE_MAGIC) {
335 throw new ProtocolException("unexpected magic: " + magic);
336 }
337
338 final int version = in.readInt();
339 switch (version) {
340 case VERSION_UNIFIED_INIT: {
341 // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
342 final int identSize = in.readInt();
343 for (int i = 0; i < identSize; i++) {
344 final NetworkIdentitySet ident = new NetworkIdentitySet(in);
345
346 final int size = in.readInt();
347 for (int j = 0; j < size; j++) {
348 final int uid = in.readInt();
349 final int set = in.readInt();
350 final int tag = in.readInt();
351
352 final Key key = new Key(ident, uid, set, tag);
353 final NetworkStatsHistory history = new NetworkStatsHistory(in);
354 recordHistory(key, history);
355 }
356 }
357 break;
358 }
359 default: {
360 throw new ProtocolException("unexpected version: " + version);
361 }
362 }
363 }
364
365 public void write(DataOutputStream out) throws IOException {
366 // cluster key lists grouped by ident
367 final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
368 for (Key key : mStats.keySet()) {
369 ArrayList<Key> keys = keysByIdent.get(key.ident);
370 if (keys == null) {
371 keys = Lists.newArrayList();
372 keysByIdent.put(key.ident, keys);
373 }
374 keys.add(key);
375 }
376
377 out.writeInt(FILE_MAGIC);
378 out.writeInt(VERSION_UNIFIED_INIT);
379
380 out.writeInt(keysByIdent.size());
381 for (NetworkIdentitySet ident : keysByIdent.keySet()) {
382 final ArrayList<Key> keys = keysByIdent.get(ident);
383 ident.writeToStream(out);
384
385 out.writeInt(keys.size());
386 for (Key key : keys) {
387 final NetworkStatsHistory history = mStats.get(key);
388 out.writeInt(key.uid);
389 out.writeInt(key.set);
390 out.writeInt(key.tag);
391 history.writeToStream(out);
392 }
393 }
394
395 out.flush();
396 }
397
398 @Deprecated
399 public void readLegacyNetwork(File file) throws IOException {
400 final AtomicFile inputFile = new AtomicFile(file);
401
402 DataInputStream in = null;
403 try {
404 in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
405
406 // verify file magic header intact
407 final int magic = in.readInt();
408 if (magic != FILE_MAGIC) {
409 throw new ProtocolException("unexpected magic: " + magic);
410 }
411
412 final int version = in.readInt();
413 switch (version) {
414 case VERSION_NETWORK_INIT: {
415 // network := size *(NetworkIdentitySet NetworkStatsHistory)
416 final int size = in.readInt();
417 for (int i = 0; i < size; i++) {
418 final NetworkIdentitySet ident = new NetworkIdentitySet(in);
419 final NetworkStatsHistory history = new NetworkStatsHistory(in);
420
421 final Key key = new Key(ident, UID_ALL, SET_ALL, TAG_NONE);
422 recordHistory(key, history);
423 }
424 break;
425 }
426 default: {
427 throw new ProtocolException("unexpected version: " + version);
428 }
429 }
430 } catch (FileNotFoundException e) {
431 // missing stats is okay, probably first boot
432 } finally {
433 IoUtils.closeQuietly(in);
434 }
435 }
436
437 @Deprecated
438 public void readLegacyUid(File file, boolean onlyTags) throws IOException {
439 final AtomicFile inputFile = new AtomicFile(file);
440
441 DataInputStream in = null;
442 try {
443 in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
444
445 // verify file magic header intact
446 final int magic = in.readInt();
447 if (magic != FILE_MAGIC) {
448 throw new ProtocolException("unexpected magic: " + magic);
449 }
450
451 final int version = in.readInt();
452 switch (version) {
453 case VERSION_UID_INIT: {
454 // uid := size *(UID NetworkStatsHistory)
455
456 // drop this data version, since we don't have a good
457 // mapping into NetworkIdentitySet.
458 break;
459 }
460 case VERSION_UID_WITH_IDENT: {
461 // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
462
463 // drop this data version, since this version only existed
464 // for a short time.
465 break;
466 }
467 case VERSION_UID_WITH_TAG:
468 case VERSION_UID_WITH_SET: {
469 // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
470 final int identSize = in.readInt();
471 for (int i = 0; i < identSize; i++) {
472 final NetworkIdentitySet ident = new NetworkIdentitySet(in);
473
474 final int size = in.readInt();
475 for (int j = 0; j < size; j++) {
476 final int uid = in.readInt();
477 final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
478 : SET_DEFAULT;
479 final int tag = in.readInt();
480
481 final Key key = new Key(ident, uid, set, tag);
482 final NetworkStatsHistory history = new NetworkStatsHistory(in);
483
484 if ((tag == TAG_NONE) != onlyTags) {
485 recordHistory(key, history);
486 }
487 }
488 }
489 break;
490 }
491 default: {
492 throw new ProtocolException("unexpected version: " + version);
493 }
494 }
495 } catch (FileNotFoundException e) {
496 // missing stats is okay, probably first boot
497 } finally {
498 IoUtils.closeQuietly(in);
499 }
500 }
501
502 /**
503 * Remove any {@link NetworkStatsHistory} attributed to the requested UID,
504 * moving any {@link NetworkStats#TAG_NONE} series to
505 * {@link TrafficStats#UID_REMOVED}.
506 */
Jeff Sharkeydaa57e82012-09-19 14:10:39 -0700507 public void removeUids(int[] uids) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800508 final ArrayList<Key> knownKeys = Lists.newArrayList();
509 knownKeys.addAll(mStats.keySet());
510
511 // migrate all UID stats into special "removed" bucket
512 for (Key key : knownKeys) {
Jeff Sharkeydaa57e82012-09-19 14:10:39 -0700513 if (ArrayUtils.contains(uids, key.uid)) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800514 // only migrate combined TAG_NONE history
515 if (key.tag == TAG_NONE) {
516 final NetworkStatsHistory uidHistory = mStats.get(key);
517 final NetworkStatsHistory removedHistory = findOrCreateHistory(
518 key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
519 removedHistory.recordEntireHistory(uidHistory);
520 }
521 mStats.remove(key);
522 mDirty = true;
523 }
524 }
525 }
526
527 private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
528 if (startMillis < mStartMillis) mStartMillis = startMillis;
529 if (endMillis > mEndMillis) mEndMillis = endMillis;
530 mTotalBytes += totalBytes;
531 mDirty = true;
532 }
533
534 private int estimateBuckets() {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800535 return (int) (Math.min(mEndMillis - mStartMillis, WEEK_IN_MILLIS * 5)
Jeff Sharkey63abc372012-01-11 18:38:16 -0800536 / mBucketDuration);
537 }
538
Makoto Onukida65a522017-01-13 10:23:30 -0800539 private ArrayList<Key> getSortedKeys() {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800540 final ArrayList<Key> keys = Lists.newArrayList();
541 keys.addAll(mStats.keySet());
542 Collections.sort(keys);
Makoto Onukida65a522017-01-13 10:23:30 -0800543 return keys;
544 }
Jeff Sharkey63abc372012-01-11 18:38:16 -0800545
Makoto Onukida65a522017-01-13 10:23:30 -0800546 public void dump(IndentingPrintWriter pw) {
547 for (Key key : getSortedKeys()) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800548 pw.print("ident="); pw.print(key.ident.toString());
549 pw.print(" uid="); pw.print(key.uid);
550 pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
551 pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
552
553 final NetworkStatsHistory history = mStats.get(key);
554 pw.increaseIndent();
555 history.dump(pw, true);
556 pw.decreaseIndent();
557 }
558 }
559
Makoto Onukida65a522017-01-13 10:23:30 -0800560 public void writeToProto(ProtoOutputStream proto, long tag) {
561 final long start = proto.start(tag);
562
563 for (Key key : getSortedKeys()) {
564 final long startStats = proto.start(NetworkStatsCollectionProto.STATS);
565
566 // Key
567 final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY);
568 key.ident.writeToProto(proto, NetworkStatsCollectionKeyProto.IDENTITY);
569 proto.write(NetworkStatsCollectionKeyProto.UID, key.uid);
570 proto.write(NetworkStatsCollectionKeyProto.SET, key.set);
571 proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag);
572 proto.end(startKey);
573
574 // Value
575 final NetworkStatsHistory history = mStats.get(key);
576 history.writeToProto(proto, NetworkStatsCollectionStatsProto.HISTORY);
577 proto.end(startStats);
578 }
579
580 proto.end(start);
581 }
582
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800583 public void dumpCheckin(PrintWriter pw, long start, long end) {
584 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
585 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
586 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateEthernet(), "eth");
587 dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateBluetooth(), "bt");
588 }
589
590 /**
591 * Dump all contained stats that match requested parameters, but group
592 * together all matching {@link NetworkTemplate} under a single prefix.
593 */
594 private void dumpCheckin(PrintWriter pw, long start, long end, NetworkTemplate groupTemplate,
595 String groupPrefix) {
596 final ArrayMap<Key, NetworkStatsHistory> grouped = new ArrayMap<>();
597
598 // Walk through all history, grouping by matching network templates
599 for (int i = 0; i < mStats.size(); i++) {
600 final Key key = mStats.keyAt(i);
601 final NetworkStatsHistory value = mStats.valueAt(i);
602
603 if (!templateMatches(groupTemplate, key.ident)) continue;
Wenchao Tong98170b02015-03-17 16:14:23 -0700604 if (key.set >= NetworkStats.SET_DEBUG_START) continue;
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800605
606 final Key groupKey = new Key(null, key.uid, key.set, key.tag);
607 NetworkStatsHistory groupHistory = grouped.get(groupKey);
608 if (groupHistory == null) {
609 groupHistory = new NetworkStatsHistory(value.getBucketDuration());
610 grouped.put(groupKey, groupHistory);
611 }
612 groupHistory.recordHistory(value, start, end);
613 }
614
615 for (int i = 0; i < grouped.size(); i++) {
616 final Key key = grouped.keyAt(i);
617 final NetworkStatsHistory value = grouped.valueAt(i);
618
619 if (value.size() == 0) continue;
620
621 pw.print("c,");
622 pw.print(groupPrefix); pw.print(',');
623 pw.print(key.uid); pw.print(',');
624 pw.print(NetworkStats.setToCheckinString(key.set)); pw.print(',');
625 pw.print(key.tag);
626 pw.println();
627
628 value.dumpCheckin(pw);
629 }
630 }
631
Jeff Sharkey63abc372012-01-11 18:38:16 -0800632 /**
633 * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
634 * in the given {@link NetworkIdentitySet}.
635 */
636 private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
637 for (NetworkIdentity ident : identSet) {
638 if (template.matches(ident)) {
639 return true;
640 }
641 }
642 return false;
643 }
644
645 private static class Key implements Comparable<Key> {
646 public final NetworkIdentitySet ident;
647 public final int uid;
648 public final int set;
649 public final int tag;
650
651 private final int hashCode;
652
653 public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
654 this.ident = ident;
655 this.uid = uid;
656 this.set = set;
657 this.tag = tag;
Kenny Roote6585b32013-12-13 12:00:26 -0800658 hashCode = Objects.hash(ident, uid, set, tag);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800659 }
660
661 @Override
662 public int hashCode() {
663 return hashCode;
664 }
665
666 @Override
667 public boolean equals(Object obj) {
668 if (obj instanceof Key) {
669 final Key key = (Key) obj;
670 return uid == key.uid && set == key.set && tag == key.tag
Kenny Roote6585b32013-12-13 12:00:26 -0800671 && Objects.equals(ident, key.ident);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800672 }
673 return false;
674 }
675
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700676 @Override
Jeff Sharkey63abc372012-01-11 18:38:16 -0800677 public int compareTo(Key another) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800678 int res = 0;
679 if (ident != null && another.ident != null) {
680 res = ident.compareTo(another.ident);
681 }
682 if (res == 0) {
683 res = Integer.compare(uid, another.uid);
684 }
685 if (res == 0) {
686 res = Integer.compare(set, another.set);
687 }
688 if (res == 0) {
689 res = Integer.compare(tag, another.tag);
690 }
691 return res;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800692 }
693 }
694}