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.net.wifi.ScanResult; 20 import android.net.wifi.WifiConfiguration; 21 import android.util.Base64; 22 import android.util.Log; 23 import android.util.SparseIntArray; 24 25 import com.android.server.wifi.hotspot2.NetworkDetail; 26 import com.android.server.wifi.util.InformationElementUtil; 27 28 import java.io.FileDescriptor; 29 import java.io.PrintWriter; 30 import java.util.ArrayList; 31 import java.util.Calendar; 32 import java.util.List; 33 34 /** 35 * Provides storage for wireless connectivity metrics, as they are generated. 36 * Metrics logged by this class include: 37 * Aggregated connection stats (num of connections, num of failures, ...) 38 * Discrete connection event stats (time, duration, failure codes, ...) 39 * Router details (technology type, authentication type, ...) 40 * Scan stats 41 */ 42 public class WifiMetrics { 43 private static final String TAG = "WifiMetrics"; 44 private static final boolean DBG = false; 45 private final Object mLock = new Object(); 46 private static final int MAX_CONNECTION_EVENTS = 256; 47 private Clock mClock; 48 private boolean mScreenOn; 49 private int mWifiState; 50 /** 51 * Metrics are stored within an instance of the WifiLog proto during runtime, 52 * The ConnectionEvent, SystemStateEntries & ScanReturnEntries metrics are stored during 53 * runtime in member lists of this WifiMetrics class, with the final WifiLog proto being pieced 54 * together at dump-time 55 */ 56 private final WifiMetricsProto.WifiLog mWifiLogProto; 57 /** 58 * Session information that gets logged for every Wifi connection attempt. 59 */ 60 private final List<ConnectionEvent> mConnectionEventList; 61 /** 62 * The latest started (but un-ended) connection attempt 63 */ 64 private ConnectionEvent mCurrentConnectionEvent; 65 /** 66 * Count of number of times each scan return code, indexed by WifiLog.ScanReturnCode 67 */ 68 private SparseIntArray mScanReturnEntries; 69 /** 70 * Mapping of system state to the counts of scans requested in that wifi state * screenOn 71 * combination. Indexed by WifiLog.WifiState * (1 + screenOn) 72 */ 73 private SparseIntArray mWifiSystemStateEntries; 74 /** 75 * Records the elapsedRealtime (in seconds) that represents the beginning of data 76 * capture for for this WifiMetricsProto 77 */ 78 private long mRecordStartTimeSec; 79 80 class RouterFingerPrint { 81 private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto; RouterFingerPrint()82 RouterFingerPrint() { 83 mRouterFingerPrintProto = new WifiMetricsProto.RouterFingerPrint(); 84 } 85 toString()86 public String toString() { 87 StringBuilder sb = new StringBuilder(); 88 synchronized (mLock) { 89 sb.append("mConnectionEvent.roamType=" + mRouterFingerPrintProto.roamType); 90 sb.append(", mChannelInfo=" + mRouterFingerPrintProto.channelInfo); 91 sb.append(", mDtim=" + mRouterFingerPrintProto.dtim); 92 sb.append(", mAuthentication=" + mRouterFingerPrintProto.authentication); 93 sb.append(", mHidden=" + mRouterFingerPrintProto.hidden); 94 sb.append(", mRouterTechnology=" + mRouterFingerPrintProto.routerTechnology); 95 sb.append(", mSupportsIpv6=" + mRouterFingerPrintProto.supportsIpv6); 96 } 97 return sb.toString(); 98 } updateFromWifiConfiguration(WifiConfiguration config)99 public void updateFromWifiConfiguration(WifiConfiguration config) { 100 synchronized (mLock) { 101 if (config != null) { 102 // Is this a hidden network 103 mRouterFingerPrintProto.hidden = config.hiddenSSID; 104 // Config may not have a valid dtimInterval set yet, in which case dtim will be zero 105 // (These are only populated from beacon frame scan results, which are returned as 106 // scan results from the chip far less frequently than Probe-responses) 107 if (config.dtimInterval > 0) { 108 mRouterFingerPrintProto.dtim = config.dtimInterval; 109 } 110 mCurrentConnectionEvent.mConfigSsid = config.SSID; 111 // Get AuthType information from config (We do this again from ScanResult after 112 // associating with BSSID) 113 if (config.allowedKeyManagement != null 114 && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) { 115 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto 116 .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_OPEN; 117 } else if (config.isEnterprise()) { 118 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto 119 .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE; 120 } else { 121 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto 122 .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL; 123 } 124 // If there's a ScanResult candidate associated with this config already, get it and 125 // log (more accurate) metrics from it 126 ScanResult candidate = config.getNetworkSelectionStatus().getCandidate(); 127 if (candidate != null) { 128 updateMetricsFromScanResult(candidate); 129 } 130 } 131 } 132 } 133 } 134 135 /** 136 * Log event, tracking the start time, end time and result of a wireless connection attempt. 137 */ 138 class ConnectionEvent { 139 WifiMetricsProto.ConnectionEvent mConnectionEvent; 140 //<TODO> Move these constants into a wifi.proto Enum, and create a new Failure Type field 141 //covering more than just l2 failures. see b/27652362 142 /** 143 * Failure codes, used for the 'level_2_failure_code' Connection event field (covers a lot 144 * more failures than just l2 though, since the proto does not have a place to log 145 * framework failures) 146 */ 147 // Failure is unknown 148 public static final int FAILURE_UNKNOWN = 0; 149 // NONE 150 public static final int FAILURE_NONE = 1; 151 // ASSOCIATION_REJECTION_EVENT 152 public static final int FAILURE_ASSOCIATION_REJECTION = 2; 153 // AUTHENTICATION_FAILURE_EVENT 154 public static final int FAILURE_AUTHENTICATION_FAILURE = 3; 155 // SSID_TEMP_DISABLED (Also Auth failure) 156 public static final int FAILURE_SSID_TEMP_DISABLED = 4; 157 // reconnect() or reassociate() call to WifiNative failed 158 public static final int FAILURE_CONNECT_NETWORK_FAILED = 5; 159 // NETWORK_DISCONNECTION_EVENT 160 public static final int FAILURE_NETWORK_DISCONNECTION = 6; 161 // NEW_CONNECTION_ATTEMPT before previous finished 162 public static final int FAILURE_NEW_CONNECTION_ATTEMPT = 7; 163 // New connection attempt to the same network & bssid 164 public static final int FAILURE_REDUNDANT_CONNECTION_ATTEMPT = 8; 165 // Roam Watchdog timer triggered (Roaming timed out) 166 public static final int FAILURE_ROAM_TIMEOUT = 9; 167 // DHCP failure 168 public static final int FAILURE_DHCP = 10; 169 170 RouterFingerPrint mRouterFingerPrint; 171 private long mRealStartTime; 172 private long mRealEndTime; 173 private String mConfigSsid; 174 private String mConfigBssid; 175 private int mWifiState; 176 private boolean mScreenOn; 177 ConnectionEvent()178 private ConnectionEvent() { 179 mConnectionEvent = new WifiMetricsProto.ConnectionEvent(); 180 mRealEndTime = 0; 181 mRealStartTime = 0; 182 mRouterFingerPrint = new RouterFingerPrint(); 183 mConnectionEvent.routerFingerprint = mRouterFingerPrint.mRouterFingerPrintProto; 184 mConfigSsid = "<NULL>"; 185 mConfigBssid = "<NULL>"; 186 mWifiState = WifiMetricsProto.WifiLog.WIFI_UNKNOWN; 187 mScreenOn = false; 188 } 189 toString()190 public String toString() { 191 StringBuilder sb = new StringBuilder(); 192 sb.append("startTime="); 193 Calendar c = Calendar.getInstance(); 194 synchronized (mLock) { 195 c.setTimeInMillis(mConnectionEvent.startTimeMillis); 196 sb.append(mConnectionEvent.startTimeMillis == 0 ? " <null>" : 197 String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); 198 sb.append(", SSID="); 199 sb.append(mConfigSsid); 200 sb.append(", BSSID="); 201 sb.append(mConfigBssid); 202 sb.append(", durationMillis="); 203 sb.append(mConnectionEvent.durationTakenToConnectMillis); 204 sb.append(", roamType="); 205 switch(mConnectionEvent.roamType) { 206 case 1: 207 sb.append("ROAM_NONE"); 208 break; 209 case 2: 210 sb.append("ROAM_DBDC"); 211 break; 212 case 3: 213 sb.append("ROAM_ENTERPRISE"); 214 break; 215 case 4: 216 sb.append("ROAM_USER_SELECTED"); 217 break; 218 case 5: 219 sb.append("ROAM_UNRELATED"); 220 break; 221 default: 222 sb.append("ROAM_UNKNOWN"); 223 } 224 sb.append(", connectionResult="); 225 sb.append(mConnectionEvent.connectionResult); 226 sb.append(", level2FailureCode="); 227 switch(mConnectionEvent.level2FailureCode) { 228 case FAILURE_NONE: 229 sb.append("NONE"); 230 break; 231 case FAILURE_ASSOCIATION_REJECTION: 232 sb.append("ASSOCIATION_REJECTION"); 233 break; 234 case FAILURE_AUTHENTICATION_FAILURE: 235 sb.append("AUTHENTICATION_FAILURE"); 236 break; 237 case FAILURE_SSID_TEMP_DISABLED: 238 sb.append("SSID_TEMP_DISABLED"); 239 break; 240 case FAILURE_CONNECT_NETWORK_FAILED: 241 sb.append("CONNECT_NETWORK_FAILED"); 242 break; 243 case FAILURE_NETWORK_DISCONNECTION: 244 sb.append("NETWORK_DISCONNECTION"); 245 break; 246 case FAILURE_NEW_CONNECTION_ATTEMPT: 247 sb.append("NEW_CONNECTION_ATTEMPT"); 248 break; 249 case FAILURE_REDUNDANT_CONNECTION_ATTEMPT: 250 sb.append("REDUNDANT_CONNECTION_ATTEMPT"); 251 break; 252 case FAILURE_ROAM_TIMEOUT: 253 sb.append("ROAM_TIMEOUT"); 254 break; 255 case FAILURE_DHCP: 256 sb.append("DHCP"); 257 default: 258 sb.append("UNKNOWN"); 259 break; 260 } 261 sb.append(", connectivityLevelFailureCode="); 262 switch(mConnectionEvent.connectivityLevelFailureCode) { 263 case WifiMetricsProto.ConnectionEvent.HLF_NONE: 264 sb.append("NONE"); 265 break; 266 case WifiMetricsProto.ConnectionEvent.HLF_DHCP: 267 sb.append("DHCP"); 268 break; 269 case WifiMetricsProto.ConnectionEvent.HLF_NO_INTERNET: 270 sb.append("NO_INTERNET"); 271 break; 272 case WifiMetricsProto.ConnectionEvent.HLF_UNWANTED: 273 sb.append("UNWANTED"); 274 break; 275 default: 276 sb.append("UNKNOWN"); 277 break; 278 } 279 sb.append(", signalStrength="); 280 sb.append(mConnectionEvent.signalStrength); 281 sb.append(", wifiState="); 282 switch(mWifiState) { 283 case WifiMetricsProto.WifiLog.WIFI_DISABLED: 284 sb.append("WIFI_DISABLED"); 285 break; 286 case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED: 287 sb.append("WIFI_DISCONNECTED"); 288 break; 289 case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED: 290 sb.append("WIFI_ASSOCIATED"); 291 break; 292 default: 293 sb.append("WIFI_UNKNOWN"); 294 break; 295 } 296 sb.append(", screenOn="); 297 sb.append(mScreenOn); 298 sb.append(". mRouterFingerprint: "); 299 sb.append(mRouterFingerPrint.toString()); 300 } 301 return sb.toString(); 302 } 303 } 304 WifiMetrics(Clock clock)305 public WifiMetrics(Clock clock) { 306 mClock = clock; 307 mWifiLogProto = new WifiMetricsProto.WifiLog(); 308 mConnectionEventList = new ArrayList<>(); 309 mScanReturnEntries = new SparseIntArray(); 310 mWifiSystemStateEntries = new SparseIntArray(); 311 mCurrentConnectionEvent = null; 312 mScreenOn = true; 313 mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED; 314 mRecordStartTimeSec = mClock.elapsedRealtime() / 1000; 315 } 316 317 // Values used for indexing SystemStateEntries 318 private static final int SCREEN_ON = 1; 319 private static final int SCREEN_OFF = 0; 320 321 /** 322 * Create a new connection event. Call when wifi attempts to make a new network connection 323 * If there is a current 'un-ended' connection event, it will be ended with UNKNOWN connectivity 324 * failure code. 325 * Gathers and sets the RouterFingerPrint data as well 326 * 327 * @param config WifiConfiguration of the config used for the current connection attempt 328 * @param roamType Roam type that caused connection attempt, see WifiMetricsProto.WifiLog.ROAM_X 329 */ startConnectionEvent(WifiConfiguration config, String targetBSSID, int roamType)330 public void startConnectionEvent(WifiConfiguration config, String targetBSSID, int roamType) { 331 synchronized (mLock) { 332 // Check if this is overlapping another current connection event 333 if (mCurrentConnectionEvent != null) { 334 //Is this new Connection Event the same as the current one 335 if (mCurrentConnectionEvent.mConfigSsid != null 336 && mCurrentConnectionEvent.mConfigBssid != null 337 && config != null 338 && mCurrentConnectionEvent.mConfigSsid.equals(config.SSID) 339 && (mCurrentConnectionEvent.mConfigBssid.equals("any") 340 || mCurrentConnectionEvent.mConfigBssid.equals(targetBSSID))) { 341 mCurrentConnectionEvent.mConfigBssid = targetBSSID; 342 // End Connection Event due to new connection attempt to the same network 343 endConnectionEvent(ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT, 344 WifiMetricsProto.ConnectionEvent.HLF_NONE); 345 } else { 346 // End Connection Event due to new connection attempt to different network 347 endConnectionEvent(ConnectionEvent.FAILURE_NEW_CONNECTION_ATTEMPT, 348 WifiMetricsProto.ConnectionEvent.HLF_NONE); 349 } 350 } 351 //If past maximum connection events, start removing the oldest 352 while(mConnectionEventList.size() >= MAX_CONNECTION_EVENTS) { 353 mConnectionEventList.remove(0); 354 } 355 mCurrentConnectionEvent = new ConnectionEvent(); 356 mCurrentConnectionEvent.mConnectionEvent.startTimeMillis = 357 mClock.currentTimeMillis(); 358 mCurrentConnectionEvent.mConfigBssid = targetBSSID; 359 mCurrentConnectionEvent.mConnectionEvent.roamType = roamType; 360 mCurrentConnectionEvent.mRouterFingerPrint.updateFromWifiConfiguration(config); 361 mCurrentConnectionEvent.mConfigBssid = "any"; 362 mCurrentConnectionEvent.mRealStartTime = mClock.elapsedRealtime(); 363 mCurrentConnectionEvent.mWifiState = mWifiState; 364 mCurrentConnectionEvent.mScreenOn = mScreenOn; 365 mConnectionEventList.add(mCurrentConnectionEvent); 366 } 367 } 368 369 /** 370 * set the RoamType of the current ConnectionEvent (if any) 371 */ setConnectionEventRoamType(int roamType)372 public void setConnectionEventRoamType(int roamType) { 373 synchronized (mLock) { 374 if (mCurrentConnectionEvent != null) { 375 mCurrentConnectionEvent.mConnectionEvent.roamType = roamType; 376 } 377 } 378 } 379 380 /** 381 * Set AP related metrics from ScanDetail 382 */ setConnectionScanDetail(ScanDetail scanDetail)383 public void setConnectionScanDetail(ScanDetail scanDetail) { 384 synchronized (mLock) { 385 if (mCurrentConnectionEvent != null && scanDetail != null) { 386 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 387 ScanResult scanResult = scanDetail.getScanResult(); 388 //Ensure that we have a networkDetail, and that it corresponds to the currently 389 //tracked connection attempt 390 if (networkDetail != null && scanResult != null 391 && mCurrentConnectionEvent.mConfigSsid != null 392 && mCurrentConnectionEvent.mConfigSsid 393 .equals("\"" + networkDetail.getSSID() + "\"")) { 394 updateMetricsFromNetworkDetail(networkDetail); 395 updateMetricsFromScanResult(scanResult); 396 } 397 } 398 } 399 } 400 401 /** 402 * End a Connection event record. Call when wifi connection attempt succeeds or fails. 403 * If a Connection event has not been started and is active when .end is called, a new one is 404 * created with zero duration. 405 * 406 * @param level2FailureCode Level 2 failure code returned by supplicant 407 * @param connectivityFailureCode WifiMetricsProto.ConnectionEvent.HLF_X 408 */ endConnectionEvent(int level2FailureCode, int connectivityFailureCode)409 public void endConnectionEvent(int level2FailureCode, int connectivityFailureCode) { 410 synchronized (mLock) { 411 if (mCurrentConnectionEvent != null) { 412 boolean result = (level2FailureCode == 1) 413 && (connectivityFailureCode == WifiMetricsProto.ConnectionEvent.HLF_NONE); 414 mCurrentConnectionEvent.mConnectionEvent.connectionResult = result ? 1 : 0; 415 mCurrentConnectionEvent.mRealEndTime = mClock.elapsedRealtime(); 416 mCurrentConnectionEvent.mConnectionEvent.durationTakenToConnectMillis = (int) 417 (mCurrentConnectionEvent.mRealEndTime 418 - mCurrentConnectionEvent.mRealStartTime); 419 mCurrentConnectionEvent.mConnectionEvent.level2FailureCode = level2FailureCode; 420 mCurrentConnectionEvent.mConnectionEvent.connectivityLevelFailureCode = 421 connectivityFailureCode; 422 // ConnectionEvent already added to ConnectionEvents List. Safe to null current here 423 mCurrentConnectionEvent = null; 424 } 425 } 426 } 427 428 /** 429 * Set ConnectionEvent DTIM Interval (if set), and 802.11 Connection mode, from NetworkDetail 430 */ updateMetricsFromNetworkDetail(NetworkDetail networkDetail)431 private void updateMetricsFromNetworkDetail(NetworkDetail networkDetail) { 432 int dtimInterval = networkDetail.getDtimInterval(); 433 if (dtimInterval > 0) { 434 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.dtim = 435 dtimInterval; 436 } 437 int connectionWifiMode; 438 switch (networkDetail.getWifiMode()) { 439 case InformationElementUtil.WifiMode.MODE_UNDEFINED: 440 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_UNKNOWN; 441 break; 442 case InformationElementUtil.WifiMode.MODE_11A: 443 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_A; 444 break; 445 case InformationElementUtil.WifiMode.MODE_11B: 446 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_B; 447 break; 448 case InformationElementUtil.WifiMode.MODE_11G: 449 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_G; 450 break; 451 case InformationElementUtil.WifiMode.MODE_11N: 452 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_N; 453 break; 454 case InformationElementUtil.WifiMode.MODE_11AC : 455 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_AC; 456 break; 457 default: 458 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_OTHER; 459 break; 460 } 461 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto 462 .routerTechnology = connectionWifiMode; 463 } 464 465 /** 466 * Set ConnectionEvent RSSI and authentication type from ScanResult 467 */ updateMetricsFromScanResult(ScanResult scanResult)468 private void updateMetricsFromScanResult(ScanResult scanResult) { 469 mCurrentConnectionEvent.mConnectionEvent.signalStrength = scanResult.level; 470 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication = 471 WifiMetricsProto.RouterFingerPrint.AUTH_OPEN; 472 mCurrentConnectionEvent.mConfigBssid = scanResult.BSSID; 473 if (scanResult.capabilities != null) { 474 if (scanResult.capabilities.contains("WEP")) { 475 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication = 476 WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL; 477 } else if (scanResult.capabilities.contains("PSK")) { 478 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication = 479 WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL; 480 } else if (scanResult.capabilities.contains("EAP")) { 481 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication = 482 WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE; 483 } 484 } 485 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.channelInfo = 486 scanResult.frequency; 487 } 488 setNumSavedNetworks(int num)489 void setNumSavedNetworks(int num) { 490 synchronized (mLock) { 491 mWifiLogProto.numSavedNetworks = num; 492 } 493 } 494 setNumOpenNetworks(int num)495 void setNumOpenNetworks(int num) { 496 synchronized (mLock) { 497 mWifiLogProto.numOpenNetworks = num; 498 } 499 } 500 setNumPersonalNetworks(int num)501 void setNumPersonalNetworks(int num) { 502 synchronized (mLock) { 503 mWifiLogProto.numPersonalNetworks = num; 504 } 505 } 506 setNumEnterpriseNetworks(int num)507 void setNumEnterpriseNetworks(int num) { 508 synchronized (mLock) { 509 mWifiLogProto.numEnterpriseNetworks = num; 510 } 511 } 512 setNumNetworksAddedByUser(int num)513 void setNumNetworksAddedByUser(int num) { 514 synchronized (mLock) { 515 mWifiLogProto.numNetworksAddedByUser = num; 516 } 517 } 518 setNumNetworksAddedByApps(int num)519 void setNumNetworksAddedByApps(int num) { 520 synchronized (mLock) { 521 mWifiLogProto.numNetworksAddedByApps = num; 522 } 523 } 524 setIsLocationEnabled(boolean enabled)525 void setIsLocationEnabled(boolean enabled) { 526 synchronized (mLock) { 527 mWifiLogProto.isLocationEnabled = enabled; 528 } 529 } 530 setIsScanningAlwaysEnabled(boolean enabled)531 void setIsScanningAlwaysEnabled(boolean enabled) { 532 synchronized (mLock) { 533 mWifiLogProto.isScanningAlwaysEnabled = enabled; 534 } 535 } 536 537 /** 538 * Increment Non Empty Scan Results count 539 */ incrementNonEmptyScanResultCount()540 public void incrementNonEmptyScanResultCount() { 541 if (DBG) Log.v(TAG, "incrementNonEmptyScanResultCount"); 542 synchronized (mLock) { 543 mWifiLogProto.numNonEmptyScanResults++; 544 } 545 } 546 547 /** 548 * Increment Empty Scan Results count 549 */ incrementEmptyScanResultCount()550 public void incrementEmptyScanResultCount() { 551 if (DBG) Log.v(TAG, "incrementEmptyScanResultCount"); 552 synchronized (mLock) { 553 mWifiLogProto.numEmptyScanResults++; 554 } 555 } 556 557 /** 558 * Increment background scan count 559 */ incrementBackgroundScanCount()560 public void incrementBackgroundScanCount() { 561 if (DBG) Log.v(TAG, "incrementBackgroundScanCount"); 562 synchronized (mLock) { 563 mWifiLogProto.numBackgroundScans++; 564 } 565 } 566 567 /** 568 * Get Background scan count 569 */ getBackgroundScanCount()570 public int getBackgroundScanCount() { 571 synchronized (mLock) { 572 return mWifiLogProto.numBackgroundScans; 573 } 574 } 575 576 /** 577 * Increment oneshot scan count, and the associated WifiSystemScanStateCount entry 578 */ incrementOneshotScanCount()579 public void incrementOneshotScanCount() { 580 synchronized (mLock) { 581 mWifiLogProto.numOneshotScans++; 582 } 583 incrementWifiSystemScanStateCount(mWifiState, mScreenOn); 584 } 585 586 /** 587 * Get oneshot scan count 588 */ getOneshotScanCount()589 public int getOneshotScanCount() { 590 synchronized (mLock) { 591 return mWifiLogProto.numOneshotScans; 592 } 593 } 594 returnCodeToString(int scanReturnCode)595 private String returnCodeToString(int scanReturnCode) { 596 switch(scanReturnCode){ 597 case WifiMetricsProto.WifiLog.SCAN_UNKNOWN: 598 return "SCAN_UNKNOWN"; 599 case WifiMetricsProto.WifiLog.SCAN_SUCCESS: 600 return "SCAN_SUCCESS"; 601 case WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED: 602 return "SCAN_FAILURE_INTERRUPTED"; 603 case WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION: 604 return "SCAN_FAILURE_INVALID_CONFIGURATION"; 605 case WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED: 606 return "FAILURE_WIFI_DISABLED"; 607 default: 608 return "<UNKNOWN>"; 609 } 610 } 611 612 /** 613 * Increment count of scan return code occurrence 614 * 615 * @param scanReturnCode Return code from scan attempt WifiMetricsProto.WifiLog.SCAN_X 616 */ incrementScanReturnEntry(int scanReturnCode, int countToAdd)617 public void incrementScanReturnEntry(int scanReturnCode, int countToAdd) { 618 synchronized (mLock) { 619 if (DBG) Log.v(TAG, "incrementScanReturnEntry " + returnCodeToString(scanReturnCode)); 620 int entry = mScanReturnEntries.get(scanReturnCode); 621 entry += countToAdd; 622 mScanReturnEntries.put(scanReturnCode, entry); 623 } 624 } 625 /** 626 * Get the count of this scanReturnCode 627 * @param scanReturnCode that we are getting the count for 628 */ getScanReturnEntry(int scanReturnCode)629 public int getScanReturnEntry(int scanReturnCode) { 630 synchronized (mLock) { 631 return mScanReturnEntries.get(scanReturnCode); 632 } 633 } 634 wifiSystemStateToString(int state)635 private String wifiSystemStateToString(int state) { 636 switch(state){ 637 case WifiMetricsProto.WifiLog.WIFI_UNKNOWN: 638 return "WIFI_UNKNOWN"; 639 case WifiMetricsProto.WifiLog.WIFI_DISABLED: 640 return "WIFI_DISABLED"; 641 case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED: 642 return "WIFI_DISCONNECTED"; 643 case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED: 644 return "WIFI_ASSOCIATED"; 645 default: 646 return "default"; 647 } 648 } 649 650 /** 651 * Increments the count of scans initiated by each wifi state, accounts for screenOn/Off 652 * 653 * @param state State of the system when scan was initiated, see WifiMetricsProto.WifiLog.WIFI_X 654 * @param screenOn Is the screen on 655 */ incrementWifiSystemScanStateCount(int state, boolean screenOn)656 public void incrementWifiSystemScanStateCount(int state, boolean screenOn) { 657 synchronized (mLock) { 658 if (DBG) { 659 Log.v(TAG, "incrementWifiSystemScanStateCount " + wifiSystemStateToString(state) 660 + " " + screenOn); 661 } 662 int index = (state * 2) + (screenOn ? SCREEN_ON : SCREEN_OFF); 663 int entry = mWifiSystemStateEntries.get(index); 664 entry++; 665 mWifiSystemStateEntries.put(index, entry); 666 } 667 } 668 669 /** 670 * Get the count of this system State Entry 671 */ getSystemStateCount(int state, boolean screenOn)672 public int getSystemStateCount(int state, boolean screenOn) { 673 synchronized (mLock) { 674 int index = state * 2 + (screenOn ? SCREEN_ON : SCREEN_OFF); 675 return mWifiSystemStateEntries.get(index); 676 } 677 } 678 679 /** 680 * Increment number of times the Watchdog of Last Resort triggered, resetting the wifi stack 681 */ incrementNumLastResortWatchdogTriggers()682 public void incrementNumLastResortWatchdogTriggers() { 683 synchronized (mLock) { 684 mWifiLogProto.numLastResortWatchdogTriggers++; 685 } 686 } 687 /** 688 * @param count number of networks over bad association threshold when watchdog triggered 689 */ addCountToNumLastResortWatchdogBadAssociationNetworksTotal(int count)690 public void addCountToNumLastResortWatchdogBadAssociationNetworksTotal(int count) { 691 synchronized (mLock) { 692 mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal += count; 693 } 694 } 695 /** 696 * @param count number of networks over bad authentication threshold when watchdog triggered 697 */ addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(int count)698 public void addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(int count) { 699 synchronized (mLock) { 700 mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal += count; 701 } 702 } 703 /** 704 * @param count number of networks over bad dhcp threshold when watchdog triggered 705 */ addCountToNumLastResortWatchdogBadDhcpNetworksTotal(int count)706 public void addCountToNumLastResortWatchdogBadDhcpNetworksTotal(int count) { 707 synchronized (mLock) { 708 mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal += count; 709 } 710 } 711 /** 712 * @param count number of networks over bad other threshold when watchdog triggered 713 */ addCountToNumLastResortWatchdogBadOtherNetworksTotal(int count)714 public void addCountToNumLastResortWatchdogBadOtherNetworksTotal(int count) { 715 synchronized (mLock) { 716 mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal += count; 717 } 718 } 719 /** 720 * @param count number of networks seen when watchdog triggered 721 */ addCountToNumLastResortWatchdogAvailableNetworksTotal(int count)722 public void addCountToNumLastResortWatchdogAvailableNetworksTotal(int count) { 723 synchronized (mLock) { 724 mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal += count; 725 } 726 } 727 /** 728 * Increment count of triggers with atleast one bad association network 729 */ incrementNumLastResortWatchdogTriggersWithBadAssociation()730 public void incrementNumLastResortWatchdogTriggersWithBadAssociation() { 731 synchronized (mLock) { 732 mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation++; 733 } 734 } 735 /** 736 * Increment count of triggers with atleast one bad authentication network 737 */ incrementNumLastResortWatchdogTriggersWithBadAuthentication()738 public void incrementNumLastResortWatchdogTriggersWithBadAuthentication() { 739 synchronized (mLock) { 740 mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication++; 741 } 742 } 743 /** 744 * Increment count of triggers with atleast one bad dhcp network 745 */ incrementNumLastResortWatchdogTriggersWithBadDhcp()746 public void incrementNumLastResortWatchdogTriggersWithBadDhcp() { 747 synchronized (mLock) { 748 mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp++; 749 } 750 } 751 /** 752 * Increment count of triggers with atleast one bad other network 753 */ incrementNumLastResortWatchdogTriggersWithBadOther()754 public void incrementNumLastResortWatchdogTriggersWithBadOther() { 755 synchronized (mLock) { 756 mWifiLogProto.numLastResortWatchdogTriggersWithBadOther++; 757 } 758 } 759 760 /** 761 * Increment number of times connectivity watchdog confirmed pno is working 762 */ incrementNumConnectivityWatchdogPnoGood()763 public void incrementNumConnectivityWatchdogPnoGood() { 764 synchronized (mLock) { 765 mWifiLogProto.numConnectivityWatchdogPnoGood++; 766 } 767 } 768 /** 769 * Increment number of times connectivity watchdog found pno not working 770 */ incrementNumConnectivityWatchdogPnoBad()771 public void incrementNumConnectivityWatchdogPnoBad() { 772 synchronized (mLock) { 773 mWifiLogProto.numConnectivityWatchdogPnoBad++; 774 } 775 } 776 /** 777 * Increment number of times connectivity watchdog confirmed background scan is working 778 */ incrementNumConnectivityWatchdogBackgroundGood()779 public void incrementNumConnectivityWatchdogBackgroundGood() { 780 synchronized (mLock) { 781 mWifiLogProto.numConnectivityWatchdogBackgroundGood++; 782 } 783 } 784 /** 785 * Increment number of times connectivity watchdog found background scan not working 786 */ incrementNumConnectivityWatchdogBackgroundBad()787 public void incrementNumConnectivityWatchdogBackgroundBad() { 788 synchronized (mLock) { 789 mWifiLogProto.numConnectivityWatchdogBackgroundBad++; 790 } 791 } 792 793 public static final String PROTO_DUMP_ARG = "wifiMetricsProto"; 794 /** 795 * Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager 796 * at this time 797 * 798 * @param fd unused 799 * @param pw PrintWriter for writing dump to 800 * @param args unused 801 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)802 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 803 synchronized (mLock) { 804 pw.println("WifiMetrics:"); 805 if (args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) { 806 //Dump serialized WifiLog proto 807 consolidateProto(true); 808 for (ConnectionEvent event : mConnectionEventList) { 809 if (mCurrentConnectionEvent != event) { 810 //indicate that automatic bug report has been taken for all valid 811 //connection events 812 event.mConnectionEvent.automaticBugReportTaken = true; 813 } 814 } 815 byte[] wifiMetricsProto = WifiMetricsProto.WifiLog.toByteArray(mWifiLogProto); 816 String metricsProtoDump = Base64.encodeToString(wifiMetricsProto, Base64.DEFAULT); 817 pw.println(metricsProtoDump); 818 pw.println("EndWifiMetrics"); 819 clear(); 820 } else { 821 pw.println("mConnectionEvents:"); 822 for (ConnectionEvent event : mConnectionEventList) { 823 String eventLine = event.toString(); 824 if (event == mCurrentConnectionEvent) { 825 eventLine += "CURRENTLY OPEN EVENT"; 826 } 827 pw.println(eventLine); 828 } 829 pw.println("mWifiLogProto.numSavedNetworks=" + mWifiLogProto.numSavedNetworks); 830 pw.println("mWifiLogProto.numOpenNetworks=" + mWifiLogProto.numOpenNetworks); 831 pw.println("mWifiLogProto.numPersonalNetworks=" 832 + mWifiLogProto.numPersonalNetworks); 833 pw.println("mWifiLogProto.numEnterpriseNetworks=" 834 + mWifiLogProto.numEnterpriseNetworks); 835 pw.println("mWifiLogProto.isLocationEnabled=" + mWifiLogProto.isLocationEnabled); 836 pw.println("mWifiLogProto.isScanningAlwaysEnabled=" 837 + mWifiLogProto.isScanningAlwaysEnabled); 838 pw.println("mWifiLogProto.numNetworksAddedByUser=" 839 + mWifiLogProto.numNetworksAddedByUser); 840 pw.println("mWifiLogProto.numNetworksAddedByApps=" 841 + mWifiLogProto.numNetworksAddedByApps); 842 pw.println("mWifiLogProto.numNonEmptyScanResults=" 843 + mWifiLogProto.numNonEmptyScanResults); 844 pw.println("mWifiLogProto.numEmptyScanResults=" 845 + mWifiLogProto.numEmptyScanResults); 846 pw.println("mWifiLogProto.numOneshotScans=" 847 + mWifiLogProto.numOneshotScans); 848 pw.println("mWifiLogProto.numBackgroundScans=" 849 + mWifiLogProto.numBackgroundScans); 850 851 pw.println("mScanReturnEntries:"); 852 pw.println(" SCAN_UNKNOWN: " + getScanReturnEntry( 853 WifiMetricsProto.WifiLog.SCAN_UNKNOWN)); 854 pw.println(" SCAN_SUCCESS: " + getScanReturnEntry( 855 WifiMetricsProto.WifiLog.SCAN_SUCCESS)); 856 pw.println(" SCAN_FAILURE_INTERRUPTED: " + getScanReturnEntry( 857 WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED)); 858 pw.println(" SCAN_FAILURE_INVALID_CONFIGURATION: " + getScanReturnEntry( 859 WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION)); 860 pw.println(" FAILURE_WIFI_DISABLED: " + getScanReturnEntry( 861 WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED)); 862 863 pw.println("mSystemStateEntries: <state><screenOn> : <scansInitiated>"); 864 pw.println(" WIFI_UNKNOWN ON: " 865 + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, true)); 866 pw.println(" WIFI_DISABLED ON: " 867 + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, true)); 868 pw.println(" WIFI_DISCONNECTED ON: " 869 + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, true)); 870 pw.println(" WIFI_ASSOCIATED ON: " 871 + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, true)); 872 pw.println(" WIFI_UNKNOWN OFF: " 873 + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, false)); 874 pw.println(" WIFI_DISABLED OFF: " 875 + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, false)); 876 pw.println(" WIFI_DISCONNECTED OFF: " 877 + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, false)); 878 pw.println(" WIFI_ASSOCIATED OFF: " 879 + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, false)); 880 pw.println("mWifiLogProto.numConnectivityWatchdogPnoGood=" 881 + mWifiLogProto.numConnectivityWatchdogPnoGood); 882 pw.println("mWifiLogProto.numConnectivityWatchdogPnoBad=" 883 + mWifiLogProto.numConnectivityWatchdogPnoBad); 884 pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundGood=" 885 + mWifiLogProto.numConnectivityWatchdogBackgroundGood); 886 pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundBad=" 887 + mWifiLogProto.numConnectivityWatchdogBackgroundBad); 888 pw.println("mWifiLogProto.numLastResortWatchdogTriggers=" 889 + mWifiLogProto.numLastResortWatchdogTriggers); 890 pw.println("mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal=" 891 + mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal); 892 pw.println("mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal=" 893 + mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal); 894 pw.println("mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal=" 895 + mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal); 896 pw.println("mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal=" 897 + mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal); 898 pw.println("mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal=" 899 + mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal); 900 pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation=" 901 + mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation); 902 pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication=" 903 + mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication); 904 pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp=" 905 + mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp); 906 pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadOther=" 907 + mWifiLogProto.numLastResortWatchdogTriggersWithBadOther); 908 pw.println("mWifiLogProto.recordDurationSec=" 909 + ((mClock.elapsedRealtime() / 1000) - mRecordStartTimeSec)); 910 } 911 } 912 } 913 914 /** 915 * append the separate ConnectionEvent, SystemStateEntry and ScanReturnCode collections to their 916 * respective lists within mWifiLogProto 917 * 918 * @param incremental Only include ConnectionEvents created since last automatic bug report 919 */ consolidateProto(boolean incremental)920 private void consolidateProto(boolean incremental) { 921 List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>(); 922 synchronized (mLock) { 923 for (ConnectionEvent event : mConnectionEventList) { 924 // If this is not incremental, dump full ConnectionEvent list 925 // Else Dump all un-dumped events except for the current one 926 if (!incremental || ((mCurrentConnectionEvent != event) 927 && !event.mConnectionEvent.automaticBugReportTaken)) { 928 //Get all ConnectionEvents that haven not been dumped as a proto, also exclude 929 //the current active un-ended connection event 930 events.add(event.mConnectionEvent); 931 if (incremental) { 932 event.mConnectionEvent.automaticBugReportTaken = true; 933 } 934 } 935 } 936 if (events.size() > 0) { 937 mWifiLogProto.connectionEvent = events.toArray(mWifiLogProto.connectionEvent); 938 } 939 940 //Convert the SparseIntArray of scanReturnEntry integers into ScanReturnEntry proto list 941 mWifiLogProto.scanReturnEntries = 942 new WifiMetricsProto.WifiLog.ScanReturnEntry[mScanReturnEntries.size()]; 943 for (int i = 0; i < mScanReturnEntries.size(); i++) { 944 mWifiLogProto.scanReturnEntries[i] = new WifiMetricsProto.WifiLog.ScanReturnEntry(); 945 mWifiLogProto.scanReturnEntries[i].scanReturnCode = mScanReturnEntries.keyAt(i); 946 mWifiLogProto.scanReturnEntries[i].scanResultsCount = mScanReturnEntries.valueAt(i); 947 } 948 949 // Convert the SparseIntArray of systemStateEntry into WifiSystemStateEntry proto list 950 // This one is slightly more complex, as the Sparse are indexed with: 951 // key: wifiState * 2 + isScreenOn, value: wifiStateCount 952 mWifiLogProto.wifiSystemStateEntries = 953 new WifiMetricsProto.WifiLog 954 .WifiSystemStateEntry[mWifiSystemStateEntries.size()]; 955 for (int i = 0; i < mWifiSystemStateEntries.size(); i++) { 956 mWifiLogProto.wifiSystemStateEntries[i] = 957 new WifiMetricsProto.WifiLog.WifiSystemStateEntry(); 958 mWifiLogProto.wifiSystemStateEntries[i].wifiState = 959 mWifiSystemStateEntries.keyAt(i) / 2; 960 mWifiLogProto.wifiSystemStateEntries[i].wifiStateCount = 961 mWifiSystemStateEntries.valueAt(i); 962 mWifiLogProto.wifiSystemStateEntries[i].isScreenOn = 963 (mWifiSystemStateEntries.keyAt(i) % 2) > 0; 964 } 965 mWifiLogProto.recordDurationSec = (int) ((mClock.elapsedRealtime() / 1000) 966 - mRecordStartTimeSec); 967 } 968 } 969 970 /** 971 * Clear all WifiMetrics, except for currentConnectionEvent. 972 */ clear()973 private void clear() { 974 synchronized (mLock) { 975 mConnectionEventList.clear(); 976 if (mCurrentConnectionEvent != null) { 977 mConnectionEventList.add(mCurrentConnectionEvent); 978 } 979 mScanReturnEntries.clear(); 980 mWifiSystemStateEntries.clear(); 981 mRecordStartTimeSec = mClock.elapsedRealtime() / 1000; 982 mWifiLogProto.clear(); 983 } 984 } 985 986 /** 987 * Set screen state (On/Off) 988 */ setScreenState(boolean screenOn)989 public void setScreenState(boolean screenOn) { 990 synchronized (mLock) { 991 mScreenOn = screenOn; 992 } 993 } 994 995 /** 996 * Set wifi state (WIFI_UNKNOWN, WIFI_DISABLED, WIFI_DISCONNECTED, WIFI_ASSOCIATED) 997 */ setWifiState(int wifiState)998 public void setWifiState(int wifiState) { 999 synchronized (mLock) { 1000 mWifiState = wifiState; 1001 } 1002 } 1003 } 1004