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