• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.car.stats;
18 
19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
20 
21 import android.app.StatsManager;
22 import android.app.StatsManager.PullAtomMetadata;
23 import android.car.builtin.util.Slogf;
24 import android.content.Context;
25 import android.content.pm.PackageManager;
26 import android.util.SparseArray;
27 import android.util.StatsEvent;
28 
29 import com.android.car.CarLog;
30 import com.android.car.CarStatsLog;
31 import com.android.car.CarSystemService;
32 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
33 import com.android.car.internal.util.ConcurrentUtils;
34 import com.android.car.internal.util.IndentingPrintWriter;
35 import com.android.car.stats.VmsClientLogger.ConnectionState;
36 import com.android.internal.annotations.GuardedBy;
37 
38 import java.util.ArrayList;
39 import java.util.Comparator;
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.function.Function;
43 
44 /**
45  * Registers pulled atoms with statsd via StatsManager.
46  *
47  * Also implements collection and dumpsys reporting of atoms in CSV format.
48  */
49 public class CarStatsService implements CarSystemService {
50     private static final boolean DEBUG = false;
51     private static final String TAG = CarLog.tagFor(CarStatsService.class);
52     private static final String VMS_CONNECTION_STATS_DUMPSYS_HEADER =
53             "uid,packageName,attempts,connected,disconnected,terminated,errors";
54 
55     private static final Function<VmsClientLogger, String> VMS_CONNECTION_STATS_DUMPSYS_FORMAT =
56             entry -> String.format(Locale.US,
57                     "%d,%s,%d,%d,%d,%d,%d",
58                     entry.getUid(), entry.getPackageName(),
59                     entry.getConnectionStateCount(ConnectionState.CONNECTING),
60                     entry.getConnectionStateCount(ConnectionState.CONNECTED),
61                     entry.getConnectionStateCount(ConnectionState.DISCONNECTED),
62                     entry.getConnectionStateCount(ConnectionState.TERMINATED),
63                     entry.getConnectionStateCount(ConnectionState.CONNECTION_ERROR));
64 
65     private static final String VMS_CLIENT_STATS_DUMPSYS_HEADER =
66             "uid,layerType,layerChannel,layerVersion,"
67                     + "txBytes,txPackets,rxBytes,rxPackets,droppedBytes,droppedPackets";
68 
69     private static final Function<VmsClientStats, String> VMS_CLIENT_STATS_DUMPSYS_FORMAT =
70             entry -> String.format(
71                     "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
72                     entry.getUid(),
73                     entry.getLayerType(), entry.getLayerChannel(), entry.getLayerVersion(),
74                     entry.getTxBytes(), entry.getTxPackets(),
75                     entry.getRxBytes(), entry.getRxPackets(),
76                     entry.getDroppedBytes(), entry.getDroppedPackets());
77 
78     private static final Comparator<VmsClientStats> VMS_CLIENT_STATS_ORDER =
79             Comparator.comparingInt(VmsClientStats::getUid)
80                     .thenComparingInt(VmsClientStats::getLayerType)
81                     .thenComparingInt(VmsClientStats::getLayerChannel)
82                     .thenComparingInt(VmsClientStats::getLayerVersion);
83 
84     private final Context mContext;
85     private final PackageManager mPackageManager;
86     private final StatsManager mStatsManager;
87 
88     @GuardedBy("mVmsClientStats")
89     private final SparseArray<VmsClientLogger> mVmsClientStats = new SparseArray();
90 
CarStatsService(Context context)91     public CarStatsService(Context context) {
92         mContext = context;
93         mPackageManager = context.getPackageManager();
94         mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER);
95     }
96 
97     /**
98      * Registers VmsClientStats puller with StatsManager.
99      */
100     @Override
init()101     public void init() {
102         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
103                 .setAdditiveFields(new int[] {5, 6, 7, 8, 9, 10})
104                 .build();
105         mStatsManager.setPullAtomCallback(
106                 CarStatsLog.VMS_CLIENT_STATS,
107                 metadata,
108                 ConcurrentUtils.DIRECT_EXECUTOR,
109                 (atomTag, data) -> pullVmsClientStats(atomTag, data)
110         );
111     }
112 
113     @Override
release()114     public void release() {
115         mStatsManager.clearPullAtomCallback(CarStatsLog.VMS_CLIENT_STATS);
116     }
117 
118     /**
119      * Gets a logger for the VMS client with a given UID.
120      */
getVmsClientLogger(int clientUid)121     public VmsClientLogger getVmsClientLogger(int clientUid) {
122         synchronized (mVmsClientStats) {
123             if (!mVmsClientStats.contains(clientUid)) {
124                 String packageName = mPackageManager.getNameForUid(clientUid);
125                 if (DEBUG) {
126                     Slogf.d(TAG, "Created VmsClientLog: " + packageName);
127                 }
128                 mVmsClientStats.put(clientUid, new VmsClientLogger(clientUid, packageName));
129             }
130             return mVmsClientStats.get(clientUid);
131         }
132     }
133 
134     interface DumpVmsClientStats {
dump(VmsClientStats vmsClientStats)135         void dump(VmsClientStats vmsClientStats);
136     }
137 
138     @Override
139     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)140     public void dump(IndentingPrintWriter writer) {
141         synchronized (mVmsClientStats) {
142             writer.println(VMS_CONNECTION_STATS_DUMPSYS_HEADER);
143             List<VmsClientLogger> loggers = new ArrayList<>();
144             for (int index = 0; index < mVmsClientStats.size(); index++) {
145                 // Unknown UID will not have connection stats
146                 if (mVmsClientStats.valueAt(index).getUid() > 0) {
147                     loggers.add(mVmsClientStats.valueAt(index));
148                 }
149             }
150             loggers.sort(Comparator.comparingInt(VmsClientLogger::getUid));
151             for (int index = 0; index < loggers.size(); index++) {
152                 writer.println(VMS_CONNECTION_STATS_DUMPSYS_FORMAT.apply(loggers.get(index)));
153             }
154             writer.println();
155 
156             writer.println(VMS_CLIENT_STATS_DUMPSYS_HEADER);
157             dumpVmsClientStats(entry -> writer.println(
158                     VMS_CLIENT_STATS_DUMPSYS_FORMAT.apply(entry)));
159         }
160     }
161 
pullVmsClientStats(int atomTag, List<StatsEvent> pulledData)162     private int pullVmsClientStats(int atomTag, List<StatsEvent> pulledData) {
163         if (atomTag != CarStatsLog.VMS_CLIENT_STATS) {
164             Slogf.w(TAG, "Unexpected atom tag: " + atomTag);
165             return StatsManager.PULL_SKIP;
166         }
167 
168         dumpVmsClientStats((entry) -> {
169             StatsEvent e = CarStatsLog.buildStatsEvent(
170                     atomTag,
171                     entry.getUid(),
172                     entry.getLayerType(),
173                     entry.getLayerChannel(),
174                     entry.getLayerVersion(),
175                     entry.getTxBytes(),
176                     entry.getTxPackets(),
177                     entry.getRxBytes(),
178                     entry.getRxPackets(),
179                     entry.getDroppedBytes(),
180                     entry.getDroppedPackets()
181                     );
182             pulledData.add(e);
183         });
184         return StatsManager.PULL_SUCCESS;
185     }
186 
dumpVmsClientStats(DumpVmsClientStats dumpFn)187     private void dumpVmsClientStats(DumpVmsClientStats dumpFn) {
188         synchronized (mVmsClientStats) {
189             List<VmsClientStats> loggers = new ArrayList<>();
190             for (int index = 0; index < mVmsClientStats.size(); index++) {
191                 loggers.addAll(mVmsClientStats.valueAt(index).getLayerEntries());
192             }
193             loggers.sort(VMS_CLIENT_STATS_ORDER);
194             for (int index = 0; index < loggers.size(); index++) {
195                 dumpFn.dump(loggers.get(index));
196             }
197         }
198     }
199 }
200