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