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 
17 package com.android.server.wifi.aware;
18 
19 import android.hardware.wifi.V1_0.NanStatusType;
20 import android.net.wifi.aware.WifiAwareNetworkSpecifier;
21 import android.text.TextUtils;
22 import android.util.Log;
23 import android.util.SparseArray;
24 import android.util.SparseIntArray;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.server.wifi.Clock;
28 import com.android.server.wifi.nano.WifiMetricsProto;
29 import com.android.server.wifi.util.MetricsUtils;
30 
31 import java.io.FileDescriptor;
32 import java.io.PrintWriter;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.Map;
37 import java.util.Set;
38 
39 /**
40  * Wi-Fi Aware metric container/processor.
41  */
42 public class WifiAwareMetrics {
43     private static final String TAG = "WifiAwareMetrics";
44     private static final boolean VDBG = false;
45     /* package */ boolean mDbg = false;
46 
47     // Histogram: 8 buckets (i=0, ..., 7) of 9 slots in range 10^i -> 10^(i+1)
48     // Buckets:
49     //    1 -> 10: 9 @ 1
50     //    10 -> 100: 9 @ 10
51     //    100 -> 1000: 9 @ 10^2
52     //    10^3 -> 10^4: 9 @ 10^3
53     //    10^4 -> 10^5: 9 @ 10^4
54     //    10^5 -> 10^6: 9 @ 10^5
55     //    10^6 -> 10^7: 9 @ 10^6
56     //    10^7 -> 10^8: 9 @ 10^7 --> 10^8 ms -> 10^5s -> 28 hours
57     private static final MetricsUtils.LogHistParms DURATION_LOG_HISTOGRAM =
58             new MetricsUtils.LogHistParms(0, 1, 10, 9, 8);
59 
60     // Histogram for ranging limits in discovery. Indicates the following 5 buckets (in meters):
61     //   < 10
62     //   [10, 30)
63     //   [30, 60)
64     //   [60, 100)
65     //   >= 100
66     private static final int[] RANGING_LIMIT_METERS = { 10, 30, 60, 100 };
67 
68     private final Object mLock = new Object();
69     private final Clock mClock;
70 
71     // enableUsage/disableUsage data
72     private long mLastEnableUsageMs = 0;
73     private long mLastEnableUsageInThisSampleWindowMs = 0;
74     private long mAvailableTimeMs = 0;
75     private SparseIntArray mHistogramAwareAvailableDurationMs = new SparseIntArray();
76 
77     // enabled data
78     private long mLastEnableAwareMs = 0;
79     private long mLastEnableAwareInThisSampleWindowMs = 0;
80     private long mEnabledTimeMs = 0;
81     private SparseIntArray mHistogramAwareEnabledDurationMs = new SparseIntArray();
82 
83     // attach data
84     private static class AttachData {
85         boolean mUsesIdentityCallback; // do any attach sessions of the UID use identity callback
86         int mMaxConcurrentAttaches;
87     }
88     private Map<Integer, AttachData> mAttachDataByUid = new HashMap<>();
89     private SparseIntArray mAttachStatusData = new SparseIntArray();
90     private SparseIntArray mHistogramAttachDuration = new SparseIntArray();
91 
92     // discovery data
93     private int mMaxPublishInApp = 0;
94     private int mMaxSubscribeInApp = 0;
95     private int mMaxDiscoveryInApp = 0;
96     private int mMaxPublishInSystem = 0;
97     private int mMaxSubscribeInSystem = 0;
98     private int mMaxDiscoveryInSystem = 0;
99     private SparseIntArray mPublishStatusData = new SparseIntArray();
100     private SparseIntArray mSubscribeStatusData = new SparseIntArray();
101     private SparseIntArray mHistogramPublishDuration = new SparseIntArray();
102     private SparseIntArray mHistogramSubscribeDuration = new SparseIntArray();
103     private Set<Integer> mAppsWithDiscoverySessionResourceFailure = new HashSet<>();
104 
105     // discovery with ranging data
106     private int mMaxPublishWithRangingInApp = 0;
107     private int mMaxSubscribeWithRangingInApp = 0;
108     private int mMaxPublishWithRangingInSystem = 0;
109     private int mMaxSubscribeWithRangingInSystem = 0;
110     private SparseIntArray mHistogramSubscribeGeofenceMin = new SparseIntArray();
111     private SparseIntArray mHistogramSubscribeGeofenceMax = new SparseIntArray();
112     private int mNumSubscribesWithRanging = 0;
113     private int mNumMatchesWithRanging = 0;
114     private int mNumMatchesWithoutRangingForRangingEnabledSubscribes = 0;
115 
116     // data-path (NDI/NDP) data
117     private int mMaxNdiInApp = 0;
118     private int mMaxNdpInApp = 0;
119     private int mMaxSecureNdpInApp = 0;
120     private int mMaxNdiInSystem = 0;
121     private int mMaxNdpInSystem = 0;
122     private int mMaxSecureNdpInSystem = 0;
123     private int mMaxNdpPerNdi = 0;
124     private SparseIntArray mInBandNdpStatusData = new SparseIntArray();
125     private SparseIntArray mOutOfBandNdpStatusData = new SparseIntArray();
126 
127     private SparseIntArray mNdpCreationTimeDuration = new SparseIntArray();
128     private long mNdpCreationTimeMin = -1;
129     private long mNdpCreationTimeMax = 0;
130     private long mNdpCreationTimeSum = 0;
131     private long mNdpCreationTimeSumSq = 0;
132     private long mNdpCreationTimeNumSamples = 0;
133 
134     private SparseIntArray mHistogramNdpDuration = new SparseIntArray();
135 
WifiAwareMetrics(Clock clock)136     public WifiAwareMetrics(Clock clock) {
137         mClock = clock;
138     }
139 
140     /**
141      * Push usage stats for WifiAwareStateMachine.enableUsage() to
142      * histogram_aware_available_duration_ms.
143      */
recordEnableUsage()144     public void recordEnableUsage() {
145         synchronized (mLock) {
146             if (mLastEnableUsageMs != 0) {
147                 Log.w(TAG, "enableUsage: mLastEnableUsage*Ms initialized!?");
148             }
149             mLastEnableUsageMs = mClock.getElapsedSinceBootMillis();
150             mLastEnableUsageInThisSampleWindowMs = mLastEnableUsageMs;
151         }
152     }
153 
154     /**
155      * Push usage stats for WifiAwareStateMachine.disableUsage() to
156      * histogram_aware_available_duration_ms.
157      */
158 
recordDisableUsage()159     public void recordDisableUsage() {
160         synchronized (mLock) {
161             if (mLastEnableUsageMs == 0) {
162                 Log.e(TAG, "disableUsage: mLastEnableUsage not initialized!?");
163                 return;
164             }
165 
166             long now = mClock.getElapsedSinceBootMillis();
167             MetricsUtils.addValueToLogHistogram(now - mLastEnableUsageMs,
168                     mHistogramAwareAvailableDurationMs, DURATION_LOG_HISTOGRAM);
169             mAvailableTimeMs += now - mLastEnableUsageInThisSampleWindowMs;
170             mLastEnableUsageMs = 0;
171             mLastEnableUsageInThisSampleWindowMs = 0;
172         }
173     }
174 
175     /**
176      * Push usage stats of Aware actually being enabled on-the-air: start
177      */
recordEnableAware()178     public void recordEnableAware() {
179         synchronized (mLock) {
180             if (mLastEnableAwareMs != 0) {
181                 return; // already enabled
182             }
183             mLastEnableAwareMs = mClock.getElapsedSinceBootMillis();
184             mLastEnableAwareInThisSampleWindowMs = mLastEnableAwareMs;
185         }
186     }
187 
188     /**
189      * Push usage stats of Aware actually being enabled on-the-air: stop (disable)
190      */
recordDisableAware()191     public void recordDisableAware() {
192         synchronized (mLock) {
193             if (mLastEnableAwareMs == 0) {
194                 return; // already disabled
195             }
196 
197             long now = mClock.getElapsedSinceBootMillis();
198             MetricsUtils.addValueToLogHistogram(now - mLastEnableAwareMs,
199                     mHistogramAwareEnabledDurationMs, DURATION_LOG_HISTOGRAM);
200             mEnabledTimeMs += now - mLastEnableAwareInThisSampleWindowMs;
201             mLastEnableAwareMs = 0;
202             mLastEnableAwareInThisSampleWindowMs = 0;
203         }
204     }
205 
206     /**
207      * Push information about a new attach session.
208      */
recordAttachSession(int uid, boolean usesIdentityCallback, SparseArray<WifiAwareClientState> clients)209     public void recordAttachSession(int uid, boolean usesIdentityCallback,
210             SparseArray<WifiAwareClientState> clients) {
211         // count the number of clients with the specific uid
212         int currentConcurrentCount = 0;
213         for (int i = 0; i < clients.size(); ++i) {
214             if (clients.valueAt(i).getUid() == uid) {
215                 ++currentConcurrentCount;
216             }
217         }
218 
219         synchronized (mLock) {
220             AttachData data = mAttachDataByUid.get(uid);
221             if (data == null) {
222                 data = new AttachData();
223                 mAttachDataByUid.put(uid, data);
224             }
225             data.mUsesIdentityCallback |= usesIdentityCallback;
226             data.mMaxConcurrentAttaches = Math.max(data.mMaxConcurrentAttaches,
227                     currentConcurrentCount);
228             recordAttachStatus(NanStatusType.SUCCESS);
229         }
230     }
231 
232     /**
233      * Push information about a new attach session status (recorded when attach session is created).
234      */
recordAttachStatus(int status)235     public void recordAttachStatus(int status) {
236         synchronized (mLock) {
237             mAttachStatusData.put(status, mAttachStatusData.get(status) + 1);
238         }
239     }
240 
241     /**
242      * Push duration information of an attach session.
243      */
recordAttachSessionDuration(long creationTime)244     public void recordAttachSessionDuration(long creationTime) {
245         synchronized (mLock) {
246             MetricsUtils.addValueToLogHistogram(mClock.getElapsedSinceBootMillis() - creationTime,
247                     mHistogramAttachDuration, DURATION_LOG_HISTOGRAM);
248         }
249     }
250 
251     /**
252      * Push information about the new discovery session.
253      */
recordDiscoverySession(int uid, SparseArray<WifiAwareClientState> clients)254     public void recordDiscoverySession(int uid, SparseArray<WifiAwareClientState> clients) {
255         recordDiscoverySessionInternal(uid, clients, false, -1, -1);
256     }
257 
258     /**
259      * Push information about the new discovery session with ranging enabled
260      */
recordDiscoverySessionWithRanging(int uid, boolean isSubscriberWithRanging, int minRange, int maxRange, SparseArray<WifiAwareClientState> clients)261     public void recordDiscoverySessionWithRanging(int uid, boolean isSubscriberWithRanging,
262             int minRange, int maxRange, SparseArray<WifiAwareClientState> clients) {
263         recordDiscoverySessionInternal(uid, clients, isSubscriberWithRanging, minRange, maxRange);
264     }
265 
266     /**
267      * Internal combiner of discovery session information.
268      */
recordDiscoverySessionInternal(int uid, SparseArray<WifiAwareClientState> clients, boolean isRangingEnabledSubscriber, int minRange, int maxRange)269     private void recordDiscoverySessionInternal(int uid, SparseArray<WifiAwareClientState> clients,
270             boolean isRangingEnabledSubscriber, int minRange, int maxRange) {
271         // count the number of sessions per uid and overall
272         int numPublishesInSystem = 0;
273         int numSubscribesInSystem = 0;
274         int numPublishesOnUid = 0;
275         int numSubscribesOnUid = 0;
276 
277         int numPublishesWithRangingInSystem = 0;
278         int numSubscribesWithRangingInSystem = 0;
279         int numPublishesWithRangingOnUid = 0;
280         int numSubscribesWithRangingOnUid = 0;
281 
282         for (int i = 0; i < clients.size(); ++i) {
283             WifiAwareClientState client = clients.valueAt(i);
284             boolean sameUid = client.getUid() == uid;
285 
286             SparseArray<WifiAwareDiscoverySessionState> sessions = client.getSessions();
287             for (int j = 0; j < sessions.size(); ++j) {
288                 WifiAwareDiscoverySessionState session = sessions.valueAt(j);
289                 boolean isRangingEnabledForThisSession = session.isRangingEnabled();
290 
291                 if (session.isPublishSession()) {
292                     numPublishesInSystem += 1;
293                     if (isRangingEnabledForThisSession) {
294                         numPublishesWithRangingInSystem += 1;
295                     }
296                     if (sameUid) {
297                         numPublishesOnUid += 1;
298                         if (isRangingEnabledForThisSession) {
299                             numPublishesWithRangingOnUid += 1;
300                         }
301                     }
302                 } else {
303                     numSubscribesInSystem += 1;
304                     if (isRangingEnabledForThisSession) {
305                         numSubscribesWithRangingInSystem += 1;
306                     }
307                     if (sameUid) {
308                         numSubscribesOnUid += 1;
309                         if (isRangingEnabledForThisSession) {
310                             numSubscribesWithRangingOnUid += 1;
311                         }
312                     }
313                 }
314             }
315         }
316 
317         synchronized (mLock) {
318             mMaxPublishInApp = Math.max(mMaxPublishInApp, numPublishesOnUid);
319             mMaxSubscribeInApp = Math.max(mMaxSubscribeInApp, numSubscribesOnUid);
320             mMaxDiscoveryInApp = Math.max(mMaxDiscoveryInApp,
321                     numPublishesOnUid + numSubscribesOnUid);
322             mMaxPublishInSystem = Math.max(mMaxPublishInSystem, numPublishesInSystem);
323             mMaxSubscribeInSystem = Math.max(mMaxSubscribeInSystem, numSubscribesInSystem);
324             mMaxDiscoveryInSystem = Math.max(mMaxDiscoveryInSystem,
325                     numPublishesInSystem + numSubscribesInSystem);
326 
327             mMaxPublishWithRangingInApp = Math.max(mMaxPublishWithRangingInApp,
328                     numPublishesWithRangingOnUid);
329             mMaxSubscribeWithRangingInApp = Math.max(mMaxSubscribeWithRangingInApp,
330                     numSubscribesWithRangingOnUid);
331             mMaxPublishWithRangingInSystem = Math.max(mMaxPublishWithRangingInSystem,
332                     numPublishesWithRangingInSystem);
333             mMaxSubscribeWithRangingInSystem = Math.max(mMaxSubscribeWithRangingInSystem,
334                     numSubscribesWithRangingInSystem);
335             if (isRangingEnabledSubscriber) {
336                 mNumSubscribesWithRanging += 1;
337             }
338 
339             if (minRange != -1) {
340                 MetricsUtils.addValueToLinearHistogram(minRange, mHistogramSubscribeGeofenceMin,
341                         RANGING_LIMIT_METERS);
342             }
343             if (maxRange != -1) {
344                 MetricsUtils.addValueToLinearHistogram(maxRange, mHistogramSubscribeGeofenceMax,
345                         RANGING_LIMIT_METERS);
346             }
347         }
348     }
349 
350     /**
351      * Push information about a new discovery session status (recorded when the discovery session is
352      * created).
353      */
recordDiscoveryStatus(int uid, int status, boolean isPublish)354     public void recordDiscoveryStatus(int uid, int status, boolean isPublish) {
355         synchronized (mLock) {
356             if (isPublish) {
357                 mPublishStatusData.put(status, mPublishStatusData.get(status) + 1);
358             } else {
359                 mSubscribeStatusData.put(status, mSubscribeStatusData.get(status) + 1);
360             }
361 
362             if (status == NanStatusType.NO_RESOURCES_AVAILABLE) {
363                 mAppsWithDiscoverySessionResourceFailure.add(uid);
364             }
365         }
366     }
367 
368     /**
369      * Push duration information of a discovery session.
370      */
recordDiscoverySessionDuration(long creationTime, boolean isPublish)371     public void recordDiscoverySessionDuration(long creationTime, boolean isPublish) {
372         synchronized (mLock) {
373             MetricsUtils.addValueToLogHistogram(mClock.getElapsedSinceBootMillis() - creationTime,
374                     isPublish ? mHistogramPublishDuration : mHistogramSubscribeDuration,
375                     DURATION_LOG_HISTOGRAM);
376         }
377     }
378 
379     /**
380      * Push information about Match indication (aka service discovered) for subscribe sessions
381      * which enabled ranging. Collect information about whether or not service discovery was
382      * triggered with ranging information or without (i.e. ranging disabled for some reason).
383      */
recordMatchIndicationForRangeEnabledSubscribe(boolean rangeProvided)384     public void recordMatchIndicationForRangeEnabledSubscribe(boolean rangeProvided) {
385         if (rangeProvided) {
386             mNumMatchesWithRanging++;
387         } else {
388             mNumMatchesWithoutRangingForRangingEnabledSubscribes++;
389         }
390     }
391 
392     /**
393      * Record NDP (and by extension NDI) usage - on successful creation of an NDP.
394      */
recordNdpCreation(int uid, Map<WifiAwareNetworkSpecifier, WifiAwareDataPathStateManager .AwareNetworkRequestInformation> networkRequestCache)395     public void recordNdpCreation(int uid,
396             Map<WifiAwareNetworkSpecifier, WifiAwareDataPathStateManager
397                     .AwareNetworkRequestInformation> networkRequestCache) {
398         int numNdpInApp = 0;
399         int numSecureNdpInApp = 0;
400         int numNdpInSystem = 0;
401         int numSecureNdpInSystem = 0;
402 
403         Map<String, Integer> ndpPerNdiMap = new HashMap<>();
404         Set<String> ndiInApp = new HashSet<>();
405         Set<String> ndiInSystem = new HashSet<>();
406 
407         for (WifiAwareDataPathStateManager.AwareNetworkRequestInformation anri :
408                 networkRequestCache.values()) {
409             if (anri.state
410                     != WifiAwareDataPathStateManager.AwareNetworkRequestInformation
411                     .STATE_CONFIRMED) {
412                 continue; // only count completed (up-and-running) NDPs
413             }
414 
415             boolean sameUid = anri.uid == uid;
416             boolean isSecure = !TextUtils.isEmpty(anri.networkSpecifier.passphrase) || (
417                     anri.networkSpecifier.pmk != null && anri.networkSpecifier.pmk.length != 0);
418 
419             // in-app stats
420             if (sameUid) {
421                 numNdpInApp += 1;
422                 if (isSecure) {
423                     numSecureNdpInApp += 1;
424                 }
425 
426                 ndiInApp.add(anri.interfaceName);
427             }
428 
429             // system stats
430             numNdpInSystem += 1;
431             if (isSecure) {
432                 numSecureNdpInSystem += 1;
433             }
434 
435             // ndp/ndi stats
436             Integer ndpCount = ndpPerNdiMap.get(anri.interfaceName);
437             if (ndpCount == null) {
438                 ndpPerNdiMap.put(anri.interfaceName, 1);
439             } else {
440                 ndpPerNdiMap.put(anri.interfaceName, ndpCount + 1);
441             }
442 
443             // ndi stats
444             ndiInSystem.add(anri.interfaceName);
445         }
446 
447         synchronized (mLock) {
448             mMaxNdiInApp = Math.max(mMaxNdiInApp, ndiInApp.size());
449             mMaxNdpInApp = Math.max(mMaxNdpInApp, numNdpInApp);
450             mMaxSecureNdpInApp = Math.max(mMaxSecureNdpInApp, numSecureNdpInApp);
451             mMaxNdiInSystem = Math.max(mMaxNdiInSystem, ndiInSystem.size());
452             mMaxNdpInSystem = Math.max(mMaxNdpInSystem, numNdpInSystem);
453             mMaxSecureNdpInSystem = Math.max(mMaxSecureNdpInSystem, numSecureNdpInSystem);
454             mMaxNdpPerNdi = Math.max(mMaxNdpPerNdi, Collections.max(ndpPerNdiMap.values()));
455         }
456     }
457 
458     /**
459      * Record the completion status of NDP negotiation. There are multiple steps in NDP negotiation
460      * a failure on any aborts the process and is recorded. A success on intermediate stages is
461      * not recorded - only the final success.
462      */
recordNdpStatus(int status, boolean isOutOfBand, long startTimestamp)463     public void recordNdpStatus(int status, boolean isOutOfBand, long startTimestamp) {
464         synchronized (mLock) {
465             if (isOutOfBand) {
466                 mOutOfBandNdpStatusData.put(status, mOutOfBandNdpStatusData.get(status) + 1);
467             } else {
468                 mInBandNdpStatusData.put(status, mOutOfBandNdpStatusData.get(status) + 1);
469             }
470 
471             if (status == NanStatusType.SUCCESS) {
472                 long creationTime = mClock.getElapsedSinceBootMillis() - startTimestamp;
473                 MetricsUtils.addValueToLogHistogram(creationTime, mNdpCreationTimeDuration,
474                         DURATION_LOG_HISTOGRAM);
475                 mNdpCreationTimeMin = (mNdpCreationTimeMin == -1) ? creationTime : Math.min(
476                         mNdpCreationTimeMin, creationTime);
477                 mNdpCreationTimeMax = Math.max(mNdpCreationTimeMax, creationTime);
478                 mNdpCreationTimeSum += creationTime;
479                 mNdpCreationTimeSumSq += creationTime * creationTime;
480                 mNdpCreationTimeNumSamples += 1;
481             }
482         }
483     }
484 
485     /**
486      * Record the duration of the NDP session. The creation time is assumed to be the time at
487      * which a confirm message was received (i.e. the end of the setup negotiation).
488      */
recordNdpSessionDuration(long creationTime)489     public void recordNdpSessionDuration(long creationTime) {
490         synchronized (mLock) {
491             MetricsUtils.addValueToLogHistogram(mClock.getElapsedSinceBootMillis() - creationTime,
492                     mHistogramNdpDuration, DURATION_LOG_HISTOGRAM);
493         }
494     }
495 
496     /**
497      * Consolidate all metrics into the proto.
498      */
consolidateProto()499     public WifiMetricsProto.WifiAwareLog consolidateProto() {
500         WifiMetricsProto.WifiAwareLog log = new WifiMetricsProto.WifiAwareLog();
501         long now = mClock.getElapsedSinceBootMillis();
502         synchronized (mLock) {
503             log.histogramAwareAvailableDurationMs = histogramToProtoArray(
504                     MetricsUtils.logHistogramToGenericBuckets(mHistogramAwareAvailableDurationMs,
505                             DURATION_LOG_HISTOGRAM));
506             log.availableTimeMs = mAvailableTimeMs;
507             if (mLastEnableUsageInThisSampleWindowMs != 0) {
508                 log.availableTimeMs += now - mLastEnableUsageInThisSampleWindowMs;
509             }
510 
511             log.histogramAwareEnabledDurationMs = histogramToProtoArray(
512                     MetricsUtils.logHistogramToGenericBuckets(mHistogramAwareEnabledDurationMs,
513                             DURATION_LOG_HISTOGRAM));
514             log.enabledTimeMs = mEnabledTimeMs;
515             if (mLastEnableAwareInThisSampleWindowMs != 0) {
516                 log.enabledTimeMs += now - mLastEnableAwareInThisSampleWindowMs;
517             }
518 
519             log.numApps = mAttachDataByUid.size();
520             log.numAppsUsingIdentityCallback = 0;
521             log.maxConcurrentAttachSessionsInApp = 0;
522             for (AttachData ad: mAttachDataByUid.values()) {
523                 if (ad.mUsesIdentityCallback) {
524                     ++log.numAppsUsingIdentityCallback;
525                 }
526                 log.maxConcurrentAttachSessionsInApp = Math.max(
527                         log.maxConcurrentAttachSessionsInApp, ad.mMaxConcurrentAttaches);
528             }
529             log.histogramAttachSessionStatus = histogramToProtoArray(mAttachStatusData);
530             log.histogramAttachDurationMs = histogramToProtoArray(
531                     MetricsUtils.logHistogramToGenericBuckets(mHistogramAttachDuration,
532                             DURATION_LOG_HISTOGRAM));
533 
534             log.maxConcurrentPublishInApp = mMaxPublishInApp;
535             log.maxConcurrentSubscribeInApp = mMaxSubscribeInApp;
536             log.maxConcurrentDiscoverySessionsInApp = mMaxDiscoveryInApp;
537             log.maxConcurrentPublishInSystem = mMaxPublishInSystem;
538             log.maxConcurrentSubscribeInSystem = mMaxSubscribeInSystem;
539             log.maxConcurrentDiscoverySessionsInSystem = mMaxDiscoveryInSystem;
540             log.histogramPublishStatus = histogramToProtoArray(mPublishStatusData);
541             log.histogramSubscribeStatus = histogramToProtoArray(mSubscribeStatusData);
542             log.numAppsWithDiscoverySessionFailureOutOfResources =
543                     mAppsWithDiscoverySessionResourceFailure.size();
544             log.histogramPublishSessionDurationMs = histogramToProtoArray(
545                     MetricsUtils.logHistogramToGenericBuckets(mHistogramPublishDuration,
546                             DURATION_LOG_HISTOGRAM));
547             log.histogramSubscribeSessionDurationMs = histogramToProtoArray(
548                     MetricsUtils.logHistogramToGenericBuckets(mHistogramSubscribeDuration,
549                             DURATION_LOG_HISTOGRAM));
550 
551             log.maxConcurrentPublishWithRangingInApp = mMaxPublishWithRangingInApp;
552             log.maxConcurrentSubscribeWithRangingInApp = mMaxSubscribeWithRangingInApp;
553             log.maxConcurrentPublishWithRangingInSystem = mMaxPublishWithRangingInSystem;
554             log.maxConcurrentSubscribeWithRangingInSystem = mMaxSubscribeWithRangingInSystem;
555             log.histogramSubscribeGeofenceMin = histogramToProtoArray(
556                     MetricsUtils.linearHistogramToGenericBuckets(mHistogramSubscribeGeofenceMin,
557                             RANGING_LIMIT_METERS));
558             log.histogramSubscribeGeofenceMax = histogramToProtoArray(
559                     MetricsUtils.linearHistogramToGenericBuckets(mHistogramSubscribeGeofenceMax,
560                             RANGING_LIMIT_METERS));
561             log.numSubscribesWithRanging = mNumSubscribesWithRanging;
562             log.numMatchesWithRanging = mNumMatchesWithRanging;
563             log.numMatchesWithoutRangingForRangingEnabledSubscribes =
564                     mNumMatchesWithoutRangingForRangingEnabledSubscribes;
565 
566             log.maxConcurrentNdiInApp = mMaxNdiInApp;
567             log.maxConcurrentNdiInSystem = mMaxNdiInSystem;
568             log.maxConcurrentNdpInApp = mMaxNdpInApp;
569             log.maxConcurrentNdpInSystem = mMaxNdpInSystem;
570             log.maxConcurrentSecureNdpInApp = mMaxSecureNdpInApp;
571             log.maxConcurrentSecureNdpInSystem = mMaxSecureNdpInSystem;
572             log.maxConcurrentNdpPerNdi = mMaxNdpPerNdi;
573             log.histogramRequestNdpStatus = histogramToProtoArray(mInBandNdpStatusData);
574             log.histogramRequestNdpOobStatus = histogramToProtoArray(mOutOfBandNdpStatusData);
575 
576             log.histogramNdpCreationTimeMs = histogramToProtoArray(
577                     MetricsUtils.logHistogramToGenericBuckets(mNdpCreationTimeDuration,
578                             DURATION_LOG_HISTOGRAM));
579             log.ndpCreationTimeMsMin = mNdpCreationTimeMin;
580             log.ndpCreationTimeMsMax = mNdpCreationTimeMax;
581             log.ndpCreationTimeMsSum = mNdpCreationTimeSum;
582             log.ndpCreationTimeMsSumOfSq = mNdpCreationTimeSumSq;
583             log.ndpCreationTimeMsNumSamples = mNdpCreationTimeNumSamples;
584 
585             log.histogramNdpSessionDurationMs = histogramToProtoArray(
586                     MetricsUtils.logHistogramToGenericBuckets(mHistogramNdpDuration,
587                             DURATION_LOG_HISTOGRAM));
588         }
589         return log;
590     }
591 
592     /**
593      * clear Wi-Fi Aware metrics
594      */
clear()595     public void clear() {
596         long now = mClock.getElapsedSinceBootMillis();
597         synchronized (mLock) {
598             // don't clear mLastEnableUsage since could be valid for next measurement period
599             mHistogramAwareAvailableDurationMs.clear();
600             mAvailableTimeMs = 0;
601             if (mLastEnableUsageInThisSampleWindowMs != 0) {
602                 mLastEnableUsageInThisSampleWindowMs = now;
603             }
604 
605             // don't clear mLastEnableAware since could be valid for next measurement period
606             mHistogramAwareEnabledDurationMs.clear();
607             mEnabledTimeMs = 0;
608             if (mLastEnableAwareInThisSampleWindowMs != 0) {
609                 mLastEnableAwareInThisSampleWindowMs = now;
610             }
611 
612             mAttachDataByUid.clear();
613             mAttachStatusData.clear();
614             mHistogramAttachDuration.clear();
615 
616             mMaxPublishInApp = 0;
617             mMaxSubscribeInApp = 0;
618             mMaxDiscoveryInApp = 0;
619             mMaxPublishInSystem = 0;
620             mMaxSubscribeInSystem = 0;
621             mMaxDiscoveryInSystem = 0;
622             mPublishStatusData.clear();
623             mSubscribeStatusData.clear();
624             mHistogramPublishDuration.clear();
625             mHistogramSubscribeDuration.clear();
626             mAppsWithDiscoverySessionResourceFailure.clear();
627 
628             mMaxPublishWithRangingInApp = 0;
629             mMaxSubscribeWithRangingInApp = 0;
630             mMaxPublishWithRangingInSystem = 0;
631             mMaxSubscribeWithRangingInSystem = 0;
632             mHistogramSubscribeGeofenceMin.clear();
633             mHistogramSubscribeGeofenceMax.clear();
634             mNumSubscribesWithRanging = 0;
635             mNumMatchesWithRanging = 0;
636             mNumMatchesWithoutRangingForRangingEnabledSubscribes = 0;
637 
638             mMaxNdiInApp = 0;
639             mMaxNdpInApp = 0;
640             mMaxSecureNdpInApp = 0;
641             mMaxNdiInSystem = 0;
642             mMaxNdpInSystem = 0;
643             mMaxSecureNdpInSystem = 0;
644             mMaxNdpPerNdi = 0;
645             mInBandNdpStatusData.clear();
646             mOutOfBandNdpStatusData.clear();
647 
648             mNdpCreationTimeDuration.clear();
649             mNdpCreationTimeMin = -1;
650             mNdpCreationTimeMax = 0;
651             mNdpCreationTimeSum = 0;
652             mNdpCreationTimeSumSq = 0;
653             mNdpCreationTimeNumSamples = 0;
654 
655             mHistogramNdpDuration.clear();
656         }
657     }
658 
659     /**
660      * Dump all WifiAwareMetrics to console (pw) - this method is never called to dump the
661      * serialized metrics (handled by parent WifiMetrics).
662      *
663      * @param fd   unused
664      * @param pw   PrintWriter for writing dump to
665      * @param args unused
666      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)667     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
668         synchronized (mLock) {
669             pw.println("mLastEnableUsageMs:" + mLastEnableUsageMs);
670             pw.println(
671                     "mLastEnableUsageInThisSampleWindowMs:" + mLastEnableUsageInThisSampleWindowMs);
672             pw.println("mAvailableTimeMs:" + mAvailableTimeMs);
673             pw.println("mHistogramAwareAvailableDurationMs:");
674             for (int i = 0; i < mHistogramAwareAvailableDurationMs.size(); ++i) {
675                 pw.println("  " + mHistogramAwareAvailableDurationMs.keyAt(i) + ": "
676                         + mHistogramAwareAvailableDurationMs.valueAt(i));
677             }
678 
679             pw.println("mLastEnableAwareMs:" + mLastEnableAwareMs);
680             pw.println(
681                     "mLastEnableAwareInThisSampleWindowMs:" + mLastEnableAwareInThisSampleWindowMs);
682             pw.println("mEnabledTimeMs:" + mEnabledTimeMs);
683             pw.println("mHistogramAwareEnabledDurationMs:");
684             for (int i = 0; i < mHistogramAwareEnabledDurationMs.size(); ++i) {
685                 pw.println("  " + mHistogramAwareEnabledDurationMs.keyAt(i) + ": "
686                         + mHistogramAwareEnabledDurationMs.valueAt(i));
687             }
688 
689             pw.println("mAttachDataByUid:");
690             for (Map.Entry<Integer, AttachData> ade: mAttachDataByUid.entrySet()) {
691                 pw.println("  " + "uid=" + ade.getKey() + ": identity="
692                         + ade.getValue().mUsesIdentityCallback + ", maxConcurrent="
693                         + ade.getValue().mMaxConcurrentAttaches);
694             }
695             pw.println("mAttachStatusData:");
696             for (int i = 0; i < mAttachStatusData.size(); ++i) {
697                 pw.println("  " + mAttachStatusData.keyAt(i) + ": "
698                         + mAttachStatusData.valueAt(i));
699             }
700             pw.println("mHistogramAttachDuration:");
701             for (int i = 0; i < mHistogramAttachDuration.size(); ++i) {
702                 pw.println("  " + mHistogramAttachDuration.keyAt(i) + ": "
703                         + mHistogramAttachDuration.valueAt(i));
704             }
705 
706             pw.println("mMaxPublishInApp:" + mMaxPublishInApp);
707             pw.println("mMaxSubscribeInApp:" + mMaxSubscribeInApp);
708             pw.println("mMaxDiscoveryInApp:" + mMaxDiscoveryInApp);
709             pw.println("mMaxPublishInSystem:" + mMaxPublishInSystem);
710             pw.println("mMaxSubscribeInSystem:" + mMaxSubscribeInSystem);
711             pw.println("mMaxDiscoveryInSystem:" + mMaxDiscoveryInSystem);
712             pw.println("mPublishStatusData:");
713             for (int i = 0; i < mPublishStatusData.size(); ++i) {
714                 pw.println("  " + mPublishStatusData.keyAt(i) + ": "
715                         + mPublishStatusData.valueAt(i));
716             }
717             pw.println("mSubscribeStatusData:");
718             for (int i = 0; i < mSubscribeStatusData.size(); ++i) {
719                 pw.println("  " + mSubscribeStatusData.keyAt(i) + ": "
720                         + mSubscribeStatusData.valueAt(i));
721             }
722             pw.println("mHistogramPublishDuration:");
723             for (int i = 0; i < mHistogramPublishDuration.size(); ++i) {
724                 pw.println("  " + mHistogramPublishDuration.keyAt(i) + ": "
725                         + mHistogramPublishDuration.valueAt(i));
726             }
727             pw.println("mHistogramSubscribeDuration:");
728             for (int i = 0; i < mHistogramSubscribeDuration.size(); ++i) {
729                 pw.println("  " + mHistogramSubscribeDuration.keyAt(i) + ": "
730                         + mHistogramSubscribeDuration.valueAt(i));
731             }
732             pw.println("mAppsWithDiscoverySessionResourceFailure:");
733             for (Integer uid: mAppsWithDiscoverySessionResourceFailure) {
734                 pw.println("  " + uid);
735 
736             }
737 
738             pw.println("mMaxPublishWithRangingInApp:" + mMaxPublishWithRangingInApp);
739             pw.println("mMaxSubscribeWithRangingInApp:" + mMaxSubscribeWithRangingInApp);
740             pw.println("mMaxPublishWithRangingInSystem:" + mMaxPublishWithRangingInSystem);
741             pw.println("mMaxSubscribeWithRangingInSystem:" + mMaxSubscribeWithRangingInSystem);
742             pw.println("mHistogramSubscribeGeofenceMin:");
743             for (int i = 0; i < mHistogramSubscribeGeofenceMin.size(); ++i) {
744                 pw.println("  " + mHistogramSubscribeGeofenceMin.keyAt(i) + ": "
745                         + mHistogramSubscribeGeofenceMin.valueAt(i));
746             }
747             pw.println("mHistogramSubscribeGeofenceMax:");
748             for (int i = 0; i < mHistogramSubscribeGeofenceMax.size(); ++i) {
749                 pw.println("  " + mHistogramSubscribeGeofenceMax.keyAt(i) + ": "
750                         + mHistogramSubscribeGeofenceMax.valueAt(i));
751             }
752             pw.println("mNumSubscribesWithRanging:" + mNumSubscribesWithRanging);
753             pw.println("mNumMatchesWithRanging:" + mNumMatchesWithRanging);
754             pw.println("mNumMatchesWithoutRangingForRangingEnabledSubscribes:"
755                     + mNumMatchesWithoutRangingForRangingEnabledSubscribes);
756 
757             pw.println("mMaxNdiInApp:" + mMaxNdiInApp);
758             pw.println("mMaxNdpInApp:" + mMaxNdpInApp);
759             pw.println("mMaxSecureNdpInApp:" + mMaxSecureNdpInApp);
760             pw.println("mMaxNdiInSystem:" + mMaxNdiInSystem);
761             pw.println("mMaxNdpInSystem:" + mMaxNdpInSystem);
762             pw.println("mMaxSecureNdpInSystem:" + mMaxSecureNdpInSystem);
763             pw.println("mMaxNdpPerNdi:" + mMaxNdpPerNdi);
764             pw.println("mInBandNdpStatusData:");
765             for (int i = 0; i < mInBandNdpStatusData.size(); ++i) {
766                 pw.println("  " + mInBandNdpStatusData.keyAt(i) + ": "
767                         + mInBandNdpStatusData.valueAt(i));
768             }
769             pw.println("mOutOfBandNdpStatusData:");
770             for (int i = 0; i < mOutOfBandNdpStatusData.size(); ++i) {
771                 pw.println("  " + mOutOfBandNdpStatusData.keyAt(i) + ": "
772                         + mOutOfBandNdpStatusData.valueAt(i));
773             }
774 
775             pw.println("mNdpCreationTimeDuration:");
776             for (int i = 0; i < mNdpCreationTimeDuration.size(); ++i) {
777                 pw.println("  " + mNdpCreationTimeDuration.keyAt(i) + ": "
778                         + mNdpCreationTimeDuration.valueAt(i));
779             }
780             pw.println("mNdpCreationTimeMin:" + mNdpCreationTimeMin);
781             pw.println("mNdpCreationTimeMax:" + mNdpCreationTimeMax);
782             pw.println("mNdpCreationTimeSum:" + mNdpCreationTimeSum);
783             pw.println("mNdpCreationTimeSumSq:" + mNdpCreationTimeSumSq);
784             pw.println("mNdpCreationTimeNumSamples:" + mNdpCreationTimeNumSamples);
785 
786             pw.println("mHistogramNdpDuration:");
787             for (int i = 0; i < mHistogramNdpDuration.size(); ++i) {
788                 pw.println("  " + mHistogramNdpDuration.keyAt(i) + ": "
789                         + mHistogramNdpDuration.valueAt(i));
790             }
791         }
792     }
793 
794     // histogram utilities
795     /**
796      * Convert a generic bucket to Aware HistogramBucket proto.
797      */
798     @VisibleForTesting
histogramToProtoArray( MetricsUtils.GenericBucket[] buckets)799     public static WifiMetricsProto.WifiAwareLog.HistogramBucket[] histogramToProtoArray(
800             MetricsUtils.GenericBucket[] buckets) {
801         WifiMetricsProto.WifiAwareLog.HistogramBucket[] protoArray =
802                 new WifiMetricsProto.WifiAwareLog.HistogramBucket[buckets.length];
803 
804         for (int i = 0; i < buckets.length; ++i) {
805             protoArray[i] = new WifiMetricsProto.WifiAwareLog.HistogramBucket();
806             protoArray[i].start = buckets[i].start;
807             protoArray[i].end = buckets[i].end;
808             protoArray[i].count = buckets[i].count;
809         }
810 
811         return protoArray;
812     }
813 
814     /**
815      * Adds the NanStatusType to the histogram (translating to the proto enumeration of the status).
816      */
addNanHalStatusToHistogram(int halStatus, SparseIntArray histogram)817     public static void addNanHalStatusToHistogram(int halStatus, SparseIntArray histogram) {
818         int protoStatus = convertNanStatusTypeToProtoEnum(halStatus);
819         int newValue = histogram.get(protoStatus) + 1;
820         histogram.put(protoStatus, newValue);
821     }
822 
823     /**
824      * Converts a histogram of proto NanStatusTypeEnum to a raw proto histogram.
825      */
826     @VisibleForTesting
histogramToProtoArray( SparseIntArray histogram)827     public static WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] histogramToProtoArray(
828             SparseIntArray histogram) {
829         WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] protoArray =
830                 new WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[histogram.size()];
831 
832         for (int i = 0; i < histogram.size(); ++i) {
833             protoArray[i] = new WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket();
834             protoArray[i].nanStatusType = histogram.keyAt(i);
835             protoArray[i].count = histogram.valueAt(i);
836         }
837 
838         return protoArray;
839     }
840 
841     /**
842      * Convert a HAL NanStatusType enum to a Metrics proto enum NanStatusTypeEnum.
843      */
convertNanStatusTypeToProtoEnum(int nanStatusType)844     public static int convertNanStatusTypeToProtoEnum(int nanStatusType) {
845         switch (nanStatusType) {
846             case NanStatusType.SUCCESS:
847                 return WifiMetricsProto.WifiAwareLog.SUCCESS;
848             case NanStatusType.INTERNAL_FAILURE:
849                 return WifiMetricsProto.WifiAwareLog.INTERNAL_FAILURE;
850             case NanStatusType.PROTOCOL_FAILURE:
851                 return WifiMetricsProto.WifiAwareLog.PROTOCOL_FAILURE;
852             case NanStatusType.INVALID_SESSION_ID:
853                 return WifiMetricsProto.WifiAwareLog.INVALID_SESSION_ID;
854             case NanStatusType.NO_RESOURCES_AVAILABLE:
855                 return WifiMetricsProto.WifiAwareLog.NO_RESOURCES_AVAILABLE;
856             case NanStatusType.INVALID_ARGS:
857                 return WifiMetricsProto.WifiAwareLog.INVALID_ARGS;
858             case NanStatusType.INVALID_PEER_ID:
859                 return WifiMetricsProto.WifiAwareLog.INVALID_PEER_ID;
860             case NanStatusType.INVALID_NDP_ID:
861                 return WifiMetricsProto.WifiAwareLog.INVALID_NDP_ID;
862             case NanStatusType.NAN_NOT_ALLOWED:
863                 return WifiMetricsProto.WifiAwareLog.NAN_NOT_ALLOWED;
864             case NanStatusType.NO_OTA_ACK:
865                 return WifiMetricsProto.WifiAwareLog.NO_OTA_ACK;
866             case NanStatusType.ALREADY_ENABLED:
867                 return WifiMetricsProto.WifiAwareLog.ALREADY_ENABLED;
868             case NanStatusType.FOLLOWUP_TX_QUEUE_FULL:
869                 return WifiMetricsProto.WifiAwareLog.FOLLOWUP_TX_QUEUE_FULL;
870             case NanStatusType.UNSUPPORTED_CONCURRENCY_NAN_DISABLED:
871                 return WifiMetricsProto.WifiAwareLog.UNSUPPORTED_CONCURRENCY_NAN_DISABLED;
872             default:
873                 Log.e(TAG, "Unrecognized NanStatusType: " + nanStatusType);
874                 return WifiMetricsProto.WifiAwareLog.UNKNOWN_HAL_STATUS;
875         }
876     }
877 }
878