WifiMetrics.java revision 96cdfc0acafef88832515ecc52c01638c1493211
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.SparseArray; 24 25import com.android.server.wifi.hotspot2.NetworkDetail; 26import com.android.server.wifi.util.InformationElementUtil; 27 28import java.io.FileDescriptor; 29import java.io.PrintWriter; 30import java.util.ArrayList; 31import java.util.Calendar; 32import 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 */ 42public class WifiMetrics { 43 private static final String TAG = "WifiMetrics"; 44 private final Object mLock = new Object(); 45 private static final int MAX_CONNECTION_EVENTS = 256; 46 /** 47 * Metrics are stored within an instance of the WifiLog proto during runtime, 48 * The ConnectionEvent, SystemStateEntries & ScanReturnEntries metrics are stored during 49 * runtime in member lists of this WifiMetrics class, with the final WifiLog proto being pieced 50 * together at dump-time 51 */ 52 private final WifiMetricsProto.WifiLog mWifiLogProto; 53 /** 54 * Session information that gets logged for every Wifi connection attempt. 55 */ 56 private final List<ConnectionEvent> mConnectionEventList; 57 /** 58 * The latest started (but un-ended) connection attempt 59 */ 60 private ConnectionEvent mCurrentConnectionEvent; 61 /** 62 * Count of number of times each scan return code, indexed by WifiLog.ScanReturnCode 63 */ 64 private final SparseArray<WifiMetricsProto.WifiLog.ScanReturnEntry> mScanReturnEntries; 65 /** 66 * Mapping of system state to the counts of scans requested in that wifi state * screenOn 67 * combination. Indexed by WifiLog.WifiState * (1 + screenOn) 68 */ 69 private final SparseArray<WifiMetricsProto.WifiLog.WifiSystemStateEntry> 70 mWifiSystemStateEntries; 71 72 class RouterFingerPrint { 73 private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto; 74 RouterFingerPrint() { 75 mRouterFingerPrintProto = new WifiMetricsProto.RouterFingerPrint(); 76 } 77 78 public String toString() { 79 StringBuilder sb = new StringBuilder(); 80 synchronized (mLock) { 81 sb.append("mConnectionEvent.roamType=" + mRouterFingerPrintProto.roamType); 82 sb.append(", mChannelInfo=" + mRouterFingerPrintProto.channelInfo); 83 sb.append(", mDtim=" + mRouterFingerPrintProto.dtim); 84 sb.append(", mAuthentication=" + mRouterFingerPrintProto.authentication); 85 sb.append(", mHidden=" + mRouterFingerPrintProto.hidden); 86 sb.append(", mRouterTechnology=" + mRouterFingerPrintProto.routerTechnology); 87 sb.append(", mSupportsIpv6=" + mRouterFingerPrintProto.supportsIpv6); 88 } 89 return sb.toString(); 90 } 91 public void updateFromWifiConfiguration(WifiConfiguration config) { 92 if (config != null) { 93 // Is this a hidden network 94 mRouterFingerPrintProto.hidden = config.hiddenSSID; 95 // Config may not have a valid dtimInterval set yet, in which case dtim will be zero 96 // (These are only populated from beacon frame scan results, which are returned as 97 // scan results from the chip far less frequently than Probe-responses) 98 if (config.dtimInterval > 0) { 99 mRouterFingerPrintProto.dtim = config.dtimInterval; 100 } 101 mCurrentConnectionEvent.mConfigSsid = config.SSID; 102 // Get AuthType information from config (We do this again from ScanResult after 103 // associating with BSSID) 104 if (config.allowedKeyManagement != null 105 && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) { 106 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto 107 .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_OPEN; 108 } else if (config.isEnterprise()) { 109 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto 110 .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE; 111 } else { 112 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto 113 .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL; 114 } 115 // If there's a ScanResult candidate associated with this config already, get it and 116 // log (more accurate) metrics from it 117 ScanResult candidate = config.getNetworkSelectionStatus().getCandidate(); 118 if (candidate != null) { 119 updateMetricsFromScanResult(candidate); 120 } 121 } 122 } 123 } 124 125 /** 126 * Log event, tracking the start time, end time and result of a wireless connection attempt. 127 */ 128 class ConnectionEvent { 129 WifiMetricsProto.ConnectionEvent mConnectionEvent; 130 //<TODO> Move these constants into a wifi.proto Enum, and create a new Failure Type field 131 //covering more than just l2 failures. see b/27652362 132 /** 133 * Failure codes, used for the 'level_2_failure_code' Connection event field (covers a lot 134 * more failures than just l2 though, since the proto does not have a place to log 135 * framework failures) 136 */ 137 // Failure is unknown 138 public static final int FAILURE_UNKNOWN = 0; 139 // NONE 140 public static final int FAILURE_NONE = 1; 141 // ASSOCIATION_REJECTION_EVENT 142 public static final int FAILURE_ASSOCIATION_REJECTION = 2; 143 // AUTHENTICATION_FAILURE_EVENT 144 public static final int FAILURE_AUTHENTICATION_FAILURE = 3; 145 // SSID_TEMP_DISABLED (Also Auth failure) 146 public static final int FAILURE_SSID_TEMP_DISABLED = 4; 147 // reconnect() or reassociate() call to WifiNative failed 148 public static final int FAILURE_CONNECT_NETWORK_FAILED = 5; 149 // NETWORK_DISCONNECTION_EVENT 150 public static final int FAILURE_NETWORK_DISCONNECTION = 6; 151 // NEW_CONNECTION_ATTEMPT before previous finished 152 public static final int FAILURE_NEW_CONNECTION_ATTEMPT = 7; 153 // New connection attempt to the same network & bssid 154 public static final int FAILURE_REDUNDANT_CONNECTION_ATTEMPT = 8; 155 // Roam Watchdog timer triggered (Roaming timed out) 156 public static final int FAILURE_ROAM_TIMEOUT = 9; 157 // DHCP failure 158 public static final int FAILURE_DHCP = 10; 159 160 RouterFingerPrint mRouterFingerPrint; 161 private long mRealStartTime; 162 private long mRealEndTime; 163 private String mConfigSsid; 164 private String mConfigBssid; 165 166 private ConnectionEvent() { 167 mConnectionEvent = new WifiMetricsProto.ConnectionEvent(); 168 mRealEndTime = 0; 169 mRealStartTime = 0; 170 mRouterFingerPrint = new RouterFingerPrint(); 171 mConnectionEvent.routerFingerprint = mRouterFingerPrint.mRouterFingerPrintProto; 172 mConfigSsid = "<NULL>"; 173 mConfigBssid = "<NULL>"; 174 } 175 176 public String toString() { 177 StringBuilder sb = new StringBuilder(); 178 sb.append("startTime="); 179 Calendar c = Calendar.getInstance(); 180 synchronized (mLock) { 181 c.setTimeInMillis(mConnectionEvent.startTimeMillis); 182 sb.append(mConnectionEvent.startTimeMillis == 0 ? " <null>" : 183 String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); 184 sb.append(", SSID="); 185 sb.append(mConfigSsid); 186 sb.append(", BSSID="); 187 sb.append(mConfigBssid); 188 sb.append(", durationMillis="); 189 sb.append(mConnectionEvent.durationTakenToConnectMillis); 190 sb.append(", roamType="); 191 switch(mConnectionEvent.roamType) { 192 case 1: 193 sb.append("ROAM_NONE"); 194 break; 195 case 2: 196 sb.append("ROAM_DBDC"); 197 break; 198 case 3: 199 sb.append("ROAM_ENTERPRISE"); 200 break; 201 case 4: 202 sb.append("ROAM_USER_SELECTED"); 203 break; 204 case 5: 205 sb.append("ROAM_UNRELATED"); 206 break; 207 default: 208 sb.append("ROAM_UNKNOWN"); 209 } 210 sb.append(", connectionResult="); 211 sb.append(mConnectionEvent.connectionResult); 212 sb.append(", level2FailureCode="); 213 switch(mConnectionEvent.level2FailureCode) { 214 case FAILURE_NONE: 215 sb.append("NONE"); 216 break; 217 case FAILURE_ASSOCIATION_REJECTION: 218 sb.append("ASSOCIATION_REJECTION"); 219 break; 220 case FAILURE_AUTHENTICATION_FAILURE: 221 sb.append("AUTHENTICATION_FAILURE"); 222 break; 223 case FAILURE_SSID_TEMP_DISABLED: 224 sb.append("SSID_TEMP_DISABLED"); 225 break; 226 case FAILURE_CONNECT_NETWORK_FAILED: 227 sb.append("CONNECT_NETWORK_FAILED"); 228 break; 229 case FAILURE_NETWORK_DISCONNECTION: 230 sb.append("NETWORK_DISCONNECTION"); 231 break; 232 case FAILURE_NEW_CONNECTION_ATTEMPT: 233 sb.append("NEW_CONNECTION_ATTEMPT"); 234 break; 235 case FAILURE_REDUNDANT_CONNECTION_ATTEMPT: 236 sb.append("REDUNDANT_CONNECTION_ATTEMPT"); 237 break; 238 case FAILURE_ROAM_TIMEOUT: 239 sb.append("ROAM_TIMEOUT"); 240 break; 241 case FAILURE_DHCP: 242 sb.append("DHCP"); 243 default: 244 sb.append("UNKNOWN"); 245 break; 246 } 247 sb.append(", connectivityLevelFailureCode="); 248 switch(mConnectionEvent.connectivityLevelFailureCode) { 249 case WifiMetricsProto.ConnectionEvent.HLF_NONE: 250 sb.append("NONE"); 251 break; 252 case WifiMetricsProto.ConnectionEvent.HLF_DHCP: 253 sb.append("DHCP"); 254 break; 255 case WifiMetricsProto.ConnectionEvent.HLF_NO_INTERNET: 256 sb.append("NO_INTERNET"); 257 break; 258 case WifiMetricsProto.ConnectionEvent.HLF_UNWANTED: 259 sb.append("UNWANTED"); 260 break; 261 default: 262 sb.append("UNKNOWN"); 263 break; 264 } 265 sb.append(", signalStrength="); 266 sb.append(mConnectionEvent.signalStrength); 267 sb.append("\n "); 268 sb.append("mRouterFingerprint: "); 269 sb.append(mRouterFingerPrint.toString()); 270 } 271 return sb.toString(); 272 } 273 } 274 275 public WifiMetrics() { 276 mWifiLogProto = new WifiMetricsProto.WifiLog(); 277 mConnectionEventList = new ArrayList<>(); 278 mCurrentConnectionEvent = null; 279 mScanReturnEntries = new SparseArray<WifiMetricsProto.WifiLog.ScanReturnEntry>(); 280 mWifiSystemStateEntries = new SparseArray<WifiMetricsProto.WifiLog.WifiSystemStateEntry>(); 281 } 282 283 /** 284 * Create a new connection event. Call when wifi attempts to make a new network connection 285 * If there is a current 'un-ended' connection event, it will be ended with UNKNOWN connectivity 286 * failure code. 287 * Gathers and sets the RouterFingerPrint data as well 288 * 289 * @param config WifiConfiguration of the config used for the current connection attempt 290 * @param roamType Roam type that caused connection attempt, see WifiMetricsProto.WifiLog.ROAM_X 291 */ 292 public void startConnectionEvent(WifiConfiguration config, String targetBSSID, int roamType) { 293 synchronized (mLock) { 294 // Check if this is overlapping another current connection event 295 if (mCurrentConnectionEvent != null) { 296 //Is this new Connection Event the same as the current one 297 if (mCurrentConnectionEvent.mConfigSsid != null 298 && mCurrentConnectionEvent.mConfigBssid != null 299 && config != null 300 && mCurrentConnectionEvent.mConfigSsid.equals(config.SSID) 301 && (mCurrentConnectionEvent.mConfigBssid.equals("any") 302 || mCurrentConnectionEvent.mConfigBssid.equals(targetBSSID))) { 303 mCurrentConnectionEvent.mConfigBssid = targetBSSID; 304 // End Connection Event due to new connection attempt to the same network 305 endConnectionEvent(ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT, 306 WifiMetricsProto.ConnectionEvent.HLF_NONE); 307 } else { 308 // End Connection Event due to new connection attempt to different network 309 endConnectionEvent(ConnectionEvent.FAILURE_NEW_CONNECTION_ATTEMPT, 310 WifiMetricsProto.ConnectionEvent.HLF_NONE); 311 } 312 } 313 //If past maximum connection events, start removing the oldest 314 while(mConnectionEventList.size() >= MAX_CONNECTION_EVENTS) { 315 mConnectionEventList.remove(0); 316 } 317 mCurrentConnectionEvent = new ConnectionEvent(); 318 mCurrentConnectionEvent.mConnectionEvent.startTimeMillis = 319 System.currentTimeMillis(); 320 mCurrentConnectionEvent.mConfigBssid = targetBSSID; 321 mCurrentConnectionEvent.mConnectionEvent.roamType = roamType; 322 mCurrentConnectionEvent.mRouterFingerPrint.updateFromWifiConfiguration(config); 323 mCurrentConnectionEvent.mConfigBssid = "any"; 324 mCurrentConnectionEvent.mRealStartTime = SystemClock.elapsedRealtime(); 325 mConnectionEventList.add(mCurrentConnectionEvent); 326 } 327 } 328 329 /** 330 * set the RoamType of the current ConnectionEvent (if any) 331 */ 332 public void setConnectionEventRoamType(int roamType) { 333 if (mCurrentConnectionEvent != null) { 334 mCurrentConnectionEvent.mConnectionEvent.roamType = roamType; 335 } 336 } 337 338 /** 339 * Set AP related metrics from ScanDetail 340 */ 341 public void setConnectionScanDetail(ScanDetail scanDetail) { 342 if (mCurrentConnectionEvent != null && scanDetail != null) { 343 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 344 ScanResult scanResult = scanDetail.getScanResult(); 345 //Ensure that we have a networkDetail, and that it corresponds to the currently 346 //tracked connection attempt 347 if (networkDetail != null && scanResult != null 348 && mCurrentConnectionEvent.mConfigSsid != null 349 && mCurrentConnectionEvent.mConfigSsid 350 .equals("\"" + networkDetail.getSSID() + "\"")) { 351 updateMetricsFromNetworkDetail(networkDetail); 352 updateMetricsFromScanResult(scanResult); 353 } 354 } 355 } 356 357 /** 358 * End a Connection event record. Call when wifi connection attempt succeeds or fails. 359 * If a Connection event has not been started and is active when .end is called, a new one is 360 * created with zero duration. 361 * 362 * @param level2FailureCode Level 2 failure code returned by supplicant 363 * @param connectivityFailureCode WifiMetricsProto.ConnectionEvent.HLF_X 364 */ 365 public void endConnectionEvent(int level2FailureCode, int connectivityFailureCode) { 366 synchronized (mLock) { 367 if (mCurrentConnectionEvent != null) { 368 boolean result = (level2FailureCode == 1) 369 && (connectivityFailureCode == WifiMetricsProto.ConnectionEvent.HLF_NONE); 370 mCurrentConnectionEvent.mConnectionEvent.connectionResult = result ? 1 : 0; 371 mCurrentConnectionEvent.mRealEndTime = SystemClock.elapsedRealtime(); 372 mCurrentConnectionEvent.mConnectionEvent.durationTakenToConnectMillis = (int) 373 (mCurrentConnectionEvent.mRealEndTime 374 - mCurrentConnectionEvent.mRealStartTime); 375 mCurrentConnectionEvent.mConnectionEvent.level2FailureCode = level2FailureCode; 376 mCurrentConnectionEvent.mConnectionEvent.connectivityLevelFailureCode = 377 connectivityFailureCode; 378 // ConnectionEvent already added to ConnectionEvents List. Safe to null current here 379 mCurrentConnectionEvent = null; 380 } 381 } 382 } 383 384 /** 385 * Set ConnectionEvent DTIM Interval (if set), and 802.11 Connection mode, from NetworkDetail 386 */ 387 private void updateMetricsFromNetworkDetail(NetworkDetail networkDetail) { 388 int dtimInterval = networkDetail.getDtimInterval(); 389 if (dtimInterval > 0) { 390 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.dtim = 391 dtimInterval; 392 } 393 int connectionWifiMode; 394 switch (networkDetail.getWifiMode()) { 395 case InformationElementUtil.WifiMode.MODE_UNDEFINED: 396 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_UNKNOWN; 397 break; 398 case InformationElementUtil.WifiMode.MODE_11A: 399 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_A; 400 break; 401 case InformationElementUtil.WifiMode.MODE_11B: 402 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_B; 403 break; 404 case InformationElementUtil.WifiMode.MODE_11G: 405 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_G; 406 break; 407 case InformationElementUtil.WifiMode.MODE_11N: 408 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_N; 409 break; 410 case InformationElementUtil.WifiMode.MODE_11AC : 411 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_AC; 412 break; 413 default: 414 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_OTHER; 415 break; 416 } 417 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto 418 .routerTechnology = connectionWifiMode; 419 } 420 421 /** 422 * Set ConnectionEvent RSSI and authentication type from ScanResult 423 */ 424 private void updateMetricsFromScanResult(ScanResult scanResult) { 425 mCurrentConnectionEvent.mConnectionEvent.signalStrength = scanResult.level; 426 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication = 427 WifiMetricsProto.RouterFingerPrint.AUTH_OPEN; 428 mCurrentConnectionEvent.mConfigBssid = scanResult.BSSID; 429 if (scanResult.capabilities != null) { 430 if (scanResult.capabilities.contains("WEP")) { 431 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication = 432 WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL; 433 } else if (scanResult.capabilities.contains("PSK")) { 434 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication = 435 WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL; 436 } else if (scanResult.capabilities.contains("EAP")) { 437 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication = 438 WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE; 439 } 440 } 441 mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.channelInfo = 442 scanResult.frequency; 443 } 444 445 void setNumSavedNetworks(int num) { 446 synchronized (mLock) { 447 mWifiLogProto.numSavedNetworks = num; 448 } 449 } 450 451 void setNumOpenNetworks(int num) { 452 synchronized (mLock) { 453 mWifiLogProto.numOpenNetworks = num; 454 } 455 } 456 457 void setNumPersonalNetworks(int num) { 458 synchronized (mLock) { 459 mWifiLogProto.numPersonalNetworks = num; 460 } 461 } 462 463 void setNumEnterpriseNetworks(int num) { 464 synchronized (mLock) { 465 mWifiLogProto.numEnterpriseNetworks = num; 466 } 467 } 468 469 void setNumNetworksAddedByUser(int num) { 470 synchronized (mLock) { 471 mWifiLogProto.numNetworksAddedByUser = num; 472 } 473 } 474 475 void setNumNetworksAddedByApps(int num) { 476 synchronized (mLock) { 477 mWifiLogProto.numNetworksAddedByApps = num; 478 } 479 } 480 481 void setIsLocationEnabled(boolean enabled) { 482 synchronized (mLock) { 483 mWifiLogProto.isLocationEnabled = enabled; 484 } 485 } 486 487 void setIsScanningAlwaysEnabled(boolean enabled) { 488 synchronized (mLock) { 489 mWifiLogProto.isScanningAlwaysEnabled = enabled; 490 } 491 } 492 493 /** 494 * Increment Non Empty Scan Results count 495 */ 496 public void incrementNonEmptyScanResultCount() { 497 synchronized (mLock) { 498 mWifiLogProto.numNonEmptyScanResults++; 499 } 500 } 501 502 /** 503 * Increment Empty Scan Results count 504 */ 505 public void incrementEmptyScanResultCount() { 506 synchronized (mLock) { 507 mWifiLogProto.numEmptyScanResults++; 508 } 509 } 510 511 /** 512 * Increment count of scan return code occurrence 513 * 514 * @param scanReturnCode Return code from scan attempt WifiMetricsProto.WifiLog.SCAN_X 515 */ 516 public void incrementScanReturnEntry(int scanReturnCode) { 517 synchronized (mLock) { 518 WifiMetricsProto.WifiLog.ScanReturnEntry entry = mScanReturnEntries.get(scanReturnCode); 519 if (entry == null) { 520 entry = new WifiMetricsProto.WifiLog.ScanReturnEntry(); 521 entry.scanReturnCode = scanReturnCode; 522 entry.scanResultsCount = 0; 523 } 524 entry.scanResultsCount++; 525 mScanReturnEntries.put(scanReturnCode, entry); 526 } 527 } 528 529 /** 530 * Increments the count of scans initiated by each wifi state, accounts for screenOn/Off 531 * 532 * @param state State of the system when scan was initiated, see WifiMetricsProto.WifiLog.WIFI_X 533 * @param screenOn Is the screen on 534 */ 535 public void incrementWifiSystemScanStateCount(int state, boolean screenOn) { 536 synchronized (mLock) { 537 int index = state * (screenOn ? 2 : 1); 538 WifiMetricsProto.WifiLog.WifiSystemStateEntry entry = 539 mWifiSystemStateEntries.get(index); 540 if (entry == null) { 541 entry = new WifiMetricsProto.WifiLog.WifiSystemStateEntry(); 542 entry.wifiState = state; 543 entry.wifiStateCount = 0; 544 entry.isScreenOn = screenOn; 545 } 546 entry.wifiStateCount++; 547 mWifiSystemStateEntries.put(state, entry); 548 } 549 } 550 551 public static final String PROTO_DUMP_ARG = "wifiMetricsProto"; 552 /** 553 * Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager 554 * at this time 555 * 556 * @param fd unused 557 * @param pw PrintWriter for writing dump to 558 * @param args unused 559 */ 560 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 561 synchronized (mLock) { 562 pw.println("WifiMetrics:"); 563 if (args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) { 564 //Dump serialized WifiLog proto 565 consolidateProto(true); 566 for (ConnectionEvent event : mConnectionEventList) { 567 if (mCurrentConnectionEvent != event) { 568 //indicate that automatic bug report has been taken for all valid 569 //connection events 570 event.mConnectionEvent.automaticBugReportTaken = true; 571 } 572 } 573 byte[] wifiMetricsProto = WifiMetricsProto.WifiLog.toByteArray(mWifiLogProto); 574 String metricsProtoDump = Base64.encodeToString(wifiMetricsProto, Base64.DEFAULT); 575 pw.println(metricsProtoDump); 576 pw.println("EndWifiMetrics"); 577 clear(); 578 } else { 579 pw.println("mConnectionEvents:"); 580 for (ConnectionEvent event : mConnectionEventList) { 581 String eventLine = event.toString(); 582 if (event == mCurrentConnectionEvent) { 583 eventLine += "CURRENTLY OPEN EVENT"; 584 } 585 pw.println(eventLine); 586 } 587 pw.println("mWifiLogProto.numSavedNetworks=" + mWifiLogProto.numSavedNetworks); 588 pw.println("mWifiLogProto.numOpenNetworks=" + mWifiLogProto.numOpenNetworks); 589 pw.println("mWifiLogProto.numPersonalNetworks=" 590 + mWifiLogProto.numPersonalNetworks); 591 pw.println("mWifiLogProto.numEnterpriseNetworks=" 592 + mWifiLogProto.numEnterpriseNetworks); 593 pw.println("mWifiLogProto.isLocationEnabled=" + mWifiLogProto.isLocationEnabled); 594 pw.println("mWifiLogProto.isScanningAlwaysEnabled=" 595 + mWifiLogProto.isScanningAlwaysEnabled); 596 pw.println("mWifiLogProto.numWifiToggledViaSettings=" 597 + mWifiLogProto.numWifiToggledViaSettings); 598 //TODO - Pending scanning refactor 599 pw.println("mWifiLogProto.numNetworksAddedByApps=" + "<TODO>"); 600 pw.println("mWifiLogProto.numNonEmptyScanResults=" + "<TODO>"); 601 pw.println("mWifiLogProto.numEmptyScanResults=" + "<TODO>"); 602 pw.println("mWifiLogProto.numOneshotScans=" + "<TODO>"); 603 pw.println("mWifiLogProto.numBackgroundScans=" + "<TODO>"); 604 pw.println("mScanReturnEntries:" + " <TODO>"); 605 pw.println("mSystemStateEntries:" + " <TODO>"); 606 } 607 } 608 } 609 610 /** 611 * Assign the separate ConnectionEvent, SystemStateEntry and ScanReturnCode lists to their 612 * respective lists within mWifiLogProto, and clear the original lists managed here. 613 * 614 * @param incremental Only include ConnectionEvents created since last automatic bug report 615 */ 616 private void consolidateProto(boolean incremental) { 617 List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>(); 618 synchronized (mLock) { 619 for (ConnectionEvent event : mConnectionEventList) { 620 if (!incremental || ((mCurrentConnectionEvent != event) 621 && !event.mConnectionEvent.automaticBugReportTaken)) { 622 //Get all ConnectionEvents that haven not been dumped as a proto, also exclude 623 //the current active un-ended connection event 624 events.add(event.mConnectionEvent); 625 event.mConnectionEvent.automaticBugReportTaken = true; 626 } 627 } 628 if (events.size() > 0) { 629 mWifiLogProto.connectionEvent = events.toArray(mWifiLogProto.connectionEvent); 630 } 631 //<TODO> SystemStateEntry and ScanReturnCode list consolidation 632 } 633 } 634 635 /** 636 * Serializes all of WifiMetrics to WifiLog proto, and returns the byte array. 637 * Does not count as taking an automatic bug report 638 * 639 * @return byte array of the deserialized & consolidated Proto 640 */ 641 public byte[] toByteArray() { 642 consolidateProto(false); 643 return mWifiLogProto.toByteArray(mWifiLogProto); 644 } 645 646 /** 647 * Clear all WifiMetrics, except for currentConnectionEvent. 648 */ 649 private void clear() { 650 synchronized (mLock) { 651 mConnectionEventList.clear(); 652 if (mCurrentConnectionEvent != null) { 653 mConnectionEventList.add(mCurrentConnectionEvent); 654 } 655 mScanReturnEntries.clear(); 656 mWifiSystemStateEntries.clear(); 657 mWifiLogProto.clear(); 658 } 659 } 660} 661