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