blob: dbe1a2968fec4dc92f657ff36c79ea21c833ca7b [file] [log] [blame]
Hall Liu32587202015-11-18 11:10:08 -08001/*
2 * Copyright (C) 2015 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.telecom;
18
Hall Liud7fe6862016-09-09 16:36:14 -070019import android.telecom.Connection;
Hall Liu32587202015-11-18 11:10:08 -080020import android.telecom.DisconnectCause;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070021import android.telecom.Logging.EventManager;
Hall Liu874c0f82016-04-29 18:13:18 -070022import android.telecom.ParcelableCallAnalytics;
23import android.telecom.TelecomAnalytics;
Hall Liu2f4f0a02016-08-08 17:23:20 -070024import android.util.Base64;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070025import android.telecom.Log;
Hall Liu32587202015-11-18 11:10:08 -080026
27import com.android.internal.annotations.VisibleForTesting;
28import com.android.internal.util.IndentingPrintWriter;
Tamas Berghammer83e88282016-09-22 16:23:24 +010029import com.android.server.telecom.nano.TelecomLogClass;
Hall Liu32587202015-11-18 11:10:08 -080030
Hall Liu2f4f0a02016-08-08 17:23:20 -070031import java.io.PrintWriter;
Hall Liu874c0f82016-04-29 18:13:18 -070032import java.util.ArrayList;
Hall Liu2f4f0a02016-08-08 17:23:20 -070033import java.util.Arrays;
Hall Liu874c0f82016-04-29 18:13:18 -070034import java.util.Collections;
Hall Liu32587202015-11-18 11:10:08 -080035import java.util.HashMap;
Hall Liu874c0f82016-04-29 18:13:18 -070036import java.util.LinkedList;
37import java.util.List;
Hall Liu32587202015-11-18 11:10:08 -080038import java.util.Map;
Hall Liud93b9ec2017-01-05 18:06:30 -080039import java.util.PriorityQueue;
Hall Liu874c0f82016-04-29 18:13:18 -070040import java.util.stream.Collectors;
41
42import static android.telecom.ParcelableCallAnalytics.AnalyticsEvent;
43import static android.telecom.TelecomAnalytics.SessionTiming;
Hall Liu32587202015-11-18 11:10:08 -080044
45/**
46 * A class that collects and stores data on how calls are being made, in order to
47 * aggregate these into useful statistics.
48 */
49public class Analytics {
Hall Liu2f4f0a02016-08-08 17:23:20 -070050 public static final String ANALYTICS_DUMPSYS_ARG = "analytics";
51 private static final String CLEAR_ANALYTICS_ARG = "clear";
52
Hall Liu874c0f82016-04-29 18:13:18 -070053 public static final Map<String, Integer> sLogEventToAnalyticsEvent =
54 new HashMap<String, Integer>() {{
Brad Ebingera3eccfe2016-10-05 15:45:22 -070055 put(LogUtils.Events.SET_SELECT_PHONE_ACCOUNT,
56 AnalyticsEvent.SET_SELECT_PHONE_ACCOUNT);
57 put(LogUtils.Events.REQUEST_HOLD, AnalyticsEvent.REQUEST_HOLD);
58 put(LogUtils.Events.REQUEST_UNHOLD, AnalyticsEvent.REQUEST_UNHOLD);
59 put(LogUtils.Events.SWAP, AnalyticsEvent.SWAP);
60 put(LogUtils.Events.SKIP_RINGING, AnalyticsEvent.SKIP_RINGING);
61 put(LogUtils.Events.CONFERENCE_WITH, AnalyticsEvent.CONFERENCE_WITH);
62 put(LogUtils.Events.SPLIT_FROM_CONFERENCE, AnalyticsEvent.SPLIT_CONFERENCE);
63 put(LogUtils.Events.SET_PARENT, AnalyticsEvent.SET_PARENT);
64 put(LogUtils.Events.MUTE, AnalyticsEvent.MUTE);
65 put(LogUtils.Events.UNMUTE, AnalyticsEvent.UNMUTE);
66 put(LogUtils.Events.AUDIO_ROUTE_BT, AnalyticsEvent.AUDIO_ROUTE_BT);
67 put(LogUtils.Events.AUDIO_ROUTE_EARPIECE, AnalyticsEvent.AUDIO_ROUTE_EARPIECE);
68 put(LogUtils.Events.AUDIO_ROUTE_HEADSET, AnalyticsEvent.AUDIO_ROUTE_HEADSET);
69 put(LogUtils.Events.AUDIO_ROUTE_SPEAKER, AnalyticsEvent.AUDIO_ROUTE_SPEAKER);
70 put(LogUtils.Events.SILENCE, AnalyticsEvent.SILENCE);
71 put(LogUtils.Events.SCREENING_COMPLETED, AnalyticsEvent.SCREENING_COMPLETED);
72 put(LogUtils.Events.BLOCK_CHECK_FINISHED, AnalyticsEvent.BLOCK_CHECK_FINISHED);
73 put(LogUtils.Events.DIRECT_TO_VM_FINISHED, AnalyticsEvent.DIRECT_TO_VM_FINISHED);
74 put(LogUtils.Events.REMOTELY_HELD, AnalyticsEvent.REMOTELY_HELD);
75 put(LogUtils.Events.REMOTELY_UNHELD, AnalyticsEvent.REMOTELY_UNHELD);
76 put(LogUtils.Events.REQUEST_PULL, AnalyticsEvent.REQUEST_PULL);
77 put(LogUtils.Events.REQUEST_ACCEPT, AnalyticsEvent.REQUEST_ACCEPT);
78 put(LogUtils.Events.REQUEST_REJECT, AnalyticsEvent.REQUEST_REJECT);
79 put(LogUtils.Events.SET_ACTIVE, AnalyticsEvent.SET_ACTIVE);
80 put(LogUtils.Events.SET_DISCONNECTED, AnalyticsEvent.SET_DISCONNECTED);
81 put(LogUtils.Events.SET_HOLD, AnalyticsEvent.SET_HOLD);
82 put(LogUtils.Events.SET_DIALING, AnalyticsEvent.SET_DIALING);
83 put(LogUtils.Events.START_CONNECTION, AnalyticsEvent.START_CONNECTION);
84 put(LogUtils.Events.BIND_CS, AnalyticsEvent.BIND_CS);
85 put(LogUtils.Events.CS_BOUND, AnalyticsEvent.CS_BOUND);
86 put(LogUtils.Events.SCREENING_SENT, AnalyticsEvent.SCREENING_SENT);
87 put(LogUtils.Events.DIRECT_TO_VM_INITIATED, AnalyticsEvent.DIRECT_TO_VM_INITIATED);
88 put(LogUtils.Events.BLOCK_CHECK_INITIATED, AnalyticsEvent.BLOCK_CHECK_INITIATED);
89 put(LogUtils.Events.FILTERING_INITIATED, AnalyticsEvent.FILTERING_INITIATED);
90 put(LogUtils.Events.FILTERING_COMPLETED, AnalyticsEvent.FILTERING_COMPLETED);
91 put(LogUtils.Events.FILTERING_TIMED_OUT, AnalyticsEvent.FILTERING_TIMED_OUT);
Hall Liu874c0f82016-04-29 18:13:18 -070092 }};
93
94 public static final Map<String, Integer> sLogSessionToSessionId =
95 new HashMap<String, Integer> () {{
Brad Ebingera3eccfe2016-10-05 15:45:22 -070096 put(LogUtils.Sessions.ICA_ANSWER_CALL, SessionTiming.ICA_ANSWER_CALL);
97 put(LogUtils.Sessions.ICA_REJECT_CALL, SessionTiming.ICA_REJECT_CALL);
98 put(LogUtils.Sessions.ICA_DISCONNECT_CALL, SessionTiming.ICA_DISCONNECT_CALL);
99 put(LogUtils.Sessions.ICA_HOLD_CALL, SessionTiming.ICA_HOLD_CALL);
100 put(LogUtils.Sessions.ICA_UNHOLD_CALL, SessionTiming.ICA_UNHOLD_CALL);
101 put(LogUtils.Sessions.ICA_MUTE, SessionTiming.ICA_MUTE);
102 put(LogUtils.Sessions.ICA_SET_AUDIO_ROUTE, SessionTiming.ICA_SET_AUDIO_ROUTE);
103 put(LogUtils.Sessions.ICA_CONFERENCE, SessionTiming.ICA_CONFERENCE);
104 put(LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE,
Hall Liu874c0f82016-04-29 18:13:18 -0700105 SessionTiming.CSW_HANDLE_CREATE_CONNECTION_COMPLETE);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700106 put(LogUtils.Sessions.CSW_SET_ACTIVE, SessionTiming.CSW_SET_ACTIVE);
107 put(LogUtils.Sessions.CSW_SET_RINGING, SessionTiming.CSW_SET_RINGING);
108 put(LogUtils.Sessions.CSW_SET_DIALING, SessionTiming.CSW_SET_DIALING);
109 put(LogUtils.Sessions.CSW_SET_DISCONNECTED, SessionTiming.CSW_SET_DISCONNECTED);
110 put(LogUtils.Sessions.CSW_SET_ON_HOLD, SessionTiming.CSW_SET_ON_HOLD);
111 put(LogUtils.Sessions.CSW_REMOVE_CALL, SessionTiming.CSW_REMOVE_CALL);
112 put(LogUtils.Sessions.CSW_SET_IS_CONFERENCED, SessionTiming.CSW_SET_IS_CONFERENCED);
113 put(LogUtils.Sessions.CSW_ADD_CONFERENCE_CALL,
114 SessionTiming.CSW_ADD_CONFERENCE_CALL);
Hall Liu874c0f82016-04-29 18:13:18 -0700115
116 }};
117
118 public static final Map<String, Integer> sLogEventTimingToAnalyticsEventTiming =
119 new HashMap<String, Integer>() {{
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700120 put(LogUtils.Events.Timings.ACCEPT_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700121 ParcelableCallAnalytics.EventTiming.ACCEPT_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700122 put(LogUtils.Events.Timings.REJECT_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700123 ParcelableCallAnalytics.EventTiming.REJECT_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700124 put(LogUtils.Events.Timings.DISCONNECT_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700125 ParcelableCallAnalytics.EventTiming.DISCONNECT_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700126 put(LogUtils.Events.Timings.HOLD_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700127 ParcelableCallAnalytics.EventTiming.HOLD_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700128 put(LogUtils.Events.Timings.UNHOLD_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700129 ParcelableCallAnalytics.EventTiming.UNHOLD_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700130 put(LogUtils.Events.Timings.OUTGOING_TIME_TO_DIALING_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700131 ParcelableCallAnalytics.EventTiming.OUTGOING_TIME_TO_DIALING_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700132 put(LogUtils.Events.Timings.BIND_CS_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700133 ParcelableCallAnalytics.EventTiming.BIND_CS_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700134 put(LogUtils.Events.Timings.SCREENING_COMPLETED_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700135 ParcelableCallAnalytics.EventTiming.SCREENING_COMPLETED_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700136 put(LogUtils.Events.Timings.DIRECT_TO_VM_FINISHED_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700137 ParcelableCallAnalytics.EventTiming.DIRECT_TO_VM_FINISHED_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700138 put(LogUtils.Events.Timings.BLOCK_CHECK_FINISHED_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700139 ParcelableCallAnalytics.EventTiming.BLOCK_CHECK_FINISHED_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700140 put(LogUtils.Events.Timings.FILTERING_COMPLETED_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700141 ParcelableCallAnalytics.EventTiming.FILTERING_COMPLETED_TIMING);
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700142 put(LogUtils.Events.Timings.FILTERING_TIMED_OUT_TIMING,
Hall Liu874c0f82016-04-29 18:13:18 -0700143 ParcelableCallAnalytics.EventTiming.FILTERING_TIMED_OUT_TIMING);
144 }};
145
146 public static final Map<Integer, String> sSessionIdToLogSession = new HashMap<>();
147 static {
148 for (Map.Entry<String, Integer> e : sLogSessionToSessionId.entrySet()) {
149 sSessionIdToLogSession.put(e.getValue(), e.getKey());
150 }
151 }
152
153 public static class CallInfo {
154 public void setCallStartTime(long startTime) {
Hall Liu32587202015-11-18 11:10:08 -0800155 }
156
Hall Liu874c0f82016-04-29 18:13:18 -0700157 public void setCallEndTime(long endTime) {
Hall Liu32587202015-11-18 11:10:08 -0800158 }
159
Hall Liu874c0f82016-04-29 18:13:18 -0700160 public void setCallIsAdditional(boolean isAdditional) {
Hall Liu32587202015-11-18 11:10:08 -0800161 }
162
Hall Liu874c0f82016-04-29 18:13:18 -0700163 public void setCallIsInterrupted(boolean isInterrupted) {
Hall Liu32587202015-11-18 11:10:08 -0800164 }
165
Hall Liu874c0f82016-04-29 18:13:18 -0700166 public void setCallDisconnectCause(DisconnectCause disconnectCause) {
Hall Liu32587202015-11-18 11:10:08 -0800167 }
168
Hall Liu874c0f82016-04-29 18:13:18 -0700169 public void addCallTechnology(int callTechnology) {
Hall Liu32587202015-11-18 11:10:08 -0800170 }
171
Hall Liu874c0f82016-04-29 18:13:18 -0700172 public void setCreatedFromExistingConnection(boolean createdFromExistingConnection) {
Hall Liu32587202015-11-18 11:10:08 -0800173 }
174
Hall Liu874c0f82016-04-29 18:13:18 -0700175 public void setCallConnectionService(String connectionServiceName) {
176 }
177
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700178 public void setCallEvents(EventManager.EventRecord records) {
Hall Liu32587202015-11-18 11:10:08 -0800179 }
Hall Liu4640c4f2016-06-22 17:13:20 -0700180
181 public void setCallIsVideo(boolean isVideo) {
182 }
183
184 public void addVideoEvent(int eventId, int videoState) {
185 }
Hall Liu9d15ca42016-08-30 17:18:36 -0700186
187 public void addInCallService(String serviceName, int type) {
188 }
Hall Liud7fe6862016-09-09 16:36:14 -0700189
190 public void addCallProperties(int properties) {
191 }
Hall Liu32587202015-11-18 11:10:08 -0800192 }
193
194 /**
195 * A class that holds data associated with a call.
196 */
197 @VisibleForTesting
198 public static class CallInfoImpl extends CallInfo {
199 public String callId;
200 public long startTime; // start time in milliseconds since the epoch. 0 if not yet set.
201 public long endTime; // end time in milliseconds since the epoch. 0 if not yet set.
202 public int callDirection; // one of UNKNOWN_DIRECTION, INCOMING_DIRECTION,
203 // or OUTGOING_DIRECTION.
204 public boolean isAdditionalCall = false; // true if the call came in while another call was
205 // in progress or if the user dialed this call
206 // while in the middle of another call.
207 public boolean isInterrupted = false; // true if the call was interrupted by an incoming
208 // or outgoing call.
209 public int callTechnologies; // bitmask denoting which technologies a call used.
210
211 // true if the Telecom Call object was created from an existing connection via
212 // CallsManager#createCallForExistingConnection, for example, by ImsConference.
213 public boolean createdFromExistingConnection = false;
214
215 public DisconnectCause callTerminationReason;
216 public String connectionService;
217 public boolean isEmergency = false;
218
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700219 public EventManager.EventRecord callEvents;
Hall Liu874c0f82016-04-29 18:13:18 -0700220
Hall Liu4640c4f2016-06-22 17:13:20 -0700221 public boolean isVideo = false;
Hall Liu2f4f0a02016-08-08 17:23:20 -0700222 public List<TelecomLogClass.VideoEvent> videoEvents;
Hall Liu9d15ca42016-08-30 17:18:36 -0700223 public List<TelecomLogClass.InCallServiceInfo> inCallServiceInfos;
Hall Liud7fe6862016-09-09 16:36:14 -0700224 public int callProperties = 0;
225
Hall Liu4640c4f2016-06-22 17:13:20 -0700226 private long mTimeOfLastVideoEvent = -1;
227
Hall Liu32587202015-11-18 11:10:08 -0800228 CallInfoImpl(String callId, int callDirection) {
229 this.callId = callId;
230 startTime = 0;
231 endTime = 0;
232 this.callDirection = callDirection;
233 callTechnologies = 0;
234 connectionService = "";
Hall Liu4640c4f2016-06-22 17:13:20 -0700235 videoEvents = new LinkedList<>();
Hall Liu9d15ca42016-08-30 17:18:36 -0700236 inCallServiceInfos = new LinkedList<>();
Hall Liu32587202015-11-18 11:10:08 -0800237 }
238
239 CallInfoImpl(CallInfoImpl other) {
240 this.callId = other.callId;
241 this.startTime = other.startTime;
242 this.endTime = other.endTime;
243 this.callDirection = other.callDirection;
244 this.isAdditionalCall = other.isAdditionalCall;
245 this.isInterrupted = other.isInterrupted;
246 this.callTechnologies = other.callTechnologies;
247 this.createdFromExistingConnection = other.createdFromExistingConnection;
248 this.connectionService = other.connectionService;
249 this.isEmergency = other.isEmergency;
Hall Liu874c0f82016-04-29 18:13:18 -0700250 this.callEvents = other.callEvents;
Hall Liu4640c4f2016-06-22 17:13:20 -0700251 this.isVideo = other.isVideo;
252 this.videoEvents = other.videoEvents;
Hall Liud7fe6862016-09-09 16:36:14 -0700253 this.callProperties = other.callProperties;
Hall Liu32587202015-11-18 11:10:08 -0800254
255 if (other.callTerminationReason != null) {
256 this.callTerminationReason = new DisconnectCause(
257 other.callTerminationReason.getCode(),
258 other.callTerminationReason.getLabel(),
259 other.callTerminationReason.getDescription(),
260 other.callTerminationReason.getReason(),
261 other.callTerminationReason.getTone());
262 } else {
263 this.callTerminationReason = null;
264 }
265 }
266
267 @Override
268 public void setCallStartTime(long startTime) {
269 Log.d(TAG, "setting startTime for call " + callId + " to " + startTime);
270 this.startTime = startTime;
271 }
272
273 @Override
274 public void setCallEndTime(long endTime) {
275 Log.d(TAG, "setting endTime for call " + callId + " to " + endTime);
276 this.endTime = endTime;
277 }
278
279 @Override
280 public void setCallIsAdditional(boolean isAdditional) {
281 Log.d(TAG, "setting isAdditional for call " + callId + " to " + isAdditional);
282 this.isAdditionalCall = isAdditional;
283 }
284
285 @Override
286 public void setCallIsInterrupted(boolean isInterrupted) {
287 Log.d(TAG, "setting isInterrupted for call " + callId + " to " + isInterrupted);
288 this.isInterrupted = isInterrupted;
289 }
290
291 @Override
292 public void addCallTechnology(int callTechnology) {
293 Log.d(TAG, "adding callTechnology for call " + callId + ": " + callTechnology);
294 this.callTechnologies |= callTechnology;
295 }
296
297 @Override
298 public void setCallDisconnectCause(DisconnectCause disconnectCause) {
299 Log.d(TAG, "setting disconnectCause for call " + callId + " to " + disconnectCause);
300 this.callTerminationReason = disconnectCause;
301 }
302
303 @Override
304 public void setCreatedFromExistingConnection(boolean createdFromExistingConnection) {
305 Log.d(TAG, "setting createdFromExistingConnection for call " + callId + " to "
306 + createdFromExistingConnection);
307 this.createdFromExistingConnection = createdFromExistingConnection;
308 }
309
310 @Override
311 public void setCallConnectionService(String connectionServiceName) {
312 Log.d(TAG, "setting connection service for call " + callId + ": "
313 + connectionServiceName);
314 this.connectionService = connectionServiceName;
315 }
316
317 @Override
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700318 public void setCallEvents(EventManager.EventRecord records) {
Hall Liu874c0f82016-04-29 18:13:18 -0700319 this.callEvents = records;
320 }
321
322 @Override
Hall Liu4640c4f2016-06-22 17:13:20 -0700323 public void setCallIsVideo(boolean isVideo) {
324 this.isVideo = isVideo;
325 }
326
327 @Override
328 public void addVideoEvent(int eventId, int videoState) {
329 long timeSinceLastEvent;
330 long currentTime = System.currentTimeMillis();
331 if (mTimeOfLastVideoEvent < 0) {
332 timeSinceLastEvent = -1;
333 } else {
334 timeSinceLastEvent = roundToOneSigFig(currentTime - mTimeOfLastVideoEvent);
335 }
336 mTimeOfLastVideoEvent = currentTime;
337
Hall Liu2f4f0a02016-08-08 17:23:20 -0700338 videoEvents.add(new TelecomLogClass.VideoEvent()
339 .setEventName(eventId)
340 .setTimeSinceLastEventMillis(timeSinceLastEvent)
341 .setVideoState(videoState));
Hall Liu4640c4f2016-06-22 17:13:20 -0700342 }
343
344 @Override
Hall Liu9d15ca42016-08-30 17:18:36 -0700345 public void addInCallService(String serviceName, int type) {
346 inCallServiceInfos.add(new TelecomLogClass.InCallServiceInfo()
347 .setInCallServiceName(serviceName)
348 .setInCallServiceType(type));
349 }
350
351 @Override
Hall Liud7fe6862016-09-09 16:36:14 -0700352 public void addCallProperties(int properties) {
353 this.callProperties |= properties;
354 }
355
356 @Override
Hall Liu32587202015-11-18 11:10:08 -0800357 public String toString() {
358 return "{\n"
359 + " startTime: " + startTime + '\n'
360 + " endTime: " + endTime + '\n'
361 + " direction: " + getCallDirectionString() + '\n'
362 + " isAdditionalCall: " + isAdditionalCall + '\n'
363 + " isInterrupted: " + isInterrupted + '\n'
364 + " callTechnologies: " + getCallTechnologiesAsString() + '\n'
365 + " callTerminationReason: " + getCallDisconnectReasonString() + '\n'
Hall Liuecd74a52016-01-12 15:26:36 -0800366 + " connectionService: " + connectionService + '\n'
Hall Liu4640c4f2016-06-22 17:13:20 -0700367 + " isVideoCall: " + isVideo + '\n'
Hall Liu9d15ca42016-08-30 17:18:36 -0700368 + " inCallServices: " + getInCallServicesString() + '\n'
Hall Liud7fe6862016-09-09 16:36:14 -0700369 + " callProperties: " + Connection.propertiesToStringShort(callProperties)
370 + '\n'
Hall Liu32587202015-11-18 11:10:08 -0800371 + "}\n";
372 }
373
Hall Liuecd74a52016-01-12 15:26:36 -0800374 public ParcelableCallAnalytics toParcelableAnalytics() {
Hall Liu2f4f0a02016-08-08 17:23:20 -0700375 TelecomLogClass.CallLog analyticsProto = toProto();
376 List<ParcelableCallAnalytics.AnalyticsEvent> events =
377 Arrays.stream(analyticsProto.callEvents)
378 .map(callEventProto -> new ParcelableCallAnalytics.AnalyticsEvent(
379 callEventProto.getEventName(),
380 callEventProto.getTimeSinceLastEventMillis())
381 ).collect(Collectors.toList());
382
383 List<ParcelableCallAnalytics.EventTiming> timings =
384 Arrays.stream(analyticsProto.callTimings)
385 .map(callTimingProto -> new ParcelableCallAnalytics.EventTiming(
386 callTimingProto.getTimingName(),
387 callTimingProto.getTimeMillis())
388 ).collect(Collectors.toList());
389
390 ParcelableCallAnalytics result = new ParcelableCallAnalytics(
391 // rounds down to nearest 5 minute mark
392 analyticsProto.getStartTime5Min(),
393 analyticsProto.getCallDurationMillis(),
394 analyticsProto.getType(),
395 analyticsProto.getIsAdditionalCall(),
396 analyticsProto.getIsInterrupted(),
397 analyticsProto.getCallTechnologies(),
398 analyticsProto.getCallTerminationCode(),
399 analyticsProto.getIsEmergencyCall(),
400 analyticsProto.connectionService[0],
401 analyticsProto.getIsCreatedFromExistingConnection(),
402 events,
403 timings);
404
405 result.setIsVideoCall(analyticsProto.getIsVideoCall());
406 result.setVideoEvents(Arrays.stream(analyticsProto.videoEvents)
407 .map(videoEventProto -> new ParcelableCallAnalytics.VideoEvent(
408 videoEventProto.getEventName(),
409 videoEventProto.getTimeSinceLastEventMillis(),
410 videoEventProto.getVideoState())
411 ).collect(Collectors.toList()));
412
413 return result;
414 }
415
416 public TelecomLogClass.CallLog toProto() {
417 TelecomLogClass.CallLog result = new TelecomLogClass.CallLog();
418 result.setStartTime5Min(
419 startTime - startTime % ParcelableCallAnalytics.MILLIS_IN_5_MINUTES);
420
Hall Liuecd74a52016-01-12 15:26:36 -0800421 // Rounds up to the nearest second.
Hall Liu0bd869c2016-03-22 14:34:56 -0700422 long callDuration = (endTime == 0 || startTime == 0) ? 0 : endTime - startTime;
Hall Liuecd74a52016-01-12 15:26:36 -0800423 callDuration += (callDuration % MILLIS_IN_1_SECOND == 0) ?
424 0 : (MILLIS_IN_1_SECOND - callDuration % MILLIS_IN_1_SECOND);
Hall Liu2f4f0a02016-08-08 17:23:20 -0700425 result.setCallDurationMillis(callDuration);
Hall Liu874c0f82016-04-29 18:13:18 -0700426
Hall Liu2f4f0a02016-08-08 17:23:20 -0700427 result.setType(callDirection)
428 .setIsAdditionalCall(isAdditionalCall)
429 .setIsInterrupted(isInterrupted)
430 .setCallTechnologies(callTechnologies)
431 .setCallTerminationCode(
432 callTerminationReason == null ?
433 ParcelableCallAnalytics.STILL_CONNECTED :
434 callTerminationReason.getCode())
435 .setIsEmergencyCall(isEmergency)
436 .setIsCreatedFromExistingConnection(createdFromExistingConnection)
437 .setIsEmergencyCall(isEmergency)
Hall Liud7fe6862016-09-09 16:36:14 -0700438 .setIsVideoCall(isVideo)
439 .setConnectionProperties(callProperties);
Hall Liu2f4f0a02016-08-08 17:23:20 -0700440
441 result.connectionService = new String[] {connectionService};
Hall Liu874c0f82016-04-29 18:13:18 -0700442 if (callEvents != null) {
Hall Liu2f4f0a02016-08-08 17:23:20 -0700443 result.callEvents = convertLogEventsToProtoEvents(callEvents.getEvents());
444 result.callTimings = callEvents.extractEventTimings().stream()
445 .map(Analytics::logEventTimingToProtoEventTiming)
446 .toArray(TelecomLogClass.EventTimingEntry[]::new);
Hall Liu874c0f82016-04-29 18:13:18 -0700447 }
Hall Liu2f4f0a02016-08-08 17:23:20 -0700448 result.videoEvents =
449 videoEvents.toArray(new TelecomLogClass.VideoEvent[videoEvents.size()]);
Hall Liu9d15ca42016-08-30 17:18:36 -0700450 result.inCallServices = inCallServiceInfos.toArray(
451 new TelecomLogClass.InCallServiceInfo[inCallServiceInfos.size()]);
452
Hall Liu4640c4f2016-06-22 17:13:20 -0700453 return result;
Hall Liuecd74a52016-01-12 15:26:36 -0800454 }
455
Hall Liu32587202015-11-18 11:10:08 -0800456 private String getCallDirectionString() {
457 switch (callDirection) {
458 case UNKNOWN_DIRECTION:
459 return "UNKNOWN";
460 case INCOMING_DIRECTION:
461 return "INCOMING";
462 case OUTGOING_DIRECTION:
463 return "OUTGOING";
464 default:
465 return "UNKNOWN";
466 }
467 }
468
469 private String getCallTechnologiesAsString() {
470 StringBuilder s = new StringBuilder();
471 s.append('[');
472 if ((callTechnologies & CDMA_PHONE) != 0) s.append("CDMA ");
473 if ((callTechnologies & GSM_PHONE) != 0) s.append("GSM ");
474 if ((callTechnologies & SIP_PHONE) != 0) s.append("SIP ");
475 if ((callTechnologies & IMS_PHONE) != 0) s.append("IMS ");
476 if ((callTechnologies & THIRD_PARTY_PHONE) != 0) s.append("THIRD_PARTY ");
477 s.append(']');
478 return s.toString();
479 }
480
481 private String getCallDisconnectReasonString() {
482 if (callTerminationReason != null) {
483 return callTerminationReason.toString();
484 } else {
485 return "NOT SET";
486 }
487 }
Hall Liu9d15ca42016-08-30 17:18:36 -0700488
489 private String getInCallServicesString() {
490 StringBuilder s = new StringBuilder();
491 s.append("[\n");
492 for (TelecomLogClass.InCallServiceInfo service : inCallServiceInfos) {
493 s.append(" ");
494 s.append("name: ");
495 s.append(service.getInCallServiceName());
496 s.append(" type: ");
497 s.append(service.getInCallServiceType());
498 s.append("\n");
499 }
500 s.append("]");
501 return s.toString();
502 }
Hall Liu32587202015-11-18 11:10:08 -0800503 }
504 public static final String TAG = "TelecomAnalytics";
505
506 // Constants for call direction
Hall Liuecd74a52016-01-12 15:26:36 -0800507 public static final int UNKNOWN_DIRECTION = ParcelableCallAnalytics.CALLTYPE_UNKNOWN;
508 public static final int INCOMING_DIRECTION = ParcelableCallAnalytics.CALLTYPE_INCOMING;
509 public static final int OUTGOING_DIRECTION = ParcelableCallAnalytics.CALLTYPE_OUTGOING;
Hall Liu32587202015-11-18 11:10:08 -0800510
511 // Constants for call technology
Hall Liuecd74a52016-01-12 15:26:36 -0800512 public static final int CDMA_PHONE = ParcelableCallAnalytics.CDMA_PHONE;
513 public static final int GSM_PHONE = ParcelableCallAnalytics.GSM_PHONE;
514 public static final int IMS_PHONE = ParcelableCallAnalytics.IMS_PHONE;
515 public static final int SIP_PHONE = ParcelableCallAnalytics.SIP_PHONE;
516 public static final int THIRD_PARTY_PHONE = ParcelableCallAnalytics.THIRD_PARTY_PHONE;
517
Hall Liu4640c4f2016-06-22 17:13:20 -0700518 // Constants for video events
519 public static final int SEND_LOCAL_SESSION_MODIFY_REQUEST =
520 ParcelableCallAnalytics.VideoEvent.SEND_LOCAL_SESSION_MODIFY_REQUEST;
521 public static final int SEND_LOCAL_SESSION_MODIFY_RESPONSE =
522 ParcelableCallAnalytics.VideoEvent.SEND_LOCAL_SESSION_MODIFY_RESPONSE;
523 public static final int RECEIVE_REMOTE_SESSION_MODIFY_REQUEST =
524 ParcelableCallAnalytics.VideoEvent.RECEIVE_REMOTE_SESSION_MODIFY_REQUEST;
525 public static final int RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE =
526 ParcelableCallAnalytics.VideoEvent.RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE;
527
Hall Liuecd74a52016-01-12 15:26:36 -0800528 public static final long MILLIS_IN_1_SECOND = ParcelableCallAnalytics.MILLIS_IN_1_SECOND;
Hall Liu32587202015-11-18 11:10:08 -0800529
Hall Liud93b9ec2017-01-05 18:06:30 -0800530 public static final int MAX_NUM_CALLS_TO_STORE = 100;
531
Hall Liu32587202015-11-18 11:10:08 -0800532 private static final Object sLock = new Object(); // Coarse lock for all of analytics
533 private static final Map<String, CallInfoImpl> sCallIdToInfo = new HashMap<>();
Hall Liud93b9ec2017-01-05 18:06:30 -0800534 private static final LinkedList<String> sActiveCallIds = new LinkedList<>();
Hall Liu874c0f82016-04-29 18:13:18 -0700535 private static final List<SessionTiming> sSessionTimings = new LinkedList<>();
536
537 public static void addSessionTiming(String sessionName, long time) {
538 if (sLogSessionToSessionId.containsKey(sessionName)) {
539 synchronized (sLock) {
540 sSessionTimings.add(new SessionTiming(sLogSessionToSessionId.get(sessionName),
541 time));
542 }
543 }
544 }
Hall Liu32587202015-11-18 11:10:08 -0800545
546 public static CallInfo initiateCallAnalytics(String callId, int direction) {
547 Log.d(TAG, "Starting analytics for call " + callId);
548 CallInfoImpl callInfo = new CallInfoImpl(callId, direction);
549 synchronized (sLock) {
Hall Liud93b9ec2017-01-05 18:06:30 -0800550 while (sActiveCallIds.size() >= MAX_NUM_CALLS_TO_STORE) {
551 String callToRemove = sActiveCallIds.remove();
552 sCallIdToInfo.remove(callToRemove);
553 }
Hall Liu32587202015-11-18 11:10:08 -0800554 sCallIdToInfo.put(callId, callInfo);
Hall Liud93b9ec2017-01-05 18:06:30 -0800555 sActiveCallIds.add(callId);
Hall Liu32587202015-11-18 11:10:08 -0800556 }
557 return callInfo;
558 }
559
Hall Liu874c0f82016-04-29 18:13:18 -0700560 public static TelecomAnalytics dumpToParcelableAnalytics() {
561 List<ParcelableCallAnalytics> calls = new LinkedList<>();
562 List<SessionTiming> sessionTimings = new LinkedList<>();
Hall Liuecd74a52016-01-12 15:26:36 -0800563 synchronized (sLock) {
Hall Liu874c0f82016-04-29 18:13:18 -0700564 calls.addAll(sCallIdToInfo.values().stream()
565 .map(CallInfoImpl::toParcelableAnalytics)
566 .collect(Collectors.toList()));
567 sessionTimings.addAll(sSessionTimings);
Hall Liuecd74a52016-01-12 15:26:36 -0800568 sCallIdToInfo.clear();
Hall Liu874c0f82016-04-29 18:13:18 -0700569 sSessionTimings.clear();
Hall Liuecd74a52016-01-12 15:26:36 -0800570 }
Hall Liu874c0f82016-04-29 18:13:18 -0700571 return new TelecomAnalytics(sessionTimings, calls);
Hall Liuecd74a52016-01-12 15:26:36 -0800572 }
573
Hall Liu2f4f0a02016-08-08 17:23:20 -0700574 public static void dumpToEncodedProto(PrintWriter pw, String[] args) {
575 TelecomLogClass.TelecomLog result = new TelecomLogClass.TelecomLog();
576
577 synchronized (sLock) {
578 result.callLogs = sCallIdToInfo.values().stream()
579 .map(CallInfoImpl::toProto)
580 .toArray(TelecomLogClass.CallLog[]::new);
581 result.sessionTimings = sSessionTimings.stream()
582 .map(timing -> new TelecomLogClass.LogSessionTiming()
583 .setSessionEntryPoint(timing.getKey())
584 .setTimeMillis(timing.getTime()))
585 .toArray(TelecomLogClass.LogSessionTiming[]::new);
586 if (args.length > 1 && CLEAR_ANALYTICS_ARG.equals(args[1])) {
587 sCallIdToInfo.clear();
588 sSessionTimings.clear();
589 }
590 }
591 String encodedProto = Base64.encodeToString(
592 TelecomLogClass.TelecomLog.toByteArray(result), Base64.DEFAULT);
593 pw.write(encodedProto);
594 }
595
Hall Liu32587202015-11-18 11:10:08 -0800596 public static void dump(IndentingPrintWriter writer) {
597 synchronized (sLock) {
Hall Liu874c0f82016-04-29 18:13:18 -0700598 int prefixLength = CallsManager.TELECOM_CALL_ID_PREFIX.length();
599 List<String> callIds = new ArrayList<>(sCallIdToInfo.keySet());
600 // Sort the analytics in increasing order of call IDs
Hall Liud7e370d2016-07-14 17:52:49 -0700601 try {
602 Collections.sort(callIds, (id1, id2) -> {
603 int i1, i2;
604 try {
605 i1 = Integer.valueOf(id1.substring(prefixLength));
606 } catch (NumberFormatException e) {
607 i1 = Integer.MAX_VALUE;
608 }
609
610 try {
611 i2 = Integer.valueOf(id2.substring(prefixLength));
612 } catch (NumberFormatException e) {
613 i2 = Integer.MAX_VALUE;
614 }
615 return i1 - i2;
616 });
617 } catch (IllegalArgumentException e) {
618 // do nothing, leave the list in a partially sorted state.
619 }
Hall Liu874c0f82016-04-29 18:13:18 -0700620
621 for (String callId : callIds) {
622 writer.printf("Call %s: ", callId);
623 writer.println(sCallIdToInfo.get(callId).toString());
Hall Liu32587202015-11-18 11:10:08 -0800624 }
Hall Liu874c0f82016-04-29 18:13:18 -0700625
626 Map<Integer, Double> averageTimings = SessionTiming.averageTimings(sSessionTimings);
627 averageTimings.entrySet().stream()
628 .filter(e -> sSessionIdToLogSession.containsKey(e.getKey()))
629 .forEach(e -> writer.printf("%s: %.2f\n",
630 sSessionIdToLogSession.get(e.getKey()), e.getValue()));
Hall Liu32587202015-11-18 11:10:08 -0800631 }
632 }
633
634 public static void reset() {
635 synchronized (sLock) {
636 sCallIdToInfo.clear();
637 }
638 }
639
640 /**
Hall Liu874c0f82016-04-29 18:13:18 -0700641 * Returns a copy of callIdToInfo. Use only for testing.
Hall Liu32587202015-11-18 11:10:08 -0800642 */
Hall Liu874c0f82016-04-29 18:13:18 -0700643 @VisibleForTesting
Hall Liu32587202015-11-18 11:10:08 -0800644 public static Map<String, CallInfoImpl> cloneData() {
645 synchronized (sLock) {
646 Map<String, CallInfoImpl> result = new HashMap<>(sCallIdToInfo.size());
647 for (Map.Entry<String, CallInfoImpl> entry : sCallIdToInfo.entrySet()) {
648 result.put(entry.getKey(), new CallInfoImpl(entry.getValue()));
649 }
650 return result;
651 }
652 }
Hall Liu874c0f82016-04-29 18:13:18 -0700653
Hall Liu2f4f0a02016-08-08 17:23:20 -0700654 private static TelecomLogClass.Event[] convertLogEventsToProtoEvents(
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700655 List<EventManager.Event> logEvents) {
Hall Liu874c0f82016-04-29 18:13:18 -0700656 long timeOfLastEvent = -1;
Hall Liu2f4f0a02016-08-08 17:23:20 -0700657 ArrayList<TelecomLogClass.Event> events = new ArrayList<>(logEvents.size());
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700658 for (EventManager.Event logEvent : logEvents) {
Hall Liu874c0f82016-04-29 18:13:18 -0700659 if (sLogEventToAnalyticsEvent.containsKey(logEvent.eventId)) {
Hall Liu2f4f0a02016-08-08 17:23:20 -0700660 TelecomLogClass.Event event = new TelecomLogClass.Event();
661 event.setEventName(sLogEventToAnalyticsEvent.get(logEvent.eventId));
662 event.setTimeSinceLastEventMillis(roundToOneSigFig(
663 timeOfLastEvent < 0 ? -1 : logEvent.time - timeOfLastEvent));
664 events.add(event);
Hall Liu874c0f82016-04-29 18:13:18 -0700665 timeOfLastEvent = logEvent.time;
666 }
667 }
Hall Liu2f4f0a02016-08-08 17:23:20 -0700668 return events.toArray(new TelecomLogClass.Event[events.size()]);
Hall Liu874c0f82016-04-29 18:13:18 -0700669 }
670
Hall Liu2f4f0a02016-08-08 17:23:20 -0700671 private static TelecomLogClass.EventTimingEntry logEventTimingToProtoEventTiming(
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700672 EventManager.EventRecord.EventTiming logEventTiming) {
Hall Liu874c0f82016-04-29 18:13:18 -0700673 int analyticsEventTimingName =
674 sLogEventTimingToAnalyticsEventTiming.containsKey(logEventTiming.name) ?
675 sLogEventTimingToAnalyticsEventTiming.get(logEventTiming.name) :
676 ParcelableCallAnalytics.EventTiming.INVALID;
Hall Liu2f4f0a02016-08-08 17:23:20 -0700677 return new TelecomLogClass.EventTimingEntry()
678 .setTimingName(analyticsEventTimingName)
679 .setTimeMillis(logEventTiming.time);
Hall Liu874c0f82016-04-29 18:13:18 -0700680 }
681
682 @VisibleForTesting
683 public static long roundToOneSigFig(long val) {
684 if (val == 0) {
685 return val;
686 }
687 int logVal = (int) Math.floor(Math.log10(val < 0 ? -val : val));
688 double s = Math.pow(10, logVal);
689 double dec = val / s;
690 return (long) (Math.round(dec) * s);
691 }
Hall Liu32587202015-11-18 11:10:08 -0800692}