WifiAwareMetrics.java revision 7a44d3fab050e1aca22317a604a68016c79f5925
1/*
2 * Copyright (C) 2017 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.aware;
18
19import android.hardware.wifi.V1_0.NanStatusType;
20import android.net.wifi.aware.WifiAwareNetworkSpecifier;
21import android.text.TextUtils;
22import android.util.Log;
23import android.util.SparseArray;
24import android.util.SparseIntArray;
25
26import com.android.internal.annotations.VisibleForTesting;
27import com.android.server.wifi.Clock;
28import com.android.server.wifi.nano.WifiMetricsProto;
29
30import java.io.FileDescriptor;
31import java.io.PrintWriter;
32import java.util.Collections;
33import java.util.HashMap;
34import java.util.HashSet;
35import java.util.Map;
36import java.util.Set;
37
38/**
39 * Wi-Fi Aware metric container/processor.
40 */
41public class WifiAwareMetrics {
42    private static final String TAG = "WifiAwareMetrics";
43    private static final boolean DBG = false;
44
45    // Histogram: 8 buckets (i=0, ..., 7) of 9 slots in range 10^i -> 10^(i+1)
46    // Buckets:
47    //    1 -> 10: 9 @ 1
48    //    10 -> 100: 9 @ 10
49    //    100 -> 1000: 9 @ 10^2
50    //    10^3 -> 10^4: 9 @ 10^3
51    //    10^4 -> 10^5: 9 @ 10^4
52    //    10^5 -> 10^6: 9 @ 10^5
53    //    10^6 -> 10^7: 9 @ 10^6
54    //    10^7 -> 10^8: 9 @ 10^7 --> 10^8 ms -> 10^5s -> 28 hours
55    private static final HistParms DURATION_LOG_HISTOGRAM = new HistParms(0, 1, 10, 9, 8);
56
57    private final Object mLock = new Object();
58    private final Clock mClock;
59
60    // enableUsage/disableUsage data
61    private long mLastEnableUsageMs = 0;
62    private long mLastEnableUsageInThisSampleWindowMs = 0;
63    private long mAvailableTimeMs = 0;
64    private SparseIntArray mHistogramAwareAvailableDurationMs = new SparseIntArray();
65
66    // enabled data
67    private long mLastEnableAwareMs = 0;
68    private long mLastEnableAwareInThisSampleWindowMs = 0;
69    private long mEnabledTimeMs = 0;
70    private SparseIntArray mHistogramAwareEnabledDurationMs = new SparseIntArray();
71
72    // attach data
73    private static class AttachData {
74        boolean mUsesIdentityCallback; // do any attach sessions of the UID use identity callback
75        int mMaxConcurrentAttaches;
76    }
77    private Map<Integer, AttachData> mAttachDataByUid = new HashMap<>();
78    private SparseIntArray mAttachStatusData = new SparseIntArray();
79    private SparseIntArray mHistogramAttachDuration = new SparseIntArray();
80
81    // discovery data
82    private int mMaxPublishInApp = 0;
83    private int mMaxSubscribeInApp = 0;
84    private int mMaxDiscoveryInApp = 0;
85    private int mMaxPublishInSystem = 0;
86    private int mMaxSubscribeInSystem = 0;
87    private int mMaxDiscoveryInSystem = 0;
88    private SparseIntArray mPublishStatusData = new SparseIntArray();
89    private SparseIntArray mSubscribeStatusData = new SparseIntArray();
90    private SparseIntArray mHistogramPublishDuration = new SparseIntArray();
91    private SparseIntArray mHistogramSubscribeDuration = new SparseIntArray();
92    private Set<Integer> mAppsWithDiscoverySessionResourceFailure = new HashSet<>();
93
94    // data-path (NDI/NDP) data
95    private int mMaxNdiInApp = 0;
96    private int mMaxNdpInApp = 0;
97    private int mMaxSecureNdpInApp = 0;
98    private int mMaxNdiInSystem = 0;
99    private int mMaxNdpInSystem = 0;
100    private int mMaxSecureNdpInSystem = 0;
101    private int mMaxNdpPerNdi = 0;
102    private SparseIntArray mInBandNdpStatusData = new SparseIntArray();
103    private SparseIntArray mOutOfBandNdpStatusData = new SparseIntArray();
104
105    private SparseIntArray mNdpCreationTimeDuration = new SparseIntArray();
106    private long mNdpCreationTimeMin = -1;
107    private long mNdpCreationTimeMax = 0;
108    private long mNdpCreationTimeSum = 0;
109    private long mNdpCreationTimeSumSq = 0;
110    private long mNdpCreationTimeNumSamples = 0;
111
112    private SparseIntArray mHistogramNdpDuration = new SparseIntArray();
113
114    public WifiAwareMetrics(Clock clock) {
115        mClock = clock;
116    }
117
118    /**
119     * Push usage stats for WifiAwareStateMachine.enableUsage() to
120     * histogram_aware_available_duration_ms.
121     */
122    public void recordEnableUsage() {
123        synchronized (mLock) {
124            if (mLastEnableUsageMs != 0) {
125                Log.w(TAG, "enableUsage: mLastEnableUsage*Ms initialized!?");
126            }
127            mLastEnableUsageMs = mClock.getElapsedSinceBootMillis();
128            mLastEnableUsageInThisSampleWindowMs = mLastEnableUsageMs;
129        }
130    }
131
132    /**
133     * Push usage stats for WifiAwareStateMachine.disableUsage() to
134     * histogram_aware_available_duration_ms.
135     */
136
137    public void recordDisableUsage() {
138        synchronized (mLock) {
139            if (mLastEnableUsageMs == 0) {
140                Log.e(TAG, "disableUsage: mLastEnableUsage not initialized!?");
141                return;
142            }
143
144            long now = mClock.getElapsedSinceBootMillis();
145            addLogValueToHistogram(now - mLastEnableUsageMs, mHistogramAwareAvailableDurationMs,
146                    DURATION_LOG_HISTOGRAM);
147            mAvailableTimeMs += now - mLastEnableUsageInThisSampleWindowMs;
148            mLastEnableUsageMs = 0;
149            mLastEnableUsageInThisSampleWindowMs = 0;
150        }
151    }
152
153    /**
154     * Push usage stats of Aware actually being enabled on-the-air: start
155     */
156    public void recordEnableAware() {
157        synchronized (mLock) {
158            if (mLastEnableAwareMs != 0) {
159                return; // already enabled
160            }
161            mLastEnableAwareMs = mClock.getElapsedSinceBootMillis();
162            mLastEnableAwareInThisSampleWindowMs = mLastEnableAwareMs;
163        }
164    }
165
166    /**
167     * Push usage stats of Aware actually being enabled on-the-air: stop (disable)
168     */
169    public void recordDisableAware() {
170        synchronized (mLock) {
171            if (mLastEnableAwareMs == 0) {
172                return; // already disabled
173            }
174
175            long now = mClock.getElapsedSinceBootMillis();
176            addLogValueToHistogram(now - mLastEnableAwareMs, mHistogramAwareEnabledDurationMs,
177                    DURATION_LOG_HISTOGRAM);
178            mEnabledTimeMs += now - mLastEnableAwareInThisSampleWindowMs;
179            mLastEnableAwareMs = 0;
180            mLastEnableAwareInThisSampleWindowMs = 0;
181        }
182    }
183
184    /**
185     * Push information about a new attach session.
186     */
187    public void recordAttachSession(int uid, boolean usesIdentityCallback,
188            SparseArray<WifiAwareClientState> clients) {
189        // count the number of clients with the specific uid
190        int currentConcurrentCount = 0;
191        for (int i = 0; i < clients.size(); ++i) {
192            if (clients.valueAt(i).getUid() == uid) {
193                ++currentConcurrentCount;
194            }
195        }
196
197        synchronized (mLock) {
198            AttachData data = mAttachDataByUid.get(uid);
199            if (data == null) {
200                data = new AttachData();
201                mAttachDataByUid.put(uid, data);
202            }
203            data.mUsesIdentityCallback |= usesIdentityCallback;
204            data.mMaxConcurrentAttaches = Math.max(data.mMaxConcurrentAttaches,
205                    currentConcurrentCount);
206            recordAttachStatus(NanStatusType.SUCCESS);
207        }
208    }
209
210    /**
211     * Push information about a new attach session status (recorded when attach session is created).
212     */
213    public void recordAttachStatus(int status) {
214        synchronized (mLock) {
215            mAttachStatusData.put(status, mAttachStatusData.get(status) + 1);
216        }
217    }
218
219    /**
220     * Push duration information of an attach session.
221     */
222    public void recordAttachSessionDuration(long creationTime) {
223        synchronized (mLock) {
224            addLogValueToHistogram(mClock.getElapsedSinceBootMillis() - creationTime,
225                    mHistogramAttachDuration,
226                    DURATION_LOG_HISTOGRAM);
227        }
228    }
229
230    /**
231     * Push information about the new discovery session.
232     */
233    public void recordDiscoverySession(int uid, boolean isPublish,
234            SparseArray<WifiAwareClientState> clients) {
235        // count the number of sessions per uid and overall
236        int numPublishesInSystem = 0;
237        int numSubscribesInSystem = 0;
238        int numPublishesOnUid = 0;
239        int numSubscribesOnUid = 0;
240
241        for (int i = 0; i < clients.size(); ++i) {
242            WifiAwareClientState client = clients.valueAt(i);
243            boolean sameUid = client.getUid() == uid;
244
245            SparseArray<WifiAwareDiscoverySessionState> sessions = client.getSessions();
246            for (int j = 0; j < sessions.size(); ++j) {
247                WifiAwareDiscoverySessionState session = sessions.valueAt(j);
248
249                if (session.isPublishSession()) {
250                    numPublishesInSystem += 1;
251                    if (sameUid) {
252                        numPublishesOnUid += 1;
253                    }
254                } else {
255                    numSubscribesInSystem += 1;
256                    if (sameUid) {
257                        numSubscribesOnUid += 1;
258                    }
259                }
260            }
261        }
262
263        synchronized (mLock) {
264            mMaxPublishInApp = Math.max(mMaxPublishInApp, numPublishesOnUid);
265            mMaxSubscribeInApp = Math.max(mMaxSubscribeInApp, numSubscribesOnUid);
266            mMaxDiscoveryInApp = Math.max(mMaxDiscoveryInApp,
267                    numPublishesOnUid + numSubscribesOnUid);
268            mMaxPublishInSystem = Math.max(mMaxPublishInSystem, numPublishesInSystem);
269            mMaxSubscribeInSystem = Math.max(mMaxSubscribeInSystem, numSubscribesInSystem);
270            mMaxDiscoveryInSystem = Math.max(mMaxDiscoveryInSystem,
271                    numPublishesInSystem + numSubscribesInSystem);
272        }
273    }
274
275    /**
276     * Push information about a new discovery session status (recorded when the discovery session is
277     * created).
278     */
279    public void recordDiscoveryStatus(int uid, int status, boolean isPublish) {
280        synchronized (mLock) {
281            if (isPublish) {
282                mPublishStatusData.put(status, mPublishStatusData.get(status) + 1);
283            } else {
284                mSubscribeStatusData.put(status, mSubscribeStatusData.get(status) + 1);
285            }
286
287            if (status == NanStatusType.NO_RESOURCES_AVAILABLE) {
288                mAppsWithDiscoverySessionResourceFailure.add(uid);
289            }
290        }
291    }
292
293    /**
294     * Push duration information of a discovery session.
295     */
296    public void recordDiscoverySessionDuration(long creationTime, boolean isPublish) {
297        synchronized (mLock) {
298            addLogValueToHistogram(mClock.getElapsedSinceBootMillis() - creationTime,
299                    isPublish ? mHistogramPublishDuration : mHistogramSubscribeDuration,
300                    DURATION_LOG_HISTOGRAM);
301        }
302    }
303
304    /**
305     * Record NDP (and by extension NDI) usage - on successful creation of an NDP.
306     */
307    public void recordNdpCreation(int uid,
308            Map<WifiAwareNetworkSpecifier, WifiAwareDataPathStateManager
309                    .AwareNetworkRequestInformation> networkRequestCache) {
310        int numNdpInApp = 0;
311        int numSecureNdpInApp = 0;
312        int numNdpInSystem = 0;
313        int numSecureNdpInSystem = 0;
314
315        Map<String, Integer> ndpPerNdiMap = new HashMap<>();
316        Set<String> ndiInApp = new HashSet<>();
317        Set<String> ndiInSystem = new HashSet<>();
318
319        for (WifiAwareDataPathStateManager.AwareNetworkRequestInformation anri :
320                networkRequestCache.values()) {
321            if (anri.state
322                    != WifiAwareDataPathStateManager.AwareNetworkRequestInformation
323                    .STATE_INITIATOR_CONFIRMED
324                    && anri.state
325                    != WifiAwareDataPathStateManager.AwareNetworkRequestInformation
326                    .STATE_RESPONDER_CONFIRMED) {
327                continue; // only count completed (up-and-running) NDPs
328            }
329
330            boolean sameUid = anri.uid == uid;
331            boolean isSecure = !TextUtils.isEmpty(anri.networkSpecifier.passphrase) || (
332                    anri.networkSpecifier.pmk != null && anri.networkSpecifier.pmk.length != 0);
333
334            // in-app stats
335            if (sameUid) {
336                numNdpInApp += 1;
337                if (isSecure) {
338                    numSecureNdpInApp += 1;
339                }
340
341                ndiInApp.add(anri.interfaceName);
342            }
343
344            // system stats
345            numNdpInSystem += 1;
346            if (isSecure) {
347                numSecureNdpInSystem += 1;
348            }
349
350            // ndp/ndi stats
351            Integer ndpCount = ndpPerNdiMap.get(anri.interfaceName);
352            if (ndpCount == null) {
353                ndpPerNdiMap.put(anri.interfaceName, 1);
354            } else {
355                ndpPerNdiMap.put(anri.interfaceName, ndpCount + 1);
356            }
357
358            // ndi stats
359            ndiInSystem.add(anri.interfaceName);
360        }
361
362        synchronized (mLock) {
363            mMaxNdiInApp = Math.max(mMaxNdiInApp, ndiInApp.size());
364            mMaxNdpInApp = Math.max(mMaxNdpInApp, numNdpInApp);
365            mMaxSecureNdpInApp = Math.max(mMaxSecureNdpInApp, numSecureNdpInApp);
366            mMaxNdiInSystem = Math.max(mMaxNdiInSystem, ndiInSystem.size());
367            mMaxNdpInSystem = Math.max(mMaxNdpInSystem, numNdpInSystem);
368            mMaxSecureNdpInSystem = Math.max(mMaxSecureNdpInSystem, numSecureNdpInSystem);
369            mMaxNdpPerNdi = Math.max(mMaxNdpPerNdi, Collections.max(ndpPerNdiMap.values()));
370        }
371    }
372
373    /**
374     * Record the completion status of NDP negotiation. There are multiple steps in NDP negotiation
375     * a failure on any aborts the process and is recorded. A success on intermediate stages is
376     * not recorded - only the final success.
377     */
378    public void recordNdpStatus(int status, boolean isOutOfBand, long startTimestamp) {
379        synchronized (mLock) {
380            if (isOutOfBand) {
381                mOutOfBandNdpStatusData.put(status, mOutOfBandNdpStatusData.get(status) + 1);
382            } else {
383                mInBandNdpStatusData.put(status, mOutOfBandNdpStatusData.get(status) + 1);
384            }
385
386            if (status == NanStatusType.SUCCESS) {
387                long creationTime = mClock.getElapsedSinceBootMillis() - startTimestamp;
388                addLogValueToHistogram(creationTime, mNdpCreationTimeDuration,
389                        DURATION_LOG_HISTOGRAM);
390                mNdpCreationTimeMin = (mNdpCreationTimeMin == -1) ? creationTime : Math.min(
391                        mNdpCreationTimeMin, creationTime);
392                mNdpCreationTimeMax = Math.max(mNdpCreationTimeMax, creationTime);
393                mNdpCreationTimeSum += creationTime;
394                mNdpCreationTimeSumSq += creationTime * creationTime;
395                mNdpCreationTimeNumSamples += 1;
396            }
397        }
398    }
399
400    /**
401     * Record the duration of the NDP session. The creation time is assumed to be the time at
402     * which a confirm message was received (i.e. the end of the setup negotiation).
403     */
404    public void recordNdpSessionDuration(long creationTime) {
405        synchronized (mLock) {
406            addLogValueToHistogram(mClock.getElapsedSinceBootMillis() - creationTime,
407                    mHistogramNdpDuration, DURATION_LOG_HISTOGRAM);
408        }
409    }
410
411    /**
412     * Consolidate all metrics into the proto.
413     */
414    public WifiMetricsProto.WifiAwareLog consolidateProto() {
415        WifiMetricsProto.WifiAwareLog log = new WifiMetricsProto.WifiAwareLog();
416        long now = mClock.getElapsedSinceBootMillis();
417        synchronized (mLock) {
418            log.histogramAwareAvailableDurationMs = histogramToProtoArray(
419                    mHistogramAwareAvailableDurationMs, DURATION_LOG_HISTOGRAM);
420            log.availableTimeMs = mAvailableTimeMs;
421            if (mLastEnableUsageInThisSampleWindowMs != 0) {
422                log.availableTimeMs += now - mLastEnableUsageInThisSampleWindowMs;
423            }
424
425            log.histogramAwareEnabledDurationMs = histogramToProtoArray(
426                    mHistogramAwareEnabledDurationMs, DURATION_LOG_HISTOGRAM);
427            log.enabledTimeMs = mEnabledTimeMs;
428            if (mLastEnableAwareInThisSampleWindowMs != 0) {
429                log.enabledTimeMs += now - mLastEnableAwareInThisSampleWindowMs;
430            }
431
432            log.numApps = mAttachDataByUid.size();
433            log.numAppsUsingIdentityCallback = 0;
434            log.maxConcurrentAttachSessionsInApp = 0;
435            for (AttachData ad: mAttachDataByUid.values()) {
436                if (ad.mUsesIdentityCallback) {
437                    ++log.numAppsUsingIdentityCallback;
438                }
439                log.maxConcurrentAttachSessionsInApp = Math.max(
440                        log.maxConcurrentAttachSessionsInApp, ad.mMaxConcurrentAttaches);
441            }
442            log.histogramAttachSessionStatus = histogramToProtoArray(mAttachStatusData);
443            log.histogramAttachDurationMs = histogramToProtoArray(mHistogramAttachDuration,
444                    DURATION_LOG_HISTOGRAM);
445
446            log.maxConcurrentPublishInApp = mMaxPublishInApp;
447            log.maxConcurrentSubscribeInApp = mMaxSubscribeInApp;
448            log.maxConcurrentDiscoverySessionsInApp = mMaxDiscoveryInApp;
449            log.maxConcurrentPublishInSystem = mMaxPublishInSystem;
450            log.maxConcurrentSubscribeInSystem = mMaxSubscribeInSystem;
451            log.maxConcurrentDiscoverySessionsInSystem = mMaxDiscoveryInSystem;
452            log.histogramPublishStatus = histogramToProtoArray(mPublishStatusData);
453            log.histogramSubscribeStatus = histogramToProtoArray(mSubscribeStatusData);
454            log.numAppsWithDiscoverySessionFailureOutOfResources =
455                    mAppsWithDiscoverySessionResourceFailure.size();
456            log.histogramPublishSessionDurationMs = histogramToProtoArray(mHistogramPublishDuration,
457                    DURATION_LOG_HISTOGRAM);
458            log.histogramSubscribeSessionDurationMs = histogramToProtoArray(
459                    mHistogramSubscribeDuration, DURATION_LOG_HISTOGRAM);
460
461            log.maxConcurrentNdiInApp = mMaxNdiInApp;
462            log.maxConcurrentNdiInSystem = mMaxNdiInSystem;
463            log.maxConcurrentNdpInApp = mMaxNdpInApp;
464            log.maxConcurrentNdpInSystem = mMaxNdpInSystem;
465            log.maxConcurrentSecureNdpInApp = mMaxSecureNdpInApp;
466            log.maxConcurrentSecureNdpInSystem = mMaxSecureNdpInSystem;
467            log.maxConcurrentNdpPerNdi = mMaxNdpPerNdi;
468            log.histogramRequestNdpStatus = histogramToProtoArray(mInBandNdpStatusData);
469            log.histogramRequestNdpOobStatus = histogramToProtoArray(mOutOfBandNdpStatusData);
470
471            log.histogramNdpCreationTimeMs = histogramToProtoArray(mNdpCreationTimeDuration,
472                    DURATION_LOG_HISTOGRAM);
473            log.ndpCreationTimeMsMin = mNdpCreationTimeMin;
474            log.ndpCreationTimeMsMax = mNdpCreationTimeMax;
475            log.ndpCreationTimeMsSum = mNdpCreationTimeSum;
476            log.ndpCreationTimeMsSumOfSq = mNdpCreationTimeSumSq;
477            log.ndpCreationTimeMsNumSamples = mNdpCreationTimeNumSamples;
478
479            log.histogramNdpSessionDurationMs = histogramToProtoArray(mHistogramNdpDuration,
480                    DURATION_LOG_HISTOGRAM);
481        }
482        return log;
483    }
484
485    /**
486     * clear Wi-Fi Aware metrics
487     */
488    public void clear() {
489        long now = mClock.getElapsedSinceBootMillis();
490        synchronized (mLock) {
491            // don't clear mLastEnableUsage since could be valid for next measurement period
492            mHistogramAwareAvailableDurationMs.clear();
493            mAvailableTimeMs = 0;
494            if (mLastEnableUsageInThisSampleWindowMs != 0) {
495                mLastEnableUsageInThisSampleWindowMs = now;
496            }
497
498            // don't clear mLastEnableAware since could be valid for next measurement period
499            mHistogramAwareEnabledDurationMs.clear();
500            mEnabledTimeMs = 0;
501            if (mLastEnableAwareInThisSampleWindowMs != 0) {
502                mLastEnableAwareInThisSampleWindowMs = now;
503            }
504
505            mAttachDataByUid.clear();
506            mAttachStatusData.clear();
507            mHistogramAttachDuration.clear();
508
509            mMaxPublishInApp = 0;
510            mMaxSubscribeInApp = 0;
511            mMaxDiscoveryInApp = 0;
512            mMaxPublishInSystem = 0;
513            mMaxSubscribeInSystem = 0;
514            mMaxDiscoveryInSystem = 0;
515            mPublishStatusData.clear();
516            mSubscribeStatusData.clear();
517            mHistogramPublishDuration.clear();
518            mHistogramSubscribeDuration.clear();
519            mAppsWithDiscoverySessionResourceFailure.clear();
520
521            mMaxNdiInApp = 0;
522            mMaxNdpInApp = 0;
523            mMaxSecureNdpInApp = 0;
524            mMaxNdiInSystem = 0;
525            mMaxNdpInSystem = 0;
526            mMaxSecureNdpInSystem = 0;
527            mMaxNdpPerNdi = 0;
528            mInBandNdpStatusData.clear();
529            mOutOfBandNdpStatusData.clear();
530
531            mNdpCreationTimeDuration.clear();
532            mNdpCreationTimeMin = -1;
533            mNdpCreationTimeMax = 0;
534            mNdpCreationTimeSum = 0;
535            mNdpCreationTimeSumSq = 0;
536            mNdpCreationTimeNumSamples = 0;
537
538            mHistogramNdpDuration.clear();
539        }
540    }
541
542    /**
543     * Dump all WifiAwareMetrics to console (pw) - this method is never called to dump the
544     * serialized metrics (handled by parent WifiMetrics).
545     *
546     * @param fd   unused
547     * @param pw   PrintWriter for writing dump to
548     * @param args unused
549     */
550    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
551        synchronized (mLock) {
552            pw.println("mLastEnableUsage:" + mLastEnableUsageMs);
553            pw.println(
554                    "mLastEnableUsageInThisSampleWindow:" + mLastEnableUsageInThisSampleWindowMs);
555            pw.println("mAvailableTime:" + mAvailableTimeMs);
556            pw.println("mHistogramAwareAvailableDurationMs:");
557            for (int i = 0; i < mHistogramAwareAvailableDurationMs.size(); ++i) {
558                pw.println("  " + mHistogramAwareAvailableDurationMs.keyAt(i) + ": "
559                        + mHistogramAwareAvailableDurationMs.valueAt(i));
560            }
561            pw.println("mAttachDataByUid:");
562            for (Map.Entry<Integer, AttachData> ade: mAttachDataByUid.entrySet()) {
563                pw.println("  " + "uid=" + ade.getKey() + ": identity="
564                        + ade.getValue().mUsesIdentityCallback + ", maxConcurrent="
565                        + ade.getValue().mMaxConcurrentAttaches);
566            }
567            pw.println("mAttachStatusData:");
568            for (int i = 0; i < mAttachStatusData.size(); ++i) {
569                pw.println("  " + mAttachStatusData.keyAt(i) + ": "
570                        + mAttachStatusData.valueAt(i));
571            }
572            pw.println("mHistogramAttachDuration:");
573            for (int i = 0; i < mHistogramAttachDuration.size(); ++i) {
574                pw.println("  " + mHistogramAttachDuration.keyAt(i) + ": "
575                        + mHistogramAttachDuration.valueAt(i));
576            }
577        }
578    }
579
580    // histogram utilities
581
582    /**
583     * Specifies a ~log histogram consisting of two levels of buckets - a set of N big buckets:
584     *
585     * Buckets starts at: B + P * M^i, where i=0, ... , N-1 (N big buckets)
586     * Each big bucket is divided into S sub-buckets
587     *
588     * Each (big) bucket is M times bigger than the previous one.
589     *
590     * The buckets are then:
591     * #0: B + P * M^0 with S buckets each of width (P*M^1-P*M^0)/S
592     * #1: B + P * M^1 with S buckets each of width (P*M^2-P*M^1)/S
593     * ...
594     * #N-1: B + P * M^(N-1) with S buckets each of width (P*M^N-P*M^(N-1))/S
595     */
596    @VisibleForTesting
597    public static class HistParms {
598        public HistParms(int b, int p, int m, int s, int n) {
599            this.b = b;
600            this.p = p;
601            this.m = m;
602            this.s = s;
603            this.n = n;
604
605            // derived values
606            mLog = Math.log(m);
607            bb = new double[n];
608            sbw = new double[n];
609            bb[0] = b + p;
610            sbw[0] = p * (m - 1.0) / (double) s;
611            for (int i = 1; i < n; ++i) {
612                bb[i] = m * (bb[i - 1] - b) + b;
613                sbw[i] = m * sbw[i - 1];
614            }
615        }
616
617        // spec
618        public int b;
619        public int p;
620        public int m;
621        public int s;
622        public int n;
623
624        // derived
625        public double mLog;
626        public double[] bb; // bucket base
627        public double[] sbw; // sub-bucket width
628    }
629
630    /**
631     * Adds the input value to the histogram based on the histogram parameters.
632     */
633    @VisibleForTesting
634    public static int addLogValueToHistogram(long x, SparseIntArray histogram, HistParms hp) {
635        double logArg = (double) (x - hp.b) / (double) hp.p;
636        int bigBucketIndex = -1;
637        if (logArg > 0) {
638            bigBucketIndex = (int) (Math.log(logArg) / hp.mLog);
639        }
640        int subBucketIndex;
641        if (bigBucketIndex < 0) {
642            bigBucketIndex = 0;
643            subBucketIndex = 0;
644        } else if (bigBucketIndex >= hp.n) {
645            bigBucketIndex = hp.n - 1;
646            subBucketIndex = hp.s - 1;
647        } else {
648            subBucketIndex = (int) ((x - hp.bb[bigBucketIndex]) / hp.sbw[bigBucketIndex]);
649            if (subBucketIndex >= hp.s) { // probably a rounding error so move to next big bucket
650                bigBucketIndex++;
651                if (bigBucketIndex >= hp.n) {
652                    bigBucketIndex = hp.n - 1;
653                    subBucketIndex = hp.s - 1;
654                } else {
655                    subBucketIndex = (int) ((x - hp.bb[bigBucketIndex]) / hp.sbw[bigBucketIndex]);
656                }
657            }
658        }
659        int key = bigBucketIndex * hp.s + subBucketIndex;
660
661        // note that get() returns 0 if index not there already
662        int newValue = histogram.get(key) + 1;
663        histogram.put(key, newValue);
664
665        return newValue;
666    }
667
668    /**
669     * Converts the histogram (with the specified histogram parameters) to an array of proto
670     * histogram buckets.
671     */
672    @VisibleForTesting
673    public static WifiMetricsProto.WifiAwareLog.HistogramBucket[] histogramToProtoArray(
674            SparseIntArray histogram, HistParms hp) {
675        WifiMetricsProto.WifiAwareLog.HistogramBucket[] protoArray =
676                new WifiMetricsProto.WifiAwareLog.HistogramBucket[histogram.size()];
677        for (int i = 0; i < histogram.size(); ++i) {
678            int key = histogram.keyAt(i);
679
680            protoArray[i] = new WifiMetricsProto.WifiAwareLog.HistogramBucket();
681            protoArray[i].start = (long) (hp.bb[key / hp.s] + hp.sbw[key / hp.s] * (key % hp.s));
682            protoArray[i].end = (long) (protoArray[i].start + hp.sbw[key / hp.s]);
683            protoArray[i].count = histogram.valueAt(i);
684        }
685
686        return protoArray;
687    }
688
689    /**
690     * Adds the NanStatusType to the histogram (translating to the proto enumeration of the status).
691     */
692    public static void addNanHalStatusToHistogram(int halStatus, SparseIntArray histogram) {
693        int protoStatus = convertNanStatusTypeToProtoEnum(halStatus);
694        int newValue = histogram.get(protoStatus) + 1;
695        histogram.put(protoStatus, newValue);
696    }
697
698    /**
699     * Converts a histogram of proto NanStatusTypeEnum to a raw proto histogram.
700     */
701    @VisibleForTesting
702    public static WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] histogramToProtoArray(
703            SparseIntArray histogram) {
704        WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] protoArray =
705                new WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[histogram.size()];
706
707        for (int i = 0; i < histogram.size(); ++i) {
708            protoArray[i] = new WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket();
709            protoArray[i].nanStatusType = histogram.keyAt(i);
710            protoArray[i].count = histogram.valueAt(i);
711        }
712
713        return protoArray;
714    }
715
716    /**
717     * Convert a HAL NanStatusType enum to a Metrics proto enum NanStatusTypeEnum.
718     */
719    public static int convertNanStatusTypeToProtoEnum(int nanStatusType) {
720        switch (nanStatusType) {
721            case NanStatusType.SUCCESS:
722                return WifiMetricsProto.WifiAwareLog.SUCCESS;
723            case NanStatusType.INTERNAL_FAILURE:
724                return WifiMetricsProto.WifiAwareLog.INTERNAL_FAILURE;
725            case NanStatusType.PROTOCOL_FAILURE:
726                return WifiMetricsProto.WifiAwareLog.PROTOCOL_FAILURE;
727            case NanStatusType.INVALID_SESSION_ID:
728                return WifiMetricsProto.WifiAwareLog.INVALID_SESSION_ID;
729            case NanStatusType.NO_RESOURCES_AVAILABLE:
730                return WifiMetricsProto.WifiAwareLog.NO_RESOURCES_AVAILABLE;
731            case NanStatusType.INVALID_ARGS:
732                return WifiMetricsProto.WifiAwareLog.INVALID_ARGS;
733            case NanStatusType.INVALID_PEER_ID:
734                return WifiMetricsProto.WifiAwareLog.INVALID_PEER_ID;
735            case NanStatusType.INVALID_NDP_ID:
736                return WifiMetricsProto.WifiAwareLog.INVALID_NDP_ID;
737            case NanStatusType.NAN_NOT_ALLOWED:
738                return WifiMetricsProto.WifiAwareLog.NAN_NOT_ALLOWED;
739            case NanStatusType.NO_OTA_ACK:
740                return WifiMetricsProto.WifiAwareLog.NO_OTA_ACK;
741            case NanStatusType.ALREADY_ENABLED:
742                return WifiMetricsProto.WifiAwareLog.ALREADY_ENABLED;
743            case NanStatusType.FOLLOWUP_TX_QUEUE_FULL:
744                return WifiMetricsProto.WifiAwareLog.FOLLOWUP_TX_QUEUE_FULL;
745            case NanStatusType.UNSUPPORTED_CONCURRENCY_NAN_DISABLED:
746                return WifiMetricsProto.WifiAwareLog.UNSUPPORTED_CONCURRENCY_NAN_DISABLED;
747            default:
748                Log.e(TAG, "Unrecognized NanStatusType: " + nanStatusType);
749                return WifiMetricsProto.WifiAwareLog.UNKNOWN_HAL_STATUS;
750        }
751    }
752}
753