• 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 static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
19 
20 import static com.android.server.uwb.data.UwbUciConstants.DEVICE_TYPE_CONTROLLER;
21 import static com.android.server.uwb.data.UwbUciConstants.FIRA_VERSION_MAJOR_2;
22 import static com.android.server.uwb.data.UwbUciConstants.MAC_ADDRESSING_MODE_EXTENDED;
23 import static com.android.server.uwb.data.UwbUciConstants.MAC_ADDRESSING_MODE_SHORT;
24 import static com.android.server.uwb.data.UwbUciConstants.RANGING_DEVICE_ROLE_OBSERVER;
25 import static com.android.server.uwb.data.UwbUciConstants.REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS;
26 import static com.android.server.uwb.data.UwbUciConstants.ROUND_USAGE_OWR_AOA_MEASUREMENT;
27 import static com.android.server.uwb.data.UwbUciConstants.STATUS_CODE_OK;
28 import static com.android.server.uwb.data.UwbUciConstants.UWB_DEVICE_EXT_MAC_ADDRESS_LEN;
29 import static com.android.server.uwb.data.UwbUciConstants.UWB_DEVICE_SHORT_MAC_ADDRESS_LEN;
30 import static com.android.server.uwb.data.UwbUciConstants.UWB_SESSION_STATE_ACTIVE;
31 import static com.android.server.uwb.util.DataTypeConversionUtil.macAddressByteArrayToLong;
32 
33 import static com.google.uwb.support.fira.FiraParams.FILTER_TYPE_APPLICATION;
34 import static com.google.uwb.support.fira.FiraParams.FILTER_TYPE_DEFAULT;
35 import static com.google.uwb.support.fira.FiraParams.FILTER_TYPE_NONE;
36 import static com.google.uwb.support.fira.FiraParams.MULTICAST_LIST_UPDATE_ACTION_ADD;
37 import static com.google.uwb.support.fira.FiraParams.MULTICAST_LIST_UPDATE_ACTION_DELETE;
38 import static com.google.uwb.support.fira.FiraParams.PROTOCOL_NAME;
39 import static com.google.uwb.support.fira.FiraParams.P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_16_BYTE;
40 import static com.google.uwb.support.fira.FiraParams.P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_32_BYTE;
41 import static com.google.uwb.support.fira.FiraParams.RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY_EDGE_TRIG;
42 import static com.google.uwb.support.fira.FiraParams.RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY_LEVEL_TRIG;
43 
44 import android.annotation.NonNull;
45 import android.annotation.Nullable;
46 import android.app.ActivityManager;
47 import android.app.AlarmManager;
48 import android.content.AttributionSource;
49 import android.os.Handler;
50 import android.os.IBinder;
51 import android.os.Looper;
52 import android.os.Message;
53 import android.os.PersistableBundle;
54 import android.os.RemoteException;
55 import android.os.Trace;
56 import android.util.Log;
57 import android.util.Pair;
58 import android.uwb.IUwbAdapter;
59 import android.uwb.IUwbRangingCallbacks;
60 import android.uwb.RangingChangeReason;
61 import android.uwb.SessionHandle;
62 import android.uwb.UwbAddress;
63 
64 import androidx.annotation.GuardedBy;
65 import androidx.annotation.VisibleForTesting;
66 
67 import com.android.modules.utils.build.SdkLevel;
68 import com.android.server.uwb.advertisement.UwbAdvertiseManager;
69 import com.android.server.uwb.data.DtTagUpdateRangingRoundsStatus;
70 import com.android.server.uwb.data.UwbDeviceInfoResponse;
71 import com.android.server.uwb.data.UwbDlTDoAMeasurement;
72 import com.android.server.uwb.data.UwbMulticastListUpdateStatus;
73 import com.android.server.uwb.data.UwbOwrAoaMeasurement;
74 import com.android.server.uwb.data.UwbRadarData;
75 import com.android.server.uwb.data.UwbRangingData;
76 import com.android.server.uwb.data.UwbTwoWayMeasurement;
77 import com.android.server.uwb.data.UwbUciConstants;
78 import com.android.server.uwb.jni.INativeUwbManager;
79 import com.android.server.uwb.jni.NativeUwbManager;
80 import com.android.server.uwb.params.TlvUtil;
81 import com.android.server.uwb.proto.UwbStatsLog;
82 import com.android.server.uwb.rftest.RfNotificationEvent;
83 import com.android.server.uwb.util.ArrayUtils;
84 import com.android.server.uwb.util.DataTypeConversionUtil;
85 import com.android.server.uwb.util.LruList;
86 import com.android.server.uwb.util.UwbUtil;
87 import com.android.uwb.fusion.UwbFilterEngine;
88 import com.android.uwb.fusion.pose.ApplicationPoseSource;
89 import com.android.uwb.fusion.pose.IPoseSource;
90 
91 import com.google.common.collect.Sets;
92 import com.google.uwb.support.aliro.AliroOpenRangingParams;
93 import com.google.uwb.support.aliro.AliroParams;
94 import com.google.uwb.support.aliro.AliroRangingStartedParams;
95 import com.google.uwb.support.aliro.AliroRangingStoppedParams;
96 import com.google.uwb.support.aliro.AliroSpecificationParams;
97 import com.google.uwb.support.aliro.AliroStartRangingParams;
98 import com.google.uwb.support.base.Params;
99 import com.google.uwb.support.ccc.CccOpenRangingParams;
100 import com.google.uwb.support.ccc.CccParams;
101 import com.google.uwb.support.ccc.CccRangingReconfiguredParams;
102 import com.google.uwb.support.ccc.CccRangingStartedParams;
103 import com.google.uwb.support.ccc.CccRangingStoppedParams;
104 import com.google.uwb.support.ccc.CccSpecificationParams;
105 import com.google.uwb.support.ccc.CccStartRangingParams;
106 import com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdate;
107 import com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdateStatus;
108 import com.google.uwb.support.fira.FiraDataTransferPhaseConfig;
109 import com.google.uwb.support.fira.FiraDataTransferPhaseConfig.FiraDataTransferPhaseManagementList;
110 import com.google.uwb.support.fira.FiraHybridSessionControleeConfig;
111 import com.google.uwb.support.fira.FiraHybridSessionControllerConfig;
112 import com.google.uwb.support.fira.FiraOnControleeAddRemoveParams;
113 import com.google.uwb.support.fira.FiraOpenSessionParams;
114 import com.google.uwb.support.fira.FiraParams;
115 import com.google.uwb.support.fira.FiraPoseUpdateParams;
116 import com.google.uwb.support.fira.FiraProtocolVersion;
117 import com.google.uwb.support.fira.FiraRangingReconfigureParams;
118 import com.google.uwb.support.fira.FiraSpecificationParams;
119 import com.google.uwb.support.generic.GenericSpecificationParams;
120 import com.google.uwb.support.oemextension.AdvertisePointedTarget;
121 import com.google.uwb.support.oemextension.SessionConfigParams;
122 import com.google.uwb.support.oemextension.SessionStatus;
123 import com.google.uwb.support.rftest.RfTestParams;
124 import com.google.uwb.support.rftest.RfTestSessionStatus;
125 import com.google.uwb.support.rftest.RfTestStartSessionParams;
126 
127 import java.io.Closeable;
128 import java.io.FileDescriptor;
129 import java.io.PrintWriter;
130 import java.nio.ByteBuffer;
131 import java.nio.ByteOrder;
132 import java.util.ArrayList;
133 import java.util.Arrays;
134 import java.util.Collection;
135 import java.util.Comparator;
136 import java.util.List;
137 import java.util.Map;
138 import java.util.NoSuchElementException;
139 import java.util.Optional;
140 import java.util.Set;
141 import java.util.SortedMap;
142 import java.util.TreeMap;
143 import java.util.concurrent.Callable;
144 import java.util.concurrent.ConcurrentHashMap;
145 import java.util.concurrent.ExecutionException;
146 import java.util.concurrent.ExecutorService;
147 import java.util.concurrent.Executors;
148 import java.util.concurrent.FutureTask;
149 import java.util.concurrent.TimeUnit;
150 import java.util.concurrent.TimeoutException;
151 import java.util.stream.Collectors;
152 
153 public class UwbSessionManager implements INativeUwbManager.SessionNotification,
154         ActivityManager.OnUidImportanceListener {
155 
156     private static final String TAG = "UwbSessionManager";
157     private static final byte OPERATION_TYPE_INIT_SESSION = 0;
158     private static final int UWB_HUS_CONTROLLER_PHASE_LIST_EXTENDED_MAC_ADDRESS_SIZE = 17;
159     private static final int UWB_HUS_CONTROLEE_PHASE_LIST_SIZE = 4;
160 
161     @VisibleForTesting
162     public static final int SESSION_OPEN_RANGING = 1;
163     @VisibleForTesting
164     public static final int SESSION_START_RANGING = 2;
165     @VisibleForTesting
166     public static final int SESSION_STOP_RANGING = 3;
167     @VisibleForTesting
168     public static final int SESSION_RECONFIG_RANGING = 4;
169     @VisibleForTesting
170     public static final int SESSION_DEINIT = 5;
171     @VisibleForTesting
172     public static final int SESSION_ON_DEINIT = 6;
173     @VisibleForTesting
174     public static final int SESSION_SEND_DATA = 7;
175     @VisibleForTesting
176     public static final int SESSION_UPDATE_DT_TAG_RANGING_ROUNDS = 8;
177     @VisibleForTesting
178     public static final int SESSION_SET_HUS_CONTROLLER_CONFIG = 9;
179     @VisibleForTesting
180     public static final int SESSION_SET_HUS_CONTROLEE_CONFIG = 10;
181     @VisibleForTesting
182     public static final int SESSION_DATA_TRANSFER_PHASE_CONFIG = 11;
183     @VisibleForTesting
184     public static final int SESSION_RF_TEST_CMD = 12;
185     @VisibleForTesting
186     public static final int SESSION_STOP_RF_TEST_SESSION = 13;
187 
188     // TODO: don't expose the internal field for testing.
189     @VisibleForTesting
190     final ConcurrentHashMap<SessionHandle, UwbSession> mSessionTable = new ConcurrentHashMap();
191     // Used for storing recently closed sessions for debugging purposes.
192     final LruList<UwbSession> mDbgRecentlyClosedSessions = new LruList<>(5);
193     final ConcurrentHashMap<Integer, List<UwbSession>> mNonPrivilegedUidToFiraSessionsTable =
194             new ConcurrentHashMap();
195     final ConcurrentHashMap<Integer, Integer> mSessionTokenMap = new ConcurrentHashMap<>();
196     private final ActivityManager mActivityManager;
197     private final NativeUwbManager mNativeUwbManager;
198     private final UwbMetrics mUwbMetrics;
199     private final UwbConfigurationManager mConfigurationManager;
200     private final UwbSessionNotificationManager mSessionNotificationManager;
201     private final UwbAdvertiseManager mAdvertiseManager;
202     private final UwbInjector mUwbInjector;
203     private final AlarmManager mAlarmManager;
204     private final Looper mLooper;
205     private final EventTask mEventTask;
206 
UwbSessionManager( UwbConfigurationManager uwbConfigurationManager, NativeUwbManager nativeUwbManager, UwbMetrics uwbMetrics, UwbAdvertiseManager uwbAdvertiseManager, UwbSessionNotificationManager uwbSessionNotificationManager, UwbInjector uwbInjector, AlarmManager alarmManager, ActivityManager activityManager, Looper serviceLooper)207     public UwbSessionManager(
208             UwbConfigurationManager uwbConfigurationManager,
209             NativeUwbManager nativeUwbManager, UwbMetrics uwbMetrics,
210             UwbAdvertiseManager uwbAdvertiseManager,
211             UwbSessionNotificationManager uwbSessionNotificationManager,
212             UwbInjector uwbInjector, AlarmManager alarmManager, ActivityManager activityManager,
213             Looper serviceLooper) {
214         mNativeUwbManager = nativeUwbManager;
215         mNativeUwbManager.setSessionListener(this);
216         mUwbMetrics = uwbMetrics;
217         mAdvertiseManager = uwbAdvertiseManager;
218         mConfigurationManager = uwbConfigurationManager;
219         mSessionNotificationManager = uwbSessionNotificationManager;
220         mUwbInjector = uwbInjector;
221         mAlarmManager = alarmManager;
222         mActivityManager = activityManager;
223         mLooper = serviceLooper;
224         mEventTask = new EventTask(serviceLooper);
225         registerUidImportanceTransitions();
226     }
227 
228     @Override
onUidImportance(final int uid, final int importance)229     public void onUidImportance(final int uid, final int importance) {
230         Handler handler = new Handler(mLooper);
231         handler.post(() -> {
232             synchronized (mNonPrivilegedUidToFiraSessionsTable) {
233                 List<UwbSession> uwbSessions = mNonPrivilegedUidToFiraSessionsTable.get(uid);
234                 // Not a uid in the watch list
235                 if (uwbSessions == null) return;
236                 boolean newModeHasNonPrivilegedFgAppOrService =
237                         UwbInjector.isForegroundAppOrServiceImportance(importance);
238                 for (UwbSession uwbSession : uwbSessions) {
239                     // already at correct state.
240                     if (newModeHasNonPrivilegedFgAppOrService
241                             == uwbSession.hasNonPrivilegedFgAppOrService()) {
242                         continue;
243                     }
244                     uwbSession.setHasNonPrivilegedFgAppOrService(
245                             newModeHasNonPrivilegedFgAppOrService);
246                     int sessionId = uwbSession.getSessionId();
247                     Log.i(TAG, "App state change for session " + sessionId + ". IsFg: "
248                             + newModeHasNonPrivilegedFgAppOrService);
249                     // Reconfigure the session based on the new fg/bg state
250                     Log.i(TAG, "Session " + sessionId
251                             + " reconfiguring ntf control due to app state change");
252                     uwbSession.reconfigureFiraSessionOnFgStateChange();
253                     // Recalculate session priority based on the new fg/bg state.
254                     if (!uwbSession.mSessionPriorityOverride) {
255                         int newSessionPriority = uwbSession.calculateSessionPriority();
256                         Log.i(TAG, "Session " + sessionId
257                                 + " recalculating session priority, new priority: "
258                                 + newSessionPriority);
259                         uwbSession.setStackSessionPriority(newSessionPriority);
260                     }
261                 }
262             }
263         });
264     }
265 
266     // Detect UIDs going foreground/background
registerUidImportanceTransitions()267     private void registerUidImportanceTransitions() {
268         mActivityManager.addOnUidImportanceListener(
269                 UwbSessionManager.this, IMPORTANCE_FOREGROUND_SERVICE);
270     }
271 
hasAllRangingResultError(@onNull UwbRangingData rangingData)272     private static boolean hasAllRangingResultError(@NonNull UwbRangingData rangingData) {
273         if (rangingData.getRangingMeasuresType()
274                 == UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY) {
275             for (UwbTwoWayMeasurement measure : rangingData.getRangingTwoWayMeasures()) {
276                 if (measure.isStatusCodeOk()) {
277                     return false;
278                 }
279             }
280         } else if (rangingData.getRangingMeasuresType()
281                 == UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA) {
282             UwbOwrAoaMeasurement measure = rangingData.getRangingOwrAoaMeasure();
283             if (measure.getRangingStatus() == UwbUciConstants.STATUS_CODE_OK) {
284                 return false;
285             }
286         } else if (rangingData.getRangingMeasuresType()
287                 == UwbUciConstants.RANGING_MEASUREMENT_TYPE_DL_TDOA) {
288             for (UwbDlTDoAMeasurement measure : rangingData.getUwbDlTDoAMeasurements()) {
289                 if (measure.getStatus() == STATUS_CODE_OK) {
290                     return false;
291                 }
292             }
293         }
294         return true;
295     }
296 
handlePerControleeErrorStreakTimers(@onNull UwbRangingData rangingData, @NonNull UwbSession uwbSession)297     private void handlePerControleeErrorStreakTimers(@NonNull UwbRangingData rangingData,
298                                                      @NonNull UwbSession uwbSession) {
299         // TODO(b/343508180): The outer conditional is a workaround for inconsistent behavior on
300         //  cuttlefish (see b/343495601). Remove and/or refactor once this behavior is fixed.
301         if (rangingData.getNoOfRangingMeasures() == 0) {
302             // If we got no ranging measurements, start a session-level error streak timer.
303             uwbSession.startRangingResultErrorStreakTimerIfNotSet();
304         } else {
305             for (UwbTwoWayMeasurement measure : rangingData.getRangingTwoWayMeasures()) {
306                 UwbAddress address = UwbAddress.fromBytes(measure.getMacAddress());
307                 if (measure.isStatusCodeOk()) {
308                     uwbSession.stopRangingResultErrorStreakTimerIfSet(address);
309                     uwbSession.stopRangingResultErrorStreakTimerIfSet();
310                 } else {
311                     uwbSession.startRangingResultErrorStreakTimerIfNotSet(address);
312                 }
313             }
314         }
315     }
316 
handlePerSessionErrorStreakTimer(@onNull UwbRangingData rangingData, @NonNull UwbSession uwbSession)317     private void handlePerSessionErrorStreakTimer(@NonNull UwbRangingData rangingData,
318                                                   @NonNull UwbSession uwbSession) {
319         if (hasAllRangingResultError(rangingData)) {
320             uwbSession.startRangingResultErrorStreakTimerIfNotSet();
321         } else {
322             uwbSession.stopRangingResultErrorStreakTimerIfSet();
323         }
324     }
325 
handleRangingResultErrorStreakTimers(@onNull UwbRangingData rangingData, @NonNull UwbSession uwbSession)326     private void handleRangingResultErrorStreakTimers(@NonNull UwbRangingData rangingData,
327                                                       @NonNull UwbSession uwbSession) {
328         if (!mUwbInjector.getDeviceConfigFacade().isRangingErrorStreakTimerEnabled()
329                 || uwbSession.mRangingErrorStreakTimeoutMs
330                 == UwbSession.RANGING_RESULT_ERROR_NO_TIMEOUT) {
331             return;
332         }
333 
334         boolean isDeviceControllerOfTwoWayRangingSession = rangingData.getRangingMeasuresType()
335                 == UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY
336                 && uwbSession.getDeviceType() == DEVICE_TYPE_CONTROLLER;
337         if (isDeviceControllerOfTwoWayRangingSession) {
338             handlePerControleeErrorStreakTimers(rangingData, uwbSession);
339         } else {
340             handlePerSessionErrorStreakTimer(rangingData, uwbSession);
341         }
342     }
343 
344     @Override
onRangeDataNotificationReceived(UwbRangingData rangingData)345     public void onRangeDataNotificationReceived(UwbRangingData rangingData) {
346         Trace.beginSection("UWB#onRangeDataNotificationReceived");
347         long sessionId = rangingData.getSessionId();
348         UwbSession uwbSession = getUwbSession((int) sessionId);
349         if (uwbSession != null) {
350             // TODO: b/268065070 Include UWB logs for both filtered and unfiltered data.
351             mSessionNotificationManager.onRangingResult(uwbSession, rangingData);
352             processRangeData(rangingData, uwbSession);
353             handleRangingResultErrorStreakTimers(rangingData, uwbSession);
354         } else {
355             Log.i(TAG, "Session is not initialized or Ranging Data is Null");
356         }
357         Trace.endSection();
358     }
359 
360     /* Notification of received data over UWB to Application*/
361     @Override
onDataReceived( long sessionId, int status, long sequenceNum, byte[] address, byte[] data)362     public void onDataReceived(
363             long sessionId, int status, long sequenceNum, byte[] address, byte[] data) {
364         Log.d(TAG, "onDataReceived(): Received data packet - "
365                 + "Address: " + UwbUtil.toHexString(address)
366                 + ", Data: " + UwbUtil.toHexString(data)
367                 + ", sessionId: " + sessionId
368                 + ", status: " + status
369                 + ", sequenceNum: " + sequenceNum);
370 
371         UwbSession uwbSession = getUwbSession((int) sessionId);
372         if (uwbSession == null) {
373             Log.e(TAG, "onDataReceived(): Received data for unknown sessionId = " + sessionId);
374             return;
375         }
376 
377         // Size of address in the UCI Packet for DATA_MESSAGE_RCV is always expected to be 8
378         // (EXTENDED_ADDRESS_BYTE_LENGTH). It can contain the MacAddress in short format however
379         // (2 LSB with MacAddress, 6 MSB zeroed out).
380         if (address.length != UWB_DEVICE_EXT_MAC_ADDRESS_LEN) {
381             Log.e(TAG, "onDataReceived(): Received data for sessionId = " + sessionId
382                     + ", with unexpected MacAddress length = " + address.length);
383             return;
384         }
385         mUwbMetrics.logDataRx(uwbSession, status);
386 
387         Long longAddress = macAddressByteArrayToLong(address);
388         UwbAddress uwbAddress = UwbAddress.fromBytes(address);
389 
390         // When the data packet is received on a non OWR-for-AoA ranging session, send it to the
391         // higher layer. For the OWR-for-AoA ranging session, the data packet is only sent when the
392         // received SESSION_INFO_NTF indicate this Observer device is pointing to an Advertiser.
393         if (uwbSession.getRangingRoundUsage() != ROUND_USAGE_OWR_AOA_MEASUREMENT) {
394             mSessionNotificationManager.onDataReceived(
395                     uwbSession, uwbAddress, new PersistableBundle(), data);
396             return;
397         }
398 
399         ReceivedDataInfo info = new ReceivedDataInfo();
400         info.sessionId = sessionId;
401         info.status = status;
402         info.sequenceNum = sequenceNum;
403         info.address = longAddress;
404         info.payload = data;
405 
406         uwbSession.addReceivedDataInfo(info);
407     }
408 
409     /* Notification of data send status */
410     @Override
onDataSendStatus( long sessionId, int dataTransferStatus, long sequenceNum, int txCount)411     public void onDataSendStatus(
412             long sessionId, int dataTransferStatus, long sequenceNum, int txCount) {
413         Log.d(TAG, "onDataSendStatus(): Received data send status - "
414                 + ", sessionId: " + sessionId
415                 + ", status: " + dataTransferStatus
416                 + ", sequenceNum: " + sequenceNum
417                 + ", txCount: " + txCount);
418 
419         UwbSession uwbSession = getUwbSession((int) sessionId);
420         if (uwbSession == null) {
421             Log.e(TAG, "onDataSendStatus(): Received data send status for unknown sessionId = "
422                     + sessionId);
423             return;
424         }
425 
426         SendDataInfo sendDataInfo = uwbSession.getSendDataInfo(sequenceNum);
427         if (sendDataInfo == null) {
428             Log.e(TAG, "onDataSendStatus(): No SendDataInfo found for data packet (sessionId = "
429                     + sessionId + ", sequenceNum = " + sequenceNum + ")");
430             return;
431         }
432 
433         // A note on status - earlier spec versions had the same status value (0x1) as an error,
434         // the code is written as per recent spec versions (v2.0.0_0.0.9r0).
435         if (dataTransferStatus == UwbUciConstants.STATUS_CODE_DATA_TRANSFER_REPETITION_OK
436                 || dataTransferStatus == UwbUciConstants.STATUS_CODE_DATA_TRANSFER_OK) {
437             mSessionNotificationManager.onDataSent(
438                     uwbSession, sendDataInfo.remoteDeviceAddress, sendDataInfo.params);
439         } else {
440             mSessionNotificationManager.onDataSendFailed(
441                     uwbSession, sendDataInfo.remoteDeviceAddress, dataTransferStatus,
442                      sendDataInfo.params);
443             uwbSession.removeSendDataInfo(sequenceNum);
444         }
445         // when transmission count equals to data repetition count, SendDataInfo will be removed for
446         // the particular sequence number
447         if (txCount >= (uwbSession.getDataRepetitionCount() + 1)
448                 && dataTransferStatus == UwbUciConstants.STATUS_CODE_DATA_TRANSFER_OK) {
449             uwbSession.removeSendDataInfo(sequenceNum);
450         }
451     }
452 
453     @Override
onRadarDataMessageReceived(UwbRadarData radarData)454     public void onRadarDataMessageReceived(UwbRadarData radarData) {
455         Trace.beginSection("UWB#onRadarDataMessageReceived");
456         long sessionId = radarData.sessionId;
457         UwbSession uwbSession = getUwbSession((int) sessionId);
458         if (uwbSession != null) {
459             mSessionNotificationManager.onRadarDataMessageReceived(uwbSession, radarData);
460         } else {
461             Log.i(TAG, "Session is not initialized or Radar Data is Null");
462         }
463         Trace.endSection();
464     }
465 
466     @Override
onDataTransferPhaseConfigNotificationReceived(long sessionId, int dataTransferPhaseConfigStatus)467     public void onDataTransferPhaseConfigNotificationReceived(long sessionId,
468             int dataTransferPhaseConfigStatus) {
469         Log.d(TAG, "onDataTransferPhaseConfigNotificationReceived:"
470                 + ", sessionId: " + sessionId
471                 + ", status: " + dataTransferPhaseConfigStatus);
472 
473         UwbSession uwbSession = getUwbSession((int) sessionId);
474         if (uwbSession == null) {
475             Log.e(TAG, "onDataTransferPhaseConfigNotificationReceived: Received data transfer"
476                     + "config notification for unknown sessionId = " + sessionId);
477             return;
478         }
479 
480         if (dataTransferPhaseConfigStatus
481                 == UwbUciConstants.STATUS_CODE_DATA_TRANSFER_PHASE_CONFIG_DTPCM_CONFIG_SUCCESS) {
482             mSessionNotificationManager.onDataTransferPhaseConfigured(
483                     uwbSession, dataTransferPhaseConfigStatus);
484         } else {
485             mSessionNotificationManager.onDataTransferPhaseConfigFailed(
486                     uwbSession, dataTransferPhaseConfigStatus);
487         }
488     }
489 
490     /** Updates pose information if the session is using an ApplicationPoseSource */
updatePose(SessionHandle sessionHandle, PersistableBundle params)491     public void updatePose(SessionHandle sessionHandle, PersistableBundle params) {
492         int sessionId = getSessionId(sessionHandle);
493         UwbSession uwbSession = getUwbSession(sessionId);
494 
495         if (uwbSession == null) {
496             // Session doesn't exist yet/anymore.
497             return;
498         }
499 
500         uwbSession.updatePose(FiraPoseUpdateParams.fromBundle(params));
501     }
502 
503     @VisibleForTesting
504     static final class ReceivedDataInfo {
505         public long sessionId;
506         public int status;
507         public long sequenceNum;
508         public long address;
509         public byte[] payload;
510     }
511 
512     @Override
onMulticastListUpdateNotificationReceived( UwbMulticastListUpdateStatus multicastListUpdateStatus)513     public void onMulticastListUpdateNotificationReceived(
514             UwbMulticastListUpdateStatus multicastListUpdateStatus) {
515         Log.d(TAG, "onMulticastListUpdateNotificationReceived");
516         UwbSession uwbSession = getUwbSession((int) multicastListUpdateStatus.getSessionId());
517         if (uwbSession == null) {
518             Log.d(TAG, "onMulticastListUpdateNotificationReceived - invalid session");
519             return;
520         }
521 
522         uwbSession.setMulticastListUpdateStatus(multicastListUpdateStatus);
523 
524         int actionStatus = UwbUciConstants.STATUS_CODE_OK;
525         for (int i = 0; i < multicastListUpdateStatus.getNumOfControlee(); i++) {
526             actionStatus = multicastListUpdateStatus.getStatus()[i];
527             // Action - delete controlee, State - Active
528             if (actionStatus == UwbUciConstants.STATUS_CODE_OK) {
529                 if (uwbSession.getOperationType() == SESSION_RECONFIG_RANGING) {
530                     synchronized (uwbSession.getWaitObj()) {
531                         uwbSession.getWaitObj().blockingNotify();
532                     }
533                     break;
534                 }
535             } else {
536                 // Handle the failure case for adding a controlee
537                 mSessionNotificationManager.onControleeAddFailed(
538                         uwbSession, multicastListUpdateStatus.getControleeUwbAddresses()[i],
539                                 actionStatus);
540             }
541 
542         }
543         if (actionStatus !=  UwbUciConstants.STATUS_CODE_OK) {
544             mSessionNotificationManager.onRangingReconfigureFailed(
545                     uwbSession, actionStatus);
546         }
547     }
548 
549     @Override
onRfTestNotificationReceived(RfNotificationEvent rfNotificationEvent)550     public void onRfTestNotificationReceived(RfNotificationEvent rfNotificationEvent) {
551         UwbSession uwbSession = getUwbSession(RfTestParams.SESSION_ID_RFTEST);
552         if (uwbSession == null) {
553             Log.d(TAG, "UwbSession doesn't exist for SESSION_ID_RFTEST");
554             return;
555         }
556 
557         mSessionNotificationManager.onRfTestNotificationReceived(uwbSession, rfNotificationEvent);
558     }
559 
560     @Override
onSessionStatusNotificationReceived(long sessionId, int sessionToken, int state, int reasonCode)561     public void onSessionStatusNotificationReceived(long sessionId, int sessionToken,
562             int state, int reasonCode) {
563         Log.i(TAG, "onSessionStatusNotificationReceived - Session ID : " + sessionId
564                 + ", sessionToken: " + sessionToken + ", state : "
565                 + UwbSessionNotificationHelper.getSessionStateString(state)
566                 + ", reasonCode:" + reasonCode);
567         UwbSession uwbSession = getUwbSession((int) sessionId);
568 
569         if (uwbSession == null) {
570             Log.d(TAG, "onSessionStatusNotificationReceived - invalid session");
571             return;
572         }
573 
574         int prevState = uwbSession.getSessionState();
575         setCurrentSessionState((int) sessionId, state);
576 
577         if ((uwbSession.getOperationType() == SESSION_ON_DEINIT
578                 && state == UwbUciConstants.UWB_SESSION_STATE_IDLE)
579                 || (uwbSession.getOperationType() == SESSION_STOP_RANGING
580                 && state == UwbUciConstants.UWB_SESSION_STATE_IDLE
581                 && reasonCode != REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS)) {
582             Log.d(TAG, "Session status NTF is received due to in-band session state change");
583         }
584 
585         // Store the reasonCode before notifying on the waitObj.
586         synchronized (uwbSession.getWaitObj()) {
587             uwbSession.setLastSessionStatusNtfReasonCode(reasonCode);
588             uwbSession.getWaitObj().blockingNotify();
589         }
590 
591         //TODO : process only error handling in this switch function, b/218921154
592         switch (state) {
593             case UwbUciConstants.UWB_SESSION_STATE_IDLE:
594                 if (prevState == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
595                     // If session was stopped explicitly, then the onStopped() is sent from
596                     // stopRanging method.
597                     if (reasonCode != REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS) {
598                         mSessionNotificationManager.onRangingStoppedWithUciReasonCode(
599                                 uwbSession, reasonCode);
600                         mUwbMetrics.longRangingStopEvent(uwbSession);
601                     }
602                 } else if (prevState == UwbUciConstants.UWB_SESSION_STATE_IDLE) {
603                     //mSessionNotificationManager.onRangingReconfigureFailed(
604                     //      uwbSession, reasonCode);
605                 }
606                 break;
607             case UwbUciConstants.UWB_SESSION_STATE_DEINIT:
608                 mEventTask.execute(SESSION_ON_DEINIT, uwbSession);
609                 break;
610             default:
611                 break;
612         }
613 
614         if (mUwbInjector.getUwbServiceCore().isOemExtensionCbRegistered()) {
615             String appPackageName = uwbSession.getAnyNonPrivilegedAppInAttributionSource() != null
616                     ? uwbSession.getAnyNonPrivilegedAppInAttributionSource().getPackageName()
617                     : uwbSession.getAttributionSource().getPackageName();
618             PersistableBundle sessionStatusBundle = new SessionStatus.Builder()
619                     .setSessionId(sessionId)
620                     .setState(state)
621                     .setReasonCode(reasonCode)
622                     .setAppPackageName(appPackageName)
623                     .setSessiontoken(sessionToken)
624                     .setProtocolName(uwbSession.getProtocolName())
625                     .build()
626                     .toBundle();
627             try {
628                 mUwbInjector.getUwbServiceCore().getOemExtensionCallback()
629                         .onSessionStatusNotificationReceived(sessionStatusBundle);
630             } catch (RemoteException e) {
631                 Log.e(TAG, "Failed to send vendor notification", e);
632             }
633         }
634     }
635 
setAppConfigurations(UwbSession uwbSession)636     private int setAppConfigurations(UwbSession uwbSession) {
637         if (uwbSession.getProtocolName().equals(FiraParams.PROTOCOL_NAME)
638                         && uwbSession.getParams() instanceof FiraOpenSessionParams) {
639             FiraOpenSessionParams params = (FiraOpenSessionParams) uwbSession.getParams();
640             if (mSessionTokenMap.containsKey(params.getReferenceSessionHandle())) {
641                 uwbSession.updateFiraParamsForSessionTimeBase(
642                            mSessionTokenMap.get(params.getReferenceSessionHandle()));
643             }
644         }
645 
646         int status = mConfigurationManager.setAppConfigurations(uwbSession.getSessionId(),
647                 uwbSession.getParams(), uwbSession.getChipId(),
648                 getUwbsFiraProtocolVersion(uwbSession.getChipId()));
649 
650         if (status == UwbUciConstants.STATUS_CODE_OK
651                 && uwbSession.getProtocolName().equals(RfTestParams.PROTOCOL_NAME)) {
652             status = mConfigurationManager.setRfTestAppConfigurations(uwbSession.getSessionId(),
653                     uwbSession.getParams(), uwbSession.getChipId());
654         }
655 
656         if (status == UwbUciConstants.STATUS_CODE_OK
657                 && mUwbInjector.getUwbServiceCore().isOemExtensionCbRegistered()) {
658             try {
659                 SessionConfigParams sessionConfigParams = new SessionConfigParams.Builder()
660                         .setSessionId(uwbSession.getSessionId())
661                         .setSessiontoken(
662                                 mSessionTokenMap.getOrDefault(uwbSession.getSessionId(), 0))
663                         .setOpenSessionParamsBundle(uwbSession.getParams().toBundle())
664                         .build();
665                 status = mUwbInjector.getUwbServiceCore().getOemExtensionCallback()
666                         .onSessionConfigurationReceived(sessionConfigParams.toBundle());
667             } catch (RemoteException e) {
668                 Log.e(TAG, "Failed to send vendor notification", e);
669             }
670         }
671         return status;
672     }
673 
initSession(AttributionSource attributionSource, SessionHandle sessionHandle, int sessionId, byte sessionType, String protocolName, Params params, IUwbRangingCallbacks rangingCallbacks, String chipId)674     public synchronized void initSession(AttributionSource attributionSource,
675             SessionHandle sessionHandle, int sessionId, byte sessionType, String protocolName,
676             Params params, IUwbRangingCallbacks rangingCallbacks, String chipId)
677             throws RemoteException {
678         Log.i(TAG, "initSession() - sessionId: " + sessionId + ", sessionHandle: " + sessionHandle
679                 + ", sessionType: " + sessionType);
680         UwbSession uwbSession =  createUwbSession(attributionSource, sessionHandle, sessionId,
681                 sessionType, protocolName, params, rangingCallbacks, chipId);
682         // Check the attribution source chain to ensure that there are no 3p apps which are not in
683         // fg which can receive the ranging results.
684         AttributionSource nonPrivilegedAppAttrSource =
685                 uwbSession.getAnyNonPrivilegedAppInAttributionSource();
686         if (nonPrivilegedAppAttrSource != null) {
687             Log.d(TAG, "Found a 3p app/service in the attribution source of request: "
688                     + nonPrivilegedAppAttrSource);
689             // TODO(b/211445008): Move this operation to uwb thread.
690             boolean hasNonPrivilegedFgAppOrService = mUwbInjector.isForegroundAppOrService(
691                     nonPrivilegedAppAttrSource.getUid(),
692                     nonPrivilegedAppAttrSource.getPackageName());
693             uwbSession.setHasNonPrivilegedFgAppOrService(hasNonPrivilegedFgAppOrService);
694             if (!hasNonPrivilegedFgAppOrService) {
695                 if (!mUwbInjector.getDeviceConfigFacade().isBackgroundRangingEnabled()) {
696                     Log.e(TAG, "openRanging - System policy disallows for non fg 3p apps");
697                     rangingCallbacks.onRangingOpenFailed(sessionHandle,
698                             RangingChangeReason.SYSTEM_POLICY, new PersistableBundle());
699                     return;
700                 } else {
701                     Log.d(TAG, "openRanging - System policy allows for non fg 3p apps");
702                 }
703             }
704         }
705         if (isExistedSession(sessionHandle) || isExistedSession(sessionId)) {
706             Log.i(TAG, "Duplicated session. SessionHandle: " + sessionHandle + ", SessionId: "
707                     + sessionId);
708             rangingCallbacks.onRangingOpenFailed(sessionHandle, RangingChangeReason.BAD_PARAMETERS,
709                     UwbSessionNotificationHelper.convertUciStatusToParam(protocolName,
710                             UwbUciConstants.STATUS_CODE_ERROR_SESSION_DUPLICATE));
711             mUwbMetrics.logRangingInitEvent(uwbSession,
712                     UwbUciConstants.STATUS_CODE_ERROR_SESSION_DUPLICATE);
713             return;
714         }
715 
716         boolean maxSessionsExceeded = false;
717         // TODO: getCccSessionCount and getFiraSessionCount should be chip specific
718         if (protocolName.equals(AliroParams.PROTOCOL_NAME)
719                 && getAliroSessionCount() >= getMaxAliroSessionsNumber(chipId)) {
720             Log.i(TAG, "Max ALIRO Sessions Exceeded");
721             // All ALIRO sessions have the same priority so there's no point in trying to make space
722             // if max sessions are already reached.
723             maxSessionsExceeded = true;
724         } else if (protocolName.equals(CccParams.PROTOCOL_NAME)
725                 && getCccSessionCount() >= getMaxCccSessionsNumber(chipId)) {
726             Log.i(TAG, "Max CCC Sessions Exceeded");
727             // All CCC sessions have the same priority so there's no point in trying to make space
728             // if max sessions are already reached.
729             maxSessionsExceeded = true;
730         } else if (protocolName.equals(FiraParams.PROTOCOL_NAME)
731                 && getFiraSessionCount() >= getMaxFiraSessionsNumber(chipId)) {
732             Log.i(TAG, "Max Fira Sessions Exceeded");
733             maxSessionsExceeded = !tryMakeSpaceForFiraSession(
734                     uwbSession.getStackSessionPriority());
735         }
736 
737         if (!maxSessionsExceeded && getSessionCount() >= getMaxSupportedSessionCount(chipId)) {
738             maxSessionsExceeded = true;
739             Log.i(TAG, "Session count exceeds max supported Session count");
740         }
741 
742         if (maxSessionsExceeded) {
743             rangingCallbacks.onRangingOpenFailed(sessionHandle,
744                     RangingChangeReason.MAX_SESSIONS_REACHED,
745                     UwbSessionNotificationHelper.convertUciStatusToParam(protocolName,
746                             UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED));
747             mUwbMetrics.logRangingInitEvent(uwbSession,
748                     UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED);
749             return;
750         }
751 
752         try {
753             uwbSession.getBinder().linkToDeath(uwbSession, 0);
754         } catch (RemoteException e) {
755             uwbSession.binderDied();
756             Log.e(TAG, "linkToDeath fail - sessionID : " + uwbSession.getSessionId());
757             rangingCallbacks.onRangingOpenFailed(sessionHandle, RangingChangeReason.UNKNOWN,
758                     UwbSessionNotificationHelper.convertUciStatusToParam(protocolName,
759                             UwbUciConstants.STATUS_CODE_FAILED));
760             mUwbMetrics.logRangingInitEvent(uwbSession,
761                     UwbUciConstants.STATUS_CODE_FAILED);
762             removeSession(uwbSession);
763             return;
764         }
765 
766         mSessionTable.put(sessionHandle, uwbSession);
767         addToNonPrivilegedUidToFiraSessionTableIfNecessary(uwbSession);
768         mEventTask.execute(SESSION_OPEN_RANGING, uwbSession);
769         return;
770     }
771 
tryMakeSpaceForFiraSession(int priorityThreshold)772     private boolean tryMakeSpaceForFiraSession(int priorityThreshold) {
773         Optional<UwbSession> lowestPrioritySession = getSessionWithLowestPriorityByProtocol(
774                 FiraParams.PROTOCOL_NAME);
775         if (!lowestPrioritySession.isPresent()) {
776             Log.w(TAG,
777                     "New session blocked by max sessions exceeded, but list of sessions is "
778                             + "empty");
779             return false;
780         }
781         if (lowestPrioritySession.get().getStackSessionPriority() < priorityThreshold) {
782             return deInitDueToLowPriority(lowestPrioritySession.get().getSessionHandle());
783         }
784         return false;
785     }
786 
787     // TODO: use UwbInjector.
788     @VisibleForTesting
createUwbSession(AttributionSource attributionSource, SessionHandle sessionHandle, int sessionId, byte sessionType, String protocolName, Params params, IUwbRangingCallbacks iUwbRangingCallbacks, String chipId)789     UwbSession createUwbSession(AttributionSource attributionSource, SessionHandle sessionHandle,
790             int sessionId, byte sessionType, String protocolName, Params params,
791             IUwbRangingCallbacks iUwbRangingCallbacks, String chipId) {
792         return new UwbSession(attributionSource, sessionHandle, sessionId, sessionType,
793                 protocolName, params, iUwbRangingCallbacks, chipId);
794     }
795 
deInitSession(SessionHandle sessionHandle)796     public synchronized void deInitSession(SessionHandle sessionHandle) {
797         if (!isExistedSession(sessionHandle)) {
798             Log.i(TAG, "Not initialized session ID");
799             return;
800         }
801 
802         int sessionId = getSessionId(sessionHandle);
803         Log.i(TAG, "deinitSession() - sessionId: " + sessionId
804                 + ", sessionHandle: " + sessionHandle);
805         mEventTask.execute(SESSION_DEINIT, sessionHandle, STATUS_CODE_OK);
806         return;
807     }
808 
809     /**
810      * Logs and executes session de-init task with low priority being sent as the reason in
811      * ranging closed callback.
812      */
deInitDueToLowPriority(SessionHandle sessionHandle)813     private synchronized boolean deInitDueToLowPriority(SessionHandle sessionHandle) {
814         int sessionId = getSessionId(sessionHandle);
815         if (!isExistedSession(sessionHandle)) {
816             Log.w(TAG, "Session " + sessionId + " expected to exist but not found. "
817                     + "Failed to de-initialize low priority session.");
818             return false;
819         }
820 
821         Log.i(TAG, "deInitDueToLowPriority() - sessionId: " + sessionId
822                 + ", sessionHandle: " + sessionHandle);
823         mEventTask.execute(SESSION_DEINIT, sessionHandle,
824                 UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED);
825         return true;
826     }
827 
startRanging(SessionHandle sessionHandle, @Nullable Params params)828     public synchronized void startRanging(SessionHandle sessionHandle, @Nullable Params params) {
829         if (!isExistedSession(sessionHandle)) {
830             Log.i(TAG, "Not initialized session ID");
831             return;
832         }
833 
834         int sessionId = getSessionId(sessionHandle);
835         Log.i(TAG, "startRanging() - sessionId: " + sessionId
836                 + ", sessionHandle: " + sessionHandle);
837 
838         UwbSession uwbSession = getUwbSession(sessionId);
839 
840         int currentSessionState = getCurrentSessionState(sessionId);
841         if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_IDLE) {
842             if (uwbSession.getProtocolName().equals(AliroParams.PROTOCOL_NAME)
843                     && params instanceof AliroStartRangingParams) {
844                 AliroStartRangingParams aliroStartRangingParams = (AliroStartRangingParams) params;
845                 Log.i(TAG, "startRanging() - update RAN multiplier: "
846                         + aliroStartRangingParams.getRanMultiplier()
847                         + ", stsIndex: " + aliroStartRangingParams.getStsIndex());
848                 // Need to update the RAN multiplier from the AliroStartRangingParams for an
849                 // ALIRO session.
850                 uwbSession.updateAliroParamsOnStart(aliroStartRangingParams);
851             } else if (uwbSession.getProtocolName().equals(CccParams.PROTOCOL_NAME)
852                     && params instanceof CccStartRangingParams) {
853                 CccStartRangingParams cccStartRangingParams = (CccStartRangingParams) params;
854                 Log.i(TAG, "startRanging() - update RAN multiplier: "
855                         + cccStartRangingParams.getRanMultiplier()
856                         + ", stsIndex: " + cccStartRangingParams.getStsIndex());
857                 // Need to update the RAN multiplier from the CccStartRangingParams for CCC session.
858                 uwbSession.updateCccParamsOnStart(cccStartRangingParams);
859             } else if (uwbSession.getProtocolName().equals(FiraParams.PROTOCOL_NAME)) {
860                 // Need to update session priority if it changed.
861                 uwbSession.updateFiraParamsOnStartIfChanged();
862             } else if (uwbSession.getProtocolName().equals(RfTestParams.PROTOCOL_NAME)) {
863                 if (params instanceof RfTestStartSessionParams) {
864                     uwbSession.setRfTestStartSessionParams((RfTestStartSessionParams) params);
865                     mEventTask.execute(SESSION_RF_TEST_CMD, uwbSession);
866                 }
867                 return;
868             }
869             mEventTask.execute(SESSION_START_RANGING, uwbSession);
870         } else if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
871             Log.i(TAG, "session is already ranging");
872             mSessionNotificationManager.onRangingStartFailed(
873                     uwbSession, UwbUciConstants.STATUS_CODE_REJECTED);
874         } else {
875             Log.i(TAG, "session can't start ranging");
876             mSessionNotificationManager.onRangingStartFailed(
877                     uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
878             mUwbMetrics.longRangingStartEvent(uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
879         }
880     }
881 
handleRfTestCommand(UwbSession uwbSession)882     private void handleRfTestCommand(UwbSession uwbSession) {
883         RfTestStartSessionParams params = uwbSession.getRfTestStartSessionParams();
884         // create task to send RF command
885         FutureTask<Integer> rfCommandTask = new FutureTask<>((Callable<Integer>) () -> {
886             int rfTestOperationType = params.getRfTestOperationType();
887             int status = UwbUciConstants.STATUS_CODE_FAILED;
888 
889             synchronized (uwbSession.getWaitObj()) {
890                 switch (rfTestOperationType) {
891                     case RfTestParams.TEST_PERIODIC_TX:
892                         status = mNativeUwbManager.testPeriodicTx(params.getPsduData(),
893                                 uwbSession.getChipId());
894                         break;
895                     case RfTestParams.TEST_PER_RX:
896                         status = mNativeUwbManager.testPerRx(params.getPsduData(),
897                                 uwbSession.getChipId());
898                         break;
899                     default:
900                         Log.i(TAG, "Unknown RF command: " + rfTestOperationType);
901                 }
902 
903                 if (status != UwbUciConstants.STATUS_CODE_OK) {
904                     mSessionNotificationManager.onRangingStartFailed(uwbSession, status);
905                 } else {
906                     uwbSession.getWaitObj().blockingWait();
907 
908                     RfTestSessionStatus rfTestSessionStatus = new RfTestSessionStatus.Builder()
909                             .setRfTestOperationType(rfTestOperationType)
910                             .setStatusCode(status).build();
911                     mSessionNotificationManager.onRangingStarted(uwbSession,
912                             rfTestSessionStatus);
913                 }
914             }
915             return status;
916         });
917 
918         // Execute task
919         int status = UwbUciConstants.STATUS_CODE_FAILED;
920 
921         try {
922             status = mUwbInjector.runTaskOnSingleThreadExecutor(rfCommandTask,
923                     IUwbAdapter.RF_TEST_OPERATION_THRESHOLD_MS);
924         } catch (TimeoutException e) {
925             Log.i(TAG, "Failed to send RF command: TIMEOUT");
926             mSessionNotificationManager.onRangingStartFailed(uwbSession, status);
927         } catch (InterruptedException | ExecutionException e) {
928             e.printStackTrace();
929         }
930     }
931 
handleStopRfTest(UwbSession uwbSession)932     private void handleStopRfTest(UwbSession uwbSession) {
933         Trace.beginSection("UWB#handleStopRfTest");
934         FutureTask<Integer> stopRfSessionTask = new FutureTask<>(
935                 () -> {
936                     int status = UwbUciConstants.STATUS_CODE_FAILED;
937                     synchronized (uwbSession.getWaitObj()) {
938                         status = mNativeUwbManager.stopRfTest(uwbSession.getChipId());
939                         if (status != UwbUciConstants.STATUS_CODE_OK) {
940                             mSessionNotificationManager.onRangingStopFailed(uwbSession,
941                                     status);
942                             return status;
943                         }
944 
945                         uwbSession.getWaitObj().blockingWait();
946                         // After stop rf test session command, UWBS will go to IDLE state
947                         if (uwbSession.getSessionState()
948                                 == UwbUciConstants.UWB_SESSION_STATE_IDLE) {
949                             mSessionNotificationManager.onRangingStopped(uwbSession,
950                                     status);
951                         } else {
952                             status = UwbUciConstants.STATUS_CODE_FAILED;
953                             mSessionNotificationManager.onRangingStopFailed(uwbSession,
954                                     status);
955                         }
956                     }
957                     return status;
958                 });
959 
960 
961         int status = UwbUciConstants.STATUS_CODE_FAILED;
962         try {
963             status = mUwbInjector.runTaskOnSingleThreadExecutor(stopRfSessionTask,
964                     IUwbAdapter.RF_TEST_OPERATION_THRESHOLD_MS);
965         } catch (TimeoutException e) {
966             Log.i(TAG, "Failed to Stop RF test - status : TIMEOUT");
967             mSessionNotificationManager.onRangingStopFailed(
968                     uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
969         } catch (InterruptedException | ExecutionException e) {
970             e.printStackTrace();
971         }
972         Trace.endSection();
973     }
974 
stopRangingInternal(SessionHandle sessionHandle, boolean triggeredBySystemPolicy)975     private synchronized void stopRangingInternal(SessionHandle sessionHandle,
976             boolean triggeredBySystemPolicy) {
977         if (!isExistedSession(sessionHandle)) {
978             Log.i(TAG, "Not initialized session ID");
979             return;
980         }
981 
982         int sessionId = getSessionId(sessionHandle);
983         Log.i(TAG, "stopRanging() - sessionId: " + sessionId
984                 + ", sessionHandle: " + sessionHandle);
985 
986         UwbSession uwbSession = getUwbSession(sessionId);
987         int currentSessionState = getCurrentSessionState(sessionId);
988 
989         //RF Test session
990         if (uwbSession.getProtocolName().equals(RfTestParams.PROTOCOL_NAME)) {
991             if (sessionId != RfTestParams.SESSION_ID_RFTEST) {
992                 mSessionNotificationManager.onRangingStopped(uwbSession,
993                         UwbUciConstants.STATUS_CODE_REJECTED);
994             }
995 
996             if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
997                 mEventTask.execute(SESSION_STOP_RF_TEST_SESSION, uwbSession);
998             } else {
999                 mSessionNotificationManager.onRangingStopped(uwbSession,
1000                         UwbUciConstants.STATUS_CODE_REJECTED);
1001                 Log.i(TAG, "RF session is not in ACTIVE state");
1002             }
1003             return;
1004         }
1005 
1006         if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
1007             mEventTask.execute(SESSION_STOP_RANGING, uwbSession, triggeredBySystemPolicy ? 1 : 0);
1008         } else if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_IDLE) {
1009             Log.i(TAG, "session is already idle state");
1010             mSessionNotificationManager.onRangingStopped(uwbSession,
1011                     UwbUciConstants.STATUS_CODE_OK);
1012             mUwbMetrics.longRangingStopEvent(uwbSession);
1013         } else {
1014             mSessionNotificationManager.onRangingStopFailed(uwbSession,
1015                     UwbUciConstants.STATUS_CODE_REJECTED);
1016             Log.i(TAG, "Not active session ID");
1017         }
1018     }
1019 
stopRanging(SessionHandle sessionHandle)1020     public synchronized void stopRanging(SessionHandle sessionHandle) {
1021         stopRangingInternal(sessionHandle, false /* triggeredBySystemPolicy */);
1022     }
1023 
1024     /**
1025      * Get the UwbSession corresponding to the given UWB Session ID. This API returns {@code null}
1026      * when the UWB session is not found.
1027      */
1028     @Nullable
getUwbSession(int sessionId)1029     public UwbSession getUwbSession(int sessionId) {
1030         return mSessionTable.values()
1031                 .stream()
1032                 .filter(v -> v.getSessionId() == sessionId)
1033                 .findAny()
1034                 .orElse(null);
1035     }
1036 
1037     /**
1038      * Get the UwbSession corresponding to the given UWB SessionHandle. This API returns
1039      * {@code null} when the UWB session is not found.
1040      */
1041     @Nullable
getUwbSession(SessionHandle sessionHandle)1042     private UwbSession getUwbSession(SessionHandle sessionHandle) {
1043         return mSessionTable.get(sessionHandle);
1044     }
1045 
1046     /**
1047      * Get the Uwb Session ID corresponding to the given UWB Session Handle. This API returns
1048      * {@code null} when the UWB session ID is not found.
1049      */
1050     @Nullable
getSessionId(SessionHandle sessionHandle)1051     public Integer getSessionId(SessionHandle sessionHandle) {
1052         UwbSession session = mSessionTable.get(sessionHandle);
1053         if (session == null) return null;
1054         return session.getSessionId();
1055     }
1056 
getActiveSessionCount()1057     private int getActiveSessionCount() {
1058         return Math.toIntExact(
1059                 mSessionTable.values()
1060                         .stream()
1061                         .filter(v -> v.getSessionState() == UwbUciConstants.DEVICE_STATE_ACTIVE)
1062                         .count()
1063         );
1064     }
1065 
processRangeData(UwbRangingData rangingData, UwbSession uwbSession)1066     private void processRangeData(UwbRangingData rangingData, UwbSession uwbSession) {
1067         if (rangingData.getRangingMeasuresType()
1068                 != UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA) {
1069             return;
1070         }
1071 
1072         if (!isValidUwbSessionForOwrAoaRanging(uwbSession)) {
1073             return;
1074         }
1075 
1076         // Record the OWR Aoa Measurement from the RANGE_DATA_NTF.
1077         UwbOwrAoaMeasurement uwbOwrAoaMeasurement = rangingData.getRangingOwrAoaMeasure();
1078         mAdvertiseManager.updateAdvertiseTarget(uwbOwrAoaMeasurement);
1079 
1080         byte[] macAddressBytes = getValidMacAddressFromOwrAoaMeasurement(
1081                 rangingData, uwbOwrAoaMeasurement);
1082         if (macAddressBytes == null)  {
1083             Log.i(TAG, "OwR Aoa UwbSession: Invalid MacAddress for remote device");
1084             return;
1085         }
1086 
1087         boolean advertisePointingResult = mAdvertiseManager.isPointedTarget(macAddressBytes);
1088         if (mUwbInjector.getUwbServiceCore().isOemExtensionCbRegistered()) {
1089             try {
1090                 PersistableBundle pointedTargetBundle = new AdvertisePointedTarget.Builder()
1091                         .setMacAddress(macAddressBytes)
1092                         .setAdvertisePointingResult(advertisePointingResult)
1093                         .build()
1094                         .toBundle();
1095 
1096                 advertisePointingResult = mUwbInjector
1097                         .getUwbServiceCore()
1098                         .getOemExtensionCallback()
1099                         .onCheckPointedTarget(pointedTargetBundle);
1100             } catch (RemoteException e) {
1101                 e.printStackTrace();
1102             }
1103         }
1104 
1105         if (advertisePointingResult) {
1106             // Use a loop to notify all the received application data payload(s) (in sequence number
1107             // order) for this OWR AOA ranging session.
1108             long macAddress = macAddressByteArrayToLong(macAddressBytes);
1109             UwbAddress uwbAddress = UwbAddress.fromBytes(macAddressBytes);
1110 
1111             List<ReceivedDataInfo> receivedDataInfoList = uwbSession.getAllReceivedDataInfo(
1112                     macAddress);
1113             if (receivedDataInfoList.isEmpty()) {
1114                 Log.i(TAG, "OwR Aoa UwbSession: Application Payload data not found for"
1115                         + " MacAddress = " + UwbUtil.toHexString(macAddress));
1116                 return;
1117             }
1118 
1119             receivedDataInfoList.stream().forEach(r ->
1120                     mSessionNotificationManager.onDataReceived(
1121                             uwbSession, uwbAddress, new PersistableBundle(), r.payload));
1122             mUwbMetrics.logDataToUpperLayer(uwbSession, receivedDataInfoList.size());
1123             mAdvertiseManager.removeAdvertiseTarget(macAddress);
1124         }
1125     }
1126 
1127     @Nullable
getValidMacAddressFromOwrAoaMeasurement(UwbRangingData rangingData, UwbOwrAoaMeasurement uwbOwrAoaMeasurement)1128     private byte[] getValidMacAddressFromOwrAoaMeasurement(UwbRangingData rangingData,
1129             UwbOwrAoaMeasurement uwbOwrAoaMeasurement) {
1130         byte[] macAddress = uwbOwrAoaMeasurement.getMacAddress();
1131         if (rangingData.getMacAddressMode() == MAC_ADDRESSING_MODE_SHORT) {
1132             return (macAddress.length == UWB_DEVICE_SHORT_MAC_ADDRESS_LEN) ? macAddress : null;
1133         } else if (rangingData.getMacAddressMode() == MAC_ADDRESSING_MODE_EXTENDED) {
1134             return (macAddress.length == UWB_DEVICE_EXT_MAC_ADDRESS_LEN) ? macAddress : null;
1135         }
1136         return null;
1137     }
1138 
isExistedSession(SessionHandle sessionHandle)1139     public boolean isExistedSession(SessionHandle sessionHandle) {
1140         return (getSessionId(sessionHandle) != null);
1141     }
1142 
isExistedSession(int sessionId)1143     public boolean isExistedSession(int sessionId) {
1144         return getUwbSession(sessionId) != null;
1145     }
1146 
stopAllRanging()1147     public void stopAllRanging() {
1148         Log.d(TAG, "stopAllRanging()");
1149         for (UwbSession uwbSession : mSessionTable.values()) {
1150             int status = mNativeUwbManager.stopRanging(uwbSession.getSessionId(),
1151                     uwbSession.getChipId());
1152 
1153             if (status != UwbUciConstants.STATUS_CODE_OK) {
1154                 Log.i(TAG, "stopAllRanging() - Session " + uwbSession.getSessionId()
1155                         + " is failed to stop ranging");
1156             } else {
1157                 mUwbMetrics.longRangingStopEvent(uwbSession);
1158                 uwbSession.setSessionState(UwbUciConstants.UWB_SESSION_STATE_IDLE);
1159             }
1160         }
1161     }
1162 
deInitAllSession()1163     public synchronized void deInitAllSession() {
1164         Log.d(TAG, "deinitAllSession()");
1165         for (UwbSession uwbSession : mSessionTable.values()) {
1166             handleOnDeInit(uwbSession);
1167         }
1168 
1169         // Not resetting chip on UWB toggle off.
1170         // mNativeUwbManager.deviceReset(UwbUciConstants.UWBS_RESET);
1171     }
1172 
handleOnDeInit(UwbSession uwbSession)1173     public synchronized void handleOnDeInit(UwbSession uwbSession) {
1174         if (!isExistedSession(uwbSession.getSessionHandle())) {
1175             Log.i(TAG, "onDeinit - Ignoring already deleted session "
1176                     + uwbSession.getSessionId());
1177             return;
1178         }
1179         Log.d(TAG, "onDeinit: " + uwbSession.getSessionId());
1180         mSessionNotificationManager.onRangingClosedWithApiReasonCode(uwbSession,
1181                 RangingChangeReason.SYSTEM_POLICY);
1182         mUwbMetrics.logRangingCloseEvent(uwbSession, UwbUciConstants.STATUS_CODE_OK);
1183 
1184         // Reset all UWB session timers when the session is de-init.
1185         uwbSession.stopTimers();
1186         removeSession(uwbSession);
1187     }
1188 
setCurrentSessionState(int sessionId, int state)1189     public void setCurrentSessionState(int sessionId, int state) {
1190         UwbSession uwbSession = getUwbSession(sessionId);
1191         if (uwbSession != null) {
1192             uwbSession.setSessionState(state);
1193         }
1194     }
1195 
getCurrentSessionState(int sessionId)1196     public int getCurrentSessionState(int sessionId) {
1197         UwbSession uwbSession = getUwbSession(sessionId);
1198         if (uwbSession != null) {
1199             return uwbSession.getSessionState();
1200         }
1201         return UwbUciConstants.UWB_SESSION_STATE_ERROR;
1202     }
1203 
getSessionCount()1204     public int getSessionCount() {
1205         return mSessionTable.size();
1206     }
1207 
getAliroSessionCount()1208     public long getAliroSessionCount() {
1209         return getProtocolSessionCount(AliroParams.PROTOCOL_NAME);
1210     }
1211 
getCccSessionCount()1212     public long getCccSessionCount() {
1213         return getProtocolSessionCount(CccParams.PROTOCOL_NAME);
1214     }
1215 
getFiraSessionCount()1216     public long getFiraSessionCount() {
1217         return getProtocolSessionCount(FiraParams.PROTOCOL_NAME);
1218     }
1219 
getProtocolSessionCount(String protocolName)1220     private long getProtocolSessionCount(String protocolName) {
1221         return mSessionTable.values().stream().filter(
1222                 s -> s.mProtocolName.equals(protocolName)).count();
1223     }
1224 
1225     /** Returns max number of ALIRO sessions possible on given chip. */
getMaxAliroSessionsNumber(String chipId)1226     public long getMaxAliroSessionsNumber(String chipId) {
1227         GenericSpecificationParams params =
1228                 mUwbInjector.getUwbServiceCore().getCachedSpecificationParams(chipId);
1229         if (params != null && params.getAliroSpecificationParams() != null) {
1230             return params.getAliroSpecificationParams().getMaxRangingSessionNumber();
1231         } else {
1232             // Specification params are empty, return the default ALIRO max sessions value
1233             return AliroSpecificationParams.DEFAULT_MAX_RANGING_SESSIONS_NUMBER;
1234         }
1235     }
1236 
1237     /** Returns max number of CCC sessions possible on given chip. */
getMaxCccSessionsNumber(String chipId)1238     public long getMaxCccSessionsNumber(String chipId) {
1239         GenericSpecificationParams params =
1240                 mUwbInjector.getUwbServiceCore().getCachedSpecificationParams(chipId);
1241         if (params != null && params.getCccSpecificationParams() != null) {
1242             return params.getCccSpecificationParams().getMaxRangingSessionNumber();
1243         } else {
1244             // specification params are empty, return the default CCC max sessions value
1245             return CccSpecificationParams.DEFAULT_MAX_RANGING_SESSIONS_NUMBER;
1246         }
1247     }
1248 
1249     /** Returns max number of Fira sessions possible on given chip. */
getMaxFiraSessionsNumber(String chipId)1250     public long getMaxFiraSessionsNumber(String chipId) {
1251         GenericSpecificationParams params =
1252                 mUwbInjector.getUwbServiceCore().getCachedSpecificationParams(chipId);
1253         if (params != null && params.getFiraSpecificationParams() != null) {
1254             return params.getFiraSpecificationParams().getMaxRangingSessionNumber();
1255         } else {
1256             // specification params are empty, return the default Fira max sessions value
1257             return FiraSpecificationParams.DEFAULT_MAX_RANGING_SESSIONS_NUMBER;
1258         }
1259     }
1260 
1261     /** Returns max supported session count possible on given chip. */
getMaxSupportedSessionCount(String chipId)1262     public long getMaxSupportedSessionCount(String chipId) {
1263         GenericSpecificationParams params =
1264                 mUwbInjector.getUwbServiceCore().getCachedSpecificationParams(chipId);
1265         return (params != null)
1266                 ? params.getMaxSupportedSessionCount()
1267                 : getMaxFiraSessionsNumber(chipId) + getMaxCccSessionsNumber(chipId);
1268     }
1269 
1270     /** Gets the session with the lowest session priority among all sessions with given protocol. */
getSessionWithLowestPriorityByProtocol(String protocolName)1271     public Optional<UwbSession> getSessionWithLowestPriorityByProtocol(String protocolName) {
1272         return mSessionTable.values().stream().filter(
1273                 s -> s.mProtocolName.equals(protocolName)).min(
1274                 Comparator.comparingInt(UwbSession::getStackSessionPriority));
1275     }
1276 
getSessionIdSet()1277     public Set<Integer> getSessionIdSet() {
1278         return mSessionTable.values()
1279                 .stream()
1280                 .map(v -> v.getSessionId())
1281                 .collect(Collectors.toSet());
1282     }
1283 
suspendRangingPreconditionCheck(UwbSession uwbSession)1284     private boolean suspendRangingPreconditionCheck(UwbSession uwbSession) {
1285         FiraOpenSessionParams firaOpenSessionParams =
1286                 (FiraOpenSessionParams) uwbSession.getParams();
1287         int deviceType = firaOpenSessionParams.getDeviceType();
1288         int scheduleMode = firaOpenSessionParams.getScheduledMode();
1289         int sessionState = uwbSession.getSessionState();
1290         if (deviceType != FiraParams.RANGING_DEVICE_TYPE_CONTROLLER ||
1291                 scheduleMode != FiraParams.TIME_SCHEDULED_RANGING ||
1292                 sessionState != UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
1293             Log.e(TAG, "suspendRangingPreconditionCheck failed - deviceType: " + deviceType +
1294                     " scheduleMode: " + scheduleMode + " sessionState: " + sessionState);
1295             return false;
1296         }
1297         return true;
1298     }
1299 
sessionUpdateMulticastListCmdPreconditioncheck(UwbSession uwbSession, int action, byte[] subSessionKeyList)1300     private boolean sessionUpdateMulticastListCmdPreconditioncheck(UwbSession uwbSession,
1301               int action, byte[] subSessionKeyList) {
1302         FiraOpenSessionParams firaOpenSessionParams =
1303                 (FiraOpenSessionParams) uwbSession.getParams();
1304         int deviceType = firaOpenSessionParams.getDeviceType();
1305         int stsConfig = firaOpenSessionParams.getStsConfig();
1306         byte[] sessionKey = firaOpenSessionParams.getSessionKey();
1307         Log.i(TAG, "sessionUpdateMulticastListCmdPreconditioncheck  - deviceType: "
1308                    + deviceType + " stsConfig: " + stsConfig + " action: " + action);
1309         if (deviceType == FiraParams.RANGING_DEVICE_TYPE_CONTROLLER) {
1310             switch (action) {
1311                 case FiraParams.MULTICAST_LIST_UPDATE_ACTION_ADD:
1312                 case FiraParams.MULTICAST_LIST_UPDATE_ACTION_DELETE:
1313                     if (subSessionKeyList != null) {
1314                         return false;
1315                     }
1316                     break;
1317                 case FiraParams.P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_16_BYTE:
1318                 case FiraParams.P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_32_BYTE:
1319                     if (stsConfig
1320                             != FiraParams.STS_CONFIG_PROVISIONED_FOR_CONTROLEE_INDIVIDUAL_KEY) {
1321                         return false;
1322                     }
1323                     // sessionKey is provided while opening the session and subSessionKeyList
1324                     // is provided while updating the multicast list. Check that both the
1325                     // sessionKey and subSessionKeyList are either set or not set (as both
1326                     // have to be provided by the same source - Host or SE).
1327                     if ((sessionKey == null && subSessionKeyList != null)
1328                             || (sessionKey != null && subSessionKeyList == null)) {
1329                         return false;
1330                     }
1331                     break;
1332                 default:
1333                     break;
1334             }
1335         } else {
1336             return false;
1337         }
1338         return true;
1339     }
1340 
reconfigureInternal(SessionHandle sessionHandle, @Nullable Params params, Reconfiguration.Reason reason)1341     private synchronized int reconfigureInternal(SessionHandle sessionHandle,
1342             @Nullable Params params, Reconfiguration.Reason reason) {
1343         int status = UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST;
1344         if (!isExistedSession(sessionHandle)) {
1345             Log.i(TAG, "Not initialized session ID");
1346             return status;
1347         }
1348         int sessionId = getSessionId(sessionHandle);
1349         Log.i(TAG, "reconfigure() - Session ID : " + sessionId);
1350         UwbSession uwbSession = getUwbSession(sessionId);
1351         if (uwbSession.getProtocolName().equals(FiraParams.PROTOCOL_NAME)
1352                 && params instanceof FiraRangingReconfigureParams) {
1353             FiraRangingReconfigureParams rangingReconfigureParams =
1354                     (FiraRangingReconfigureParams) params;
1355             Log.i(TAG, "reconfigure() - update reconfigure params: "
1356                     + rangingReconfigureParams);
1357             // suspendRangingPreconditionCheck only on suspend ranging reconfigure
1358             if ((rangingReconfigureParams.getSuspendRangingRounds() != null) &&
1359                     (!suspendRangingPreconditionCheck(uwbSession))) {
1360                 return UwbUciConstants.STATUS_CODE_REJECTED;
1361             }
1362             if ((rangingReconfigureParams.getAddressList() != null)
1363                     && (!sessionUpdateMulticastListCmdPreconditioncheck(uwbSession,
1364                         rangingReconfigureParams.getAction(),
1365                         rangingReconfigureParams.getSubSessionKeyList()))) {
1366                 mSessionNotificationManager.onRangingReconfigureFailed(
1367                                 uwbSession, UwbUciConstants.STATUS_CODE_INVALID_PARAM);
1368                 return UwbUciConstants.STATUS_CODE_REJECTED;
1369             }
1370             // Do not update mParams if this was triggered by framework.
1371             if (reason != Reconfiguration.Reason.FG_STATE_CHANGE) {
1372                 uwbSession.updateFiraParamsOnReconfigure(rangingReconfigureParams);
1373             }
1374         } else if (uwbSession.getProtocolName().equals(CccParams.PROTOCOL_NAME)
1375                 && params instanceof CccRangingReconfiguredParams) {
1376             CccRangingReconfiguredParams rangingReconfigureParams =
1377                     (CccRangingReconfiguredParams) params;
1378             // Do not update mParams if this was triggered by framework.
1379             if (reason != Reconfiguration.Reason.FG_STATE_CHANGE) {
1380                 uwbSession.updateCccParamsOnReconfigure(rangingReconfigureParams);
1381             }
1382         }
1383         mEventTask.execute(SESSION_RECONFIG_RANGING,
1384                 new Reconfiguration(uwbSession, params, reason));
1385         return 0;
1386     }
1387 
reconfigure(SessionHandle sessionHandle, @Nullable Params params)1388     public synchronized int reconfigure(SessionHandle sessionHandle, @Nullable Params params) {
1389         return reconfigureInternal(sessionHandle, params, Reconfiguration.Reason.REQUESTED_BY_API);
1390     }
1391 
1392     /** Send the payload data to a remote device in the UWB session */
sendData(SessionHandle sessionHandle, UwbAddress remoteDeviceAddress, PersistableBundle params, byte[] data)1393     public synchronized void sendData(SessionHandle sessionHandle, UwbAddress remoteDeviceAddress,
1394             PersistableBundle params, byte[] data) {
1395         SendDataInfo info = new SendDataInfo();
1396         info.sessionHandle = sessionHandle;
1397         info.remoteDeviceAddress = remoteDeviceAddress;
1398         info.params = params;
1399         info.data = data;
1400 
1401         mEventTask.execute(SESSION_SEND_DATA, info);
1402     }
1403 
1404     /**
1405      * Sets the hybrid UWB controller configuration.
1406      *
1407      * @param sessionHandle : Primary session handle.
1408      * @param params        : protocol specific parameters to configure the hybrid
1409      *                        session controller.
1410      */
setHybridSessionControllerConfiguration(SessionHandle sessionHandle, PersistableBundle params)1411     public void setHybridSessionControllerConfiguration(SessionHandle sessionHandle,
1412             PersistableBundle params) {
1413 
1414         if (!isExistedSession(sessionHandle)) {
1415             throw new IllegalStateException("Not initialized session ID: "
1416                     + getSessionId(sessionHandle));
1417         }
1418 
1419         HybridSessionConfig hybridSessionConfig = new HybridSessionConfig();
1420         hybridSessionConfig.sessionHandle = sessionHandle;
1421         hybridSessionConfig.params = params;
1422 
1423         mEventTask.execute(SESSION_SET_HUS_CONTROLLER_CONFIG, hybridSessionConfig);
1424     }
1425 
1426     /**
1427      * Sets the hybrid UWB controlee configuration.
1428      *
1429      * @param sessionHandle : Primary session handle.
1430      * @param params        : protocol specific parameters to configure the hybrid
1431      *                        session controlee.
1432      */
setHybridSessionControleeConfiguration(SessionHandle sessionHandle, PersistableBundle params)1433     public void setHybridSessionControleeConfiguration(SessionHandle sessionHandle,
1434             PersistableBundle params) {
1435         if (!isExistedSession(sessionHandle)) {
1436             throw new IllegalStateException("Not initialized session ID: "
1437                 + getSessionId(sessionHandle));
1438         }
1439 
1440         HybridSessionConfig hybridSessionConfig = new HybridSessionConfig();
1441         hybridSessionConfig.sessionHandle = sessionHandle;
1442         hybridSessionConfig.params = params;
1443 
1444         mEventTask.execute(SESSION_SET_HUS_CONTROLEE_CONFIG, hybridSessionConfig);
1445     }
1446 
1447     /**
1448      * Sets the data transfer session configuration
1449      *
1450      * @param sessionHandle : session handle
1451      * @param params        : protocol specific parameters to configure data transfer session
1452      */
setDataTransferPhaseConfig(SessionHandle sessionHandle, PersistableBundle params)1453     public void setDataTransferPhaseConfig(SessionHandle sessionHandle, PersistableBundle params) {
1454         if (!isExistedSession(sessionHandle)) {
1455             throw new IllegalStateException("Not initialized session ID: "
1456                 + getSessionId(sessionHandle));
1457         }
1458 
1459         UpdateSessionInfo updateSessionInfo = new UpdateSessionInfo();
1460         updateSessionInfo.sessionHandle = sessionHandle;
1461         updateSessionInfo.params = params;
1462 
1463         mEventTask.execute(SESSION_DATA_TRANSFER_PHASE_CONFIG, updateSessionInfo);
1464     }
1465 
1466     private static final class SendDataInfo {
1467         public SessionHandle sessionHandle;
1468         public UwbAddress remoteDeviceAddress;
1469         public PersistableBundle params;
1470         public byte[] data;
1471     }
1472 
1473     private static final class RangingRoundsUpdateDtTagInfo {
1474         public SessionHandle sessionHandle;
1475         public PersistableBundle params;
1476     }
1477 
1478     private static final class UpdateSessionInfo {
1479         public SessionHandle sessionHandle;
1480         public PersistableBundle params;
1481     }
1482 
1483     private static final class HybridSessionConfig {
1484         public SessionHandle sessionHandle;
1485         public PersistableBundle params;
1486     }
1487 
1488     /** DT Tag ranging round update */
rangingRoundsUpdateDtTag(SessionHandle sessionHandle, PersistableBundle bundle)1489     public void rangingRoundsUpdateDtTag(SessionHandle sessionHandle,
1490             PersistableBundle bundle) {
1491         RangingRoundsUpdateDtTagInfo info = new RangingRoundsUpdateDtTagInfo();
1492         info.sessionHandle = sessionHandle;
1493         info.params = bundle;
1494 
1495         mEventTask.execute(SESSION_UPDATE_DT_TAG_RANGING_ROUNDS, info);
1496     }
1497 
1498     /** Query Max Application data size for the given UWB Session */
queryMaxDataSizeBytes(SessionHandle sessionHandle)1499     public synchronized int queryMaxDataSizeBytes(SessionHandle sessionHandle) {
1500         if (!isExistedSession(sessionHandle)) {
1501             throw new IllegalStateException("Not initialized session ID");
1502         }
1503 
1504         int sessionId = getSessionId(sessionHandle);
1505         UwbSession uwbSession = getUwbSession(sessionId);
1506         if (uwbSession == null) {
1507             throw new IllegalStateException("UwbSession not found");
1508         }
1509 
1510         synchronized (uwbSession.getWaitObj()) {
1511             return mNativeUwbManager.queryMaxDataSizeBytes(uwbSession.getSessionId(),
1512                     uwbSession.getChipId());
1513         }
1514     }
1515 
1516     /** Handle ranging rounds update for DT Tag */
handleRangingRoundsUpdateDtTag(RangingRoundsUpdateDtTagInfo info)1517     private void handleRangingRoundsUpdateDtTag(RangingRoundsUpdateDtTagInfo info) {
1518         SessionHandle sessionHandle = info.sessionHandle;
1519         Integer sessionId = getSessionId(sessionHandle);
1520         if (sessionId == null) {
1521             Log.i(TAG, "UwbSessionId not found");
1522             return;
1523         }
1524         UwbSession uwbSession = getUwbSession(sessionId);
1525         if (uwbSession == null) {
1526             Log.i(TAG, "UwbSession not found");
1527             return;
1528         }
1529         DlTDoARangingRoundsUpdate dlTDoARangingRoundsUpdate = DlTDoARangingRoundsUpdate
1530                 .fromBundle(info.params);
1531 
1532         if (dlTDoARangingRoundsUpdate.getSessionId() != getSessionId(sessionHandle)) {
1533             throw new IllegalArgumentException("Wrong session ID");
1534         }
1535 
1536         FutureTask<DtTagUpdateRangingRoundsStatus> rangingRoundsUpdateTask = new FutureTask<>(
1537                 () -> {
1538                     synchronized (uwbSession.getWaitObj()) {
1539                         return mNativeUwbManager.sessionUpdateDtTagRangingRounds(
1540                                 (int) dlTDoARangingRoundsUpdate.getSessionId(),
1541                                 dlTDoARangingRoundsUpdate.getNoOfRangingRounds(),
1542                                 dlTDoARangingRoundsUpdate.getRangingRoundIndexes(),
1543                                 uwbSession.getChipId());
1544                     }
1545                 }
1546         );
1547 
1548         DtTagUpdateRangingRoundsStatus status = null;
1549         ExecutorService executor = Executors.newSingleThreadExecutor();
1550         executor.submit(rangingRoundsUpdateTask);
1551         try {
1552             status = rangingRoundsUpdateTask.get(IUwbAdapter
1553                     .RANGING_ROUNDS_UPDATE_DT_TAG_THRESHOLD_MS, TimeUnit.MILLISECONDS);
1554         } catch (TimeoutException e) {
1555             Log.i(TAG, "Failed to update ranging rounds for Dt tag - status : TIMEOUT");
1556             executor.shutdownNow();
1557         } catch (InterruptedException | ExecutionException e) {
1558             e.printStackTrace();
1559         }
1560         // Native stack returns null if unsuccessful
1561         if (status == null) {
1562             status = new DtTagUpdateRangingRoundsStatus(
1563                     UwbUciConstants.STATUS_CODE_ERROR_ROUND_INDEX_NOT_ACTIVATED,
1564                     0,
1565                     new byte[]{});
1566         }
1567         PersistableBundle params = new DlTDoARangingRoundsUpdateStatus.Builder()
1568                 .setStatus(status.getStatus())
1569                 .setNoOfRangingRounds(status.getNoOfRangingRounds())
1570                 .setRangingRoundIndexes(status.getRangingRoundIndexes())
1571                 .build()
1572                 .toBundle();
1573         mSessionNotificationManager.onRangingRoundsUpdateStatus(uwbSession, params);
1574     }
1575 
handleSetHybridSessionControllerConfiguration(HybridSessionConfig info)1576     private void handleSetHybridSessionControllerConfiguration(HybridSessionConfig info) {
1577         SessionHandle sessionHandle = info.sessionHandle;
1578         if (!isExistedSession(sessionHandle)) {
1579             Log.e(TAG, "handleSetHybridSessionControllerConfiguration() - cannot find session");
1580             return;
1581         }
1582 
1583         int sessionId = getSessionId(sessionHandle);
1584         UwbSession uwbSession = getUwbSession(sessionId);
1585 
1586         // precondition check
1587         int deviceType = uwbSession.getDeviceType();
1588         int scheduleMode = uwbSession.getScheduledMode();
1589         int sessionType = uwbSession.getSessionType();
1590         if (UwbUciConstants.DEVICE_TYPE_CONTROLLER != deviceType
1591                 || UwbUciConstants.HYBRID_SCHEDULED_RANGING != scheduleMode
1592                 || UwbUciConstants.SESSION_TYPE_HUS_PRIMARY_SESSION != sessionType) {
1593             Log.e(TAG, "SetHybridSessionControllerConfiguration() failed: device type: "
1594                     + deviceType + " schedule mode: "
1595                     + scheduleMode + " sessionType: " + sessionType);
1596             mSessionNotificationManager.onHybridSessionControllerConfigurationFailed(
1597                     uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
1598             return;
1599         }
1600 
1601         FiraHybridSessionControllerConfig husConfig =
1602                 FiraHybridSessionControllerConfig.fromBundle(info.params);
1603         int numberOfPhases = husConfig.getNumberOfPhases();
1604 
1605         Log.i(TAG, "handleSetHybridSessionControllerConfiguration() - sessionId: " + sessionId
1606                 + ", sessionHandle: " + sessionHandle
1607                 + ", numberOfPhases: " + numberOfPhases);
1608 
1609         ByteBuffer buffer = ByteBuffer.allocate(numberOfPhases
1610                 * UWB_HUS_CONTROLLER_PHASE_LIST_EXTENDED_MAC_ADDRESS_SIZE);
1611         buffer.order(ByteOrder.LITTLE_ENDIAN);
1612 
1613         for (FiraHybridSessionControllerConfig.FiraHybridSessionPhaseList phaseList :
1614                 husConfig.getPhaseList()) {
1615             buffer.putInt(phaseList.getSessionId());
1616             buffer.putShort(phaseList.getStartSlotIndex());
1617             buffer.putShort(phaseList.getEndSlotIndex());
1618 
1619             byte messageControl = phaseList.getMessageControl();
1620             buffer.put(messageControl);
1621 
1622             // validate the MacAddress
1623             byte macAddressMode = (byte) (messageControl & 0x01);
1624             int addressByteLength = (macAddressMode
1625                         == UwbUciConstants.SHORT_MAC_ADDRESS)
1626                     ? UwbAddress.SHORT_ADDRESS_BYTE_LENGTH
1627                     : UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH;
1628             UwbAddress uwbAddress = phaseList.getMacAddress();
1629             if (uwbAddress == null || uwbAddress.size() != addressByteLength) {
1630                 Log.e(TAG, "handleSetHybridSessionControllerConfiguration() invalid UWB address");
1631                 mSessionNotificationManager.onHybridSessionControllerConfigurationFailed(
1632                          uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
1633                 return;
1634             }
1635 
1636             buffer.put(getComputedMacAddress(uwbAddress));
1637         }
1638 
1639         byte[] phaseListArray = Arrays.copyOf(buffer.array(), buffer.position());
1640         // create session set hus controller configuration task
1641         FutureTask<Integer> sessionsetHybridControllerConfigTask = new FutureTask<>(
1642                 (Callable<Integer>) () -> {
1643                     int status = UwbUciConstants.STATUS_CODE_FAILED;
1644                     synchronized (uwbSession.getWaitObj()) {
1645                         status = mNativeUwbManager.setHybridSessionControllerConfiguration(
1646                                 sessionId, numberOfPhases, phaseListArray,
1647                                 uwbSession.getChipId());
1648                     }
1649                     return status;
1650                 }
1651         );
1652 
1653         // execute task
1654         int status = UwbUciConstants.STATUS_CODE_FAILED;
1655         try {
1656             status = mUwbInjector.runTaskOnSingleThreadExecutor(
1657                 sessionsetHybridControllerConfigTask,
1658                     IUwbAdapter.SESSION_CONFIGURATION_THRESHOLD_MS);
1659         } catch (TimeoutException e) {
1660             Log.e(TAG, "Failed to set session hybrid controller config : TIMEOUT");
1661             mSessionNotificationManager.onHybridSessionControllerConfigurationFailed(
1662                     uwbSession, status);
1663         } catch (InterruptedException | ExecutionException e) {
1664             Log.e(TAG, "Exception while executing task " + e);
1665         }
1666 
1667         if (UwbUciConstants.STATUS_CODE_OK == status) {
1668             mSessionNotificationManager.onHybridSessionControllerConfigured(uwbSession,
1669                     status);
1670         } else {
1671             Log.e(TAG, "Failed to configure controller hybrid session - status : " + status);
1672             mSessionNotificationManager.onHybridSessionControllerConfigurationFailed(uwbSession,
1673                     status);
1674         }
1675     }
1676 
handleSetHybridSessionControleeConfiguration(HybridSessionConfig info)1677     private void handleSetHybridSessionControleeConfiguration(HybridSessionConfig info) {
1678         SessionHandle sessionHandle = info.sessionHandle;
1679         if (!isExistedSession(sessionHandle)) {
1680             Log.e(TAG, "handleSetHybridSessionControlleeConfiguration() - cannot find session");
1681             return;
1682         }
1683 
1684         int sessionId = getSessionId(sessionHandle);
1685         UwbSession uwbSession = getUwbSession(sessionId);
1686 
1687         // precondition check
1688         int deviceType = uwbSession.getDeviceType();
1689         int scheduleMode = uwbSession.getScheduledMode();
1690         int sessionType = uwbSession.getSessionType();
1691         if (UwbUciConstants.DEVICE_TYPE_CONTROLEE != deviceType
1692                 || UwbUciConstants.HYBRID_SCHEDULED_RANGING != scheduleMode
1693                 || UwbUciConstants.SESSION_TYPE_HUS_PRIMARY_SESSION != sessionType) {
1694             Log.e(TAG, "handleSetHybridSessionControleeConfiguration() failed: device type: "
1695                     + deviceType + " schedule mode: " + scheduleMode
1696                     + " sessionType: " + sessionType);
1697             mSessionNotificationManager.onHybridSessionControleeConfigurationFailed(
1698                     uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
1699             return;
1700         }
1701 
1702         FiraHybridSessionControleeConfig controleeConfig =
1703                 FiraHybridSessionControleeConfig.fromBundle(info.params);
1704         int numberOfPhases = controleeConfig.getNumberOfPhases();
1705 
1706         Log.i(TAG, "handleSetHybridSessionControleeConfiguration() - sessionId: " + sessionId
1707                 + ", sessionHandle: " + sessionHandle
1708                 + ", numberOfPhases: " + numberOfPhases);
1709 
1710         ByteBuffer phaseListBuffer = ByteBuffer.allocate(numberOfPhases
1711                 * UWB_HUS_CONTROLEE_PHASE_LIST_SIZE);
1712         phaseListBuffer.order(ByteOrder.LITTLE_ENDIAN);
1713 
1714         for (FiraHybridSessionControleeConfig.FiraHybridSessionPhaseList phaseList :
1715                 controleeConfig.getPhaseList()) {
1716             phaseListBuffer.putInt(mNativeUwbManager.getSessionToken(phaseList.getSessionHandle(),
1717                     uwbSession.getChipId()));
1718         }
1719 
1720         // create session set hus controlee configuration task
1721         FutureTask<Integer> sessionsetHybridControleeConfigTask = new FutureTask<>(
1722                 (Callable<Integer>) () -> {
1723                     int status = UwbUciConstants.STATUS_CODE_FAILED;
1724                     synchronized (uwbSession.getWaitObj()) {
1725                         status = mNativeUwbManager.setHybridSessionControleeConfiguration(
1726                                 sessionId, numberOfPhases,
1727                                 phaseListBuffer.array(),
1728                                 uwbSession.getChipId());
1729                     }
1730                     return status;
1731                 }
1732         );
1733 
1734         // execute task
1735         int status = UwbUciConstants.STATUS_CODE_FAILED;
1736         try {
1737             status = mUwbInjector.runTaskOnSingleThreadExecutor(
1738                 sessionsetHybridControleeConfigTask,
1739                     IUwbAdapter.SESSION_CONFIGURATION_THRESHOLD_MS);
1740         } catch (TimeoutException e) {
1741             Log.e(TAG, "Failed to set session hybrid controlee config : TIMEOUT");
1742             mSessionNotificationManager.onHybridSessionControleeConfigurationFailed(
1743                     uwbSession, status);
1744         } catch (InterruptedException | ExecutionException e) {
1745             Log.e(TAG, "Exception while executing task " + e);
1746         }
1747 
1748         if (UwbUciConstants.STATUS_CODE_OK == status) {
1749             mSessionNotificationManager.onHybridSessionControleeConfigured(uwbSession,
1750                     status);
1751         } else {
1752             Log.e(TAG, "Failed to configure controlee hybrid session - status : " + status);
1753             mSessionNotificationManager.onHybridSessionControleeConfigurationFailed(uwbSession,
1754                     status);
1755         }
1756     }
1757 
handleSetDataTransferPhaseConfig(UpdateSessionInfo info)1758     private void handleSetDataTransferPhaseConfig(UpdateSessionInfo info) {
1759         SessionHandle sessionHandle = info.sessionHandle;
1760         Integer sessionId = getSessionId(sessionHandle);
1761         UwbSession uwbSession = getUwbSession(sessionHandle);
1762 
1763         int sessionType = uwbSession.getSessionType();
1764         int deviceType = uwbSession.getDeviceType();
1765         int sessionState = uwbSession.getSessionState();
1766         if (UwbUciConstants.DEVICE_TYPE_CONTROLLER != deviceType
1767                 || (sessionType != FiraParams.SESSION_TYPE_DATA_TRANSFER
1768                         && sessionType !=  FiraParams.SESSION_TYPE_IN_BAND_DATA_PHASE)
1769                 || (sessionState != UwbUciConstants.UWB_SESSION_STATE_IDLE
1770                         && sessionState != UwbUciConstants.UWB_SESSION_STATE_ACTIVE)) {
1771             Log.e(TAG, "SetDataTransferPhaseConfig failed: session type:" + sessionType
1772                     + " device type:" + deviceType + " sessionState:" + sessionState);
1773 
1774             mSessionNotificationManager.onDataTransferPhaseConfigFailed(uwbSession,
1775                     UwbUciConstants.STATUS_CODE_REJECTED);
1776             return;
1777         }
1778 
1779         FiraDataTransferPhaseConfig dataTransferPhaseConfig =
1780                 FiraDataTransferPhaseConfig.fromBundle(info.params);
1781 
1782         List<FiraDataTransferPhaseManagementList> mDataTransferPhaseManagementList =
1783                 dataTransferPhaseConfig.getDataTransferPhaseManagementList();
1784         int dataTransferManagementListSize = mDataTransferPhaseManagementList.size();
1785         int dataTransferControl = dataTransferPhaseConfig.getDataTransferControl();
1786         int slotBitmapSizeInBytes = 1 << ((dataTransferControl & 0X0F) >> 1);
1787 
1788         List<byte[]> macAddressList = new ArrayList<>();
1789         ByteBuffer slotBitmapByteBuffer = ByteBuffer.allocate(dataTransferManagementListSize
1790                 * slotBitmapSizeInBytes);
1791         slotBitmapByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
1792         ByteBuffer stopDataTransferByteBuffer = ByteBuffer.allocate(dataTransferManagementListSize);
1793 
1794         int addressByteLength = ((dataTransferControl & 0x01)
1795                        == UwbUciConstants.SHORT_MAC_ADDRESS)
1796                 ? UwbAddress.SHORT_ADDRESS_BYTE_LENGTH : UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH;
1797 
1798         for (FiraDataTransferPhaseManagementList dataTransferPhaseManagementList :
1799                 mDataTransferPhaseManagementList) {
1800             UwbAddress uwbAddress = dataTransferPhaseManagementList.getUwbAddress();
1801             byte[] slotBitMap = dataTransferPhaseManagementList.getSlotBitMap();
1802             byte stopDataTransfer = dataTransferPhaseManagementList.getStopDataTransfer();
1803 
1804             if (uwbAddress != null && uwbAddress.size() == addressByteLength
1805                     && slotBitMap.length == slotBitmapSizeInBytes) {
1806                 macAddressList.add(getComputedMacAddress(uwbAddress));
1807                 slotBitmapByteBuffer.put(slotBitMap);
1808                 stopDataTransferByteBuffer.put(stopDataTransfer);
1809             } else {
1810                 Log.e(TAG, "handleSetDataTransferPhaseConfig: slot bitmap size "
1811                             + "or address is not matching");
1812                 return;
1813             }
1814         }
1815 
1816         // Check for buffer size mismatches
1817         if (slotBitmapByteBuffer.array().length
1818                 != (slotBitmapSizeInBytes * dataTransferManagementListSize)
1819                 || macAddressList.size() != dataTransferManagementListSize) {
1820             Log.e(TAG, "handleSetDataTransferPhaseConfig: slot bitmap buffer size or address list"
1821                     + " size mismatch");
1822             return;
1823         }
1824 
1825         // create session data transfer phase configuration task
1826         FutureTask<Integer> sessionDataTransferPhaseConfigTask = new FutureTask<>(
1827                 (Callable<Integer>) () -> {
1828                     int status = UwbUciConstants.STATUS_CODE_FAILED;
1829                     synchronized (uwbSession.getWaitObj()) {
1830                         status = mNativeUwbManager.setDataTransferPhaseConfig(sessionId,
1831                                 (byte) dataTransferPhaseConfig.getDtpcmRepetition(),
1832                                 (byte) dataTransferControl,
1833                                 (byte) dataTransferManagementListSize,
1834                                 ArrayUtils.toPrimitive(macAddressList),
1835                                 slotBitmapByteBuffer.array(),
1836                                 stopDataTransferByteBuffer.array(),
1837                                 uwbSession.getChipId());
1838                     }
1839                     return status;
1840                 }
1841         );
1842 
1843         // execute task
1844         int status = UwbUciConstants.STATUS_CODE_FAILED;
1845         try {
1846             status = mUwbInjector.runTaskOnSingleThreadExecutor(sessionDataTransferPhaseConfigTask,
1847                     IUwbAdapter.SESSION_DATA_TRANSFER_PHASE_CONFIG_THRESHOLD_MS);
1848         } catch (TimeoutException e) {
1849             Log.e(TAG, "Failed to set session data transfer phase config : TIMEOUT");
1850             mSessionNotificationManager.onDataTransferPhaseConfigFailed(uwbSession, status);
1851         } catch (InterruptedException | ExecutionException e) {
1852             Log.e(TAG, "Exception while executing task " + e);
1853         }
1854 
1855         if (status != UwbUciConstants.STATUS_CODE_OK) {
1856             mSessionNotificationManager.onDataTransferPhaseConfigFailed(uwbSession, status);
1857         }
1858     }
1859 
removeSession(UwbSession uwbSession)1860     void removeSession(UwbSession uwbSession) {
1861         if (uwbSession != null) {
1862             try {
1863                 uwbSession.getBinder().unlinkToDeath(uwbSession, 0);
1864             } catch (NoSuchElementException e) {
1865                 Log.e(TAG, "unlinkToDeath fail - sessionID : " + uwbSession.getSessionId());
1866             }
1867             removeAdvertiserData(uwbSession);
1868             uwbSession.close();
1869             removeFromNonPrivilegedUidToFiraSessionTableIfNecessary(uwbSession);
1870             if (!uwbSession.isDataDeliveryPermissionCheckNeeded()) {
1871                 mUwbInjector.finishUwbRangingPermissionForDataDelivery(
1872                         uwbSession.getAttributionSource());
1873             }
1874             mSessionTokenMap.remove(uwbSession.getSessionId());
1875             mSessionTable.remove(uwbSession.getSessionHandle());
1876             mDbgRecentlyClosedSessions.add(uwbSession);
1877         }
1878     }
1879 
removeAdvertiserData(UwbSession uwbSession)1880     private void removeAdvertiserData(UwbSession uwbSession) {
1881         for (long remoteMacAddress : uwbSession.getRemoteMacAddressList()) {
1882             mAdvertiseManager.removeAdvertiseTarget(remoteMacAddress);
1883         }
1884     }
1885 
addToNonPrivilegedUidToFiraSessionTableIfNecessary(@onNull UwbSession uwbSession)1886     void addToNonPrivilegedUidToFiraSessionTableIfNecessary(@NonNull UwbSession uwbSession) {
1887         if (uwbSession.getSessionType() != UwbUciConstants.SESSION_TYPE_RANGING) {
1888             return;
1889         }
1890         synchronized (mNonPrivilegedUidToFiraSessionsTable) {
1891             AttributionSource nonPrivilegedAppAttrSource =
1892                     uwbSession.getAnyNonPrivilegedAppInAttributionSource();
1893             if (nonPrivilegedAppAttrSource != null) {
1894                 Log.d(TAG, "Detected start of non privileged FIRA session from "
1895                         + nonPrivilegedAppAttrSource);
1896                 List<UwbSession> sessions = mNonPrivilegedUidToFiraSessionsTable.computeIfAbsent(
1897                         nonPrivilegedAppAttrSource.getUid(), v -> new ArrayList<>());
1898                 sessions.add(uwbSession);
1899             }
1900         }
1901     }
1902 
removeFromNonPrivilegedUidToFiraSessionTableIfNecessary(@onNull UwbSession uwbSession)1903     void removeFromNonPrivilegedUidToFiraSessionTableIfNecessary(@NonNull UwbSession uwbSession) {
1904         if (uwbSession.getSessionType() != UwbUciConstants.SESSION_TYPE_RANGING) {
1905             return;
1906         }
1907         AttributionSource nonPrivilegedAppAttrSource =
1908                 uwbSession.getAnyNonPrivilegedAppInAttributionSource();
1909         if (nonPrivilegedAppAttrSource == null) {
1910             return;
1911         }
1912         Log.d(TAG, "Detected end of non privileged FIRA session from "
1913                 + nonPrivilegedAppAttrSource);
1914         synchronized (mNonPrivilegedUidToFiraSessionsTable) {
1915             List<UwbSession> sessions = mNonPrivilegedUidToFiraSessionsTable.get(
1916                     nonPrivilegedAppAttrSource.getUid());
1917             if (sessions == null) {
1918                 Log.wtf(TAG, "No sessions found for uid: "
1919                         + nonPrivilegedAppAttrSource.getUid());
1920                 return;
1921             }
1922             sessions.remove(uwbSession);
1923             if (sessions.isEmpty()) {
1924                 mNonPrivilegedUidToFiraSessionsTable.remove(
1925                         nonPrivilegedAppAttrSource.getUid());
1926             }
1927         }
1928     }
1929 
1930     private static class Reconfiguration {
1931         public final UwbSession mUwbSession;
1932         public final Params mParams;
1933         public final Reason mReason;
1934 
1935         /**
1936          * Reason for the reconfiguration. If mParams is an instance of {@link
1937          * FiraRangingReconfigureParams}, the reason can be interpreted as an action-specific
1938          * reason code.
1939          */
1940         public enum Reason {
1941             UNKNOWN, LOST_CONNECTION, REQUESTED_BY_API, FG_STATE_CHANGE;
1942 
1943             /**
1944              * Use this for {@link FiraParams.MULTICAST_LIST_UPDATE_ACTION_DELETE} actions.
1945              * @return the reason for controlee removal.
1946              */
asControleeRemovedReason()1947             public @FiraOnControleeAddRemoveParams.Reason int asControleeRemovedReason() {
1948                 switch (this) {
1949                     case LOST_CONNECTION:
1950                         return FiraOnControleeAddRemoveParams.Reason.LOST_CONNECTION;
1951                     case REQUESTED_BY_API:
1952                         return FiraOnControleeAddRemoveParams.Reason.REQUESTED_BY_API;
1953                     default:
1954                         return FiraOnControleeAddRemoveParams.Reason.UNKNOWN;
1955                 }
1956             }
1957         }
1958 
Reconfiguration(UwbSession uwbSession, Params params, Reason reason)1959         Reconfiguration(UwbSession uwbSession, Params params, Reason reason) {
1960             mUwbSession = uwbSession;
1961             mParams = params;
1962             mReason = reason;
1963         }
1964 
1965     }
1966 
1967     private class EventTask extends Handler {
1968 
EventTask(Looper looper)1969         EventTask(Looper looper) {
1970             super(looper);
1971         }
1972 
1973         @Override
handleMessage(Message msg)1974         public void handleMessage(Message msg) {
1975             int type = msg.what;
1976             switch (type) {
1977                 case SESSION_OPEN_RANGING: {
1978                     UwbSession uwbSession = (UwbSession) msg.obj;
1979                     handleOpenRanging(uwbSession);
1980                     break;
1981                 }
1982 
1983                 case SESSION_START_RANGING: {
1984                     UwbSession uwbSession = (UwbSession) msg.obj;
1985                     handleStartRanging(uwbSession);
1986                     break;
1987                 }
1988 
1989                 case SESSION_STOP_RANGING: {
1990                     UwbSession uwbSession = (UwbSession) msg.obj;
1991                     boolean triggeredBySystemPolicy = msg.arg1 == 1;
1992                     handleStopRanging(uwbSession, triggeredBySystemPolicy);
1993                     break;
1994                 }
1995 
1996                 case SESSION_RECONFIG_RANGING: {
1997                     Log.d(TAG, "SESSION_RECONFIG_RANGING");
1998                     Reconfiguration reconfiguration = (Reconfiguration) msg.obj;
1999                     handleReconfigure(reconfiguration.mUwbSession, reconfiguration.mParams,
2000                             reconfiguration.mReason);
2001                     break;
2002                 }
2003 
2004                 case SESSION_DEINIT: {
2005                     SessionHandle sessionHandle = (SessionHandle) msg.obj;
2006                     int reason = msg.arg1;
2007                     handleDeInitWithReason(sessionHandle, reason);
2008                     break;
2009                 }
2010 
2011                 case SESSION_ON_DEINIT: {
2012                     UwbSession uwbSession = (UwbSession) msg.obj;
2013                     handleOnDeInit(uwbSession);
2014                     break;
2015                 }
2016 
2017                 case SESSION_SEND_DATA: {
2018                     Log.d(TAG, "SESSION_SEND_DATA");
2019                     SendDataInfo info = (SendDataInfo) msg.obj;
2020                     handleSendData(info);
2021                     break;
2022                 }
2023 
2024                 case SESSION_UPDATE_DT_TAG_RANGING_ROUNDS: {
2025                     Log.d(TAG, "SESSION_UPDATE_DT_TAG_RANGING_ROUNDS");
2026                     RangingRoundsUpdateDtTagInfo info = (RangingRoundsUpdateDtTagInfo) msg.obj;
2027                     handleRangingRoundsUpdateDtTag(info);
2028                     break;
2029                 }
2030 
2031                 case SESSION_SET_HUS_CONTROLLER_CONFIG: {
2032                     Log.d(TAG, "SESSION_SET_HUS_CONTROLLER_CONFIG");
2033                     HybridSessionConfig info = (HybridSessionConfig) msg.obj;
2034                     handleSetHybridSessionControllerConfiguration(info);
2035                     break;
2036                 }
2037 
2038                 case SESSION_SET_HUS_CONTROLEE_CONFIG: {
2039                     Log.d(TAG, "SESSION_SET_HUS_CONTROLEE_CONFIG");
2040                     HybridSessionConfig info = (HybridSessionConfig) msg.obj;
2041                     handleSetHybridSessionControleeConfiguration(info);
2042                     break;
2043                 }
2044 
2045                 case SESSION_DATA_TRANSFER_PHASE_CONFIG: {
2046                     Log.d(TAG, "SESSION_DATA_TRANSFER_PHASE_CONFIG");
2047                     UpdateSessionInfo info = (UpdateSessionInfo) msg.obj;
2048                     handleSetDataTransferPhaseConfig(info);
2049                     break;
2050                 }
2051 
2052                 case SESSION_RF_TEST_CMD: {
2053                     Log.d(TAG, "SESSION_RF_TEST_CMD");
2054                     UwbSession uwbSession = (UwbSession) msg.obj;
2055                     handleRfTestCommand(uwbSession);
2056                     break;
2057                 }
2058 
2059                 case SESSION_STOP_RF_TEST_SESSION: {
2060                     Log.d(TAG, "SESSION_STOP_RF_TEST_SESSION");
2061                     UwbSession uwbSession = (UwbSession) msg.obj;
2062                     handleStopRfTest(uwbSession);
2063                     break;
2064                 }
2065 
2066                 default: {
2067                     Log.d(TAG, "EventTask : Undefined Task");
2068                     break;
2069                 }
2070             }
2071         }
2072 
execute(int task, Object obj)2073         public void execute(int task, Object obj) {
2074             Message msg = mEventTask.obtainMessage();
2075             msg.what = task;
2076             msg.obj = obj;
2077             this.sendMessage(msg);
2078         }
2079 
execute(int task, Object obj, int arg1)2080         public void execute(int task, Object obj, int arg1) {
2081             Message msg = mEventTask.obtainMessage();
2082             msg.what = task;
2083             msg.obj = obj;
2084             msg.arg1 = arg1;
2085             this.sendMessage(msg);
2086         }
2087 
handleOpenRanging(UwbSession uwbSession)2088         private void handleOpenRanging(UwbSession uwbSession) {
2089             Trace.beginSection("UWB#handleOpenRanging");
2090             // TODO(b/211445008): Consolidate to a single uwb thread.
2091             FutureTask<Integer> initSessionTask = new FutureTask<>(
2092                     () -> {
2093                         int status = UwbUciConstants.STATUS_CODE_FAILED;
2094                         synchronized (uwbSession.getWaitObj()) {
2095                             uwbSession.setOperationType(OPERATION_TYPE_INIT_SESSION);
2096                             status = mNativeUwbManager.initSession(
2097                                     uwbSession.getSessionId(),
2098                                     uwbSession.getSessionType(),
2099                                     uwbSession.getChipId());
2100                             if (status != UwbUciConstants.STATUS_CODE_OK) {
2101                                 return status;
2102                             }
2103                             mSessionTokenMap.put(uwbSession.getSessionId(), mNativeUwbManager
2104                                     .getSessionToken(uwbSession.getSessionId(),
2105                                             uwbSession.getChipId()));
2106                             uwbSession.getWaitObj().blockingWait();
2107                             status = UwbUciConstants.STATUS_CODE_FAILED;
2108                             if (uwbSession.getSessionState()
2109                                     == UwbUciConstants.UWB_SESSION_STATE_INIT) {
2110                                 uwbSession.setNeedsQueryUwbsTimestamp(
2111                                         null /* cccRangingStartParams */);
2112                                 uwbSession.setAbsoluteInitiationTimeIfNeeded();
2113                                 status = UwbSessionManager.this.setAppConfigurations(uwbSession);
2114                                 uwbSession.resetAbsoluteInitiationTime();
2115                                 if (status != UwbUciConstants.STATUS_CODE_OK) {
2116                                     return status;
2117                                 }
2118 
2119                                 uwbSession.getWaitObj().blockingWait();
2120                                 status = UwbUciConstants.STATUS_CODE_FAILED;
2121                                 if (uwbSession.getSessionState()
2122                                         == UwbUciConstants.UWB_SESSION_STATE_IDLE) {
2123                                     mSessionNotificationManager.onRangingOpened(uwbSession);
2124                                     status = UwbUciConstants.STATUS_CODE_OK;
2125                                 } else {
2126                                     status = UwbUciConstants.STATUS_CODE_FAILED;
2127                                 }
2128                                 return status;
2129                             }
2130                             return status;
2131                         }
2132                     });
2133 
2134             int status = UwbUciConstants.STATUS_CODE_FAILED;
2135             try {
2136                 status = mUwbInjector.runTaskOnSingleThreadExecutor(initSessionTask,
2137                         IUwbAdapter.RANGING_SESSION_OPEN_THRESHOLD_MS);
2138             } catch (TimeoutException e) {
2139                 Log.i(TAG, "Failed to initialize session - status : TIMEOUT");
2140             } catch (InterruptedException e) {
2141                 e.printStackTrace();
2142             } catch (ExecutionException e) {
2143                 e.printStackTrace();
2144             }
2145 
2146             mUwbMetrics.logRangingInitEvent(uwbSession, status);
2147             if (status != UwbUciConstants.STATUS_CODE_OK) {
2148                 Log.i(TAG, "Failed to initialize session - status : " + status);
2149                 mSessionNotificationManager.onRangingOpenFailed(uwbSession, status);
2150                 uwbSession.setOperationType(SESSION_ON_DEINIT);
2151                 mNativeUwbManager.deInitSession(uwbSession.getSessionId(), uwbSession.getChipId());
2152                 removeSession(uwbSession);
2153             }
2154             Log.i(TAG, "sessionInit() : finish - sessionId : " + uwbSession.getSessionId());
2155             Trace.endSection();
2156         }
2157 
handleStartRanging(UwbSession uwbSession)2158         private void handleStartRanging(UwbSession uwbSession) {
2159             Trace.beginSection("UWB#handleStartRanging");
2160             // TODO(b/211445008): Consolidate to a single uwb thread.
2161             FutureTask<Integer> startRangingTask = new FutureTask<>(
2162                     () -> {
2163                         int status = UwbUciConstants.STATUS_CODE_FAILED;
2164                         synchronized (uwbSession.getWaitObj()) {
2165                             uwbSession.setAbsoluteInitiationTimeIfNeeded();
2166                             if (uwbSession.getNeedsAppConfigUpdate()) {
2167                                 uwbSession.resetNeedsAppConfigUpdate();
2168                                 status = mConfigurationManager.setAppConfigurations(
2169                                         uwbSession.getSessionId(),
2170                                         uwbSession.getParams(), uwbSession.getChipId(),
2171                                         getUwbsFiraProtocolVersion(uwbSession.getChipId()));
2172                                 uwbSession.resetAbsoluteInitiationTime();
2173                                 if (status != UwbUciConstants.STATUS_CODE_OK) {
2174                                     mSessionNotificationManager.onRangingStartFailed(
2175                                             uwbSession, status);
2176                                     return status;
2177                                 }
2178                             }
2179 
2180                             uwbSession.setOperationType(SESSION_START_RANGING);
2181                             status = mNativeUwbManager.startRanging(uwbSession.getSessionId(),
2182                                     uwbSession.getChipId());
2183                             if (status != UwbUciConstants.STATUS_CODE_OK) {
2184                                 mSessionNotificationManager.onRangingStartFailed(
2185                                         uwbSession, status);
2186                                 return status;
2187                             }
2188                             uwbSession.getWaitObj().blockingWait();
2189                             if (uwbSession.getSessionState()
2190                                     == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
2191                                 // TODO: Ensure |rangingStartedParams| is valid for FIRA sessions
2192                                 // as well.
2193                                 Params rangingStartedParams = uwbSession.getParams();
2194 
2195                                 // For ALIRO sessions, retrieve the app configs
2196                                 if (uwbSession.getProtocolName().equals(
2197                                         AliroParams.PROTOCOL_NAME)) {
2198                                     Pair<Integer, AliroRangingStartedParams> statusAndParams  =
2199                                             mConfigurationManager.getAppConfigurations(
2200                                                     uwbSession.getSessionId(),
2201                                                     AliroParams.PROTOCOL_NAME,
2202                                                     new byte[0],
2203                                                     AliroRangingStartedParams.class,
2204                                                     uwbSession.getChipId(),
2205                                                     AliroParams.PROTOCOL_VERSION_1_0);
2206                                     if (statusAndParams.first != UwbUciConstants.STATUS_CODE_OK) {
2207                                         Log.e(TAG, "Failed to get ALIRO ranging started params");
2208                                     }
2209                                     rangingStartedParams = statusAndParams.second;
2210                                 }
2211 
2212                                 // For CCC sessions, retrieve the app configs
2213                                 if (uwbSession.getProtocolName().equals(CccParams.PROTOCOL_NAME)) {
2214                                     Pair<Integer, CccRangingStartedParams> statusAndParams  =
2215                                             mConfigurationManager.getAppConfigurations(
2216                                                     uwbSession.getSessionId(),
2217                                                     CccParams.PROTOCOL_NAME,
2218                                                     new byte[0],
2219                                                     CccRangingStartedParams.class,
2220                                                     uwbSession.getChipId(),
2221                                                     CccParams.PROTOCOL_VERSION_1_0);
2222                                     if (statusAndParams.first != UwbUciConstants.STATUS_CODE_OK) {
2223                                         Log.e(TAG, "Failed to get CCC ranging started params");
2224                                     }
2225                                     rangingStartedParams = statusAndParams.second;
2226                                 }
2227 
2228                                 mSessionNotificationManager.onRangingStarted(
2229                                         uwbSession, rangingStartedParams);
2230                                 if (uwbSession.hasNonPrivilegedApp()
2231                                         && !uwbSession.hasNonPrivilegedFgAppOrService()) {
2232                                     Log.i(TAG, "Session " + uwbSession.getSessionId()
2233                                             + " reconfiguring ntf control due to app state change");
2234                                     uwbSession.reconfigureFiraSessionOnFgStateChange();
2235                                 }
2236                             } else {
2237                                 int reasonCode = uwbSession.getLastSessionStatusNtfReasonCode();
2238                                 status =
2239                                         UwbSessionNotificationHelper.convertUciReasonCodeToUciStatusCode(
2240                                                reasonCode);
2241                                 mSessionNotificationManager.onRangingStartFailedWithUciReasonCode(
2242                                         uwbSession, reasonCode);
2243                             }
2244                         }
2245                         return status;
2246                     });
2247             int status = UwbUciConstants.STATUS_CODE_FAILED;
2248             try {
2249                 status = mUwbInjector.runTaskOnSingleThreadExecutor(startRangingTask,
2250                         IUwbAdapter.RANGING_SESSION_START_THRESHOLD_MS);
2251             } catch (TimeoutException e) {
2252                 Log.i(TAG, "Failed to Start Ranging - status : TIMEOUT");
2253                 mSessionNotificationManager.onRangingStartFailed(
2254                         uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
2255             } catch (InterruptedException e) {
2256                 e.printStackTrace();
2257             } catch (ExecutionException e) {
2258                 e.printStackTrace();
2259             }
2260             mUwbMetrics.longRangingStartEvent(uwbSession, status);
2261             Trace.endSection();
2262         }
2263 
handleStopRanging(UwbSession uwbSession, boolean triggeredBySystemPolicy)2264         private void handleStopRanging(UwbSession uwbSession, boolean triggeredBySystemPolicy) {
2265             Trace.beginSection("UWB#handleStopRanging");
2266             // TODO(b/211445008): Consolidate to a single uwb thread.
2267             FutureTask<Integer> stopRangingTask = new FutureTask<>(
2268                     () -> {
2269                         int status = UwbUciConstants.STATUS_CODE_FAILED;
2270                         synchronized (uwbSession.getWaitObj()) {
2271                             uwbSession.setOperationType(SESSION_STOP_RANGING);
2272                             status = mNativeUwbManager.stopRanging(uwbSession.getSessionId(),
2273                                     uwbSession.getChipId());
2274                             if (status != UwbUciConstants.STATUS_CODE_OK) {
2275                                 if (uwbSession.getSessionState()
2276                                         == UwbUciConstants.UWB_SESSION_STATE_IDLE) {
2277                                     handleStopRangingParams(uwbSession, true /*systemPolicy*/);
2278                                     return UwbUciConstants.STATUS_CODE_OK;
2279                                 }
2280                                 mSessionNotificationManager.onRangingStopFailed(uwbSession, status);
2281                                 return status;
2282                             }
2283                             uwbSession.getWaitObj().blockingWait();
2284                             if (uwbSession.getSessionState()
2285                                     == UwbUciConstants.UWB_SESSION_STATE_IDLE) {
2286                                 handleStopRangingParams(uwbSession, triggeredBySystemPolicy);
2287                             } else {
2288                                 status = UwbUciConstants.STATUS_CODE_FAILED;
2289                                 mSessionNotificationManager.onRangingStopFailed(uwbSession,
2290                                         status);
2291                             }
2292                         }
2293                         return status;
2294                     });
2295 
2296 
2297             int status = UwbUciConstants.STATUS_CODE_FAILED;
2298             int timeoutMs = IUwbAdapter.RANGING_SESSION_START_THRESHOLD_MS;
2299             if (uwbSession.getProtocolName().equals(PROTOCOL_NAME)) {
2300                 int minTimeoutNecessary = uwbSession.getCurrentFiraRangingIntervalMs() * 4;
2301                 timeoutMs = timeoutMs > minTimeoutNecessary ? timeoutMs : minTimeoutNecessary;
2302             }
2303             Log.v(TAG, "Stop timeout: " + timeoutMs);
2304             try {
2305                 status = mUwbInjector.runTaskOnSingleThreadExecutor(stopRangingTask, timeoutMs);
2306             } catch (TimeoutException e) {
2307                 Log.i(TAG, "Failed to Stop Ranging - status : TIMEOUT");
2308                 mSessionNotificationManager.onRangingStopFailed(
2309                         uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
2310             } catch (InterruptedException e) {
2311                 e.printStackTrace();
2312             } catch (ExecutionException e) {
2313                 e.printStackTrace();
2314             }
2315             if (status != UwbUciConstants.STATUS_CODE_FAILED) {
2316                 mUwbMetrics.longRangingStopEvent(uwbSession);
2317             }
2318             // Reset all UWB session timers when the session is stopped.
2319             uwbSession.stopTimers();
2320             removeAdvertiserData(uwbSession);
2321             Trace.endSection();
2322         }
2323 
handleStopRangingParams(UwbSession uwbSession, boolean triggeredBySystemPolicy)2324         private void handleStopRangingParams(UwbSession uwbSession,
2325                 boolean triggeredBySystemPolicy) {
2326             PersistableBundle rangingStoppedParamsBundle = new PersistableBundle();
2327 
2328             // For ALIRO sessions, retrieve the app configs
2329             if (uwbSession.getProtocolName().equals(AliroParams.PROTOCOL_NAME)
2330                     && mUwbInjector.getDeviceConfigFacade()
2331                     .isCccRangingStoppedParamsSendEnabled()) { // Use CCC Flag for ALIRO.
2332                 Pair<Integer, AliroRangingStoppedParams> statusAndParams  =
2333                         mConfigurationManager.getAppConfigurations(
2334                                 uwbSession.getSessionId(),
2335                                 AliroParams.PROTOCOL_NAME,
2336                                 new byte[0],
2337                                 AliroRangingStoppedParams.class,
2338                                 uwbSession.getChipId(),
2339                                 AliroParams.PROTOCOL_VERSION_1_0);
2340                 if (statusAndParams.first != UwbUciConstants.STATUS_CODE_OK) {
2341                     Log.e(TAG, "Failed to get ALIRO ranging stopped params");
2342                 }
2343                 rangingStoppedParamsBundle = statusAndParams.second.toBundle();
2344             }
2345 
2346             // For CCC sessions, retrieve the app configs
2347             if (uwbSession.getProtocolName().equals(CccParams.PROTOCOL_NAME)
2348                     && mUwbInjector.getDeviceConfigFacade()
2349                     .isCccRangingStoppedParamsSendEnabled()) {
2350                 Pair<Integer, CccRangingStoppedParams> statusAndParams  =
2351                         mConfigurationManager.getAppConfigurations(
2352                                 uwbSession.getSessionId(),
2353                                 CccParams.PROTOCOL_NAME,
2354                                 new byte[0],
2355                                 CccRangingStoppedParams.class,
2356                                 uwbSession.getChipId(),
2357                                 CccParams.PROTOCOL_VERSION_1_0);
2358                 if (statusAndParams.first != UwbUciConstants.STATUS_CODE_OK) {
2359                     Log.e(TAG, "Failed to get CCC ranging stopped params");
2360                 }
2361                 rangingStoppedParamsBundle = statusAndParams.second.toBundle();
2362             }
2363 
2364             int apiReasonCode = triggeredBySystemPolicy
2365                     ? RangingChangeReason.SYSTEM_POLICY
2366                     : RangingChangeReason.LOCAL_API;
2367             mSessionNotificationManager.onRangingStoppedWithApiReasonCode(
2368                     uwbSession, apiReasonCode, rangingStoppedParamsBundle);
2369         }
2370 
suspendRangingCallbacks(int suspendRangingRounds, int status, UwbSession uwbSession)2371         private void suspendRangingCallbacks(int suspendRangingRounds, int status,
2372                 UwbSession uwbSession) {
2373             if (suspendRangingRounds == FiraParams.SUSPEND_RANGING_ENABLED) {
2374                 if (status == UwbUciConstants.STATUS_CODE_OK) {
2375                     mSessionNotificationManager.onRangingPaused(uwbSession);
2376                 } else {
2377                     mSessionNotificationManager.onRangingPauseFailed(uwbSession, status);
2378                 }
2379             } else if (suspendRangingRounds == FiraParams.SUSPEND_RANGING_DISABLED) {
2380                 if (status == UwbUciConstants.STATUS_CODE_OK) {
2381                     mSessionNotificationManager.onRangingResumed(uwbSession);
2382                 } else {
2383                     mSessionNotificationManager.onRangingResumeFailed(uwbSession, status);
2384                 }
2385             }
2386         }
2387 
updateAddRemoveCallbacks(UwbSession uwbSession, UwbMulticastListUpdateStatus multicastList, Integer action, Reconfiguration.Reason reason)2388         private int updateAddRemoveCallbacks(UwbSession uwbSession,
2389                 UwbMulticastListUpdateStatus multicastList, Integer action,
2390                 Reconfiguration.Reason reason) {
2391             int actionStatus = UwbUciConstants.STATUS_CODE_OK;
2392             for (int i = 0; i < multicastList.getNumOfControlee(); i++) {
2393                 actionStatus = multicastList.getStatus()[i];
2394                 final UwbAddress address = multicastList.getControleeUwbAddresses()[i];
2395                 if (actionStatus == UwbUciConstants.STATUS_CODE_OK) {
2396                     if (isMulticastActionAdd(action)) {
2397                         uwbSession.addControlee(address);
2398                         mSessionNotificationManager.onControleeAdded(
2399                                 uwbSession, address);
2400                     } else if (action == MULTICAST_LIST_UPDATE_ACTION_DELETE) {
2401                         uwbSession.removeControlee(address);
2402                         mSessionNotificationManager.onControleeRemoved(uwbSession, address,
2403                                 reason.asControleeRemovedReason());
2404                     }
2405                 } else {
2406                     if (isMulticastActionAdd(action)) {
2407                         mSessionNotificationManager.onControleeAddFailed(
2408                                 uwbSession, address, actionStatus);
2409                     } else if (action == MULTICAST_LIST_UPDATE_ACTION_DELETE) {
2410                         mSessionNotificationManager.onControleeRemoveFailed(
2411                                 uwbSession, address,
2412                                 actionStatus, reason.asControleeRemovedReason());
2413                     }
2414                 }
2415             }
2416             return actionStatus;
2417         }
2418 
handleReconfigure(UwbSession uwbSession, @Nullable Params param, Reconfiguration.Reason reason)2419         private void handleReconfigure(UwbSession uwbSession, @Nullable Params param,
2420                 Reconfiguration.Reason reason) {
2421             if (!(param instanceof FiraRangingReconfigureParams
2422                     || param instanceof CccRangingReconfiguredParams)) {
2423                 Log.e(TAG, "Invalid reconfigure params: " + param);
2424                 mSessionNotificationManager.onRangingReconfigureFailed(
2425                         uwbSession, UwbUciConstants.STATUS_CODE_INVALID_PARAM);
2426                 return;
2427             }
2428             Trace.beginSection("UWB#handleReconfigure");
2429 
2430             final FiraRangingReconfigureParams rangingReconfigureParams =
2431                     (param instanceof FiraRangingReconfigureParams)
2432                             ? (FiraRangingReconfigureParams) param : null;
2433             // TODO(b/211445008): Consolidate to a single uwb thread.
2434             FutureTask<Integer> cmdTask = new FutureTask<>(
2435                     () -> {
2436                         int status = UwbUciConstants.STATUS_CODE_FAILED;
2437                         int ntfStatus = UwbUciConstants.STATUS_CODE_OK;
2438                         synchronized (uwbSession.getWaitObj()) {
2439                             // Handle SESSION_UPDATE_CONTROLLER_MULTICAST_LIST_CMD
2440                             UwbAddress[] addrList = null;
2441                             Integer action = null;
2442 
2443                             if (rangingReconfigureParams != null) {
2444                                 addrList = rangingReconfigureParams.getAddressList();
2445                                 action = rangingReconfigureParams.getAction();
2446                             }
2447                             uwbSession.setOperationType(SESSION_RECONFIG_RANGING);
2448                             // Action will indicate if this is a controlee add/remove.
2449                             //  if null, it's a session configuration change.
2450                             if (action != null) {
2451                                 if (addrList == null) {
2452                                     Log.e(TAG,
2453                                             "Multicast update missing the address list.");
2454                                     return status;
2455                                 }
2456                                 int dstAddressListSize = addrList.length;
2457                                 List<byte[]> dstAddressList = new ArrayList<>();
2458                                 for (UwbAddress address : addrList) {
2459                                     dstAddressList.add(getComputedMacAddress(address));
2460                                 }
2461                                 int[] subSessionIdList;
2462                                 if (!ArrayUtils.isEmpty(
2463                                         rangingReconfigureParams.getSubSessionIdList())) {
2464                                     subSessionIdList =
2465                                         rangingReconfigureParams.getSubSessionIdList();
2466                                 } else {
2467                                     // Set to 0's for the UCI stack.
2468                                     subSessionIdList = new int[dstAddressListSize];
2469                                 }
2470                                 boolean isV2 = action
2471                                         == P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_16_BYTE
2472                                         || action
2473                                         == P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_32_BYTE;
2474                                 UwbMulticastListUpdateStatus multicastListUpdateStatus =
2475                                         mNativeUwbManager.controllerMulticastListUpdate(
2476                                         uwbSession.getSessionId(),
2477                                         action,
2478                                         subSessionIdList.length,
2479                                         ArrayUtils.toPrimitive(dstAddressList),
2480                                         subSessionIdList,
2481                                         isV2 ? rangingReconfigureParams
2482                                                 .getSubSessionKeyList() : null,
2483                                         uwbSession.getChipId());
2484                                 status = (multicastListUpdateStatus.getNumOfControlee() == 0)
2485                                         ? UwbUciConstants.STATUS_CODE_OK :
2486                                         UwbUciConstants.STATUS_CODE_FAILED;
2487 
2488                                 if (status != UwbUciConstants.STATUS_CODE_OK) {
2489                                     Log.e(TAG, "Unable to update controller multicast list.");
2490                                     int i = 0;
2491                                     UwbAddress[] addresses =
2492                                        multicastListUpdateStatus.getControleeUwbAddresses();
2493                                     for (int st : multicastListUpdateStatus.getStatus()) {
2494                                         if (st == UwbUciConstants.STATUS_CODE_OK) {
2495                                             if (isMulticastActionAdd(action)) {
2496                                                 uwbSession.addControlee(addresses[i]);
2497                                                 mSessionNotificationManager.onControleeAdded(
2498                                                                   uwbSession, addresses[i]);
2499                                             } else if (action
2500                                                     == MULTICAST_LIST_UPDATE_ACTION_DELETE) {
2501                                                 uwbSession.removeControlee(addresses[i]);
2502                                                 mSessionNotificationManager.onControleeRemoved(
2503                                                         uwbSession, addresses[i],
2504                                                         reason.asControleeRemovedReason());
2505                                             }
2506                                         } else {
2507                                             if (isMulticastActionAdd(action)) {
2508                                                 mSessionNotificationManager.onControleeAddFailed(
2509                                                           uwbSession, addresses[i], st);
2510                                             } else if (action
2511                                                     == MULTICAST_LIST_UPDATE_ACTION_DELETE) {
2512                                                 mSessionNotificationManager.onControleeRemoveFailed(
2513                                                         uwbSession, addresses[i], st,
2514                                                                 reason.asControleeRemovedReason());
2515                                             }
2516                                             status = st;
2517                                         }
2518                                         i++;
2519                                     }
2520                                     if (getUwbsFiraProtocolVersion(uwbSession.getChipId())
2521                                                 .getMajor() < FIRA_VERSION_MAJOR_2
2522                                             || (uwbSession.getSessionState()
2523                                                 == UwbUciConstants.UWB_SESSION_STATE_IDLE)
2524                                             || (multicastListUpdateStatus.getNumOfControlee()
2525                                                 == subSessionIdList.length)) {
2526                                         return status;
2527                                     }
2528                                 }
2529                                 //Fira 2.0
2530                                 if (getUwbsFiraProtocolVersion(
2531                                         uwbSession.getChipId()).getMajor()
2532                                             >= FIRA_VERSION_MAJOR_2) {
2533                                     // Action - Add, Status - STATUS_OK
2534                                     if (isMulticastActionAdd(action)) {
2535                                         for (UwbAddress address : addrList) {
2536                                             Log.i(TAG, "address: " + address + " added");
2537                                             uwbSession.addControlee(address);
2538                                             mSessionNotificationManager.onControleeAdded(
2539                                                     uwbSession, address);
2540                                         }
2541                                     } else {
2542                                         if (uwbSession.getSessionState()
2543                                                 == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
2544                                             //wait for NTF for delete action only
2545                                             uwbSession.getWaitObj().blockingWait();
2546                                             UwbMulticastListUpdateStatus multicastList =
2547                                                 uwbSession.getMulticastListUpdateStatus();
2548 
2549                                             if (multicastList == null) {
2550                                                 Log.e(TAG, "controller multicast list is empty!");
2551                                                 return status;
2552                                             }
2553                                             ntfStatus = updateAddRemoveCallbacks(uwbSession,
2554                                                     multicastList, action, reason);
2555                                         } else {
2556                                             // Action - Delete, State - Idle, Status - STATUS_OK
2557                                             for (UwbAddress address : addrList) {
2558                                                 Log.i(TAG, "address: " + address + " removed");
2559                                                 uwbSession.removeControlee(address);
2560                                                 mSessionNotificationManager.onControleeRemoved(
2561                                                         uwbSession, address,
2562                                                         reason.asControleeRemovedReason());
2563                                             }
2564                                         }
2565                                     }
2566                                 } else {
2567                                     //Fira 1.1
2568                                     uwbSession.getWaitObj().blockingWait();
2569 
2570                                     UwbMulticastListUpdateStatus multicastList =
2571                                             uwbSession.getMulticastListUpdateStatus();
2572 
2573                                     if (multicastList == null) {
2574                                         Log.e(TAG, "Confirmed controller multicast list is "
2575                                                 + "empty!");
2576                                         return status;
2577                                     }
2578                                     status = updateAddRemoveCallbacks(uwbSession, multicastList,
2579                                             action, reason);
2580                                 }
2581                             } else {
2582                                 // setAppConfigurations only applies to config changes,
2583                                 //  not controlee list changes
2584                                 status = mConfigurationManager.setAppConfigurations(
2585                                         uwbSession.getSessionId(), param, uwbSession.getChipId(),
2586                                         getUwbsFiraProtocolVersion(uwbSession.getChipId()));
2587                                     // send suspendRangingCallbacks only on suspend ranging
2588                                     // reconfigure
2589                                 Integer suspendRangingRounds = rangingReconfigureParams
2590                                         .getSuspendRangingRounds();
2591                                 if (suspendRangingRounds != null) {
2592                                     suspendRangingCallbacks(suspendRangingRounds, status,
2593                                             uwbSession);
2594                                 }
2595                             }
2596                             if (status == UwbUciConstants.STATUS_CODE_OK
2597                                     && ntfStatus == UwbUciConstants.STATUS_CODE_OK) {
2598                                 // only call this if all controlees succeeded otherwise the
2599                                 //  fail status cause a onRangingReconfigureFailed later.
2600                                 if (reason != Reconfiguration.Reason.FG_STATE_CHANGE) {
2601                                     mSessionNotificationManager.onRangingReconfigured(uwbSession);
2602                                 }
2603                             }
2604                             Log.d(TAG, "Multicast update status: " + status);
2605                             return status;
2606                         }
2607                     });
2608             int status = UwbUciConstants.STATUS_CODE_FAILED;
2609             try {
2610                 status = mUwbInjector.runTaskOnSingleThreadExecutor(cmdTask,
2611                         IUwbAdapter.RANGING_SESSION_OPEN_THRESHOLD_MS);
2612             } catch (TimeoutException e) {
2613                 Log.i(TAG, "Failed to Reconfigure - status : TIMEOUT");
2614             } catch (InterruptedException e) {
2615                 e.printStackTrace();
2616             } catch (ExecutionException e) {
2617                 e.printStackTrace();
2618             }
2619             if (status != UwbUciConstants.STATUS_CODE_OK) {
2620                 Log.i(TAG, "Failed to Reconfigure : " + status);
2621                 if (reason != Reconfiguration.Reason.FG_STATE_CHANGE) {
2622                     mSessionNotificationManager.onRangingReconfigureFailed(uwbSession, status);
2623                 }
2624             }
2625             Trace.endSection();
2626         }
2627 
isMulticastActionAdd(Integer action)2628         private boolean isMulticastActionAdd(Integer action) {
2629             return action == MULTICAST_LIST_UPDATE_ACTION_ADD
2630                     || action == P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_16_BYTE
2631                     || action == P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_32_BYTE;
2632         }
2633 
handleDeInitWithReason(SessionHandle sessionHandle, int reason)2634         private void handleDeInitWithReason(SessionHandle sessionHandle, int reason) {
2635             Trace.beginSection("UWB#handleDeInitWithReason");
2636             UwbSession uwbSession = getUwbSession(sessionHandle);
2637             if (uwbSession == null) {
2638                 Log.w(TAG, "handleDeInitWithReason(): UWB session not found for sessionHandle: "
2639                         + sessionHandle);
2640                 return;
2641             }
2642 
2643             // TODO(b/211445008): Consolidate to a single uwb thread.
2644             FutureTask<Integer> deInitTask = new FutureTask<>(
2645                     (Callable<Integer>) () -> {
2646                         int status = UwbUciConstants.STATUS_CODE_FAILED;
2647                         synchronized (uwbSession.getWaitObj()) {
2648                             status = mNativeUwbManager.deInitSession(uwbSession.getSessionId(),
2649                                     uwbSession.getChipId());
2650                             if (status != UwbUciConstants.STATUS_CODE_OK) {
2651                                 return status;
2652                             }
2653                             uwbSession.getWaitObj().blockingWait();
2654                         }
2655                         return status;
2656                     });
2657 
2658             int status = UwbUciConstants.STATUS_CODE_FAILED;
2659             try {
2660                 status = mUwbInjector.runTaskOnSingleThreadExecutor(deInitTask,
2661                         IUwbAdapter.RANGING_SESSION_CLOSE_THRESHOLD_MS);
2662             } catch (TimeoutException e) {
2663                 Log.i(TAG, "Failed to Stop Ranging - status : TIMEOUT");
2664             } catch (InterruptedException | ExecutionException e) {
2665                 e.printStackTrace();
2666             }
2667             mUwbMetrics.logRangingCloseEvent(uwbSession, status);
2668 
2669             // Reset all UWB session timers when the session is de-initialized (ie, closed).
2670             uwbSession.stopTimers();
2671             removeSession(uwbSession);
2672 
2673             // Notify about Session closure after removing it from the SessionTable.
2674             Log.i(TAG, "onRangingClosed - status : " + status);
2675             mSessionNotificationManager.onRangingClosed(uwbSession,
2676                     status == STATUS_CODE_OK ? reason : status);
2677 
2678             Log.i(TAG, "deinit finish : status :" + status);
2679             Trace.endSection();
2680         }
2681 
handleSendData(SendDataInfo sendDataInfo)2682         private void handleSendData(SendDataInfo sendDataInfo) {
2683             int status = UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST;
2684             SessionHandle sessionHandle = sendDataInfo.sessionHandle;
2685             if (sessionHandle == null) {
2686                 Log.i(TAG, "Not present sessionHandle");
2687                 mSessionNotificationManager.onDataSendFailed(
2688                         null, sendDataInfo.remoteDeviceAddress, status, sendDataInfo.params);
2689                 return;
2690             }
2691 
2692             Integer sessionId = getSessionId(sessionHandle);
2693             if (sessionId == null) {
2694                 Log.i(TAG, "UwbSessionId not found");
2695                 mSessionNotificationManager.onDataSendFailed(
2696                         null, sendDataInfo.remoteDeviceAddress, status, sendDataInfo.params);
2697                 return;
2698             }
2699 
2700             // TODO(b/256675656): Check if there is race condition between uwbSession being
2701             // retrieved here and used below (and similar for uwbSession being stored in the
2702             //  mLooper message and being used during processing for all other message types).
2703             UwbSession uwbSession = getUwbSession(sessionId);
2704             if (uwbSession == null) {
2705                 Log.i(TAG, "UwbSession not found");
2706                 mSessionNotificationManager.onDataSendFailed(
2707                         null, sendDataInfo.remoteDeviceAddress, status, sendDataInfo.params);
2708                 return;
2709             }
2710 
2711             // TODO(b/211445008): Consolidate to a single uwb thread.
2712             FutureTask<Integer> sendDataTask = new FutureTask<>((Callable<Integer>) () -> {
2713                 int sendDataStatus = UwbUciConstants.STATUS_CODE_FAILED;
2714                 synchronized (uwbSession.getWaitObj()) {
2715                     if (!isValidUwbSessionForApplicationDataTransfer(uwbSession)) {
2716                         sendDataStatus = UwbUciConstants.STATUS_CODE_FAILED;
2717                         Log.i(TAG, "UwbSession not in active state");
2718                         mSessionNotificationManager.onDataSendFailed(
2719                                 uwbSession, sendDataInfo.remoteDeviceAddress, sendDataStatus,
2720                                 sendDataInfo.params);
2721                         return sendDataStatus;
2722                     }
2723                     if (!isValidSendDataInfo(sendDataInfo, uwbSession.getChipId())) {
2724                         sendDataStatus = UwbUciConstants.STATUS_CODE_INVALID_PARAM;
2725                         mSessionNotificationManager.onDataSendFailed(
2726                                 uwbSession, sendDataInfo.remoteDeviceAddress, sendDataStatus,
2727                                 sendDataInfo.params);
2728                         return sendDataStatus;
2729                     }
2730 
2731                     // Get the UCI sequence number for this data packet, and store it.
2732                     short sequenceNum = uwbSession.getAndIncrementDataSndSequenceNumber();
2733                     uwbSession.addSendDataInfo(sequenceNum, sendDataInfo);
2734 
2735                     sendDataStatus = mNativeUwbManager.sendData(
2736                             uwbSession.getSessionId(),
2737                             DataTypeConversionUtil.convertShortMacAddressBytesToExtended(
2738                                     sendDataInfo.remoteDeviceAddress.toBytes()),
2739                             sequenceNum, sendDataInfo.data, uwbSession.getChipId());
2740                     mUwbMetrics.logDataTx(uwbSession, sendDataStatus);
2741                     if (sendDataStatus != STATUS_CODE_OK) {
2742                         Log.e(TAG, "MSG_SESSION_SEND_DATA error status: " + sendDataStatus
2743                                 + " for data packet sessionId: " + sessionId
2744                                 + ", sequence number: " + sequenceNum);
2745                         mSessionNotificationManager.onDataSendFailed(
2746                                 uwbSession, sendDataInfo.remoteDeviceAddress, sendDataStatus,
2747                                 sendDataInfo.params);
2748                         uwbSession.removeSendDataInfo(sequenceNum);
2749                     }
2750                     return sendDataStatus;
2751                 }
2752             });
2753 
2754             status = UwbUciConstants.STATUS_CODE_FAILED;
2755             try {
2756                 status = mUwbInjector.runTaskOnSingleThreadExecutor(sendDataTask,
2757                         IUwbAdapter.RANGING_SESSION_OPEN_THRESHOLD_MS);
2758             } catch (TimeoutException e) {
2759                 Log.i(TAG, "Failed to Send data - status : TIMEOUT");
2760                 mSessionNotificationManager.onDataSendFailed(uwbSession,
2761                         sendDataInfo.remoteDeviceAddress, status, sendDataInfo.params);
2762             } catch (InterruptedException | ExecutionException e) {
2763                 e.printStackTrace();
2764             }
2765         }
2766     }
2767 
isValidUwbSessionForOwrAoaRanging(UwbSession uwbSession)2768     private boolean isValidUwbSessionForOwrAoaRanging(UwbSession uwbSession) {
2769         Params params = uwbSession.getParams();
2770         if (params instanceof FiraOpenSessionParams) {
2771             FiraOpenSessionParams firaParams = (FiraOpenSessionParams) params;
2772             if (firaParams.getRangingRoundUsage() != ROUND_USAGE_OWR_AOA_MEASUREMENT) {
2773                 Log.i(TAG, "OwR Aoa UwbSession: Invalid ranging round usage value = "
2774                         + firaParams.getRangingRoundUsage());
2775                 return false;
2776             }
2777             if (firaParams.getDeviceRole() != RANGING_DEVICE_ROLE_OBSERVER) {
2778                 Log.i(TAG, "OwR Aoa UwbSession: Invalid device role value = "
2779                         + firaParams.getDeviceRole());
2780                 return false;
2781             }
2782             return true;
2783         }
2784         return false;
2785     }
2786 
isValidUwbSessionForApplicationDataTransfer(UwbSession uwbSession)2787     private boolean isValidUwbSessionForApplicationDataTransfer(UwbSession uwbSession) {
2788         // The session state must be SESSION_STATE_ACTIVE, as that's required to transmit or receive
2789         // application data.
2790         return uwbSession != null && uwbSession.getSessionState() == UWB_SESSION_STATE_ACTIVE;
2791     }
2792 
2793     /** Returns max length of data message possible on a given chip */
getMaxMessageSize(String chipId)2794     private int getMaxMessageSize(String chipId) {
2795         GenericSpecificationParams params =
2796                 mUwbInjector.getUwbServiceCore().getCachedSpecificationParams(chipId);
2797 
2798         return (params != null && params.getFiraSpecificationParams() != null)
2799                 ? params.getFiraSpecificationParams().getMaxMessageSize()
2800                 : 0;
2801     }
2802 
isValidSendDataInfo(SendDataInfo sendDataInfo, String chipId)2803     private boolean isValidSendDataInfo(SendDataInfo sendDataInfo, String chipId) {
2804         if (sendDataInfo == null || sendDataInfo.data == null
2805                 || sendDataInfo.remoteDeviceAddress == null) {
2806             return false;
2807         }
2808 
2809         if (sendDataInfo.remoteDeviceAddress.size()
2810                 > UwbUciConstants.UWB_DEVICE_EXT_MAC_ADDRESS_LEN) {
2811             return false;
2812         }
2813 
2814         final int fixedLength = FiraParams.SESSION_HANDLE_LEN
2815                                 + UwbUciConstants.UWB_DEVICE_EXT_MAC_ADDRESS_LEN
2816                                 + FiraParams.SEQUENCE_NUMBER_LENGTH
2817                                 + FiraParams.DATA_MSG_LENGTH;
2818 
2819         int sendDataInfoLength = fixedLength + sendDataInfo.data.length;
2820         int maxMessageSize = getMaxMessageSize(chipId);
2821         if (sendDataInfoLength > maxMessageSize) {
2822             Log.e(TAG, "SendDataInfo length:" + sendDataInfoLength
2823                     + " exceeds max supported message size:" + maxMessageSize + " for chipId: "
2824                     + chipId);
2825             return false;
2826         }
2827 
2828         return true;
2829     }
2830 
getUwbsFiraProtocolVersion(String chipId)2831     protected FiraProtocolVersion getUwbsFiraProtocolVersion(String chipId) {
2832         UwbDeviceInfoResponse deviceInfo =
2833                 mUwbInjector.getUwbServiceCore().getCachedDeviceInfoResponse(chipId);
2834         if (deviceInfo != null) {
2835             return FiraProtocolVersion.fromLEShort((short) deviceInfo.mUciVersion);
2836         }
2837 
2838         // Return a (safe) backward-compatible FiraProtocolVersion if we couldn't retrieve it
2839         // from the UWBS.
2840         return FiraParams.PROTOCOL_VERSION_1_1;
2841     }
2842 
2843     /** Represents a UWB session */
2844     public class UwbSession implements IBinder.DeathRecipient, Closeable {
2845         @VisibleForTesting
2846         public static final long RANGING_RESULT_ERROR_NO_TIMEOUT = 0;
2847         private static final String RANGING_RESULT_ERROR_STREAK_TIMER_TAG =
2848                 "UwbSessionRangingResultError";
2849         private static final long NON_PRIVILEGED_BG_APP_TIMEOUT_MS = 120_000;
2850         @VisibleForTesting
2851         public static final String NON_PRIVILEGED_BG_APP_TIMER_TAG =
2852                 "UwbSessionNonPrivilegedBgAppError";
2853         @VisibleForTesting
2854         static final int ALIRO_SESSION_PRIORITY = 80;
2855         @VisibleForTesting
2856         static final int CCC_SESSION_PRIORITY = 80;
2857         @VisibleForTesting
2858         static final int SYSTEM_APP_SESSION_PRIORITY = 70;
2859         @VisibleForTesting
2860         static final int FG_SESSION_PRIORITY = 60;
2861         // Default session priority value needs to be different from other session priority buckets,
2862         // so we can detect overrides from the shell or System API.
2863         @VisibleForTesting
2864         static final int DEFAULT_SESSION_PRIORITY = 50;
2865         @VisibleForTesting
2866         static final int BG_SESSION_PRIORITY = 40;
2867 
2868         private final AttributionSource mAttributionSource;
2869         private final SessionHandle mSessionHandle;
2870         private final int mSessionId;
2871         private final byte mSessionType;
2872         private final int mRangingRoundUsage;
2873         private final IUwbRangingCallbacks mIUwbRangingCallbacks;
2874         private final String mProtocolName;
2875         private final IBinder mIBinder;
2876         private final WaitObj mWaitObj;
2877         private final AttributionSource mNonPrivilegedAppInAttributionSource;
2878         private boolean mAcquiredDefaultPose = false;
2879         private Params mParams;
2880         private int mSessionState;
2881         // Session priority as tracked by the UWB stack that changes based on the requesting
2882         // app/service bg/fg state changes. Note, it will differ from the Fira SESSION_PRIORITY
2883         // param given to UWBS if the state changed after the session became active.
2884         private int mStackSessionPriority;
2885         private boolean mSessionPriorityOverride = false;
2886         private boolean mNeedsAppConfigUpdate = false;
2887         private boolean mNeedsQueryUwbsTimestamp = false;
2888         private UwbMulticastListUpdateStatus mMulticastListUpdateStatus;
2889         private final int mProfileType;
2890 
2891         /**
2892          * Keeps track of per-controlee error streak timers for ranging sessions with multiple
2893          * controlees.
2894          */
2895         @VisibleForTesting
2896         public Map<UwbAddress, AlarmManager.OnAlarmListener>
2897                 mMulticastRangingErrorStreakTimerListeners;
2898         /**
2899          * Per-session error streak timer for all session modes except for two-way ranging.
2900          */
2901         private AlarmManager.OnAlarmListener mRangingResultErrorStreakTimerListener;
2902         private AlarmManager.OnAlarmListener mNonPrivilegedBgAppTimerListener;
2903         private int mOperationType = OPERATION_TYPE_INIT_SESSION;
2904         private final String mChipId;
2905         private boolean mHasNonPrivilegedFgAppOrService = false;
2906         private long mRangingErrorStreakTimeoutMs = RANGING_RESULT_ERROR_NO_TIMEOUT;
2907         // Use a Map<RemoteMacAddress, SortedMap<SequenceNumber, ReceivedDataInfo>> to store all
2908         // the Application payload data packets received in this (active) UWB Session.
2909         // - The outer key (RemoteMacAddress) is used to identify the Advertiser device that sends
2910         //   the data (there can be multiple advertisers in the same UWB session).
2911         // - The inner key (SequenceNumber) is used to ensure we don't store duplicate packets,
2912         //   and notify them to the higher layers in-order.
2913         // TODO(b/270068278): Change the type of SequenceNumber from Long to Integer everywhere.
2914         private final ConcurrentHashMap<Long, SortedMap<Long, ReceivedDataInfo>>
2915                 mReceivedDataInfoMap;
2916         private IPoseSource mPoseSource;
2917         // Application data repetition count
2918         private int mDataRepetitionCount;
2919         // Hybrid session
2920         private int mDeviceType;
2921         private int mScheduleMode;
2922 
2923         // Store the UCI sequence number for the next Data packet (to be sent to UWBS).
2924         private short mDataSndSequenceNumber;
2925         // Store a Map<SequenceNumber, SendDataInfo>, for every Data packet (sent to UWBS). It's
2926         // used when the corresponding DataTransferStatusNtf is received (from UWBS).
2927         private final ConcurrentHashMap<Long, SendDataInfo> mSendDataInfoMap;
2928 
2929         // Whether data delivery permission check is needed for the ranging session.
2930         private boolean mDataDeliveryPermissionCheckNeeded = true;
2931 
2932         // reasonCode from the last received SESSION_STATUS_NTF for this session.
2933         private int mLastSessionStatusNtfReasonCode = -1;
2934 
2935         /**
2936          * Acquire to synchronized changes in controlee count
2937          * Guards mControlees and mControleesPendingDisconnection
2938          */
2939         private final Object mControleeCountLock = new Object();
2940 
2941         /**
2942          * Keeps track of all controlees in the session.
2943          */
2944         public Map<UwbAddress, UwbControlee> mControlees;
2945 
2946         /** Number of controlees pending disconnection due to error streak timeout */
2947         @GuardedBy("mControleeCountLock")
2948         private final Set<UwbAddress> mControleesPendingDisconnection;
2949 
2950         // Keep track of RF Test start session params
2951         private RfTestStartSessionParams mRfTestStartSessionParams = null;
2952 
UwbSession(AttributionSource attributionSource, SessionHandle sessionHandle, int sessionId, byte sessionType, String protocolName, Params params, IUwbRangingCallbacks iUwbRangingCallbacks, String chipId)2953         UwbSession(AttributionSource attributionSource, SessionHandle sessionHandle, int sessionId,
2954                 byte sessionType, String protocolName, Params params,
2955                 IUwbRangingCallbacks iUwbRangingCallbacks, String chipId) {
2956             this.mAttributionSource = attributionSource;
2957             this.mSessionHandle = sessionHandle;
2958             this.mSessionId = sessionId;
2959             this.mSessionType = sessionType;
2960             this.mProtocolName = protocolName;
2961             this.mIUwbRangingCallbacks = iUwbRangingCallbacks;
2962             this.mIBinder = iUwbRangingCallbacks.asBinder();
2963             this.mSessionState = UwbUciConstants.UWB_SESSION_STATE_DEINIT;
2964             this.mParams = params;
2965             this.mWaitObj = new WaitObj();
2966             this.mProfileType = convertProtolNameToProfileType(protocolName);
2967             this.mChipId = chipId;
2968             this.mNonPrivilegedAppInAttributionSource =
2969                     mUwbInjector.getAnyNonPrivilegedAppInAttributionSource(mAttributionSource);
2970             this.mStackSessionPriority = calculateSessionPriority();
2971             this.mControlees = new ConcurrentHashMap<>();
2972             this.mControleesPendingDisconnection = Sets.newConcurrentHashSet();
2973 
2974             if (params instanceof FiraOpenSessionParams) {
2975                 FiraOpenSessionParams firaParams = (FiraOpenSessionParams) params;
2976 
2977                 this.mRangingRoundUsage = firaParams.getRangingRoundUsage();
2978 
2979                 // Set up pose sources before we start creating UwbControlees.
2980                 switch (firaParams.getFilterType()) {
2981                     case FILTER_TYPE_DEFAULT:
2982                         this.mPoseSource = mUwbInjector.acquirePoseSource();
2983                         this.mAcquiredDefaultPose = true;
2984                         break;
2985                     case FILTER_TYPE_APPLICATION:
2986                         this.mPoseSource = new ApplicationPoseSource();
2987                         break;
2988                 }
2989 
2990                 if (firaParams.getDestAddressList() != null) {
2991                     // Set up list of all controlees involved.
2992                     for (UwbAddress address : firaParams.getDestAddressList()) {
2993                         mControlees.put(address,
2994                                 new UwbControlee(address, createFilterEngine(), mUwbInjector));
2995                     }
2996                 }
2997                 mRangingErrorStreakTimeoutMs = firaParams
2998                         .getRangingErrorStreakTimeoutMs();
2999 
3000                 // Add stack calculated session priority to Fira open session params. The stack
3001                 // session priority might change later based on fg/bg state changes, but the
3002                 // SESSION_PRIORITY given to the UWBS on open session will stay the same since
3003                 // UWBS doesn't support reconfiguring session priority while the session is active.
3004                 // In case the session stops being active, session priority will update on next
3005                 // start ranging call.
3006                 if (firaParams.getSessionPriority() != DEFAULT_SESSION_PRIORITY) {
3007                     mSessionPriorityOverride = true;
3008                     mStackSessionPriority = firaParams.getSessionPriority();
3009                 } else {
3010                     mParams = firaParams.toBuilder().setSessionPriority(
3011                             mStackSessionPriority).build();
3012                 }
3013                 this.mDataRepetitionCount = firaParams.getDataRepetitionCount();
3014                 this.mDeviceType = firaParams.getDeviceType();
3015                 this.mScheduleMode = firaParams.getScheduledMode();
3016             } else {
3017                 this.mRangingRoundUsage = -1;
3018                 this.mDataRepetitionCount = 0;
3019                 this.mDeviceType = -1;
3020                 this.mScheduleMode = -1;
3021             }
3022 
3023             this.mReceivedDataInfoMap = new ConcurrentHashMap<>();
3024             this.mDataSndSequenceNumber = 0;
3025             this.mSendDataInfoMap = new ConcurrentHashMap<>();
3026             this.mMulticastRangingErrorStreakTimerListeners = new ConcurrentHashMap<>();
3027         }
3028 
3029         /**
3030          * Calculates the priority of the session based on the protocol type and the originating
3031          * app/service requesting the session.
3032          *
3033          * Session priority ranking order (from highest to lowest priority):
3034          *  1. Any CCC session
3035          *  2. Any System app/service
3036          *  3. Other apps/services running in Foreground
3037          *  4. Other apps/services running in Background
3038          */
calculateSessionPriority()3039         public int calculateSessionPriority() {
3040             if (mProtocolName.equals(AliroParams.PROTOCOL_NAME)) {
3041                 return ALIRO_SESSION_PRIORITY;
3042             }
3043             if (mProtocolName.equals(CccParams.PROTOCOL_NAME)) {
3044                 return CCC_SESSION_PRIORITY;
3045             }
3046             if (mNonPrivilegedAppInAttributionSource == null) {
3047                 return SYSTEM_APP_SESSION_PRIORITY;
3048             }
3049             boolean isFgAppOrService = mUwbInjector.isForegroundAppOrService(
3050                     mNonPrivilegedAppInAttributionSource.getUid(),
3051                     mNonPrivilegedAppInAttributionSource.getPackageName());
3052             if (isFgAppOrService) {
3053                 return FG_SESSION_PRIORITY;
3054             }
3055             return BG_SESSION_PRIORITY;
3056         }
3057 
3058         /**
3059          * Check the attribution source chain to check if there are any 3p apps.
3060          * @return AttributionSource of first non-system app found in the chain, null otherwise.
3061          */
3062         @Nullable
getAnyNonPrivilegedAppInAttributionSource()3063         public AttributionSource getAnyNonPrivilegedAppInAttributionSource() {
3064             return mNonPrivilegedAppInAttributionSource;
3065         }
3066 
3067         /**
3068          * Check the attribution source chain to check if there are any 3p apps.
3069          * @return true if 3p app found in attribution source chain.
3070          */
hasNonPrivilegedApp()3071         public boolean hasNonPrivilegedApp() {
3072             return mNonPrivilegedAppInAttributionSource != null;
3073         }
3074 
3075         /**
3076          * Gets the list of controlees active under this session.
3077          */
getControleeList()3078         public List<UwbControlee> getControleeList() {
3079             return new ArrayList<>(mControlees.values());
3080         }
3081 
3082         /**
3083          * Must be public for testing.
3084          * @return The list of controlee addresses that have active ranging error streak timers.
3085          */
getControleesWithOngoingRangingErrorStreak()3086         public List<UwbAddress> getControleesWithOngoingRangingErrorStreak() {
3087             return new ArrayList<>(mMulticastRangingErrorStreakTimerListeners.keySet());
3088         }
3089 
3090         /**
3091          * Store a ReceivedDataInfo for the UwbSession. If we already have stored data from the
3092          * same advertiser and with the same sequence number, this is a no-op.
3093          */
addReceivedDataInfo(ReceivedDataInfo receivedDataInfo)3094         public void addReceivedDataInfo(ReceivedDataInfo receivedDataInfo) {
3095             SortedMap<Long, ReceivedDataInfo> innerMap = mReceivedDataInfoMap.get(
3096                     receivedDataInfo.address);
3097             if (innerMap == null) {
3098                 innerMap = new TreeMap<>();
3099                 mReceivedDataInfoMap.put(receivedDataInfo.address, innerMap);
3100             }
3101 
3102             // Check if the sorted InnerMap has reached the max number of Rx packets we want to
3103             // store; if so we drop the smallest (sequence number) packet between the new received
3104             // packet and the stored packets.
3105             int maxRxPacketsToStore =
3106                     mUwbInjector.getDeviceConfigFacade().getRxDataMaxPacketsToStore();
3107             if (innerMap.size() < maxRxPacketsToStore) {
3108                 innerMap.putIfAbsent(receivedDataInfo.sequenceNum, receivedDataInfo);
3109             } else if (innerMap.size() == maxRxPacketsToStore) {
3110                 Long smallestStoredSequenceNumber = innerMap.firstKey();
3111                 if (smallestStoredSequenceNumber < receivedDataInfo.sequenceNum
3112                         && !innerMap.containsKey(receivedDataInfo.sequenceNum)) {
3113                     innerMap.remove(smallestStoredSequenceNumber);
3114                     innerMap.putIfAbsent(receivedDataInfo.sequenceNum, receivedDataInfo);
3115                 }
3116             }
3117         }
3118 
3119         /**
3120           * Return all the ReceivedDataInfo from the given remote device, in sequence number order.
3121           * This method also removes the returned packets from the Map, so the same packet will
3122           * not be returned again (in a future call).
3123           */
getAllReceivedDataInfo(long macAddress)3124         public List<ReceivedDataInfo> getAllReceivedDataInfo(long macAddress) {
3125             SortedMap<Long, ReceivedDataInfo> innerMap = mReceivedDataInfoMap.get(macAddress);
3126             if (innerMap == null) {
3127                 // No stored ReceivedDataInfo(s) for the address.
3128                 return List.of();
3129             }
3130 
3131             List<ReceivedDataInfo> receivedDataInfoList = new ArrayList<>(innerMap.values());
3132             innerMap.clear();
3133             return receivedDataInfoList;
3134         }
3135 
clearReceivedDataInfo()3136         private void clearReceivedDataInfo() {
3137             for (long macAddress : getRemoteMacAddressList()) {
3138                 SortedMap<Long, ReceivedDataInfo> innerMap = mReceivedDataInfoMap.get(macAddress);
3139                 innerMap.clear();
3140             }
3141             mReceivedDataInfoMap.clear();
3142         }
3143 
3144         /**
3145          * Get (and increment) the UCI sequence number for the next Data packet to be sent to UWBS.
3146          */
getAndIncrementDataSndSequenceNumber()3147         public short getAndIncrementDataSndSequenceNumber() {
3148             return mDataSndSequenceNumber++;
3149         }
3150 
3151         /**
3152          * Store a SendDataInfo for a UCI Data packet sent to UWBS.
3153          */
addSendDataInfo(long sequenceNumber, SendDataInfo sendDataInfo)3154         public void addSendDataInfo(long sequenceNumber, SendDataInfo sendDataInfo) {
3155             mSendDataInfoMap.put(sequenceNumber, sendDataInfo);
3156         }
3157 
3158         /**
3159          * Remove the SendDataInfo for a UCI packet from the current UWB Session.
3160          */
removeSendDataInfo(long sequenceNumber)3161         public void removeSendDataInfo(long sequenceNumber) {
3162             mSendDataInfoMap.remove(sequenceNumber);
3163         }
3164 
3165         /**
3166          * Get the SendDataInfo for a UCI packet from the current UWB Session.
3167          */
3168         @Nullable
getSendDataInfo(long sequenceNumber)3169         public SendDataInfo getSendDataInfo(long sequenceNumber) {
3170             return mSendDataInfoMap.get(sequenceNumber);
3171         }
3172 
3173         /**
3174          * Adds a Controlee to the session. This should only be called to reflect
3175          *  the state of the native UWB interface.
3176          * @param address The UWB address of the Controlee to add.
3177          */
addControlee(UwbAddress address)3178         public void addControlee(UwbAddress address) {
3179             if (mControlees.containsKey(address)) {
3180                 return;
3181             }
3182             synchronized (mControleeCountLock) {
3183                 mControlees.put(address,
3184                         new UwbControlee(address, createFilterEngine(), mUwbInjector));
3185             }
3186         }
3187 
3188         /**
3189          * Fetches a {@link UwbControlee} object by {@link UwbAddress}.
3190          * @param address The UWB address of the Controlee to find.
3191          * @return The matching {@link UwbControlee}, or null if not found.
3192          */
getControlee(UwbAddress address)3193         public UwbControlee getControlee(UwbAddress address) {
3194             if (mControlees == null || mControlees.isEmpty()) {
3195                 Log.d(TAG, "Controlee list is null or empty");
3196                 return null;
3197             }
3198             UwbControlee result = mControlees.get(address);
3199             if (result == null) {
3200                 Log.d(TAG, "Failure to find controlee " + address);
3201             }
3202             return result;
3203         }
3204 
3205         /**
3206          * Removes a Controlee from the session. This should only be called to reflect
3207          *  the state of the native UWB interface.
3208          * @param address The UWB address of the Controlee to remove.
3209          */
removeControlee(UwbAddress address)3210         public void removeControlee(UwbAddress address) {
3211             if (!mControlees.containsKey(address)) {
3212                 Log.w(TAG, "Attempted to remove controlee with address " + address
3213                         + " that is not in the session.");
3214                 return;
3215             }
3216             Log.d(TAG, "Removing controlee.");
3217             stopRangingResultErrorStreakTimerIfSet(address);
3218             mControlees.get(address).close();
3219 
3220             synchronized (mControleeCountLock) {
3221                 mControlees.remove(address);
3222                 mControleesPendingDisconnection.remove(address);
3223             }
3224         }
3225 
getAttributionSource()3226         public AttributionSource getAttributionSource() {
3227             return this.mAttributionSource;
3228         }
3229 
getSessionId()3230         public int getSessionId() {
3231             return this.mSessionId;
3232         }
3233 
getSessionType()3234         public byte getSessionType() {
3235             return this.mSessionType;
3236         }
3237 
getRangingRoundUsage()3238         public int getRangingRoundUsage() {
3239             return this.mRangingRoundUsage;
3240         }
3241 
getChipId()3242         public String getChipId() {
3243             return this.mChipId;
3244         }
3245 
getSessionHandle()3246         public SessionHandle getSessionHandle() {
3247             return this.mSessionHandle;
3248         }
3249 
getParams()3250         public Params getParams() {
3251             return this.mParams;
3252         }
3253 
getDataRepetitionCount()3254         public int getDataRepetitionCount() {
3255             return mDataRepetitionCount;
3256         }
3257 
getDeviceType()3258         public int getDeviceType() {
3259             return mDeviceType;
3260         }
3261 
getScheduledMode()3262         public int getScheduledMode() {
3263             return mScheduleMode;
3264         }
3265 
updateAliroParamsOnStart(AliroStartRangingParams rangingStartParams)3266         public void updateAliroParamsOnStart(AliroStartRangingParams rangingStartParams) {
3267             setNeedsQueryUwbsTimestamp(rangingStartParams);
3268 
3269             // Need to update the RAN multiplier and initiation time
3270             // from the AliroStartRangingParams for CCC session.
3271             AliroOpenRangingParams newParams =
3272                     new AliroOpenRangingParams.Builder((AliroOpenRangingParams) mParams)
3273                             .setRanMultiplier(rangingStartParams.getRanMultiplier())
3274                             .setInitiationTimeMs(rangingStartParams.getInitiationTimeMs())
3275                             .setAbsoluteInitiationTimeUs(rangingStartParams
3276                                     .getAbsoluteInitiationTimeUs())
3277                             .setStsIndex(rangingStartParams.getStsIndex())
3278                             .build();
3279             this.mParams = newParams;
3280             this.mNeedsAppConfigUpdate = true;
3281         }
3282 
updateCccParamsOnStart(CccStartRangingParams rangingStartParams)3283         public void updateCccParamsOnStart(CccStartRangingParams rangingStartParams) {
3284             setNeedsQueryUwbsTimestamp(rangingStartParams);
3285 
3286             // Need to update the RAN multiplier and initiation time
3287             // from the CccStartRangingParams for CCC session.
3288             CccOpenRangingParams newParams =
3289                     new CccOpenRangingParams.Builder((CccOpenRangingParams) mParams)
3290                             .setRanMultiplier(rangingStartParams.getRanMultiplier())
3291                             .setInitiationTimeMs(rangingStartParams.getInitiationTimeMs())
3292                             .setAbsoluteInitiationTimeUs(rangingStartParams
3293                                     .getAbsoluteInitiationTimeUs())
3294                             .setStsIndex(rangingStartParams.getStsIndex())
3295                             .build();
3296             this.mParams = newParams;
3297             this.mNeedsAppConfigUpdate = true;
3298         }
3299 
3300         /**
3301          * Checks if session priority of the current session changed from the initial value, if so
3302          * updates the session priority param and marks session for needed app config update.
3303          */
updateFiraParamsOnStartIfChanged()3304         public void updateFiraParamsOnStartIfChanged() {
3305             // Need to check if session priority changed and update if it did
3306             FiraOpenSessionParams firaOpenSessionParams = (FiraOpenSessionParams) mParams;
3307             if (mStackSessionPriority != firaOpenSessionParams.getSessionPriority()) {
3308                 this.mParams = ((FiraOpenSessionParams) mParams).toBuilder().setSessionPriority(
3309                         mStackSessionPriority).build();
3310                 this.mNeedsAppConfigUpdate = true;
3311             }
3312 
3313             setNeedsQueryUwbsTimestamp(null /* rangingStartParams */);
3314         }
3315 
3316         /**
3317          * Sets {@code mNeedsQueryUwbsTimestamp} to {@code true}, if the UWBS Timestamp needs to be
3318          * fetched from the UWBS controller (for computing an absolute UWB initiation time).
3319          */
setNeedsQueryUwbsTimestamp(@ullable Params startRangingParams)3320         public void setNeedsQueryUwbsTimestamp(@Nullable Params startRangingParams) {
3321             // When the UWBS supports Fira 2.0+, the application has configured a relative UWB
3322             // initation time, but not configured an absolute UWB initiation time, we must fetch
3323             // the UWBS timestamp (to compute the absolute UWB initiation time).
3324             if (getUwbsFiraProtocolVersion(mChipId).getMajor() >= 2) {
3325                 if (mParams instanceof FiraOpenSessionParams) {
3326                     FiraOpenSessionParams firaOpenSessionParams = (FiraOpenSessionParams) mParams;
3327                     if (firaOpenSessionParams.getInitiationTime() != 0
3328                             && firaOpenSessionParams.getAbsoluteInitiationTime() == 0) {
3329                         this.mNeedsQueryUwbsTimestamp = true;
3330                     }
3331                 } else if (mParams instanceof CccOpenRangingParams
3332                         && mUwbInjector.getDeviceConfigFacade()
3333                         .isCccAbsoluteUwbInitiationTimeEnabled()) {
3334                     // When CccStartRangingParams is present; we check only for it's fields,
3335                     // since its values overrides the earlier CccOpenRangingParams.
3336                     if (startRangingParams != null
3337                                 && startRangingParams instanceof CccStartRangingParams) {
3338                         CccStartRangingParams cccStartRangingParams =
3339                                 (CccStartRangingParams) startRangingParams;
3340                         if (cccStartRangingParams.getInitiationTimeMs() != 0
3341                                 && cccStartRangingParams.getAbsoluteInitiationTimeUs() == 0) {
3342                             this.mNeedsQueryUwbsTimestamp = true;
3343                         }
3344                     } else {
3345                         CccOpenRangingParams cccOpenRangingParams = (CccOpenRangingParams) mParams;
3346                         if (cccOpenRangingParams.getInitiationTimeMs() != 0
3347                                 && cccOpenRangingParams.getAbsoluteInitiationTimeUs() == 0) {
3348                             this.mNeedsQueryUwbsTimestamp = true;
3349                         }
3350                     }
3351                 } else if (mParams instanceof AliroOpenRangingParams
3352                         && mUwbInjector.getDeviceConfigFacade()
3353                         .isCccAbsoluteUwbInitiationTimeEnabled()) { // Re-use CCC flag for ALIRO
3354                     // When AliroStartRangingParams is present; we check only for it's fields,
3355                     // since its values overrides the earlier AliroOpenRangingParams.
3356                     if (startRangingParams != null
3357                                 && startRangingParams instanceof AliroStartRangingParams) {
3358                         AliroStartRangingParams aliroStartRangingParams =
3359                                 (AliroStartRangingParams) startRangingParams;
3360                         if (aliroStartRangingParams.getInitiationTimeMs() != 0
3361                                 && aliroStartRangingParams.getAbsoluteInitiationTimeUs() == 0) {
3362                             this.mNeedsQueryUwbsTimestamp = true;
3363                         }
3364                     } else {
3365                         AliroOpenRangingParams aliroOpenRangingParams =
3366                                 (AliroOpenRangingParams) mParams;
3367                         if (aliroOpenRangingParams.getInitiationTimeMs() != 0
3368                                 && aliroOpenRangingParams.getAbsoluteInitiationTimeUs() == 0) {
3369                             this.mNeedsQueryUwbsTimestamp = true;
3370                         }
3371                     }
3372                 }
3373             }
3374         }
3375 
3376         /**
3377          * Computes an absolute UWB initiation time, if it's needed.
3378          */
setAbsoluteInitiationTimeIfNeeded()3379         public void setAbsoluteInitiationTimeIfNeeded() {
3380             if (this.mNeedsQueryUwbsTimestamp) {
3381                 // Query the UWBS timestamp and add the relative initiation time
3382                 // stored in the FiraOpenSessionParams, to get the absolute
3383                 // initiation time to be configured.
3384                 long uwbsTimestamp =
3385                         mUwbInjector.getUwbServiceCore().queryUwbsTimestampMicros();
3386                 computeAbsoluteInitiationTime(uwbsTimestamp);
3387             }
3388         }
3389 
3390         /**
3391          * For Fira 2.0+ controller devices, replace the reference Session's SessionID with
3392          * its SessionToken, in the SessionTimeBase AppConfig parameter.
3393          */
updateFiraParamsForSessionTimeBase(int sessionToken)3394         public void updateFiraParamsForSessionTimeBase(int sessionToken) {
3395             if (mParams instanceof FiraOpenSessionParams) {
3396                 FiraOpenSessionParams firaOpenSessionParams = (FiraOpenSessionParams) mParams;
3397                 int deviceRole = firaOpenSessionParams.getDeviceRole();
3398                 if (deviceRole == FiraParams.RANGING_DEVICE_TYPE_CONTROLLER
3399                            && UwbUtil.isBitSet(firaOpenSessionParams.getReferenceTimeBase(),
3400                            FiraParams.SESSION_TIME_BASE_REFERENCE_FEATURE_ENABLED)) {
3401                     this.mParams = ((FiraOpenSessionParams) mParams).toBuilder().setSessionTimeBase(
3402                         firaOpenSessionParams.getReferenceTimeBase(), sessionToken,
3403                         firaOpenSessionParams.getSessionOffsetInMicroSeconds())
3404                         .build();
3405                 }
3406             }
3407         }
3408 
3409        /**
3410          * Compute absolute initiation time, by doing a sum of the UWBS Timestamp (in micro-seconds)
3411          * and the relative initiation time (in milli-seconds). This method should be
3412          * called only for FiRa UCI ProtocolVersion >= 2.0 devices.
3413          */
computeAbsoluteInitiationTime(long uwbsTimestamp)3414         public void computeAbsoluteInitiationTime(long uwbsTimestamp) {
3415             if (this.mNeedsQueryUwbsTimestamp) {
3416                 if (mParams instanceof FiraOpenSessionParams) {
3417                     FiraOpenSessionParams firaOpenSessionParams = (FiraOpenSessionParams) mParams;
3418                     this.mParams = ((FiraOpenSessionParams) mParams).toBuilder()
3419                             .setAbsoluteInitiationTime(uwbsTimestamp
3420                                     + (firaOpenSessionParams.getInitiationTime() * 1000))
3421                             .build();
3422                 } else if (mParams instanceof CccOpenRangingParams) {
3423                     CccOpenRangingParams cccOpenRangingParams = (CccOpenRangingParams) mParams;
3424                     this.mParams = ((CccOpenRangingParams) mParams).toBuilder()
3425                             .setAbsoluteInitiationTimeUs(uwbsTimestamp
3426                                     + (cccOpenRangingParams.getInitiationTimeMs() * 1000))
3427                             .build();
3428                 } else if (mParams instanceof AliroOpenRangingParams) {
3429                     AliroOpenRangingParams aliroOpenRangingParams =
3430                             (AliroOpenRangingParams) mParams;
3431                     this.mParams = ((AliroOpenRangingParams) mParams).toBuilder()
3432                             .setAbsoluteInitiationTimeUs(uwbsTimestamp
3433                                     + (aliroOpenRangingParams.getInitiationTimeMs() * 1000))
3434                             .build();
3435                 }
3436                 this.mNeedsAppConfigUpdate = true;
3437             }
3438         }
3439 
3440         /**
3441          * Reset the computed absolute initiation time, only when it was computed and set by this
3442          * class (it should not be reset when it was provided by the application).
3443          */
resetAbsoluteInitiationTime()3444         public void resetAbsoluteInitiationTime() {
3445             if (this.mNeedsQueryUwbsTimestamp) {
3446                 if (mParams instanceof FiraOpenSessionParams) {
3447                     // Reset the absolute Initiation time, so that it's re-computed if start
3448                     // ranging is called in the future for this UWB session.
3449                     this.mParams = ((FiraOpenSessionParams) mParams).toBuilder()
3450                             .setAbsoluteInitiationTime(0)
3451                             .build();
3452                 } else if (mParams instanceof CccOpenRangingParams) {
3453                     this.mParams = ((CccOpenRangingParams) mParams).toBuilder()
3454                             .setAbsoluteInitiationTimeUs(0)
3455                             .build();
3456                 } else if (mParams instanceof AliroOpenRangingParams) {
3457                     this.mParams = ((AliroOpenRangingParams) mParams).toBuilder()
3458                             .setAbsoluteInitiationTimeUs(0)
3459                             .build();
3460                 }
3461                 this.mNeedsQueryUwbsTimestamp = false;
3462             }
3463         }
3464 
updateFiraParamsOnReconfigure(FiraRangingReconfigureParams reconfigureParams)3465         public void updateFiraParamsOnReconfigure(FiraRangingReconfigureParams reconfigureParams) {
3466             // Need to update the reconfigure params from the FiraRangingReconfigureParams for
3467             // FiRa session.
3468             FiraOpenSessionParams.Builder newParamsBuilder =
3469                     new FiraOpenSessionParams.Builder((FiraOpenSessionParams) mParams);
3470             if (reconfigureParams.getBlockStrideLength() != null) {
3471                 newParamsBuilder.setBlockStrideLength(reconfigureParams.getBlockStrideLength());
3472             }
3473             if (reconfigureParams.getRangeDataNtfConfig() != null) {
3474                 newParamsBuilder.setRangeDataNtfConfig(reconfigureParams.getRangeDataNtfConfig());
3475             }
3476             if (reconfigureParams.getRangeDataProximityNear() != null) {
3477                 newParamsBuilder.setRangeDataNtfProximityNear(
3478                         reconfigureParams.getRangeDataProximityNear());
3479             }
3480             if (reconfigureParams.getRangeDataProximityFar() != null) {
3481                 newParamsBuilder.setRangeDataNtfProximityFar(
3482                         reconfigureParams.getRangeDataProximityFar());
3483             }
3484             if (reconfigureParams.getRangeDataAoaAzimuthLower() != null) {
3485                 newParamsBuilder.setRangeDataNtfAoaAzimuthLower(
3486                         reconfigureParams.getRangeDataAoaAzimuthLower());
3487             }
3488             if (reconfigureParams.getRangeDataAoaAzimuthUpper() != null) {
3489                 newParamsBuilder.setRangeDataNtfAoaAzimuthUpper(
3490                         reconfigureParams.getRangeDataAoaAzimuthUpper());
3491             }
3492             if (reconfigureParams.getRangeDataAoaElevationLower() != null) {
3493                 newParamsBuilder.setRangeDataNtfAoaElevationLower(
3494                         reconfigureParams.getRangeDataAoaElevationLower());
3495             }
3496             if (reconfigureParams.getRangeDataAoaElevationUpper() != null) {
3497                 newParamsBuilder.setRangeDataNtfAoaElevationUpper(
3498                         reconfigureParams.getRangeDataAoaElevationUpper());
3499             }
3500             this.mParams = newParamsBuilder.build();
3501         }
3502 
3503         // Return the Ranging Interval (Fira 2.0: Ranging Duration) in milliseconds.
updateCccParamsOnReconfigure(CccRangingReconfiguredParams reconfigureParams)3504         public void updateCccParamsOnReconfigure(CccRangingReconfiguredParams reconfigureParams) {
3505             // Need to update the reconfigure params from the CccRangingReconfiguredParams for
3506             // Ccc session.
3507             CccOpenRangingParams.Builder newParamsBuilder =
3508                     new CccOpenRangingParams.Builder((CccOpenRangingParams) mParams);
3509             if (reconfigureParams.getRangeDataNtfConfig() != null) {
3510                 newParamsBuilder.setRangeDataNtfConfig(reconfigureParams.getRangeDataNtfConfig());
3511             }
3512             if (reconfigureParams.getRangeDataProximityNear() != null) {
3513                 newParamsBuilder.setRangeDataNtfProximityNear(
3514                         reconfigureParams.getRangeDataProximityNear());
3515             }
3516             if (reconfigureParams.getRangeDataProximityFar() != null) {
3517                 newParamsBuilder.setRangeDataNtfProximityFar(
3518                         reconfigureParams.getRangeDataProximityFar());
3519             }
3520             if (reconfigureParams.getRangeDataAoaAzimuthLower() != null) {
3521                 newParamsBuilder.setRangeDataNtfAoaAzimuthLower(
3522                         reconfigureParams.getRangeDataAoaAzimuthLower());
3523             }
3524             if (reconfigureParams.getRangeDataAoaAzimuthUpper() != null) {
3525                 newParamsBuilder.setRangeDataNtfAoaAzimuthUpper(
3526                         reconfigureParams.getRangeDataAoaAzimuthUpper());
3527             }
3528             if (reconfigureParams.getRangeDataAoaElevationLower() != null) {
3529                 newParamsBuilder.setRangeDataNtfAoaElevationLower(
3530                         reconfigureParams.getRangeDataAoaElevationLower());
3531             }
3532             if (reconfigureParams.getRangeDataAoaElevationUpper() != null) {
3533                 newParamsBuilder.setRangeDataNtfAoaElevationUpper(
3534                         reconfigureParams.getRangeDataAoaElevationUpper());
3535             }
3536             this.mParams = newParamsBuilder.build();
3537         }
3538 
getCurrentFiraRangingIntervalMs()3539         public int getCurrentFiraRangingIntervalMs() {
3540             FiraOpenSessionParams firaOpenSessionParams = (FiraOpenSessionParams) mParams;
3541             return firaOpenSessionParams.getRangingIntervalMs()
3542                     * (firaOpenSessionParams.getBlockStrideLength() + 1);
3543         }
3544 
getProtocolName()3545         public String getProtocolName() {
3546             return this.mProtocolName;
3547         }
3548 
getIUwbRangingCallbacks()3549         public IUwbRangingCallbacks getIUwbRangingCallbacks() {
3550             return this.mIUwbRangingCallbacks;
3551         }
3552 
getSessionState()3553         public int getSessionState() {
3554             return this.mSessionState;
3555         }
3556 
setSessionState(int state)3557         public void setSessionState(int state) {
3558             this.mSessionState = state;
3559         }
3560 
getRfTestStartSessionParams()3561         public RfTestStartSessionParams getRfTestStartSessionParams() {
3562             return this.mRfTestStartSessionParams;
3563         }
3564 
setRfTestStartSessionParams(RfTestStartSessionParams params)3565         public void setRfTestStartSessionParams(RfTestStartSessionParams params) {
3566             this.mRfTestStartSessionParams = params;
3567         }
3568 
getStackSessionPriority()3569         public int getStackSessionPriority() {
3570             return this.mStackSessionPriority;
3571         }
3572 
setStackSessionPriority(int priority)3573         public void setStackSessionPriority(int priority) {
3574             this.mStackSessionPriority = priority;
3575         }
3576 
getNeedsAppConfigUpdate()3577         public boolean getNeedsAppConfigUpdate() {
3578             return this.mNeedsAppConfigUpdate;
3579         }
3580 
3581         /** Reset the needsAppConfigUpdate flag to false. */
resetNeedsAppConfigUpdate()3582         public void resetNeedsAppConfigUpdate() {
3583             this.mNeedsAppConfigUpdate = false;
3584         }
3585 
getNeedsQueryUwbsTimestamp()3586         public boolean getNeedsQueryUwbsTimestamp() {
3587             return this.mNeedsQueryUwbsTimestamp;
3588         }
3589 
getRemoteMacAddressList()3590         public Set<Long> getRemoteMacAddressList() {
3591             return mReceivedDataInfoMap.keySet();
3592         }
3593 
isDataDeliveryPermissionCheckNeeded()3594         public boolean isDataDeliveryPermissionCheckNeeded() {
3595             return mDataDeliveryPermissionCheckNeeded;
3596         }
3597 
setDataDeliveryPermissionCheckNeeded(boolean permissionCheckNeeded)3598         public void setDataDeliveryPermissionCheckNeeded(boolean permissionCheckNeeded) {
3599             mDataDeliveryPermissionCheckNeeded = permissionCheckNeeded;
3600         }
setMulticastListUpdateStatus( UwbMulticastListUpdateStatus multicastListUpdateStatus)3601         public void setMulticastListUpdateStatus(
3602                 UwbMulticastListUpdateStatus multicastListUpdateStatus) {
3603             mMulticastListUpdateStatus = multicastListUpdateStatus;
3604         }
3605 
getMulticastListUpdateStatus()3606         public UwbMulticastListUpdateStatus getMulticastListUpdateStatus() {
3607             return mMulticastListUpdateStatus;
3608         }
3609 
convertProtolNameToProfileType(String protocolName)3610         private int convertProtolNameToProfileType(String protocolName) {
3611             if (protocolName.equals(FiraParams.PROTOCOL_NAME)) {
3612                 return UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__FIRA;
3613             } else if (protocolName.equals(CccParams.PROTOCOL_NAME)) {
3614                 return UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__CCC;
3615             } else if (protocolName.equals(AliroParams.PROTOCOL_NAME)) {
3616                 return UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__ALIRO;
3617             } else {
3618                 return UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__CUSTOMIZED;
3619             }
3620         }
3621 
getProfileType()3622         public int getProfileType() {
3623             return mProfileType;
3624         }
3625 
getParallelSessionCount()3626         public int getParallelSessionCount() {
3627             if (mSessionTable.containsKey(mSessionHandle)) {
3628                 return getSessionCount() - 1;
3629             }
3630             return getSessionCount();
3631         }
3632 
getBinder()3633         public IBinder getBinder() {
3634             return mIBinder;
3635         }
3636 
getWaitObj()3637         public WaitObj getWaitObj() {
3638             return mWaitObj;
3639         }
3640 
hasNonPrivilegedFgAppOrService()3641         public boolean hasNonPrivilegedFgAppOrService() {
3642             return mHasNonPrivilegedFgAppOrService;
3643         }
3644 
setHasNonPrivilegedFgAppOrService(boolean hasNonPrivilegedFgAppOrService)3645         public void setHasNonPrivilegedFgAppOrService(boolean hasNonPrivilegedFgAppOrService) {
3646             mHasNonPrivilegedFgAppOrService = hasNonPrivilegedFgAppOrService;
3647         }
3648 
3649         /**
3650          * Starts a timer to detect if the error streak is longer than
3651          * {@link UwbSession#mRangingErrorStreakTimeoutMs }. The session is ended when the alarm
3652          * triggers.
3653          */
startRangingResultErrorStreakTimerIfNotSet()3654         public void startRangingResultErrorStreakTimerIfNotSet() {
3655             // Start a timer on first failure to detect continuous failures.
3656             if (mRangingResultErrorStreakTimerListener == null) {
3657                 mRangingResultErrorStreakTimerListener = () -> {
3658                     Log.w(TAG, "Continuous errors or no ranging results detected for "
3659                             + mRangingErrorStreakTimeoutMs + " ms."
3660                             + " Stopping session");
3661                     if (getSessionState() == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
3662                         stopRangingInternal(mSessionHandle, true /* triggeredBySystemPolicy */);
3663                     } else {
3664                         Log.i(TAG, "Session is not in an active state");
3665                     }
3666                 };
3667                 Log.v(TAG, "Starting error timer for "
3668                         + mRangingErrorStreakTimeoutMs + " ms.");
3669                 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
3670                         mUwbInjector.getElapsedSinceBootMillis()
3671                                 + mRangingErrorStreakTimeoutMs,
3672                         RANGING_RESULT_ERROR_STREAK_TIMER_TAG,
3673                         mRangingResultErrorStreakTimerListener, mEventTask);
3674             }
3675         }
3676 
stopRangingResultErrorStreakTimerIfSet()3677         public void stopRangingResultErrorStreakTimerIfSet() {
3678             // Cancel error streak timer on any success.
3679             if (mRangingResultErrorStreakTimerListener != null) {
3680                 mAlarmManager.cancel(mRangingResultErrorStreakTimerListener);
3681                 mRangingResultErrorStreakTimerListener = null;
3682             }
3683         }
3684 
removeControleeDueToErrorStreakTimeout(UwbAddress address)3685         private void removeControleeDueToErrorStreakTimeout(UwbAddress address) {
3686             reconfigureInternal(mSessionHandle,
3687                     new FiraRangingReconfigureParams.Builder()
3688                             .setAction(MULTICAST_LIST_UPDATE_ACTION_DELETE)
3689                             .setAddressList(new UwbAddress[] { address })
3690                             .setSubSessionIdList(new int[] { 0 })
3691                             .build(),
3692                     Reconfiguration.Reason.LOST_CONNECTION);
3693         }
3694 
3695         /**
3696          * Same as {@link UwbSession#startRangingResultErrorStreakTimerIfNotSet()}, except
3697          * starts multiple timers on a per-controlee basis for two-way ranging sessions. The
3698          * controlee will be removed from the session when the alarm triggers. The session is ended
3699          * only when the last controlee is removed.
3700          *
3701          * @param address : Address of the controlee to associate the timer with.
3702          */
startRangingResultErrorStreakTimerIfNotSet(UwbAddress address)3703         public void startRangingResultErrorStreakTimerIfNotSet(UwbAddress address) {
3704             if (!mControlees.containsKey(address)) {
3705                 Log.w(TAG, "Attempted to start error timer for controlee " + address
3706                         + " that is not in the session.");
3707                 return;
3708             }
3709             if (mMulticastRangingErrorStreakTimerListeners.containsKey(address)) {
3710                 return;
3711             }
3712             Log.v(TAG, "Starting error timer for controlee " + address + " for "
3713                     + mRangingErrorStreakTimeoutMs + " ms.");
3714 
3715             AlarmManager.OnAlarmListener onAlarm = () -> {
3716                 Log.w(TAG, "Continuous errors or no ranging results detected from controlee "
3717                         + address + " for " + mRangingErrorStreakTimeoutMs + " ms.");
3718 
3719                 synchronized (mControleeCountLock) {
3720                     if (mControlees.size() - mControleesPendingDisconnection.size() == 1) {
3721                         Log.w(TAG, "Last controlee in session has disconnected, stopping session");
3722                         if (getSessionState() == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
3723                             stopRangingInternal(mSessionHandle, true /* triggeredBySystemPolicy */);
3724                         } else {
3725                             Log.i(TAG, "Session is not in an active state");
3726                         }
3727                     } else {
3728                         mControleesPendingDisconnection.add(address);
3729                         removeControleeDueToErrorStreakTimeout(address);
3730                     }
3731                 }
3732             };
3733 
3734             mMulticastRangingErrorStreakTimerListeners.put(address, onAlarm);
3735             mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
3736                     mUwbInjector.getElapsedSinceBootMillis() + mRangingErrorStreakTimeoutMs,
3737                     RANGING_RESULT_ERROR_STREAK_TIMER_TAG,
3738                     onAlarm,
3739                     mEventTask);
3740         }
3741 
3742         /**
3743          * Stops the timer associated with a controlee, if set.
3744          * This function will never stop the session.
3745          *
3746          * @param address : Address of the controlee whose timer to stop.
3747          */
stopRangingResultErrorStreakTimerIfSet(UwbAddress address)3748         public void stopRangingResultErrorStreakTimerIfSet(UwbAddress address) {
3749             if (!mControlees.containsKey(address)) {
3750                 Log.w(TAG, "Attempted to stop error timer for controlee " + address
3751                         + "that is not in the session");
3752                 return;
3753             }
3754             if (!mMulticastRangingErrorStreakTimerListeners.containsKey(address)) {
3755                 return;
3756             }
3757             mAlarmManager.cancel(mMulticastRangingErrorStreakTimerListeners.get(address));
3758             mMulticastRangingErrorStreakTimerListeners.remove(address);
3759         }
3760 
3761         /**
3762          * Starts a timer to detect if the app that started the UWB session is in the background
3763          * for longer than {@link UwbSession#NON_PRIVILEGED_BG_APP_TIMEOUT_MS}.
3764          */
startNonPrivilegedBgAppTimerIfNotSet()3765         private void startNonPrivilegedBgAppTimerIfNotSet() {
3766             // Start a timer when the non-privileged app goes into the background.
3767             if (mNonPrivilegedBgAppTimerListener == null) {
3768                 mNonPrivilegedBgAppTimerListener = () -> {
3769                     Log.w(TAG, "Non-privileged app in background for longer than timeout - "
3770                             + " Stopping session");
3771                     if (getSessionState() == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
3772                         stopRangingInternal(mSessionHandle, true /* triggeredBySystemPolicy */);
3773                     } else {
3774                         Log.i(TAG, "Session is not in an active state");
3775                     }
3776                 };
3777                 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
3778                         mUwbInjector.getElapsedSinceBootMillis()
3779                                 + NON_PRIVILEGED_BG_APP_TIMEOUT_MS,
3780                         NON_PRIVILEGED_BG_APP_TIMER_TAG,
3781                         mNonPrivilegedBgAppTimerListener, mEventTask);
3782             }
3783         }
3784 
stopNonPrivilegedBgAppTimerIfSet()3785         private void stopNonPrivilegedBgAppTimerIfSet() {
3786             // Stop the timer when the non-privileged app goes into the foreground.
3787             if (mNonPrivilegedBgAppTimerListener != null) {
3788                 mAlarmManager.cancel(mNonPrivilegedBgAppTimerListener);
3789                 mNonPrivilegedBgAppTimerListener = null;
3790             }
3791         }
3792 
stopTimers()3793         private void stopTimers() {
3794             // Reset any stored error streak or non-privileged background app timestamps.
3795             stopRangingResultErrorStreakTimerIfSet();
3796             for (UwbAddress address : getControleesWithOngoingRangingErrorStreak()) {
3797                 stopRangingResultErrorStreakTimerIfSet(address);
3798             }
3799             stopNonPrivilegedBgAppTimerIfSet();
3800         }
3801 
reconfigureFiraSessionOnFgStateChange()3802         public void reconfigureFiraSessionOnFgStateChange() {
3803             // Reconfigure the session to change notification control when the app transitions
3804             // from fg to bg and vice versa.
3805             FiraRangingReconfigureParams.Builder builder =
3806                     new FiraRangingReconfigureParams.Builder();
3807             // If app is in fg, use the configured ntf control, else disable.
3808             if (mHasNonPrivilegedFgAppOrService) {
3809                 FiraOpenSessionParams params = (FiraOpenSessionParams) mParams;
3810                 int rangeDataNtfConfig = params.getRangeDataNtfConfig();
3811                 builder.setRangeDataNtfConfig(rangeDataNtfConfig);
3812                 if (rangeDataNtfConfig == RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY_LEVEL_TRIG
3813                         || rangeDataNtfConfig == RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY_EDGE_TRIG) {
3814                     builder
3815                         .setRangeDataProximityNear(params.getRangeDataNtfProximityNear())
3816                         .setRangeDataProximityFar(params.getRangeDataNtfProximityFar());
3817                 }
3818             } else {
3819                 builder.setRangeDataNtfConfig(FiraParams.RANGE_DATA_NTF_CONFIG_DISABLE);
3820             }
3821             FiraRangingReconfigureParams reconfigureParams = builder.build();
3822             reconfigureInternal(mSessionHandle, reconfigureParams,
3823                     Reconfiguration.Reason.FG_STATE_CHANGE);
3824 
3825             if (!mUwbInjector.getDeviceConfigFacade().isBackgroundRangingEnabled()) {
3826                 Log.d(TAG, "reconfigureFiraSessionOnFgStateChange - System policy disallows for "
3827                         + "non fg 3p apps");
3828                 // When a non-privileged app goes into the background, start a timer (that will stop
3829                 // the ranging session). If the app goes back into the foreground, the timer will
3830                 // get reset (but any stopped UWB session will not be auto-resumed).
3831                 if (!mHasNonPrivilegedFgAppOrService) {
3832                     startNonPrivilegedBgAppTimerIfNotSet();
3833                 } else {
3834                     stopNonPrivilegedBgAppTimerIfSet();
3835                 }
3836             } else {
3837                 Log.d(TAG, "reconfigureFiraSessionOnFgStateChange - System policy allows for "
3838                         + "non fg 3p apps");
3839             }
3840         }
3841 
getOperationType()3842         public int getOperationType() {
3843             return mOperationType;
3844         }
3845 
setOperationType(int type)3846         public void setOperationType(int type) {
3847             mOperationType = type;
3848         }
3849 
getLastSessionStatusNtfReasonCode()3850         public int getLastSessionStatusNtfReasonCode() {
3851             return mLastSessionStatusNtfReasonCode;
3852         }
3853 
setLastSessionStatusNtfReasonCode(int lastSessionStatusNtfReasonCode)3854         public void setLastSessionStatusNtfReasonCode(int lastSessionStatusNtfReasonCode) {
3855             mLastSessionStatusNtfReasonCode = lastSessionStatusNtfReasonCode;
3856         }
3857 
3858         /** Creates a filter engine based on the device configuration. */
createFilterEngine()3859         public UwbFilterEngine createFilterEngine() {
3860             if (mParams instanceof FiraOpenSessionParams) {
3861                 FiraOpenSessionParams firaParams = (FiraOpenSessionParams) mParams;
3862                 if (firaParams.getFilterType() == FILTER_TYPE_NONE) {
3863                     return null; /* Bail early. App requested no engine. */
3864                 }
3865             }
3866 
3867             return mUwbInjector.createFilterEngine(mPoseSource);
3868         }
3869 
3870         /** Updates the pose information if an ApplicationPoseSource is being used. */
updatePose(FiraPoseUpdateParams updateParams)3871         public void updatePose(FiraPoseUpdateParams updateParams) {
3872             if (mPoseSource instanceof ApplicationPoseSource) {
3873                 ApplicationPoseSource aps = (ApplicationPoseSource) mPoseSource;
3874                 aps.applyPose(updateParams.getPoseInfo());
3875             } else {
3876                 throw new IllegalStateException("Session not configured for application poses.");
3877             }
3878         }
3879 
3880         @Override
binderDied()3881         public void binderDied() {
3882             Log.i(TAG, "binderDied : getSessionId is getSessionId() " + getSessionId());
3883 
3884             synchronized (UwbSessionManager.this) {
3885                 int status = mNativeUwbManager.deInitSession(getSessionId(), getChipId());
3886                 mUwbMetrics.logRangingCloseEvent(this, status);
3887                 if (status == UwbUciConstants.STATUS_CODE_OK) {
3888                     removeSession(this);
3889                     Log.i(TAG,
3890                             "binderDied : Fira/CCC/ALIRO Session counts currently are "
3891                                     + getFiraSessionCount()
3892                                     + "/" + getCccSessionCount()
3893                                     + "/" + getAliroSessionCount());
3894                 } else {
3895                     Log.e(TAG,
3896                             "binderDied : sessionDeinit Failure because of NativeSessionDeinit "
3897                                     + "Error");
3898                 }
3899             }
3900         }
3901 
3902         /**
3903          * Cleans up resources held by this object.
3904          */
close()3905         public void close() {
3906             if (this.mAcquiredDefaultPose) {
3907                 for (UwbControlee controlee : mControlees.values()) {
3908                     controlee.close();
3909                 }
3910                 synchronized (mControleeCountLock) {
3911                     mControlees.clear();
3912                     mControleesPendingDisconnection.clear();
3913                 }
3914 
3915                 this.mAcquiredDefaultPose = false;
3916                 mUwbInjector.releasePoseSource();
3917             }
3918 
3919             mSendDataInfoMap.clear();
3920             clearReceivedDataInfo();
3921         }
3922 
3923         /**
3924          * Gets the pose source for this session. This may be the default pose source provided
3925          * by UwbInjector.java when the session was created, or a specialized pose source later
3926          * requested by the application.
3927          */
getPoseSource()3928         public IPoseSource getPoseSource() {
3929             return mPoseSource;
3930         }
3931 
3932         @Override
toString()3933         public String toString() {
3934             return "UwbSession: { Session Id: " + getSessionId()
3935                     + ", Handle: " + getSessionHandle()
3936                     + ", Protocol: " + getProtocolName()
3937                     + ", State: " + getSessionState()
3938                     + ", Data Send Sequence Number: " + mDataSndSequenceNumber
3939                     + ", Params: " + getParams()
3940                     + ", AttributionSource: " + getAttributionSource()
3941                     + " }";
3942         }
3943     }
3944 
3945     // TODO: refactor the async operation flow.
3946     // Wrapper for unit test.
3947     @VisibleForTesting
3948     static class WaitObj {
WaitObj()3949         WaitObj() {
3950         }
3951 
blockingWait()3952         void blockingWait() throws InterruptedException {
3953             wait();
3954         }
3955 
blockingNotify()3956         void blockingNotify() {
3957             notify();
3958         }
3959     }
3960 
3961     /**
3962      * Dump the UWB session manager debug info
3963      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)3964     public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3965         pw.println("---- Dump of UwbSessionManager ----");
3966         pw.println("Active sessions: ");
3967         for (UwbSession uwbSession : mSessionTable.values()) {
3968             pw.println(uwbSession);
3969         }
3970         pw.println("Recently closed sessions: ");
3971         for (UwbSession uwbSession: mDbgRecentlyClosedSessions.getEntries()) {
3972             pw.println(uwbSession);
3973         }
3974         List<Integer> nonPrivilegedSessionIds =
3975                 mNonPrivilegedUidToFiraSessionsTable.entrySet()
3976                         .stream()
3977                         .map(e -> e.getValue()
3978                                 .stream()
3979                                 .map(UwbSession::getSessionId)
3980                                 .collect(Collectors.toList()))
3981                         .flatMap(Collection::stream)
3982                         .collect(Collectors.toList());
3983         pw.println("Non Privileged Fira Session Ids: " + nonPrivilegedSessionIds);
3984         pw.println("---- Dump of UwbSessionManager ----");
3985     }
3986 
getComputedMacAddress(UwbAddress address)3987     private static byte[] getComputedMacAddress(UwbAddress address) {
3988         if (!SdkLevel.isAtLeastU()) {
3989             return TlvUtil.getReverseBytes(address.toBytes());
3990         }
3991         return address.toBytes();
3992     }
3993 }
3994