1/*
2 * Copyright (C) 2016 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.am;
18
19import android.os.SystemClock;
20import android.util.ArrayMap;
21import android.util.TimeUtils;
22
23import java.io.PrintWriter;
24import java.util.ArrayList;
25import java.util.Collections;
26import java.util.Comparator;
27
28public final class BroadcastStats {
29    final long mStartRealtime;
30    final long mStartUptime;
31    long mEndRealtime;
32    long mEndUptime;
33    final ArrayMap<String, ActionEntry> mActions = new ArrayMap<>();
34
35    static final Comparator<ActionEntry> ACTIONS_COMPARATOR = new Comparator<ActionEntry>() {
36        @Override public int compare(ActionEntry o1, ActionEntry o2) {
37            if (o1.mTotalDispatchTime < o2.mTotalDispatchTime) {
38                return -1;
39            }
40            if (o1.mTotalDispatchTime > o2.mTotalDispatchTime) {
41                return 1;
42            }
43            return 0;
44        }
45    };
46
47    static final class ActionEntry {
48        final String mAction;
49        final ArrayMap<String, PackageEntry> mPackages = new ArrayMap<>();
50        final ArrayMap<String, ViolationEntry> mBackgroundCheckViolations = new ArrayMap<>();
51        int mReceiveCount;
52        int mSkipCount;
53        long mTotalDispatchTime;
54        long mMaxDispatchTime;
55
56        ActionEntry(String action) {
57            mAction = action;
58        }
59    }
60
61    static final class PackageEntry {
62        int mSendCount;
63    }
64
65    static final class ViolationEntry {
66        int mCount;
67    }
68
69    public BroadcastStats() {
70        mStartRealtime = SystemClock.elapsedRealtime();
71        mStartUptime = SystemClock.uptimeMillis();
72    }
73
74    public void addBroadcast(String action, String srcPackage, int receiveCount,
75            int skipCount, long dispatchTime) {
76        ActionEntry ae = mActions.get(action);
77        if (ae == null) {
78            ae = new ActionEntry(action);
79            mActions.put(action, ae);
80        }
81        ae.mReceiveCount += receiveCount;
82        ae.mSkipCount += skipCount;
83        ae.mTotalDispatchTime += dispatchTime;
84        if (ae.mMaxDispatchTime < dispatchTime) {
85            ae.mMaxDispatchTime = dispatchTime;
86        }
87        PackageEntry pe = ae.mPackages.get(srcPackage);
88        if (pe == null) {
89            pe = new PackageEntry();
90            ae.mPackages.put(srcPackage, pe);
91        }
92        pe.mSendCount++;
93    }
94
95    public void addBackgroundCheckViolation(String action, String targetPackage) {
96        ActionEntry ae = mActions.get(action);
97        if (ae == null) {
98            ae = new ActionEntry(action);
99            mActions.put(action, ae);
100        }
101        ViolationEntry ve = ae.mBackgroundCheckViolations.get(targetPackage);
102        if (ve == null) {
103            ve = new ViolationEntry();
104            ae.mBackgroundCheckViolations.put(targetPackage, ve);
105        }
106        ve.mCount++;
107    }
108
109    public boolean dumpStats(PrintWriter pw, String prefix, String dumpPackage) {
110        boolean printedSomething = false;
111        ArrayList<ActionEntry> actions = new ArrayList<>(mActions.size());
112        for (int i=mActions.size()-1; i>=0; i--) {
113            actions.add(mActions.valueAt(i));
114        }
115        Collections.sort(actions, ACTIONS_COMPARATOR);
116        for (int i=actions.size()-1; i>=0; i--) {
117            ActionEntry ae = actions.get(i);
118            if (dumpPackage != null && !ae.mPackages.containsKey(dumpPackage)) {
119                continue;
120            }
121            printedSomething = true;
122            pw.print(prefix);
123            pw.print(ae.mAction);
124            pw.println(":");
125            pw.print(prefix);
126            pw.print("  Number received: ");
127            pw.print(ae.mReceiveCount);
128            pw.print(", skipped: ");
129            pw.println(ae.mSkipCount);
130            pw.print(prefix);
131            pw.print("  Total dispatch time: ");
132            TimeUtils.formatDuration(ae.mTotalDispatchTime, pw);
133            pw.print(", max: ");
134            TimeUtils.formatDuration(ae.mMaxDispatchTime, pw);
135            pw.println();
136            for (int j=ae.mPackages.size()-1; j>=0; j--) {
137                pw.print(prefix);
138                pw.print("  Package ");
139                pw.print(ae.mPackages.keyAt(j));
140                pw.print(": ");
141                PackageEntry pe = ae.mPackages.valueAt(j);
142                pw.print(pe.mSendCount);
143                pw.println(" times");
144            }
145            for (int j=ae.mBackgroundCheckViolations.size()-1; j>=0; j--) {
146                pw.print(prefix);
147                pw.print("  Bg Check Violation ");
148                pw.print(ae.mBackgroundCheckViolations.keyAt(j));
149                pw.print(": ");
150                ViolationEntry ve = ae.mBackgroundCheckViolations.valueAt(j);
151                pw.print(ve.mCount);
152                pw.println(" times");
153            }
154        }
155        return printedSomething;
156    }
157
158    public void dumpCheckinStats(PrintWriter pw, String dumpPackage) {
159        pw.print("broadcast-stats,1,");
160        pw.print(mStartRealtime);
161        pw.print(",");
162        pw.print(mEndRealtime == 0 ? SystemClock.elapsedRealtime() : mEndRealtime);
163        pw.print(",");
164        pw.println((mEndUptime == 0 ? SystemClock.uptimeMillis() : mEndUptime) - mStartUptime);
165        for (int i=mActions.size()-1; i>=0; i--) {
166            ActionEntry ae = mActions.valueAt(i);
167            if (dumpPackage != null && !ae.mPackages.containsKey(dumpPackage)) {
168                continue;
169            }
170            pw.print("a,");
171            pw.print(mActions.keyAt(i));
172            pw.print(",");
173            pw.print(ae.mReceiveCount);
174            pw.print(",");
175            pw.print(ae.mSkipCount);
176            pw.print(",");
177            pw.print(ae.mTotalDispatchTime);
178            pw.print(",");
179            pw.print(ae.mMaxDispatchTime);
180            pw.println();
181            for (int j=ae.mPackages.size()-1; j>=0; j--) {
182                pw.print("p,");
183                pw.print(ae.mPackages.keyAt(j));
184                PackageEntry pe = ae.mPackages.valueAt(j);
185                pw.print(",");
186                pw.print(pe.mSendCount);
187                pw.println();
188            }
189            for (int j=ae.mBackgroundCheckViolations.size()-1; j>=0; j--) {
190                pw.print("v,");
191                pw.print(ae.mBackgroundCheckViolations.keyAt(j));
192                ViolationEntry ve = ae.mBackgroundCheckViolations.valueAt(j);
193                pw.print(",");
194                pw.print(ve.mCount);
195                pw.println();
196            }
197        }
198    }
199}
200