1 /* 2 * Copyright (C) 2018 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 17 package com.android.server.wifi.rtt; 18 19 import static com.android.server.wifi.util.MetricsUtils.addValueToLinearHistogram; 20 import static com.android.server.wifi.util.MetricsUtils.addValueToLogHistogram; 21 import static com.android.server.wifi.util.MetricsUtils.linearHistogramToGenericBuckets; 22 import static com.android.server.wifi.util.MetricsUtils.logHistogramToGenericBuckets; 23 24 import android.net.MacAddress; 25 import android.net.wifi.rtt.RangingRequest; 26 import android.net.wifi.rtt.RangingResult; 27 import android.net.wifi.rtt.ResponderConfig; 28 import android.os.WorkSource; 29 import android.util.Log; 30 import android.util.SparseArray; 31 import android.util.SparseIntArray; 32 33 import com.android.server.wifi.Clock; 34 import com.android.server.wifi.hal.WifiRttController; 35 import com.android.server.wifi.proto.nano.WifiMetricsProto; 36 import com.android.server.wifi.util.MetricsUtils; 37 38 import java.io.FileDescriptor; 39 import java.io.PrintWriter; 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.Map; 43 44 /** 45 * Wi-Fi RTT metric container/processor. 46 */ 47 public class RttMetrics { 48 private static final String TAG = "RttMetrics"; 49 private static final boolean VDBG = false; 50 private boolean mVerboseLoggingEnabled = false; 51 52 private final Object mLock = new Object(); 53 private final Clock mClock; 54 55 // accumulated metrics data 56 57 // Histogram: 7 buckets (i=0, ..., 6) of 1 slots in range 10^i -> 10^(i+1) 58 // Buckets: 59 // 1 -> 10 60 // 10 -> 100 61 // 100 -> 1000 62 // 10^3 -> 10^4 63 // 10^4 -> 10^5 64 // 10^5 -> 10^6 65 // 10^5 -> 10^7 (10^7 ms = 160 minutes) 66 private static final MetricsUtils.LogHistParms COUNT_LOG_HISTOGRAM = 67 new MetricsUtils.LogHistParms(0, 1, 10, 1, 7); 68 69 // Histogram for ranging limits in discovery. Indicates the following 7 buckets (in meters): 70 // < 0 71 // [0, 5) 72 // [5, 15) 73 // [15, 30) 74 // [30, 60) 75 // [60, 100) 76 // >= 100 77 private static final int[] DISTANCE_MM_HISTOGRAM = 78 {0, 5 * 1000, 15 * 1000, 30 * 1000, 60 * 1000, 100 * 1000}; 79 // Histogram for duration for ap only measurement. Indicates 5 buckets with 1000 ms interval. 80 private static final int[] MEASUREMENT_DURATION_HISTOGRAM_AP = 81 {1 * 1000, 2 * 1000, 3 * 1000, 4 * 1000}; 82 83 // Histogram for duration for measurement with aware. Indicates 5 buckets with 2000 ms interval. 84 private static final int[] MEASUREMENT_DURATION_HISTOGRAM_AWARE = 85 {2 * 1000, 4 * 1000, 6 * 1000, 8 * 1000}; 86 87 private static final int PEER_AP = 0; 88 private static final int PEER_AWARE = 1; 89 90 private int mNumStartRangingCalls = 0; 91 private SparseIntArray mOverallStatusHistogram = new SparseIntArray(); 92 private SparseIntArray mMeasurementDurationApOnlyHistogram = new SparseIntArray(); 93 private SparseIntArray mMeasurementDurationWithAwareHistogram = new SparseIntArray(); 94 private PerPeerTypeInfo[] mPerPeerTypeInfo; 95 RttMetrics(Clock clock)96 public RttMetrics(Clock clock) { 97 mClock = clock; 98 99 mPerPeerTypeInfo = new PerPeerTypeInfo[2]; 100 mPerPeerTypeInfo[PEER_AP] = new PerPeerTypeInfo(); 101 mPerPeerTypeInfo[PEER_AWARE] = new PerPeerTypeInfo(); 102 } 103 104 private class PerUidInfo { 105 public int numRequests; 106 public long lastRequestMs; 107 108 @Override toString()109 public String toString() { 110 return "numRequests=" + numRequests + ", lastRequestMs=" + lastRequestMs; 111 } 112 } 113 114 private class PerPeerTypeInfo { 115 public int numCalls; 116 public int numIndividualCalls; 117 public SparseArray<PerUidInfo> perUidInfo = new SparseArray<>(); 118 public SparseIntArray numRequestsHistogram = new SparseIntArray(); 119 public SparseIntArray requestGapHistogram = new SparseIntArray(); 120 public SparseIntArray statusHistogram = new SparseIntArray(); 121 public SparseIntArray measuredDistanceHistogram = new SparseIntArray(); 122 123 @Override toString()124 public String toString() { 125 return "numCalls=" + numCalls + ", numIndividualCalls=" + numIndividualCalls 126 + ", perUidInfo=" + perUidInfo + ", numRequestsHistogram=" 127 + numRequestsHistogram + ", requestGapHistogram=" + requestGapHistogram 128 + ", statusHistogram=" + statusHistogram 129 + ", measuredDistanceHistogram=" + measuredDistanceHistogram; 130 } 131 } 132 133 /** 134 * Enable/Disable verbose logging. 135 */ enableVerboseLogging(boolean verboseEnabled)136 public void enableVerboseLogging(boolean verboseEnabled) { 137 mVerboseLoggingEnabled = verboseEnabled; 138 } 139 140 // metric recording API 141 142 /** 143 * Record metrics for the range request. 144 */ recordRequest(WorkSource ws, RangingRequest requests)145 public void recordRequest(WorkSource ws, RangingRequest requests) { 146 mNumStartRangingCalls++; 147 148 int numApRequests = 0; 149 int numAwareRequests = 0; 150 for (ResponderConfig request : requests.mRttPeers) { 151 if (request == null) { 152 continue; 153 } 154 if (request.responderType == ResponderConfig.RESPONDER_AWARE) { 155 numAwareRequests++; 156 } else if (request.responderType == ResponderConfig.RESPONDER_AP) { 157 numApRequests++; 158 } else { 159 if (mVerboseLoggingEnabled) { 160 Log.d(TAG, 161 "Unexpected Responder type: " + request.responderType); 162 } 163 } 164 } 165 166 updatePeerInfoWithRequestInfo(mPerPeerTypeInfo[PEER_AP], ws, numApRequests); 167 updatePeerInfoWithRequestInfo(mPerPeerTypeInfo[PEER_AWARE], ws, numAwareRequests); 168 } 169 170 /** 171 * Record metrics for the range results. 172 */ recordResult(RangingRequest requests, List<RangingResult> results, int measurementDuration)173 public void recordResult(RangingRequest requests, List<RangingResult> results, 174 int measurementDuration) { 175 Map<MacAddress, ResponderConfig> requestEntries = new HashMap<>(); 176 for (ResponderConfig responder : requests.mRttPeers) { 177 requestEntries.put(responder.macAddress, responder); 178 } 179 if (results != null) { 180 boolean containsAwarePeer = false; 181 for (RangingResult result : results) { 182 if (result == null) { 183 continue; 184 } 185 ResponderConfig responder = requestEntries.remove(result.getMacAddress()); 186 if (responder == null) { 187 Log.e(TAG, 188 "recordResult: found a result which doesn't match any requests: " 189 + result); 190 continue; 191 } 192 193 if (responder.responderType == ResponderConfig.RESPONDER_AP) { 194 updatePeerInfoWithResultInfo(mPerPeerTypeInfo[PEER_AP], result); 195 } else if (responder.responderType == ResponderConfig.RESPONDER_AWARE) { 196 containsAwarePeer = true; 197 updatePeerInfoWithResultInfo(mPerPeerTypeInfo[PEER_AWARE], result); 198 } else { 199 Log.e(TAG, "recordResult: unexpected peer type in responder: " + responder); 200 } 201 } 202 if (containsAwarePeer) { 203 addValueToLinearHistogram(measurementDuration, 204 mMeasurementDurationWithAwareHistogram, 205 MEASUREMENT_DURATION_HISTOGRAM_AWARE); 206 } else { 207 addValueToLinearHistogram(measurementDuration, 208 mMeasurementDurationApOnlyHistogram, 209 MEASUREMENT_DURATION_HISTOGRAM_AP); 210 } 211 } 212 213 for (ResponderConfig responder : requestEntries.values()) { 214 PerPeerTypeInfo peerInfo; 215 if (responder.responderType == ResponderConfig.RESPONDER_AP) { 216 peerInfo = mPerPeerTypeInfo[PEER_AP]; 217 } else if (responder.responderType == ResponderConfig.RESPONDER_AWARE) { 218 peerInfo = mPerPeerTypeInfo[PEER_AWARE]; 219 } else { 220 Log.e(TAG, "recordResult: unexpected peer type in responder: " + responder); 221 continue; 222 } 223 peerInfo.statusHistogram.put(WifiMetricsProto.WifiRttLog.MISSING_RESULT, 224 peerInfo.statusHistogram.get(WifiMetricsProto.WifiRttLog.MISSING_RESULT) + 1); 225 } 226 } 227 228 /** 229 * Record metrics for the overall ranging request status. 230 */ recordOverallStatus(int status)231 public void recordOverallStatus(int status) { 232 mOverallStatusHistogram.put(status, mOverallStatusHistogram.get(status) + 1); 233 } 234 updatePeerInfoWithRequestInfo(PerPeerTypeInfo peerInfo, WorkSource ws, int numIndividualCalls)235 private void updatePeerInfoWithRequestInfo(PerPeerTypeInfo peerInfo, WorkSource ws, 236 int numIndividualCalls) { 237 if (numIndividualCalls == 0) { 238 return; 239 } 240 241 long nowMs = mClock.getElapsedSinceBootMillis(); 242 peerInfo.numCalls++; 243 peerInfo.numIndividualCalls += numIndividualCalls; 244 peerInfo.numRequestsHistogram.put(numIndividualCalls, 245 peerInfo.numRequestsHistogram.get(numIndividualCalls) + 1); 246 boolean recordedIntervals = false; 247 248 for (int i = 0; i < ws.size(); ++i) { 249 int uid = ws.getUid(i); 250 251 PerUidInfo perUidInfo = peerInfo.perUidInfo.get(uid); 252 if (perUidInfo == null) { 253 perUidInfo = new PerUidInfo(); 254 } 255 256 perUidInfo.numRequests++; 257 258 if (!recordedIntervals && perUidInfo.lastRequestMs != 0) { 259 recordedIntervals = true; // don't want to record twice 260 addValueToLogHistogram(nowMs - perUidInfo.lastRequestMs, 261 peerInfo.requestGapHistogram, COUNT_LOG_HISTOGRAM); 262 } 263 perUidInfo.lastRequestMs = nowMs; 264 265 peerInfo.perUidInfo.put(uid, perUidInfo); 266 } 267 } 268 updatePeerInfoWithResultInfo(PerPeerTypeInfo peerInfo, RangingResult result)269 private void updatePeerInfoWithResultInfo(PerPeerTypeInfo peerInfo, RangingResult result) { 270 int protoStatus = convertRttStatusTypeToProtoEnum(result.getStatus()); 271 peerInfo.statusHistogram.put(protoStatus, peerInfo.statusHistogram.get(protoStatus) + 1); 272 if (result.getStatus() != RangingResult.STATUS_SUCCESS) { 273 return; 274 } 275 addValueToLinearHistogram(result.getDistanceMm(), peerInfo.measuredDistanceHistogram, 276 DISTANCE_MM_HISTOGRAM); 277 } 278 279 /** 280 * Consolidate all metrics into the proto. 281 */ consolidateProto()282 public WifiMetricsProto.WifiRttLog consolidateProto() { 283 WifiMetricsProto.WifiRttLog log = new WifiMetricsProto.WifiRttLog(); 284 log.rttToAp = new WifiMetricsProto.WifiRttLog.RttToPeerLog(); 285 log.rttToAware = new WifiMetricsProto.WifiRttLog.RttToPeerLog(); 286 synchronized (mLock) { 287 log.numRequests = mNumStartRangingCalls; 288 log.histogramOverallStatus = consolidateOverallStatus(mOverallStatusHistogram); 289 log.histogramMeasurementDurationApOnly = genericBucketsToRttBuckets( 290 linearHistogramToGenericBuckets(mMeasurementDurationApOnlyHistogram, 291 MEASUREMENT_DURATION_HISTOGRAM_AP)); 292 log.histogramMeasurementDurationWithAware = genericBucketsToRttBuckets( 293 linearHistogramToGenericBuckets(mMeasurementDurationWithAwareHistogram, 294 MEASUREMENT_DURATION_HISTOGRAM_AWARE)); 295 296 consolidatePeerType(log.rttToAp, mPerPeerTypeInfo[PEER_AP]); 297 consolidatePeerType(log.rttToAware, mPerPeerTypeInfo[PEER_AWARE]); 298 } 299 return log; 300 } 301 consolidateOverallStatus( SparseIntArray histogram)302 private WifiMetricsProto.WifiRttLog.RttOverallStatusHistogramBucket[] consolidateOverallStatus( 303 SparseIntArray histogram) { 304 WifiMetricsProto.WifiRttLog.RttOverallStatusHistogramBucket[] h = 305 new WifiMetricsProto.WifiRttLog.RttOverallStatusHistogramBucket[histogram.size()]; 306 for (int i = 0; i < histogram.size(); i++) { 307 h[i] = new WifiMetricsProto.WifiRttLog.RttOverallStatusHistogramBucket(); 308 h[i].statusType = histogram.keyAt(i); 309 h[i].count = histogram.valueAt(i); 310 } 311 return h; 312 } 313 consolidatePeerType(WifiMetricsProto.WifiRttLog.RttToPeerLog peerLog, PerPeerTypeInfo peerInfo)314 private void consolidatePeerType(WifiMetricsProto.WifiRttLog.RttToPeerLog peerLog, 315 PerPeerTypeInfo peerInfo) { 316 peerLog.numRequests = peerInfo.numCalls; 317 peerLog.numIndividualRequests = peerInfo.numIndividualCalls; 318 peerLog.numApps = peerInfo.perUidInfo.size(); 319 peerLog.histogramNumPeersPerRequest = consolidateNumPeersPerRequest( 320 peerInfo.numRequestsHistogram); 321 peerLog.histogramNumRequestsPerApp = consolidateNumRequestsPerApp(peerInfo.perUidInfo); 322 peerLog.histogramRequestIntervalMs = genericBucketsToRttBuckets( 323 logHistogramToGenericBuckets(peerInfo.requestGapHistogram, COUNT_LOG_HISTOGRAM)); 324 peerLog.histogramIndividualStatus = consolidateIndividualStatus(peerInfo.statusHistogram); 325 peerLog.histogramDistance = genericBucketsToRttBuckets( 326 linearHistogramToGenericBuckets(peerInfo.measuredDistanceHistogram, 327 DISTANCE_MM_HISTOGRAM)); 328 } 329 330 private WifiMetricsProto.WifiRttLog.RttIndividualStatusHistogramBucket[] consolidateIndividualStatus(SparseIntArray histogram)331 consolidateIndividualStatus(SparseIntArray histogram) { 332 WifiMetricsProto.WifiRttLog.RttIndividualStatusHistogramBucket[] h = 333 new WifiMetricsProto.WifiRttLog.RttIndividualStatusHistogramBucket[histogram.size( 334 )]; 335 for (int i = 0; i < histogram.size(); i++) { 336 h[i] = new WifiMetricsProto.WifiRttLog.RttIndividualStatusHistogramBucket(); 337 h[i].statusType = histogram.keyAt(i); 338 h[i].count = histogram.valueAt(i); 339 } 340 return h; 341 } 342 consolidateNumPeersPerRequest( SparseIntArray data)343 private WifiMetricsProto.WifiRttLog.HistogramBucket[] consolidateNumPeersPerRequest( 344 SparseIntArray data) { 345 WifiMetricsProto.WifiRttLog.HistogramBucket[] protoArray = 346 new WifiMetricsProto.WifiRttLog.HistogramBucket[data.size()]; 347 348 for (int i = 0; i < data.size(); i++) { 349 protoArray[i] = new WifiMetricsProto.WifiRttLog.HistogramBucket(); 350 protoArray[i].start = data.keyAt(i); 351 protoArray[i].end = data.keyAt(i); 352 protoArray[i].count = data.valueAt(i); 353 } 354 355 return protoArray; 356 } 357 consolidateNumRequestsPerApp( SparseArray<PerUidInfo> perUidInfos)358 private WifiMetricsProto.WifiRttLog.HistogramBucket[] consolidateNumRequestsPerApp( 359 SparseArray<PerUidInfo> perUidInfos) { 360 SparseIntArray histogramNumRequestsPerUid = new SparseIntArray(); 361 for (int i = 0; i < perUidInfos.size(); i++) { 362 addValueToLogHistogram(perUidInfos.valueAt(i).numRequests, histogramNumRequestsPerUid, 363 COUNT_LOG_HISTOGRAM); 364 } 365 366 return genericBucketsToRttBuckets(logHistogramToGenericBuckets( 367 histogramNumRequestsPerUid, COUNT_LOG_HISTOGRAM)); 368 } 369 genericBucketsToRttBuckets( MetricsUtils.GenericBucket[] genericHistogram)370 private WifiMetricsProto.WifiRttLog.HistogramBucket[] genericBucketsToRttBuckets( 371 MetricsUtils.GenericBucket[] genericHistogram) { 372 WifiMetricsProto.WifiRttLog.HistogramBucket[] histogram = 373 new WifiMetricsProto.WifiRttLog.HistogramBucket[genericHistogram.length]; 374 for (int i = 0; i < genericHistogram.length; i++) { 375 histogram[i] = new WifiMetricsProto.WifiRttLog.HistogramBucket(); 376 histogram[i].start = genericHistogram[i].start; 377 histogram[i].end = genericHistogram[i].end; 378 histogram[i].count = genericHistogram[i].count; 379 } 380 return histogram; 381 } 382 383 /** 384 * Dump all RttMetrics to console (pw) - this method is never called to dump the serialized 385 * metrics (handled by parent WifiMetrics). 386 * 387 * @param fd unused 388 * @param pw PrintWriter for writing dump to 389 * @param args unused 390 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)391 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 392 synchronized (mLock) { 393 pw.println("RTT Metrics:"); 394 pw.println("mNumStartRangingCalls:" + mNumStartRangingCalls); 395 pw.println("mOverallStatusHistogram:" + mOverallStatusHistogram); 396 pw.println("mMeasurementDurationApOnlyHistogram" + mMeasurementDurationApOnlyHistogram); 397 pw.println("mMeasurementDurationWithAwareHistogram" 398 + mMeasurementDurationWithAwareHistogram); 399 pw.println("AP:" + mPerPeerTypeInfo[PEER_AP]); 400 pw.println("AWARE:" + mPerPeerTypeInfo[PEER_AWARE]); 401 } 402 } 403 404 /** 405 * clear Wi-Fi RTT metrics 406 */ clear()407 public void clear() { 408 synchronized (mLock) { 409 mNumStartRangingCalls = 0; 410 mOverallStatusHistogram.clear(); 411 mPerPeerTypeInfo[PEER_AP] = new PerPeerTypeInfo(); 412 mPerPeerTypeInfo[PEER_AWARE] = new PerPeerTypeInfo(); 413 mMeasurementDurationApOnlyHistogram.clear(); 414 mMeasurementDurationWithAwareHistogram.clear(); 415 } 416 } 417 418 /** 419 * Convert a HAL RttStatus enum to a Metrics proto enum RttIndividualStatusTypeEnum. 420 */ convertRttStatusTypeToProtoEnum(int rttStatusType)421 public static int convertRttStatusTypeToProtoEnum(int rttStatusType) { 422 switch (rttStatusType) { 423 case WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS: 424 return WifiMetricsProto.WifiRttLog.SUCCESS; 425 case WifiRttController.FRAMEWORK_RTT_STATUS_FAILURE: 426 return WifiMetricsProto.WifiRttLog.FAILURE; 427 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_NO_RSP: 428 return WifiMetricsProto.WifiRttLog.FAIL_NO_RSP; 429 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_REJECTED: 430 return WifiMetricsProto.WifiRttLog.FAIL_REJECTED; 431 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_NOT_SCHEDULED_YET: 432 return WifiMetricsProto.WifiRttLog.FAIL_NOT_SCHEDULED_YET; 433 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_TM_TIMEOUT: 434 return WifiMetricsProto.WifiRttLog.FAIL_TM_TIMEOUT; 435 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL: 436 return WifiMetricsProto.WifiRttLog.FAIL_AP_ON_DIFF_CHANNEL; 437 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_NO_CAPABILITY: 438 return WifiMetricsProto.WifiRttLog.FAIL_NO_CAPABILITY; 439 case WifiRttController.FRAMEWORK_RTT_STATUS_ABORTED: 440 return WifiMetricsProto.WifiRttLog.ABORTED; 441 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_INVALID_TS: 442 return WifiMetricsProto.WifiRttLog.FAIL_INVALID_TS; 443 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_PROTOCOL: 444 return WifiMetricsProto.WifiRttLog.FAIL_PROTOCOL; 445 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_SCHEDULE: 446 return WifiMetricsProto.WifiRttLog.FAIL_SCHEDULE; 447 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_BUSY_TRY_LATER: 448 return WifiMetricsProto.WifiRttLog.FAIL_BUSY_TRY_LATER; 449 case WifiRttController.FRAMEWORK_RTT_STATUS_INVALID_REQ: 450 return WifiMetricsProto.WifiRttLog.INVALID_REQ; 451 case WifiRttController.FRAMEWORK_RTT_STATUS_NO_WIFI: 452 return WifiMetricsProto.WifiRttLog.NO_WIFI; 453 case WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE: 454 return WifiMetricsProto.WifiRttLog.FAIL_FTM_PARAM_OVERRIDE; 455 default: 456 Log.e(TAG, "Unrecognized RttStatus: " + rttStatusType); 457 return WifiMetricsProto.WifiRttLog.UNKNOWN; 458 } 459 } 460 } 461