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