Define and track Wifi scores to report Wifi metrics

Add a proto definition to track counts corresponding to wifi scores
Scores are calculated in WifiScoreReport and metrics are updated in
calculateAndReportScore. Wifi scores are limited in a range [0, 60]
where the max score is the base score assigned by Network Agent.
To ensure the size of the map is limited, any scores out of this
range will be dropped. Updated test framework to validate score
tracking and check out of bound values are dropped. The unit tests
also ensure the base score is within reasonable limits.
Merge details:
This is a merge from CL 1326502 on master. Merge conflicts were
mainly due to code placement and only major difference was the call
to calculateScore as it has different arguments on the branches.

BUG=29418013

Change-Id: Iaf22ed7dc7fb73413739507406ec88d7719b362e
TEST=Wifi Unit tests, Walk tests and log scores via dumpsys
DEVICE=angler
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index 643060b..120e870 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wifi;
 
+import android.net.NetworkAgent;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.util.Base64;
@@ -47,6 +48,8 @@
      */
     private static final int MAX_RSSI_POLL = 0;
     private static final int MIN_RSSI_POLL = -127;
+    private static final int MIN_WIFI_SCORE = 0;
+    private static final int MAX_WIFI_SCORE = NetworkAgent.WIFI_BASE_SCORE;
     private final Object mLock = new Object();
     private static final int MAX_CONNECTION_EVENTS = 256;
     private Clock mClock;
@@ -85,7 +88,8 @@
      * capture for for this WifiMetricsProto
      */
     private long mRecordStartTimeSec;
-
+    /** Mapping of Wifi Scores to counts */
+    private final SparseIntArray mWifiScoreCounts = new SparseIntArray();
     class RouterFingerPrint {
         private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
         RouterFingerPrint() {
@@ -901,6 +905,21 @@
         }
     }
 
+    /**
+     * Increments occurence of a particular wifi score calculated
+     * in WifiScoreReport by current connected network. Scores are bounded
+     * within  [MIN_WIFI_SCORE, MAX_WIFI_SCORE] to limit size of SparseArray
+     */
+    public void incrementWifiScoreCount(int score) {
+        if (score < MIN_WIFI_SCORE || score > MAX_WIFI_SCORE) {
+            return;
+        }
+        synchronized (mLock) {
+            int count = mWifiScoreCounts.get(score);
+            mWifiScoreCounts.put(score, count + 1);
+        }
+    }
+
     public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
     /**
      * Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager
@@ -1060,6 +1079,12 @@
                 pw.println("mWifiLogProto.numHotspot2R2NetworkScanResults="
                         + mWifiLogProto.numHotspot2R2NetworkScanResults);
                 pw.println("mWifiLogProto.numScans=" + mWifiLogProto.numScans);
+                pw.println("mWifiLogProto.WifiScoreCount: [" + MIN_WIFI_SCORE + ", "
+                        + MAX_WIFI_SCORE + "]");
+                for (int i = 0; i <= MAX_WIFI_SCORE; i++) {
+                    pw.print(mWifiScoreCounts.get(i) + " ");
+                }
+                pw.print("\n");
             }
         }
     }
@@ -1074,6 +1099,7 @@
         List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>();
         List<WifiMetricsProto.RssiPollCount> rssis = new ArrayList<>();
         List<WifiMetricsProto.AlertReasonCount> alertReasons = new ArrayList<>();
+        List<WifiMetricsProto.WifiScoreCount> scores = new ArrayList<>();
         synchronized (mLock) {
             for (ConnectionEvent event : mConnectionEventList) {
                 // If this is not incremental, dump full ConnectionEvent list
@@ -1143,6 +1169,17 @@
                 alertReasons.add(keyVal);
             }
             mWifiLogProto.alertReasonCount = alertReasons.toArray(mWifiLogProto.alertReasonCount);
+            /**
+            *  Convert the SparseIntArray of Wifi Score and counts to proto's repeated
+            * IntKeyVal array.
+            */
+            for (int score = 0; score < mWifiScoreCounts.size(); score++) {
+                WifiMetricsProto.WifiScoreCount keyVal = new WifiMetricsProto.WifiScoreCount();
+                keyVal.score = mWifiScoreCounts.keyAt(score);
+                keyVal.count = mWifiScoreCounts.valueAt(score);
+                scores.add(keyVal);
+            }
+            mWifiLogProto.wifiScoreCount = scores.toArray(mWifiLogProto.wifiScoreCount);
         }
     }
 
@@ -1160,6 +1197,7 @@
             mRecordStartTimeSec = mClock.elapsedRealtime() / 1000;
             mRssiPollCounts.clear();
             mWifiAlertReasonCounts.clear();
+            mWifiScoreCounts.clear();
             mWifiLogProto.clear();
         }
     }
diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java
index 50e28bf..d32c722 100644
--- a/service/java/com/android/server/wifi/WifiScoreReport.java
+++ b/service/java/com/android/server/wifi/WifiScoreReport.java
@@ -97,7 +97,8 @@
                                                  WifiConfigManager wifiConfigManager,
                                                  NetworkAgent networkAgent,
                                                  WifiScoreReport lastReport,
-                                                 int aggressiveHandover) {
+                                                 int aggressiveHandover,
+                                                 WifiMetrics wifiMetrics) {
         boolean debugLogging = false;
         if (wifiConfigManager.mEnableVerboseLogging.get() > 0) {
             debugLogging = true;
@@ -370,6 +371,7 @@
                 networkAgent.sendNetworkScore(score);
             }
         }
+        wifiMetrics.incrementWifiScoreCount(score);
         return new WifiScoreReport(sb.toString(), badLinkspeedcount);
     }
 }
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index f47e751..37734f0 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -6658,7 +6658,8 @@
                                                                    mWifiConfigManager,
                                                                    mNetworkAgent,
                                                                    mWifiScoreReport,
-                                                                   mAggressiveHandover);
+                                                                   mAggressiveHandover,
+                                                                   mWifiMetrics);
                         }
                         sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
                                 mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
diff --git a/service/proto/wifi.proto b/service/proto/wifi.proto
index b2ced3c..8128ec1 100644
--- a/service/proto/wifi.proto
+++ b/service/proto/wifi.proto
@@ -226,6 +226,9 @@
 
   // Total number of scans handled by framework (oneshot or otherwise)
   optional int32 num_scans = 46;
+
+  // Counts the occurrences of each Wifi score
+  repeated WifiScoreCount wifi_score_count = 48;
 }
 
 // Information that gets logged for every WiFi connection.
@@ -398,3 +401,12 @@
   // Number of alerts with |reason|.
   optional int32 count = 2;
 }
+
+// Counts the number of instances of a specific Wifi Score calculated by WifiScoreReport
+message WifiScoreCount {
+  // Wifi Score
+  optional int32 score = 1;
+
+  // Number of Wifi score reports with this score
+  optional int32 count = 2;
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index cf8204b..8e7b6c3 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.*;
 
+import android.net.NetworkAgent;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -186,6 +187,12 @@
     private static final int NUM_HOTSPOT2_R2_NETWORK_SCAN_RESULTS = 2;
     private static final int NUM_SCANS = 5;
     private static final int NUM_TOTAL_SCAN_RESULTS = 8;
+    private static final int MIN_RSSI_LEVEL = -127;
+    private static final int MAX_RSSI_LEVEL = 0;
+    private static final int WIFI_SCORE_RANGE_MIN = 0;
+    private static final int NUM_WIFI_SCORES_TO_INCREMENT = 20;
+    private static final int WIFI_SCORE_RANGE_MAX = 60;
+    private static final int NUM_OUT_OF_BOUND_ENTRIES = 10;
 
     private ScanDetail buildMockScanDetail(boolean hidden, NetworkDetail.HSRelease hSRelease,
             String capabilities) {
@@ -305,9 +312,15 @@
         }
         for (int i = 0; i < NUM_RSSI_LEVELS_TO_INCREMENT; i++) {
             for (int j = 0; j <= i; j++) {
-                mWifiMetrics.incrementRssiPollRssiCount(FIRST_RSSI_LEVEL + i);
+                mWifiMetrics.incrementRssiPollRssiCount(MIN_RSSI_LEVEL + i);
             }
         }
+        for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
+            mWifiMetrics.incrementRssiPollRssiCount(MIN_RSSI_LEVEL - i);
+        }
+        for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
+            mWifiMetrics.incrementRssiPollRssiCount(MAX_RSSI_LEVEL + i);
+        }
         // Test alert-reason clamping.
         mWifiMetrics.incrementAlertReasonCount(WifiLoggerHal.WIFI_ALERT_REASON_MIN - 1);
         mWifiMetrics.incrementAlertReasonCount(WifiLoggerHal.WIFI_ALERT_REASON_MAX + 1);
@@ -320,6 +333,17 @@
         for (int i = 0; i < NUM_SCANS; i++) {
             mWifiMetrics.countScanResults(mockScanDetails);
         }
+        for (int score = WIFI_SCORE_RANGE_MIN; score < NUM_WIFI_SCORES_TO_INCREMENT; score++) {
+            for (int offset = 0; offset <= score; offset++) {
+                mWifiMetrics.incrementWifiScoreCount(WIFI_SCORE_RANGE_MIN + score);
+            }
+        }
+        for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
+            mWifiMetrics.incrementWifiScoreCount(WIFI_SCORE_RANGE_MIN - i);
+        }
+        for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
+            mWifiMetrics.incrementWifiScoreCount(WIFI_SCORE_RANGE_MAX + i);
+        }
     }
 
     /**
@@ -402,9 +426,13 @@
         assertEquals(TEST_RECORD_DURATION_SEC,
                 mDeserializedWifiMetrics.recordDurationSec);
         for (int i = 0; i < NUM_RSSI_LEVELS_TO_INCREMENT; i++) {
-            assertEquals(FIRST_RSSI_LEVEL + i, mDeserializedWifiMetrics.rssiPollRssiCount[i].rssi);
+            assertEquals(MIN_RSSI_LEVEL + i, mDeserializedWifiMetrics.rssiPollRssiCount[i].rssi);
             assertEquals(i + 1, mDeserializedWifiMetrics.rssiPollRssiCount[i].count);
         }
+        StringBuilder sb_rssi = new StringBuilder();
+        sb_rssi.append("Number of RSSIs = " + mDeserializedWifiMetrics.rssiPollRssiCount.length);
+        assertTrue(sb_rssi.toString(), (mDeserializedWifiMetrics.rssiPollRssiCount.length
+                     <= (MAX_RSSI_LEVEL - MIN_RSSI_LEVEL + 1)));
         assertEquals(2, mDeserializedWifiMetrics.alertReasonCount[0].count);  // Clamped reasons.
         assertEquals(3, mDeserializedWifiMetrics.alertReasonCount[1].count);
         assertEquals(1, mDeserializedWifiMetrics.alertReasonCount[2].count);
@@ -425,6 +453,21 @@
                 mDeserializedWifiMetrics.numHotspot2R2NetworkScanResults);
         assertEquals(NUM_SCANS,
                 mDeserializedWifiMetrics.numScans);
+        for (int score_index = 0; score_index < NUM_WIFI_SCORES_TO_INCREMENT; score_index++) {
+            assertEquals(WIFI_SCORE_RANGE_MIN + score_index,
+                    mDeserializedWifiMetrics.wifiScoreCount[score_index].score);
+            assertEquals(score_index + 1,
+                    mDeserializedWifiMetrics.wifiScoreCount[score_index].count);
+        }
+        StringBuilder sb_wifi_score = new StringBuilder();
+        sb_wifi_score.append("Number of wifi_scores = "
+                + mDeserializedWifiMetrics.wifiScoreCount.length);
+        assertTrue(sb_wifi_score.toString(), (mDeserializedWifiMetrics.wifiScoreCount.length
+                <= (WIFI_SCORE_RANGE_MAX - WIFI_SCORE_RANGE_MIN + 1)));
+        StringBuilder sb_wifi_limits = new StringBuilder();
+        sb_wifi_limits.append("Wifi Score limit is " +  NetworkAgent.WIFI_BASE_SCORE
+                + ">= " + WIFI_SCORE_RANGE_MAX);
+        assertTrue(sb_wifi_limits.toString(), NetworkAgent.WIFI_BASE_SCORE <= WIFI_SCORE_RANGE_MAX);
     }
 
     /**