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