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