WifiAwareMetrics.java revision a83967f56939392072253162731bfe090c2e7709
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.SparseArray; 22import android.util.SparseIntArray; 23 24import com.android.internal.annotations.VisibleForTesting; 25import com.android.server.wifi.Clock; 26import com.android.server.wifi.nano.WifiMetricsProto; 27 28import java.io.FileDescriptor; 29import java.io.PrintWriter; 30import java.util.HashMap; 31import java.util.Map; 32 33/** 34 * Wi-Fi Aware metric container/processor. 35 */ 36public class WifiAwareMetrics { 37 private static final String TAG = "WifiAwareMetrics"; 38 private static final boolean DBG = false; 39 40 // Histogram: 8 buckets (i=0, ..., 7) of 9 slots in range 10^i -> 10^(i+1) 41 // Buckets: 42 // 1 -> 10: 9 @ 1 43 // 10 -> 100: 9 @ 10 44 // 100 -> 1000: 9 @ 10^2 45 // 10^3 -> 10^4: 9 @ 10^3 46 // 10^4 -> 10^5: 9 @ 10^4 47 // 10^5 -> 10^6: 9 @ 10^5 48 // 10^6 -> 10^7: 9 @ 10^6 49 // 10^7 -> 10^8: 9 @ 10^7 --> 10^8 ms -> 10^5s -> 28 hours 50 private static final HistParms DURATION_LOG_HISTOGRAM = new HistParms(0, 1, 10, 9, 8); 51 52 private final Object mLock = new Object(); 53 private final Clock mClock; 54 55 // enableUsage/disableUsage data 56 private long mLastEnableUsage = 0; 57 private long mLastEnableUsageInThisLogWindow = 0; 58 private long mAvailableTime = 0; 59 private SparseIntArray mHistogramAwareAvailableDurationMs = new SparseIntArray(); 60 61 // app data (attach) 62 private static class AttachData { 63 boolean mUsesIdentityCallback; // do any attach sessions of the UID use identity callback 64 int mMaxConcurrentAttaches; 65 } 66 private Map<Integer, AttachData> mAttachDataByUid = new HashMap<>(); 67 private SparseIntArray mAttachStatusData = new SparseIntArray(); 68 private SparseIntArray mHistogramAttachDuration = new SparseIntArray(); 69 70 public WifiAwareMetrics(Clock clock) { 71 mClock = clock; 72 } 73 74 /** 75 * Push usage stats for WifiAwareStateMachine.enableUsage() to 76 * histogram_aware_available_duration_ms. 77 */ 78 public void recordEnableUsage() { 79 synchronized (mLock) { 80 if (mLastEnableUsage != 0) { 81 Log.w(TAG, "enableUsage: mLastEnableUsage* initialized!?"); 82 } 83 mLastEnableUsage = mClock.getElapsedSinceBootMillis(); 84 mLastEnableUsageInThisLogWindow = mLastEnableUsage; 85 } 86 } 87 88 /** 89 * Push usage stats for WifiAwareStateMachine.disableUsage() to 90 * histogram_aware_available_duration_ms. 91 */ 92 93 public void recordDisableUsage() { 94 synchronized (mLock) { 95 if (mLastEnableUsage == 0) { 96 Log.e(TAG, "disableUsage: mLastEnableUsage not initialized!?"); 97 return; 98 } 99 100 long now = mClock.getElapsedSinceBootMillis(); 101 addLogValueToHistogram(now - mLastEnableUsage, mHistogramAwareAvailableDurationMs, 102 DURATION_LOG_HISTOGRAM); 103 mAvailableTime += now - mLastEnableUsageInThisLogWindow; 104 mLastEnableUsage = 0; 105 mLastEnableUsageInThisLogWindow = 0; 106 } 107 } 108 109 /** 110 * Push information about a new attach session. 111 */ 112 public void recordAttachSession(int uid, boolean usesIdentityCallback, 113 SparseArray<WifiAwareClientState> clients) { 114 // count the number of clients with the specific uid 115 int currentConcurrentCount = 0; 116 for (int i = 0; i < clients.size(); ++i) { 117 if (clients.valueAt(i).getUid() == uid) { 118 ++currentConcurrentCount; 119 } 120 } 121 122 synchronized (mLock) { 123 AttachData data = mAttachDataByUid.get(uid); 124 if (data == null) { 125 data = new AttachData(); 126 mAttachDataByUid.put(uid, data); 127 } 128 data.mUsesIdentityCallback |= usesIdentityCallback; 129 data.mMaxConcurrentAttaches = Math.max(data.mMaxConcurrentAttaches, 130 currentConcurrentCount); 131 recordAttachStatus(NanStatusType.SUCCESS); 132 } 133 } 134 135 /** 136 * Push information about a new attach session status (recorded when attach session is created). 137 */ 138 public void recordAttachStatus(int status) { 139 synchronized (mLock) { 140 mAttachStatusData.put(status, mAttachStatusData.get(status) + 1); 141 } 142 } 143 144 /** 145 * Push duration information of an attach session. 146 */ 147 public void recordAttachSessionDuration(long creationTime) { 148 synchronized (mLock) { 149 addLogValueToHistogram(mClock.getElapsedSinceBootMillis() - creationTime, 150 mHistogramAttachDuration, 151 DURATION_LOG_HISTOGRAM); 152 } 153 } 154 155 /** 156 * Consolidate all metrics into the proto. 157 */ 158 public WifiMetricsProto.WifiAwareLog consolidateProto() { 159 WifiMetricsProto.WifiAwareLog log = new WifiMetricsProto.WifiAwareLog(); 160 long now = mClock.getElapsedSinceBootMillis(); 161 synchronized (mLock) { 162 log.histogramAwareAvailableDurationMs = histogramToProtoArray( 163 mHistogramAwareAvailableDurationMs, DURATION_LOG_HISTOGRAM); 164 log.availableTimeMs = mAvailableTime; 165 if (mLastEnableUsageInThisLogWindow != 0) { 166 log.availableTimeMs += now - mLastEnableUsageInThisLogWindow; 167 } 168 log.numApps = mAttachDataByUid.size(); 169 log.numAppsUsingIdentityCallback = 0; 170 log.maxConcurrentAttachSessionsInApp = 0; 171 for (AttachData ad: mAttachDataByUid.values()) { 172 if (ad.mUsesIdentityCallback) { 173 ++log.numAppsUsingIdentityCallback; 174 } 175 log.maxConcurrentAttachSessionsInApp = Math.max( 176 log.maxConcurrentAttachSessionsInApp, ad.mMaxConcurrentAttaches); 177 } 178 log.histogramAttachSessionStatus = histogramToProtoArray(mAttachStatusData); 179 log.histogramAttachDurationMs = histogramToProtoArray(mHistogramAttachDuration, 180 DURATION_LOG_HISTOGRAM); 181 } 182 return log; 183 } 184 185 /** 186 * clear Wi-Fi Aware metrics 187 */ 188 public void clear() { 189 long now = mClock.getElapsedSinceBootMillis(); 190 synchronized (mLock) { 191 // don't clear mLastEnableUsage since could be valid for next measurement period 192 mHistogramAwareAvailableDurationMs.clear(); 193 mAvailableTime = 0; 194 if (mLastEnableUsageInThisLogWindow != 0) { 195 mLastEnableUsageInThisLogWindow = now; 196 } 197 mAttachDataByUid.clear(); 198 mAttachStatusData.clear(); 199 mHistogramAttachDuration.clear(); 200 } 201 } 202 203 /** 204 * Dump all WifiAwareMetrics to console (pw) - this method is never called to dump the 205 * serialized metrics (handled by parent WifiMetrics). 206 * 207 * @param fd unused 208 * @param pw PrintWriter for writing dump to 209 * @param args unused 210 */ 211 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 212 synchronized (mLock) { 213 pw.println("mLastEnableUsage:" + mLastEnableUsage); 214 pw.println("mLastEnableUsageInThisLogWindow:" + mLastEnableUsageInThisLogWindow); 215 pw.println("mAvailableTime:" + mAvailableTime); 216 pw.println("mHistogramAwareAvailableDurationMs:"); 217 for (int i = 0; i < mHistogramAwareAvailableDurationMs.size(); ++i) { 218 pw.println(" " + mHistogramAwareAvailableDurationMs.keyAt(i) + ": " 219 + mHistogramAwareAvailableDurationMs.valueAt(i)); 220 } 221 pw.println("mAttachDataByUid:"); 222 for (Map.Entry<Integer, AttachData> ade: mAttachDataByUid.entrySet()) { 223 pw.println(" " + "uid=" + ade.getKey() + ": identity=" 224 + ade.getValue().mUsesIdentityCallback + ", maxConcurrent=" 225 + ade.getValue().mMaxConcurrentAttaches); 226 } 227 pw.println("mAttachStatusData:"); 228 for (int i = 0; i < mAttachStatusData.size(); ++i) { 229 pw.println(" " + mAttachStatusData.keyAt(i) + ": " 230 + mAttachStatusData.valueAt(i)); 231 } 232 pw.println("mHistogramAttachDuration:"); 233 for (int i = 0; i < mHistogramAttachDuration.size(); ++i) { 234 pw.println(" " + mHistogramAttachDuration.keyAt(i) + ": " 235 + mHistogramAttachDuration.valueAt(i)); 236 } 237 } 238 } 239 240 // histogram utilities 241 242 /** 243 * Specifies a ~log histogram consisting of two levels of buckets - a set of N big buckets: 244 * 245 * Buckets starts at: B + P * M^i, where i=0, ... , N-1 (N big buckets) 246 * Each big bucket is divided into S sub-buckets 247 * 248 * Each (big) bucket is M times bigger than the previous one. 249 * 250 * The buckets are then: 251 * #0: B + P * M^0 with S buckets each of width (P*M^1-P*M^0)/S 252 * #1: B + P * M^1 with S buckets each of width (P*M^2-P*M^1)/S 253 * ... 254 * #N-1: B + P * M^(N-1) with S buckets each of width (P*M^N-P*M^(N-1))/S 255 */ 256 @VisibleForTesting 257 public static class HistParms { 258 public HistParms(int b, int p, int m, int s, int n) { 259 this.b = b; 260 this.p = p; 261 this.m = m; 262 this.s = s; 263 this.n = n; 264 265 // derived values 266 mLog = Math.log(m); 267 bb = new double[n]; 268 sbw = new double[n]; 269 bb[0] = b + p; 270 sbw[0] = p * (m - 1.0) / (double) s; 271 for (int i = 1; i < n; ++i) { 272 bb[i] = m * (bb[i - 1] - b) + b; 273 sbw[i] = m * sbw[i - 1]; 274 } 275 } 276 277 // spec 278 public int b; 279 public int p; 280 public int m; 281 public int s; 282 public int n; 283 284 // derived 285 public double mLog; 286 public double[] bb; // bucket base 287 public double[] sbw; // sub-bucket width 288 } 289 290 /** 291 * Adds the input value to the histogram based on the histogram parameters. 292 */ 293 @VisibleForTesting 294 public static int addLogValueToHistogram(long x, SparseIntArray histogram, HistParms hp) { 295 double logArg = (double) (x - hp.b) / (double) hp.p; 296 int bigBucketIndex = -1; 297 if (logArg > 0) { 298 bigBucketIndex = (int) (Math.log(logArg) / hp.mLog); 299 } 300 int subBucketIndex; 301 if (bigBucketIndex < 0) { 302 bigBucketIndex = 0; 303 subBucketIndex = 0; 304 } else if (bigBucketIndex >= hp.n) { 305 bigBucketIndex = hp.n - 1; 306 subBucketIndex = hp.s - 1; 307 } else { 308 subBucketIndex = (int) ((x - hp.bb[bigBucketIndex]) / hp.sbw[bigBucketIndex]); 309 if (subBucketIndex >= hp.s) { // probably a rounding error so move to next big bucket 310 bigBucketIndex++; 311 if (bigBucketIndex >= hp.n) { 312 bigBucketIndex = hp.n - 1; 313 subBucketIndex = hp.s - 1; 314 } else { 315 subBucketIndex = (int) ((x - hp.bb[bigBucketIndex]) / hp.sbw[bigBucketIndex]); 316 } 317 } 318 } 319 int key = bigBucketIndex * hp.s + subBucketIndex; 320 321 // note that get() returns 0 if index not there already 322 int newValue = histogram.get(key) + 1; 323 histogram.put(key, newValue); 324 325 return newValue; 326 } 327 328 /** 329 * Converts the histogram (with the specified histogram parameters) to an array of proto 330 * histogram buckets. 331 */ 332 @VisibleForTesting 333 public static WifiMetricsProto.WifiAwareLog.HistogramBucket[] histogramToProtoArray( 334 SparseIntArray histogram, HistParms hp) { 335 WifiMetricsProto.WifiAwareLog.HistogramBucket[] protoArray = 336 new WifiMetricsProto.WifiAwareLog.HistogramBucket[histogram.size()]; 337 for (int i = 0; i < histogram.size(); ++i) { 338 int key = histogram.keyAt(i); 339 340 protoArray[i] = new WifiMetricsProto.WifiAwareLog.HistogramBucket(); 341 protoArray[i].start = (long) (hp.bb[key / hp.s] + hp.sbw[key / hp.s] * (key % hp.s)); 342 protoArray[i].end = (long) (protoArray[i].start + hp.sbw[key / hp.s]); 343 protoArray[i].count = histogram.valueAt(i); 344 } 345 346 return protoArray; 347 } 348 349 /** 350 * Adds the NanStatusType to the histogram (translating to the proto enumeration of the status). 351 */ 352 public static void addNanHalStatusToHistogram(int halStatus, SparseIntArray histogram) { 353 int protoStatus = convertNanStatusTypeToProtoEnum(halStatus); 354 int newValue = histogram.get(protoStatus) + 1; 355 histogram.put(protoStatus, newValue); 356 } 357 358 /** 359 * Converts a histogram of proto NanStatusTypeEnum to a raw proto histogram. 360 */ 361 @VisibleForTesting 362 public static WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] histogramToProtoArray( 363 SparseIntArray histogram) { 364 WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] protoArray = 365 new WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[histogram.size()]; 366 367 for (int i = 0; i < histogram.size(); ++i) { 368 protoArray[i] = new WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket(); 369 protoArray[i].nanStatusType = histogram.keyAt(i); 370 protoArray[i].count = histogram.valueAt(i); 371 } 372 373 return protoArray; 374 } 375 376 /** 377 * Convert a HAL NanStatusType enum to a Metrics proto enum NanStatusTypeEnum. 378 */ 379 public static int convertNanStatusTypeToProtoEnum(int nanStatusType) { 380 switch (nanStatusType) { 381 case NanStatusType.SUCCESS: 382 return WifiMetricsProto.WifiAwareLog.SUCCESS; 383 case NanStatusType.INTERNAL_FAILURE: 384 return WifiMetricsProto.WifiAwareLog.INTERNAL_FAILURE; 385 case NanStatusType.PROTOCOL_FAILURE: 386 return WifiMetricsProto.WifiAwareLog.PROTOCOL_FAILURE; 387 case NanStatusType.INVALID_SESSION_ID: 388 return WifiMetricsProto.WifiAwareLog.INVALID_SESSION_ID; 389 case NanStatusType.NO_RESOURCES_AVAILABLE: 390 return WifiMetricsProto.WifiAwareLog.NO_RESOURCES_AVAILABLE; 391 case NanStatusType.INVALID_ARGS: 392 return WifiMetricsProto.WifiAwareLog.INVALID_ARGS; 393 case NanStatusType.INVALID_PEER_ID: 394 return WifiMetricsProto.WifiAwareLog.INVALID_PEER_ID; 395 case NanStatusType.INVALID_NDP_ID: 396 return WifiMetricsProto.WifiAwareLog.INVALID_NDP_ID; 397 case NanStatusType.NAN_NOT_ALLOWED: 398 return WifiMetricsProto.WifiAwareLog.NAN_NOT_ALLOWED; 399 case NanStatusType.NO_OTA_ACK: 400 return WifiMetricsProto.WifiAwareLog.NO_OTA_ACK; 401 case NanStatusType.ALREADY_ENABLED: 402 return WifiMetricsProto.WifiAwareLog.ALREADY_ENABLED; 403 case NanStatusType.FOLLOWUP_TX_QUEUE_FULL: 404 return WifiMetricsProto.WifiAwareLog.FOLLOWUP_TX_QUEUE_FULL; 405 case NanStatusType.UNSUPPORTED_CONCURRENCY_NAN_DISABLED: 406 return WifiMetricsProto.WifiAwareLog.UNSUPPORTED_CONCURRENCY_NAN_DISABLED; 407 default: 408 Log.e(TAG, "Unrecognized NanStatusType: " + nanStatusType); 409 return WifiMetricsProto.WifiAwareLog.UNKNOWN_HAL_STATUS; 410 } 411 } 412} 413