1 /*
2  * Copyright (C) 2021 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 package com.android.server.uwb;
17 
18 import android.content.AttributionSource;
19 import android.util.Log;
20 import android.util.SparseArray;
21 import android.uwb.RangingMeasurement;
22 
23 import com.android.server.uwb.UwbSessionManager.UwbSession;
24 import com.android.server.uwb.data.UwbDlTDoAMeasurement;
25 import com.android.server.uwb.data.UwbOwrAoaMeasurement;
26 import com.android.server.uwb.data.UwbRangingData;
27 import com.android.server.uwb.data.UwbTwoWayMeasurement;
28 import com.android.server.uwb.data.UwbUciConstants;
29 import com.android.server.uwb.proto.UwbStatsLog;
30 
31 import com.google.common.collect.ImmutableSet;
32 import com.google.uwb.support.aliro.AliroOpenRangingParams;
33 import com.google.uwb.support.base.Params;
34 import com.google.uwb.support.ccc.CccOpenRangingParams;
35 import com.google.uwb.support.fira.FiraOpenSessionParams;
36 import com.google.uwb.support.fira.FiraParams;
37 
38 import java.io.FileDescriptor;
39 import java.io.PrintWriter;
40 import java.util.ArrayDeque;
41 import java.util.Calendar;
42 import java.util.Deque;
43 
44 /**
45  * A class to collect and report UWB metrics.
46  */
47 public class UwbMetrics {
48     private static final String TAG = "UwbMetrics";
49 
50     private static final int MAX_STATE_CHANGES = 20;
51     private static final int MAX_RANGING_SESSIONS = 128;
52     private static final int MAX_RANGING_REPORTS = 1024;
53     public static final int INVALID_DISTANCE = 0xFFFF;
54     private static final int ONE_SECOND_IN_MS = 1000;
55     private static final int TEN_SECOND_IN_MS = 10 * 1000;
56     private static final int ONE_MIN_IN_MS = 60 * 1000;
57     private static final int TEN_MIN_IN_MS = 600 * 1000;
58     private static final int ONE_HOUR_IN_MS = 3600 * 1000;
59     private static final ImmutableSet<Integer> SUPPORTED_RANGING_MEASUREMENT_TYPES = ImmutableSet
60             .of((int) UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY,
61             (int) UwbUciConstants.RANGING_MEASUREMENT_TYPE_DL_TDOA,
62             (int) UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA);
63     private final UwbInjector mUwbInjector;
64     private final Deque<UwbStateChangeInfo> mUwbStateChangeInfoList = new ArrayDeque<>();
65     private final Deque<RangingSessionStats> mRangingSessionList = new ArrayDeque<>();
66     private final SparseArray<RangingSessionStats> mOpenedSessionMap = new SparseArray<>();
67     private final Deque<RangingReportEvent> mRangingReportList = new ArrayDeque<>();
68     private int mNumApps = 0;
69     private long mLastRangingDataLogTimeMs;
70     private final Object mLock = new Object();
71 
72     public class UwbStateChangeInfo {
73         private boolean mEnable;
74         private boolean mSucceeded;
75         private long mInitTimeWallClockMs;
76 
UwbStateChangeInfo(boolean enable, boolean succeeded)77         public UwbStateChangeInfo(boolean enable, boolean succeeded) {
78             mEnable = enable;
79             mSucceeded = succeeded;
80             mInitTimeWallClockMs = mUwbInjector.getWallClockMillis();
81         }
82 
83         @Override
toString()84         public String toString() {
85             StringBuilder sb = new StringBuilder();
86             sb.append("initTime=");
87             Calendar c = Calendar.getInstance();
88             synchronized (mLock) {
89                 c.setTimeInMillis(mInitTimeWallClockMs);
90                 sb.append(mInitTimeWallClockMs == 0 ? "            <null>" :
91                         String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
92                 sb.append(", mEnable=").append(mEnable);
93                 sb.append(", mSucceeded=").append(mSucceeded);
94                 return sb.toString();
95             }
96         }
97     }
98 
99     /**
100      * The class storing the stats of a ranging session.
101      */
102     public class RangingSessionStats {
103         private int mSessionId;
104         private int mChannel = 9;
105         private long mInitTimeWallClockMs;
106         private long mStartTimeSinceBootMs;
107         private int mInitLatencyMs;
108         private int mInitStatus;
109         private int mRangingStatus;
110         private int mActiveDuration;
111         private int mRangingCount;
112         private int mValidRangingCount;
113         private boolean mHasValidRangingSinceStart;
114         private int mStartCount;
115         private int mStartFailureCount;
116         private int mStartNoValidReportCount;
117         private int mStsType = UwbStatsLog.UWB_SESSION_INITIATED__STS__UNKNOWN_STS;
118         private boolean mIsInitiator;
119         private boolean mIsController;
120         private boolean mIsDiscoveredByFramework = false;
121         private boolean mIsOutOfBand = true;
122         private int mRangingIntervalMs;
123         private int mParallelSessionCount;
124         private int mRxPacketCount;
125         private int mTxPacketCount;
126         private int mRxErrorCount;
127         private int mTxErrorCount;
128         private int mRxToUpperLayerCount;
129         private int mRangingType = UwbStatsLog
130                 .UWB_RANGING_MEASUREMENT_RECEIVED__RANGING_TYPE__TYPE_UNKNOWN;
131         private int mFilterConfigValue = composeFilterConfigValue();
132         private AttributionSource mAttributionSource;
133 
RangingSessionStats(int sessionId, AttributionSource attributionSource, int parallelSessionCount)134         RangingSessionStats(int sessionId, AttributionSource attributionSource,
135                 int parallelSessionCount) {
136             mSessionId = sessionId;
137             mInitTimeWallClockMs = mUwbInjector.getWallClockMillis();
138             mAttributionSource = attributionSource;
139             mParallelSessionCount = parallelSessionCount;
140         }
141 
composeFilterConfigValue()142         private int composeFilterConfigValue() {
143             DeviceConfigFacade cfg = mUwbInjector.getDeviceConfigFacade();
144             int filter_enabled = cfg.isEnableFilters() ? 1 : 0;
145             int enable_azimuth_mirroring = (cfg.isEnableBackAzimuth() ? 1 : 0) << 1;
146             int enable_primer_aoa = (cfg.isEnablePrimerAoA() ? 1 : 0) << 2;
147             int enable_primer_est_elevation = (cfg.isEnablePrimerEstElevation() ? 1 : 0) << 3;
148             int enable_primer_fov = (cfg.isEnablePrimerFov() ? 1 : 0) << 4;
149             int predict_rear_azimuths = (cfg.isEnableBackAzimuthMasking() ? 1 : 0) << 5;
150             return filter_enabled + enable_azimuth_mirroring + enable_primer_aoa
151                     + enable_primer_est_elevation + enable_primer_fov + predict_rear_azimuths;
152         }
153 
154         /**
155          * Parse UWB profile parameters
156          */
parseParams(Params params)157         public void parseParams(Params params) {
158             if (params instanceof FiraOpenSessionParams) {
159                 parseFiraParams((FiraOpenSessionParams) params);
160             } else if (params instanceof CccOpenRangingParams) {
161                 parseCccParams((CccOpenRangingParams) params);
162             } else if (params instanceof AliroOpenRangingParams) {
163                 parseAliroParams((AliroOpenRangingParams) params);
164             }
165         }
166 
parseFiraParams(FiraOpenSessionParams params)167         private void parseFiraParams(FiraOpenSessionParams params) {
168             if (params.getStsConfig() == FiraParams.STS_CONFIG_STATIC) {
169                 mStsType = UwbStatsLog.UWB_SESSION_INITIATED__STS__STATIC;
170             } else if (params.getStsConfig() == FiraParams.STS_CONFIG_DYNAMIC) {
171                 mStsType = UwbStatsLog.UWB_SESSION_INITIATED__STS__DYNAMIC;
172             } else {
173                 mStsType = UwbStatsLog.UWB_SESSION_INITIATED__STS__PROVISIONED;
174             }
175 
176             mIsInitiator = params.getDeviceRole() == FiraParams.RANGING_DEVICE_ROLE_INITIATOR;
177             mIsController = params.getDeviceType() == FiraParams.RANGING_DEVICE_TYPE_CONTROLLER;
178             mChannel = params.getChannelNumber();
179             mRangingIntervalMs = params.getRangingIntervalMs();
180         }
181 
parseCccParams(CccOpenRangingParams params)182         private void parseCccParams(CccOpenRangingParams params) {
183             mChannel = params.getChannel();
184         }
185 
parseAliroParams(AliroOpenRangingParams params)186         private void parseAliroParams(AliroOpenRangingParams params) {
187             mChannel = params.getChannel();
188         }
189 
convertInitStatus(int status)190         private void convertInitStatus(int status) {
191             mInitStatus = UwbStatsLog.UWB_SESSION_INITIATED__STATUS__GENERAL_FAILURE;
192             switch (status) {
193                 case UwbUciConstants.STATUS_CODE_OK:
194                     mInitStatus = UwbStatsLog.UWB_SESSION_INITIATED__STATUS__SUCCESS;
195                     break;
196                 case UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED:
197                     mInitStatus = UwbStatsLog.UWB_SESSION_INITIATED__STATUS__SESSION_EXCEEDED;
198                     break;
199                 case UwbUciConstants.STATUS_CODE_ERROR_SESSION_DUPLICATE:
200                     mInitStatus = UwbStatsLog.UWB_SESSION_INITIATED__STATUS__SESSION_DUPLICATE;
201                     break;
202                 case UwbUciConstants.STATUS_CODE_INVALID_PARAM:
203                 case UwbUciConstants.STATUS_CODE_INVALID_RANGE:
204                 case UwbUciConstants.STATUS_CODE_INVALID_MESSAGE_SIZE:
205                     mInitStatus = UwbStatsLog.UWB_SESSION_INITIATED__STATUS__BAD_PARAMS;
206                     break;
207             }
208         }
209 
convertRangingStatus(int status)210         private void convertRangingStatus(int status) {
211             mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RANGING_GENERAL_FAILURE;
212             switch (status) {
213                 case UwbUciConstants.STATUS_CODE_OK:
214                 case UwbUciConstants.STATUS_CODE_OK_NEGATIVE_DISTANCE_REPORT:
215                     mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RANGING_SUCCESS;
216                     break;
217                 case UwbUciConstants.STATUS_CODE_RANGING_TX_FAILED:
218                     mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__TX_FAILED;
219                     break;
220                 case UwbUciConstants.STATUS_CODE_RANGING_RX_PHY_DEC_FAILED:
221                     mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_PHY_DEC_FAILED;
222                     break;
223                 case UwbUciConstants.STATUS_CODE_RANGING_RX_PHY_TOA_FAILED:
224                     mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_PHY_TOA_FAILED;
225                     break;
226                 case UwbUciConstants.STATUS_CODE_RANGING_RX_PHY_STS_FAILED:
227                     mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_PHY_STS_FAILED;
228                     break;
229                 case UwbUciConstants.STATUS_CODE_RANGING_RX_MAC_DEC_FAILED:
230                     mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_MAC_DEC_FAILED;
231                     break;
232                 case UwbUciConstants.STATUS_CODE_RANGING_RX_MAC_IE_DEC_FAILED:
233                     mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_MAC_IE_DEC_FAILED;
234                     break;
235                 case UwbUciConstants.STATUS_CODE_RANGING_RX_MAC_IE_MISSING:
236                     mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_MAC_IE_MISSING;
237                     break;
238                 case UwbUciConstants.STATUS_CODE_INVALID_PARAM:
239                 case UwbUciConstants.STATUS_CODE_INVALID_RANGE:
240                 case UwbUciConstants.STATUS_CODE_INVALID_MESSAGE_SIZE:
241                     mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RANGING_BAD_PARAMS;
242                     break;
243             }
244         }
245 
246         @Override
toString()247         public String toString() {
248             StringBuilder sb = new StringBuilder();
249             sb.append("initTime=");
250             Calendar c = Calendar.getInstance();
251             synchronized (mLock) {
252                 c.setTimeInMillis(mInitTimeWallClockMs);
253                 sb.append(mInitTimeWallClockMs == 0 ? "            <null>" :
254                         String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
255                 sb.append(", sessionId=").append(mSessionId);
256                 sb.append(", initLatencyMs=").append(mInitLatencyMs);
257                 sb.append(", activeDurationMs=").append(mActiveDuration);
258                 sb.append(", rangingCount=").append(mRangingCount);
259                 sb.append(", validRangingCount=").append(mValidRangingCount);
260                 sb.append(", startCount=").append(mStartCount);
261                 sb.append(", startFailureCount=").append(mStartFailureCount);
262                 sb.append(", startNoValidReportCount=").append(mStartNoValidReportCount);
263                 sb.append(", initStatus=").append(mInitStatus);
264                 sb.append(", channel=").append(mChannel);
265                 sb.append(", initiator=").append(mIsInitiator);
266                 sb.append(", controller=").append(mIsController);
267                 sb.append(", discoveredByFramework=").append(mIsDiscoveredByFramework);
268                 sb.append(", uid=").append(mAttributionSource.getUid());
269                 sb.append(", packageName=").append(mAttributionSource.getPackageName());
270                 sb.append(", rangingIntervalMs=").append(mRangingIntervalMs);
271                 sb.append(", parallelSessionCount=").append(mParallelSessionCount);
272                 sb.append(", rxPacketCount=").append(mRxPacketCount);
273                 sb.append(", txPacketCount=").append(mTxPacketCount);
274                 sb.append(", rxErrorCount=").append(mRxErrorCount);
275                 sb.append(", txErrorCount=").append(mTxErrorCount);
276                 sb.append(", rxToUpperLayerCount=").append(mRxToUpperLayerCount);
277                 sb.append(", rangingType=").append(mRangingType);
278                 return sb.toString();
279             }
280         }
281     }
282 
283     private class RangingReportEvent {
284         private int mSessionId;
285         private int mNlos;
286         private int mDistanceCm = INVALID_DISTANCE;
287         private int mAzimuthDegree;
288         private int mAzimuthFom;
289         private int mElevationDegree;
290         private int mElevationFom;
291         private int mRssiDbm = RangingMeasurement.RSSI_UNKNOWN;
292         private int mRangingType;
293         private int mFilteredDistanceCm = INVALID_DISTANCE;
294         private int mFilteredAzimuthDegree;
295         private int mFilteredAzimuthFom;
296         private int mFilteredElevationDegree;
297         private int mFilteredElevationFom;
298         private long mWallClockMillis = mUwbInjector.getWallClockMillis();;
299         private boolean mIsStatusOk;
300 
RangingReportEvent(UwbTwoWayMeasurement measurement)301         RangingReportEvent(UwbTwoWayMeasurement measurement) {
302             mNlos = convertNlos(measurement.getNLoS());
303             mDistanceCm = measurement.getDistance();
304             mAzimuthDegree = (int) measurement.getAoaAzimuth();
305             mAzimuthFom = measurement.getAoaAzimuthFom();
306             mElevationDegree = (int) measurement.getAoaElevation();
307             mElevationFom = measurement.getAoaElevationFom();
308             mRssiDbm = measurement.getRssi();
309             mRangingType = UwbStatsLog.UWB_RANGING_MEASUREMENT_RECEIVED__RANGING_TYPE__TWO_WAY;
310             mIsStatusOk = measurement.isStatusCodeOk();
311         }
312 
RangingReportEvent(UwbDlTDoAMeasurement measurement)313         RangingReportEvent(UwbDlTDoAMeasurement measurement) {
314             mNlos = convertNlos(measurement.getNLoS());
315             mAzimuthDegree = (int) measurement.getAoaAzimuth();
316             mAzimuthFom = measurement.getAoaAzimuthFom();
317             mElevationDegree = (int) measurement.getAoaElevation();
318             mElevationFom = measurement.getAoaElevationFom();
319             mRssiDbm = measurement.getRssi();
320             mRangingType = UwbStatsLog.UWB_RANGING_MEASUREMENT_RECEIVED__RANGING_TYPE__DL_TDOA;
321             mIsStatusOk = measurement.getStatus() == UwbUciConstants.STATUS_CODE_OK;
322         }
323 
RangingReportEvent(UwbOwrAoaMeasurement measurement)324         RangingReportEvent(UwbOwrAoaMeasurement measurement) {
325             mNlos = convertNlos(measurement.getNLoS());
326             mAzimuthDegree = (int) measurement.getAoaAzimuth();
327             mAzimuthFom = measurement.getAoaAzimuthFom();
328             mElevationDegree = (int) measurement.getAoaElevation();
329             mElevationFom = measurement.getAoaElevationFom();
330             mRangingType = UwbStatsLog.UWB_RANGING_MEASUREMENT_RECEIVED__RANGING_TYPE__OWR_AOA;
331             mIsStatusOk = measurement.getRangingStatus() == UwbUciConstants.STATUS_CODE_OK;
332         }
333 
addFilteredResults(RangingMeasurement filteredRangingMeasurement)334         private void addFilteredResults(RangingMeasurement filteredRangingMeasurement) {
335             if (filteredRangingMeasurement == null) {
336                 return;
337             }
338             if (filteredRangingMeasurement.getDistanceMeasurement() != null) {
339                 mFilteredDistanceCm = (int) (filteredRangingMeasurement
340                         .getDistanceMeasurement().getMeters() * 100);
341             }
342             if (filteredRangingMeasurement.getAngleOfArrivalMeasurement() != null) {
343                 mFilteredAzimuthDegree = (int) Math.toDegrees(filteredRangingMeasurement
344                         .getAngleOfArrivalMeasurement().getAzimuth().getRadians());
345                 mFilteredAzimuthFom = (int) (filteredRangingMeasurement
346                         .getAngleOfArrivalMeasurement().getAzimuth()
347                         .getConfidenceLevel() * 100);
348                 mFilteredElevationDegree = (int) Math.toDegrees(filteredRangingMeasurement
349                         .getAngleOfArrivalMeasurement().getAltitude().getRadians());
350                 mFilteredElevationFom = (int) (filteredRangingMeasurement
351                         .getAngleOfArrivalMeasurement().getAltitude()
352                         .getConfidenceLevel() * 100);
353             }
354         }
355 
356         @Override
toString()357         public String toString() {
358             StringBuilder sb = new StringBuilder();
359             sb.append("time=");
360             Calendar c = Calendar.getInstance();
361             synchronized (mLock) {
362                 c.setTimeInMillis(mWallClockMillis);
363                 sb.append(mWallClockMillis == 0 ? "            <null>" :
364                         String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
365                 sb.append(", sessionId=").append(mSessionId);
366                 sb.append(", Nlos=").append(mNlos);
367                 sb.append(", DistanceCm=").append(mDistanceCm);
368                 sb.append(", AzimuthDegree=").append(mAzimuthDegree);
369                 sb.append(", AzimuthFom=").append(mAzimuthFom);
370                 sb.append(", ElevationDegree=").append(mElevationDegree);
371                 sb.append(", ElevationFom=").append(mElevationFom);
372                 sb.append(", RssiDbm=").append(mRssiDbm);
373                 sb.append(", FilteredDistanceCm=").append(mFilteredDistanceCm);
374                 sb.append(", FilteredAzimuthDegree=").append(mFilteredAzimuthDegree);
375                 sb.append(", FilteredAzimuthFom=").append(mFilteredAzimuthFom);
376                 sb.append(", FilteredElevationDegree=").append(mFilteredElevationDegree);
377                 sb.append(", FilteredElevationFom=").append(mFilteredElevationFom);
378                 sb.append(", RangingType=").append(mRangingType);
379                 return sb.toString();
380             }
381         }
382     }
383 
UwbMetrics(UwbInjector uwbInjector)384     public UwbMetrics(UwbInjector uwbInjector) {
385         mUwbInjector = uwbInjector;
386     }
387 
388     /**
389      * Log UWB state change event.
390      */
logUwbStateChangeEvent(boolean enable, boolean succeeded, boolean isFirstInitAttempt)391     public void logUwbStateChangeEvent(boolean enable, boolean succeeded,
392             boolean isFirstInitAttempt) {
393         synchronized (mLock) {
394             // If past maximum events, start removing the oldest
395             while (mUwbStateChangeInfoList.size() >= MAX_STATE_CHANGES) {
396                 mUwbStateChangeInfoList.removeFirst();
397             }
398             UwbStateChangeInfo uwbStateChangeInfo = new UwbStateChangeInfo(enable, succeeded);
399             mUwbStateChangeInfoList.add(uwbStateChangeInfo);
400             if (enable) {
401                 if (succeeded) {
402                     incrementDeviceInitSuccessCount();
403                 } else {
404                     incrementDeviceInitFailureCount(isFirstInitAttempt);
405                 }
406             }
407         }
408     }
409 
410     /**
411      * Log the ranging session initialization event
412      */
logRangingInitEvent(UwbSession uwbSession, int status)413     public void logRangingInitEvent(UwbSession uwbSession, int status) {
414         synchronized (mLock) {
415             // If past maximum events, start removing the oldest
416             while (mRangingSessionList.size() >= MAX_RANGING_SESSIONS) {
417                 mRangingSessionList.removeFirst();
418             }
419             RangingSessionStats session = new RangingSessionStats(uwbSession.getSessionId(),
420                     uwbSession.getAttributionSource(), uwbSession.getParallelSessionCount());
421             session.parseParams(uwbSession.getParams());
422             session.convertInitStatus(status);
423             mRangingSessionList.add(session);
424             mOpenedSessionMap.put(uwbSession.getSessionId(), session);
425             if (status != UwbUciConstants.STATUS_CODE_OK) {
426                 Log.wtf(TAG, "Session init failed with status " + status);
427                 takBugReportSessionInitError("UWB Bugreport: session init failed reason " + status);
428             }
429             UwbStatsLog.write(UwbStatsLog.UWB_SESSION_INITED, uwbSession.getProfileType(),
430                     session.mStsType, session.mIsInitiator,
431                     session.mIsController, session.mIsDiscoveredByFramework, session.mIsOutOfBand,
432                     session.mChannel, session.mInitStatus,
433                     session.mInitLatencyMs, session.mInitLatencyMs / 20,
434                     uwbSession.getAttributionSource().getUid(), session.mRangingIntervalMs,
435                     session.mParallelSessionCount, session.mFilterConfigValue
436             );
437         }
438     }
439 
440     /**
441      * Log the ranging session start event
442      */
longRangingStartEvent(UwbSession uwbSession, int status)443     public void longRangingStartEvent(UwbSession uwbSession, int status) {
444         synchronized (mLock) {
445             RangingSessionStats session = mOpenedSessionMap.get(uwbSession.getSessionId());
446             if (session == null) {
447                 return;
448             }
449             session.mStartCount++;
450             session.convertRangingStatus(status);
451             UwbStatsLog.write(UwbStatsLog.UWB_RANGING_START, uwbSession.getProfileType(),
452                     session.mStsType, session.mIsInitiator,
453                     session.mIsController, session.mIsDiscoveredByFramework, session.mIsOutOfBand,
454                     session.mRangingStatus);
455             if (status != UwbUciConstants.STATUS_CODE_OK) {
456                 session.mStartFailureCount++;
457                 session.mStartTimeSinceBootMs = 0;
458                 session.mHasValidRangingSinceStart = false;
459                 return;
460             }
461             session.mStartTimeSinceBootMs = mUwbInjector.getElapsedSinceBootMillis();
462         }
463     }
464 
takBugReportSessionInitError(String bugTitle)465     private void takBugReportSessionInitError(String bugTitle) {
466         if (mUwbInjector.getDeviceConfigFacade().isSessionInitErrorBugreportEnabled()) {
467             mUwbInjector.getUwbDiagnostics().takeBugReport(bugTitle);
468         }
469     }
470 
471     /**
472      * Log the ranging session stop event
473      */
longRangingStopEvent(UwbSession uwbSession)474     public void longRangingStopEvent(UwbSession uwbSession) {
475         synchronized (mLock) {
476             RangingSessionStats session = mOpenedSessionMap.get(uwbSession.getSessionId());
477             if (session == null) {
478                 return;
479             }
480             if (session.mStartTimeSinceBootMs == 0) {
481                 return;
482             }
483             if (!session.mHasValidRangingSinceStart) {
484                 session.mStartNoValidReportCount++;
485             }
486             session.mHasValidRangingSinceStart = false;
487             session.mActiveDuration += (int) (mUwbInjector.getElapsedSinceBootMillis()
488                     - session.mStartTimeSinceBootMs);
489             session.mStartTimeSinceBootMs = 0;
490         }
491     }
492 
493     /**
494      * Log the ranging session close event
495      */
logRangingCloseEvent(UwbSession uwbSession, int status)496     public void logRangingCloseEvent(UwbSession uwbSession, int status) {
497         synchronized (mLock) {
498             RangingSessionStats session = mOpenedSessionMap.get(uwbSession.getSessionId());
499             if (session == null) {
500                 return;
501             }
502             if (status != UwbUciConstants.STATUS_CODE_OK) {
503                 return;
504             }
505             // Ranging may close without stop event
506             if (session.mStartTimeSinceBootMs != 0) {
507                 session.mActiveDuration += (int) (mUwbInjector.getElapsedSinceBootMillis()
508                         - session.mStartTimeSinceBootMs);
509                 if (!session.mHasValidRangingSinceStart) {
510                     session.mStartNoValidReportCount++;
511                 }
512                 session.mStartTimeSinceBootMs = 0;
513                 session.mHasValidRangingSinceStart = false;
514             }
515 
516             UwbStatsLog.write(UwbStatsLog.UWB_SESSION_CLOSED, uwbSession.getProfileType(),
517                     session.mStsType, session.mIsInitiator,
518                     session.mIsController, session.mIsDiscoveredByFramework, session.mIsOutOfBand,
519                     session.mActiveDuration, getDurationBucket(session.mActiveDuration),
520                     session.mRangingCount, session.mValidRangingCount,
521                     getCountBucket(session.mRangingCount),
522                     getCountBucket(session.mValidRangingCount),
523                     session.mStartCount,
524                     session.mStartFailureCount,
525                     session.mStartNoValidReportCount,
526                     session.mRxPacketCount, session.mTxPacketCount, session.mRxErrorCount,
527                     session.mTxErrorCount, session.mRxToUpperLayerCount, session.mRangingType);
528             mOpenedSessionMap.delete(uwbSession.getSessionId());
529         }
530     }
531 
getDurationBucket(int durationMs)532     private int getDurationBucket(int durationMs) {
533         if (durationMs <= ONE_SECOND_IN_MS) {
534             return UwbStatsLog.UWB_SESSION_CLOSED__DURATION_BUCKET__WITHIN_ONE_SEC;
535         } else if (durationMs <= TEN_SECOND_IN_MS) {
536             return UwbStatsLog.UWB_SESSION_CLOSED__DURATION_BUCKET__ONE_TO_TEN_SEC;
537         } else if (durationMs <= ONE_MIN_IN_MS) {
538             return UwbStatsLog.UWB_SESSION_CLOSED__DURATION_BUCKET__TEN_SEC_TO_ONE_MIN;
539         } else if (durationMs <= TEN_MIN_IN_MS) {
540             return UwbStatsLog.UWB_SESSION_CLOSED__DURATION_BUCKET__ONE_TO_TEN_MIN;
541         } else if (durationMs <= ONE_HOUR_IN_MS) {
542             return UwbStatsLog.UWB_SESSION_CLOSED__DURATION_BUCKET__TEN_MIN_TO_ONE_HOUR;
543         } else {
544             return UwbStatsLog.UWB_SESSION_CLOSED__DURATION_BUCKET__MORE_THAN_ONE_HOUR;
545         }
546     }
547 
getCountBucket(int count)548     private int getCountBucket(int count) {
549         if (count <= 0) {
550             return UwbStatsLog.UWB_SESSION_CLOSED__RANGING_COUNT_BUCKET__ZERO;
551         } else if (count <= 5) {
552             return UwbStatsLog.UWB_SESSION_CLOSED__RANGING_COUNT_BUCKET__ONE_TO_FIVE;
553         } else if (count <= 20) {
554             return UwbStatsLog.UWB_SESSION_CLOSED__RANGING_COUNT_BUCKET__FIVE_TO_TWENTY;
555         } else if (count <= 100) {
556             return UwbStatsLog.UWB_SESSION_CLOSED__RANGING_COUNT_BUCKET__TWENTY_TO_ONE_HUNDRED;
557         } else if (count <= 500) {
558             return UwbStatsLog
559                     .UWB_SESSION_CLOSED__RANGING_COUNT_BUCKET__ONE_HUNDRED_TO_FIVE_HUNDRED;
560         } else {
561             return UwbStatsLog.UWB_SESSION_CLOSED__RANGING_COUNT_BUCKET__MORE_THAN_FIVE_HUNDRED;
562         }
563     }
564 
565     /**
566      * Log the usage of API from a new App
567      */
logNewAppUsage()568     public void logNewAppUsage() {
569         synchronized (mLock) {
570             mNumApps++;
571         }
572     }
573 
574     /**
575      * Log the ranging measurement result
576      */
logRangingResult(int profileType, UwbRangingData rawRangingData, RangingMeasurement filteredRangingMeasurement)577     public void logRangingResult(int profileType, UwbRangingData rawRangingData,
578             RangingMeasurement filteredRangingMeasurement) {
579         synchronized (mLock) {
580             int rangingMeasuresType = rawRangingData.getRangingMeasuresType();
581             if (!SUPPORTED_RANGING_MEASUREMENT_TYPES.contains(rangingMeasuresType)
582                     || rawRangingData.getNoOfRangingMeasures() < 1) {
583                 return;
584             }
585 
586             int sessionId = (int) rawRangingData.getSessionId();
587             RangingSessionStats session = mOpenedSessionMap.get(sessionId);
588             if (session == null) {
589                 return;
590             }
591             session.mRangingCount++;
592 
593             RangingReportEvent report = getRangingReport(rangingMeasuresType, rawRangingData);
594             if (report == null) {
595                 return;
596             }
597             report.mSessionId = sessionId;
598             session.mRangingType = report.mRangingType;
599 
600             if (!report.mIsStatusOk) {
601                 return;
602             }
603             report.addFilteredResults(filteredRangingMeasurement);
604 
605             session.mValidRangingCount++;
606             if (!session.mHasValidRangingSinceStart) {
607                 session.mHasValidRangingSinceStart = true;
608                 writeFirstValidRangingResultSinceStart(profileType, session);
609             }
610 
611             while (mRangingReportList.size() >= MAX_RANGING_REPORTS) {
612                 mRangingReportList.removeFirst();
613             }
614             mRangingReportList.add(report);
615 
616             long currTimeMs = mUwbInjector.getElapsedSinceBootMillis();
617             if ((currTimeMs - mLastRangingDataLogTimeMs) < mUwbInjector.getDeviceConfigFacade()
618                     .getRangingResultLogIntervalMs()) {
619                 return;
620             }
621             mLastRangingDataLogTimeMs = currTimeMs;
622 
623             boolean isDistanceValid = report.mDistanceCm != INVALID_DISTANCE;
624             boolean isAzimuthValid = report.mAzimuthFom > 0;
625             boolean isElevationValid = report.mElevationFom > 0;
626             int distance50Cm = isDistanceValid ? report.mDistanceCm / 50 : 0;
627             int azimuth10Degree = isAzimuthValid ? report.mAzimuthDegree / 10 : 0;
628             int elevation10Degree = isElevationValid ? report.mElevationDegree / 10 : 0;
629             UwbStatsLog.write(UwbStatsLog.UWB_RANGING_MEASUREMENT_RECEIVED,
630                     profileType, report.mNlos,
631                     isDistanceValid, report.mDistanceCm, distance50Cm, report.mRssiDbm,
632                     isAzimuthValid, report.mAzimuthDegree, azimuth10Degree, report.mAzimuthFom,
633                     isElevationValid, report.mElevationDegree, elevation10Degree,
634                     report.mElevationFom, session.mRangingType, report.mFilteredDistanceCm,
635                     report.mFilteredAzimuthDegree, report.mFilteredAzimuthFom,
636                     report.mFilteredElevationDegree, report.mFilteredElevationFom);
637         }
638     }
639 
writeFirstValidRangingResultSinceStart(int profileType, RangingSessionStats session)640     private void writeFirstValidRangingResultSinceStart(int profileType,
641             RangingSessionStats session) {
642         int latencyMs = (int) (mUwbInjector.getElapsedSinceBootMillis()
643                 - session.mStartTimeSinceBootMs);
644         UwbStatsLog.write(UwbStatsLog.UWB_FIRST_RANGING_RECEIVED,
645                 profileType, latencyMs, latencyMs / 200);
646     }
647 
convertNlos(int nlos)648     private int convertNlos(int nlos) {
649         if (nlos == 0) {
650             return UwbStatsLog.UWB_RANGING_MEASUREMENT_RECEIVED__NLOS__LOS;
651         } else if (nlos == 1) {
652             return UwbStatsLog.UWB_RANGING_MEASUREMENT_RECEIVED__NLOS__NLOS;
653         } else {
654             return UwbStatsLog.UWB_RANGING_MEASUREMENT_RECEIVED__NLOS__NLOS_UNKNOWN;
655         }
656     }
657 
getRangingReport(int rangingType, UwbRangingData rangingData)658     private RangingReportEvent getRangingReport(int rangingType, UwbRangingData rangingData) {
659         switch (rangingType) {
660             case UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY:
661                 UwbTwoWayMeasurement[] uwbTwoWayMeasurements =
662                         rangingData.getRangingTwoWayMeasures();
663                 return new RangingReportEvent(uwbTwoWayMeasurements[0]);
664             case UwbUciConstants.RANGING_MEASUREMENT_TYPE_DL_TDOA:
665                 UwbDlTDoAMeasurement[] uwbDlTDoAMeasurements =
666                         rangingData.getUwbDlTDoAMeasurements();
667                 return new RangingReportEvent(uwbDlTDoAMeasurements[0]);
668             case UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA:
669                 return new RangingReportEvent(rangingData.getRangingOwrAoaMeasure());
670             default:
671                 return null;
672         }
673     }
674 
675     /**
676      * Log Rx data packet count
677      */
logDataRx(UwbSession uwbSession, int status)678     public synchronized void logDataRx(UwbSession uwbSession, int status) {
679         synchronized (mLock) {
680             RangingSessionStats session = mOpenedSessionMap.get(uwbSession.getSessionId());
681             if (session == null) {
682                 return;
683             }
684             if (status == UwbUciConstants.STATUS_CODE_OK) {
685                 session.mRxPacketCount++;
686             } else {
687                 session.mRxErrorCount++;
688             }
689         }
690     }
691 
692     /**
693      * Log Tx data packet count
694      */
logDataTx(UwbSession uwbSession, int status)695     public synchronized void logDataTx(UwbSession uwbSession, int status) {
696         synchronized (mLock) {
697             RangingSessionStats session = mOpenedSessionMap.get(uwbSession.getSessionId());
698             if (session == null) {
699                 return;
700             }
701             if (status == UwbUciConstants.STATUS_CODE_OK) {
702                 session.mTxPacketCount++;
703             } else {
704                 session.mTxErrorCount++;
705             }
706         }
707     }
708 
709     /**
710      * Log count of Rx data packets sent to upper layer
711      */
logDataToUpperLayer(UwbSession uwbSession, int packetCount)712     public synchronized void logDataToUpperLayer(UwbSession uwbSession, int packetCount) {
713         synchronized (mLock) {
714             RangingSessionStats session = mOpenedSessionMap.get(uwbSession.getSessionId());
715             if (session == null) {
716                 return;
717             }
718             session.mRxToUpperLayerCount += packetCount;
719         }
720     }
721 
722     private int mNumDeviceInitSuccess = 0;
723     private int mNumDeviceInitFailure = 0;
724     private boolean mFirstDeviceInitFailure = false;
725     private int mNumDeviceStatusError = 0;
726     private int mNumUciGenericError = 0;
727 
728     /**
729      * Increment the count of device initialization success
730      */
incrementDeviceInitSuccessCount()731     private void incrementDeviceInitSuccessCount() {
732         mNumDeviceInitSuccess++;
733     }
734 
735     /**
736      * Increment the count of device initialization failure
737      */
incrementDeviceInitFailureCount(boolean isFirstInitAttempt)738     private void incrementDeviceInitFailureCount(boolean isFirstInitAttempt) {
739         mNumDeviceInitFailure++;
740         if (isFirstInitAttempt) {
741             mFirstDeviceInitFailure = true;
742         } else {
743             UwbStatsLog.write(UwbStatsLog.UWB_DEVICE_ERROR_REPORTED,
744                     UwbStatsLog.UWB_DEVICE_ERROR_REPORTED__TYPE__INIT_ERROR);
745         }
746     }
747 
748     /**
749      * Increment the count of device status error
750      */
incrementDeviceStatusErrorCount()751     public synchronized void incrementDeviceStatusErrorCount() {
752         mNumDeviceStatusError++;
753         UwbStatsLog.write(UwbStatsLog.UWB_DEVICE_ERROR_REPORTED,
754                 UwbStatsLog.UWB_DEVICE_ERROR_REPORTED__TYPE__DEVICE_STATUS_ERROR);
755     }
756 
757     /**
758      * Increment the count of UCI generic error which will trigger UCI command retry
759      */
incrementUciGenericErrorCount()760     public synchronized void incrementUciGenericErrorCount() {
761         mNumUciGenericError++;
762         UwbStatsLog.write(UwbStatsLog.UWB_DEVICE_ERROR_REPORTED,
763                 UwbStatsLog.UWB_DEVICE_ERROR_REPORTED__TYPE__UCI_GENERIC_ERROR);
764     }
765 
766     /**
767      * Dump the UWB logs
768      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)769     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
770         synchronized (mLock) {
771             pw.println("---- Dump of UwbMetrics ----");
772             pw.println("-- mUwbStateChangeInfoList --");
773             for (UwbStateChangeInfo stateChangeInfo: mUwbStateChangeInfoList) {
774                 pw.println(stateChangeInfo.toString());
775             }
776             pw.println("-- mRangingSessionList --");
777             for (RangingSessionStats stats: mRangingSessionList) {
778                 pw.println(stats.toString());
779             }
780             pw.println("-- mOpenedSessionMap --");
781             for (int i = 0; i < mOpenedSessionMap.size(); i++) {
782                 pw.println(mOpenedSessionMap.valueAt(i).toString());
783             }
784             pw.println("-- mRangingReportList --");
785             for (RangingReportEvent event: mRangingReportList) {
786                 pw.println(event.toString());
787             }
788             pw.println("mNumApps=" + mNumApps);
789             pw.println("-- Device operation success/error count --");
790             pw.println("mNumDeviceInitSuccess = " + mNumDeviceInitSuccess);
791             pw.println("mNumDeviceInitFailure = " + mNumDeviceInitFailure);
792             pw.println("mFirstDeviceInitFailure = " + mFirstDeviceInitFailure);
793             pw.println("mNumDeviceStatusError = " + mNumDeviceStatusError);
794             pw.println("mNumUciGenericError = " + mNumUciGenericError);
795             pw.println("---- Dump of UwbMetrics ----");
796         }
797     }
798 }
799