diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManager.java index fa8c96b1d..28b22b0df 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManager.java @@ -53,7 +53,13 @@ public class CallQualitySurveyManager { final CallQualitySurveyResponsePubSubMessage.Builder pubSubMessageBuilder = CallQualitySurveyResponsePubSubMessage.newBuilder() .setResponseId(UUID.randomUUID().toString()) - .setSubmissionTimestamp(clock.millis() * 1000); + .setSubmissionTimestamp(clock.millis() * 1000) + .setUserSatisfied(submitCallQualitySurveyRequest.getUserSatisfied()) + .setStartTimestamp(submitCallQualitySurveyRequest.getStartTimestamp()) + .setEndTimestamp(submitCallQualitySurveyRequest.getEndTimestamp()) + .setCallType(submitCallQualitySurveyRequest.getCallType()) + .setSuccess(submitCallQualitySurveyRequest.getSuccess()) + .setCallEndReason(submitCallQualitySurveyRequest.getCallEndReason()); try { final UserAgent userAgent = UserAgentUtil.parseUserAgentString(userAgentString); @@ -70,10 +76,6 @@ public class CallQualitySurveyManager { asnInfoProviderSupplier.get().lookup(remoteAddress) .ifPresent(asnInfo -> pubSubMessageBuilder.setAsnRegion(asnInfo.regionCode())); - if (submitCallQualitySurveyRequest.hasUserSatisfied()) { - pubSubMessageBuilder.setUserSatisfied(submitCallQualitySurveyRequest.getUserSatisfied()); - } - pubSubMessageBuilder.addAllCallQualityIssues(submitCallQualitySurveyRequest.getCallQualityIssuesList()); if (submitCallQualitySurveyRequest.hasAdditionalIssuesDescription()) { @@ -84,36 +86,28 @@ public class CallQualitySurveyManager { pubSubMessageBuilder.setDebugLogUrl(submitCallQualitySurveyRequest.getDebugLogUrl()); } - if (submitCallQualitySurveyRequest.hasStartTimestamp()) { - pubSubMessageBuilder.setStartTimestamp(submitCallQualitySurveyRequest.getStartTimestamp()); + if (submitCallQualitySurveyRequest.hasRttMedianConnection()) { + pubSubMessageBuilder.setRttMedianConnection(submitCallQualitySurveyRequest.getRttMedianConnection()); } - if (submitCallQualitySurveyRequest.hasEndTimestamp()) { - pubSubMessageBuilder.setEndTimestamp(submitCallQualitySurveyRequest.getEndTimestamp()); + if (submitCallQualitySurveyRequest.hasRttMedianMedia()) { + pubSubMessageBuilder.setRttMedianMedia(submitCallQualitySurveyRequest.getRttMedianMedia()); } - if (submitCallQualitySurveyRequest.hasCallType()) { - pubSubMessageBuilder.setCallType(submitCallQualitySurveyRequest.getCallType()); + if (submitCallQualitySurveyRequest.hasJitterMedianRecv()) { + pubSubMessageBuilder.setJitterMedianRecv(submitCallQualitySurveyRequest.getJitterMedianRecv()); } - if (submitCallQualitySurveyRequest.hasSuccess()) { - pubSubMessageBuilder.setSuccess(submitCallQualitySurveyRequest.getSuccess()); + if (submitCallQualitySurveyRequest.hasJitterMedianSend()) { + pubSubMessageBuilder.setJitterMedianSend(submitCallQualitySurveyRequest.getJitterMedianSend()); } - if (submitCallQualitySurveyRequest.hasCallEndReason()) { - pubSubMessageBuilder.setCallEndReason(submitCallQualitySurveyRequest.getCallEndReason()); + if (submitCallQualitySurveyRequest.hasPacketLossFractionRecv()) { + pubSubMessageBuilder.setPacketLossFractionRecv(submitCallQualitySurveyRequest.getPacketLossFractionRecv()); } - if (submitCallQualitySurveyRequest.hasRttMedian()) { - pubSubMessageBuilder.setRttMedian(submitCallQualitySurveyRequest.getRttMedian()); - } - - if (submitCallQualitySurveyRequest.hasJitterMedian()) { - pubSubMessageBuilder.setJitterMedian(submitCallQualitySurveyRequest.getJitterMedian()); - } - - if (submitCallQualitySurveyRequest.hasPacketLossFraction()) { - pubSubMessageBuilder.setPacketLossFraction(submitCallQualitySurveyRequest.getPacketLossFraction()); + if (submitCallQualitySurveyRequest.hasPacketLossFractionSend()) { + pubSubMessageBuilder.setPacketLossFractionSend(submitCallQualitySurveyRequest.getPacketLossFractionSend()); } if (submitCallQualitySurveyRequest.hasCallTelemetry()) { diff --git a/service/src/main/proto/CallQualitySurveyPubSub.proto b/service/src/main/proto/CallQualitySurveyPubSub.proto index abce776f5..f6268efcb 100644 --- a/service/src/main/proto/CallQualitySurveyPubSub.proto +++ b/service/src/main/proto/CallQualitySurveyPubSub.proto @@ -38,7 +38,7 @@ message CallQualitySurveyResponsePubSubMessage { // Indicates whether the user was generally satisfied with the quality of the // call - optional bool user_satisfied = 7; + required bool user_satisfied = 7; // A list of call quality issues selected by the user repeated string call_quality_issues = 8; @@ -52,37 +52,53 @@ message CallQualitySurveyResponsePubSubMessage { // The time at which the call started in microseconds since the epoch (see // https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#timestamp_type) - optional int64 start_timestamp = 11; + required int64 start_timestamp = 11; // The time at which the call ended in microseconds since the epoch (see // https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#timestamp_type) - optional int64 end_timestamp = 12; + required int64 end_timestamp = 12; // The type of call; note that direct voice calls can become video calls and // vice versa, and this field indicates which mode was selected at call // initiation time. At the time of writing, expected call types are // "direct_voice", "direct_video", "group", and "call_link". - optional string call_type = 13; + required string call_type = 13; // Indicates whether the call completed without error or if it terminated // abnormally - optional bool success = 14; + required bool success = 14; // A client-defined, but human-readable reason for call termination - optional string call_end_reason = 15; + required string call_end_reason = 15; - // The median round-trip time, measured in milliseconds, for packets over the - // duration of the call - optional float rtt_median = 16; + // The median round-trip time, measured in milliseconds, for STUN/ICE packets + // (i.e. connection maintenance and establishment) + optional float rtt_median_connection = 16; + + // The median round-trip time, measured in milliseconds, for RTP/RTCP packets + // (i.e. call audio/video) + optional float rtt_median_media = 17; // The median jitter, measured in milliseconds, for the duration of the call - optional float jitter_median = 17; + // as measured by the client submitting the survey + optional float jitter_median_recv = 18; - // The fraction of all packets lost over the duration of the call - optional float packet_loss_fraction = 18; + // The median jitter, measured in milliseconds, for the duration of the call + // as measured by the remote endpoint in the call (either the peer of the + // client submitting the survey in a direct call or the SFU in a group call) + optional float jitter_median_send = 19; + + // The fraction of all packets lost over the duration of the call as measured + // by the client submitting the survey + optional float packet_loss_fraction_recv = 20; + + // The fraction of all packets lost over the duration of the call as measured + // by the remote endpoint in the call (either the peer of the client + // submitting the survey in a direct call or the SFU in a group call) + optional float packet_loss_fraction_send = 21; // Technical, machine-generated data about the quality and mechanics of a // call; this is a serialized protobuf entity generated (and, critically, // explained to the user!) by the calling library - optional bytes call_telemetry = 19; + optional bytes call_telemetry = 22; } diff --git a/service/src/main/proto/org/signal/chat/call_quality.proto b/service/src/main/proto/org/signal/chat/call_quality.proto index 8b9acca42..e67a06a3e 100644 --- a/service/src/main/proto/org/signal/chat/call_quality.proto +++ b/service/src/main/proto/org/signal/chat/call_quality.proto @@ -24,7 +24,7 @@ service CallQuality { message SubmitCallQualitySurveyRequest { // Indicates whether the caller was generally satisfied with the quality of // the call - optional bool user_satisfied = 1; + bool user_satisfied = 1; // A list of call quality issues selected by the caller repeated string call_quality_issues = 2; @@ -38,39 +38,55 @@ message SubmitCallQualitySurveyRequest { // The time at which the call started in microseconds since the epoch (see // https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#timestamp_type) - optional int64 start_timestamp = 5; + int64 start_timestamp = 5; // The time at which the call ended in microseconds since the epoch (see // https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#timestamp_type) - optional int64 end_timestamp = 6; + int64 end_timestamp = 6; // The type of call; note that direct voice calls can become video calls and // vice versa, and this field indicates which mode was selected at call // initiation time. At the time of writing, expected call types are // "direct_voice", "direct_video", "group", and "call_link". - optional string call_type = 7; + string call_type = 7; // Indicates whether the call completed without error or if it terminated // abnormally - optional bool success = 8; + bool success = 8; // A client-defined, but human-readable reason for call termination - optional string call_end_reason = 9; + string call_end_reason = 9; - // The median round-trip time, measured in milliseconds, for packets over the - // duration of the call - optional float rtt_median = 10; + // The median round-trip time, measured in milliseconds, for STUN/ICE packets + // (i.e. connection maintenance and establishment) + optional float rtt_median_connection = 10; + + // The median round-trip time, measured in milliseconds, for RTP/RTCP packets + // (i.e. call audio/video) + optional float rtt_median_media = 11; // The median jitter, measured in milliseconds, for the duration of the call - optional float jitter_median = 11; + // as measured by the client submitting the survey + optional float jitter_median_recv = 12; - // The fraction of all packets lost over the duration of the call - optional float packet_loss_fraction = 12; + // The median jitter, measured in milliseconds, for the duration of the call + // as measured by the remote endpoint in the call (either the peer of the + // client submitting the survey in a direct call or the SFU in a group call) + optional float jitter_median_send = 13; + + // The fraction of all packets lost over the duration of the call as measured + // by the client submitting the survey + optional float packet_loss_fraction_recv = 14; + + // The fraction of all packets lost over the duration of the call as measured + // by the remote endpoint in the call (either the peer of the client + // submitting the survey in a direct call or the SFU in a group call) + optional float packet_loss_fraction_send = 15; // Machine-generated telemetry from the call; this is a serialized protobuf // entity generated (and, critically, explained to the user!) by the calling // library - optional bytes call_telemetry = 13; + optional bytes call_telemetry = 16; } message SubmitCallQualitySurveyResponse { diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManagerTest.java index f7b0dbcb0..fb6f01e60 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManagerTest.java @@ -62,9 +62,12 @@ class CallQualitySurveyManagerTest { final byte[] telemetryBytes = TestRandomUtil.nextBytes(32); - final float rttMedian = ThreadLocalRandom.current().nextFloat(); - final float jitterMedian = ThreadLocalRandom.current().nextFloat(); - final float packetLossFraction = ThreadLocalRandom.current().nextFloat(); + final float rttMedianConnection = ThreadLocalRandom.current().nextFloat(); + final float rttMedianMedia = ThreadLocalRandom.current().nextFloat(); + final float jitterMedianRecv = ThreadLocalRandom.current().nextFloat(); + final float jitterMedianSend = ThreadLocalRandom.current().nextFloat(); + final float packetLossFractionRecv = ThreadLocalRandom.current().nextFloat(); + final float packetLossFractionSend = ThreadLocalRandom.current().nextFloat(); when(asnInfoProvider.lookup(REMOTE_ADDRESS)).thenReturn(Optional.of(new AsnInfo(asn, asnRegion))); @@ -79,9 +82,12 @@ class CallQualitySurveyManagerTest { .setCallType("direct_video") .setSuccess(true) .setCallEndReason("caller_hang_up") - .setRttMedian(rttMedian) - .setJitterMedian(jitterMedian) - .setPacketLossFraction(packetLossFraction) + .setRttMedianConnection(rttMedianConnection) + .setRttMedianMedia(rttMedianMedia) + .setJitterMedianRecv(jitterMedianRecv) + .setJitterMedianSend(jitterMedianSend) + .setPacketLossFractionRecv(packetLossFractionRecv) + .setPacketLossFractionSend(packetLossFractionSend) .setCallTelemetry(ByteString.copyFrom(telemetryBytes)) .build(); @@ -111,9 +117,12 @@ class CallQualitySurveyManagerTest { assertEquals("direct_video", callQualitySurveyResponsePubSubMessage.getCallType()); assertTrue(callQualitySurveyResponsePubSubMessage.getSuccess()); assertEquals("caller_hang_up", callQualitySurveyResponsePubSubMessage.getCallEndReason()); - assertEquals(rttMedian, callQualitySurveyResponsePubSubMessage.getRttMedian()); - assertEquals(jitterMedian, callQualitySurveyResponsePubSubMessage.getJitterMedian()); - assertEquals(packetLossFraction, callQualitySurveyResponsePubSubMessage.getPacketLossFraction()); + assertEquals(rttMedianConnection, callQualitySurveyResponsePubSubMessage.getRttMedianConnection()); + assertEquals(rttMedianMedia, callQualitySurveyResponsePubSubMessage.getRttMedianMedia()); + assertEquals(jitterMedianRecv, callQualitySurveyResponsePubSubMessage.getJitterMedianRecv()); + assertEquals(jitterMedianSend, callQualitySurveyResponsePubSubMessage.getJitterMedianSend()); + assertEquals(packetLossFractionRecv, callQualitySurveyResponsePubSubMessage.getPacketLossFractionRecv()); + assertEquals(packetLossFractionSend, callQualitySurveyResponsePubSubMessage.getPacketLossFractionSend()); assertArrayEquals(telemetryBytes, callQualitySurveyResponsePubSubMessage.getCallTelemetry().toByteArray()); } }