Add dumpsys to Telecom analytics output

Output a Base64-encoded proto when Telecom's dumpsys is called with the
"analytics" argument, with an option to clear analytics at the same
time. This is in preparation for moving Connectivity Metrics to GmsCore.

Also fixed a bug in video call analytics and adds a test for it.

Bug: 30600318

Change-Id: I567d769123d78b7e5e2b9e8865fccf36f4f30fae
diff --git a/tests/Android.mk b/tests/Android.mk
index e639b55..f57beb7 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -25,7 +25,12 @@
 
 LOCAL_SRC_FILES := \
         $(call all-java-files-under, src) \
-        $(call all-java-files-under, ../src)
+        $(call all-java-files-under, ../src) \
+        $(call all-proto-files-under, ../proto)
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../proto/
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
 
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res \
diff --git a/tests/src/com/android/server/telecom/tests/AnalyticsTests.java b/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
index 241f66d..d8e152a 100644
--- a/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
+++ b/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
@@ -18,21 +18,34 @@
 
 import android.content.Context;
 import android.telecom.DisconnectCause;
+import android.telecom.InCallService;
 import android.telecom.ParcelableCallAnalytics;
 import android.telecom.TelecomAnalytics;
 import android.telecom.TelecomManager;
+import android.telecom.VideoCallImpl;
+import android.telecom.VideoProfile;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
 
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.telecom.Analytics;
 import com.android.server.telecom.Log;
+import com.android.server.telecom.TelecomLogClass;
 
+import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
 
 public class AnalyticsTests extends TelecomSystemTest {
     @MediumTest
@@ -110,6 +123,7 @@
         Set<Integer> capturedEvents = new HashSet<>();
         for (ParcelableCallAnalytics.AnalyticsEvent e : analyticsEvents) {
             capturedEvents.add(e.getEventName());
+            assertIsRoundedToOneSigFig(e.getTimeSinceLastEvent());
         }
         assertTrue(capturedEvents.contains(ParcelableCallAnalytics.AnalyticsEvent.SET_ACTIVE));
         assertTrue(capturedEvents.contains(
@@ -168,6 +182,54 @@
         assertEquals(DisconnectCause.REMOTE, callAnalytics2.callTerminationReason.getCode());
     }
 
+    @MediumTest
+    public void testAnalyticsVideo() throws Exception {
+        Analytics.reset();
+        IdPair callIds = startAndMakeActiveOutgoingCall(
+                "650-555-1212",
+                mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+
+        CountDownLatch counter = new CountDownLatch(1);
+        InCallService.VideoCall.Callback callback = mock(InCallService.VideoCall.Callback.class);
+
+        doAnswer(invocation -> {
+            counter.countDown();
+            return null;
+        }).when(callback)
+                .onSessionModifyResponseReceived(anyInt(), any(VideoProfile.class),
+                        any(VideoProfile.class));
+
+        mConnectionServiceFixtureA.sendSetVideoProvider(
+                mConnectionServiceFixtureA.mLatestConnectionId);
+        InCallService.VideoCall videoCall =
+                mInCallServiceFixtureX.getCall(callIds.mCallId).getVideoCallImpl();
+        videoCall.registerCallback(callback);
+        ((VideoCallImpl) videoCall).setVideoState(VideoProfile.STATE_BIDIRECTIONAL);
+
+        videoCall.sendSessionModifyRequest(new VideoProfile(VideoProfile.STATE_RX_ENABLED));
+        counter.await(10000, TimeUnit.MILLISECONDS);
+
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        Analytics.dumpToEncodedProto(pw, new String[]{});
+        TelecomLogClass.TelecomLog analyticsProto =
+                TelecomLogClass.TelecomLog.parseFrom(Base64.decode(sw.toString(), Base64.DEFAULT));
+
+        assertEquals(1, analyticsProto.callLogs.length);
+        TelecomLogClass.VideoEvent[] videoEvents = analyticsProto.callLogs[0].videoEvents;
+        assertEquals(2, videoEvents.length);
+
+        assertEquals(Analytics.SEND_LOCAL_SESSION_MODIFY_REQUEST, videoEvents[0].getEventName());
+        assertEquals(VideoProfile.STATE_RX_ENABLED, videoEvents[0].getVideoState());
+        assertEquals(-1, videoEvents[0].getTimeSinceLastEventMillis());
+
+        assertEquals(Analytics.RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE,
+                videoEvents[1].getEventName());
+        assertEquals(VideoProfile.STATE_RX_ENABLED, videoEvents[1].getVideoState());
+        assertIsRoundedToOneSigFig(videoEvents[1].getTimeSinceLastEventMillis());
+    }
+
     @SmallTest
     public void testAnalyticsRounding() {
         long[] testVals = {0, -1, -10, -100, -57836, 1, 10, 100, 1000, 458457};
@@ -190,4 +252,55 @@
                         Analytics.sSessionIdToLogSession.get(s.getKey())))
                 .forEach(s -> assertTrue(s.getTime() > minTime));
     }
+
+    @MediumTest
+    public void testAnalyticsDumpToProto() throws Exception {
+        Analytics.reset();
+        IdPair testCall = startAndMakeActiveIncomingCall(
+                "650-555-1212",
+                mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+
+        mConnectionServiceFixtureA.
+                sendSetDisconnected(testCall.mConnectionId, DisconnectCause.ERROR);
+        Analytics.CallInfoImpl expectedAnalytics = Analytics.cloneData().get(testCall.mCallId);
+
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        Analytics.dumpToEncodedProto(pw, new String[]{});
+        TelecomLogClass.TelecomLog analyticsProto =
+                TelecomLogClass.TelecomLog.parseFrom(Base64.decode(sw.toString(), Base64.DEFAULT));
+
+        assertEquals(1, analyticsProto.callLogs.length);
+        TelecomLogClass.CallLog callLog = analyticsProto.callLogs[0];
+
+        assertTrue(Math.abs(expectedAnalytics.startTime - callLog.getStartTime5Min()) <
+                ParcelableCallAnalytics.MILLIS_IN_5_MINUTES);
+        assertEquals(0, callLog.getStartTime5Min() % ParcelableCallAnalytics.MILLIS_IN_5_MINUTES);
+        assertTrue(Math.abs((expectedAnalytics.endTime - expectedAnalytics.startTime) -
+                callLog.getCallDurationMillis()) < ParcelableCallAnalytics.MILLIS_IN_1_SECOND);
+        assertEquals(0,
+                callLog.getCallDurationMillis() % ParcelableCallAnalytics.MILLIS_IN_1_SECOND);
+
+        assertEquals(expectedAnalytics.callDirection, callLog.getType());
+        assertEquals(expectedAnalytics.isAdditionalCall, callLog.getIsAdditionalCall());
+        assertEquals(expectedAnalytics.isInterrupted, callLog.getIsInterrupted());
+        assertEquals(expectedAnalytics.callTechnologies, callLog.getCallTechnologies());
+        assertEquals(expectedAnalytics.callTerminationReason.getCode(),
+                callLog.getCallTerminationCode());
+        assertEquals(expectedAnalytics.connectionService, callLog.connectionService[0]);
+        TelecomLogClass.Event[] analyticsEvents = callLog.callEvents;
+        Set<Integer> capturedEvents = new HashSet<>();
+        for (TelecomLogClass.Event e : analyticsEvents) {
+            capturedEvents.add(e.getEventName());
+            assertIsRoundedToOneSigFig(e.getTimeSinceLastEventMillis());
+        }
+        assertTrue(capturedEvents.contains(ParcelableCallAnalytics.AnalyticsEvent.SET_ACTIVE));
+        assertTrue(capturedEvents.contains(
+                ParcelableCallAnalytics.AnalyticsEvent.FILTERING_INITIATED));
+    }
+
+    private void assertIsRoundedToOneSigFig(long x) {
+        assertEquals(x, Analytics.roundToOneSigFig(x));
+    }
 }