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