• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.CALL_AUDIO_ROUTE_STATS;
20 import static com.android.server.telecom.TelecomStatsLog.CALL_STATS;
21 import static com.android.server.telecom.TelecomStatsLog.TELECOM_API_STATS;
22 import static com.android.server.telecom.TelecomStatsLog.TELECOM_ERROR_STATS;
23 import static com.android.server.telecom.TelecomStatsLog.TELECOM_EVENT_STATS;
24 
25 import android.annotation.NonNull;
26 import android.app.StatsManager;
27 import android.content.Context;
28 import android.os.Binder;
29 import android.os.HandlerThread;
30 import android.telecom.Log;
31 import android.util.StatsEvent;
32 
33 import androidx.annotation.VisibleForTesting;
34 
35 import com.android.modules.utils.HandlerExecutor;
36 
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Objects;
40 import java.util.concurrent.ConcurrentHashMap;
41 import java.util.concurrent.atomic.AtomicBoolean;
42 
43 public class TelecomMetricsController implements StatsManager.StatsPullAtomCallback {
44 
45     private static final String TAG = TelecomMetricsController.class.getSimpleName();
46 
47     private final Context mContext;
48     private final HandlerThread mHandlerThread;
49     private final ConcurrentHashMap<Integer, TelecomPulledAtom> mStats = new ConcurrentHashMap<>();
50     private final AtomicBoolean mIsTestMode = new AtomicBoolean(false);
51 
TelecomMetricsController(@onNull Context context, @NonNull HandlerThread handlerThread)52     private TelecomMetricsController(@NonNull Context context,
53                                      @NonNull HandlerThread handlerThread) {
54         mContext = context;
55         mHandlerThread = handlerThread;
56     }
57 
58     @NonNull
make(@onNull Context context)59     public static TelecomMetricsController make(@NonNull Context context) {
60         Log.i(TAG, "TMC.m1");
61         HandlerThread handlerThread = new HandlerThread(TAG);
62         handlerThread.start();
63         return make(context, handlerThread);
64     }
65 
66     @VisibleForTesting
67     @NonNull
make(@onNull Context context, @NonNull HandlerThread handlerThread)68     public static TelecomMetricsController make(@NonNull Context context,
69                                                 @NonNull HandlerThread handlerThread) {
70         Log.i(TAG, "TMC.m2");
71         Objects.requireNonNull(context);
72         Objects.requireNonNull(handlerThread);
73         return new TelecomMetricsController(context, handlerThread);
74     }
75 
76     @NonNull
getApiStats()77     public ApiStats getApiStats() {
78         ApiStats stats = (ApiStats) mStats.get(TELECOM_API_STATS);
79         if (stats == null) {
80             long token = Binder.clearCallingIdentity();
81             try {
82                 stats = new ApiStats(mContext, mHandlerThread.getLooper(), isTestMode());
83                 registerAtom(stats.getTag(), stats);
84             } finally {
85                 Binder.restoreCallingIdentity(token);
86             }
87         }
88         return stats;
89     }
90 
91     @NonNull
getAudioRouteStats()92     public AudioRouteStats getAudioRouteStats() {
93         AudioRouteStats stats = (AudioRouteStats) mStats.get(CALL_AUDIO_ROUTE_STATS);
94         if (stats == null) {
95             stats = new AudioRouteStats(mContext, mHandlerThread.getLooper(), isTestMode());
96             registerAtom(stats.getTag(), stats);
97         }
98         return stats;
99     }
100 
101     @NonNull
getCallStats()102     public CallStats getCallStats() {
103         CallStats stats = (CallStats) mStats.get(CALL_STATS);
104         if (stats == null) {
105             stats = new CallStats(mContext, mHandlerThread.getLooper(), isTestMode());
106             registerAtom(stats.getTag(), stats);
107         }
108         return stats;
109     }
110 
111     @NonNull
getErrorStats()112     public ErrorStats getErrorStats() {
113         ErrorStats stats = (ErrorStats) mStats.get(TELECOM_ERROR_STATS);
114         if (stats == null) {
115             stats = new ErrorStats(mContext, mHandlerThread.getLooper(), isTestMode());
116             registerAtom(stats.getTag(), stats);
117         }
118         return stats;
119     }
120 
121     @NonNull
getEventStats()122     public EventStats getEventStats() {
123         EventStats stats = (EventStats) mStats.get(TELECOM_EVENT_STATS);
124         if (stats == null) {
125             stats = new EventStats(mContext, mHandlerThread.getLooper(), isTestMode());
126             registerAtom(stats.getTag(), stats);
127         }
128         return stats;
129     }
130 
131     @Override
onPullAtom(final int atomTag, final List<StatsEvent> data)132     public int onPullAtom(final int atomTag, final List<StatsEvent> data) {
133         if (mStats.containsKey(atomTag)) {
134             return Objects.requireNonNull(mStats.get(atomTag)).pull(data);
135         }
136         return StatsManager.PULL_SKIP;
137     }
138 
139     @VisibleForTesting
getStats()140     public Map<Integer, TelecomPulledAtom> getStats() {
141         return mStats;
142     }
143 
144     @VisibleForTesting
registerAtom(int tag, TelecomPulledAtom atom)145     public void registerAtom(int tag, TelecomPulledAtom atom) {
146         final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
147         if (statsManager != null) {
148             statsManager.setPullAtomCallback(tag, null, new HandlerExecutor(atom), this);
149             mStats.put(tag, atom);
150         } else {
151             Log.w(TAG, "Unable to register the pulled atom as StatsManager is null");
152         }
153     }
154 
destroy()155     public void destroy() {
156         clearStats();
157         mHandlerThread.quitSafely();
158     }
159 
setTestMode(boolean enabled)160     public void setTestMode(boolean enabled) {
161         mIsTestMode.set(enabled);
162         clearStats();
163     }
164 
isTestMode()165     public boolean isTestMode() {
166         return mIsTestMode.get();
167     }
168 
clearStats()169     private void clearStats() {
170         final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
171         if (statsManager != null) {
172             mStats.forEach((tag, stat) -> {
173                 statsManager.clearPullAtomCallback(tag);
174                 stat.flush();
175             });
176         } else {
177             Log.w(TAG, "Unable to clear pulled atoms as StatsManager is null");
178         }
179 
180         mStats.clear();
181     }
182 }
183