Log the number of consecutive connection failures

Log the number of consecutive connection failures and screen_on in each connection event.

Bug: 152925929
Test: atest com.android.server.wifi
Test: manual test with adb shell dumpsys wifi and check
numConsecutiveConnectionFailure from dump print

Change-Id: I75828fc5feff7dc1c2a35fc0c2e7d02147bd6022
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 6daca90..85ccfd1 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -264,6 +264,7 @@
                 mFrameworkFacade, mContext, wifiHandler);
         String l2KeySeed = Secure.getString(mContext.getContentResolver(), Secure.ANDROID_ID);
         mWifiScoreCard = new WifiScoreCard(mClock, l2KeySeed, mDeviceConfigFacade);
+        mWifiMetrics.setWifiScoreCard(mWifiScoreCard);
         mLruConnectionTracker = new LruConnectionTracker(MAX_RECENTLY_CONNECTED_NETWORK,
                 mContext);
         // Config Manager
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index 481fa20f..d5a3f35 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -204,6 +204,7 @@
     private WifiDataStall mWifiDataStall;
     private WifiLinkLayerStats mLastLinkLayerStats;
     private WifiHealthMonitor mWifiHealthMonitor;
+    private WifiScoreCard mWifiScoreCard;
     private String mLastBssid;
     private int mLastFrequency = -1;
     private int mSeqNumInsideFramework = 0;
@@ -979,6 +980,8 @@
                         sb.append("CREATOR_CARRIER");
                         break;
                 }
+                sb.append(", numConsecutiveConnectionFailure="
+                        + mConnectionEvent.numConsecutiveConnectionFailure);
             }
             return sb.toString();
         }
@@ -1134,6 +1137,11 @@
         mWifiHealthMonitor = wifiHealthMonitor;
     }
 
+    /** Sets internal WifiScoreCard member */
+    public void setWifiScoreCard(WifiScoreCard wifiScoreCard) {
+        mWifiScoreCard = wifiScoreCard;
+    }
+
     /**
      * Increment cumulative counters for link layer stats.
      * @param newStats
@@ -1393,6 +1401,14 @@
                     mCurrentConnectionEvent.mConnectionEvent.networkCreator =
                             WifiMetricsProto.ConnectionEvent.CREATOR_UNKNOWN;
                 }
+
+                mCurrentConnectionEvent.mConnectionEvent.screenOn = mScreenOn;
+                if (mCurrentConnectionEvent.mConfigSsid != null) {
+                    WifiScoreCard.NetworkConnectionStats recentStats = mWifiScoreCard.lookupNetwork(
+                            mCurrentConnectionEvent.mConfigSsid).getRecentStats();
+                    mCurrentConnectionEvent.mConnectionEvent.numConsecutiveConnectionFailure =
+                            recentStats.getCount(WifiScoreCard.CNT_CONSECUTIVE_CONNECTION_FAILURE);
+                }
             }
         }
     }
diff --git a/service/java/com/android/server/wifi/WifiScoreCard.java b/service/java/com/android/server/wifi/WifiScoreCard.java
index 61a8435..4f12470 100644
--- a/service/java/com/android/server/wifi/WifiScoreCard.java
+++ b/service/java/com/android/server/wifi/WifiScoreCard.java
@@ -838,6 +838,7 @@
                     if (rssi >= mDeviceConfigFacade.getHealthMonitorMinRssiThrDbm()) {
                         if (failureReason != BssidBlocklistMonitor.REASON_WRONG_PASSWORD) {
                             mRecentStats.incrementCount(CNT_CONNECTION_FAILURE);
+                            mRecentStats.incrementCount(CNT_CONSECUTIVE_CONNECTION_FAILURE);
                         }
                         switch (failureReason) {
                             case BssidBlocklistMonitor.REASON_AP_UNABLE_TO_HANDLE_NEW_STA:
@@ -904,6 +905,9 @@
                     }
                 }
             }
+            // Reset CNT_CONSECUTIVE_CONNECTION_FAILURE here so that it can report the correct
+            // failure count after a connection success
+            mRecentStats.clearCount(CNT_CONSECUTIVE_CONNECTION_FAILURE);
             mConnectionSessionStartTimeMs = TS_NONE;
             mLastRssiPollTimeMs = TS_NONE;
         }
@@ -1225,8 +1229,9 @@
     public static final int CNT_SHORT_CONNECTION_NONLOCAL = 6;
     public static final int CNT_DISCONNECTION_NONLOCAL = 7;
     public static final int CNT_DISCONNECTION = 8;
+    public static final int CNT_CONSECUTIVE_CONNECTION_FAILURE = 9;
     // Constant being used to keep track of how many counter there are.
-    public static final int NUMBER_CONNECTION_CNT_CODE = 9;
+    public static final int NUMBER_CONNECTION_CNT_CODE = 10;
     private static final String[] CONNECTION_CNT_NAME = {
         " ConnectAttempt: ",
         " ConnectFailure: ",
@@ -1236,7 +1241,8 @@
         " AuthFailure: ",
         " ShortDiscNonlocal: ",
         " DisconnectNonlocal: ",
-        " Disconnect: "
+        " Disconnect: ",
+        " ConsecutiveConnectFailure: "
     };
 
     @IntDef(prefix = { "CNT_" }, value = {
@@ -1248,7 +1254,8 @@
         CNT_AUTHENTICATION_FAILURE,
         CNT_SHORT_CONNECTION_NONLOCAL,
         CNT_DISCONNECTION_NONLOCAL,
-        CNT_DISCONNECTION
+        CNT_DISCONNECTION,
+        CNT_CONSECUTIVE_CONNECTION_FAILURE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ConnectionCountCode {}
@@ -1290,13 +1297,11 @@
         }
 
         /**
-         * Set counter value
-         * @param countCode is the selected counter
-         * @param cnt is the value set to the selected counter
+         * Clear counter value
+         * @param countCode is the selected counter to be cleared
          */
-        public void setCount(@ConnectionCountCode int countCode, int cnt) {
-            mCount[countCode] = cnt;
-            mRecentCountCode = countCode;
+        public void clearCount(@ConnectionCountCode int countCode) {
+            mCount[countCode] = 0;
         }
 
         /**
@@ -1309,7 +1314,7 @@
         }
 
         /**
-         * Got the recent count code
+         * Got the recent incremented count code
          */
         public int getRecentCountCode() {
             return mRecentCountCode;
diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto
index 12fc7bc..1c9ea37 100644
--- a/service/proto/src/metrics.proto
+++ b/service/proto/src/metrics.proto
@@ -1016,6 +1016,15 @@
 
   // UID of the app that created this network.
   optional NetworkCreator network_creator = 17;
+
+  // Whether the screen is on when the connection event starts
+  optional bool screen_on = 18;
+
+  // Number of consecutive connection failures with the same SSID at high RSSI
+  // before current connection event. Any connection failure at low RSSI in the
+  // middle won't break the streak count. The count is cleared after
+  // a network disconnection event.
+  optional int32 num_consecutive_connection_failure = 19 [default = -1];
 }
 
 // Number of occurrences of a specific RSSI poll rssi value
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index 2e739ab..dde6369 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -44,6 +44,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
@@ -164,6 +165,10 @@
     @Mock ExternalCallbackTracker<IOnWifiUsabilityStatsListener> mListenerTracker;
     @Mock WifiP2pMetrics mWifiP2pMetrics;
     @Mock DppMetrics mDppMetrics;
+    @Mock WifiScoreCard mWifiScoreCard;
+    @Mock WifiScoreCard.PerNetwork mPerNetwork;
+    @Mock WifiScoreCard.NetworkConnectionStats mNetworkConnectionStats;
+    @Mock WifiConfiguration mWifiConfig;
 
     @Before
     public void setUp() throws Exception {
@@ -183,6 +188,9 @@
         mWifiMetrics.setWifiNetworkSelector(mWns);
         mWifiMetrics.setWifiDataStall(mWifiDataStall);
         mWifiMetrics.setWifiHealthMonitor(mWifiHealthMonitor);
+        mWifiMetrics.setWifiScoreCard(mWifiScoreCard);
+        when(mWifiScoreCard.lookupNetwork(anyString())).thenReturn(mPerNetwork);
+        when(mPerNetwork.getRecentStats()).thenReturn(mNetworkConnectionStats);
     }
 
     /**
@@ -2629,10 +2637,13 @@
     }
 
     /**
-     * Check max supported link speed
+     * Check max supported link speed and consecutive connection failure count
      */
     @Test
-    public void testConnectionMaxSupportedLinkSpeed() throws Exception {
+    public void testConnectionMaxSupportedLinkSpeedConsecutiveFailureCnt() throws Exception {
+        mWifiMetrics.setScreenState(true);
+        when(mNetworkConnectionStats.getCount(WifiScoreCard.CNT_CONSECUTIVE_CONNECTION_FAILURE))
+                .thenReturn(2);
         mWifiMetrics.startConnectionEvent(mTestWifiConfig, "TestNetwork",
                 WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
         mWifiMetrics.setConnectionMaxSupportedLinkSpeedMbps(MAX_SUPPORTED_TX_LINK_SPEED_MBPS,
@@ -2646,6 +2657,8 @@
                 .routerFingerprint.maxSupportedTxLinkSpeedMbps);
         assertEquals(MAX_SUPPORTED_RX_LINK_SPEED_MBPS, mDecodedProto.connectionEvent[0]
                 .routerFingerprint.maxSupportedRxLinkSpeedMbps);
+        assertEquals(2, mDecodedProto.connectionEvent[0].numConsecutiveConnectionFailure);
+        assertEquals(true, mDecodedProto.connectionEvent[0].screenOn);
     }
 
     /**
@@ -2765,6 +2778,7 @@
 
     private WifiConfiguration createComplexWifiConfig() {
         WifiConfiguration config = new WifiConfiguration();
+        config.SSID = SSID;
         config.allowedKeyManagement = intToBitSet(TEST_ALLOWED_KEY_MANAGEMENT);
         config.allowedProtocols = intToBitSet(TEST_ALLOWED_PROTOCOLS);
         config.allowedAuthAlgorithms = intToBitSet(TEST_ALLOWED_AUTH_ALGORITHMS);
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java b/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java
index d6dc020..498b18d 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java
@@ -29,6 +29,7 @@
 import static com.android.server.wifi.WifiScoreCard.CNT_CONNECTION_ATTEMPT;
 import static com.android.server.wifi.WifiScoreCard.CNT_CONNECTION_DURATION_SEC;
 import static com.android.server.wifi.WifiScoreCard.CNT_CONNECTION_FAILURE;
+import static com.android.server.wifi.WifiScoreCard.CNT_CONSECUTIVE_CONNECTION_FAILURE;
 import static com.android.server.wifi.WifiScoreCard.CNT_DISCONNECTION_NONLOCAL;
 import static com.android.server.wifi.WifiScoreCard.CNT_SHORT_CONNECTION_NONLOCAL;
 import static com.android.server.wifi.util.NativeUtil.hexStringFromByteArray;
@@ -758,6 +759,7 @@
         assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_REJECTION));
         assertEquals(1, dailyStats.getCount(CNT_ASSOCIATION_TIMEOUT));
         assertEquals(0, dailyStats.getCount(CNT_AUTHENTICATION_FAILURE));
+        assertEquals(1, dailyStats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
     }
 
     private void makeAuthFailureAndWrongPassword() {
@@ -794,6 +796,10 @@
         assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_REJECTION));
         assertEquals(0, dailyStats.getCount(CNT_ASSOCIATION_TIMEOUT));
         assertEquals(1, dailyStats.getCount(CNT_AUTHENTICATION_FAILURE));
+        assertEquals(1, dailyStats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
+
+        makeNormalConnectionExample();
+        assertEquals(0, dailyStats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
     }
 
     /**
@@ -878,6 +884,7 @@
         assertEquals(0, stats.getCount(CNT_AUTHENTICATION_FAILURE));
         assertEquals(1 * scale, stats.getCount(CNT_SHORT_CONNECTION_NONLOCAL));
         assertEquals(1 * scale, stats.getCount(CNT_DISCONNECTION_NONLOCAL));
+        assertEquals(0, stats.getCount(CNT_CONSECUTIVE_CONNECTION_FAILURE));
     }
 
     private void makeShortConnectionOldRssiPollingExample() {