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