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