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 android.annotation.Nullable;
20 import android.car.vms.VmsLayer;
21 import android.util.ArrayMap;
22 import android.util.StatsLog;
23 
24 import com.android.internal.annotations.GuardedBy;
25 
26 import java.util.Collection;
27 import java.util.Map;
28 import java.util.concurrent.atomic.AtomicLong;
29 import java.util.stream.Collectors;
30 
31 /**
32  * Logger for per-client VMS statistics.
33  */
34 public class VmsClientLogger {
35     /**
36      * Constants used for identifying client connection states.
37      */
38     public static class ConnectionState {
39         // Attempting to connect to the client
40         public static final int CONNECTING =
41                 StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__CONNECTING;
42         // Client connection established
43         public static final int CONNECTED =
44                 StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__CONNECTED;
45         // Client connection closed unexpectedly
46         public static final int DISCONNECTED =
47                 StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__DISCONNECTED;
48         // Client connection closed by VMS
49         public static final int TERMINATED =
50                 StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__TERMINATED;
51         // Error establishing the client connection
52         public static final int CONNECTION_ERROR =
53                 StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__CONNECTION_ERROR;
54     }
55 
56     private final Object mLock = new Object();
57 
58     private final int mUid;
59     private final String mPackageName;
60 
61     @GuardedBy("mLock")
62     private Map<Integer, AtomicLong> mConnectionStateCounters = new ArrayMap<>();
63 
64     @GuardedBy("mLock")
65     private final Map<VmsLayer, VmsClientStats> mLayerStats = new ArrayMap<>();
66 
VmsClientLogger(int clientUid, @Nullable String clientPackage)67     VmsClientLogger(int clientUid, @Nullable String clientPackage) {
68         mUid = clientUid;
69         mPackageName = clientPackage != null ? clientPackage : "";
70     }
71 
getUid()72     public int getUid() {
73         return mUid;
74     }
75 
getPackageName()76     public String getPackageName() {
77         return mPackageName;
78     }
79 
80     /**
81      * Logs a connection state change for the client.
82      *
83      * @param connectionState New connection state
84      */
logConnectionState(int connectionState)85     public void logConnectionState(int connectionState) {
86         StatsLog.write(StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED,
87                 mUid, mPackageName, connectionState);
88 
89         AtomicLong counter;
90         synchronized (mLock) {
91             counter = mConnectionStateCounters.computeIfAbsent(connectionState,
92                     ignored -> new AtomicLong());
93         }
94         counter.incrementAndGet();
95     }
96 
getConnectionStateCount(int connectionState)97     long getConnectionStateCount(int connectionState) {
98         AtomicLong counter;
99         synchronized (mLock) {
100             counter = mConnectionStateCounters.get(connectionState);
101         }
102         return counter == null ? 0L : counter.get();
103     }
104 
105     /**
106      * Logs that a packet was published by the client.
107      *
108      * @param layer Layer of packet
109      * @param size Size of packet
110      */
logPacketSent(VmsLayer layer, long size)111     public void logPacketSent(VmsLayer layer, long size) {
112         getLayerEntry(layer).packetSent(size);
113     }
114 
115     /**
116      * Logs that a packet was received successfully by the client.
117      *
118      * @param layer Layer of packet
119      * @param size Size of packet
120      */
logPacketReceived(VmsLayer layer, long size)121     public void logPacketReceived(VmsLayer layer, long size) {
122         getLayerEntry(layer).packetReceived(size);
123     }
124 
125     /**
126      * Logs that a packet was dropped due to an error delivering to the client.
127      *
128      * @param layer Layer of packet
129      * @param size Size of packet
130      */
logPacketDropped(VmsLayer layer, long size)131     public void logPacketDropped(VmsLayer layer, long size) {
132         getLayerEntry(layer).packetDropped(size);
133     }
134 
getLayerEntries()135     Collection<VmsClientStats> getLayerEntries() {
136         synchronized (mLock) {
137             return mLayerStats.values().stream()
138                     .map(VmsClientStats::new) // Make a deep copy of the entries
139                     .collect(Collectors.toList());
140         }
141     }
142 
getLayerEntry(VmsLayer layer)143     private VmsClientStats getLayerEntry(VmsLayer layer) {
144         synchronized (mLock) {
145             return mLayerStats.computeIfAbsent(
146                     layer,
147                     (k) -> new VmsClientStats(mUid, layer));
148         }
149     }
150 }
151