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