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