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