• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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.telecom.metrics;
18 
19 import static com.android.server.telecom.TelecomStatsLog.TELECOM_EVENT_STATS;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.app.StatsManager;
24 import android.content.Context;
25 import android.os.Looper;
26 import android.telecom.CallException;
27 import android.telecom.Log;
28 import android.util.StatsEvent;
29 
30 import androidx.annotation.VisibleForTesting;
31 
32 import com.android.server.telecom.TelecomStatsLog;
33 import com.android.server.telecom.metrics.ApiStats.ApiEvent;
34 import com.android.server.telecom.nano.PulledAtomsClass;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.util.Arrays;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Objects;
43 
44 public class EventStats extends TelecomPulledAtom {
45     public static final int ID_UNKNOWN = TelecomStatsLog.TELECOM_EVENT_STATS__EVENT__EVENT_UNKNOWN;
46     public static final int ID_INIT = TelecomStatsLog.TELECOM_EVENT_STATS__EVENT__EVENT_INIT;
47     public static final int ID_DEFAULT_DIALER_CHANGED = TelecomStatsLog
48             .TELECOM_EVENT_STATS__EVENT__EVENT_DEFAULT_DIALER_CHANGED;
49     public static final int ID_ADD_CALL = TelecomStatsLog
50             .TELECOM_EVENT_STATS__EVENT__EVENT_ADD_CALL;
51 
52     public static final int CAUSE_UNKNOWN = TelecomStatsLog
53             .TELECOM_EVENT_STATS__EVENT_CAUSE__CAUSE_UNKNOWN;
54     public static final int CAUSE_GENERIC_SUCCESS = TelecomStatsLog
55             .TELECOM_EVENT_STATS__EVENT_CAUSE__CAUSE_GENERIC_SUCCESS;
56     public static final int CAUSE_GENERIC_FAILURE = TelecomStatsLog
57             .TELECOM_EVENT_STATS__EVENT_CAUSE__CAUSE_GENERIC_FAILURE;
58     public static final int CAUSE_CALL_TRANSACTION_SUCCESS = TelecomStatsLog
59             .TELECOM_EVENT_STATS__EVENT_CAUSE__CALL_TRANSACTION_SUCCESS;
60     public static final int CAUSE_CALL_TRANSACTION_BASE = CAUSE_CALL_TRANSACTION_SUCCESS;
61     public static final int CAUSE_CALL_TRANSACTION_ERROR_UNKNOWN =
62             CAUSE_CALL_TRANSACTION_BASE + CallException.CODE_ERROR_UNKNOWN;
63     public static final int CAUSE_CALL_TRANSACTION_CANNOT_HOLD_CURRENT_ACTIVE_CALL =
64             CAUSE_CALL_TRANSACTION_BASE + CallException.CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL;
65     public static final int CAUSE_CALL_TRANSACTION_CALL_IS_NOT_BEING_TRACKED =
66             CAUSE_CALL_TRANSACTION_BASE + CallException.CODE_CALL_IS_NOT_BEING_TRACKED;
67     public static final int CAUSE_CALL_TRANSACTION_CALL_CANNOT_BE_SET_TO_ACTIVE =
68             CAUSE_CALL_TRANSACTION_BASE + CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE;
69     public static final int CAUSE_CALL_TRANSACTION_CALL_NOT_PERMITTED_AT_PRESENT_TIME =
70             CAUSE_CALL_TRANSACTION_BASE + CallException.CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME;
71     public static final int CAUSE_CALL_TRANSACTION_OPERATION_TIMED_OUT =
72             CAUSE_CALL_TRANSACTION_BASE + CallException.CODE_OPERATION_TIMED_OUT;
73     private static final String TAG = EventStats.class.getSimpleName();
74     private static final String FILE_NAME = "event_stats";
75     private Map<CriticalEvent, Integer> mEventStatsMap;
76 
EventStats(@onNull Context context, @NonNull Looper looper, boolean isTestMode)77     public EventStats(@NonNull Context context, @NonNull Looper looper,
78                       boolean isTestMode) {
79         super(context, looper, isTestMode);
80     }
81 
82     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
83     @Override
getTag()84     public int getTag() {
85         return TELECOM_EVENT_STATS;
86     }
87 
88     @Override
getFileName()89     protected String getFileName() {
90         return FILE_NAME;
91     }
92 
93     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
94     @Override
onPull(final List<StatsEvent> data)95     public synchronized int onPull(final List<StatsEvent> data) {
96         if (mPulledAtoms.telecomEventStats.length != 0) {
97             Arrays.stream(mPulledAtoms.telecomEventStats).forEach(v -> data.add(
98                     TelecomStatsLog.buildStatsEvent(getTag(),
99                             v.getEvent(), v.getUid(), v.getEventCause(), v.getCount())));
100             mEventStatsMap.clear();
101             onAggregate();
102             return StatsManager.PULL_SUCCESS;
103         } else {
104             return StatsManager.PULL_SKIP;
105         }
106     }
107 
108     @Override
onLoad()109     protected synchronized void onLoad() {
110         if (mPulledAtoms.telecomEventStats != null) {
111             mEventStatsMap = new HashMap<>();
112             for (PulledAtomsClass.TelecomEventStats v : mPulledAtoms.telecomEventStats) {
113                 mEventStatsMap.put(new CriticalEvent(v.getEvent(), v.getUid(),
114                         v.getEventCause()), v.getCount());
115             }
116             mLastPulledTimestamps = mPulledAtoms.getTelecomEventStatsPullTimestampMillis();
117         }
118     }
119 
120     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
121     @Override
onAggregate()122     public synchronized void onAggregate() {
123         Log.d(TAG, "onAggregate: %s", mEventStatsMap);
124         clearAtoms();
125         if (mEventStatsMap.isEmpty()) {
126             return;
127         }
128         mPulledAtoms.setTelecomEventStatsPullTimestampMillis(mLastPulledTimestamps);
129         mPulledAtoms.telecomEventStats =
130                 new PulledAtomsClass.TelecomEventStats[mEventStatsMap.size()];
131         int[] index = new int[1];
132         mEventStatsMap.forEach((k, v) -> {
133             mPulledAtoms.telecomEventStats[index[0]] = new PulledAtomsClass.TelecomEventStats();
134             mPulledAtoms.telecomEventStats[index[0]].setEvent(k.mId);
135             mPulledAtoms.telecomEventStats[index[0]].setUid(k.mUid);
136             mPulledAtoms.telecomEventStats[index[0]].setEventCause(k.mCause);
137             mPulledAtoms.telecomEventStats[index[0]].setCount(v);
138             index[0]++;
139         });
140         save(DELAY_FOR_PERSISTENT_MILLIS);
141     }
142 
log(@onNull CriticalEvent event)143     public void log(@NonNull CriticalEvent event) {
144         post(() -> {
145             mEventStatsMap.put(event, mEventStatsMap.getOrDefault(event, 0) + 1);
146             onAggregate();
147         });
148     }
149 
150     @IntDef(prefix = "ID_", value = {
151             ID_UNKNOWN,
152             ID_INIT,
153             ID_DEFAULT_DIALER_CHANGED,
154             ID_ADD_CALL
155     })
156     @Retention(RetentionPolicy.SOURCE)
157     public @interface EventId {
158     }
159 
160     @IntDef(prefix = "CAUSE_", value = {
161             CAUSE_UNKNOWN,
162             CAUSE_GENERIC_SUCCESS,
163             CAUSE_GENERIC_FAILURE,
164             CAUSE_CALL_TRANSACTION_SUCCESS,
165             CAUSE_CALL_TRANSACTION_ERROR_UNKNOWN,
166             CAUSE_CALL_TRANSACTION_CANNOT_HOLD_CURRENT_ACTIVE_CALL,
167             CAUSE_CALL_TRANSACTION_CALL_IS_NOT_BEING_TRACKED,
168             CAUSE_CALL_TRANSACTION_CALL_CANNOT_BE_SET_TO_ACTIVE,
169             CAUSE_CALL_TRANSACTION_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
170             CAUSE_CALL_TRANSACTION_OPERATION_TIMED_OUT
171     })
172     @Retention(RetentionPolicy.SOURCE)
173     public @interface CauseId {
174     }
175 
176     public static class CriticalEvent {
177 
178         @EventId
179         int mId;
180         int mUid;
181         @CauseId
182         int mCause;
183 
CriticalEvent(@ventId int id, int uid, @CauseId int cause)184         public CriticalEvent(@EventId int id, int uid, @CauseId int cause) {
185             mId = id;
186             mUid = uid;
187             mCause = cause;
188         }
189 
setUid(int uid)190         public void setUid(int uid) {
191             this.mUid = uid;
192         }
193 
setResult(@auseId int result)194         public void setResult(@CauseId int result) {
195             this.mCause = result;
196         }
197 
198         @Override
equals(Object other)199         public boolean equals(Object other) {
200             if (this == other) {
201                 return true;
202             }
203             if (!(other instanceof ApiEvent obj)) {
204                 return false;
205             }
206             return this.mId == obj.mId && this.mUid == obj.mCallerUid
207                     && this.mCause == obj.mResult;
208         }
209 
210         @Override
hashCode()211         public int hashCode() {
212             return Objects.hash(mId, mUid, mCause);
213         }
214 
215         @Override
toString()216         public String toString() {
217             return "[CriticalEvent: mId=" + mId + ", m"
218                     + "Uid=" + mUid
219                     + ", mResult=" + mCause + "]";
220         }
221     }
222 
223 
224 }
225