Loading Android.mk +5 −1 Original line number Diff line number Diff line Loading @@ -5,9 +5,13 @@ include $(CLEAR_VARS) LOCAL_JAVA_LIBRARIES := telephony-common LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under, proto) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_PROTOC_OPTIMIZE_TYPE := nano LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/proto/ LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors LOCAL_PACKAGE_NAME := Telecom LOCAL_CERTIFICATE := platform Loading proto/telecom.proto 0 → 100644 +256 −0 Original line number Diff line number Diff line syntax = "proto2"; package com.android.server.telecom; option java_package = "com.android.server.telecom"; option java_outer_classname = "TelecomLogClass"; // The information about the telecom events. message TelecomLog { // Information about each call. repeated CallLog call_logs = 1; // Timing information for the logging sessions repeated LogSessionTiming session_timings = 2; } message LogSessionTiming { enum SessionEntryPoint { ICA_ANSWER_CALL = 1; ICA_REJECT_CALL = 2; ICA_DISCONNECT_CALL = 3; ICA_HOLD_CALL = 4; ICA_UNHOLD_CALL = 5; ICA_MUTE = 6; ICA_SET_AUDIO_ROUTE = 7; ICA_CONFERENCE = 8; CSW_HANDLE_CREATE_CONNECTION_COMPLETE = 100; CSW_SET_ACTIVE = 101; CSW_SET_RINGING = 102; CSW_SET_DIALING = 103; CSW_SET_DISCONNECTED = 104; CSW_SET_ON_HOLD = 105; CSW_REMOVE_CALL = 106; CSW_SET_IS_CONFERENCED = 107; CSW_ADD_CONFERENCE_CALL = 108; } // The entry point into Telecom code that this session tracks. optional SessionEntryPoint sessionEntryPoint = 1; // The time it took for this session to finish. optional int64 time_millis = 2; } message Event { // From android.telecom.ParcelableAnalytics enum EventName { SET_SELECT_PHONE_ACCOUNT = 0; SET_ACTIVE = 1; SET_DISCONNECTED = 2; START_CONNECTION = 3; SET_DIALING = 4; BIND_CS = 5; CS_BOUND = 6; REQUEST_ACCEPT = 7; REQUEST_REJECT = 8; SCREENING_SENT = 100; SCREENING_COMPLETED = 101; DIRECT_TO_VM_INITIATED = 102; DIRECT_TO_VM_FINISHED = 103; BLOCK_CHECK_INITIATED = 104; BLOCK_CHECK_FINISHED = 105; FILTERING_INITIATED = 106; FILTERING_COMPLETED = 107; FILTERING_TIMED_OUT = 108; SKIP_RINGING = 200; SILENCE = 201; MUTE = 202; UNMUTE = 203; AUDIO_ROUTE_BT = 204; AUDIO_ROUTE_EARPIECE = 205; AUDIO_ROUTE_HEADSET = 206; AUDIO_ROUTE_SPEAKER = 207; CONFERENCE_WITH = 300; SPLIT_CONFERENCE = 301; SET_PARENT = 302; REQUEST_HOLD = 400; REQUEST_UNHOLD = 401; REMOTELY_HELD = 402; REMOTELY_UNHELD = 403; SET_HOLD = 404; SWAP = 405; REQUEST_PULL = 500; } // The ID of the event. optional EventName event_name = 1; // The elapsed time since the last event, rounded to one significant digit. // If the event is the first, this will be negative. optional int64 time_since_last_event_millis = 2; } message VideoEvent { // From android.telecom.ParcelableCallAnalytics enum VideoEventName { SEND_LOCAL_SESSION_MODIFY_REQUEST = 0; SEND_LOCAL_SESSION_MODIFY_RESPONSE = 1; RECEIVE_REMOTE_SESSION_MODIFY_REQUEST = 2; RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE = 3; } // From android.telecom.VideoProfile enum VideoState { STATE_AUDIO_ONLY = 0; STATE_TX_ENABLED = 1; STATE_RX_ENABLED = 2; STATE_BIDIRECTIONAL = 3; STATE_PAUSED = 4; } // The ID of the event. optional VideoEventName event_name = 1; // The elapsed time since the last event, rounded to one significant digit. // If the event is the first, this will be negative. optional int64 time_since_last_event_millis = 2; // The video state optional int32 video_state = 3; } message EventTimingEntry { enum EventTimingName { ACCEPT_TIMING = 0; REJECT_TIMING = 1; DISCONNECT_TIMING = 2; HOLD_TIMING = 3; UNHOLD_TIMING = 4; OUTGOING_TIME_TO_DIALING_TIMING = 5; BIND_CS_TIMING = 6; SCREENING_COMPLETED_TIMING = 7; DIRECT_TO_VM_FINISHED_TIMING = 8; BLOCK_CHECK_FINISHED_TIMING = 9; FILTERING_COMPLETED_TIMING = 10; FILTERING_TIMED_OUT_TIMING = 11; } // The name of the event timing. optional EventTimingName timing_name = 1; // The number of milliseconds that this event pair took. optional int64 time_millis = 2; } // Information about each call. message CallLog { // Information on call-types. enum CallType { // Call type is not known. CALLTYPE_UNKNOWN = 0; // Incoming call. CALLTYPE_INCOMING = 1; // Outgoing call. CALLTYPE_OUTGOING = 2; } // Termination code. enum CallTerminationCode { // Disconnected because of an unknown or unspecified reason. CALL_TERMINATION_CODE_UNKNOWN = 0; // Disconnected because there was an error, such as a problem // with the network. CALL_TERMINATION_CODE_ERROR = 1; // Disconnected because of a local user-initiated action, // such as hanging up. CALL_TERMINATION_CODE_LOCAL = 2; // Disconnected because of a remote user-initiated action, // such as the other party hanging up. CALL_TERMINATION_CODE_REMOTE = 3; // Disconnected because it has been canceled. CALL_TERMINATION_CODE_CANCELED = 4; // Disconnected because there was no response to an incoming call. CALL_TERMINATION_CODE_MISSED = 5; // Disconnected because the user rejected an incoming call. CALL_TERMINATION_CODE_REJECTED = 6; // Disconnected because the other party was busy. CALL_TERMINATION_CODE_BUSY = 7; // Disconnected because of a restriction on placing the call, // such as dialing in airplane mode. CALL_TERMINATION_CODE_RESTRICTED = 8; // Disconnected for reason not described by other disconnect codes. CALL_TERMINATION_CODE_OTHER = 9; // Disconnected because the connection manager did not support the call. // The call will be tried again without a connection manager. CONNECTION_MANAGER_NOT_SUPPORTED = 10; } // Start time of the connection. // Rounded to the nearest 5 minute interval. optional int64 start_time_5min = 1; // Duration in millis. optional int64 call_duration_millis = 2; // Call type. optional CallType type = 3; // True if the call interrupted an in-progress call, whether it was the // user dialing out during a call or an incoming call during another call. optional bool is_additional_call = 4 [default = false]; // True if the call was interrupted by another call. optional bool is_interrupted = 5 [default = false]; // A bitmask with bits corresponding to call technologies that were used // during the call. The ones that we will record are CDMA, GSM, IMS, SIP, // and third-party. // https://googleplex-android-review.git.corp.google.com/#/c/816516/6/src/com/android/server/telecom/Analytics.java optional int32 call_technologies = 6; // Indicates the call termination code. optional CallTerminationCode call_termination_code = 7; // A list of the package names of connection services used. repeated string connection_service = 9; // Set to true if the call was created from createCallForExistingConnection. optional bool is_created_from_existing_connection = 10 [default = false]; // Set to true if its an emergency call. optional bool is_emergency_call = 11 [default = false]; // A list of the events that occur during the call. repeated Event call_events = 12; // A map from the names of latency timings to the timings. repeated EventTimingEntry call_timings = 13; // Whether this call has ever been a video call optional bool is_video_call = 14 [default = false]; // A list of the video events during the call. repeated VideoEvent video_events = 15; } src/com/android/server/telecom/Analytics.java +113 −44 Original line number Diff line number Diff line Loading @@ -19,11 +19,14 @@ package com.android.server.telecom; import android.telecom.DisconnectCause; import android.telecom.ParcelableCallAnalytics; import android.telecom.TelecomAnalytics; import android.util.Base64; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; Loading @@ -39,6 +42,9 @@ import static android.telecom.TelecomAnalytics.SessionTiming; * aggregate these into useful statistics. */ public class Analytics { public static final String ANALYTICS_DUMPSYS_ARG = "analytics"; private static final String CLEAR_ANALYTICS_ARG = "clear"; public static final Map<String, Integer> sLogEventToAnalyticsEvent = new HashMap<String, Integer>() {{ put(Log.Events.SET_SELECT_PHONE_ACCOUNT, AnalyticsEvent.SET_SELECT_PHONE_ACCOUNT); Loading Loading @@ -200,7 +206,7 @@ public class Analytics { public Log.CallEventRecord callEvents; public boolean isVideo = false; public List<ParcelableCallAnalytics.VideoEvent> videoEvents; public List<TelecomLogClass.VideoEvent> videoEvents; private long mTimeOfLastVideoEvent = -1; CallInfoImpl(String callId, int callDirection) { Loading Loading @@ -311,8 +317,10 @@ public class Analytics { } mTimeOfLastVideoEvent = currentTime; videoEvents.add(new ParcelableCallAnalytics.VideoEvent( eventId, timeSinceLastEvent, videoState)); videoEvents.add(new TelecomLogClass.VideoEvent() .setEventName(eventId) .setTimeSinceLastEventMillis(timeSinceLastEvent) .setVideoState(videoState)); } @Override Loading @@ -331,40 +339,80 @@ public class Analytics { } public ParcelableCallAnalytics toParcelableAnalytics() { TelecomLogClass.CallLog analyticsProto = toProto(); List<ParcelableCallAnalytics.AnalyticsEvent> events = Arrays.stream(analyticsProto.callEvents) .map(callEventProto -> new ParcelableCallAnalytics.AnalyticsEvent( callEventProto.getEventName(), callEventProto.getTimeSinceLastEventMillis()) ).collect(Collectors.toList()); List<ParcelableCallAnalytics.EventTiming> timings = Arrays.stream(analyticsProto.callTimings) .map(callTimingProto -> new ParcelableCallAnalytics.EventTiming( callTimingProto.getTimingName(), callTimingProto.getTimeMillis()) ).collect(Collectors.toList()); ParcelableCallAnalytics result = new ParcelableCallAnalytics( // rounds down to nearest 5 minute mark analyticsProto.getStartTime5Min(), analyticsProto.getCallDurationMillis(), analyticsProto.getType(), analyticsProto.getIsAdditionalCall(), analyticsProto.getIsInterrupted(), analyticsProto.getCallTechnologies(), analyticsProto.getCallTerminationCode(), analyticsProto.getIsEmergencyCall(), analyticsProto.connectionService[0], analyticsProto.getIsCreatedFromExistingConnection(), events, timings); result.setIsVideoCall(analyticsProto.getIsVideoCall()); result.setVideoEvents(Arrays.stream(analyticsProto.videoEvents) .map(videoEventProto -> new ParcelableCallAnalytics.VideoEvent( videoEventProto.getEventName(), videoEventProto.getTimeSinceLastEventMillis(), videoEventProto.getVideoState()) ).collect(Collectors.toList())); return result; } public TelecomLogClass.CallLog toProto() { TelecomLogClass.CallLog result = new TelecomLogClass.CallLog(); result.setStartTime5Min( startTime - startTime % ParcelableCallAnalytics.MILLIS_IN_5_MINUTES); // Rounds up to the nearest second. long callDuration = (endTime == 0 || startTime == 0) ? 0 : endTime - startTime; callDuration += (callDuration % MILLIS_IN_1_SECOND == 0) ? 0 : (MILLIS_IN_1_SECOND - callDuration % MILLIS_IN_1_SECOND); result.setCallDurationMillis(callDuration); List<AnalyticsEvent> events; List<ParcelableCallAnalytics.EventTiming> timings; if (callEvents != null) { events = convertLogEventsToAnalyticsEvents(callEvents.getEvents()); timings = callEvents.extractEventTimings().stream() .map(Analytics::logEventTimingToAnalyticsEventTiming) .collect(Collectors.toList()); } else { events = Collections.emptyList(); timings = Collections.emptyList(); } ParcelableCallAnalytics result = new ParcelableCallAnalytics( // rounds down to nearest 5 minute mark startTime - startTime % ParcelableCallAnalytics.MILLIS_IN_5_MINUTES, callDuration, callDirection, isAdditionalCall, isInterrupted, callTechnologies, result.setType(callDirection) .setIsAdditionalCall(isAdditionalCall) .setIsInterrupted(isInterrupted) .setCallTechnologies(callTechnologies) .setCallTerminationCode( callTerminationReason == null ? ParcelableCallAnalytics.STILL_CONNECTED : callTerminationReason.getCode(), isEmergency, connectionService, createdFromExistingConnection, events, timings); result.setIsVideoCall(isVideo); result.setVideoEvents(videoEvents); callTerminationReason.getCode()) .setIsEmergencyCall(isEmergency) .setIsCreatedFromExistingConnection(createdFromExistingConnection) .setIsEmergencyCall(isEmergency) .setIsVideoCall(isVideo); result.connectionService = new String[] {connectionService}; if (callEvents != null) { result.callEvents = convertLogEventsToProtoEvents(callEvents.getEvents()); result.callTimings = callEvents.extractEventTimings().stream() .map(Analytics::logEventTimingToProtoEventTiming) .toArray(TelecomLogClass.EventTimingEntry[]::new); } result.videoEvents = videoEvents.toArray(new TelecomLogClass.VideoEvent[videoEvents.size()]); return result; } Loading Loading @@ -463,6 +511,28 @@ public class Analytics { return new TelecomAnalytics(sessionTimings, calls); } public static void dumpToEncodedProto(PrintWriter pw, String[] args) { TelecomLogClass.TelecomLog result = new TelecomLogClass.TelecomLog(); synchronized (sLock) { result.callLogs = sCallIdToInfo.values().stream() .map(CallInfoImpl::toProto) .toArray(TelecomLogClass.CallLog[]::new); result.sessionTimings = sSessionTimings.stream() .map(timing -> new TelecomLogClass.LogSessionTiming() .setSessionEntryPoint(timing.getKey()) .setTimeMillis(timing.getTime())) .toArray(TelecomLogClass.LogSessionTiming[]::new); if (args.length > 1 && CLEAR_ANALYTICS_ARG.equals(args[1])) { sCallIdToInfo.clear(); sSessionTimings.clear(); } } String encodedProto = Base64.encodeToString( TelecomLogClass.TelecomLog.toByteArray(result), Base64.DEFAULT); pw.write(encodedProto); } public static void dump(IndentingPrintWriter writer) { synchronized (sLock) { int prefixLength = CallsManager.TELECOM_CALL_ID_PREFIX.length(); Loading Loading @@ -521,33 +591,32 @@ public class Analytics { } } private static List<AnalyticsEvent> convertLogEventsToAnalyticsEvents( private static TelecomLogClass.Event[] convertLogEventsToProtoEvents( List<Log.CallEvent> logEvents) { long timeOfLastEvent = -1; ArrayList<AnalyticsEvent> events = new ArrayList<>(logEvents.size()); ArrayList<TelecomLogClass.Event> events = new ArrayList<>(logEvents.size()); for (Log.CallEvent logEvent : logEvents) { if (sLogEventToAnalyticsEvent.containsKey(logEvent.eventId)) { int analyticsEventId = sLogEventToAnalyticsEvent.get(logEvent.eventId); long timeSinceLastEvent = timeOfLastEvent < 0 ? -1 : logEvent.time - timeOfLastEvent; events.add(new AnalyticsEvent( analyticsEventId, roundToOneSigFig(timeSinceLastEvent) )); TelecomLogClass.Event event = new TelecomLogClass.Event(); event.setEventName(sLogEventToAnalyticsEvent.get(logEvent.eventId)); event.setTimeSinceLastEventMillis(roundToOneSigFig( timeOfLastEvent < 0 ? -1 : logEvent.time - timeOfLastEvent)); events.add(event); timeOfLastEvent = logEvent.time; } } return events; return events.toArray(new TelecomLogClass.Event[events.size()]); } private static ParcelableCallAnalytics.EventTiming logEventTimingToAnalyticsEventTiming( private static TelecomLogClass.EventTimingEntry logEventTimingToProtoEventTiming( Log.CallEventRecord.EventTiming logEventTiming) { int analyticsEventTimingName = sLogEventTimingToAnalyticsEventTiming.containsKey(logEventTiming.name) ? sLogEventTimingToAnalyticsEventTiming.get(logEventTiming.name) : ParcelableCallAnalytics.EventTiming.INVALID; return new ParcelableCallAnalytics.EventTiming(analyticsEventTimingName, (long) logEventTiming.time); return new TelecomLogClass.EventTimingEntry() .setTimingName(analyticsEventTimingName) .setTimeMillis(logEventTiming.time); } @VisibleForTesting Loading src/com/android/server/telecom/TelecomServiceImpl.java +5 −3 Original line number Diff line number Diff line Loading @@ -39,7 +39,6 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.telecom.DefaultDialerManager; import android.telecom.ParcelableCallAnalytics; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomAnalytics; Loading @@ -57,8 +56,6 @@ import com.android.server.telecom.settings.BlockedNumbersActivity; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; Loading Loading @@ -1174,6 +1171,11 @@ public class TelecomServiceImpl { return; } if (args.length > 0 && Analytics.ANALYTICS_DUMPSYS_ARG.equals(args[0])) { Analytics.dumpToEncodedProto(writer, args); return; } final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); if (mCallsManager != null) { pw.println("CallsManager: "); Loading src/com/android/server/telecom/VideoProviderProxy.java +3 −1 Original line number Diff line number Diff line Loading @@ -187,7 +187,9 @@ public class VideoProviderProxy extends Connection.VideoProvider { if (status == Connection.VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) { mCall.getAnalytics().addVideoEvent( Analytics.RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE, requestProfile.getVideoState()); responseProfile == null ? VideoProfile.STATE_AUDIO_ONLY : responseProfile.getVideoState()); } VideoProviderProxy.this.receiveSessionModifyResponse(status, requestProfile, responseProfile); Loading Loading
Android.mk +5 −1 Original line number Diff line number Diff line Loading @@ -5,9 +5,13 @@ include $(CLEAR_VARS) LOCAL_JAVA_LIBRARIES := telephony-common LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under, proto) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_PROTOC_OPTIMIZE_TYPE := nano LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/proto/ LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors LOCAL_PACKAGE_NAME := Telecom LOCAL_CERTIFICATE := platform Loading
proto/telecom.proto 0 → 100644 +256 −0 Original line number Diff line number Diff line syntax = "proto2"; package com.android.server.telecom; option java_package = "com.android.server.telecom"; option java_outer_classname = "TelecomLogClass"; // The information about the telecom events. message TelecomLog { // Information about each call. repeated CallLog call_logs = 1; // Timing information for the logging sessions repeated LogSessionTiming session_timings = 2; } message LogSessionTiming { enum SessionEntryPoint { ICA_ANSWER_CALL = 1; ICA_REJECT_CALL = 2; ICA_DISCONNECT_CALL = 3; ICA_HOLD_CALL = 4; ICA_UNHOLD_CALL = 5; ICA_MUTE = 6; ICA_SET_AUDIO_ROUTE = 7; ICA_CONFERENCE = 8; CSW_HANDLE_CREATE_CONNECTION_COMPLETE = 100; CSW_SET_ACTIVE = 101; CSW_SET_RINGING = 102; CSW_SET_DIALING = 103; CSW_SET_DISCONNECTED = 104; CSW_SET_ON_HOLD = 105; CSW_REMOVE_CALL = 106; CSW_SET_IS_CONFERENCED = 107; CSW_ADD_CONFERENCE_CALL = 108; } // The entry point into Telecom code that this session tracks. optional SessionEntryPoint sessionEntryPoint = 1; // The time it took for this session to finish. optional int64 time_millis = 2; } message Event { // From android.telecom.ParcelableAnalytics enum EventName { SET_SELECT_PHONE_ACCOUNT = 0; SET_ACTIVE = 1; SET_DISCONNECTED = 2; START_CONNECTION = 3; SET_DIALING = 4; BIND_CS = 5; CS_BOUND = 6; REQUEST_ACCEPT = 7; REQUEST_REJECT = 8; SCREENING_SENT = 100; SCREENING_COMPLETED = 101; DIRECT_TO_VM_INITIATED = 102; DIRECT_TO_VM_FINISHED = 103; BLOCK_CHECK_INITIATED = 104; BLOCK_CHECK_FINISHED = 105; FILTERING_INITIATED = 106; FILTERING_COMPLETED = 107; FILTERING_TIMED_OUT = 108; SKIP_RINGING = 200; SILENCE = 201; MUTE = 202; UNMUTE = 203; AUDIO_ROUTE_BT = 204; AUDIO_ROUTE_EARPIECE = 205; AUDIO_ROUTE_HEADSET = 206; AUDIO_ROUTE_SPEAKER = 207; CONFERENCE_WITH = 300; SPLIT_CONFERENCE = 301; SET_PARENT = 302; REQUEST_HOLD = 400; REQUEST_UNHOLD = 401; REMOTELY_HELD = 402; REMOTELY_UNHELD = 403; SET_HOLD = 404; SWAP = 405; REQUEST_PULL = 500; } // The ID of the event. optional EventName event_name = 1; // The elapsed time since the last event, rounded to one significant digit. // If the event is the first, this will be negative. optional int64 time_since_last_event_millis = 2; } message VideoEvent { // From android.telecom.ParcelableCallAnalytics enum VideoEventName { SEND_LOCAL_SESSION_MODIFY_REQUEST = 0; SEND_LOCAL_SESSION_MODIFY_RESPONSE = 1; RECEIVE_REMOTE_SESSION_MODIFY_REQUEST = 2; RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE = 3; } // From android.telecom.VideoProfile enum VideoState { STATE_AUDIO_ONLY = 0; STATE_TX_ENABLED = 1; STATE_RX_ENABLED = 2; STATE_BIDIRECTIONAL = 3; STATE_PAUSED = 4; } // The ID of the event. optional VideoEventName event_name = 1; // The elapsed time since the last event, rounded to one significant digit. // If the event is the first, this will be negative. optional int64 time_since_last_event_millis = 2; // The video state optional int32 video_state = 3; } message EventTimingEntry { enum EventTimingName { ACCEPT_TIMING = 0; REJECT_TIMING = 1; DISCONNECT_TIMING = 2; HOLD_TIMING = 3; UNHOLD_TIMING = 4; OUTGOING_TIME_TO_DIALING_TIMING = 5; BIND_CS_TIMING = 6; SCREENING_COMPLETED_TIMING = 7; DIRECT_TO_VM_FINISHED_TIMING = 8; BLOCK_CHECK_FINISHED_TIMING = 9; FILTERING_COMPLETED_TIMING = 10; FILTERING_TIMED_OUT_TIMING = 11; } // The name of the event timing. optional EventTimingName timing_name = 1; // The number of milliseconds that this event pair took. optional int64 time_millis = 2; } // Information about each call. message CallLog { // Information on call-types. enum CallType { // Call type is not known. CALLTYPE_UNKNOWN = 0; // Incoming call. CALLTYPE_INCOMING = 1; // Outgoing call. CALLTYPE_OUTGOING = 2; } // Termination code. enum CallTerminationCode { // Disconnected because of an unknown or unspecified reason. CALL_TERMINATION_CODE_UNKNOWN = 0; // Disconnected because there was an error, such as a problem // with the network. CALL_TERMINATION_CODE_ERROR = 1; // Disconnected because of a local user-initiated action, // such as hanging up. CALL_TERMINATION_CODE_LOCAL = 2; // Disconnected because of a remote user-initiated action, // such as the other party hanging up. CALL_TERMINATION_CODE_REMOTE = 3; // Disconnected because it has been canceled. CALL_TERMINATION_CODE_CANCELED = 4; // Disconnected because there was no response to an incoming call. CALL_TERMINATION_CODE_MISSED = 5; // Disconnected because the user rejected an incoming call. CALL_TERMINATION_CODE_REJECTED = 6; // Disconnected because the other party was busy. CALL_TERMINATION_CODE_BUSY = 7; // Disconnected because of a restriction on placing the call, // such as dialing in airplane mode. CALL_TERMINATION_CODE_RESTRICTED = 8; // Disconnected for reason not described by other disconnect codes. CALL_TERMINATION_CODE_OTHER = 9; // Disconnected because the connection manager did not support the call. // The call will be tried again without a connection manager. CONNECTION_MANAGER_NOT_SUPPORTED = 10; } // Start time of the connection. // Rounded to the nearest 5 minute interval. optional int64 start_time_5min = 1; // Duration in millis. optional int64 call_duration_millis = 2; // Call type. optional CallType type = 3; // True if the call interrupted an in-progress call, whether it was the // user dialing out during a call or an incoming call during another call. optional bool is_additional_call = 4 [default = false]; // True if the call was interrupted by another call. optional bool is_interrupted = 5 [default = false]; // A bitmask with bits corresponding to call technologies that were used // during the call. The ones that we will record are CDMA, GSM, IMS, SIP, // and third-party. // https://googleplex-android-review.git.corp.google.com/#/c/816516/6/src/com/android/server/telecom/Analytics.java optional int32 call_technologies = 6; // Indicates the call termination code. optional CallTerminationCode call_termination_code = 7; // A list of the package names of connection services used. repeated string connection_service = 9; // Set to true if the call was created from createCallForExistingConnection. optional bool is_created_from_existing_connection = 10 [default = false]; // Set to true if its an emergency call. optional bool is_emergency_call = 11 [default = false]; // A list of the events that occur during the call. repeated Event call_events = 12; // A map from the names of latency timings to the timings. repeated EventTimingEntry call_timings = 13; // Whether this call has ever been a video call optional bool is_video_call = 14 [default = false]; // A list of the video events during the call. repeated VideoEvent video_events = 15; }
src/com/android/server/telecom/Analytics.java +113 −44 Original line number Diff line number Diff line Loading @@ -19,11 +19,14 @@ package com.android.server.telecom; import android.telecom.DisconnectCause; import android.telecom.ParcelableCallAnalytics; import android.telecom.TelecomAnalytics; import android.util.Base64; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; Loading @@ -39,6 +42,9 @@ import static android.telecom.TelecomAnalytics.SessionTiming; * aggregate these into useful statistics. */ public class Analytics { public static final String ANALYTICS_DUMPSYS_ARG = "analytics"; private static final String CLEAR_ANALYTICS_ARG = "clear"; public static final Map<String, Integer> sLogEventToAnalyticsEvent = new HashMap<String, Integer>() {{ put(Log.Events.SET_SELECT_PHONE_ACCOUNT, AnalyticsEvent.SET_SELECT_PHONE_ACCOUNT); Loading Loading @@ -200,7 +206,7 @@ public class Analytics { public Log.CallEventRecord callEvents; public boolean isVideo = false; public List<ParcelableCallAnalytics.VideoEvent> videoEvents; public List<TelecomLogClass.VideoEvent> videoEvents; private long mTimeOfLastVideoEvent = -1; CallInfoImpl(String callId, int callDirection) { Loading Loading @@ -311,8 +317,10 @@ public class Analytics { } mTimeOfLastVideoEvent = currentTime; videoEvents.add(new ParcelableCallAnalytics.VideoEvent( eventId, timeSinceLastEvent, videoState)); videoEvents.add(new TelecomLogClass.VideoEvent() .setEventName(eventId) .setTimeSinceLastEventMillis(timeSinceLastEvent) .setVideoState(videoState)); } @Override Loading @@ -331,40 +339,80 @@ public class Analytics { } public ParcelableCallAnalytics toParcelableAnalytics() { TelecomLogClass.CallLog analyticsProto = toProto(); List<ParcelableCallAnalytics.AnalyticsEvent> events = Arrays.stream(analyticsProto.callEvents) .map(callEventProto -> new ParcelableCallAnalytics.AnalyticsEvent( callEventProto.getEventName(), callEventProto.getTimeSinceLastEventMillis()) ).collect(Collectors.toList()); List<ParcelableCallAnalytics.EventTiming> timings = Arrays.stream(analyticsProto.callTimings) .map(callTimingProto -> new ParcelableCallAnalytics.EventTiming( callTimingProto.getTimingName(), callTimingProto.getTimeMillis()) ).collect(Collectors.toList()); ParcelableCallAnalytics result = new ParcelableCallAnalytics( // rounds down to nearest 5 minute mark analyticsProto.getStartTime5Min(), analyticsProto.getCallDurationMillis(), analyticsProto.getType(), analyticsProto.getIsAdditionalCall(), analyticsProto.getIsInterrupted(), analyticsProto.getCallTechnologies(), analyticsProto.getCallTerminationCode(), analyticsProto.getIsEmergencyCall(), analyticsProto.connectionService[0], analyticsProto.getIsCreatedFromExistingConnection(), events, timings); result.setIsVideoCall(analyticsProto.getIsVideoCall()); result.setVideoEvents(Arrays.stream(analyticsProto.videoEvents) .map(videoEventProto -> new ParcelableCallAnalytics.VideoEvent( videoEventProto.getEventName(), videoEventProto.getTimeSinceLastEventMillis(), videoEventProto.getVideoState()) ).collect(Collectors.toList())); return result; } public TelecomLogClass.CallLog toProto() { TelecomLogClass.CallLog result = new TelecomLogClass.CallLog(); result.setStartTime5Min( startTime - startTime % ParcelableCallAnalytics.MILLIS_IN_5_MINUTES); // Rounds up to the nearest second. long callDuration = (endTime == 0 || startTime == 0) ? 0 : endTime - startTime; callDuration += (callDuration % MILLIS_IN_1_SECOND == 0) ? 0 : (MILLIS_IN_1_SECOND - callDuration % MILLIS_IN_1_SECOND); result.setCallDurationMillis(callDuration); List<AnalyticsEvent> events; List<ParcelableCallAnalytics.EventTiming> timings; if (callEvents != null) { events = convertLogEventsToAnalyticsEvents(callEvents.getEvents()); timings = callEvents.extractEventTimings().stream() .map(Analytics::logEventTimingToAnalyticsEventTiming) .collect(Collectors.toList()); } else { events = Collections.emptyList(); timings = Collections.emptyList(); } ParcelableCallAnalytics result = new ParcelableCallAnalytics( // rounds down to nearest 5 minute mark startTime - startTime % ParcelableCallAnalytics.MILLIS_IN_5_MINUTES, callDuration, callDirection, isAdditionalCall, isInterrupted, callTechnologies, result.setType(callDirection) .setIsAdditionalCall(isAdditionalCall) .setIsInterrupted(isInterrupted) .setCallTechnologies(callTechnologies) .setCallTerminationCode( callTerminationReason == null ? ParcelableCallAnalytics.STILL_CONNECTED : callTerminationReason.getCode(), isEmergency, connectionService, createdFromExistingConnection, events, timings); result.setIsVideoCall(isVideo); result.setVideoEvents(videoEvents); callTerminationReason.getCode()) .setIsEmergencyCall(isEmergency) .setIsCreatedFromExistingConnection(createdFromExistingConnection) .setIsEmergencyCall(isEmergency) .setIsVideoCall(isVideo); result.connectionService = new String[] {connectionService}; if (callEvents != null) { result.callEvents = convertLogEventsToProtoEvents(callEvents.getEvents()); result.callTimings = callEvents.extractEventTimings().stream() .map(Analytics::logEventTimingToProtoEventTiming) .toArray(TelecomLogClass.EventTimingEntry[]::new); } result.videoEvents = videoEvents.toArray(new TelecomLogClass.VideoEvent[videoEvents.size()]); return result; } Loading Loading @@ -463,6 +511,28 @@ public class Analytics { return new TelecomAnalytics(sessionTimings, calls); } public static void dumpToEncodedProto(PrintWriter pw, String[] args) { TelecomLogClass.TelecomLog result = new TelecomLogClass.TelecomLog(); synchronized (sLock) { result.callLogs = sCallIdToInfo.values().stream() .map(CallInfoImpl::toProto) .toArray(TelecomLogClass.CallLog[]::new); result.sessionTimings = sSessionTimings.stream() .map(timing -> new TelecomLogClass.LogSessionTiming() .setSessionEntryPoint(timing.getKey()) .setTimeMillis(timing.getTime())) .toArray(TelecomLogClass.LogSessionTiming[]::new); if (args.length > 1 && CLEAR_ANALYTICS_ARG.equals(args[1])) { sCallIdToInfo.clear(); sSessionTimings.clear(); } } String encodedProto = Base64.encodeToString( TelecomLogClass.TelecomLog.toByteArray(result), Base64.DEFAULT); pw.write(encodedProto); } public static void dump(IndentingPrintWriter writer) { synchronized (sLock) { int prefixLength = CallsManager.TELECOM_CALL_ID_PREFIX.length(); Loading Loading @@ -521,33 +591,32 @@ public class Analytics { } } private static List<AnalyticsEvent> convertLogEventsToAnalyticsEvents( private static TelecomLogClass.Event[] convertLogEventsToProtoEvents( List<Log.CallEvent> logEvents) { long timeOfLastEvent = -1; ArrayList<AnalyticsEvent> events = new ArrayList<>(logEvents.size()); ArrayList<TelecomLogClass.Event> events = new ArrayList<>(logEvents.size()); for (Log.CallEvent logEvent : logEvents) { if (sLogEventToAnalyticsEvent.containsKey(logEvent.eventId)) { int analyticsEventId = sLogEventToAnalyticsEvent.get(logEvent.eventId); long timeSinceLastEvent = timeOfLastEvent < 0 ? -1 : logEvent.time - timeOfLastEvent; events.add(new AnalyticsEvent( analyticsEventId, roundToOneSigFig(timeSinceLastEvent) )); TelecomLogClass.Event event = new TelecomLogClass.Event(); event.setEventName(sLogEventToAnalyticsEvent.get(logEvent.eventId)); event.setTimeSinceLastEventMillis(roundToOneSigFig( timeOfLastEvent < 0 ? -1 : logEvent.time - timeOfLastEvent)); events.add(event); timeOfLastEvent = logEvent.time; } } return events; return events.toArray(new TelecomLogClass.Event[events.size()]); } private static ParcelableCallAnalytics.EventTiming logEventTimingToAnalyticsEventTiming( private static TelecomLogClass.EventTimingEntry logEventTimingToProtoEventTiming( Log.CallEventRecord.EventTiming logEventTiming) { int analyticsEventTimingName = sLogEventTimingToAnalyticsEventTiming.containsKey(logEventTiming.name) ? sLogEventTimingToAnalyticsEventTiming.get(logEventTiming.name) : ParcelableCallAnalytics.EventTiming.INVALID; return new ParcelableCallAnalytics.EventTiming(analyticsEventTimingName, (long) logEventTiming.time); return new TelecomLogClass.EventTimingEntry() .setTimingName(analyticsEventTimingName) .setTimeMillis(logEventTiming.time); } @VisibleForTesting Loading
src/com/android/server/telecom/TelecomServiceImpl.java +5 −3 Original line number Diff line number Diff line Loading @@ -39,7 +39,6 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.telecom.DefaultDialerManager; import android.telecom.ParcelableCallAnalytics; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomAnalytics; Loading @@ -57,8 +56,6 @@ import com.android.server.telecom.settings.BlockedNumbersActivity; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; Loading Loading @@ -1174,6 +1171,11 @@ public class TelecomServiceImpl { return; } if (args.length > 0 && Analytics.ANALYTICS_DUMPSYS_ARG.equals(args[0])) { Analytics.dumpToEncodedProto(writer, args); return; } final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); if (mCallsManager != null) { pw.println("CallsManager: "); Loading
src/com/android/server/telecom/VideoProviderProxy.java +3 −1 Original line number Diff line number Diff line Loading @@ -187,7 +187,9 @@ public class VideoProviderProxy extends Connection.VideoProvider { if (status == Connection.VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) { mCall.getAnalytics().addVideoEvent( Analytics.RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE, requestProfile.getVideoState()); responseProfile == null ? VideoProfile.STATE_AUDIO_ONLY : responseProfile.getVideoState()); } VideoProviderProxy.this.receiveSessionModifyResponse(status, requestProfile, responseProfile); Loading