WifiAwareMetrics.java revision 910fdc720f0bf62b5529cb37a5f001ae585a5c59
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.util.Log;
21import android.util.SparseIntArray;
22
23import com.android.internal.annotations.VisibleForTesting;
24import com.android.server.wifi.Clock;
25import com.android.server.wifi.nano.WifiMetricsProto;
26
27import java.io.FileDescriptor;
28import java.io.PrintWriter;
29
30/**
31 * Wi-Fi Aware metric container/processor.
32 */
33public class WifiAwareMetrics {
34    private static final String TAG = "WifiAwareMetrics";
35    private static final boolean DBG = false;
36
37    // Histogram: 8 buckets (i=0, ..., 7) of 9 slots in range 10^i -> 10^(i+1)
38    // Buckets:
39    //    1 -> 10: 9 @ 1
40    //    10 -> 100: 9 @ 10
41    //    100 -> 1000: 9 @ 10^2
42    //    10^3 -> 10^4: 9 @ 10^3
43    //    10^4 -> 10^5: 9 @ 10^4
44    //    10^5 -> 10^6: 9 @ 10^5
45    //    10^6 -> 10^7: 9 @ 10^6
46    //    10^7 -> 10^8: 9 @ 10^7 --> 10^8 ms -> 10^5s -> 28 hours
47    private static final HistParms DURATION_LOG_HISTOGRAM = new HistParms(0, 1, 10, 9, 8);
48
49    private final Object mLock = new Object();
50    private final Clock mClock;
51
52    private long mLastEnableUsage = 0;
53    private long mLastEnableUsageInThisLogWindow = 0;
54    private long mAvailableTime = 0;
55    private SparseIntArray mHistogramAwareAvailableDurationMs = new SparseIntArray();
56
57    public WifiAwareMetrics(Clock clock) {
58        mClock = clock;
59    }
60
61    /**
62     * Push usage stats for WifiAwareStateMachine.enableUsage() to
63     * histogram_aware_available_duration_ms.
64     */
65    public void recordEnableUsage() {
66        synchronized (mLock) {
67            if (mLastEnableUsage != 0) {
68                Log.w(TAG, "enableUsage: mLastEnableUsage* initialized!?");
69            }
70            mLastEnableUsage = mClock.getElapsedSinceBootMillis();
71            mLastEnableUsageInThisLogWindow = mLastEnableUsage;
72        }
73    }
74
75    /**
76     * Push usage stats for WifiAwareStateMachine.disableUsage() to
77     * histogram_aware_available_duration_ms.
78     */
79
80    public void recordDisableUsage() {
81        synchronized (mLock) {
82            if (mLastEnableUsage == 0) {
83                Log.e(TAG, "disableUsage: mLastEnableUsage not initialized!?");
84                return;
85            }
86
87            long now = mClock.getElapsedSinceBootMillis();
88            addLogValueToHistogram(now - mLastEnableUsage, mHistogramAwareAvailableDurationMs,
89                    DURATION_LOG_HISTOGRAM);
90            mAvailableTime += now - mLastEnableUsageInThisLogWindow;
91            mLastEnableUsage = 0;
92            mLastEnableUsageInThisLogWindow = 0;
93        }
94    }
95
96    /**
97     * Consolidate all metrics into the proto.
98     */
99    public WifiMetricsProto.WifiAwareLog consolidateProto() {
100        WifiMetricsProto.WifiAwareLog log = new WifiMetricsProto.WifiAwareLog();
101        long now = mClock.getElapsedSinceBootMillis();
102        synchronized (mLock) {
103            log.histogramAwareAvailableDurationMs = histogramToProtoArray(
104                    mHistogramAwareAvailableDurationMs, DURATION_LOG_HISTOGRAM);
105            log.availableTimeMs = mAvailableTime;
106            if (mLastEnableUsageInThisLogWindow != 0) {
107                log.availableTimeMs += now - mLastEnableUsageInThisLogWindow;
108            }
109        }
110        return log;
111    }
112
113    /**
114     * clear Wi-Fi Aware metrics
115     */
116    public void clear() {
117        long now = mClock.getElapsedSinceBootMillis();
118        synchronized (mLock) {
119            // don't clear mLastEnableUsage since could be valid for next measurement period
120            mHistogramAwareAvailableDurationMs.clear();
121            mAvailableTime = 0;
122            if (mLastEnableUsageInThisLogWindow != 0) {
123                mLastEnableUsageInThisLogWindow = now;
124            }
125        }
126    }
127
128    /**
129     * Dump all WifiAwareMetrics to console (pw) - this method is never called to dump the
130     * serialized metrics (handled by parent WifiMetrics).
131     *
132     * @param fd   unused
133     * @param pw   PrintWriter for writing dump to
134     * @param args unused
135     */
136    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
137        synchronized (mLock) {
138            pw.println("mLastEnableUsage:" + mLastEnableUsage);
139            pw.println("mLastEnableUsageInThisLogWindow:" + mLastEnableUsageInThisLogWindow);
140            pw.println("mAvailableTime:" + mAvailableTime);
141            pw.println("mHistogramAwareAvailableDurationMs:");
142            for (int i = 0; i < mHistogramAwareAvailableDurationMs.size(); ++i) {
143                pw.println("  " + mHistogramAwareAvailableDurationMs.keyAt(i) + ": "
144                        + mHistogramAwareAvailableDurationMs.valueAt(i));
145            }
146        }
147    }
148
149    // histogram utilities
150
151    /**
152     * Specifies a ~log histogram consisting of two levels of buckets - a set of N big buckets:
153     *
154     * Buckets starts at: B + P * M^i, where i=0, ... , N-1 (N big buckets)
155     * Each big bucket is divided into S sub-buckets
156     *
157     * Each (big) bucket is M times bigger than the previous one.
158     *
159     * The buckets are then:
160     * #0: B + P * M^0 with S buckets each of width (P*M^1-P*M^0)/S
161     * #1: B + P * M^1 with S buckets each of width (P*M^2-P*M^1)/S
162     * ...
163     * #N-1: B + P * M^(N-1) with S buckets each of width (P*M^N-P*M^(N-1))/S
164     */
165    @VisibleForTesting
166    public static class HistParms {
167        public HistParms(int b, int p, int m, int s, int n) {
168            this.b = b;
169            this.p = p;
170            this.m = m;
171            this.s = s;
172            this.n = n;
173
174            // derived values
175            mLog = Math.log(m);
176            bb = new double[n];
177            sbw = new double[n];
178            bb[0] = b + p;
179            sbw[0] = p * (m - 1.0) / (double) s;
180            for (int i = 1; i < n; ++i) {
181                bb[i] = m * (bb[i - 1] - b) + b;
182                sbw[i] = m * sbw[i - 1];
183            }
184        }
185
186        // spec
187        public int b;
188        public int p;
189        public int m;
190        public int s;
191        public int n;
192
193        // derived
194        public double mLog;
195        public double[] bb; // bucket base
196        public double[] sbw; // sub-bucket width
197    }
198
199    /**
200     * Adds the input value to the histogram based on the histogram parameters.
201     */
202    @VisibleForTesting
203    public static int addLogValueToHistogram(long x, SparseIntArray histogram, HistParms hp) {
204        double logArg = (double) (x - hp.b) / (double) hp.p;
205        int bigBucketIndex = -1;
206        if (logArg > 0) {
207            bigBucketIndex = (int) (Math.log(logArg) / hp.mLog);
208        }
209        int subBucketIndex;
210        if (bigBucketIndex < 0) {
211            bigBucketIndex = 0;
212            subBucketIndex = 0;
213        } else if (bigBucketIndex >= hp.n) {
214            bigBucketIndex = hp.n - 1;
215            subBucketIndex = hp.s - 1;
216        } else {
217            subBucketIndex = (int) ((x - hp.bb[bigBucketIndex]) / hp.sbw[bigBucketIndex]);
218            if (subBucketIndex >= hp.s) { // probably a rounding error so move to next big bucket
219                bigBucketIndex++;
220                if (bigBucketIndex >= hp.n) {
221                    bigBucketIndex = hp.n - 1;
222                    subBucketIndex = hp.s - 1;
223                } else {
224                    subBucketIndex = (int) ((x - hp.bb[bigBucketIndex]) / hp.sbw[bigBucketIndex]);
225                }
226            }
227        }
228        int key = bigBucketIndex * hp.s + subBucketIndex;
229
230        // note that get() returns 0 if index not there already
231        int newValue = histogram.get(key) + 1;
232        histogram.put(key, newValue);
233
234        return newValue;
235    }
236
237    /**
238     * Converts the histogram (with the specified histogram parameters) to an array of proto
239     * histogram buckets.
240     */
241    @VisibleForTesting
242    public static WifiMetricsProto.WifiAwareLog.HistogramBucket[] histogramToProtoArray(
243            SparseIntArray histogram, HistParms hp) {
244        WifiMetricsProto.WifiAwareLog.HistogramBucket[] protoArray =
245                new WifiMetricsProto.WifiAwareLog.HistogramBucket[histogram.size()];
246        for (int i = 0; i < histogram.size(); ++i) {
247            int key = histogram.keyAt(i);
248
249            protoArray[i] = new WifiMetricsProto.WifiAwareLog.HistogramBucket();
250            protoArray[i].start = (long) (hp.bb[key / hp.s] + hp.sbw[key / hp.s] * (key % hp.s));
251            protoArray[i].end = (long) (protoArray[i].start + hp.sbw[key / hp.s]);
252            protoArray[i].count = histogram.valueAt(i);
253        }
254
255        return protoArray;
256    }
257
258    /**
259     * Adds the NanStatusType to the histogram (translating to the proto enumeration of the status).
260     */
261    public static void addNanHalStatusToHistogram(int halStatus, SparseIntArray histogram) {
262        int protoStatus = convertNanStatusTypeToProtoEnum(halStatus);
263        int newValue = histogram.get(protoStatus) + 1;
264        histogram.put(protoStatus, newValue);
265    }
266
267    /**
268     * Converts a histogram of proto NanStatusTypeEnum to a raw proto histogram.
269     */
270    @VisibleForTesting
271    public static WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] histogramToProtoArray(
272            SparseIntArray histogram) {
273        WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] protoArray =
274                new WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[histogram.size()];
275
276        for (int i = 0; i < histogram.size(); ++i) {
277            protoArray[i] = new WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket();
278            protoArray[i].nanStatusType = histogram.keyAt(i);
279            protoArray[i].count = histogram.valueAt(i);
280        }
281
282        return protoArray;
283    }
284
285    /**
286     * Convert a HAL NanStatusType enum to a Metrics proto enum NanStatusTypeEnum.
287     */
288    public static int convertNanStatusTypeToProtoEnum(int nanStatusType) {
289        switch (nanStatusType) {
290            case NanStatusType.SUCCESS:
291                return WifiMetricsProto.WifiAwareLog.SUCCESS;
292            case NanStatusType.INTERNAL_FAILURE:
293                return WifiMetricsProto.WifiAwareLog.INTERNAL_FAILURE;
294            case NanStatusType.PROTOCOL_FAILURE:
295                return WifiMetricsProto.WifiAwareLog.PROTOCOL_FAILURE;
296            case NanStatusType.INVALID_SESSION_ID:
297                return WifiMetricsProto.WifiAwareLog.INVALID_SESSION_ID;
298            case NanStatusType.NO_RESOURCES_AVAILABLE:
299                return WifiMetricsProto.WifiAwareLog.NO_RESOURCES_AVAILABLE;
300            case NanStatusType.INVALID_ARGS:
301                return WifiMetricsProto.WifiAwareLog.INVALID_ARGS;
302            case NanStatusType.INVALID_PEER_ID:
303                return WifiMetricsProto.WifiAwareLog.INVALID_PEER_ID;
304            case NanStatusType.INVALID_NDP_ID:
305                return WifiMetricsProto.WifiAwareLog.INVALID_NDP_ID;
306            case NanStatusType.NAN_NOT_ALLOWED:
307                return WifiMetricsProto.WifiAwareLog.NAN_NOT_ALLOWED;
308            case NanStatusType.NO_OTA_ACK:
309                return WifiMetricsProto.WifiAwareLog.NO_OTA_ACK;
310            case NanStatusType.ALREADY_ENABLED:
311                return WifiMetricsProto.WifiAwareLog.ALREADY_ENABLED;
312            case NanStatusType.FOLLOWUP_TX_QUEUE_FULL:
313                return WifiMetricsProto.WifiAwareLog.FOLLOWUP_TX_QUEUE_FULL;
314            case NanStatusType.UNSUPPORTED_CONCURRENCY_NAN_DISABLED:
315                return WifiMetricsProto.WifiAwareLog.UNSUPPORTED_CONCURRENCY_NAN_DISABLED;
316            default:
317                Log.e(TAG, "Unrecognized NanStatusType: " + nanStatusType);
318                return WifiMetricsProto.WifiAwareLog.UNKNOWN_HAL_STATUS;
319        }
320    }
321}
322