1 /* 2 * Copyright (C) 2018 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.backup.transport; 18 19 import android.annotation.Nullable; 20 import android.content.ComponentName; 21 22 import java.io.PrintWriter; 23 import java.util.HashMap; 24 import java.util.Locale; 25 import java.util.Map; 26 import java.util.Optional; 27 28 /** Responsible for aggregating {@link TransportClient} relevant times. */ 29 public class TransportStats { 30 private final Object mStatsLock = new Object(); 31 private final Map<ComponentName, Stats> mTransportStats = new HashMap<>(); 32 registerConnectionTime(ComponentName transportComponent, long timeMs)33 void registerConnectionTime(ComponentName transportComponent, long timeMs) { 34 synchronized (mStatsLock) { 35 Stats stats = mTransportStats.get(transportComponent); 36 if (stats == null) { 37 stats = new Stats(); 38 mTransportStats.put(transportComponent, stats); 39 } 40 stats.register(timeMs); 41 } 42 } 43 44 /** Returns {@link Stats} for transport whose host service is {@code transportComponent}. */ 45 @Nullable getStatsForTransport(ComponentName transportComponent)46 public Stats getStatsForTransport(ComponentName transportComponent) { 47 synchronized (mStatsLock) { 48 Stats stats = mTransportStats.get(transportComponent); 49 if (stats == null) { 50 return null; 51 } 52 return new Stats(stats); 53 } 54 } 55 dump(PrintWriter pw)56 public void dump(PrintWriter pw) { 57 synchronized (mStatsLock) { 58 Optional<Stats> aggregatedStats = 59 mTransportStats.values().stream().reduce(Stats::merge); 60 if (aggregatedStats.isPresent()) { 61 dumpStats(pw, "", aggregatedStats.get()); 62 } 63 if (!mTransportStats.isEmpty()) { 64 pw.println("Per transport:"); 65 for (ComponentName transportComponent : mTransportStats.keySet()) { 66 Stats stats = mTransportStats.get(transportComponent); 67 pw.println(" " + transportComponent.flattenToShortString()); 68 dumpStats(pw, " ", stats); 69 } 70 } 71 } 72 } 73 dumpStats(PrintWriter pw, String prefix, Stats stats)74 private static void dumpStats(PrintWriter pw, String prefix, Stats stats) { 75 pw.println( 76 String.format( 77 Locale.US, "%sAverage connection time: %.2f ms", prefix, stats.average)); 78 pw.println(String.format(Locale.US, "%sMax connection time: %d ms", prefix, stats.max)); 79 pw.println(String.format(Locale.US, "%sMin connection time: %d ms", prefix, stats.min)); 80 pw.println(String.format(Locale.US, "%sNumber of connections: %d ", prefix, stats.n)); 81 } 82 83 public static final class Stats { merge(Stats a, Stats b)84 public static Stats merge(Stats a, Stats b) { 85 return new Stats( 86 a.n + b.n, 87 (a.average * a.n + b.average * b.n) / (a.n + b.n), 88 Math.max(a.max, b.max), 89 Math.min(a.min, b.min)); 90 } 91 92 public int n; 93 public double average; 94 public long max; 95 public long min; 96 Stats()97 public Stats() { 98 n = 0; 99 average = 0; 100 max = 0; 101 min = Long.MAX_VALUE; 102 } 103 Stats(int n, double average, long max, long min)104 private Stats(int n, double average, long max, long min) { 105 this.n = n; 106 this.average = average; 107 this.max = max; 108 this.min = min; 109 } 110 Stats(Stats original)111 private Stats(Stats original) { 112 this(original.n, original.average, original.max, original.min); 113 } 114 register(long sample)115 private void register(long sample) { 116 average = (average * n + sample) / (n + 1); 117 n++; 118 max = Math.max(max, sample); 119 min = Math.min(min, sample); 120 } 121 } 122 } 123