Add cap on number of calls stored in analytics

Cap the number of calls stored in analytics in order to avoid chewing up
excessive memory on devices that are used to make >100 calls per day.

Test: unit test added + manual testing
Bug: 33965537
Change-Id: Ibb5508b4e79f5dde03caa44abe1da02116ae81fc
diff --git a/src/com/android/server/telecom/Analytics.java b/src/com/android/server/telecom/Analytics.java
index 4456734..2b6ace5 100644
--- a/src/com/android/server/telecom/Analytics.java
+++ b/src/com/android/server/telecom/Analytics.java
@@ -33,6 +33,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.PriorityQueue;
 import java.util.stream.Collectors;
 
 import static android.telecom.ParcelableCallAnalytics.AnalyticsEvent;
@@ -521,8 +522,11 @@
 
     public static final long MILLIS_IN_1_SECOND = ParcelableCallAnalytics.MILLIS_IN_1_SECOND;
 
+    public static final int MAX_NUM_CALLS_TO_STORE = 100;
+
     private static final Object sLock = new Object(); // Coarse lock for all of analytics
     private static final Map<String, CallInfoImpl> sCallIdToInfo = new HashMap<>();
+    private static final LinkedList<String> sActiveCallIds = new LinkedList<>();
     private static final List<SessionTiming> sSessionTimings = new LinkedList<>();
 
     public static void addSessionTiming(String sessionName, long time) {
@@ -538,7 +542,12 @@
         Log.d(TAG, "Starting analytics for call " + callId);
         CallInfoImpl callInfo = new CallInfoImpl(callId, direction);
         synchronized (sLock) {
+            while (sActiveCallIds.size() >= MAX_NUM_CALLS_TO_STORE) {
+                String callToRemove = sActiveCallIds.remove();
+                sCallIdToInfo.remove(callToRemove);
+            }
             sCallIdToInfo.put(callId, callInfo);
+            sActiveCallIds.add(callId);
         }
         return callInfo;
     }
diff --git a/tests/src/com/android/server/telecom/tests/AnalyticsTests.java b/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
index 81d4aa5..021ce82 100644
--- a/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
+++ b/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
@@ -36,6 +36,7 @@
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -337,6 +338,26 @@
                 analyticsProto.callLogs[0].getConnectionProperties() & expectedProperties);
     }
 
+    @SmallTest
+    public void testAnalyticsMaxSize() throws Exception {
+        Analytics.reset();
+        for (int i = 0; i < Analytics.MAX_NUM_CALLS_TO_STORE * 2; i++) {
+            Analytics.initiateCallAnalytics(String.valueOf(i), Analytics.INCOMING_DIRECTION)
+                    .addCallTechnology(i);
+        }
+
+        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(Analytics.MAX_NUM_CALLS_TO_STORE, analyticsProto.callLogs.length);
+        assertEquals(Arrays.stream(analyticsProto.callLogs)
+                .filter(x -> x.getCallTechnologies() < 100)
+                .count(), 0);
+    }
+
     private void assertIsRoundedToOneSigFig(long x) {
         assertEquals(x, Analytics.roundToOneSigFig(x));
     }