[WifiScoreReport] synchronize dumpsys

Fix the possibility of a concurrent modification exception during
dumpsys handling.

Bug: 69064836
Test: unit tests
Change-Id: Ib9de18b1b02bff1fade59b7642042e73d1ab3511
diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java
index af53850..e5281ef 100644
--- a/service/java/com/android/server/wifi/WifiScoreReport.java
+++ b/service/java/com/android/server/wifi/WifiScoreReport.java
@@ -179,19 +179,23 @@
         double txRetriesRate = wifiInfo.txRetriesRate;
         double txBadRate = wifiInfo.txBadRate;
         double rxSuccessRate = wifiInfo.rxSuccessRate;
+        String s;
         try {
             String timestamp = new SimpleDateFormat("MM-dd HH:mm:ss.SSS").format(new Date(now));
-            String s = String.format(Locale.US, // Use US to avoid comma/decimal confusion
+            s = String.format(Locale.US, // Use US to avoid comma/decimal confusion
                     "%s,%d,%.1f,%.1f,%.1f,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d,%d",
                     timestamp, mSessionNumber, rssi, filteredRssi, rssiThreshold, freq, linkSpeed,
                     txSuccessRate, txRetriesRate, txBadRate, rxSuccessRate,
                     s0, s1, s2);
-            mLinkMetricsHistory.add(s);
         } catch (Exception e) {
             Log.e(TAG, "format problem", e);
+            return;
         }
-        while (mLinkMetricsHistory.size() > DUMPSYS_ENTRY_COUNT_LIMIT) {
-            mLinkMetricsHistory.removeFirst();
+        synchronized (mLinkMetricsHistory) {
+            mLinkMetricsHistory.add(s);
+            while (mLinkMetricsHistory.size() > DUMPSYS_ENTRY_COUNT_LIMIT) {
+                mLinkMetricsHistory.removeFirst();
+            }
         }
     }
 
@@ -207,10 +211,15 @@
      * @param args unused
      */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        LinkedList<String> history;
+        synchronized (mLinkMetricsHistory) {
+            history = new LinkedList<>(mLinkMetricsHistory);
+        }
         pw.println("time,session,rssi,filtered_rssi,rssi_threshold,"
                 + "freq,linkspeed,tx_good,tx_retry,tx_bad,rx,s0,s1,s2");
-        for (String line : mLinkMetricsHistory) {
+        for (String line : history) {
             pw.println(line);
         }
+        history.clear();
     }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
index c334e7c..022d5dd 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
@@ -17,10 +17,13 @@
 package com.android.server.wifi;
 
 import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalAnswers.answerVoid;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -266,6 +269,21 @@
     }
 
     /**
+     * This setup causes some reports to be generated when println
+     * methods are called, to check for "concurrent" modification
+     * errors.
+     */
+    private void setupToGenerateAReportWhenPrintlnIsCalled() {
+        int[] counter = new int[1];
+        doAnswer(answerVoid((String line) -> {
+            if (counter[0]++ < 3) {
+                mWifiScoreReport.calculateAndReportScore(
+                        mWifiInfo, mNetworkAgent, mAggr, mWifiMetrics);
+            }
+        })).when(mPrintWriter).println(anyString());
+    }
+
+    /**
      * Test data logging
      */
     @Test
@@ -281,8 +299,9 @@
             mWifiInfo.rxSuccessRate = 0.3 + i;
             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mAggr, mWifiMetrics);
         }
+        setupToGenerateAReportWhenPrintlnIsCalled();
         mWifiScoreReport.dump(null, mPrintWriter, null);
-        verify(mPrintWriter, atLeast(11)).println(anyString());
+        verify(mPrintWriter, times(11)).println(anyString());
     }
 
     /**