• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.wifi;
18 
19 import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
20 import android.net.NetworkAgent;
21 import android.net.wifi.ScanResult;
22 import android.net.wifi.SupplicantState;
23 import android.net.wifi.WifiConfiguration;
24 import android.net.wifi.WifiInfo;
25 import android.net.wifi.WifiManager;
26 import android.os.Handler;
27 import android.os.Looper;
28 import android.os.Message;
29 import android.util.Base64;
30 import android.util.Log;
31 import android.util.SparseIntArray;
32 
33 import com.android.server.wifi.hotspot2.NetworkDetail;
34 import com.android.server.wifi.nano.WifiMetricsProto;
35 import com.android.server.wifi.nano.WifiMetricsProto.StaEvent;
36 import com.android.server.wifi.nano.WifiMetricsProto.StaEvent.ConfigInfo;
37 import com.android.server.wifi.util.InformationElementUtil;
38 import com.android.server.wifi.util.ScanResultUtil;
39 
40 import java.io.FileDescriptor;
41 import java.io.PrintWriter;
42 import java.util.ArrayList;
43 import java.util.BitSet;
44 import java.util.Calendar;
45 import java.util.LinkedList;
46 import java.util.List;
47 
48 /**
49  * Provides storage for wireless connectivity metrics, as they are generated.
50  * Metrics logged by this class include:
51  *   Aggregated connection stats (num of connections, num of failures, ...)
52  *   Discrete connection event stats (time, duration, failure codes, ...)
53  *   Router details (technology type, authentication type, ...)
54  *   Scan stats
55  */
56 public class WifiMetrics {
57     private static final String TAG = "WifiMetrics";
58     private static final boolean DBG = false;
59     /**
60      * Clamp the RSSI poll counts to values between [MIN,MAX]_RSSI_POLL
61      */
62     private static final int MAX_RSSI_POLL = 0;
63     private static final int MIN_RSSI_POLL = -127;
64     public static final int MAX_RSSI_DELTA = 127;
65     public static final int MIN_RSSI_DELTA = -127;
66     /** Maximum time period between ScanResult and RSSI poll to generate rssi delta datapoint */
67     public static final long TIMEOUT_RSSI_DELTA_MILLIS =  3000;
68     private static final int MIN_WIFI_SCORE = 0;
69     private static final int MAX_WIFI_SCORE = NetworkAgent.WIFI_BASE_SCORE;
70     private final Object mLock = new Object();
71     private static final int MAX_CONNECTION_EVENTS = 256;
72     private Clock mClock;
73     private boolean mScreenOn;
74     private int mWifiState;
75     private Handler mHandler;
76     /**
77      * Metrics are stored within an instance of the WifiLog proto during runtime,
78      * The ConnectionEvent, SystemStateEntries & ScanReturnEntries metrics are stored during
79      * runtime in member lists of this WifiMetrics class, with the final WifiLog proto being pieced
80      * together at dump-time
81      */
82     private final WifiMetricsProto.WifiLog mWifiLogProto = new WifiMetricsProto.WifiLog();
83     /**
84      * Session information that gets logged for every Wifi connection attempt.
85      */
86     private final List<ConnectionEvent> mConnectionEventList = new ArrayList<>();
87     /**
88      * The latest started (but un-ended) connection attempt
89      */
90     private ConnectionEvent mCurrentConnectionEvent;
91     /**
92      * Count of number of times each scan return code, indexed by WifiLog.ScanReturnCode
93      */
94     private final SparseIntArray mScanReturnEntries = new SparseIntArray();
95     /**
96      * Mapping of system state to the counts of scans requested in that wifi state * screenOn
97      * combination. Indexed by WifiLog.WifiState * (1 + screenOn)
98      */
99     private final SparseIntArray mWifiSystemStateEntries = new SparseIntArray();
100     /** Mapping of RSSI values to counts. */
101     private final SparseIntArray mRssiPollCounts = new SparseIntArray();
102     /** Mapping of RSSI scan-poll delta values to counts. */
103     private final SparseIntArray mRssiDeltaCounts = new SparseIntArray();
104     /** RSSI of the scan result for the last connection event*/
105     private int mScanResultRssi = 0;
106     /** Boot-relative timestamp when the last candidate scanresult was received, used to calculate
107         RSSI deltas. -1 designates no candidate scanResult being tracked */
108     private long mScanResultRssiTimestampMillis = -1;
109     /** Mapping of alert reason to the respective alert count. */
110     private final SparseIntArray mWifiAlertReasonCounts = new SparseIntArray();
111     /**
112      * Records the getElapsedSinceBootMillis (in seconds) that represents the beginning of data
113      * capture for for this WifiMetricsProto
114      */
115     private long mRecordStartTimeSec;
116     /** Mapping of Wifi Scores to counts */
117     private final SparseIntArray mWifiScoreCounts = new SparseIntArray();
118     /** Mapping of SoftApManager start SoftAp return codes to counts */
119     private final SparseIntArray mSoftApManagerReturnCodeCounts = new SparseIntArray();
120     class RouterFingerPrint {
121         private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
RouterFingerPrint()122         RouterFingerPrint() {
123             mRouterFingerPrintProto = new WifiMetricsProto.RouterFingerPrint();
124         }
125 
toString()126         public String toString() {
127             StringBuilder sb = new StringBuilder();
128             synchronized (mLock) {
129                 sb.append("mConnectionEvent.roamType=" + mRouterFingerPrintProto.roamType);
130                 sb.append(", mChannelInfo=" + mRouterFingerPrintProto.channelInfo);
131                 sb.append(", mDtim=" + mRouterFingerPrintProto.dtim);
132                 sb.append(", mAuthentication=" + mRouterFingerPrintProto.authentication);
133                 sb.append(", mHidden=" + mRouterFingerPrintProto.hidden);
134                 sb.append(", mRouterTechnology=" + mRouterFingerPrintProto.routerTechnology);
135                 sb.append(", mSupportsIpv6=" + mRouterFingerPrintProto.supportsIpv6);
136             }
137             return sb.toString();
138         }
updateFromWifiConfiguration(WifiConfiguration config)139         public void updateFromWifiConfiguration(WifiConfiguration config) {
140             synchronized (mLock) {
141                 if (config != null) {
142                     // Is this a hidden network
143                     mRouterFingerPrintProto.hidden = config.hiddenSSID;
144                     // Config may not have a valid dtimInterval set yet, in which case dtim will be zero
145                     // (These are only populated from beacon frame scan results, which are returned as
146                     // scan results from the chip far less frequently than Probe-responses)
147                     if (config.dtimInterval > 0) {
148                         mRouterFingerPrintProto.dtim = config.dtimInterval;
149                     }
150                     mCurrentConnectionEvent.mConfigSsid = config.SSID;
151                     // Get AuthType information from config (We do this again from ScanResult after
152                     // associating with BSSID)
153                     if (config.allowedKeyManagement != null
154                             && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
155                         mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
156                                 .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
157                     } else if (config.isEnterprise()) {
158                         mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
159                                 .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
160                     } else {
161                         mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
162                                 .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
163                     }
164                     mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
165                             .passpoint = config.isPasspoint();
166                     // If there's a ScanResult candidate associated with this config already, get it and
167                     // log (more accurate) metrics from it
168                     ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
169                     if (candidate != null) {
170                         updateMetricsFromScanResult(candidate);
171                     }
172                 }
173             }
174         }
175     }
176 
177     /**
178      * Log event, tracking the start time, end time and result of a wireless connection attempt.
179      */
180     class ConnectionEvent {
181         WifiMetricsProto.ConnectionEvent mConnectionEvent;
182         //<TODO> Move these constants into a wifi.proto Enum, and create a new Failure Type field
183         //covering more than just l2 failures. see b/27652362
184         /**
185          * Failure codes, used for the 'level_2_failure_code' Connection event field (covers a lot
186          * more failures than just l2 though, since the proto does not have a place to log
187          * framework failures)
188          */
189         // Failure is unknown
190         public static final int FAILURE_UNKNOWN = 0;
191         // NONE
192         public static final int FAILURE_NONE = 1;
193         // ASSOCIATION_REJECTION_EVENT
194         public static final int FAILURE_ASSOCIATION_REJECTION = 2;
195         // AUTHENTICATION_FAILURE_EVENT
196         public static final int FAILURE_AUTHENTICATION_FAILURE = 3;
197         // SSID_TEMP_DISABLED (Also Auth failure)
198         public static final int FAILURE_SSID_TEMP_DISABLED = 4;
199         // reconnect() or reassociate() call to WifiNative failed
200         public static final int FAILURE_CONNECT_NETWORK_FAILED = 5;
201         // NETWORK_DISCONNECTION_EVENT
202         public static final int FAILURE_NETWORK_DISCONNECTION = 6;
203         // NEW_CONNECTION_ATTEMPT before previous finished
204         public static final int FAILURE_NEW_CONNECTION_ATTEMPT = 7;
205         // New connection attempt to the same network & bssid
206         public static final int FAILURE_REDUNDANT_CONNECTION_ATTEMPT = 8;
207         // Roam Watchdog timer triggered (Roaming timed out)
208         public static final int FAILURE_ROAM_TIMEOUT = 9;
209         // DHCP failure
210         public static final int FAILURE_DHCP = 10;
211 
212         RouterFingerPrint mRouterFingerPrint;
213         private long mRealStartTime;
214         private long mRealEndTime;
215         private String mConfigSsid;
216         private String mConfigBssid;
217         private int mWifiState;
218         private boolean mScreenOn;
219 
ConnectionEvent()220         private ConnectionEvent() {
221             mConnectionEvent = new WifiMetricsProto.ConnectionEvent();
222             mRealEndTime = 0;
223             mRealStartTime = 0;
224             mRouterFingerPrint = new RouterFingerPrint();
225             mConnectionEvent.routerFingerprint = mRouterFingerPrint.mRouterFingerPrintProto;
226             mConfigSsid = "<NULL>";
227             mConfigBssid = "<NULL>";
228             mWifiState = WifiMetricsProto.WifiLog.WIFI_UNKNOWN;
229             mScreenOn = false;
230         }
231 
toString()232         public String toString() {
233             StringBuilder sb = new StringBuilder();
234             sb.append("startTime=");
235             Calendar c = Calendar.getInstance();
236             synchronized (mLock) {
237                 c.setTimeInMillis(mConnectionEvent.startTimeMillis);
238                 sb.append(mConnectionEvent.startTimeMillis == 0 ? "            <null>" :
239                         String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
240                 sb.append(", SSID=");
241                 sb.append(mConfigSsid);
242                 sb.append(", BSSID=");
243                 sb.append(mConfigBssid);
244                 sb.append(", durationMillis=");
245                 sb.append(mConnectionEvent.durationTakenToConnectMillis);
246                 sb.append(", roamType=");
247                 switch(mConnectionEvent.roamType) {
248                     case 1:
249                         sb.append("ROAM_NONE");
250                         break;
251                     case 2:
252                         sb.append("ROAM_DBDC");
253                         break;
254                     case 3:
255                         sb.append("ROAM_ENTERPRISE");
256                         break;
257                     case 4:
258                         sb.append("ROAM_USER_SELECTED");
259                         break;
260                     case 5:
261                         sb.append("ROAM_UNRELATED");
262                         break;
263                     default:
264                         sb.append("ROAM_UNKNOWN");
265                 }
266                 sb.append(", connectionResult=");
267                 sb.append(mConnectionEvent.connectionResult);
268                 sb.append(", level2FailureCode=");
269                 switch(mConnectionEvent.level2FailureCode) {
270                     case FAILURE_NONE:
271                         sb.append("NONE");
272                         break;
273                     case FAILURE_ASSOCIATION_REJECTION:
274                         sb.append("ASSOCIATION_REJECTION");
275                         break;
276                     case FAILURE_AUTHENTICATION_FAILURE:
277                         sb.append("AUTHENTICATION_FAILURE");
278                         break;
279                     case FAILURE_SSID_TEMP_DISABLED:
280                         sb.append("SSID_TEMP_DISABLED");
281                         break;
282                     case FAILURE_CONNECT_NETWORK_FAILED:
283                         sb.append("CONNECT_NETWORK_FAILED");
284                         break;
285                     case FAILURE_NETWORK_DISCONNECTION:
286                         sb.append("NETWORK_DISCONNECTION");
287                         break;
288                     case FAILURE_NEW_CONNECTION_ATTEMPT:
289                         sb.append("NEW_CONNECTION_ATTEMPT");
290                         break;
291                     case FAILURE_REDUNDANT_CONNECTION_ATTEMPT:
292                         sb.append("REDUNDANT_CONNECTION_ATTEMPT");
293                         break;
294                     case FAILURE_ROAM_TIMEOUT:
295                         sb.append("ROAM_TIMEOUT");
296                         break;
297                     case FAILURE_DHCP:
298                         sb.append("DHCP");
299                     default:
300                         sb.append("UNKNOWN");
301                         break;
302                 }
303                 sb.append(", connectivityLevelFailureCode=");
304                 switch(mConnectionEvent.connectivityLevelFailureCode) {
305                     case WifiMetricsProto.ConnectionEvent.HLF_NONE:
306                         sb.append("NONE");
307                         break;
308                     case WifiMetricsProto.ConnectionEvent.HLF_DHCP:
309                         sb.append("DHCP");
310                         break;
311                     case WifiMetricsProto.ConnectionEvent.HLF_NO_INTERNET:
312                         sb.append("NO_INTERNET");
313                         break;
314                     case WifiMetricsProto.ConnectionEvent.HLF_UNWANTED:
315                         sb.append("UNWANTED");
316                         break;
317                     default:
318                         sb.append("UNKNOWN");
319                         break;
320                 }
321                 sb.append(", signalStrength=");
322                 sb.append(mConnectionEvent.signalStrength);
323                 sb.append(", wifiState=");
324                 switch(mWifiState) {
325                     case WifiMetricsProto.WifiLog.WIFI_DISABLED:
326                         sb.append("WIFI_DISABLED");
327                         break;
328                     case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
329                         sb.append("WIFI_DISCONNECTED");
330                         break;
331                     case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
332                         sb.append("WIFI_ASSOCIATED");
333                         break;
334                     default:
335                         sb.append("WIFI_UNKNOWN");
336                         break;
337                 }
338                 sb.append(", screenOn=");
339                 sb.append(mScreenOn);
340                 sb.append(". mRouterFingerprint: ");
341                 sb.append(mRouterFingerPrint.toString());
342             }
343             return sb.toString();
344         }
345     }
346 
WifiMetrics(Clock clock, Looper looper)347     public WifiMetrics(Clock clock, Looper looper) {
348         mClock = clock;
349         mCurrentConnectionEvent = null;
350         mScreenOn = true;
351         mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED;
352         mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
353 
354         mHandler = new Handler(looper) {
355             public void handleMessage(Message msg) {
356                 synchronized (mLock) {
357                     processMessage(msg);
358                 }
359             }
360         };
361     }
362 
363     // Values used for indexing SystemStateEntries
364     private static final int SCREEN_ON = 1;
365     private static final int SCREEN_OFF = 0;
366 
367     /**
368      * Create a new connection event. Call when wifi attempts to make a new network connection
369      * If there is a current 'un-ended' connection event, it will be ended with UNKNOWN connectivity
370      * failure code.
371      * Gathers and sets the RouterFingerPrint data as well
372      *
373      * @param config WifiConfiguration of the config used for the current connection attempt
374      * @param roamType Roam type that caused connection attempt, see WifiMetricsProto.WifiLog.ROAM_X
375      */
startConnectionEvent(WifiConfiguration config, String targetBSSID, int roamType)376     public void startConnectionEvent(WifiConfiguration config, String targetBSSID, int roamType) {
377         synchronized (mLock) {
378             // Check if this is overlapping another current connection event
379             if (mCurrentConnectionEvent != null) {
380                 //Is this new Connection Event the same as the current one
381                 if (mCurrentConnectionEvent.mConfigSsid != null
382                         && mCurrentConnectionEvent.mConfigBssid != null
383                         && config != null
384                         && mCurrentConnectionEvent.mConfigSsid.equals(config.SSID)
385                         && (mCurrentConnectionEvent.mConfigBssid.equals("any")
386                         || mCurrentConnectionEvent.mConfigBssid.equals(targetBSSID))) {
387                     mCurrentConnectionEvent.mConfigBssid = targetBSSID;
388                     // End Connection Event due to new connection attempt to the same network
389                     endConnectionEvent(ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT,
390                             WifiMetricsProto.ConnectionEvent.HLF_NONE);
391                 } else {
392                     // End Connection Event due to new connection attempt to different network
393                     endConnectionEvent(ConnectionEvent.FAILURE_NEW_CONNECTION_ATTEMPT,
394                             WifiMetricsProto.ConnectionEvent.HLF_NONE);
395                 }
396             }
397             //If past maximum connection events, start removing the oldest
398             while(mConnectionEventList.size() >= MAX_CONNECTION_EVENTS) {
399                 mConnectionEventList.remove(0);
400             }
401             mCurrentConnectionEvent = new ConnectionEvent();
402             mCurrentConnectionEvent.mConnectionEvent.startTimeMillis =
403                     mClock.getWallClockMillis();
404             mCurrentConnectionEvent.mConfigBssid = targetBSSID;
405             mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
406             mCurrentConnectionEvent.mRouterFingerPrint.updateFromWifiConfiguration(config);
407             mCurrentConnectionEvent.mConfigBssid = "any";
408             mCurrentConnectionEvent.mRealStartTime = mClock.getElapsedSinceBootMillis();
409             mCurrentConnectionEvent.mWifiState = mWifiState;
410             mCurrentConnectionEvent.mScreenOn = mScreenOn;
411             mConnectionEventList.add(mCurrentConnectionEvent);
412             mScanResultRssiTimestampMillis = -1;
413             if (config != null) {
414                 ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
415                 if (candidate != null) {
416                     // Cache the RSSI of the candidate, as the connection event level is updated
417                     // from other sources (polls, bssid_associations) and delta requires the
418                     // scanResult rssi
419                     mScanResultRssi = candidate.level;
420                     mScanResultRssiTimestampMillis = mClock.getElapsedSinceBootMillis();
421                 }
422             }
423         }
424     }
425 
426     /**
427      * set the RoamType of the current ConnectionEvent (if any)
428      */
setConnectionEventRoamType(int roamType)429     public void setConnectionEventRoamType(int roamType) {
430         synchronized (mLock) {
431             if (mCurrentConnectionEvent != null) {
432                 mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
433             }
434         }
435     }
436 
437     /**
438      * Set AP related metrics from ScanDetail
439      */
setConnectionScanDetail(ScanDetail scanDetail)440     public void setConnectionScanDetail(ScanDetail scanDetail) {
441         synchronized (mLock) {
442             if (mCurrentConnectionEvent != null && scanDetail != null) {
443                 NetworkDetail networkDetail = scanDetail.getNetworkDetail();
444                 ScanResult scanResult = scanDetail.getScanResult();
445                 //Ensure that we have a networkDetail, and that it corresponds to the currently
446                 //tracked connection attempt
447                 if (networkDetail != null && scanResult != null
448                         && mCurrentConnectionEvent.mConfigSsid != null
449                         && mCurrentConnectionEvent.mConfigSsid
450                         .equals("\"" + networkDetail.getSSID() + "\"")) {
451                     updateMetricsFromNetworkDetail(networkDetail);
452                     updateMetricsFromScanResult(scanResult);
453                 }
454             }
455         }
456     }
457 
458     /**
459      * End a Connection event record. Call when wifi connection attempt succeeds or fails.
460      * If a Connection event has not been started and is active when .end is called, a new one is
461      * created with zero duration.
462      *
463      * @param level2FailureCode Level 2 failure code returned by supplicant
464      * @param connectivityFailureCode WifiMetricsProto.ConnectionEvent.HLF_X
465      */
endConnectionEvent(int level2FailureCode, int connectivityFailureCode)466     public void endConnectionEvent(int level2FailureCode, int connectivityFailureCode) {
467         synchronized (mLock) {
468             if (mCurrentConnectionEvent != null) {
469                 boolean result = (level2FailureCode == 1)
470                         && (connectivityFailureCode == WifiMetricsProto.ConnectionEvent.HLF_NONE);
471                 mCurrentConnectionEvent.mConnectionEvent.connectionResult = result ? 1 : 0;
472                 mCurrentConnectionEvent.mRealEndTime = mClock.getElapsedSinceBootMillis();
473                 mCurrentConnectionEvent.mConnectionEvent.durationTakenToConnectMillis = (int)
474                         (mCurrentConnectionEvent.mRealEndTime
475                         - mCurrentConnectionEvent.mRealStartTime);
476                 mCurrentConnectionEvent.mConnectionEvent.level2FailureCode = level2FailureCode;
477                 mCurrentConnectionEvent.mConnectionEvent.connectivityLevelFailureCode =
478                         connectivityFailureCode;
479                 // ConnectionEvent already added to ConnectionEvents List. Safe to null current here
480                 mCurrentConnectionEvent = null;
481                 if (!result) {
482                     mScanResultRssiTimestampMillis = -1;
483                 }
484             }
485         }
486     }
487 
488     /**
489      * Set ConnectionEvent DTIM Interval (if set), and 802.11 Connection mode, from NetworkDetail
490      */
updateMetricsFromNetworkDetail(NetworkDetail networkDetail)491     private void updateMetricsFromNetworkDetail(NetworkDetail networkDetail) {
492         int dtimInterval = networkDetail.getDtimInterval();
493         if (dtimInterval > 0) {
494             mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.dtim =
495                     dtimInterval;
496         }
497         int connectionWifiMode;
498         switch (networkDetail.getWifiMode()) {
499             case InformationElementUtil.WifiMode.MODE_UNDEFINED:
500                 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_UNKNOWN;
501                 break;
502             case InformationElementUtil.WifiMode.MODE_11A:
503                 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_A;
504                 break;
505             case InformationElementUtil.WifiMode.MODE_11B:
506                 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_B;
507                 break;
508             case InformationElementUtil.WifiMode.MODE_11G:
509                 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_G;
510                 break;
511             case InformationElementUtil.WifiMode.MODE_11N:
512                 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_N;
513                 break;
514             case InformationElementUtil.WifiMode.MODE_11AC  :
515                 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_AC;
516                 break;
517             default:
518                 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_OTHER;
519                 break;
520         }
521         mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
522                 .routerTechnology = connectionWifiMode;
523     }
524 
525     /**
526      * Set ConnectionEvent RSSI and authentication type from ScanResult
527      */
updateMetricsFromScanResult(ScanResult scanResult)528     private void updateMetricsFromScanResult(ScanResult scanResult) {
529         mCurrentConnectionEvent.mConnectionEvent.signalStrength = scanResult.level;
530         mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
531                 WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
532         mCurrentConnectionEvent.mConfigBssid = scanResult.BSSID;
533         if (scanResult.capabilities != null) {
534             if (ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
535                 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
536                         WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
537             } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) {
538                 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
539                         WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
540             } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
541                 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
542                         WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
543             }
544         }
545         mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.channelInfo =
546                 scanResult.frequency;
547     }
548 
setIsLocationEnabled(boolean enabled)549     void setIsLocationEnabled(boolean enabled) {
550         synchronized (mLock) {
551             mWifiLogProto.isLocationEnabled = enabled;
552         }
553     }
554 
setIsScanningAlwaysEnabled(boolean enabled)555     void setIsScanningAlwaysEnabled(boolean enabled) {
556         synchronized (mLock) {
557             mWifiLogProto.isScanningAlwaysEnabled = enabled;
558         }
559     }
560 
561     /**
562      * Increment Non Empty Scan Results count
563      */
incrementNonEmptyScanResultCount()564     public void incrementNonEmptyScanResultCount() {
565         if (DBG) Log.v(TAG, "incrementNonEmptyScanResultCount");
566         synchronized (mLock) {
567             mWifiLogProto.numNonEmptyScanResults++;
568         }
569     }
570 
571     /**
572      * Increment Empty Scan Results count
573      */
incrementEmptyScanResultCount()574     public void incrementEmptyScanResultCount() {
575         if (DBG) Log.v(TAG, "incrementEmptyScanResultCount");
576         synchronized (mLock) {
577             mWifiLogProto.numEmptyScanResults++;
578         }
579     }
580 
581     /**
582      * Increment background scan count
583      */
incrementBackgroundScanCount()584     public void incrementBackgroundScanCount() {
585         if (DBG) Log.v(TAG, "incrementBackgroundScanCount");
586         synchronized (mLock) {
587             mWifiLogProto.numBackgroundScans++;
588         }
589     }
590 
591    /**
592      * Get Background scan count
593      */
getBackgroundScanCount()594     public int getBackgroundScanCount() {
595         synchronized (mLock) {
596             return mWifiLogProto.numBackgroundScans;
597         }
598     }
599 
600     /**
601      * Increment oneshot scan count, and the associated WifiSystemScanStateCount entry
602      */
incrementOneshotScanCount()603     public void incrementOneshotScanCount() {
604         synchronized (mLock) {
605             mWifiLogProto.numOneshotScans++;
606         }
607         incrementWifiSystemScanStateCount(mWifiState, mScreenOn);
608     }
609 
610     /**
611      * Get oneshot scan count
612      */
getOneshotScanCount()613     public int getOneshotScanCount() {
614         synchronized (mLock) {
615             return mWifiLogProto.numOneshotScans;
616         }
617     }
618 
returnCodeToString(int scanReturnCode)619     private String returnCodeToString(int scanReturnCode) {
620         switch(scanReturnCode){
621             case WifiMetricsProto.WifiLog.SCAN_UNKNOWN:
622                 return "SCAN_UNKNOWN";
623             case WifiMetricsProto.WifiLog.SCAN_SUCCESS:
624                 return "SCAN_SUCCESS";
625             case WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED:
626                 return "SCAN_FAILURE_INTERRUPTED";
627             case WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION:
628                 return "SCAN_FAILURE_INVALID_CONFIGURATION";
629             case WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED:
630                 return "FAILURE_WIFI_DISABLED";
631             default:
632                 return "<UNKNOWN>";
633         }
634     }
635 
636     /**
637      * Increment count of scan return code occurrence
638      *
639      * @param scanReturnCode Return code from scan attempt WifiMetricsProto.WifiLog.SCAN_X
640      */
incrementScanReturnEntry(int scanReturnCode, int countToAdd)641     public void incrementScanReturnEntry(int scanReturnCode, int countToAdd) {
642         synchronized (mLock) {
643             if (DBG) Log.v(TAG, "incrementScanReturnEntry " + returnCodeToString(scanReturnCode));
644             int entry = mScanReturnEntries.get(scanReturnCode);
645             entry += countToAdd;
646             mScanReturnEntries.put(scanReturnCode, entry);
647         }
648     }
649     /**
650      * Get the count of this scanReturnCode
651      * @param scanReturnCode that we are getting the count for
652      */
getScanReturnEntry(int scanReturnCode)653     public int getScanReturnEntry(int scanReturnCode) {
654         synchronized (mLock) {
655             return mScanReturnEntries.get(scanReturnCode);
656         }
657     }
658 
wifiSystemStateToString(int state)659     private String wifiSystemStateToString(int state) {
660         switch(state){
661             case WifiMetricsProto.WifiLog.WIFI_UNKNOWN:
662                 return "WIFI_UNKNOWN";
663             case WifiMetricsProto.WifiLog.WIFI_DISABLED:
664                 return "WIFI_DISABLED";
665             case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
666                 return "WIFI_DISCONNECTED";
667             case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
668                 return "WIFI_ASSOCIATED";
669             default:
670                 return "default";
671         }
672     }
673 
674     /**
675      * Increments the count of scans initiated by each wifi state, accounts for screenOn/Off
676      *
677      * @param state State of the system when scan was initiated, see WifiMetricsProto.WifiLog.WIFI_X
678      * @param screenOn Is the screen on
679      */
incrementWifiSystemScanStateCount(int state, boolean screenOn)680     public void incrementWifiSystemScanStateCount(int state, boolean screenOn) {
681         synchronized (mLock) {
682             if (DBG) {
683                 Log.v(TAG, "incrementWifiSystemScanStateCount " + wifiSystemStateToString(state)
684                         + " " + screenOn);
685             }
686             int index = (state * 2) + (screenOn ? SCREEN_ON : SCREEN_OFF);
687             int entry = mWifiSystemStateEntries.get(index);
688             entry++;
689             mWifiSystemStateEntries.put(index, entry);
690         }
691     }
692 
693     /**
694      * Get the count of this system State Entry
695      */
getSystemStateCount(int state, boolean screenOn)696     public int getSystemStateCount(int state, boolean screenOn) {
697         synchronized (mLock) {
698             int index = state * 2 + (screenOn ? SCREEN_ON : SCREEN_OFF);
699             return mWifiSystemStateEntries.get(index);
700         }
701     }
702 
703     /**
704      * Increment number of times the Watchdog of Last Resort triggered, resetting the wifi stack
705      */
incrementNumLastResortWatchdogTriggers()706     public void incrementNumLastResortWatchdogTriggers() {
707         synchronized (mLock) {
708             mWifiLogProto.numLastResortWatchdogTriggers++;
709         }
710     }
711     /**
712      * @param count number of networks over bad association threshold when watchdog triggered
713      */
addCountToNumLastResortWatchdogBadAssociationNetworksTotal(int count)714     public void addCountToNumLastResortWatchdogBadAssociationNetworksTotal(int count) {
715         synchronized (mLock) {
716             mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal += count;
717         }
718     }
719     /**
720      * @param count number of networks over bad authentication threshold when watchdog triggered
721      */
addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(int count)722     public void addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(int count) {
723         synchronized (mLock) {
724             mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal += count;
725         }
726     }
727     /**
728      * @param count number of networks over bad dhcp threshold when watchdog triggered
729      */
addCountToNumLastResortWatchdogBadDhcpNetworksTotal(int count)730     public void addCountToNumLastResortWatchdogBadDhcpNetworksTotal(int count) {
731         synchronized (mLock) {
732             mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal += count;
733         }
734     }
735     /**
736      * @param count number of networks over bad other threshold when watchdog triggered
737      */
addCountToNumLastResortWatchdogBadOtherNetworksTotal(int count)738     public void addCountToNumLastResortWatchdogBadOtherNetworksTotal(int count) {
739         synchronized (mLock) {
740             mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal += count;
741         }
742     }
743     /**
744      * @param count number of networks seen when watchdog triggered
745      */
addCountToNumLastResortWatchdogAvailableNetworksTotal(int count)746     public void addCountToNumLastResortWatchdogAvailableNetworksTotal(int count) {
747         synchronized (mLock) {
748             mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal += count;
749         }
750     }
751     /**
752      * Increment count of triggers with atleast one bad association network
753      */
incrementNumLastResortWatchdogTriggersWithBadAssociation()754     public void incrementNumLastResortWatchdogTriggersWithBadAssociation() {
755         synchronized (mLock) {
756             mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation++;
757         }
758     }
759     /**
760      * Increment count of triggers with atleast one bad authentication network
761      */
incrementNumLastResortWatchdogTriggersWithBadAuthentication()762     public void incrementNumLastResortWatchdogTriggersWithBadAuthentication() {
763         synchronized (mLock) {
764             mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication++;
765         }
766     }
767     /**
768      * Increment count of triggers with atleast one bad dhcp network
769      */
incrementNumLastResortWatchdogTriggersWithBadDhcp()770     public void incrementNumLastResortWatchdogTriggersWithBadDhcp() {
771         synchronized (mLock) {
772             mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp++;
773         }
774     }
775     /**
776      * Increment count of triggers with atleast one bad other network
777      */
incrementNumLastResortWatchdogTriggersWithBadOther()778     public void incrementNumLastResortWatchdogTriggersWithBadOther() {
779         synchronized (mLock) {
780             mWifiLogProto.numLastResortWatchdogTriggersWithBadOther++;
781         }
782     }
783 
784     /**
785      * Increment number of times connectivity watchdog confirmed pno is working
786      */
incrementNumConnectivityWatchdogPnoGood()787     public void incrementNumConnectivityWatchdogPnoGood() {
788         synchronized (mLock) {
789             mWifiLogProto.numConnectivityWatchdogPnoGood++;
790         }
791     }
792     /**
793      * Increment number of times connectivity watchdog found pno not working
794      */
incrementNumConnectivityWatchdogPnoBad()795     public void incrementNumConnectivityWatchdogPnoBad() {
796         synchronized (mLock) {
797             mWifiLogProto.numConnectivityWatchdogPnoBad++;
798         }
799     }
800     /**
801      * Increment number of times connectivity watchdog confirmed background scan is working
802      */
incrementNumConnectivityWatchdogBackgroundGood()803     public void incrementNumConnectivityWatchdogBackgroundGood() {
804         synchronized (mLock) {
805             mWifiLogProto.numConnectivityWatchdogBackgroundGood++;
806         }
807     }
808     /**
809      * Increment number of times connectivity watchdog found background scan not working
810      */
incrementNumConnectivityWatchdogBackgroundBad()811     public void incrementNumConnectivityWatchdogBackgroundBad() {
812         synchronized (mLock) {
813             mWifiLogProto.numConnectivityWatchdogBackgroundBad++;
814         }
815     }
816 
817     /**
818      * Increment various poll related metrics, and cache performance data for StaEvent logging
819      */
handlePollResult(WifiInfo wifiInfo)820     public void handlePollResult(WifiInfo wifiInfo) {
821         mLastPollRssi = wifiInfo.getRssi();
822         mLastPollLinkSpeed = wifiInfo.getLinkSpeed();
823         mLastPollFreq = wifiInfo.getFrequency();
824         incrementRssiPollRssiCount(mLastPollRssi);
825     }
826 
827     /**
828      * Increment occurence count of RSSI level from RSSI poll.
829      * Ignores rssi values outside the bounds of [MIN_RSSI_POLL, MAX_RSSI_POLL]
830      */
incrementRssiPollRssiCount(int rssi)831     public void incrementRssiPollRssiCount(int rssi) {
832         if (!(rssi >= MIN_RSSI_POLL && rssi <= MAX_RSSI_POLL)) {
833             return;
834         }
835         synchronized (mLock) {
836             int count = mRssiPollCounts.get(rssi);
837             mRssiPollCounts.put(rssi, count + 1);
838             maybeIncrementRssiDeltaCount(rssi - mScanResultRssi);
839         }
840     }
841 
842     /**
843      * Increment occurence count of difference between scan result RSSI and the first RSSI poll.
844      * Ignores rssi values outside the bounds of [MIN_RSSI_DELTA, MAX_RSSI_DELTA]
845      * mLock must be held when calling this method.
846      */
maybeIncrementRssiDeltaCount(int rssi)847     private void maybeIncrementRssiDeltaCount(int rssi) {
848         // Check if this RSSI poll is close enough to a scan result RSSI to log a delta value
849         if (mScanResultRssiTimestampMillis >= 0) {
850             long timeDelta = mClock.getElapsedSinceBootMillis() - mScanResultRssiTimestampMillis;
851             if (timeDelta <= TIMEOUT_RSSI_DELTA_MILLIS) {
852                 if (rssi >= MIN_RSSI_DELTA && rssi <= MAX_RSSI_DELTA) {
853                     int count = mRssiDeltaCounts.get(rssi);
854                     mRssiDeltaCounts.put(rssi, count + 1);
855                 }
856             }
857             mScanResultRssiTimestampMillis = -1;
858         }
859     }
860 
861     /**
862      * Increment count of Watchdog successes.
863      */
incrementNumLastResortWatchdogSuccesses()864     public void incrementNumLastResortWatchdogSuccesses() {
865         synchronized (mLock) {
866             mWifiLogProto.numLastResortWatchdogSuccesses++;
867         }
868     }
869 
870     /**
871      * Increments the count of alerts by alert reason.
872      *
873      * @param reason The cause of the alert. The reason values are driver-specific.
874      */
incrementAlertReasonCount(int reason)875     public void incrementAlertReasonCount(int reason) {
876         if (reason > WifiLoggerHal.WIFI_ALERT_REASON_MAX
877                 || reason < WifiLoggerHal.WIFI_ALERT_REASON_MIN) {
878             reason = WifiLoggerHal.WIFI_ALERT_REASON_RESERVED;
879         }
880         synchronized (mLock) {
881             int alertCount = mWifiAlertReasonCounts.get(reason);
882             mWifiAlertReasonCounts.put(reason, alertCount + 1);
883         }
884     }
885 
886     /**
887      * Counts all the different types of networks seen in a set of scan results
888      */
countScanResults(List<ScanDetail> scanDetails)889     public void countScanResults(List<ScanDetail> scanDetails) {
890         if (scanDetails == null) {
891             return;
892         }
893         int totalResults = 0;
894         int openNetworks = 0;
895         int personalNetworks = 0;
896         int enterpriseNetworks = 0;
897         int hiddenNetworks = 0;
898         int hotspot2r1Networks = 0;
899         int hotspot2r2Networks = 0;
900         for (ScanDetail scanDetail : scanDetails) {
901             NetworkDetail networkDetail = scanDetail.getNetworkDetail();
902             ScanResult scanResult = scanDetail.getScanResult();
903             totalResults++;
904             if (networkDetail != null) {
905                 if (networkDetail.isHiddenBeaconFrame()) {
906                     hiddenNetworks++;
907                 }
908                 if (networkDetail.getHSRelease() != null) {
909                     if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R1) {
910                         hotspot2r1Networks++;
911                     } else if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R2) {
912                         hotspot2r2Networks++;
913                     }
914                 }
915             }
916             if (scanResult != null && scanResult.capabilities != null) {
917                 if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
918                     enterpriseNetworks++;
919                 } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)
920                         || ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
921                     personalNetworks++;
922                 } else {
923                     openNetworks++;
924                 }
925             }
926         }
927         synchronized (mLock) {
928             mWifiLogProto.numTotalScanResults += totalResults;
929             mWifiLogProto.numOpenNetworkScanResults += openNetworks;
930             mWifiLogProto.numPersonalNetworkScanResults += personalNetworks;
931             mWifiLogProto.numEnterpriseNetworkScanResults += enterpriseNetworks;
932             mWifiLogProto.numHiddenNetworkScanResults += hiddenNetworks;
933             mWifiLogProto.numHotspot2R1NetworkScanResults += hotspot2r1Networks;
934             mWifiLogProto.numHotspot2R2NetworkScanResults += hotspot2r2Networks;
935             mWifiLogProto.numScans++;
936         }
937     }
938 
939     /**
940      * Increments occurence of a particular wifi score calculated
941      * in WifiScoreReport by current connected network. Scores are bounded
942      * within  [MIN_WIFI_SCORE, MAX_WIFI_SCORE] to limit size of SparseArray
943      */
incrementWifiScoreCount(int score)944     public void incrementWifiScoreCount(int score) {
945         if (score < MIN_WIFI_SCORE || score > MAX_WIFI_SCORE) {
946             return;
947         }
948         synchronized (mLock) {
949             int count = mWifiScoreCounts.get(score);
950             mWifiScoreCounts.put(score, count + 1);
951         }
952     }
953 
954     /**
955      * Increments occurence of the results from attempting to start SoftAp.
956      * Maps the |result| and WifiManager |failureCode| constant to proto defined SoftApStartResult
957      * codes.
958      */
incrementSoftApStartResult(boolean result, int failureCode)959     public void incrementSoftApStartResult(boolean result, int failureCode) {
960         synchronized (mLock) {
961             if (result) {
962                 int count = mSoftApManagerReturnCodeCounts.get(
963                         WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY);
964                 mSoftApManagerReturnCodeCounts.put(
965                         WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY,
966                         count + 1);
967                 return;
968             }
969 
970             // now increment failure modes - if not explicitly handled, dump into the general
971             // error bucket.
972             if (failureCode == WifiManager.SAP_START_FAILURE_NO_CHANNEL) {
973                 int count = mSoftApManagerReturnCodeCounts.get(
974                         WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL);
975                 mSoftApManagerReturnCodeCounts.put(
976                         WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL,
977                         count + 1);
978             } else {
979                 // failure mode not tracked at this time...  count as a general error for now.
980                 int count = mSoftApManagerReturnCodeCounts.get(
981                         WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR);
982                 mSoftApManagerReturnCodeCounts.put(
983                         WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR,
984                         count + 1);
985             }
986         }
987     }
988 
989     /**
990      * Increment number of times the HAL crashed.
991      */
incrementNumHalCrashes()992     public void incrementNumHalCrashes() {
993         synchronized (mLock) {
994             mWifiLogProto.numHalCrashes++;
995         }
996     }
997 
998     /**
999      * Increment number of times the Wificond crashed.
1000      */
incrementNumWificondCrashes()1001     public void incrementNumWificondCrashes() {
1002         synchronized (mLock) {
1003             mWifiLogProto.numWificondCrashes++;
1004         }
1005     }
1006 
1007     /**
1008      * Increment number of times the wifi on failed due to an error in HAL.
1009      */
incrementNumWifiOnFailureDueToHal()1010     public void incrementNumWifiOnFailureDueToHal() {
1011         synchronized (mLock) {
1012             mWifiLogProto.numWifiOnFailureDueToHal++;
1013         }
1014     }
1015 
1016     /**
1017      * Increment number of times the wifi on failed due to an error in wificond.
1018      */
incrementNumWifiOnFailureDueToWificond()1019     public void incrementNumWifiOnFailureDueToWificond() {
1020         synchronized (mLock) {
1021             mWifiLogProto.numWifiOnFailureDueToWificond++;
1022         }
1023     }
1024 
1025 
1026     public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
1027     public static final String CLEAN_DUMP_ARG = "clean";
1028 
1029     /**
1030      * Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager
1031      * at this time.
1032      *
1033      * @param fd unused
1034      * @param pw PrintWriter for writing dump to
1035      * @param args unused
1036      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)1037     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1038         synchronized (mLock) {
1039             if (args != null && args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) {
1040                 // Dump serialized WifiLog proto
1041                 consolidateProto(true);
1042                 for (ConnectionEvent event : mConnectionEventList) {
1043                     if (mCurrentConnectionEvent != event) {
1044                         //indicate that automatic bug report has been taken for all valid
1045                         //connection events
1046                         event.mConnectionEvent.automaticBugReportTaken = true;
1047                     }
1048                 }
1049                 byte[] wifiMetricsProto = WifiMetricsProto.WifiLog.toByteArray(mWifiLogProto);
1050                 String metricsProtoDump = Base64.encodeToString(wifiMetricsProto, Base64.DEFAULT);
1051                 if (args.length > 1 && CLEAN_DUMP_ARG.equals(args[1])) {
1052                     // Output metrics proto bytes (base64) and nothing else
1053                     pw.print(metricsProtoDump);
1054                 } else {
1055                     // Tag the start and end of the metrics proto bytes
1056                     pw.println("WifiMetrics:");
1057                     pw.println(metricsProtoDump);
1058                     pw.println("EndWifiMetrics");
1059                 }
1060                 clear();
1061             } else {
1062                 pw.println("WifiMetrics:");
1063                 pw.println("mConnectionEvents:");
1064                 for (ConnectionEvent event : mConnectionEventList) {
1065                     String eventLine = event.toString();
1066                     if (event == mCurrentConnectionEvent) {
1067                         eventLine += "CURRENTLY OPEN EVENT";
1068                     }
1069                     pw.println(eventLine);
1070                 }
1071                 pw.println("mWifiLogProto.numSavedNetworks=" + mWifiLogProto.numSavedNetworks);
1072                 pw.println("mWifiLogProto.numOpenNetworks=" + mWifiLogProto.numOpenNetworks);
1073                 pw.println("mWifiLogProto.numPersonalNetworks="
1074                         + mWifiLogProto.numPersonalNetworks);
1075                 pw.println("mWifiLogProto.numEnterpriseNetworks="
1076                         + mWifiLogProto.numEnterpriseNetworks);
1077                 pw.println("mWifiLogProto.numHiddenNetworks=" + mWifiLogProto.numHiddenNetworks);
1078                 pw.println("mWifiLogProto.numPasspointNetworks="
1079                         + mWifiLogProto.numPasspointNetworks);
1080                 pw.println("mWifiLogProto.isLocationEnabled=" + mWifiLogProto.isLocationEnabled);
1081                 pw.println("mWifiLogProto.isScanningAlwaysEnabled="
1082                         + mWifiLogProto.isScanningAlwaysEnabled);
1083                 pw.println("mWifiLogProto.numNetworksAddedByUser="
1084                         + mWifiLogProto.numNetworksAddedByUser);
1085                 pw.println("mWifiLogProto.numNetworksAddedByApps="
1086                         + mWifiLogProto.numNetworksAddedByApps);
1087                 pw.println("mWifiLogProto.numNonEmptyScanResults="
1088                         + mWifiLogProto.numNonEmptyScanResults);
1089                 pw.println("mWifiLogProto.numEmptyScanResults="
1090                         + mWifiLogProto.numEmptyScanResults);
1091                 pw.println("mWifiLogProto.numOneshotScans="
1092                         + mWifiLogProto.numOneshotScans);
1093                 pw.println("mWifiLogProto.numBackgroundScans="
1094                         + mWifiLogProto.numBackgroundScans);
1095 
1096                 pw.println("mScanReturnEntries:");
1097                 pw.println("  SCAN_UNKNOWN: " + getScanReturnEntry(
1098                         WifiMetricsProto.WifiLog.SCAN_UNKNOWN));
1099                 pw.println("  SCAN_SUCCESS: " + getScanReturnEntry(
1100                         WifiMetricsProto.WifiLog.SCAN_SUCCESS));
1101                 pw.println("  SCAN_FAILURE_INTERRUPTED: " + getScanReturnEntry(
1102                         WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED));
1103                 pw.println("  SCAN_FAILURE_INVALID_CONFIGURATION: " + getScanReturnEntry(
1104                         WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION));
1105                 pw.println("  FAILURE_WIFI_DISABLED: " + getScanReturnEntry(
1106                         WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED));
1107 
1108                 pw.println("mSystemStateEntries: <state><screenOn> : <scansInitiated>");
1109                 pw.println("  WIFI_UNKNOWN       ON: "
1110                         + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, true));
1111                 pw.println("  WIFI_DISABLED      ON: "
1112                         + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, true));
1113                 pw.println("  WIFI_DISCONNECTED  ON: "
1114                         + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, true));
1115                 pw.println("  WIFI_ASSOCIATED    ON: "
1116                         + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, true));
1117                 pw.println("  WIFI_UNKNOWN      OFF: "
1118                         + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, false));
1119                 pw.println("  WIFI_DISABLED     OFF: "
1120                         + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, false));
1121                 pw.println("  WIFI_DISCONNECTED OFF: "
1122                         + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, false));
1123                 pw.println("  WIFI_ASSOCIATED   OFF: "
1124                         + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, false));
1125                 pw.println("mWifiLogProto.numConnectivityWatchdogPnoGood="
1126                         + mWifiLogProto.numConnectivityWatchdogPnoGood);
1127                 pw.println("mWifiLogProto.numConnectivityWatchdogPnoBad="
1128                         + mWifiLogProto.numConnectivityWatchdogPnoBad);
1129                 pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundGood="
1130                         + mWifiLogProto.numConnectivityWatchdogBackgroundGood);
1131                 pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundBad="
1132                         + mWifiLogProto.numConnectivityWatchdogBackgroundBad);
1133                 pw.println("mWifiLogProto.numLastResortWatchdogTriggers="
1134                         + mWifiLogProto.numLastResortWatchdogTriggers);
1135                 pw.println("mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal="
1136                         + mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal);
1137                 pw.println("mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal="
1138                         + mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal);
1139                 pw.println("mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal="
1140                         + mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal);
1141                 pw.println("mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal="
1142                         + mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal);
1143                 pw.println("mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal="
1144                         + mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal);
1145                 pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation="
1146                         + mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation);
1147                 pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication="
1148                         + mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication);
1149                 pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp="
1150                         + mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp);
1151                 pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadOther="
1152                         + mWifiLogProto.numLastResortWatchdogTriggersWithBadOther);
1153                 pw.println("mWifiLogProto.numLastResortWatchdogSuccesses="
1154                         + mWifiLogProto.numLastResortWatchdogSuccesses);
1155                 pw.println("mWifiLogProto.recordDurationSec="
1156                         + ((mClock.getElapsedSinceBootMillis() / 1000) - mRecordStartTimeSec));
1157                 pw.println("mWifiLogProto.rssiPollRssiCount: Printing counts for [" + MIN_RSSI_POLL
1158                         + ", " + MAX_RSSI_POLL + "]");
1159                 StringBuilder sb = new StringBuilder();
1160                 for (int i = MIN_RSSI_POLL; i <= MAX_RSSI_POLL; i++) {
1161                     sb.append(mRssiPollCounts.get(i) + " ");
1162                 }
1163                 pw.println("  " + sb.toString());
1164                 pw.println("mWifiLogProto.rssiPollDeltaCount: Printing counts for ["
1165                         + MIN_RSSI_DELTA + ", " + MAX_RSSI_DELTA + "]");
1166                 sb.setLength(0);
1167                 for (int i = MIN_RSSI_DELTA; i <= MAX_RSSI_DELTA; i++) {
1168                     sb.append(mRssiDeltaCounts.get(i) + " ");
1169                 }
1170                 pw.println("  " + sb.toString());
1171                 pw.print("mWifiLogProto.alertReasonCounts=");
1172                 sb.setLength(0);
1173                 for (int i = WifiLoggerHal.WIFI_ALERT_REASON_MIN;
1174                         i <= WifiLoggerHal.WIFI_ALERT_REASON_MAX; i++) {
1175                     int count = mWifiAlertReasonCounts.get(i);
1176                     if (count > 0) {
1177                         sb.append("(" + i + "," + count + "),");
1178                     }
1179                 }
1180                 if (sb.length() > 1) {
1181                     sb.setLength(sb.length() - 1);  // strip trailing comma
1182                     pw.println(sb.toString());
1183                 } else {
1184                     pw.println("()");
1185                 }
1186                 pw.println("mWifiLogProto.numTotalScanResults="
1187                         + mWifiLogProto.numTotalScanResults);
1188                 pw.println("mWifiLogProto.numOpenNetworkScanResults="
1189                         + mWifiLogProto.numOpenNetworkScanResults);
1190                 pw.println("mWifiLogProto.numPersonalNetworkScanResults="
1191                         + mWifiLogProto.numPersonalNetworkScanResults);
1192                 pw.println("mWifiLogProto.numEnterpriseNetworkScanResults="
1193                         + mWifiLogProto.numEnterpriseNetworkScanResults);
1194                 pw.println("mWifiLogProto.numHiddenNetworkScanResults="
1195                         + mWifiLogProto.numHiddenNetworkScanResults);
1196                 pw.println("mWifiLogProto.numHotspot2R1NetworkScanResults="
1197                         + mWifiLogProto.numHotspot2R1NetworkScanResults);
1198                 pw.println("mWifiLogProto.numHotspot2R2NetworkScanResults="
1199                         + mWifiLogProto.numHotspot2R2NetworkScanResults);
1200                 pw.println("mWifiLogProto.numScans=" + mWifiLogProto.numScans);
1201                 pw.println("mWifiLogProto.WifiScoreCount: [" + MIN_WIFI_SCORE + ", "
1202                         + MAX_WIFI_SCORE + "]");
1203                 for (int i = 0; i <= MAX_WIFI_SCORE; i++) {
1204                     pw.print(mWifiScoreCounts.get(i) + " ");
1205                 }
1206                 pw.println(); // add a line after wifi scores
1207                 pw.println("mWifiLogProto.SoftApManagerReturnCodeCounts:");
1208                 pw.println("  SUCCESS: " + mSoftApManagerReturnCodeCounts.get(
1209                         WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY));
1210                 pw.println("  FAILED_GENERAL_ERROR: " + mSoftApManagerReturnCodeCounts.get(
1211                         WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR));
1212                 pw.println("  FAILED_NO_CHANNEL: " + mSoftApManagerReturnCodeCounts.get(
1213                         WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL));
1214                 pw.print("\n");
1215                 pw.println("mWifiLogProto.numHalCrashes="
1216                         + mWifiLogProto.numHalCrashes);
1217                 pw.println("mWifiLogProto.numWificondCrashes="
1218                         + mWifiLogProto.numWificondCrashes);
1219                 pw.println("mWifiLogProto.numWifiOnFailureDueToHal="
1220                         + mWifiLogProto.numWifiOnFailureDueToHal);
1221                 pw.println("mWifiLogProto.numWifiOnFailureDueToWificond="
1222                         + mWifiLogProto.numWifiOnFailureDueToWificond);
1223                 pw.println("StaEventList:");
1224                 for (StaEvent event : mStaEventList) {
1225                     pw.println(staEventToString(event));
1226                 }
1227             }
1228         }
1229     }
1230 
1231 
1232     /**
1233      * Update various counts of saved network types
1234      * @param networks List of WifiConfigurations representing all saved networks, must not be null
1235      */
updateSavedNetworks(List<WifiConfiguration> networks)1236     public void updateSavedNetworks(List<WifiConfiguration> networks) {
1237         synchronized (mLock) {
1238             mWifiLogProto.numSavedNetworks = networks.size();
1239             mWifiLogProto.numOpenNetworks = 0;
1240             mWifiLogProto.numPersonalNetworks = 0;
1241             mWifiLogProto.numEnterpriseNetworks = 0;
1242             mWifiLogProto.numNetworksAddedByUser = 0;
1243             mWifiLogProto.numNetworksAddedByApps = 0;
1244             mWifiLogProto.numHiddenNetworks = 0;
1245             mWifiLogProto.numPasspointNetworks = 0;
1246             for (WifiConfiguration config : networks) {
1247                 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
1248                     mWifiLogProto.numOpenNetworks++;
1249                 } else if (config.isEnterprise()) {
1250                     mWifiLogProto.numEnterpriseNetworks++;
1251                 } else {
1252                     mWifiLogProto.numPersonalNetworks++;
1253                 }
1254                 if (config.selfAdded) {
1255                     mWifiLogProto.numNetworksAddedByUser++;
1256                 } else {
1257                     mWifiLogProto.numNetworksAddedByApps++;
1258                 }
1259                 if (config.hiddenSSID) {
1260                     mWifiLogProto.numHiddenNetworks++;
1261                 }
1262                 if (config.isPasspoint()) {
1263                     mWifiLogProto.numPasspointNetworks++;
1264                 }
1265             }
1266         }
1267     }
1268 
1269     /**
1270      * append the separate ConnectionEvent, SystemStateEntry and ScanReturnCode collections to their
1271      * respective lists within mWifiLogProto
1272      *
1273      * @param incremental Only include ConnectionEvents created since last automatic bug report
1274      */
consolidateProto(boolean incremental)1275     private void consolidateProto(boolean incremental) {
1276         List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>();
1277         List<WifiMetricsProto.RssiPollCount> rssis = new ArrayList<>();
1278         List<WifiMetricsProto.RssiPollCount> rssiDeltas = new ArrayList<>();
1279         List<WifiMetricsProto.AlertReasonCount> alertReasons = new ArrayList<>();
1280         List<WifiMetricsProto.WifiScoreCount> scores = new ArrayList<>();
1281         synchronized (mLock) {
1282             for (ConnectionEvent event : mConnectionEventList) {
1283                 // If this is not incremental, dump full ConnectionEvent list
1284                 // Else Dump all un-dumped events except for the current one
1285                 if (!incremental || ((mCurrentConnectionEvent != event)
1286                         && !event.mConnectionEvent.automaticBugReportTaken)) {
1287                     //Get all ConnectionEvents that haven not been dumped as a proto, also exclude
1288                     //the current active un-ended connection event
1289                     events.add(event.mConnectionEvent);
1290                     if (incremental) {
1291                         event.mConnectionEvent.automaticBugReportTaken = true;
1292                     }
1293                 }
1294             }
1295             if (events.size() > 0) {
1296                 mWifiLogProto.connectionEvent = events.toArray(mWifiLogProto.connectionEvent);
1297             }
1298 
1299             //Convert the SparseIntArray of scanReturnEntry integers into ScanReturnEntry proto list
1300             mWifiLogProto.scanReturnEntries =
1301                     new WifiMetricsProto.WifiLog.ScanReturnEntry[mScanReturnEntries.size()];
1302             for (int i = 0; i < mScanReturnEntries.size(); i++) {
1303                 mWifiLogProto.scanReturnEntries[i] = new WifiMetricsProto.WifiLog.ScanReturnEntry();
1304                 mWifiLogProto.scanReturnEntries[i].scanReturnCode = mScanReturnEntries.keyAt(i);
1305                 mWifiLogProto.scanReturnEntries[i].scanResultsCount = mScanReturnEntries.valueAt(i);
1306             }
1307 
1308             // Convert the SparseIntArray of systemStateEntry into WifiSystemStateEntry proto list
1309             // This one is slightly more complex, as the Sparse are indexed with:
1310             //     key: wifiState * 2 + isScreenOn, value: wifiStateCount
1311             mWifiLogProto.wifiSystemStateEntries =
1312                     new WifiMetricsProto.WifiLog
1313                     .WifiSystemStateEntry[mWifiSystemStateEntries.size()];
1314             for (int i = 0; i < mWifiSystemStateEntries.size(); i++) {
1315                 mWifiLogProto.wifiSystemStateEntries[i] =
1316                         new WifiMetricsProto.WifiLog.WifiSystemStateEntry();
1317                 mWifiLogProto.wifiSystemStateEntries[i].wifiState =
1318                         mWifiSystemStateEntries.keyAt(i) / 2;
1319                 mWifiLogProto.wifiSystemStateEntries[i].wifiStateCount =
1320                         mWifiSystemStateEntries.valueAt(i);
1321                 mWifiLogProto.wifiSystemStateEntries[i].isScreenOn =
1322                         (mWifiSystemStateEntries.keyAt(i) % 2) > 0;
1323             }
1324             mWifiLogProto.recordDurationSec = (int) ((mClock.getElapsedSinceBootMillis() / 1000)
1325                     - mRecordStartTimeSec);
1326 
1327             /**
1328              * Convert the SparseIntArray of RSSI poll rssi's and counts to the proto's repeated
1329              * IntKeyVal array.
1330              */
1331             for (int i = 0; i < mRssiPollCounts.size(); i++) {
1332                 WifiMetricsProto.RssiPollCount keyVal = new WifiMetricsProto.RssiPollCount();
1333                 keyVal.rssi = mRssiPollCounts.keyAt(i);
1334                 keyVal.count = mRssiPollCounts.valueAt(i);
1335                 rssis.add(keyVal);
1336             }
1337             mWifiLogProto.rssiPollRssiCount = rssis.toArray(mWifiLogProto.rssiPollRssiCount);
1338 
1339             /**
1340              * Convert the SparseIntArray of RSSI delta rssi's and counts to the proto's repeated
1341              * IntKeyVal array.
1342              */
1343             for (int i = 0; i < mRssiDeltaCounts.size(); i++) {
1344                 WifiMetricsProto.RssiPollCount keyVal = new WifiMetricsProto.RssiPollCount();
1345                 keyVal.rssi = mRssiDeltaCounts.keyAt(i);
1346                 keyVal.count = mRssiDeltaCounts.valueAt(i);
1347                 rssiDeltas.add(keyVal);
1348             }
1349             mWifiLogProto.rssiPollDeltaCount = rssiDeltas.toArray(mWifiLogProto.rssiPollDeltaCount);
1350 
1351             /**
1352              * Convert the SparseIntArray of alert reasons and counts to the proto's repeated
1353              * IntKeyVal array.
1354              */
1355             for (int i = 0; i < mWifiAlertReasonCounts.size(); i++) {
1356                 WifiMetricsProto.AlertReasonCount keyVal = new WifiMetricsProto.AlertReasonCount();
1357                 keyVal.reason = mWifiAlertReasonCounts.keyAt(i);
1358                 keyVal.count = mWifiAlertReasonCounts.valueAt(i);
1359                 alertReasons.add(keyVal);
1360             }
1361             mWifiLogProto.alertReasonCount = alertReasons.toArray(mWifiLogProto.alertReasonCount);
1362 
1363             /**
1364             *  Convert the SparseIntArray of Wifi Score and counts to proto's repeated
1365             * IntKeyVal array.
1366             */
1367             for (int score = 0; score < mWifiScoreCounts.size(); score++) {
1368                 WifiMetricsProto.WifiScoreCount keyVal = new WifiMetricsProto.WifiScoreCount();
1369                 keyVal.score = mWifiScoreCounts.keyAt(score);
1370                 keyVal.count = mWifiScoreCounts.valueAt(score);
1371                 scores.add(keyVal);
1372             }
1373             mWifiLogProto.wifiScoreCount = scores.toArray(mWifiLogProto.wifiScoreCount);
1374 
1375             /**
1376              * Convert the SparseIntArray of SoftAp Return codes and counts to proto's repeated
1377              * IntKeyVal array.
1378              */
1379             int codeCounts = mSoftApManagerReturnCodeCounts.size();
1380             mWifiLogProto.softApReturnCode = new WifiMetricsProto.SoftApReturnCodeCount[codeCounts];
1381             for (int sapCode = 0; sapCode < codeCounts; sapCode++) {
1382                 mWifiLogProto.softApReturnCode[sapCode] =
1383                         new WifiMetricsProto.SoftApReturnCodeCount();
1384                 mWifiLogProto.softApReturnCode[sapCode].startResult =
1385                         mSoftApManagerReturnCodeCounts.keyAt(sapCode);
1386                 mWifiLogProto.softApReturnCode[sapCode].count =
1387                         mSoftApManagerReturnCodeCounts.valueAt(sapCode);
1388             }
1389 
1390             mWifiLogProto.staEventList = mStaEventList.toArray(mWifiLogProto.staEventList);
1391         }
1392     }
1393 
1394     /**
1395      * Clear all WifiMetrics, except for currentConnectionEvent.
1396      */
clear()1397     private void clear() {
1398         synchronized (mLock) {
1399             mConnectionEventList.clear();
1400             if (mCurrentConnectionEvent != null) {
1401                 mConnectionEventList.add(mCurrentConnectionEvent);
1402             }
1403             mScanReturnEntries.clear();
1404             mWifiSystemStateEntries.clear();
1405             mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
1406             mRssiPollCounts.clear();
1407             mRssiDeltaCounts.clear();
1408             mWifiAlertReasonCounts.clear();
1409             mWifiScoreCounts.clear();
1410             mWifiLogProto.clear();
1411             mScanResultRssiTimestampMillis = -1;
1412             mSoftApManagerReturnCodeCounts.clear();
1413             mStaEventList.clear();
1414         }
1415     }
1416 
1417     /**
1418      *  Set screen state (On/Off)
1419      */
setScreenState(boolean screenOn)1420     public void setScreenState(boolean screenOn) {
1421         synchronized (mLock) {
1422             mScreenOn = screenOn;
1423         }
1424     }
1425 
1426     /**
1427      *  Set wifi state (WIFI_UNKNOWN, WIFI_DISABLED, WIFI_DISCONNECTED, WIFI_ASSOCIATED)
1428      */
setWifiState(int wifiState)1429     public void setWifiState(int wifiState) {
1430         synchronized (mLock) {
1431             mWifiState = wifiState;
1432         }
1433     }
1434 
1435     /**
1436      * Message handler for interesting WifiMonitor messages. Generates StaEvents
1437      */
processMessage(Message msg)1438     private void processMessage(Message msg) {
1439         StaEvent event = new StaEvent();
1440         boolean logEvent = true;
1441         switch (msg.what) {
1442             case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
1443                 event.type = StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT;
1444                 event.associationTimedOut = msg.arg1 > 0 ? true : false;
1445                 event.status = msg.arg2;
1446                 break;
1447             case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
1448                 event.type = StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT;
1449                 switch (msg.arg2) {
1450                     case WifiManager.ERROR_AUTH_FAILURE_NONE:
1451                         event.authFailureReason = StaEvent.AUTH_FAILURE_NONE;
1452                         break;
1453                     case WifiManager.ERROR_AUTH_FAILURE_TIMEOUT:
1454                         event.authFailureReason = StaEvent.AUTH_FAILURE_TIMEOUT;
1455                         break;
1456                     case WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD:
1457                         event.authFailureReason = StaEvent.AUTH_FAILURE_WRONG_PSWD;
1458                         break;
1459                     case WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE:
1460                         event.authFailureReason = StaEvent.AUTH_FAILURE_EAP_FAILURE;
1461                         break;
1462                     default:
1463                         break;
1464                 }
1465                 break;
1466             case WifiMonitor.NETWORK_CONNECTION_EVENT:
1467                 event.type = StaEvent.TYPE_NETWORK_CONNECTION_EVENT;
1468                 break;
1469             case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
1470                 event.type = StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT;
1471                 event.reason = msg.arg2;
1472                 event.localGen = msg.arg1 == 0 ? false : true;
1473                 break;
1474             case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
1475                 logEvent = false;
1476                 StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
1477                 mSupplicantStateChangeBitmask |= supplicantStateToBit(stateChangeResult.state);
1478                 break;
1479             case WifiStateMachine.CMD_ASSOCIATED_BSSID:
1480                 event.type = StaEvent.TYPE_CMD_ASSOCIATED_BSSID;
1481                 break;
1482             case WifiStateMachine.CMD_TARGET_BSSID:
1483                 event.type = StaEvent.TYPE_CMD_TARGET_BSSID;
1484                 break;
1485             default:
1486                 return;
1487         }
1488         if (logEvent) {
1489             addStaEvent(event);
1490         }
1491     }
1492     /**
1493      * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
1494      * generated event types, which are logged through 'sendMessage'
1495      * @param type StaEvent.EventType describing the event
1496      */
logStaEvent(int type)1497     public void logStaEvent(int type) {
1498         logStaEvent(type, StaEvent.DISCONNECT_UNKNOWN, null);
1499     }
1500     /**
1501      * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
1502      * generated event types, which are logged through 'sendMessage'
1503      * @param type StaEvent.EventType describing the event
1504      * @param config WifiConfiguration for a framework initiated connection attempt
1505      */
logStaEvent(int type, WifiConfiguration config)1506     public void logStaEvent(int type, WifiConfiguration config) {
1507         logStaEvent(type, StaEvent.DISCONNECT_UNKNOWN, config);
1508     }
1509     /**
1510      * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
1511      * generated event types, which are logged through 'sendMessage'
1512      * @param type StaEvent.EventType describing the event
1513      * @param frameworkDisconnectReason StaEvent.FrameworkDisconnectReason explaining why framework
1514      *                                  initiated a FRAMEWORK_DISCONNECT
1515      */
logStaEvent(int type, int frameworkDisconnectReason)1516     public void logStaEvent(int type, int frameworkDisconnectReason) {
1517         logStaEvent(type, frameworkDisconnectReason, null);
1518     }
1519     /**
1520      * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
1521      * generated event types, which are logged through 'sendMessage'
1522      * @param type StaEvent.EventType describing the event
1523      * @param frameworkDisconnectReason StaEvent.FrameworkDisconnectReason explaining why framework
1524      *                                  initiated a FRAMEWORK_DISCONNECT
1525      * @param config WifiConfiguration for a framework initiated connection attempt
1526      */
logStaEvent(int type, int frameworkDisconnectReason, WifiConfiguration config)1527     public void logStaEvent(int type, int frameworkDisconnectReason, WifiConfiguration config) {
1528         switch (type) {
1529             case StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL:
1530             case StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST:
1531             case StaEvent.TYPE_CMD_IP_REACHABILITY_LOST:
1532             case StaEvent.TYPE_CMD_START_CONNECT:
1533             case StaEvent.TYPE_CMD_START_ROAM:
1534             case StaEvent.TYPE_CONNECT_NETWORK:
1535             case StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK:
1536             case StaEvent.TYPE_FRAMEWORK_DISCONNECT:
1537                 break;
1538             default:
1539                 Log.e(TAG, "Unknown StaEvent:" + type);
1540                 return;
1541         }
1542         StaEvent event = new StaEvent();
1543         event.type = type;
1544         if (frameworkDisconnectReason != StaEvent.DISCONNECT_UNKNOWN) {
1545             event.frameworkDisconnectReason = frameworkDisconnectReason;
1546         }
1547         event.configInfo = createConfigInfo(config);
1548         addStaEvent(event);
1549     }
1550 
addStaEvent(StaEvent staEvent)1551     private void addStaEvent(StaEvent staEvent) {
1552         staEvent.startTimeMillis = mClock.getElapsedSinceBootMillis();
1553         staEvent.lastRssi = mLastPollRssi;
1554         staEvent.lastFreq = mLastPollFreq;
1555         staEvent.lastLinkSpeed = mLastPollLinkSpeed;
1556         staEvent.supplicantStateChangesBitmask = mSupplicantStateChangeBitmask;
1557         mSupplicantStateChangeBitmask = 0;
1558         mLastPollRssi = -127;
1559         mLastPollFreq = -1;
1560         mLastPollLinkSpeed = -1;
1561         mStaEventList.add(staEvent);
1562         // Prune StaEventList if it gets too long
1563         if (mStaEventList.size() > MAX_STA_EVENTS) mStaEventList.remove();
1564     }
1565 
createConfigInfo(WifiConfiguration config)1566     private ConfigInfo createConfigInfo(WifiConfiguration config) {
1567         if (config == null) return null;
1568         ConfigInfo info = new ConfigInfo();
1569         info.allowedKeyManagement = bitSetToInt(config.allowedKeyManagement);
1570         info.allowedProtocols = bitSetToInt(config.allowedProtocols);
1571         info.allowedAuthAlgorithms = bitSetToInt(config.allowedAuthAlgorithms);
1572         info.allowedPairwiseCiphers = bitSetToInt(config.allowedPairwiseCiphers);
1573         info.allowedGroupCiphers = bitSetToInt(config.allowedGroupCiphers);
1574         info.hiddenSsid = config.hiddenSSID;
1575         info.isPasspoint = config.isPasspoint();
1576         info.isEphemeral = config.isEphemeral();
1577         info.hasEverConnected = config.getNetworkSelectionStatus().getHasEverConnected();
1578         ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
1579         if (candidate != null) {
1580             info.scanRssi = candidate.level;
1581             info.scanFreq = candidate.frequency;
1582         }
1583         return info;
1584     }
1585 
getHandler()1586     public Handler getHandler() {
1587         return mHandler;
1588     }
1589 
1590     // Rather than generate a StaEvent for each SUPPLICANT_STATE_CHANGE, cache these in a bitmask
1591     // and attach it to the next event which is generated.
1592     private int mSupplicantStateChangeBitmask = 0;
1593 
1594     /**
1595      * Converts a SupplicantState value to a single bit, with position defined by
1596      * {@code StaEvent.SupplicantState}
1597      */
supplicantStateToBit(SupplicantState state)1598     public static int supplicantStateToBit(SupplicantState state) {
1599         switch(state) {
1600             case DISCONNECTED:
1601                 return 1 << StaEvent.STATE_DISCONNECTED;
1602             case INTERFACE_DISABLED:
1603                 return 1 << StaEvent.STATE_INTERFACE_DISABLED;
1604             case INACTIVE:
1605                 return 1 << StaEvent.STATE_INACTIVE;
1606             case SCANNING:
1607                 return 1 << StaEvent.STATE_SCANNING;
1608             case AUTHENTICATING:
1609                 return 1 << StaEvent.STATE_AUTHENTICATING;
1610             case ASSOCIATING:
1611                 return 1 << StaEvent.STATE_ASSOCIATING;
1612             case ASSOCIATED:
1613                 return 1 << StaEvent.STATE_ASSOCIATED;
1614             case FOUR_WAY_HANDSHAKE:
1615                 return 1 << StaEvent.STATE_FOUR_WAY_HANDSHAKE;
1616             case GROUP_HANDSHAKE:
1617                 return 1 << StaEvent.STATE_GROUP_HANDSHAKE;
1618             case COMPLETED:
1619                 return 1 << StaEvent.STATE_COMPLETED;
1620             case DORMANT:
1621                 return 1 << StaEvent.STATE_DORMANT;
1622             case UNINITIALIZED:
1623                 return 1 << StaEvent.STATE_UNINITIALIZED;
1624             case INVALID:
1625                 return 1 << StaEvent.STATE_INVALID;
1626             default:
1627                 Log.wtf(TAG, "Got unknown supplicant state: " + state.ordinal());
1628                 return 0;
1629         }
1630     }
1631 
supplicantStateChangesBitmaskToString(int mask)1632     private static String supplicantStateChangesBitmaskToString(int mask) {
1633         StringBuilder sb = new StringBuilder();
1634         sb.append("SUPPLICANT_STATE_CHANGE_EVENTS: {");
1635         if ((mask & (1 << StaEvent.STATE_DISCONNECTED)) > 0) sb.append(" DISCONNECTED");
1636         if ((mask & (1 << StaEvent.STATE_INTERFACE_DISABLED)) > 0) sb.append(" INTERFACE_DISABLED");
1637         if ((mask & (1 << StaEvent.STATE_INACTIVE)) > 0) sb.append(" INACTIVE");
1638         if ((mask & (1 << StaEvent.STATE_SCANNING)) > 0) sb.append(" SCANNING");
1639         if ((mask & (1 << StaEvent.STATE_AUTHENTICATING)) > 0) sb.append(" AUTHENTICATING");
1640         if ((mask & (1 << StaEvent.STATE_ASSOCIATING)) > 0) sb.append(" ASSOCIATING");
1641         if ((mask & (1 << StaEvent.STATE_ASSOCIATED)) > 0) sb.append(" ASSOCIATED");
1642         if ((mask & (1 << StaEvent.STATE_FOUR_WAY_HANDSHAKE)) > 0) sb.append(" FOUR_WAY_HANDSHAKE");
1643         if ((mask & (1 << StaEvent.STATE_GROUP_HANDSHAKE)) > 0) sb.append(" GROUP_HANDSHAKE");
1644         if ((mask & (1 << StaEvent.STATE_COMPLETED)) > 0) sb.append(" COMPLETED");
1645         if ((mask & (1 << StaEvent.STATE_DORMANT)) > 0) sb.append(" DORMANT");
1646         if ((mask & (1 << StaEvent.STATE_UNINITIALIZED)) > 0) sb.append(" UNINITIALIZED");
1647         if ((mask & (1 << StaEvent.STATE_INVALID)) > 0) sb.append(" INVALID");
1648         sb.append("}");
1649         return sb.toString();
1650     }
1651 
1652     /**
1653      * Returns a human readable string from a Sta Event. Only adds information relevant to the event
1654      * type.
1655      */
staEventToString(StaEvent event)1656     public static String staEventToString(StaEvent event) {
1657         if (event == null) return "<NULL>";
1658         StringBuilder sb = new StringBuilder();
1659         Long time = event.startTimeMillis;
1660         sb.append(String.format("%9d ", time.longValue())).append(" ");
1661         switch (event.type) {
1662             case StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT:
1663                 sb.append("ASSOCIATION_REJECTION_EVENT:")
1664                         .append(" timedOut=").append(event.associationTimedOut)
1665                         .append(" status=").append(event.status).append(":")
1666                         .append(ISupplicantStaIfaceCallback.StatusCode.toString(event.status));
1667                 break;
1668             case StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT:
1669                 sb.append("AUTHENTICATION_FAILURE_EVENT: reason=").append(event.authFailureReason)
1670                         .append(":").append(authFailureReasonToString(event.authFailureReason));
1671                 break;
1672             case StaEvent.TYPE_NETWORK_CONNECTION_EVENT:
1673                 sb.append("NETWORK_CONNECTION_EVENT:");
1674                 break;
1675             case StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT:
1676                 sb.append("NETWORK_DISCONNECTION_EVENT:")
1677                         .append(" local_gen=").append(event.localGen)
1678                         .append(" reason=").append(event.reason).append(":")
1679                         .append(ISupplicantStaIfaceCallback.ReasonCode.toString(
1680                                 (event.reason >= 0 ? event.reason : -1 * event.reason)));
1681                 break;
1682             case StaEvent.TYPE_CMD_ASSOCIATED_BSSID:
1683                 sb.append("CMD_ASSOCIATED_BSSID:");
1684                 break;
1685             case StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL:
1686                 sb.append("CMD_IP_CONFIGURATION_SUCCESSFUL:");
1687                 break;
1688             case StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST:
1689                 sb.append("CMD_IP_CONFIGURATION_LOST:");
1690                 break;
1691             case StaEvent.TYPE_CMD_IP_REACHABILITY_LOST:
1692                 sb.append("CMD_IP_REACHABILITY_LOST:");
1693                 break;
1694             case StaEvent.TYPE_CMD_TARGET_BSSID:
1695                 sb.append("CMD_TARGET_BSSID:");
1696                 break;
1697             case StaEvent.TYPE_CMD_START_CONNECT:
1698                 sb.append("CMD_START_CONNECT:");
1699                 break;
1700             case StaEvent.TYPE_CMD_START_ROAM:
1701                 sb.append("CMD_START_ROAM:");
1702                 break;
1703             case StaEvent.TYPE_CONNECT_NETWORK:
1704                 sb.append("CONNECT_NETWORK:");
1705                 break;
1706             case StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK:
1707                 sb.append("NETWORK_AGENT_VALID_NETWORK:");
1708                 break;
1709             case StaEvent.TYPE_FRAMEWORK_DISCONNECT:
1710                 sb.append("FRAMEWORK_DISCONNECT:")
1711                         .append(" reason=")
1712                         .append(frameworkDisconnectReasonToString(event.frameworkDisconnectReason));
1713                 break;
1714             default:
1715                 sb.append("UNKNOWN " + event.type + ":");
1716                 break;
1717         }
1718         if (event.lastRssi != -127) sb.append(" lastRssi=").append(event.lastRssi);
1719         if (event.lastFreq != -1) sb.append(" lastFreq=").append(event.lastFreq);
1720         if (event.lastLinkSpeed != -1) sb.append(" lastLinkSpeed=").append(event.lastLinkSpeed);
1721         if (event.supplicantStateChangesBitmask != 0) {
1722             sb.append("\n             ").append(supplicantStateChangesBitmaskToString(
1723                     event.supplicantStateChangesBitmask));
1724         }
1725         if (event.configInfo != null) {
1726             sb.append("\n             ").append(configInfoToString(event.configInfo));
1727         }
1728 
1729         return sb.toString();
1730     }
1731 
authFailureReasonToString(int authFailureReason)1732     private static String authFailureReasonToString(int authFailureReason) {
1733         switch (authFailureReason) {
1734             case StaEvent.AUTH_FAILURE_NONE:
1735                 return "ERROR_AUTH_FAILURE_NONE";
1736             case StaEvent.AUTH_FAILURE_TIMEOUT:
1737                 return "ERROR_AUTH_FAILURE_TIMEOUT";
1738             case StaEvent.AUTH_FAILURE_WRONG_PSWD:
1739                 return "ERROR_AUTH_FAILURE_WRONG_PSWD";
1740             case StaEvent.AUTH_FAILURE_EAP_FAILURE:
1741                 return "ERROR_AUTH_FAILURE_EAP_FAILURE";
1742             default:
1743                 return "";
1744         }
1745     }
1746 
frameworkDisconnectReasonToString(int frameworkDisconnectReason)1747     private static String frameworkDisconnectReasonToString(int frameworkDisconnectReason) {
1748         switch (frameworkDisconnectReason) {
1749             case StaEvent.DISCONNECT_API:
1750                 return "DISCONNECT_API";
1751             case StaEvent.DISCONNECT_GENERIC:
1752                 return "DISCONNECT_GENERIC";
1753             case StaEvent.DISCONNECT_UNWANTED:
1754                 return "DISCONNECT_UNWANTED";
1755             case StaEvent.DISCONNECT_ROAM_WATCHDOG_TIMER:
1756                 return "DISCONNECT_ROAM_WATCHDOG_TIMER";
1757             case StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST:
1758                 return "DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST";
1759             case StaEvent.DISCONNECT_RESET_SIM_NETWORKS:
1760                 return "DISCONNECT_RESET_SIM_NETWORKS";
1761             default:
1762                 return "DISCONNECT_UNKNOWN=" + frameworkDisconnectReason;
1763         }
1764     }
1765 
configInfoToString(ConfigInfo info)1766     private static String configInfoToString(ConfigInfo info) {
1767         StringBuilder sb = new StringBuilder();
1768         sb.append("ConfigInfo:")
1769                 .append(" allowed_key_management=").append(info.allowedKeyManagement)
1770                 .append(" allowed_protocols=").append(info.allowedProtocols)
1771                 .append(" allowed_auth_algorithms=").append(info.allowedAuthAlgorithms)
1772                 .append(" allowed_pairwise_ciphers=").append(info.allowedPairwiseCiphers)
1773                 .append(" allowed_group_ciphers=").append(info.allowedGroupCiphers)
1774                 .append(" hidden_ssid=").append(info.hiddenSsid)
1775                 .append(" is_passpoint=").append(info.isPasspoint)
1776                 .append(" is_ephemeral=").append(info.isEphemeral)
1777                 .append(" has_ever_connected=").append(info.hasEverConnected)
1778                 .append(" scan_rssi=").append(info.scanRssi)
1779                 .append(" scan_freq=").append(info.scanFreq);
1780         return sb.toString();
1781     }
1782 
1783     public static final int MAX_STA_EVENTS = 512;
1784     private LinkedList<StaEvent> mStaEventList = new LinkedList<StaEvent>();
1785     private int mLastPollRssi = -127;
1786     private int mLastPollLinkSpeed = -1;
1787     private int mLastPollFreq = -1;
1788 
1789     /**
1790      * Converts the first 31 bits of a BitSet to a little endian int
1791      */
bitSetToInt(BitSet bits)1792     private static int bitSetToInt(BitSet bits) {
1793         int value = 0;
1794         int nBits = bits.length() < 31 ? bits.length() : 31;
1795         for (int i = 0; i < nBits; i++) {
1796             value += bits.get(i) ? (1 << i) : 0;
1797         }
1798         return value;
1799     }
1800 }
1801