1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
20import android.net.NetworkAgent;
21import android.net.wifi.ScanResult;
22import android.net.wifi.SupplicantState;
23import android.net.wifi.WifiConfiguration;
24import android.net.wifi.WifiInfo;
25import android.net.wifi.WifiManager;
26import android.os.Handler;
27import android.os.Looper;
28import android.os.Message;
29import android.util.Base64;
30import android.util.Log;
31import android.util.Pair;
32import android.util.SparseIntArray;
33
34import com.android.internal.annotations.VisibleForTesting;
35import com.android.server.wifi.aware.WifiAwareMetrics;
36import com.android.server.wifi.hotspot2.ANQPNetworkKey;
37import com.android.server.wifi.hotspot2.NetworkDetail;
38import com.android.server.wifi.hotspot2.PasspointManager;
39import com.android.server.wifi.hotspot2.PasspointMatch;
40import com.android.server.wifi.hotspot2.PasspointProvider;
41import com.android.server.wifi.hotspot2.Utils;
42import com.android.server.wifi.nano.WifiMetricsProto;
43import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount;
44import com.android.server.wifi.nano.WifiMetricsProto.PnoScanMetrics;
45import com.android.server.wifi.nano.WifiMetricsProto.SoftApConnectedClientsEvent;
46import com.android.server.wifi.nano.WifiMetricsProto.StaEvent;
47import com.android.server.wifi.nano.WifiMetricsProto.StaEvent.ConfigInfo;
48import com.android.server.wifi.nano.WifiMetricsProto.WpsMetrics;
49import com.android.server.wifi.rtt.RttMetrics;
50import com.android.server.wifi.util.InformationElementUtil;
51import com.android.server.wifi.util.ScanResultUtil;
52
53import org.json.JSONArray;
54import org.json.JSONException;
55import org.json.JSONObject;
56
57import java.io.FileDescriptor;
58import java.io.PrintWriter;
59import java.util.ArrayList;
60import java.util.BitSet;
61import java.util.Calendar;
62import java.util.HashMap;
63import java.util.HashSet;
64import java.util.LinkedList;
65import java.util.List;
66import java.util.Map;
67import java.util.Set;
68
69/**
70 * Provides storage for wireless connectivity metrics, as they are generated.
71 * Metrics logged by this class include:
72 *   Aggregated connection stats (num of connections, num of failures, ...)
73 *   Discrete connection event stats (time, duration, failure codes, ...)
74 *   Router details (technology type, authentication type, ...)
75 *   Scan stats
76 */
77public class WifiMetrics {
78    private static final String TAG = "WifiMetrics";
79    private static final boolean DBG = false;
80    /**
81     * Clamp the RSSI poll counts to values between [MIN,MAX]_RSSI_POLL
82     */
83    private static final int MAX_RSSI_POLL = 0;
84    private static final int MIN_RSSI_POLL = -127;
85    public static final int MAX_RSSI_DELTA = 127;
86    public static final int MIN_RSSI_DELTA = -127;
87    /** Maximum time period between ScanResult and RSSI poll to generate rssi delta datapoint */
88    public static final long TIMEOUT_RSSI_DELTA_MILLIS =  3000;
89    private static final int MIN_WIFI_SCORE = 0;
90    private static final int MAX_WIFI_SCORE = NetworkAgent.WIFI_BASE_SCORE;
91    @VisibleForTesting
92    static final int LOW_WIFI_SCORE = 50; // Mobile data score
93    private final Object mLock = new Object();
94    private static final int MAX_CONNECTION_EVENTS = 256;
95    // Largest bucket in the NumConnectableNetworkCount histogram,
96    // anything large will be stored in this bucket
97    public static final int MAX_CONNECTABLE_SSID_NETWORK_BUCKET = 20;
98    public static final int MAX_CONNECTABLE_BSSID_NETWORK_BUCKET = 50;
99    public static final int MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET = 100;
100    public static final int MAX_TOTAL_SCAN_RESULTS_BUCKET = 250;
101    public static final int MAX_TOTAL_PASSPOINT_APS_BUCKET = 50;
102    public static final int MAX_TOTAL_PASSPOINT_UNIQUE_ESS_BUCKET = 20;
103    public static final int MAX_PASSPOINT_APS_PER_UNIQUE_ESS_BUCKET = 50;
104    public static final int MAX_TOTAL_80211MC_APS_BUCKET = 20;
105    private static final int CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER = 1000;
106    // Max limit for number of soft AP related events, extra events will be dropped.
107    private static final int MAX_NUM_SOFT_AP_EVENTS = 256;
108    private Clock mClock;
109    private boolean mScreenOn;
110    private int mWifiState;
111    private WifiAwareMetrics mWifiAwareMetrics;
112    private RttMetrics mRttMetrics;
113    private final PnoScanMetrics mPnoScanMetrics = new PnoScanMetrics();
114    private final WpsMetrics mWpsMetrics = new WpsMetrics();
115    private Handler mHandler;
116    private ScoringParams mScoringParams;
117    private WifiConfigManager mWifiConfigManager;
118    private WifiNetworkSelector mWifiNetworkSelector;
119    private PasspointManager mPasspointManager;
120    /**
121     * Metrics are stored within an instance of the WifiLog proto during runtime,
122     * The ConnectionEvent, SystemStateEntries & ScanReturnEntries metrics are stored during
123     * runtime in member lists of this WifiMetrics class, with the final WifiLog proto being pieced
124     * together at dump-time
125     */
126    private final WifiMetricsProto.WifiLog mWifiLogProto = new WifiMetricsProto.WifiLog();
127    /**
128     * Session information that gets logged for every Wifi connection attempt.
129     */
130    private final List<ConnectionEvent> mConnectionEventList = new ArrayList<>();
131    /**
132     * The latest started (but un-ended) connection attempt
133     */
134    private ConnectionEvent mCurrentConnectionEvent;
135    /**
136     * Count of number of times each scan return code, indexed by WifiLog.ScanReturnCode
137     */
138    private final SparseIntArray mScanReturnEntries = new SparseIntArray();
139    /**
140     * Mapping of system state to the counts of scans requested in that wifi state * screenOn
141     * combination. Indexed by WifiLog.WifiState * (1 + screenOn)
142     */
143    private final SparseIntArray mWifiSystemStateEntries = new SparseIntArray();
144    /** Mapping of channel frequency to its RSSI distribution histogram **/
145    private final Map<Integer, SparseIntArray> mRssiPollCountsMap = new HashMap<>();
146    /** Mapping of RSSI scan-poll delta values to counts. */
147    private final SparseIntArray mRssiDeltaCounts = new SparseIntArray();
148    /** RSSI of the scan result for the last connection event*/
149    private int mScanResultRssi = 0;
150    /** Boot-relative timestamp when the last candidate scanresult was received, used to calculate
151        RSSI deltas. -1 designates no candidate scanResult being tracked */
152    private long mScanResultRssiTimestampMillis = -1;
153    /** Mapping of alert reason to the respective alert count. */
154    private final SparseIntArray mWifiAlertReasonCounts = new SparseIntArray();
155    /**
156     * Records the getElapsedSinceBootMillis (in seconds) that represents the beginning of data
157     * capture for for this WifiMetricsProto
158     */
159    private long mRecordStartTimeSec;
160    /** Mapping of Wifi Scores to counts */
161    private final SparseIntArray mWifiScoreCounts = new SparseIntArray();
162    /** Mapping of SoftApManager start SoftAp return codes to counts */
163    private final SparseIntArray mSoftApManagerReturnCodeCounts = new SparseIntArray();
164
165    private final SparseIntArray mTotalSsidsInScanHistogram = new SparseIntArray();
166    private final SparseIntArray mTotalBssidsInScanHistogram = new SparseIntArray();
167    private final SparseIntArray mAvailableOpenSsidsInScanHistogram = new SparseIntArray();
168    private final SparseIntArray mAvailableOpenBssidsInScanHistogram = new SparseIntArray();
169    private final SparseIntArray mAvailableSavedSsidsInScanHistogram = new SparseIntArray();
170    private final SparseIntArray mAvailableSavedBssidsInScanHistogram = new SparseIntArray();
171    private final SparseIntArray mAvailableOpenOrSavedSsidsInScanHistogram = new SparseIntArray();
172    private final SparseIntArray mAvailableOpenOrSavedBssidsInScanHistogram = new SparseIntArray();
173    private final SparseIntArray mAvailableSavedPasspointProviderProfilesInScanHistogram =
174            new SparseIntArray();
175    private final SparseIntArray mAvailableSavedPasspointProviderBssidsInScanHistogram =
176            new SparseIntArray();
177
178    /** Mapping of "Connect to Network" notifications to counts. */
179    private final SparseIntArray mConnectToNetworkNotificationCount = new SparseIntArray();
180    /** Mapping of "Connect to Network" notification user actions to counts. */
181    private final SparseIntArray mConnectToNetworkNotificationActionCount = new SparseIntArray();
182    private int mOpenNetworkRecommenderBlacklistSize = 0;
183    private boolean mIsWifiNetworksAvailableNotificationOn = false;
184    private int mNumOpenNetworkConnectMessageFailedToSend = 0;
185    private int mNumOpenNetworkRecommendationUpdates = 0;
186    /** List of soft AP events related to number of connected clients in tethered mode */
187    private final List<SoftApConnectedClientsEvent> mSoftApEventListTethered = new ArrayList<>();
188    /** List of soft AP events related to number of connected clients in local only mode */
189    private final List<SoftApConnectedClientsEvent> mSoftApEventListLocalOnly = new ArrayList<>();
190
191    private final SparseIntArray mObservedHotspotR1ApInScanHistogram = new SparseIntArray();
192    private final SparseIntArray mObservedHotspotR2ApInScanHistogram = new SparseIntArray();
193    private final SparseIntArray mObservedHotspotR1EssInScanHistogram = new SparseIntArray();
194    private final SparseIntArray mObservedHotspotR2EssInScanHistogram = new SparseIntArray();
195    private final SparseIntArray mObservedHotspotR1ApsPerEssInScanHistogram = new SparseIntArray();
196    private final SparseIntArray mObservedHotspotR2ApsPerEssInScanHistogram = new SparseIntArray();
197
198    private final SparseIntArray mObserved80211mcApInScanHistogram = new SparseIntArray();
199
200    /** Wifi power metrics*/
201    private WifiPowerMetrics mWifiPowerMetrics = new WifiPowerMetrics();
202
203    /** Wifi Wake metrics */
204    private final WifiWakeMetrics mWifiWakeMetrics = new WifiWakeMetrics();
205
206    private boolean mIsMacRandomizationOn = false;
207
208    class RouterFingerPrint {
209        private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
210        RouterFingerPrint() {
211            mRouterFingerPrintProto = new WifiMetricsProto.RouterFingerPrint();
212        }
213
214        public String toString() {
215            StringBuilder sb = new StringBuilder();
216            synchronized (mLock) {
217                sb.append("mConnectionEvent.roamType=" + mRouterFingerPrintProto.roamType);
218                sb.append(", mChannelInfo=" + mRouterFingerPrintProto.channelInfo);
219                sb.append(", mDtim=" + mRouterFingerPrintProto.dtim);
220                sb.append(", mAuthentication=" + mRouterFingerPrintProto.authentication);
221                sb.append(", mHidden=" + mRouterFingerPrintProto.hidden);
222                sb.append(", mRouterTechnology=" + mRouterFingerPrintProto.routerTechnology);
223                sb.append(", mSupportsIpv6=" + mRouterFingerPrintProto.supportsIpv6);
224            }
225            return sb.toString();
226        }
227        public void updateFromWifiConfiguration(WifiConfiguration config) {
228            synchronized (mLock) {
229                if (config != null) {
230                    // Is this a hidden network
231                    mRouterFingerPrintProto.hidden = config.hiddenSSID;
232                    // Config may not have a valid dtimInterval set yet, in which case dtim will be zero
233                    // (These are only populated from beacon frame scan results, which are returned as
234                    // scan results from the chip far less frequently than Probe-responses)
235                    if (config.dtimInterval > 0) {
236                        mRouterFingerPrintProto.dtim = config.dtimInterval;
237                    }
238                    mCurrentConnectionEvent.mConfigSsid = config.SSID;
239                    // Get AuthType information from config (We do this again from ScanResult after
240                    // associating with BSSID)
241                    if (config.allowedKeyManagement != null
242                            && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
243                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
244                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
245                    } else if (config.isEnterprise()) {
246                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
247                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
248                    } else {
249                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
250                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
251                    }
252                    mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
253                            .passpoint = config.isPasspoint();
254                    // If there's a ScanResult candidate associated with this config already, get it and
255                    // log (more accurate) metrics from it
256                    ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
257                    if (candidate != null) {
258                        updateMetricsFromScanResult(candidate);
259                    }
260                }
261            }
262        }
263    }
264
265    /**
266     * Log event, tracking the start time, end time and result of a wireless connection attempt.
267     */
268    class ConnectionEvent {
269        WifiMetricsProto.ConnectionEvent mConnectionEvent;
270        //<TODO> Move these constants into a wifi.proto Enum, and create a new Failure Type field
271        //covering more than just l2 failures. see b/27652362
272        /**
273         * Failure codes, used for the 'level_2_failure_code' Connection event field (covers a lot
274         * more failures than just l2 though, since the proto does not have a place to log
275         * framework failures)
276         */
277        // Failure is unknown
278        public static final int FAILURE_UNKNOWN = 0;
279        // NONE
280        public static final int FAILURE_NONE = 1;
281        // ASSOCIATION_REJECTION_EVENT
282        public static final int FAILURE_ASSOCIATION_REJECTION = 2;
283        // AUTHENTICATION_FAILURE_EVENT
284        public static final int FAILURE_AUTHENTICATION_FAILURE = 3;
285        // SSID_TEMP_DISABLED (Also Auth failure)
286        public static final int FAILURE_SSID_TEMP_DISABLED = 4;
287        // reconnect() or reassociate() call to WifiNative failed
288        public static final int FAILURE_CONNECT_NETWORK_FAILED = 5;
289        // NETWORK_DISCONNECTION_EVENT
290        public static final int FAILURE_NETWORK_DISCONNECTION = 6;
291        // NEW_CONNECTION_ATTEMPT before previous finished
292        public static final int FAILURE_NEW_CONNECTION_ATTEMPT = 7;
293        // New connection attempt to the same network & bssid
294        public static final int FAILURE_REDUNDANT_CONNECTION_ATTEMPT = 8;
295        // Roam Watchdog timer triggered (Roaming timed out)
296        public static final int FAILURE_ROAM_TIMEOUT = 9;
297        // DHCP failure
298        public static final int FAILURE_DHCP = 10;
299        // ASSOCIATION_TIMED_OUT
300        public static final int FAILURE_ASSOCIATION_TIMED_OUT = 11;
301
302        RouterFingerPrint mRouterFingerPrint;
303        private long mRealStartTime;
304        private long mRealEndTime;
305        private String mConfigSsid;
306        private String mConfigBssid;
307        private int mWifiState;
308        private boolean mScreenOn;
309
310        private ConnectionEvent() {
311            mConnectionEvent = new WifiMetricsProto.ConnectionEvent();
312            mRealEndTime = 0;
313            mRealStartTime = 0;
314            mRouterFingerPrint = new RouterFingerPrint();
315            mConnectionEvent.routerFingerprint = mRouterFingerPrint.mRouterFingerPrintProto;
316            mConfigSsid = "<NULL>";
317            mConfigBssid = "<NULL>";
318            mWifiState = WifiMetricsProto.WifiLog.WIFI_UNKNOWN;
319            mScreenOn = false;
320        }
321
322        public String toString() {
323            StringBuilder sb = new StringBuilder();
324            sb.append("startTime=");
325            Calendar c = Calendar.getInstance();
326            synchronized (mLock) {
327                c.setTimeInMillis(mConnectionEvent.startTimeMillis);
328                sb.append(mConnectionEvent.startTimeMillis == 0 ? "            <null>" :
329                        String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
330                sb.append(", SSID=");
331                sb.append(mConfigSsid);
332                sb.append(", BSSID=");
333                sb.append(mConfigBssid);
334                sb.append(", durationMillis=");
335                sb.append(mConnectionEvent.durationTakenToConnectMillis);
336                sb.append(", roamType=");
337                switch(mConnectionEvent.roamType) {
338                    case 1:
339                        sb.append("ROAM_NONE");
340                        break;
341                    case 2:
342                        sb.append("ROAM_DBDC");
343                        break;
344                    case 3:
345                        sb.append("ROAM_ENTERPRISE");
346                        break;
347                    case 4:
348                        sb.append("ROAM_USER_SELECTED");
349                        break;
350                    case 5:
351                        sb.append("ROAM_UNRELATED");
352                        break;
353                    default:
354                        sb.append("ROAM_UNKNOWN");
355                }
356                sb.append(", connectionResult=");
357                sb.append(mConnectionEvent.connectionResult);
358                sb.append(", level2FailureCode=");
359                switch(mConnectionEvent.level2FailureCode) {
360                    case FAILURE_NONE:
361                        sb.append("NONE");
362                        break;
363                    case FAILURE_ASSOCIATION_REJECTION:
364                        sb.append("ASSOCIATION_REJECTION");
365                        break;
366                    case FAILURE_AUTHENTICATION_FAILURE:
367                        sb.append("AUTHENTICATION_FAILURE");
368                        break;
369                    case FAILURE_SSID_TEMP_DISABLED:
370                        sb.append("SSID_TEMP_DISABLED");
371                        break;
372                    case FAILURE_CONNECT_NETWORK_FAILED:
373                        sb.append("CONNECT_NETWORK_FAILED");
374                        break;
375                    case FAILURE_NETWORK_DISCONNECTION:
376                        sb.append("NETWORK_DISCONNECTION");
377                        break;
378                    case FAILURE_NEW_CONNECTION_ATTEMPT:
379                        sb.append("NEW_CONNECTION_ATTEMPT");
380                        break;
381                    case FAILURE_REDUNDANT_CONNECTION_ATTEMPT:
382                        sb.append("REDUNDANT_CONNECTION_ATTEMPT");
383                        break;
384                    case FAILURE_ROAM_TIMEOUT:
385                        sb.append("ROAM_TIMEOUT");
386                        break;
387                    case FAILURE_DHCP:
388                        sb.append("DHCP");
389                        break;
390                    case FAILURE_ASSOCIATION_TIMED_OUT:
391                        sb.append("ASSOCIATION_TIMED_OUT");
392                        break;
393                    default:
394                        sb.append("UNKNOWN");
395                        break;
396                }
397                sb.append(", connectivityLevelFailureCode=");
398                switch(mConnectionEvent.connectivityLevelFailureCode) {
399                    case WifiMetricsProto.ConnectionEvent.HLF_NONE:
400                        sb.append("NONE");
401                        break;
402                    case WifiMetricsProto.ConnectionEvent.HLF_DHCP:
403                        sb.append("DHCP");
404                        break;
405                    case WifiMetricsProto.ConnectionEvent.HLF_NO_INTERNET:
406                        sb.append("NO_INTERNET");
407                        break;
408                    case WifiMetricsProto.ConnectionEvent.HLF_UNWANTED:
409                        sb.append("UNWANTED");
410                        break;
411                    default:
412                        sb.append("UNKNOWN");
413                        break;
414                }
415                sb.append(", signalStrength=");
416                sb.append(mConnectionEvent.signalStrength);
417                sb.append(", wifiState=");
418                switch(mWifiState) {
419                    case WifiMetricsProto.WifiLog.WIFI_DISABLED:
420                        sb.append("WIFI_DISABLED");
421                        break;
422                    case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
423                        sb.append("WIFI_DISCONNECTED");
424                        break;
425                    case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
426                        sb.append("WIFI_ASSOCIATED");
427                        break;
428                    default:
429                        sb.append("WIFI_UNKNOWN");
430                        break;
431                }
432                sb.append(", screenOn=");
433                sb.append(mScreenOn);
434                sb.append(". mRouterFingerprint: ");
435                sb.append(mRouterFingerPrint.toString());
436            }
437            return sb.toString();
438        }
439    }
440
441    public WifiMetrics(Clock clock, Looper looper, WifiAwareMetrics awareMetrics,
442            RttMetrics rttMetrics) {
443        mClock = clock;
444        mCurrentConnectionEvent = null;
445        mScreenOn = true;
446        mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED;
447        mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
448        mWifiAwareMetrics = awareMetrics;
449        mRttMetrics = rttMetrics;
450
451        mHandler = new Handler(looper) {
452            public void handleMessage(Message msg) {
453                synchronized (mLock) {
454                    processMessage(msg);
455                }
456            }
457        };
458    }
459
460    /** Sets internal ScoringParams member */
461    public void setScoringParams(ScoringParams scoringParams) {
462        mScoringParams = scoringParams;
463    }
464
465    /** Sets internal WifiConfigManager member */
466    public void setWifiConfigManager(WifiConfigManager wifiConfigManager) {
467        mWifiConfigManager = wifiConfigManager;
468    }
469
470    /** Sets internal WifiNetworkSelector member */
471    public void setWifiNetworkSelector(WifiNetworkSelector wifiNetworkSelector) {
472        mWifiNetworkSelector = wifiNetworkSelector;
473    }
474
475    /** Sets internal PasspointManager member */
476    public void setPasspointManager(PasspointManager passpointManager) {
477        mPasspointManager = passpointManager;
478    }
479
480    /**
481     * Increment total number of attempts to start a pno scan
482     */
483    public void incrementPnoScanStartAttempCount() {
484        synchronized (mLock) {
485            mPnoScanMetrics.numPnoScanAttempts++;
486        }
487    }
488
489    /**
490     * Increment total number of attempts with pno scan failed
491     */
492    public void incrementPnoScanFailedCount() {
493        synchronized (mLock) {
494            mPnoScanMetrics.numPnoScanFailed++;
495        }
496    }
497
498    /**
499     * Increment number of pno scans started successfully over offload
500     */
501    public void incrementPnoScanStartedOverOffloadCount() {
502        synchronized (mLock) {
503            mPnoScanMetrics.numPnoScanStartedOverOffload++;
504        }
505    }
506
507    /**
508     * Increment number of pno scans failed over offload
509     */
510    public void incrementPnoScanFailedOverOffloadCount() {
511        synchronized (mLock) {
512            mPnoScanMetrics.numPnoScanFailedOverOffload++;
513        }
514    }
515
516    /**
517     * Increment number of times pno scan found a result
518     */
519    public void incrementPnoFoundNetworkEventCount() {
520        synchronized (mLock) {
521            mPnoScanMetrics.numPnoFoundNetworkEvents++;
522        }
523    }
524
525    /**
526     * Increment total number of wps connection attempts
527     */
528    public void incrementWpsAttemptCount() {
529        synchronized (mLock) {
530            mWpsMetrics.numWpsAttempts++;
531        }
532    }
533
534    /**
535     * Increment total number of wps connection success
536     */
537    public void incrementWpsSuccessCount() {
538        synchronized (mLock) {
539            mWpsMetrics.numWpsSuccess++;
540        }
541    }
542
543    /**
544     * Increment total number of wps failure on start
545     */
546    public void incrementWpsStartFailureCount() {
547        synchronized (mLock) {
548            mWpsMetrics.numWpsStartFailure++;
549        }
550    }
551
552    /**
553     * Increment total number of wps overlap failure
554     */
555    public void incrementWpsOverlapFailureCount() {
556        synchronized (mLock) {
557            mWpsMetrics.numWpsOverlapFailure++;
558        }
559    }
560
561    /**
562     * Increment total number of wps timeout failure
563     */
564    public void incrementWpsTimeoutFailureCount() {
565        synchronized (mLock) {
566            mWpsMetrics.numWpsTimeoutFailure++;
567        }
568    }
569
570    /**
571     * Increment total number of other wps failure during connection
572     */
573    public void incrementWpsOtherConnectionFailureCount() {
574        synchronized (mLock) {
575            mWpsMetrics.numWpsOtherConnectionFailure++;
576        }
577    }
578
579    /**
580     * Increment total number of supplicant failure after wps
581     */
582    public void incrementWpsSupplicantFailureCount() {
583        synchronized (mLock) {
584            mWpsMetrics.numWpsSupplicantFailure++;
585        }
586    }
587
588    /**
589     * Increment total number of wps cancellation
590     */
591    public void incrementWpsCancellationCount() {
592        synchronized (mLock) {
593            mWpsMetrics.numWpsCancellation++;
594        }
595    }
596
597    // Values used for indexing SystemStateEntries
598    private static final int SCREEN_ON = 1;
599    private static final int SCREEN_OFF = 0;
600
601    /**
602     * Create a new connection event. Call when wifi attempts to make a new network connection
603     * If there is a current 'un-ended' connection event, it will be ended with UNKNOWN connectivity
604     * failure code.
605     * Gathers and sets the RouterFingerPrint data as well
606     *
607     * @param config WifiConfiguration of the config used for the current connection attempt
608     * @param roamType Roam type that caused connection attempt, see WifiMetricsProto.WifiLog.ROAM_X
609     */
610    public void startConnectionEvent(WifiConfiguration config, String targetBSSID, int roamType) {
611        synchronized (mLock) {
612            // Check if this is overlapping another current connection event
613            if (mCurrentConnectionEvent != null) {
614                //Is this new Connection Event the same as the current one
615                if (mCurrentConnectionEvent.mConfigSsid != null
616                        && mCurrentConnectionEvent.mConfigBssid != null
617                        && config != null
618                        && mCurrentConnectionEvent.mConfigSsid.equals(config.SSID)
619                        && (mCurrentConnectionEvent.mConfigBssid.equals("any")
620                        || mCurrentConnectionEvent.mConfigBssid.equals(targetBSSID))) {
621                    mCurrentConnectionEvent.mConfigBssid = targetBSSID;
622                    // End Connection Event due to new connection attempt to the same network
623                    endConnectionEvent(ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT,
624                            WifiMetricsProto.ConnectionEvent.HLF_NONE);
625                } else {
626                    // End Connection Event due to new connection attempt to different network
627                    endConnectionEvent(ConnectionEvent.FAILURE_NEW_CONNECTION_ATTEMPT,
628                            WifiMetricsProto.ConnectionEvent.HLF_NONE);
629                }
630            }
631            //If past maximum connection events, start removing the oldest
632            while(mConnectionEventList.size() >= MAX_CONNECTION_EVENTS) {
633                mConnectionEventList.remove(0);
634            }
635            mCurrentConnectionEvent = new ConnectionEvent();
636            mCurrentConnectionEvent.mConnectionEvent.startTimeMillis =
637                    mClock.getWallClockMillis();
638            mCurrentConnectionEvent.mConfigBssid = targetBSSID;
639            mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
640            mCurrentConnectionEvent.mRouterFingerPrint.updateFromWifiConfiguration(config);
641            mCurrentConnectionEvent.mConfigBssid = "any";
642            mCurrentConnectionEvent.mRealStartTime = mClock.getElapsedSinceBootMillis();
643            mCurrentConnectionEvent.mWifiState = mWifiState;
644            mCurrentConnectionEvent.mScreenOn = mScreenOn;
645            mConnectionEventList.add(mCurrentConnectionEvent);
646            mScanResultRssiTimestampMillis = -1;
647            if (config != null) {
648                ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
649                if (candidate != null) {
650                    // Cache the RSSI of the candidate, as the connection event level is updated
651                    // from other sources (polls, bssid_associations) and delta requires the
652                    // scanResult rssi
653                    mScanResultRssi = candidate.level;
654                    mScanResultRssiTimestampMillis = mClock.getElapsedSinceBootMillis();
655                }
656            }
657        }
658    }
659
660    /**
661     * set the RoamType of the current ConnectionEvent (if any)
662     */
663    public void setConnectionEventRoamType(int roamType) {
664        synchronized (mLock) {
665            if (mCurrentConnectionEvent != null) {
666                mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
667            }
668        }
669    }
670
671    /**
672     * Set AP related metrics from ScanDetail
673     */
674    public void setConnectionScanDetail(ScanDetail scanDetail) {
675        synchronized (mLock) {
676            if (mCurrentConnectionEvent != null && scanDetail != null) {
677                NetworkDetail networkDetail = scanDetail.getNetworkDetail();
678                ScanResult scanResult = scanDetail.getScanResult();
679                //Ensure that we have a networkDetail, and that it corresponds to the currently
680                //tracked connection attempt
681                if (networkDetail != null && scanResult != null
682                        && mCurrentConnectionEvent.mConfigSsid != null
683                        && mCurrentConnectionEvent.mConfigSsid
684                        .equals("\"" + networkDetail.getSSID() + "\"")) {
685                    updateMetricsFromNetworkDetail(networkDetail);
686                    updateMetricsFromScanResult(scanResult);
687                }
688            }
689        }
690    }
691
692    /**
693     * End a Connection event record. Call when wifi connection attempt succeeds or fails.
694     * If a Connection event has not been started and is active when .end is called, a new one is
695     * created with zero duration.
696     *
697     * @param level2FailureCode Level 2 failure code returned by supplicant
698     * @param connectivityFailureCode WifiMetricsProto.ConnectionEvent.HLF_X
699     */
700    public void endConnectionEvent(int level2FailureCode, int connectivityFailureCode) {
701        synchronized (mLock) {
702            if (mCurrentConnectionEvent != null) {
703                boolean result = (level2FailureCode == 1)
704                        && (connectivityFailureCode == WifiMetricsProto.ConnectionEvent.HLF_NONE);
705                mCurrentConnectionEvent.mConnectionEvent.connectionResult = result ? 1 : 0;
706                mCurrentConnectionEvent.mRealEndTime = mClock.getElapsedSinceBootMillis();
707                mCurrentConnectionEvent.mConnectionEvent.durationTakenToConnectMillis = (int)
708                        (mCurrentConnectionEvent.mRealEndTime
709                        - mCurrentConnectionEvent.mRealStartTime);
710                mCurrentConnectionEvent.mConnectionEvent.level2FailureCode = level2FailureCode;
711                mCurrentConnectionEvent.mConnectionEvent.connectivityLevelFailureCode =
712                        connectivityFailureCode;
713                // ConnectionEvent already added to ConnectionEvents List. Safe to null current here
714                mCurrentConnectionEvent = null;
715                if (!result) {
716                    mScanResultRssiTimestampMillis = -1;
717                }
718            }
719        }
720    }
721
722    /**
723     * Set ConnectionEvent DTIM Interval (if set), and 802.11 Connection mode, from NetworkDetail
724     */
725    private void updateMetricsFromNetworkDetail(NetworkDetail networkDetail) {
726        int dtimInterval = networkDetail.getDtimInterval();
727        if (dtimInterval > 0) {
728            mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.dtim =
729                    dtimInterval;
730        }
731        int connectionWifiMode;
732        switch (networkDetail.getWifiMode()) {
733            case InformationElementUtil.WifiMode.MODE_UNDEFINED:
734                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_UNKNOWN;
735                break;
736            case InformationElementUtil.WifiMode.MODE_11A:
737                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_A;
738                break;
739            case InformationElementUtil.WifiMode.MODE_11B:
740                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_B;
741                break;
742            case InformationElementUtil.WifiMode.MODE_11G:
743                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_G;
744                break;
745            case InformationElementUtil.WifiMode.MODE_11N:
746                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_N;
747                break;
748            case InformationElementUtil.WifiMode.MODE_11AC  :
749                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_AC;
750                break;
751            default:
752                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_OTHER;
753                break;
754        }
755        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
756                .routerTechnology = connectionWifiMode;
757    }
758
759    /**
760     * Set ConnectionEvent RSSI and authentication type from ScanResult
761     */
762    private void updateMetricsFromScanResult(ScanResult scanResult) {
763        mCurrentConnectionEvent.mConnectionEvent.signalStrength = scanResult.level;
764        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
765                WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
766        mCurrentConnectionEvent.mConfigBssid = scanResult.BSSID;
767        if (scanResult.capabilities != null) {
768            if (ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
769                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
770                        WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
771            } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) {
772                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
773                        WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
774            } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
775                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
776                        WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
777            }
778        }
779        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.channelInfo =
780                scanResult.frequency;
781    }
782
783    void setIsLocationEnabled(boolean enabled) {
784        synchronized (mLock) {
785            mWifiLogProto.isLocationEnabled = enabled;
786        }
787    }
788
789    void setIsScanningAlwaysEnabled(boolean enabled) {
790        synchronized (mLock) {
791            mWifiLogProto.isScanningAlwaysEnabled = enabled;
792        }
793    }
794
795    /**
796     * Increment Non Empty Scan Results count
797     */
798    public void incrementNonEmptyScanResultCount() {
799        if (DBG) Log.v(TAG, "incrementNonEmptyScanResultCount");
800        synchronized (mLock) {
801            mWifiLogProto.numNonEmptyScanResults++;
802        }
803    }
804
805    /**
806     * Increment Empty Scan Results count
807     */
808    public void incrementEmptyScanResultCount() {
809        if (DBG) Log.v(TAG, "incrementEmptyScanResultCount");
810        synchronized (mLock) {
811            mWifiLogProto.numEmptyScanResults++;
812        }
813    }
814
815    /**
816     * Increment background scan count
817     */
818    public void incrementBackgroundScanCount() {
819        if (DBG) Log.v(TAG, "incrementBackgroundScanCount");
820        synchronized (mLock) {
821            mWifiLogProto.numBackgroundScans++;
822        }
823    }
824
825   /**
826     * Get Background scan count
827     */
828    public int getBackgroundScanCount() {
829        synchronized (mLock) {
830            return mWifiLogProto.numBackgroundScans;
831        }
832    }
833
834    /**
835     * Increment oneshot scan count, and the associated WifiSystemScanStateCount entry
836     */
837    public void incrementOneshotScanCount() {
838        synchronized (mLock) {
839            mWifiLogProto.numOneshotScans++;
840        }
841        incrementWifiSystemScanStateCount(mWifiState, mScreenOn);
842    }
843
844    /**
845     * Increment connectivity oneshot scan count.
846     */
847    public void incrementConnectivityOneshotScanCount() {
848        synchronized (mLock) {
849            mWifiLogProto.numConnectivityOneshotScans++;
850        }
851    }
852
853    /**
854     * Get oneshot scan count
855     */
856    public int getOneshotScanCount() {
857        synchronized (mLock) {
858            return mWifiLogProto.numOneshotScans;
859        }
860    }
861
862    /**
863     * Get connectivity oneshot scan count
864     */
865    public int getConnectivityOneshotScanCount() {
866        synchronized (mLock) {
867            return mWifiLogProto.numConnectivityOneshotScans;
868        }
869    }
870
871    /**
872     * Increment oneshot scan count for external apps.
873     */
874    public void incrementExternalAppOneshotScanRequestsCount() {
875        synchronized (mLock) {
876            mWifiLogProto.numExternalAppOneshotScanRequests++;
877        }
878    }
879    /**
880     * Increment oneshot scan throttle count for external foreground apps.
881     */
882    public void incrementExternalForegroundAppOneshotScanRequestsThrottledCount() {
883        synchronized (mLock) {
884            mWifiLogProto.numExternalForegroundAppOneshotScanRequestsThrottled++;
885        }
886    }
887
888    /**
889     * Increment oneshot scan throttle count for external background apps.
890     */
891    public void incrementExternalBackgroundAppOneshotScanRequestsThrottledCount() {
892        synchronized (mLock) {
893            mWifiLogProto.numExternalBackgroundAppOneshotScanRequestsThrottled++;
894        }
895    }
896
897    private String returnCodeToString(int scanReturnCode) {
898        switch(scanReturnCode){
899            case WifiMetricsProto.WifiLog.SCAN_UNKNOWN:
900                return "SCAN_UNKNOWN";
901            case WifiMetricsProto.WifiLog.SCAN_SUCCESS:
902                return "SCAN_SUCCESS";
903            case WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED:
904                return "SCAN_FAILURE_INTERRUPTED";
905            case WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION:
906                return "SCAN_FAILURE_INVALID_CONFIGURATION";
907            case WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED:
908                return "FAILURE_WIFI_DISABLED";
909            default:
910                return "<UNKNOWN>";
911        }
912    }
913
914    /**
915     * Increment count of scan return code occurrence
916     *
917     * @param scanReturnCode Return code from scan attempt WifiMetricsProto.WifiLog.SCAN_X
918     */
919    public void incrementScanReturnEntry(int scanReturnCode, int countToAdd) {
920        synchronized (mLock) {
921            if (DBG) Log.v(TAG, "incrementScanReturnEntry " + returnCodeToString(scanReturnCode));
922            int entry = mScanReturnEntries.get(scanReturnCode);
923            entry += countToAdd;
924            mScanReturnEntries.put(scanReturnCode, entry);
925        }
926    }
927    /**
928     * Get the count of this scanReturnCode
929     * @param scanReturnCode that we are getting the count for
930     */
931    public int getScanReturnEntry(int scanReturnCode) {
932        synchronized (mLock) {
933            return mScanReturnEntries.get(scanReturnCode);
934        }
935    }
936
937    private String wifiSystemStateToString(int state) {
938        switch(state){
939            case WifiMetricsProto.WifiLog.WIFI_UNKNOWN:
940                return "WIFI_UNKNOWN";
941            case WifiMetricsProto.WifiLog.WIFI_DISABLED:
942                return "WIFI_DISABLED";
943            case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
944                return "WIFI_DISCONNECTED";
945            case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
946                return "WIFI_ASSOCIATED";
947            default:
948                return "default";
949        }
950    }
951
952    /**
953     * Increments the count of scans initiated by each wifi state, accounts for screenOn/Off
954     *
955     * @param state State of the system when scan was initiated, see WifiMetricsProto.WifiLog.WIFI_X
956     * @param screenOn Is the screen on
957     */
958    public void incrementWifiSystemScanStateCount(int state, boolean screenOn) {
959        synchronized (mLock) {
960            if (DBG) {
961                Log.v(TAG, "incrementWifiSystemScanStateCount " + wifiSystemStateToString(state)
962                        + " " + screenOn);
963            }
964            int index = (state * 2) + (screenOn ? SCREEN_ON : SCREEN_OFF);
965            int entry = mWifiSystemStateEntries.get(index);
966            entry++;
967            mWifiSystemStateEntries.put(index, entry);
968        }
969    }
970
971    /**
972     * Get the count of this system State Entry
973     */
974    public int getSystemStateCount(int state, boolean screenOn) {
975        synchronized (mLock) {
976            int index = state * 2 + (screenOn ? SCREEN_ON : SCREEN_OFF);
977            return mWifiSystemStateEntries.get(index);
978        }
979    }
980
981    /**
982     * Increment number of times the Watchdog of Last Resort triggered, resetting the wifi stack
983     */
984    public void incrementNumLastResortWatchdogTriggers() {
985        synchronized (mLock) {
986            mWifiLogProto.numLastResortWatchdogTriggers++;
987        }
988    }
989    /**
990     * @param count number of networks over bad association threshold when watchdog triggered
991     */
992    public void addCountToNumLastResortWatchdogBadAssociationNetworksTotal(int count) {
993        synchronized (mLock) {
994            mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal += count;
995        }
996    }
997    /**
998     * @param count number of networks over bad authentication threshold when watchdog triggered
999     */
1000    public void addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(int count) {
1001        synchronized (mLock) {
1002            mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal += count;
1003        }
1004    }
1005    /**
1006     * @param count number of networks over bad dhcp threshold when watchdog triggered
1007     */
1008    public void addCountToNumLastResortWatchdogBadDhcpNetworksTotal(int count) {
1009        synchronized (mLock) {
1010            mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal += count;
1011        }
1012    }
1013    /**
1014     * @param count number of networks over bad other threshold when watchdog triggered
1015     */
1016    public void addCountToNumLastResortWatchdogBadOtherNetworksTotal(int count) {
1017        synchronized (mLock) {
1018            mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal += count;
1019        }
1020    }
1021    /**
1022     * @param count number of networks seen when watchdog triggered
1023     */
1024    public void addCountToNumLastResortWatchdogAvailableNetworksTotal(int count) {
1025        synchronized (mLock) {
1026            mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal += count;
1027        }
1028    }
1029    /**
1030     * Increment count of triggers with atleast one bad association network
1031     */
1032    public void incrementNumLastResortWatchdogTriggersWithBadAssociation() {
1033        synchronized (mLock) {
1034            mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation++;
1035        }
1036    }
1037    /**
1038     * Increment count of triggers with atleast one bad authentication network
1039     */
1040    public void incrementNumLastResortWatchdogTriggersWithBadAuthentication() {
1041        synchronized (mLock) {
1042            mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication++;
1043        }
1044    }
1045    /**
1046     * Increment count of triggers with atleast one bad dhcp network
1047     */
1048    public void incrementNumLastResortWatchdogTriggersWithBadDhcp() {
1049        synchronized (mLock) {
1050            mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp++;
1051        }
1052    }
1053    /**
1054     * Increment count of triggers with atleast one bad other network
1055     */
1056    public void incrementNumLastResortWatchdogTriggersWithBadOther() {
1057        synchronized (mLock) {
1058            mWifiLogProto.numLastResortWatchdogTriggersWithBadOther++;
1059        }
1060    }
1061
1062    /**
1063     * Increment number of times connectivity watchdog confirmed pno is working
1064     */
1065    public void incrementNumConnectivityWatchdogPnoGood() {
1066        synchronized (mLock) {
1067            mWifiLogProto.numConnectivityWatchdogPnoGood++;
1068        }
1069    }
1070    /**
1071     * Increment number of times connectivity watchdog found pno not working
1072     */
1073    public void incrementNumConnectivityWatchdogPnoBad() {
1074        synchronized (mLock) {
1075            mWifiLogProto.numConnectivityWatchdogPnoBad++;
1076        }
1077    }
1078    /**
1079     * Increment number of times connectivity watchdog confirmed background scan is working
1080     */
1081    public void incrementNumConnectivityWatchdogBackgroundGood() {
1082        synchronized (mLock) {
1083            mWifiLogProto.numConnectivityWatchdogBackgroundGood++;
1084        }
1085    }
1086    /**
1087     * Increment number of times connectivity watchdog found background scan not working
1088     */
1089    public void incrementNumConnectivityWatchdogBackgroundBad() {
1090        synchronized (mLock) {
1091            mWifiLogProto.numConnectivityWatchdogBackgroundBad++;
1092        }
1093    }
1094
1095    /**
1096     * Increment various poll related metrics, and cache performance data for StaEvent logging
1097     */
1098    public void handlePollResult(WifiInfo wifiInfo) {
1099        mLastPollRssi = wifiInfo.getRssi();
1100        mLastPollLinkSpeed = wifiInfo.getLinkSpeed();
1101        mLastPollFreq = wifiInfo.getFrequency();
1102        incrementRssiPollRssiCount(mLastPollFreq, mLastPollRssi);
1103    }
1104
1105    /**
1106     * Increment occurence count of RSSI level from RSSI poll for the given frequency.
1107     * @param frequency (MHz)
1108     * @param rssi
1109     */
1110    @VisibleForTesting
1111    public void incrementRssiPollRssiCount(int frequency, int rssi) {
1112        if (!(rssi >= MIN_RSSI_POLL && rssi <= MAX_RSSI_POLL)) {
1113            return;
1114        }
1115        synchronized (mLock) {
1116            if (!mRssiPollCountsMap.containsKey(frequency)) {
1117                mRssiPollCountsMap.put(frequency, new SparseIntArray());
1118            }
1119            SparseIntArray sparseIntArray = mRssiPollCountsMap.get(frequency);
1120            int count = sparseIntArray.get(rssi);
1121            sparseIntArray.put(rssi, count + 1);
1122            maybeIncrementRssiDeltaCount(rssi - mScanResultRssi);
1123        }
1124    }
1125
1126    /**
1127     * Increment occurence count of difference between scan result RSSI and the first RSSI poll.
1128     * Ignores rssi values outside the bounds of [MIN_RSSI_DELTA, MAX_RSSI_DELTA]
1129     * mLock must be held when calling this method.
1130     */
1131    private void maybeIncrementRssiDeltaCount(int rssi) {
1132        // Check if this RSSI poll is close enough to a scan result RSSI to log a delta value
1133        if (mScanResultRssiTimestampMillis >= 0) {
1134            long timeDelta = mClock.getElapsedSinceBootMillis() - mScanResultRssiTimestampMillis;
1135            if (timeDelta <= TIMEOUT_RSSI_DELTA_MILLIS) {
1136                if (rssi >= MIN_RSSI_DELTA && rssi <= MAX_RSSI_DELTA) {
1137                    int count = mRssiDeltaCounts.get(rssi);
1138                    mRssiDeltaCounts.put(rssi, count + 1);
1139                }
1140            }
1141            mScanResultRssiTimestampMillis = -1;
1142        }
1143    }
1144
1145    /**
1146     * Increment count of Watchdog successes.
1147     */
1148    public void incrementNumLastResortWatchdogSuccesses() {
1149        synchronized (mLock) {
1150            mWifiLogProto.numLastResortWatchdogSuccesses++;
1151        }
1152    }
1153
1154    /**
1155     * Increment the count of network connection failures that happened after watchdog has been
1156     * triggered.
1157     */
1158    public void incrementWatchdogTotalConnectionFailureCountAfterTrigger() {
1159        synchronized (mLock) {
1160            mWifiLogProto.watchdogTotalConnectionFailureCountAfterTrigger++;
1161        }
1162    }
1163
1164    /**
1165     * Sets the time taken for wifi to connect after a watchdog triggers a restart.
1166     * @param milliseconds
1167     */
1168    public void setWatchdogSuccessTimeDurationMs(long ms) {
1169        synchronized (mLock) {
1170            mWifiLogProto.watchdogTriggerToConnectionSuccessDurationMs = ms;
1171        }
1172    }
1173
1174    /**
1175     * Increments the count of alerts by alert reason.
1176     *
1177     * @param reason The cause of the alert. The reason values are driver-specific.
1178     */
1179    public void incrementAlertReasonCount(int reason) {
1180        if (reason > WifiLoggerHal.WIFI_ALERT_REASON_MAX
1181                || reason < WifiLoggerHal.WIFI_ALERT_REASON_MIN) {
1182            reason = WifiLoggerHal.WIFI_ALERT_REASON_RESERVED;
1183        }
1184        synchronized (mLock) {
1185            int alertCount = mWifiAlertReasonCounts.get(reason);
1186            mWifiAlertReasonCounts.put(reason, alertCount + 1);
1187        }
1188    }
1189
1190    /**
1191     * Counts all the different types of networks seen in a set of scan results
1192     */
1193    public void countScanResults(List<ScanDetail> scanDetails) {
1194        if (scanDetails == null) {
1195            return;
1196        }
1197        int totalResults = 0;
1198        int openNetworks = 0;
1199        int personalNetworks = 0;
1200        int enterpriseNetworks = 0;
1201        int hiddenNetworks = 0;
1202        int hotspot2r1Networks = 0;
1203        int hotspot2r2Networks = 0;
1204        for (ScanDetail scanDetail : scanDetails) {
1205            NetworkDetail networkDetail = scanDetail.getNetworkDetail();
1206            ScanResult scanResult = scanDetail.getScanResult();
1207            totalResults++;
1208            if (networkDetail != null) {
1209                if (networkDetail.isHiddenBeaconFrame()) {
1210                    hiddenNetworks++;
1211                }
1212                if (networkDetail.getHSRelease() != null) {
1213                    if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R1) {
1214                        hotspot2r1Networks++;
1215                    } else if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R2) {
1216                        hotspot2r2Networks++;
1217                    }
1218                }
1219            }
1220            if (scanResult != null && scanResult.capabilities != null) {
1221                if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
1222                    enterpriseNetworks++;
1223                } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)
1224                        || ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
1225                    personalNetworks++;
1226                } else {
1227                    openNetworks++;
1228                }
1229            }
1230        }
1231        synchronized (mLock) {
1232            mWifiLogProto.numTotalScanResults += totalResults;
1233            mWifiLogProto.numOpenNetworkScanResults += openNetworks;
1234            mWifiLogProto.numPersonalNetworkScanResults += personalNetworks;
1235            mWifiLogProto.numEnterpriseNetworkScanResults += enterpriseNetworks;
1236            mWifiLogProto.numHiddenNetworkScanResults += hiddenNetworks;
1237            mWifiLogProto.numHotspot2R1NetworkScanResults += hotspot2r1Networks;
1238            mWifiLogProto.numHotspot2R2NetworkScanResults += hotspot2r2Networks;
1239            mWifiLogProto.numScans++;
1240        }
1241    }
1242
1243    private boolean mWifiWins = false; // Based on scores, use wifi instead of mobile data?
1244
1245    /**
1246     * Increments occurence of a particular wifi score calculated
1247     * in WifiScoreReport by current connected network. Scores are bounded
1248     * within  [MIN_WIFI_SCORE, MAX_WIFI_SCORE] to limit size of SparseArray.
1249     *
1250     * Also records events when the current score breaches significant thresholds.
1251     */
1252    public void incrementWifiScoreCount(int score) {
1253        if (score < MIN_WIFI_SCORE || score > MAX_WIFI_SCORE) {
1254            return;
1255        }
1256        synchronized (mLock) {
1257            int count = mWifiScoreCounts.get(score);
1258            mWifiScoreCounts.put(score, count + 1);
1259
1260            boolean wifiWins = mWifiWins;
1261            if (mWifiWins && score < LOW_WIFI_SCORE) {
1262                wifiWins = false;
1263            } else if (!mWifiWins && score > LOW_WIFI_SCORE) {
1264                wifiWins = true;
1265            }
1266            mLastScore = score;
1267            if (wifiWins != mWifiWins) {
1268                mWifiWins = wifiWins;
1269                StaEvent event = new StaEvent();
1270                event.type = StaEvent.TYPE_SCORE_BREACH;
1271                addStaEvent(event);
1272            }
1273        }
1274    }
1275
1276    /**
1277     * Increments occurence of the results from attempting to start SoftAp.
1278     * Maps the |result| and WifiManager |failureCode| constant to proto defined SoftApStartResult
1279     * codes.
1280     */
1281    public void incrementSoftApStartResult(boolean result, int failureCode) {
1282        synchronized (mLock) {
1283            if (result) {
1284                int count = mSoftApManagerReturnCodeCounts.get(
1285                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY);
1286                mSoftApManagerReturnCodeCounts.put(
1287                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY,
1288                        count + 1);
1289                return;
1290            }
1291
1292            // now increment failure modes - if not explicitly handled, dump into the general
1293            // error bucket.
1294            if (failureCode == WifiManager.SAP_START_FAILURE_NO_CHANNEL) {
1295                int count = mSoftApManagerReturnCodeCounts.get(
1296                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL);
1297                mSoftApManagerReturnCodeCounts.put(
1298                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL,
1299                        count + 1);
1300            } else {
1301                // failure mode not tracked at this time...  count as a general error for now.
1302                int count = mSoftApManagerReturnCodeCounts.get(
1303                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR);
1304                mSoftApManagerReturnCodeCounts.put(
1305                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR,
1306                        count + 1);
1307            }
1308        }
1309    }
1310
1311    /**
1312     * Adds a record indicating the current up state of soft AP
1313     */
1314    public void addSoftApUpChangedEvent(boolean isUp, int mode) {
1315        SoftApConnectedClientsEvent event = new SoftApConnectedClientsEvent();
1316        event.eventType = isUp ? SoftApConnectedClientsEvent.SOFT_AP_UP :
1317                SoftApConnectedClientsEvent.SOFT_AP_DOWN;
1318        event.numConnectedClients = 0;
1319        addSoftApConnectedClientsEvent(event, mode);
1320    }
1321
1322    /**
1323     * Adds a record for current number of associated stations to soft AP
1324     */
1325    public void addSoftApNumAssociatedStationsChangedEvent(int numStations, int mode) {
1326        SoftApConnectedClientsEvent event = new SoftApConnectedClientsEvent();
1327        event.eventType = SoftApConnectedClientsEvent.NUM_CLIENTS_CHANGED;
1328        event.numConnectedClients = numStations;
1329        addSoftApConnectedClientsEvent(event, mode);
1330    }
1331
1332    /**
1333     * Adds a record to the corresponding event list based on mode param
1334     */
1335    private void addSoftApConnectedClientsEvent(SoftApConnectedClientsEvent event, int mode) {
1336        synchronized (mLock) {
1337            List<SoftApConnectedClientsEvent> softApEventList;
1338            switch (mode) {
1339                case WifiManager.IFACE_IP_MODE_TETHERED:
1340                    softApEventList = mSoftApEventListTethered;
1341                    break;
1342                case WifiManager.IFACE_IP_MODE_LOCAL_ONLY:
1343                    softApEventList = mSoftApEventListLocalOnly;
1344                    break;
1345                default:
1346                    return;
1347            }
1348
1349            if (softApEventList.size() > MAX_NUM_SOFT_AP_EVENTS) {
1350                return;
1351            }
1352
1353            event.timeStampMillis = mClock.getElapsedSinceBootMillis();
1354            softApEventList.add(event);
1355        }
1356    }
1357
1358    /**
1359     * Updates current soft AP events with channel info
1360     */
1361    public void addSoftApChannelSwitchedEvent(int frequency, int bandwidth, int mode) {
1362        synchronized (mLock) {
1363            List<SoftApConnectedClientsEvent> softApEventList;
1364            switch (mode) {
1365                case WifiManager.IFACE_IP_MODE_TETHERED:
1366                    softApEventList = mSoftApEventListTethered;
1367                    break;
1368                case WifiManager.IFACE_IP_MODE_LOCAL_ONLY:
1369                    softApEventList = mSoftApEventListLocalOnly;
1370                    break;
1371                default:
1372                    return;
1373            }
1374
1375            for (int index = softApEventList.size() - 1; index >= 0; index--) {
1376                SoftApConnectedClientsEvent event = softApEventList.get(index);
1377
1378                if (event != null && event.eventType == SoftApConnectedClientsEvent.SOFT_AP_UP) {
1379                    event.channelFrequency = frequency;
1380                    event.channelBandwidth = bandwidth;
1381                    break;
1382                }
1383            }
1384        }
1385    }
1386
1387    /**
1388     * Increment number of times the HAL crashed.
1389     */
1390    public void incrementNumHalCrashes() {
1391        synchronized (mLock) {
1392            mWifiLogProto.numHalCrashes++;
1393        }
1394    }
1395
1396    /**
1397     * Increment number of times the Wificond crashed.
1398     */
1399    public void incrementNumWificondCrashes() {
1400        synchronized (mLock) {
1401            mWifiLogProto.numWificondCrashes++;
1402        }
1403    }
1404
1405    /**
1406     * Increment number of times the supplicant crashed.
1407     */
1408    public void incrementNumSupplicantCrashes() {
1409        synchronized (mLock) {
1410            mWifiLogProto.numSupplicantCrashes++;
1411        }
1412    }
1413
1414    /**
1415     * Increment number of times the hostapd crashed.
1416     */
1417    public void incrementNumHostapdCrashes() {
1418        synchronized (mLock) {
1419            mWifiLogProto.numHostapdCrashes++;
1420        }
1421    }
1422
1423    /**
1424     * Increment number of times the wifi on failed due to an error in HAL.
1425     */
1426    public void incrementNumSetupClientInterfaceFailureDueToHal() {
1427        synchronized (mLock) {
1428            mWifiLogProto.numSetupClientInterfaceFailureDueToHal++;
1429        }
1430    }
1431
1432    /**
1433     * Increment number of times the wifi on failed due to an error in wificond.
1434     */
1435    public void incrementNumSetupClientInterfaceFailureDueToWificond() {
1436        synchronized (mLock) {
1437            mWifiLogProto.numSetupClientInterfaceFailureDueToWificond++;
1438        }
1439    }
1440
1441    /**
1442     * Increment number of times the wifi on failed due to an error in supplicant.
1443     */
1444    public void incrementNumSetupClientInterfaceFailureDueToSupplicant() {
1445        synchronized (mLock) {
1446            mWifiLogProto.numSetupClientInterfaceFailureDueToSupplicant++;
1447        }
1448    }
1449
1450    /**
1451     * Increment number of times the SoftAp on failed due to an error in HAL.
1452     */
1453    public void incrementNumSetupSoftApInterfaceFailureDueToHal() {
1454        synchronized (mLock) {
1455            mWifiLogProto.numSetupSoftApInterfaceFailureDueToHal++;
1456        }
1457    }
1458
1459    /**
1460     * Increment number of times the SoftAp on failed due to an error in wificond.
1461     */
1462    public void incrementNumSetupSoftApInterfaceFailureDueToWificond() {
1463        synchronized (mLock) {
1464            mWifiLogProto.numSetupSoftApInterfaceFailureDueToWificond++;
1465        }
1466    }
1467
1468    /**
1469     * Increment number of times the SoftAp on failed due to an error in hostapd.
1470     */
1471    public void incrementNumSetupSoftApInterfaceFailureDueToHostapd() {
1472        synchronized (mLock) {
1473            mWifiLogProto.numSetupSoftApInterfaceFailureDueToHostapd++;
1474        }
1475    }
1476
1477    /**
1478     * Increment number of times we got client interface down.
1479     */
1480    public void incrementNumClientInterfaceDown() {
1481        synchronized (mLock) {
1482            mWifiLogProto.numClientInterfaceDown++;
1483        }
1484    }
1485
1486    /**
1487     * Increment number of times we got client interface down.
1488     */
1489    public void incrementNumSoftApInterfaceDown() {
1490        synchronized (mLock) {
1491            mWifiLogProto.numSoftApInterfaceDown++;
1492        }
1493    }
1494
1495    /**
1496     * Increment number of times Passpoint provider being installed.
1497     */
1498    public void incrementNumPasspointProviderInstallation() {
1499        synchronized (mLock) {
1500            mWifiLogProto.numPasspointProviderInstallation++;
1501        }
1502    }
1503
1504    /**
1505     * Increment number of times Passpoint provider is installed successfully.
1506     */
1507    public void incrementNumPasspointProviderInstallSuccess() {
1508        synchronized (mLock) {
1509            mWifiLogProto.numPasspointProviderInstallSuccess++;
1510        }
1511    }
1512
1513    /**
1514     * Increment number of times Passpoint provider being uninstalled.
1515     */
1516    public void incrementNumPasspointProviderUninstallation() {
1517        synchronized (mLock) {
1518            mWifiLogProto.numPasspointProviderUninstallation++;
1519        }
1520    }
1521
1522    /**
1523     * Increment number of times Passpoint provider is uninstalled successfully.
1524     */
1525    public void incrementNumPasspointProviderUninstallSuccess() {
1526        synchronized (mLock) {
1527            mWifiLogProto.numPasspointProviderUninstallSuccess++;
1528        }
1529    }
1530
1531    /**
1532     * Increment number of times we detected a radio mode change to MCC.
1533     */
1534    public void incrementNumRadioModeChangeToMcc() {
1535        synchronized (mLock) {
1536            mWifiLogProto.numRadioModeChangeToMcc++;
1537        }
1538    }
1539
1540    /**
1541     * Increment number of times we detected a radio mode change to SCC.
1542     */
1543    public void incrementNumRadioModeChangeToScc() {
1544        synchronized (mLock) {
1545            mWifiLogProto.numRadioModeChangeToScc++;
1546        }
1547    }
1548
1549    /**
1550     * Increment number of times we detected a radio mode change to SBS.
1551     */
1552    public void incrementNumRadioModeChangeToSbs() {
1553        synchronized (mLock) {
1554            mWifiLogProto.numRadioModeChangeToSbs++;
1555        }
1556    }
1557
1558    /**
1559     * Increment number of times we detected a radio mode change to DBS.
1560     */
1561    public void incrementNumRadioModeChangeToDbs() {
1562        synchronized (mLock) {
1563            mWifiLogProto.numRadioModeChangeToDbs++;
1564        }
1565    }
1566
1567    /**
1568     * Increment number of times we detected a channel did not satisfy user band preference.
1569     */
1570    public void incrementNumSoftApUserBandPreferenceUnsatisfied() {
1571        synchronized (mLock) {
1572            mWifiLogProto.numSoftApUserBandPreferenceUnsatisfied++;
1573        }
1574    }
1575
1576    /**
1577     * Increment N-Way network selection decision histograms:
1578     * Counts the size of various sets of scanDetails within a scan, and increment the occurrence
1579     * of that size for the associated histogram. There are ten histograms generated for each
1580     * combination of: {SSID, BSSID} *{Total, Saved, Open, Saved_or_Open, Passpoint}
1581     * Only performs this count if isFullBand is true, otherwise, increments the partial scan count
1582     */
1583    public void incrementAvailableNetworksHistograms(List<ScanDetail> scanDetails,
1584            boolean isFullBand) {
1585        synchronized (mLock) {
1586            if (mWifiConfigManager == null || mWifiNetworkSelector == null
1587                    || mPasspointManager == null) {
1588                return;
1589            }
1590            if (!isFullBand) {
1591                mWifiLogProto.partialAllSingleScanListenerResults++;
1592                return;
1593            }
1594            Set<ScanResultMatchInfo> ssids = new HashSet<ScanResultMatchInfo>();
1595            int bssids = 0;
1596            Set<ScanResultMatchInfo> openSsids = new HashSet<ScanResultMatchInfo>();
1597            int openBssids = 0;
1598            Set<ScanResultMatchInfo> savedSsids = new HashSet<ScanResultMatchInfo>();
1599            int savedBssids = 0;
1600            // openOrSavedSsids calculated from union of savedSsids & openSsids
1601            int openOrSavedBssids = 0;
1602            Set<PasspointProvider> savedPasspointProviderProfiles =
1603                    new HashSet<PasspointProvider>();
1604            int savedPasspointProviderBssids = 0;
1605            int passpointR1Aps = 0;
1606            int passpointR2Aps = 0;
1607            Map<ANQPNetworkKey, Integer> passpointR1UniqueEss = new HashMap<>();
1608            Map<ANQPNetworkKey, Integer> passpointR2UniqueEss = new HashMap<>();
1609            int supporting80211mcAps = 0;
1610            for (ScanDetail scanDetail : scanDetails) {
1611                NetworkDetail networkDetail = scanDetail.getNetworkDetail();
1612                ScanResult scanResult = scanDetail.getScanResult();
1613
1614                // statistics to be collected for ALL APs (irrespective of signal power)
1615                if (networkDetail.is80211McResponderSupport()) {
1616                    supporting80211mcAps++;
1617                }
1618
1619                ScanResultMatchInfo matchInfo = ScanResultMatchInfo.fromScanResult(scanResult);
1620                Pair<PasspointProvider, PasspointMatch> providerMatch = null;
1621                PasspointProvider passpointProvider = null;
1622                if (networkDetail.isInterworking()) {
1623                    providerMatch =
1624                            mPasspointManager.matchProvider(scanResult);
1625                    passpointProvider = providerMatch != null ? providerMatch.first : null;
1626
1627                    if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R1) {
1628                        passpointR1Aps++;
1629                    } else if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R2) {
1630                        passpointR2Aps++;
1631                    }
1632
1633                    long bssid = 0;
1634                    boolean validBssid = false;
1635                    try {
1636                        bssid = Utils.parseMac(scanResult.BSSID);
1637                        validBssid = true;
1638                    } catch (IllegalArgumentException e) {
1639                        Log.e(TAG,
1640                                "Invalid BSSID provided in the scan result: " + scanResult.BSSID);
1641                    }
1642                    if (validBssid) {
1643                        ANQPNetworkKey uniqueEss = ANQPNetworkKey.buildKey(scanResult.SSID, bssid,
1644                                scanResult.hessid, networkDetail.getAnqpDomainID());
1645                        if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R1) {
1646                            Integer countObj = passpointR1UniqueEss.get(uniqueEss);
1647                            int count = countObj == null ? 0 : countObj;
1648                            passpointR1UniqueEss.put(uniqueEss, count + 1);
1649                        } else if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R2) {
1650                            Integer countObj = passpointR2UniqueEss.get(uniqueEss);
1651                            int count = countObj == null ? 0 : countObj;
1652                            passpointR2UniqueEss.put(uniqueEss, count + 1);
1653                        }
1654                    }
1655
1656                }
1657
1658                if (mWifiNetworkSelector.isSignalTooWeak(scanResult)) {
1659                    continue;
1660                }
1661
1662                // statistics to be collected ONLY for those APs with sufficient signal power
1663
1664                ssids.add(matchInfo);
1665                bssids++;
1666                boolean isOpen = matchInfo.networkType == ScanResultMatchInfo.NETWORK_TYPE_OPEN;
1667                WifiConfiguration config =
1668                        mWifiConfigManager.getConfiguredNetworkForScanDetail(scanDetail);
1669                boolean isSaved = (config != null) && !config.isEphemeral()
1670                        && !config.isPasspoint();
1671                boolean isSavedPasspoint = passpointProvider != null;
1672                if (isOpen) {
1673                    openSsids.add(matchInfo);
1674                    openBssids++;
1675                }
1676                if (isSaved) {
1677                    savedSsids.add(matchInfo);
1678                    savedBssids++;
1679                }
1680                if (isOpen || isSaved) {
1681                    openOrSavedBssids++;
1682                    // Calculate openOrSavedSsids union later
1683                }
1684                if (isSavedPasspoint) {
1685                    savedPasspointProviderProfiles.add(passpointProvider);
1686                    savedPasspointProviderBssids++;
1687                }
1688            }
1689            mWifiLogProto.fullBandAllSingleScanListenerResults++;
1690            incrementTotalScanSsids(mTotalSsidsInScanHistogram, ssids.size());
1691            incrementTotalScanResults(mTotalBssidsInScanHistogram, bssids);
1692            incrementSsid(mAvailableOpenSsidsInScanHistogram, openSsids.size());
1693            incrementBssid(mAvailableOpenBssidsInScanHistogram, openBssids);
1694            incrementSsid(mAvailableSavedSsidsInScanHistogram, savedSsids.size());
1695            incrementBssid(mAvailableSavedBssidsInScanHistogram, savedBssids);
1696            openSsids.addAll(savedSsids); // openSsids = Union(openSsids, savedSsids)
1697            incrementSsid(mAvailableOpenOrSavedSsidsInScanHistogram, openSsids.size());
1698            incrementBssid(mAvailableOpenOrSavedBssidsInScanHistogram, openOrSavedBssids);
1699            incrementSsid(mAvailableSavedPasspointProviderProfilesInScanHistogram,
1700                    savedPasspointProviderProfiles.size());
1701            incrementBssid(mAvailableSavedPasspointProviderBssidsInScanHistogram,
1702                    savedPasspointProviderBssids);
1703            incrementTotalPasspointAps(mObservedHotspotR1ApInScanHistogram, passpointR1Aps);
1704            incrementTotalPasspointAps(mObservedHotspotR2ApInScanHistogram, passpointR2Aps);
1705            incrementTotalUniquePasspointEss(mObservedHotspotR1EssInScanHistogram,
1706                    passpointR1UniqueEss.size());
1707            incrementTotalUniquePasspointEss(mObservedHotspotR2EssInScanHistogram,
1708                    passpointR2UniqueEss.size());
1709            for (Integer count : passpointR1UniqueEss.values()) {
1710                incrementPasspointPerUniqueEss(mObservedHotspotR1ApsPerEssInScanHistogram, count);
1711            }
1712            for (Integer count : passpointR2UniqueEss.values()) {
1713                incrementPasspointPerUniqueEss(mObservedHotspotR2ApsPerEssInScanHistogram, count);
1714            }
1715            increment80211mcAps(mObserved80211mcApInScanHistogram, supporting80211mcAps);
1716        }
1717    }
1718
1719    /**
1720     * TODO: (b/72443859) Use notifierTag param to separate metrics for OpenNetworkNotifier and
1721     * CarrierNetworkNotifier, for this method and all other related metrics.
1722     */
1723    /** Increments the occurence of a "Connect to Network" notification. */
1724    public void incrementConnectToNetworkNotification(String notifierTag, int notificationType) {
1725        synchronized (mLock) {
1726            int count = mConnectToNetworkNotificationCount.get(notificationType);
1727            mConnectToNetworkNotificationCount.put(notificationType, count + 1);
1728        }
1729    }
1730
1731    /** Increments the occurence of an "Connect to Network" notification user action. */
1732    public void incrementConnectToNetworkNotificationAction(String notifierTag,
1733            int notificationType, int actionType) {
1734        synchronized (mLock) {
1735            int key = notificationType * CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER
1736                    + actionType;
1737            int count = mConnectToNetworkNotificationActionCount.get(key);
1738            mConnectToNetworkNotificationActionCount.put(key, count + 1);
1739        }
1740    }
1741
1742    /**
1743     * Sets the number of SSIDs blacklisted from recommendation by the open network notification
1744     * recommender.
1745     */
1746    public void setNetworkRecommenderBlacklistSize(String notifierTag, int size) {
1747        synchronized (mLock) {
1748            mOpenNetworkRecommenderBlacklistSize = size;
1749        }
1750    }
1751
1752    /** Sets if the available network notification feature is enabled. */
1753    public void setIsWifiNetworksAvailableNotificationEnabled(String notifierTag, boolean enabled) {
1754        synchronized (mLock) {
1755            mIsWifiNetworksAvailableNotificationOn = enabled;
1756        }
1757    }
1758
1759    /** Increments the occurence of connection attempts that were initiated unsuccessfully */
1760    public void incrementNumNetworkRecommendationUpdates(String notifierTag) {
1761        synchronized (mLock) {
1762            mNumOpenNetworkRecommendationUpdates++;
1763        }
1764    }
1765
1766    /** Increments the occurence of connection attempts that were initiated unsuccessfully */
1767    public void incrementNumNetworkConnectMessageFailedToSend(String notifierTag) {
1768        synchronized (mLock) {
1769            mNumOpenNetworkConnectMessageFailedToSend++;
1770        }
1771    }
1772
1773    /** Sets if Connected MAC Randomization feature is enabled */
1774    public void setIsMacRandomizationOn(boolean enabled) {
1775        synchronized (mLock) {
1776            mIsMacRandomizationOn = enabled;
1777        }
1778    }
1779
1780    public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
1781    public static final String CLEAN_DUMP_ARG = "clean";
1782
1783    /**
1784     * Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager
1785     * at this time.
1786     *
1787     * @param fd unused
1788     * @param pw PrintWriter for writing dump to
1789     * @param args [wifiMetricsProto [clean]]
1790     */
1791    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1792        synchronized (mLock) {
1793            consolidateScoringParams();
1794            if (args != null && args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) {
1795                // Dump serialized WifiLog proto
1796                consolidateProto(true);
1797                for (ConnectionEvent event : mConnectionEventList) {
1798                    if (mCurrentConnectionEvent != event) {
1799                        //indicate that automatic bug report has been taken for all valid
1800                        //connection events
1801                        event.mConnectionEvent.automaticBugReportTaken = true;
1802                    }
1803                }
1804                byte[] wifiMetricsProto = WifiMetricsProto.WifiLog.toByteArray(mWifiLogProto);
1805                String metricsProtoDump = Base64.encodeToString(wifiMetricsProto, Base64.DEFAULT);
1806                if (args.length > 1 && CLEAN_DUMP_ARG.equals(args[1])) {
1807                    // Output metrics proto bytes (base64) and nothing else
1808                    pw.print(metricsProtoDump);
1809                } else {
1810                    // Tag the start and end of the metrics proto bytes
1811                    pw.println("WifiMetrics:");
1812                    pw.println(metricsProtoDump);
1813                    pw.println("EndWifiMetrics");
1814                }
1815                clear();
1816            } else {
1817                pw.println("WifiMetrics:");
1818                pw.println("mConnectionEvents:");
1819                for (ConnectionEvent event : mConnectionEventList) {
1820                    String eventLine = event.toString();
1821                    if (event == mCurrentConnectionEvent) {
1822                        eventLine += "CURRENTLY OPEN EVENT";
1823                    }
1824                    pw.println(eventLine);
1825                }
1826                pw.println("mWifiLogProto.numSavedNetworks=" + mWifiLogProto.numSavedNetworks);
1827                pw.println("mWifiLogProto.numOpenNetworks=" + mWifiLogProto.numOpenNetworks);
1828                pw.println("mWifiLogProto.numPersonalNetworks="
1829                        + mWifiLogProto.numPersonalNetworks);
1830                pw.println("mWifiLogProto.numEnterpriseNetworks="
1831                        + mWifiLogProto.numEnterpriseNetworks);
1832                pw.println("mWifiLogProto.numHiddenNetworks=" + mWifiLogProto.numHiddenNetworks);
1833                pw.println("mWifiLogProto.numPasspointNetworks="
1834                        + mWifiLogProto.numPasspointNetworks);
1835                pw.println("mWifiLogProto.isLocationEnabled=" + mWifiLogProto.isLocationEnabled);
1836                pw.println("mWifiLogProto.isScanningAlwaysEnabled="
1837                        + mWifiLogProto.isScanningAlwaysEnabled);
1838                pw.println("mWifiLogProto.numNetworksAddedByUser="
1839                        + mWifiLogProto.numNetworksAddedByUser);
1840                pw.println("mWifiLogProto.numNetworksAddedByApps="
1841                        + mWifiLogProto.numNetworksAddedByApps);
1842                pw.println("mWifiLogProto.numNonEmptyScanResults="
1843                        + mWifiLogProto.numNonEmptyScanResults);
1844                pw.println("mWifiLogProto.numEmptyScanResults="
1845                        + mWifiLogProto.numEmptyScanResults);
1846                pw.println("mWifiLogProto.numConnecitvityOneshotScans="
1847                        + mWifiLogProto.numConnectivityOneshotScans);
1848                pw.println("mWifiLogProto.numOneshotScans="
1849                        + mWifiLogProto.numOneshotScans);
1850                pw.println("mWifiLogProto.numBackgroundScans="
1851                        + mWifiLogProto.numBackgroundScans);
1852                pw.println("mWifiLogProto.numExternalAppOneshotScanRequests="
1853                        + mWifiLogProto.numExternalAppOneshotScanRequests);
1854                pw.println("mWifiLogProto.numExternalForegroundAppOneshotScanRequestsThrottled="
1855                        + mWifiLogProto.numExternalForegroundAppOneshotScanRequestsThrottled);
1856                pw.println("mWifiLogProto.numExternalBackgroundAppOneshotScanRequestsThrottled="
1857                        + mWifiLogProto.numExternalBackgroundAppOneshotScanRequestsThrottled);
1858
1859                pw.println("mScanReturnEntries:");
1860                pw.println("  SCAN_UNKNOWN: " + getScanReturnEntry(
1861                        WifiMetricsProto.WifiLog.SCAN_UNKNOWN));
1862                pw.println("  SCAN_SUCCESS: " + getScanReturnEntry(
1863                        WifiMetricsProto.WifiLog.SCAN_SUCCESS));
1864                pw.println("  SCAN_FAILURE_INTERRUPTED: " + getScanReturnEntry(
1865                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED));
1866                pw.println("  SCAN_FAILURE_INVALID_CONFIGURATION: " + getScanReturnEntry(
1867                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION));
1868                pw.println("  FAILURE_WIFI_DISABLED: " + getScanReturnEntry(
1869                        WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED));
1870
1871                pw.println("mSystemStateEntries: <state><screenOn> : <scansInitiated>");
1872                pw.println("  WIFI_UNKNOWN       ON: "
1873                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, true));
1874                pw.println("  WIFI_DISABLED      ON: "
1875                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, true));
1876                pw.println("  WIFI_DISCONNECTED  ON: "
1877                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, true));
1878                pw.println("  WIFI_ASSOCIATED    ON: "
1879                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, true));
1880                pw.println("  WIFI_UNKNOWN      OFF: "
1881                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, false));
1882                pw.println("  WIFI_DISABLED     OFF: "
1883                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, false));
1884                pw.println("  WIFI_DISCONNECTED OFF: "
1885                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, false));
1886                pw.println("  WIFI_ASSOCIATED   OFF: "
1887                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, false));
1888                pw.println("mWifiLogProto.numConnectivityWatchdogPnoGood="
1889                        + mWifiLogProto.numConnectivityWatchdogPnoGood);
1890                pw.println("mWifiLogProto.numConnectivityWatchdogPnoBad="
1891                        + mWifiLogProto.numConnectivityWatchdogPnoBad);
1892                pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundGood="
1893                        + mWifiLogProto.numConnectivityWatchdogBackgroundGood);
1894                pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundBad="
1895                        + mWifiLogProto.numConnectivityWatchdogBackgroundBad);
1896                pw.println("mWifiLogProto.numLastResortWatchdogTriggers="
1897                        + mWifiLogProto.numLastResortWatchdogTriggers);
1898                pw.println("mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal="
1899                        + mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal);
1900                pw.println("mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal="
1901                        + mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal);
1902                pw.println("mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal="
1903                        + mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal);
1904                pw.println("mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal="
1905                        + mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal);
1906                pw.println("mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal="
1907                        + mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal);
1908                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation="
1909                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation);
1910                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication="
1911                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication);
1912                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp="
1913                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp);
1914                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadOther="
1915                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadOther);
1916                pw.println("mWifiLogProto.numLastResortWatchdogSuccesses="
1917                        + mWifiLogProto.numLastResortWatchdogSuccesses);
1918                pw.println("mWifiLogProto.recordDurationSec="
1919                        + ((mClock.getElapsedSinceBootMillis() / 1000) - mRecordStartTimeSec));
1920
1921                try {
1922                    JSONObject rssiMap = new JSONObject();
1923                    for (Map.Entry<Integer, SparseIntArray> entry : mRssiPollCountsMap.entrySet()) {
1924                        int frequency = entry.getKey();
1925                        final SparseIntArray histogram = entry.getValue();
1926                        JSONArray histogramElements = new JSONArray();
1927                        for (int i = MIN_RSSI_POLL; i <= MAX_RSSI_POLL; i++) {
1928                            int count = histogram.get(i);
1929                            if (count == 0) {
1930                                continue;
1931                            }
1932                            JSONObject histogramElement = new JSONObject();
1933                            histogramElement.put(Integer.toString(i), count);
1934                            histogramElements.put(histogramElement);
1935                        }
1936                        rssiMap.put(Integer.toString(frequency), histogramElements);
1937                    }
1938                    pw.println("mWifiLogProto.rssiPollCount: " + rssiMap.toString());
1939                } catch (JSONException e) {
1940                    pw.println("JSONException occurred: " + e.getMessage());
1941                }
1942
1943                pw.println("mWifiLogProto.rssiPollDeltaCount: Printing counts for ["
1944                        + MIN_RSSI_DELTA + ", " + MAX_RSSI_DELTA + "]");
1945                StringBuilder sb = new StringBuilder();
1946                for (int i = MIN_RSSI_DELTA; i <= MAX_RSSI_DELTA; i++) {
1947                    sb.append(mRssiDeltaCounts.get(i) + " ");
1948                }
1949                pw.println("  " + sb.toString());
1950                pw.print("mWifiLogProto.alertReasonCounts=");
1951                sb.setLength(0);
1952                for (int i = WifiLoggerHal.WIFI_ALERT_REASON_MIN;
1953                        i <= WifiLoggerHal.WIFI_ALERT_REASON_MAX; i++) {
1954                    int count = mWifiAlertReasonCounts.get(i);
1955                    if (count > 0) {
1956                        sb.append("(" + i + "," + count + "),");
1957                    }
1958                }
1959                if (sb.length() > 1) {
1960                    sb.setLength(sb.length() - 1);  // strip trailing comma
1961                    pw.println(sb.toString());
1962                } else {
1963                    pw.println("()");
1964                }
1965                pw.println("mWifiLogProto.numTotalScanResults="
1966                        + mWifiLogProto.numTotalScanResults);
1967                pw.println("mWifiLogProto.numOpenNetworkScanResults="
1968                        + mWifiLogProto.numOpenNetworkScanResults);
1969                pw.println("mWifiLogProto.numPersonalNetworkScanResults="
1970                        + mWifiLogProto.numPersonalNetworkScanResults);
1971                pw.println("mWifiLogProto.numEnterpriseNetworkScanResults="
1972                        + mWifiLogProto.numEnterpriseNetworkScanResults);
1973                pw.println("mWifiLogProto.numHiddenNetworkScanResults="
1974                        + mWifiLogProto.numHiddenNetworkScanResults);
1975                pw.println("mWifiLogProto.numHotspot2R1NetworkScanResults="
1976                        + mWifiLogProto.numHotspot2R1NetworkScanResults);
1977                pw.println("mWifiLogProto.numHotspot2R2NetworkScanResults="
1978                        + mWifiLogProto.numHotspot2R2NetworkScanResults);
1979                pw.println("mWifiLogProto.numScans=" + mWifiLogProto.numScans);
1980                pw.println("mWifiLogProto.WifiScoreCount: [" + MIN_WIFI_SCORE + ", "
1981                        + MAX_WIFI_SCORE + "]");
1982                for (int i = 0; i <= MAX_WIFI_SCORE; i++) {
1983                    pw.print(mWifiScoreCounts.get(i) + " ");
1984                }
1985                pw.println(); // add a line after wifi scores
1986                pw.println("mWifiLogProto.SoftApManagerReturnCodeCounts:");
1987                pw.println("  SUCCESS: " + mSoftApManagerReturnCodeCounts.get(
1988                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY));
1989                pw.println("  FAILED_GENERAL_ERROR: " + mSoftApManagerReturnCodeCounts.get(
1990                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR));
1991                pw.println("  FAILED_NO_CHANNEL: " + mSoftApManagerReturnCodeCounts.get(
1992                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL));
1993                pw.print("\n");
1994                pw.println("mWifiLogProto.numHalCrashes="
1995                        + mWifiLogProto.numHalCrashes);
1996                pw.println("mWifiLogProto.numWificondCrashes="
1997                        + mWifiLogProto.numWificondCrashes);
1998                pw.println("mWifiLogProto.numSupplicantCrashes="
1999                        + mWifiLogProto.numSupplicantCrashes);
2000                pw.println("mWifiLogProto.numHostapdCrashes="
2001                        + mWifiLogProto.numHostapdCrashes);
2002                pw.println("mWifiLogProto.numSetupClientInterfaceFailureDueToHal="
2003                        + mWifiLogProto.numSetupClientInterfaceFailureDueToHal);
2004                pw.println("mWifiLogProto.numSetupClientInterfaceFailureDueToWificond="
2005                        + mWifiLogProto.numSetupClientInterfaceFailureDueToWificond);
2006                pw.println("mWifiLogProto.numSetupClientInterfaceFailureDueToSupplicant="
2007                        + mWifiLogProto.numSetupClientInterfaceFailureDueToSupplicant);
2008                pw.println("mWifiLogProto.numSetupSoftApInterfaceFailureDueToHal="
2009                        + mWifiLogProto.numSetupSoftApInterfaceFailureDueToHal);
2010                pw.println("mWifiLogProto.numSetupSoftApInterfaceFailureDueToWificond="
2011                        + mWifiLogProto.numSetupSoftApInterfaceFailureDueToWificond);
2012                pw.println("mWifiLogProto.numSetupSoftApInterfaceFailureDueToHostapd="
2013                        + mWifiLogProto.numSetupSoftApInterfaceFailureDueToHostapd);
2014                pw.println("StaEventList:");
2015                for (StaEventWithTime event : mStaEventList) {
2016                    pw.println(event);
2017                }
2018
2019                pw.println("mWifiLogProto.numPasspointProviders="
2020                        + mWifiLogProto.numPasspointProviders);
2021                pw.println("mWifiLogProto.numPasspointProviderInstallation="
2022                        + mWifiLogProto.numPasspointProviderInstallation);
2023                pw.println("mWifiLogProto.numPasspointProviderInstallSuccess="
2024                        + mWifiLogProto.numPasspointProviderInstallSuccess);
2025                pw.println("mWifiLogProto.numPasspointProviderUninstallation="
2026                        + mWifiLogProto.numPasspointProviderUninstallation);
2027                pw.println("mWifiLogProto.numPasspointProviderUninstallSuccess="
2028                        + mWifiLogProto.numPasspointProviderUninstallSuccess);
2029                pw.println("mWifiLogProto.numPasspointProvidersSuccessfullyConnected="
2030                        + mWifiLogProto.numPasspointProvidersSuccessfullyConnected);
2031                pw.println("mWifiLogProto.numRadioModeChangeToMcc="
2032                        + mWifiLogProto.numRadioModeChangeToMcc);
2033                pw.println("mWifiLogProto.numRadioModeChangeToScc="
2034                        + mWifiLogProto.numRadioModeChangeToScc);
2035                pw.println("mWifiLogProto.numRadioModeChangeToSbs="
2036                        + mWifiLogProto.numRadioModeChangeToSbs);
2037                pw.println("mWifiLogProto.numRadioModeChangeToDbs="
2038                        + mWifiLogProto.numRadioModeChangeToDbs);
2039                pw.println("mWifiLogProto.numSoftApUserBandPreferenceUnsatisfied="
2040                        + mWifiLogProto.numSoftApUserBandPreferenceUnsatisfied);
2041                pw.println("mTotalSsidsInScanHistogram:"
2042                        + mTotalSsidsInScanHistogram.toString());
2043                pw.println("mTotalBssidsInScanHistogram:"
2044                        + mTotalBssidsInScanHistogram.toString());
2045                pw.println("mAvailableOpenSsidsInScanHistogram:"
2046                        + mAvailableOpenSsidsInScanHistogram.toString());
2047                pw.println("mAvailableOpenBssidsInScanHistogram:"
2048                        + mAvailableOpenBssidsInScanHistogram.toString());
2049                pw.println("mAvailableSavedSsidsInScanHistogram:"
2050                        + mAvailableSavedSsidsInScanHistogram.toString());
2051                pw.println("mAvailableSavedBssidsInScanHistogram:"
2052                        + mAvailableSavedBssidsInScanHistogram.toString());
2053                pw.println("mAvailableOpenOrSavedSsidsInScanHistogram:"
2054                        + mAvailableOpenOrSavedSsidsInScanHistogram.toString());
2055                pw.println("mAvailableOpenOrSavedBssidsInScanHistogram:"
2056                        + mAvailableOpenOrSavedBssidsInScanHistogram.toString());
2057                pw.println("mAvailableSavedPasspointProviderProfilesInScanHistogram:"
2058                        + mAvailableSavedPasspointProviderProfilesInScanHistogram.toString());
2059                pw.println("mAvailableSavedPasspointProviderBssidsInScanHistogram:"
2060                        + mAvailableSavedPasspointProviderBssidsInScanHistogram.toString());
2061                pw.println("mWifiLogProto.partialAllSingleScanListenerResults="
2062                        + mWifiLogProto.partialAllSingleScanListenerResults);
2063                pw.println("mWifiLogProto.fullBandAllSingleScanListenerResults="
2064                        + mWifiLogProto.fullBandAllSingleScanListenerResults);
2065                pw.println("mWifiAwareMetrics:");
2066                mWifiAwareMetrics.dump(fd, pw, args);
2067                pw.println("mRttMetrics:");
2068                mRttMetrics.dump(fd, pw, args);
2069
2070                pw.println("mPnoScanMetrics.numPnoScanAttempts="
2071                        + mPnoScanMetrics.numPnoScanAttempts);
2072                pw.println("mPnoScanMetrics.numPnoScanFailed="
2073                        + mPnoScanMetrics.numPnoScanFailed);
2074                pw.println("mPnoScanMetrics.numPnoScanStartedOverOffload="
2075                        + mPnoScanMetrics.numPnoScanStartedOverOffload);
2076                pw.println("mPnoScanMetrics.numPnoScanFailedOverOffload="
2077                        + mPnoScanMetrics.numPnoScanFailedOverOffload);
2078                pw.println("mPnoScanMetrics.numPnoFoundNetworkEvents="
2079                        + mPnoScanMetrics.numPnoFoundNetworkEvents);
2080
2081                pw.println("mWifiLogProto.connectToNetworkNotificationCount="
2082                        + mConnectToNetworkNotificationCount.toString());
2083                pw.println("mWifiLogProto.connectToNetworkNotificationActionCount="
2084                        + mConnectToNetworkNotificationActionCount.toString());
2085                pw.println("mWifiLogProto.openNetworkRecommenderBlacklistSize="
2086                        + mOpenNetworkRecommenderBlacklistSize);
2087                pw.println("mWifiLogProto.isWifiNetworksAvailableNotificationOn="
2088                        + mIsWifiNetworksAvailableNotificationOn);
2089                pw.println("mWifiLogProto.numOpenNetworkRecommendationUpdates="
2090                        + mNumOpenNetworkRecommendationUpdates);
2091                pw.println("mWifiLogProto.numOpenNetworkConnectMessageFailedToSend="
2092                        + mNumOpenNetworkConnectMessageFailedToSend);
2093
2094                pw.println("mWifiLogProto.observedHotspotR1ApInScanHistogram="
2095                        + mObservedHotspotR1ApInScanHistogram);
2096                pw.println("mWifiLogProto.observedHotspotR2ApInScanHistogram="
2097                        + mObservedHotspotR2ApInScanHistogram);
2098                pw.println("mWifiLogProto.observedHotspotR1EssInScanHistogram="
2099                        + mObservedHotspotR1EssInScanHistogram);
2100                pw.println("mWifiLogProto.observedHotspotR2EssInScanHistogram="
2101                        + mObservedHotspotR2EssInScanHistogram);
2102                pw.println("mWifiLogProto.observedHotspotR1ApsPerEssInScanHistogram="
2103                        + mObservedHotspotR1ApsPerEssInScanHistogram);
2104                pw.println("mWifiLogProto.observedHotspotR2ApsPerEssInScanHistogram="
2105                        + mObservedHotspotR2ApsPerEssInScanHistogram);
2106
2107                pw.println("mWifiLogProto.observed80211mcSupportingApsInScanHistogram"
2108                        + mObserved80211mcApInScanHistogram);
2109
2110                pw.println("mSoftApTetheredEvents:");
2111                for (SoftApConnectedClientsEvent event : mSoftApEventListTethered) {
2112                    StringBuilder eventLine = new StringBuilder();
2113                    eventLine.append("event_type=" + event.eventType);
2114                    eventLine.append(",time_stamp_millis=" + event.timeStampMillis);
2115                    eventLine.append(",num_connected_clients=" + event.numConnectedClients);
2116                    eventLine.append(",channel_frequency=" + event.channelFrequency);
2117                    eventLine.append(",channel_bandwidth=" + event.channelBandwidth);
2118                    pw.println(eventLine.toString());
2119                }
2120                pw.println("mSoftApLocalOnlyEvents:");
2121                for (SoftApConnectedClientsEvent event : mSoftApEventListLocalOnly) {
2122                    StringBuilder eventLine = new StringBuilder();
2123                    eventLine.append("event_type=" + event.eventType);
2124                    eventLine.append(",time_stamp_millis=" + event.timeStampMillis);
2125                    eventLine.append(",num_connected_clients=" + event.numConnectedClients);
2126                    eventLine.append(",channel_frequency=" + event.channelFrequency);
2127                    eventLine.append(",channel_bandwidth=" + event.channelBandwidth);
2128                    pw.println(eventLine.toString());
2129                }
2130
2131                pw.println("mWpsMetrics.numWpsAttempts="
2132                        + mWpsMetrics.numWpsAttempts);
2133                pw.println("mWpsMetrics.numWpsSuccess="
2134                        + mWpsMetrics.numWpsSuccess);
2135                pw.println("mWpsMetrics.numWpsStartFailure="
2136                        + mWpsMetrics.numWpsStartFailure);
2137                pw.println("mWpsMetrics.numWpsOverlapFailure="
2138                        + mWpsMetrics.numWpsOverlapFailure);
2139                pw.println("mWpsMetrics.numWpsTimeoutFailure="
2140                        + mWpsMetrics.numWpsTimeoutFailure);
2141                pw.println("mWpsMetrics.numWpsOtherConnectionFailure="
2142                        + mWpsMetrics.numWpsOtherConnectionFailure);
2143                pw.println("mWpsMetrics.numWpsSupplicantFailure="
2144                        + mWpsMetrics.numWpsSupplicantFailure);
2145                pw.println("mWpsMetrics.numWpsCancellation="
2146                        + mWpsMetrics.numWpsCancellation);
2147
2148                mWifiPowerMetrics.dump(pw);
2149                mWifiWakeMetrics.dump(pw);
2150
2151                pw.println("mWifiLogProto.isMacRandomizationOn=" + mIsMacRandomizationOn);
2152                pw.println("mWifiLogProto.scoreExperimentId=" + mWifiLogProto.scoreExperimentId);
2153            }
2154        }
2155    }
2156
2157    /**
2158     * Update various counts of saved network types
2159     * @param networks List of WifiConfigurations representing all saved networks, must not be null
2160     */
2161    public void updateSavedNetworks(List<WifiConfiguration> networks) {
2162        synchronized (mLock) {
2163            mWifiLogProto.numSavedNetworks = networks.size();
2164            mWifiLogProto.numOpenNetworks = 0;
2165            mWifiLogProto.numPersonalNetworks = 0;
2166            mWifiLogProto.numEnterpriseNetworks = 0;
2167            mWifiLogProto.numNetworksAddedByUser = 0;
2168            mWifiLogProto.numNetworksAddedByApps = 0;
2169            mWifiLogProto.numHiddenNetworks = 0;
2170            mWifiLogProto.numPasspointNetworks = 0;
2171            for (WifiConfiguration config : networks) {
2172                if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
2173                    mWifiLogProto.numOpenNetworks++;
2174                } else if (config.isEnterprise()) {
2175                    mWifiLogProto.numEnterpriseNetworks++;
2176                } else {
2177                    mWifiLogProto.numPersonalNetworks++;
2178                }
2179                if (config.selfAdded) {
2180                    mWifiLogProto.numNetworksAddedByUser++;
2181                } else {
2182                    mWifiLogProto.numNetworksAddedByApps++;
2183                }
2184                if (config.hiddenSSID) {
2185                    mWifiLogProto.numHiddenNetworks++;
2186                }
2187                if (config.isPasspoint()) {
2188                    mWifiLogProto.numPasspointNetworks++;
2189                }
2190            }
2191        }
2192    }
2193
2194    /**
2195     * Update metrics for saved Passpoint profiles.
2196     *
2197     * @param numSavedProfiles The number of saved Passpoint profiles
2198     * @param numConnectedProfiles The number of saved Passpoint profiles that have ever resulted
2199     *                             in a successful network connection
2200     */
2201    public void updateSavedPasspointProfiles(int numSavedProfiles, int numConnectedProfiles) {
2202        synchronized (mLock) {
2203            mWifiLogProto.numPasspointProviders = numSavedProfiles;
2204            mWifiLogProto.numPasspointProvidersSuccessfullyConnected = numConnectedProfiles;
2205        }
2206    }
2207
2208    /**
2209     * append the separate ConnectionEvent, SystemStateEntry and ScanReturnCode collections to their
2210     * respective lists within mWifiLogProto
2211     *
2212     * @param incremental Only include ConnectionEvents created since last automatic bug report
2213     */
2214    private void consolidateProto(boolean incremental) {
2215        List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>();
2216        List<WifiMetricsProto.RssiPollCount> rssis = new ArrayList<>();
2217        List<WifiMetricsProto.RssiPollCount> rssiDeltas = new ArrayList<>();
2218        List<WifiMetricsProto.AlertReasonCount> alertReasons = new ArrayList<>();
2219        List<WifiMetricsProto.WifiScoreCount> scores = new ArrayList<>();
2220        synchronized (mLock) {
2221            for (ConnectionEvent event : mConnectionEventList) {
2222                // If this is not incremental, dump full ConnectionEvent list
2223                // Else Dump all un-dumped events except for the current one
2224                if (!incremental || ((mCurrentConnectionEvent != event)
2225                        && !event.mConnectionEvent.automaticBugReportTaken)) {
2226                    //Get all ConnectionEvents that haven not been dumped as a proto, also exclude
2227                    //the current active un-ended connection event
2228                    events.add(event.mConnectionEvent);
2229                    if (incremental) {
2230                        event.mConnectionEvent.automaticBugReportTaken = true;
2231                    }
2232                }
2233            }
2234            if (events.size() > 0) {
2235                mWifiLogProto.connectionEvent = events.toArray(mWifiLogProto.connectionEvent);
2236            }
2237
2238            //Convert the SparseIntArray of scanReturnEntry integers into ScanReturnEntry proto list
2239            mWifiLogProto.scanReturnEntries =
2240                    new WifiMetricsProto.WifiLog.ScanReturnEntry[mScanReturnEntries.size()];
2241            for (int i = 0; i < mScanReturnEntries.size(); i++) {
2242                mWifiLogProto.scanReturnEntries[i] = new WifiMetricsProto.WifiLog.ScanReturnEntry();
2243                mWifiLogProto.scanReturnEntries[i].scanReturnCode = mScanReturnEntries.keyAt(i);
2244                mWifiLogProto.scanReturnEntries[i].scanResultsCount = mScanReturnEntries.valueAt(i);
2245            }
2246
2247            // Convert the SparseIntArray of systemStateEntry into WifiSystemStateEntry proto list
2248            // This one is slightly more complex, as the Sparse are indexed with:
2249            //     key: wifiState * 2 + isScreenOn, value: wifiStateCount
2250            mWifiLogProto.wifiSystemStateEntries =
2251                    new WifiMetricsProto.WifiLog
2252                    .WifiSystemStateEntry[mWifiSystemStateEntries.size()];
2253            for (int i = 0; i < mWifiSystemStateEntries.size(); i++) {
2254                mWifiLogProto.wifiSystemStateEntries[i] =
2255                        new WifiMetricsProto.WifiLog.WifiSystemStateEntry();
2256                mWifiLogProto.wifiSystemStateEntries[i].wifiState =
2257                        mWifiSystemStateEntries.keyAt(i) / 2;
2258                mWifiLogProto.wifiSystemStateEntries[i].wifiStateCount =
2259                        mWifiSystemStateEntries.valueAt(i);
2260                mWifiLogProto.wifiSystemStateEntries[i].isScreenOn =
2261                        (mWifiSystemStateEntries.keyAt(i) % 2) > 0;
2262            }
2263            mWifiLogProto.recordDurationSec = (int) ((mClock.getElapsedSinceBootMillis() / 1000)
2264                    - mRecordStartTimeSec);
2265
2266            /**
2267             * Convert the SparseIntArrays of RSSI poll rssi, counts, and frequency to the
2268             * proto's repeated ntKeyVal array.
2269             */
2270            for (Map.Entry<Integer, SparseIntArray> entry : mRssiPollCountsMap.entrySet()) {
2271                int frequency = entry.getKey();
2272                SparseIntArray histogram = entry.getValue();
2273                for (int i = 0; i < histogram.size(); i++) {
2274                    WifiMetricsProto.RssiPollCount keyVal = new WifiMetricsProto.RssiPollCount();
2275                    keyVal.rssi = histogram.keyAt(i);
2276                    keyVal.count = histogram.valueAt(i);
2277                    keyVal.frequency = frequency;
2278                    rssis.add(keyVal);
2279                }
2280            }
2281            mWifiLogProto.rssiPollRssiCount = rssis.toArray(mWifiLogProto.rssiPollRssiCount);
2282
2283            /**
2284             * Convert the SparseIntArray of RSSI delta rssi's and counts to the proto's repeated
2285             * IntKeyVal array.
2286             */
2287            for (int i = 0; i < mRssiDeltaCounts.size(); i++) {
2288                WifiMetricsProto.RssiPollCount keyVal = new WifiMetricsProto.RssiPollCount();
2289                keyVal.rssi = mRssiDeltaCounts.keyAt(i);
2290                keyVal.count = mRssiDeltaCounts.valueAt(i);
2291                rssiDeltas.add(keyVal);
2292            }
2293            mWifiLogProto.rssiPollDeltaCount = rssiDeltas.toArray(mWifiLogProto.rssiPollDeltaCount);
2294
2295
2296
2297            /**
2298             * Convert the SparseIntArray of alert reasons and counts to the proto's repeated
2299             * IntKeyVal array.
2300             */
2301            for (int i = 0; i < mWifiAlertReasonCounts.size(); i++) {
2302                WifiMetricsProto.AlertReasonCount keyVal = new WifiMetricsProto.AlertReasonCount();
2303                keyVal.reason = mWifiAlertReasonCounts.keyAt(i);
2304                keyVal.count = mWifiAlertReasonCounts.valueAt(i);
2305                alertReasons.add(keyVal);
2306            }
2307            mWifiLogProto.alertReasonCount = alertReasons.toArray(mWifiLogProto.alertReasonCount);
2308
2309            /**
2310            *  Convert the SparseIntArray of Wifi Score and counts to proto's repeated
2311            * IntKeyVal array.
2312            */
2313            for (int score = 0; score < mWifiScoreCounts.size(); score++) {
2314                WifiMetricsProto.WifiScoreCount keyVal = new WifiMetricsProto.WifiScoreCount();
2315                keyVal.score = mWifiScoreCounts.keyAt(score);
2316                keyVal.count = mWifiScoreCounts.valueAt(score);
2317                scores.add(keyVal);
2318            }
2319            mWifiLogProto.wifiScoreCount = scores.toArray(mWifiLogProto.wifiScoreCount);
2320
2321            /**
2322             * Convert the SparseIntArray of SoftAp Return codes and counts to proto's repeated
2323             * IntKeyVal array.
2324             */
2325            int codeCounts = mSoftApManagerReturnCodeCounts.size();
2326            mWifiLogProto.softApReturnCode = new WifiMetricsProto.SoftApReturnCodeCount[codeCounts];
2327            for (int sapCode = 0; sapCode < codeCounts; sapCode++) {
2328                mWifiLogProto.softApReturnCode[sapCode] =
2329                        new WifiMetricsProto.SoftApReturnCodeCount();
2330                mWifiLogProto.softApReturnCode[sapCode].startResult =
2331                        mSoftApManagerReturnCodeCounts.keyAt(sapCode);
2332                mWifiLogProto.softApReturnCode[sapCode].count =
2333                        mSoftApManagerReturnCodeCounts.valueAt(sapCode);
2334            }
2335
2336            /**
2337             * Convert StaEventList to array of StaEvents
2338             */
2339            mWifiLogProto.staEventList = new StaEvent[mStaEventList.size()];
2340            for (int i = 0; i < mStaEventList.size(); i++) {
2341                mWifiLogProto.staEventList[i] = mStaEventList.get(i).staEvent;
2342            }
2343            mWifiLogProto.totalSsidsInScanHistogram =
2344                    makeNumConnectableNetworksBucketArray(mTotalSsidsInScanHistogram);
2345            mWifiLogProto.totalBssidsInScanHistogram =
2346                    makeNumConnectableNetworksBucketArray(mTotalBssidsInScanHistogram);
2347            mWifiLogProto.availableOpenSsidsInScanHistogram =
2348                    makeNumConnectableNetworksBucketArray(mAvailableOpenSsidsInScanHistogram);
2349            mWifiLogProto.availableOpenBssidsInScanHistogram =
2350                    makeNumConnectableNetworksBucketArray(mAvailableOpenBssidsInScanHistogram);
2351            mWifiLogProto.availableSavedSsidsInScanHistogram =
2352                    makeNumConnectableNetworksBucketArray(mAvailableSavedSsidsInScanHistogram);
2353            mWifiLogProto.availableSavedBssidsInScanHistogram =
2354                    makeNumConnectableNetworksBucketArray(mAvailableSavedBssidsInScanHistogram);
2355            mWifiLogProto.availableOpenOrSavedSsidsInScanHistogram =
2356                    makeNumConnectableNetworksBucketArray(
2357                    mAvailableOpenOrSavedSsidsInScanHistogram);
2358            mWifiLogProto.availableOpenOrSavedBssidsInScanHistogram =
2359                    makeNumConnectableNetworksBucketArray(
2360                    mAvailableOpenOrSavedBssidsInScanHistogram);
2361            mWifiLogProto.availableSavedPasspointProviderProfilesInScanHistogram =
2362                    makeNumConnectableNetworksBucketArray(
2363                    mAvailableSavedPasspointProviderProfilesInScanHistogram);
2364            mWifiLogProto.availableSavedPasspointProviderBssidsInScanHistogram =
2365                    makeNumConnectableNetworksBucketArray(
2366                    mAvailableSavedPasspointProviderBssidsInScanHistogram);
2367            mWifiLogProto.wifiAwareLog = mWifiAwareMetrics.consolidateProto();
2368            mWifiLogProto.wifiRttLog = mRttMetrics.consolidateProto();
2369
2370            mWifiLogProto.pnoScanMetrics = mPnoScanMetrics;
2371
2372            /**
2373             * Convert the SparseIntArray of "Connect to Network" notification types and counts to
2374             * proto's repeated IntKeyVal array.
2375             */
2376            ConnectToNetworkNotificationAndActionCount[] notificationCountArray =
2377                    new ConnectToNetworkNotificationAndActionCount[
2378                            mConnectToNetworkNotificationCount.size()];
2379            for (int i = 0; i < mConnectToNetworkNotificationCount.size(); i++) {
2380                ConnectToNetworkNotificationAndActionCount keyVal =
2381                        new ConnectToNetworkNotificationAndActionCount();
2382                keyVal.notification = mConnectToNetworkNotificationCount.keyAt(i);
2383                keyVal.recommender =
2384                        ConnectToNetworkNotificationAndActionCount.RECOMMENDER_OPEN;
2385                keyVal.count = mConnectToNetworkNotificationCount.valueAt(i);
2386                notificationCountArray[i] = keyVal;
2387            }
2388            mWifiLogProto.connectToNetworkNotificationCount = notificationCountArray;
2389
2390            /**
2391             * Convert the SparseIntArray of "Connect to Network" notification types and counts to
2392             * proto's repeated IntKeyVal array.
2393             */
2394            ConnectToNetworkNotificationAndActionCount[] notificationActionCountArray =
2395                    new ConnectToNetworkNotificationAndActionCount[
2396                            mConnectToNetworkNotificationActionCount.size()];
2397            for (int i = 0; i < mConnectToNetworkNotificationActionCount.size(); i++) {
2398                ConnectToNetworkNotificationAndActionCount keyVal =
2399                        new ConnectToNetworkNotificationAndActionCount();
2400                int key = mConnectToNetworkNotificationActionCount.keyAt(i);
2401                keyVal.notification = key / CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER;
2402                keyVal.action = key % CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER;
2403                keyVal.recommender =
2404                        ConnectToNetworkNotificationAndActionCount.RECOMMENDER_OPEN;
2405                keyVal.count = mConnectToNetworkNotificationActionCount.valueAt(i);
2406                notificationActionCountArray[i] = keyVal;
2407            }
2408            mWifiLogProto.connectToNetworkNotificationActionCount = notificationActionCountArray;
2409
2410            mWifiLogProto.openNetworkRecommenderBlacklistSize =
2411                    mOpenNetworkRecommenderBlacklistSize;
2412            mWifiLogProto.isWifiNetworksAvailableNotificationOn =
2413                    mIsWifiNetworksAvailableNotificationOn;
2414            mWifiLogProto.numOpenNetworkRecommendationUpdates =
2415                    mNumOpenNetworkRecommendationUpdates;
2416            mWifiLogProto.numOpenNetworkConnectMessageFailedToSend =
2417                    mNumOpenNetworkConnectMessageFailedToSend;
2418
2419            mWifiLogProto.observedHotspotR1ApsInScanHistogram =
2420                    makeNumConnectableNetworksBucketArray(mObservedHotspotR1ApInScanHistogram);
2421            mWifiLogProto.observedHotspotR2ApsInScanHistogram =
2422                    makeNumConnectableNetworksBucketArray(mObservedHotspotR2ApInScanHistogram);
2423            mWifiLogProto.observedHotspotR1EssInScanHistogram =
2424                    makeNumConnectableNetworksBucketArray(mObservedHotspotR1EssInScanHistogram);
2425            mWifiLogProto.observedHotspotR2EssInScanHistogram =
2426                    makeNumConnectableNetworksBucketArray(mObservedHotspotR2EssInScanHistogram);
2427            mWifiLogProto.observedHotspotR1ApsPerEssInScanHistogram =
2428                    makeNumConnectableNetworksBucketArray(
2429                            mObservedHotspotR1ApsPerEssInScanHistogram);
2430            mWifiLogProto.observedHotspotR2ApsPerEssInScanHistogram =
2431                    makeNumConnectableNetworksBucketArray(
2432                            mObservedHotspotR2ApsPerEssInScanHistogram);
2433
2434            mWifiLogProto.observed80211McSupportingApsInScanHistogram =
2435                    makeNumConnectableNetworksBucketArray(mObserved80211mcApInScanHistogram);
2436
2437            if (mSoftApEventListTethered.size() > 0) {
2438                mWifiLogProto.softApConnectedClientsEventsTethered =
2439                        mSoftApEventListTethered.toArray(
2440                        mWifiLogProto.softApConnectedClientsEventsTethered);
2441            }
2442            if (mSoftApEventListLocalOnly.size() > 0) {
2443                mWifiLogProto.softApConnectedClientsEventsLocalOnly =
2444                        mSoftApEventListLocalOnly.toArray(
2445                        mWifiLogProto.softApConnectedClientsEventsLocalOnly);
2446            }
2447
2448            mWifiLogProto.wpsMetrics = mWpsMetrics;
2449            mWifiLogProto.wifiPowerStats = mWifiPowerMetrics.buildProto();
2450            mWifiLogProto.wifiWakeStats = mWifiWakeMetrics.buildProto();
2451            mWifiLogProto.isMacRandomizationOn = mIsMacRandomizationOn;
2452        }
2453    }
2454
2455    /** Sets the scoring experiment id to current value */
2456    private void consolidateScoringParams() {
2457        synchronized (mLock) {
2458            if (mScoringParams != null) {
2459                int experimentIdentifier = mScoringParams.getExperimentIdentifier();
2460                if (experimentIdentifier == 0) {
2461                    mWifiLogProto.scoreExperimentId = "";
2462                } else {
2463                    mWifiLogProto.scoreExperimentId = "x" + experimentIdentifier;
2464                }
2465            }
2466        }
2467    }
2468
2469    private WifiMetricsProto.NumConnectableNetworksBucket[] makeNumConnectableNetworksBucketArray(
2470            SparseIntArray sia) {
2471        WifiMetricsProto.NumConnectableNetworksBucket[] array =
2472                new WifiMetricsProto.NumConnectableNetworksBucket[sia.size()];
2473        for (int i = 0; i < sia.size(); i++) {
2474            WifiMetricsProto.NumConnectableNetworksBucket keyVal =
2475                    new WifiMetricsProto.NumConnectableNetworksBucket();
2476            keyVal.numConnectableNetworks = sia.keyAt(i);
2477            keyVal.count = sia.valueAt(i);
2478            array[i] = keyVal;
2479        }
2480        return array;
2481    }
2482
2483    /**
2484     * Clear all WifiMetrics, except for currentConnectionEvent and Open Network Notification
2485     * feature enabled state, blacklist size.
2486     */
2487    private void clear() {
2488        synchronized (mLock) {
2489            mConnectionEventList.clear();
2490            if (mCurrentConnectionEvent != null) {
2491                mConnectionEventList.add(mCurrentConnectionEvent);
2492            }
2493            mScanReturnEntries.clear();
2494            mWifiSystemStateEntries.clear();
2495            mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
2496            mRssiPollCountsMap.clear();
2497            mRssiDeltaCounts.clear();
2498            mWifiAlertReasonCounts.clear();
2499            mWifiScoreCounts.clear();
2500            mWifiLogProto.clear();
2501            mScanResultRssiTimestampMillis = -1;
2502            mSoftApManagerReturnCodeCounts.clear();
2503            mStaEventList.clear();
2504            mWifiAwareMetrics.clear();
2505            mRttMetrics.clear();
2506            mTotalSsidsInScanHistogram.clear();
2507            mTotalBssidsInScanHistogram.clear();
2508            mAvailableOpenSsidsInScanHistogram.clear();
2509            mAvailableOpenBssidsInScanHistogram.clear();
2510            mAvailableSavedSsidsInScanHistogram.clear();
2511            mAvailableSavedBssidsInScanHistogram.clear();
2512            mAvailableOpenOrSavedSsidsInScanHistogram.clear();
2513            mAvailableOpenOrSavedBssidsInScanHistogram.clear();
2514            mAvailableSavedPasspointProviderProfilesInScanHistogram.clear();
2515            mAvailableSavedPasspointProviderBssidsInScanHistogram.clear();
2516            mPnoScanMetrics.clear();
2517            mConnectToNetworkNotificationCount.clear();
2518            mConnectToNetworkNotificationActionCount.clear();
2519            mNumOpenNetworkRecommendationUpdates = 0;
2520            mNumOpenNetworkConnectMessageFailedToSend = 0;
2521            mObservedHotspotR1ApInScanHistogram.clear();
2522            mObservedHotspotR2ApInScanHistogram.clear();
2523            mObservedHotspotR1EssInScanHistogram.clear();
2524            mObservedHotspotR2EssInScanHistogram.clear();
2525            mObservedHotspotR1ApsPerEssInScanHistogram.clear();
2526            mObservedHotspotR2ApsPerEssInScanHistogram.clear();
2527            mSoftApEventListTethered.clear();
2528            mSoftApEventListLocalOnly.clear();
2529            mWpsMetrics.clear();
2530            mWifiWakeMetrics.clear();
2531            mObserved80211mcApInScanHistogram.clear();
2532        }
2533    }
2534
2535    /**
2536     *  Set screen state (On/Off)
2537     */
2538    public void setScreenState(boolean screenOn) {
2539        synchronized (mLock) {
2540            mScreenOn = screenOn;
2541        }
2542    }
2543
2544    /**
2545     *  Set wifi state (WIFI_UNKNOWN, WIFI_DISABLED, WIFI_DISCONNECTED, WIFI_ASSOCIATED)
2546     */
2547    public void setWifiState(int wifiState) {
2548        synchronized (mLock) {
2549            mWifiState = wifiState;
2550            mWifiWins = (wifiState == WifiMetricsProto.WifiLog.WIFI_ASSOCIATED);
2551        }
2552    }
2553
2554    /**
2555     * Message handler for interesting WifiMonitor messages. Generates StaEvents
2556     */
2557    private void processMessage(Message msg) {
2558        StaEvent event = new StaEvent();
2559        boolean logEvent = true;
2560        switch (msg.what) {
2561            case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
2562                event.type = StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT;
2563                event.associationTimedOut = msg.arg1 > 0 ? true : false;
2564                event.status = msg.arg2;
2565                break;
2566            case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
2567                event.type = StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT;
2568                switch (msg.arg1) {
2569                    case WifiManager.ERROR_AUTH_FAILURE_NONE:
2570                        event.authFailureReason = StaEvent.AUTH_FAILURE_NONE;
2571                        break;
2572                    case WifiManager.ERROR_AUTH_FAILURE_TIMEOUT:
2573                        event.authFailureReason = StaEvent.AUTH_FAILURE_TIMEOUT;
2574                        break;
2575                    case WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD:
2576                        event.authFailureReason = StaEvent.AUTH_FAILURE_WRONG_PSWD;
2577                        break;
2578                    case WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE:
2579                        event.authFailureReason = StaEvent.AUTH_FAILURE_EAP_FAILURE;
2580                        break;
2581                    default:
2582                        break;
2583                }
2584                break;
2585            case WifiMonitor.NETWORK_CONNECTION_EVENT:
2586                event.type = StaEvent.TYPE_NETWORK_CONNECTION_EVENT;
2587                break;
2588            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2589                event.type = StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT;
2590                event.reason = msg.arg2;
2591                event.localGen = msg.arg1 == 0 ? false : true;
2592                break;
2593            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2594                logEvent = false;
2595                StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
2596                mSupplicantStateChangeBitmask |= supplicantStateToBit(stateChangeResult.state);
2597                break;
2598            case WifiStateMachine.CMD_ASSOCIATED_BSSID:
2599                event.type = StaEvent.TYPE_CMD_ASSOCIATED_BSSID;
2600                break;
2601            case WifiStateMachine.CMD_TARGET_BSSID:
2602                event.type = StaEvent.TYPE_CMD_TARGET_BSSID;
2603                break;
2604            default:
2605                return;
2606        }
2607        if (logEvent) {
2608            addStaEvent(event);
2609        }
2610    }
2611    /**
2612     * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
2613     * generated event types, which are logged through 'sendMessage'
2614     * @param type StaEvent.EventType describing the event
2615     */
2616    public void logStaEvent(int type) {
2617        logStaEvent(type, StaEvent.DISCONNECT_UNKNOWN, null);
2618    }
2619    /**
2620     * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
2621     * generated event types, which are logged through 'sendMessage'
2622     * @param type StaEvent.EventType describing the event
2623     * @param config WifiConfiguration for a framework initiated connection attempt
2624     */
2625    public void logStaEvent(int type, WifiConfiguration config) {
2626        logStaEvent(type, StaEvent.DISCONNECT_UNKNOWN, config);
2627    }
2628    /**
2629     * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
2630     * generated event types, which are logged through 'sendMessage'
2631     * @param type StaEvent.EventType describing the event
2632     * @param frameworkDisconnectReason StaEvent.FrameworkDisconnectReason explaining why framework
2633     *                                  initiated a FRAMEWORK_DISCONNECT
2634     */
2635    public void logStaEvent(int type, int frameworkDisconnectReason) {
2636        logStaEvent(type, frameworkDisconnectReason, null);
2637    }
2638    /**
2639     * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
2640     * generated event types, which are logged through 'sendMessage'
2641     * @param type StaEvent.EventType describing the event
2642     * @param frameworkDisconnectReason StaEvent.FrameworkDisconnectReason explaining why framework
2643     *                                  initiated a FRAMEWORK_DISCONNECT
2644     * @param config WifiConfiguration for a framework initiated connection attempt
2645     */
2646    public void logStaEvent(int type, int frameworkDisconnectReason, WifiConfiguration config) {
2647        switch (type) {
2648            case StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL:
2649            case StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST:
2650            case StaEvent.TYPE_CMD_IP_REACHABILITY_LOST:
2651            case StaEvent.TYPE_CMD_START_CONNECT:
2652            case StaEvent.TYPE_CMD_START_ROAM:
2653            case StaEvent.TYPE_CONNECT_NETWORK:
2654            case StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK:
2655            case StaEvent.TYPE_FRAMEWORK_DISCONNECT:
2656            case StaEvent.TYPE_SCORE_BREACH:
2657            case StaEvent.TYPE_MAC_CHANGE:
2658                break;
2659            default:
2660                Log.e(TAG, "Unknown StaEvent:" + type);
2661                return;
2662        }
2663        StaEvent event = new StaEvent();
2664        event.type = type;
2665        if (frameworkDisconnectReason != StaEvent.DISCONNECT_UNKNOWN) {
2666            event.frameworkDisconnectReason = frameworkDisconnectReason;
2667        }
2668        event.configInfo = createConfigInfo(config);
2669        addStaEvent(event);
2670    }
2671
2672    private void addStaEvent(StaEvent staEvent) {
2673        staEvent.startTimeMillis = mClock.getElapsedSinceBootMillis();
2674        staEvent.lastRssi = mLastPollRssi;
2675        staEvent.lastFreq = mLastPollFreq;
2676        staEvent.lastLinkSpeed = mLastPollLinkSpeed;
2677        staEvent.supplicantStateChangesBitmask = mSupplicantStateChangeBitmask;
2678        staEvent.lastScore = mLastScore;
2679        mSupplicantStateChangeBitmask = 0;
2680        mLastPollRssi = -127;
2681        mLastPollFreq = -1;
2682        mLastPollLinkSpeed = -1;
2683        mLastScore = -1;
2684        mStaEventList.add(new StaEventWithTime(staEvent, mClock.getWallClockMillis()));
2685        // Prune StaEventList if it gets too long
2686        if (mStaEventList.size() > MAX_STA_EVENTS) mStaEventList.remove();
2687    }
2688
2689    private ConfigInfo createConfigInfo(WifiConfiguration config) {
2690        if (config == null) return null;
2691        ConfigInfo info = new ConfigInfo();
2692        info.allowedKeyManagement = bitSetToInt(config.allowedKeyManagement);
2693        info.allowedProtocols = bitSetToInt(config.allowedProtocols);
2694        info.allowedAuthAlgorithms = bitSetToInt(config.allowedAuthAlgorithms);
2695        info.allowedPairwiseCiphers = bitSetToInt(config.allowedPairwiseCiphers);
2696        info.allowedGroupCiphers = bitSetToInt(config.allowedGroupCiphers);
2697        info.hiddenSsid = config.hiddenSSID;
2698        info.isPasspoint = config.isPasspoint();
2699        info.isEphemeral = config.isEphemeral();
2700        info.hasEverConnected = config.getNetworkSelectionStatus().getHasEverConnected();
2701        ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
2702        if (candidate != null) {
2703            info.scanRssi = candidate.level;
2704            info.scanFreq = candidate.frequency;
2705        }
2706        return info;
2707    }
2708
2709    public Handler getHandler() {
2710        return mHandler;
2711    }
2712
2713    public WifiAwareMetrics getWifiAwareMetrics() {
2714        return mWifiAwareMetrics;
2715    }
2716
2717    public WifiWakeMetrics getWakeupMetrics() {
2718        return mWifiWakeMetrics;
2719    }
2720
2721    public RttMetrics getRttMetrics() {
2722        return mRttMetrics;
2723    }
2724
2725    // Rather than generate a StaEvent for each SUPPLICANT_STATE_CHANGE, cache these in a bitmask
2726    // and attach it to the next event which is generated.
2727    private int mSupplicantStateChangeBitmask = 0;
2728
2729    /**
2730     * Converts a SupplicantState value to a single bit, with position defined by
2731     * {@code StaEvent.SupplicantState}
2732     */
2733    public static int supplicantStateToBit(SupplicantState state) {
2734        switch(state) {
2735            case DISCONNECTED:
2736                return 1 << StaEvent.STATE_DISCONNECTED;
2737            case INTERFACE_DISABLED:
2738                return 1 << StaEvent.STATE_INTERFACE_DISABLED;
2739            case INACTIVE:
2740                return 1 << StaEvent.STATE_INACTIVE;
2741            case SCANNING:
2742                return 1 << StaEvent.STATE_SCANNING;
2743            case AUTHENTICATING:
2744                return 1 << StaEvent.STATE_AUTHENTICATING;
2745            case ASSOCIATING:
2746                return 1 << StaEvent.STATE_ASSOCIATING;
2747            case ASSOCIATED:
2748                return 1 << StaEvent.STATE_ASSOCIATED;
2749            case FOUR_WAY_HANDSHAKE:
2750                return 1 << StaEvent.STATE_FOUR_WAY_HANDSHAKE;
2751            case GROUP_HANDSHAKE:
2752                return 1 << StaEvent.STATE_GROUP_HANDSHAKE;
2753            case COMPLETED:
2754                return 1 << StaEvent.STATE_COMPLETED;
2755            case DORMANT:
2756                return 1 << StaEvent.STATE_DORMANT;
2757            case UNINITIALIZED:
2758                return 1 << StaEvent.STATE_UNINITIALIZED;
2759            case INVALID:
2760                return 1 << StaEvent.STATE_INVALID;
2761            default:
2762                Log.wtf(TAG, "Got unknown supplicant state: " + state.ordinal());
2763                return 0;
2764        }
2765    }
2766
2767    private static String supplicantStateChangesBitmaskToString(int mask) {
2768        StringBuilder sb = new StringBuilder();
2769        sb.append("supplicantStateChangeEvents: {");
2770        if ((mask & (1 << StaEvent.STATE_DISCONNECTED)) > 0) sb.append(" DISCONNECTED");
2771        if ((mask & (1 << StaEvent.STATE_INTERFACE_DISABLED)) > 0) sb.append(" INTERFACE_DISABLED");
2772        if ((mask & (1 << StaEvent.STATE_INACTIVE)) > 0) sb.append(" INACTIVE");
2773        if ((mask & (1 << StaEvent.STATE_SCANNING)) > 0) sb.append(" SCANNING");
2774        if ((mask & (1 << StaEvent.STATE_AUTHENTICATING)) > 0) sb.append(" AUTHENTICATING");
2775        if ((mask & (1 << StaEvent.STATE_ASSOCIATING)) > 0) sb.append(" ASSOCIATING");
2776        if ((mask & (1 << StaEvent.STATE_ASSOCIATED)) > 0) sb.append(" ASSOCIATED");
2777        if ((mask & (1 << StaEvent.STATE_FOUR_WAY_HANDSHAKE)) > 0) sb.append(" FOUR_WAY_HANDSHAKE");
2778        if ((mask & (1 << StaEvent.STATE_GROUP_HANDSHAKE)) > 0) sb.append(" GROUP_HANDSHAKE");
2779        if ((mask & (1 << StaEvent.STATE_COMPLETED)) > 0) sb.append(" COMPLETED");
2780        if ((mask & (1 << StaEvent.STATE_DORMANT)) > 0) sb.append(" DORMANT");
2781        if ((mask & (1 << StaEvent.STATE_UNINITIALIZED)) > 0) sb.append(" UNINITIALIZED");
2782        if ((mask & (1 << StaEvent.STATE_INVALID)) > 0) sb.append(" INVALID");
2783        sb.append("}");
2784        return sb.toString();
2785    }
2786
2787    /**
2788     * Returns a human readable string from a Sta Event. Only adds information relevant to the event
2789     * type.
2790     */
2791    public static String staEventToString(StaEvent event) {
2792        if (event == null) return "<NULL>";
2793        StringBuilder sb = new StringBuilder();
2794        switch (event.type) {
2795            case StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT:
2796                sb.append("ASSOCIATION_REJECTION_EVENT")
2797                        .append(" timedOut=").append(event.associationTimedOut)
2798                        .append(" status=").append(event.status).append(":")
2799                        .append(ISupplicantStaIfaceCallback.StatusCode.toString(event.status));
2800                break;
2801            case StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT:
2802                sb.append("AUTHENTICATION_FAILURE_EVENT reason=").append(event.authFailureReason)
2803                        .append(":").append(authFailureReasonToString(event.authFailureReason));
2804                break;
2805            case StaEvent.TYPE_NETWORK_CONNECTION_EVENT:
2806                sb.append("NETWORK_CONNECTION_EVENT");
2807                break;
2808            case StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT:
2809                sb.append("NETWORK_DISCONNECTION_EVENT")
2810                        .append(" local_gen=").append(event.localGen)
2811                        .append(" reason=").append(event.reason).append(":")
2812                        .append(ISupplicantStaIfaceCallback.ReasonCode.toString(
2813                                (event.reason >= 0 ? event.reason : -1 * event.reason)));
2814                break;
2815            case StaEvent.TYPE_CMD_ASSOCIATED_BSSID:
2816                sb.append("CMD_ASSOCIATED_BSSID");
2817                break;
2818            case StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL:
2819                sb.append("CMD_IP_CONFIGURATION_SUCCESSFUL");
2820                break;
2821            case StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST:
2822                sb.append("CMD_IP_CONFIGURATION_LOST");
2823                break;
2824            case StaEvent.TYPE_CMD_IP_REACHABILITY_LOST:
2825                sb.append("CMD_IP_REACHABILITY_LOST");
2826                break;
2827            case StaEvent.TYPE_CMD_TARGET_BSSID:
2828                sb.append("CMD_TARGET_BSSID");
2829                break;
2830            case StaEvent.TYPE_CMD_START_CONNECT:
2831                sb.append("CMD_START_CONNECT");
2832                break;
2833            case StaEvent.TYPE_CMD_START_ROAM:
2834                sb.append("CMD_START_ROAM");
2835                break;
2836            case StaEvent.TYPE_CONNECT_NETWORK:
2837                sb.append("CONNECT_NETWORK");
2838                break;
2839            case StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK:
2840                sb.append("NETWORK_AGENT_VALID_NETWORK");
2841                break;
2842            case StaEvent.TYPE_FRAMEWORK_DISCONNECT:
2843                sb.append("FRAMEWORK_DISCONNECT")
2844                        .append(" reason=")
2845                        .append(frameworkDisconnectReasonToString(event.frameworkDisconnectReason));
2846                break;
2847            case StaEvent.TYPE_SCORE_BREACH:
2848                sb.append("SCORE_BREACH");
2849                break;
2850            case StaEvent.TYPE_MAC_CHANGE:
2851                sb.append("MAC_CHANGE");
2852                break;
2853            default:
2854                sb.append("UNKNOWN " + event.type + ":");
2855                break;
2856        }
2857        if (event.lastRssi != -127) sb.append(" lastRssi=").append(event.lastRssi);
2858        if (event.lastFreq != -1) sb.append(" lastFreq=").append(event.lastFreq);
2859        if (event.lastLinkSpeed != -1) sb.append(" lastLinkSpeed=").append(event.lastLinkSpeed);
2860        if (event.lastScore != -1) sb.append(" lastScore=").append(event.lastScore);
2861        if (event.supplicantStateChangesBitmask != 0) {
2862            sb.append(", ").append(supplicantStateChangesBitmaskToString(
2863                    event.supplicantStateChangesBitmask));
2864        }
2865        if (event.configInfo != null) {
2866            sb.append(", ").append(configInfoToString(event.configInfo));
2867        }
2868
2869        return sb.toString();
2870    }
2871
2872    private static String authFailureReasonToString(int authFailureReason) {
2873        switch (authFailureReason) {
2874            case StaEvent.AUTH_FAILURE_NONE:
2875                return "ERROR_AUTH_FAILURE_NONE";
2876            case StaEvent.AUTH_FAILURE_TIMEOUT:
2877                return "ERROR_AUTH_FAILURE_TIMEOUT";
2878            case StaEvent.AUTH_FAILURE_WRONG_PSWD:
2879                return "ERROR_AUTH_FAILURE_WRONG_PSWD";
2880            case StaEvent.AUTH_FAILURE_EAP_FAILURE:
2881                return "ERROR_AUTH_FAILURE_EAP_FAILURE";
2882            default:
2883                return "";
2884        }
2885    }
2886
2887    private static String frameworkDisconnectReasonToString(int frameworkDisconnectReason) {
2888        switch (frameworkDisconnectReason) {
2889            case StaEvent.DISCONNECT_API:
2890                return "DISCONNECT_API";
2891            case StaEvent.DISCONNECT_GENERIC:
2892                return "DISCONNECT_GENERIC";
2893            case StaEvent.DISCONNECT_UNWANTED:
2894                return "DISCONNECT_UNWANTED";
2895            case StaEvent.DISCONNECT_ROAM_WATCHDOG_TIMER:
2896                return "DISCONNECT_ROAM_WATCHDOG_TIMER";
2897            case StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST:
2898                return "DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST";
2899            case StaEvent.DISCONNECT_RESET_SIM_NETWORKS:
2900                return "DISCONNECT_RESET_SIM_NETWORKS";
2901            default:
2902                return "DISCONNECT_UNKNOWN=" + frameworkDisconnectReason;
2903        }
2904    }
2905
2906    private static String configInfoToString(ConfigInfo info) {
2907        StringBuilder sb = new StringBuilder();
2908        sb.append("ConfigInfo:")
2909                .append(" allowed_key_management=").append(info.allowedKeyManagement)
2910                .append(" allowed_protocols=").append(info.allowedProtocols)
2911                .append(" allowed_auth_algorithms=").append(info.allowedAuthAlgorithms)
2912                .append(" allowed_pairwise_ciphers=").append(info.allowedPairwiseCiphers)
2913                .append(" allowed_group_ciphers=").append(info.allowedGroupCiphers)
2914                .append(" hidden_ssid=").append(info.hiddenSsid)
2915                .append(" is_passpoint=").append(info.isPasspoint)
2916                .append(" is_ephemeral=").append(info.isEphemeral)
2917                .append(" has_ever_connected=").append(info.hasEverConnected)
2918                .append(" scan_rssi=").append(info.scanRssi)
2919                .append(" scan_freq=").append(info.scanFreq);
2920        return sb.toString();
2921    }
2922
2923    public static final int MAX_STA_EVENTS = 768;
2924    private LinkedList<StaEventWithTime> mStaEventList = new LinkedList<StaEventWithTime>();
2925    private int mLastPollRssi = -127;
2926    private int mLastPollLinkSpeed = -1;
2927    private int mLastPollFreq = -1;
2928    private int mLastScore = -1;
2929
2930    /**
2931     * Converts the first 31 bits of a BitSet to a little endian int
2932     */
2933    private static int bitSetToInt(BitSet bits) {
2934        int value = 0;
2935        int nBits = bits.length() < 31 ? bits.length() : 31;
2936        for (int i = 0; i < nBits; i++) {
2937            value += bits.get(i) ? (1 << i) : 0;
2938        }
2939        return value;
2940    }
2941    private void incrementSsid(SparseIntArray sia, int element) {
2942        increment(sia, Math.min(element, MAX_CONNECTABLE_SSID_NETWORK_BUCKET));
2943    }
2944    private void incrementBssid(SparseIntArray sia, int element) {
2945        increment(sia, Math.min(element, MAX_CONNECTABLE_BSSID_NETWORK_BUCKET));
2946    }
2947    private void incrementTotalScanResults(SparseIntArray sia, int element) {
2948        increment(sia, Math.min(element, MAX_TOTAL_SCAN_RESULTS_BUCKET));
2949    }
2950    private void incrementTotalScanSsids(SparseIntArray sia, int element) {
2951        increment(sia, Math.min(element, MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET));
2952    }
2953    private void incrementTotalPasspointAps(SparseIntArray sia, int element) {
2954        increment(sia, Math.min(element, MAX_TOTAL_PASSPOINT_APS_BUCKET));
2955    }
2956    private void incrementTotalUniquePasspointEss(SparseIntArray sia, int element) {
2957        increment(sia, Math.min(element, MAX_TOTAL_PASSPOINT_UNIQUE_ESS_BUCKET));
2958    }
2959    private void incrementPasspointPerUniqueEss(SparseIntArray sia, int element) {
2960        increment(sia, Math.min(element, MAX_PASSPOINT_APS_PER_UNIQUE_ESS_BUCKET));
2961    }
2962    private void increment80211mcAps(SparseIntArray sia, int element) {
2963        increment(sia, Math.min(element, MAX_TOTAL_80211MC_APS_BUCKET));
2964    }
2965    private void increment(SparseIntArray sia, int element) {
2966        int count = sia.get(element);
2967        sia.put(element, count + 1);
2968    }
2969
2970    private static class StaEventWithTime {
2971        public StaEvent staEvent;
2972        public long wallClockMillis;
2973
2974        StaEventWithTime(StaEvent event, long wallClockMillis) {
2975            staEvent = event;
2976            this.wallClockMillis = wallClockMillis;
2977        }
2978
2979        public String toString() {
2980            StringBuilder sb = new StringBuilder();
2981            Calendar c = Calendar.getInstance();
2982            c.setTimeInMillis(wallClockMillis);
2983            if (wallClockMillis != 0) {
2984                sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
2985            } else {
2986                sb.append("                  ");
2987            }
2988            sb.append(" ").append(staEventToString(staEvent));
2989            return sb.toString();
2990        }
2991    }
2992}
2993