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.MAC_ADDRESSING_MODE_EXTENDED; 21 import static com.android.server.uwb.data.UwbUciConstants.MAC_ADDRESSING_MODE_SHORT; 22 import static com.android.server.uwb.data.UwbUciConstants.RANGING_DEVICE_ROLE_OBSERVER; 23 import static com.android.server.uwb.data.UwbUciConstants.REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS; 24 import static com.android.server.uwb.data.UwbUciConstants.ROUND_USAGE_OWR_AOA_MEASUREMENT; 25 import static com.android.server.uwb.data.UwbUciConstants.STATUS_CODE_OK; 26 import static com.android.server.uwb.data.UwbUciConstants.UWB_DEVICE_EXT_MAC_ADDRESS_LEN; 27 import static com.android.server.uwb.data.UwbUciConstants.UWB_DEVICE_SHORT_MAC_ADDRESS_LEN; 28 import static com.android.server.uwb.data.UwbUciConstants.UWB_SESSION_STATE_ACTIVE; 29 import static com.android.server.uwb.util.DataTypeConversionUtil.macAddressByteArrayToLong; 30 31 import static com.google.uwb.support.fira.FiraParams.FILTER_TYPE_APPLICATION; 32 import static com.google.uwb.support.fira.FiraParams.FILTER_TYPE_DEFAULT; 33 import static com.google.uwb.support.fira.FiraParams.FILTER_TYPE_NONE; 34 import static com.google.uwb.support.fira.FiraParams.MULTICAST_LIST_UPDATE_ACTION_ADD; 35 import static com.google.uwb.support.fira.FiraParams.MULTICAST_LIST_UPDATE_ACTION_DELETE; 36 import static com.google.uwb.support.fira.FiraParams.PROTOCOL_NAME; 37 import static com.google.uwb.support.fira.FiraParams.P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_16_BYTE; 38 import static com.google.uwb.support.fira.FiraParams.P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_32_BYTE; 39 import static com.google.uwb.support.fira.FiraParams.RangeDataNtfConfigCapabilityFlag.HAS_RANGE_DATA_NTF_CONFIG_DISABLE; 40 import static com.google.uwb.support.fira.FiraParams.RangeDataNtfConfigCapabilityFlag.HAS_RANGE_DATA_NTF_CONFIG_ENABLE; 41 42 import android.annotation.NonNull; 43 import android.annotation.Nullable; 44 import android.app.ActivityManager; 45 import android.app.AlarmManager; 46 import android.content.AttributionSource; 47 import android.os.Binder; 48 import android.os.Handler; 49 import android.os.IBinder; 50 import android.os.Looper; 51 import android.os.Message; 52 import android.os.PersistableBundle; 53 import android.os.RemoteException; 54 import android.os.Trace; 55 import android.util.Log; 56 import android.util.Pair; 57 import android.uwb.IUwbAdapter; 58 import android.uwb.IUwbRangingCallbacks; 59 import android.uwb.RangingChangeReason; 60 import android.uwb.SessionHandle; 61 import android.uwb.UwbAddress; 62 63 import androidx.annotation.VisibleForTesting; 64 65 import com.android.modules.utils.build.SdkLevel; 66 import com.android.server.uwb.advertisement.UwbAdvertiseManager; 67 import com.android.server.uwb.correction.UwbFilterEngine; 68 import com.android.server.uwb.correction.pose.ApplicationPoseSource; 69 import com.android.server.uwb.correction.pose.IPoseSource; 70 import com.android.server.uwb.data.DtTagUpdateRangingRoundsStatus; 71 import com.android.server.uwb.data.UwbMulticastListUpdateStatus; 72 import com.android.server.uwb.data.UwbOwrAoaMeasurement; 73 import com.android.server.uwb.data.UwbRangingData; 74 import com.android.server.uwb.data.UwbTwoWayMeasurement; 75 import com.android.server.uwb.data.UwbUciConstants; 76 import com.android.server.uwb.jni.INativeUwbManager; 77 import com.android.server.uwb.jni.NativeUwbManager; 78 import com.android.server.uwb.params.TlvUtil; 79 import com.android.server.uwb.proto.UwbStatsLog; 80 import com.android.server.uwb.util.ArrayUtils; 81 import com.android.server.uwb.util.DataTypeConversionUtil; 82 import com.android.server.uwb.util.LruList; 83 import com.android.server.uwb.util.UwbUtil; 84 85 import com.google.uwb.support.base.Params; 86 import com.google.uwb.support.ccc.CccOpenRangingParams; 87 import com.google.uwb.support.ccc.CccParams; 88 import com.google.uwb.support.ccc.CccRangingStartedParams; 89 import com.google.uwb.support.ccc.CccSpecificationParams; 90 import com.google.uwb.support.ccc.CccStartRangingParams; 91 import com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdate; 92 import com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdateStatus; 93 import com.google.uwb.support.fira.FiraOpenSessionParams; 94 import com.google.uwb.support.fira.FiraParams; 95 import com.google.uwb.support.fira.FiraPoseUpdateParams; 96 import com.google.uwb.support.fira.FiraRangingReconfigureParams; 97 import com.google.uwb.support.fira.FiraSpecificationParams; 98 import com.google.uwb.support.generic.GenericSpecificationParams; 99 import com.google.uwb.support.oemextension.AdvertisePointedTarget; 100 import com.google.uwb.support.oemextension.SessionStatus; 101 102 import java.io.Closeable; 103 import java.io.FileDescriptor; 104 import java.io.PrintWriter; 105 import java.util.ArrayList; 106 import java.util.Collection; 107 import java.util.Collections; 108 import java.util.Comparator; 109 import java.util.EnumSet; 110 import java.util.List; 111 import java.util.Optional; 112 import java.util.Set; 113 import java.util.SortedMap; 114 import java.util.TreeMap; 115 import java.util.concurrent.Callable; 116 import java.util.concurrent.ConcurrentHashMap; 117 import java.util.concurrent.ExecutionException; 118 import java.util.concurrent.ExecutorService; 119 import java.util.concurrent.Executors; 120 import java.util.concurrent.FutureTask; 121 import java.util.concurrent.TimeUnit; 122 import java.util.concurrent.TimeoutException; 123 import java.util.stream.Collectors; 124 125 public class UwbSessionManager implements INativeUwbManager.SessionNotification, 126 ActivityManager.OnUidImportanceListener { 127 128 private static final String TAG = "UwbSessionManager"; 129 private static final byte OPERATION_TYPE_INIT_SESSION = 0; 130 131 @VisibleForTesting 132 public static final int SESSION_OPEN_RANGING = 1; 133 @VisibleForTesting 134 public static final int SESSION_START_RANGING = 2; 135 @VisibleForTesting 136 public static final int SESSION_STOP_RANGING = 3; 137 @VisibleForTesting 138 public static final int SESSION_RECONFIG_RANGING = 4; 139 @VisibleForTesting 140 public static final int SESSION_DEINIT = 5; 141 @VisibleForTesting 142 public static final int SESSION_ON_DEINIT = 6; 143 @VisibleForTesting 144 public static final int SESSION_SEND_DATA = 7; 145 @VisibleForTesting 146 public static final int SESSION_UPDATE_DT_TAG_RANGING_ROUNDS = 8; 147 148 // TODO: don't expose the internal field for testing. 149 @VisibleForTesting 150 final ConcurrentHashMap<SessionHandle, UwbSession> mSessionTable = new ConcurrentHashMap(); 151 // Used for storing recently closed sessions for debugging purposes. 152 final LruList<UwbSession> mDbgRecentlyClosedSessions = new LruList<>(5); 153 final ConcurrentHashMap<Integer, List<UwbSession>> mNonPrivilegedUidToFiraSessionsTable = 154 new ConcurrentHashMap(); 155 final ConcurrentHashMap<Integer, Integer> mSessionTokenMap = new ConcurrentHashMap<>(); 156 private final ActivityManager mActivityManager; 157 private final NativeUwbManager mNativeUwbManager; 158 private final UwbMetrics mUwbMetrics; 159 private final UwbConfigurationManager mConfigurationManager; 160 private final UwbSessionNotificationManager mSessionNotificationManager; 161 private final UwbAdvertiseManager mAdvertiseManager; 162 private final UwbInjector mUwbInjector; 163 private final AlarmManager mAlarmManager; 164 private final Looper mLooper; 165 private final EventTask mEventTask; 166 167 private Boolean mIsRangeDataNtfConfigEnableDisableSupported; 168 UwbSessionManager( UwbConfigurationManager uwbConfigurationManager, NativeUwbManager nativeUwbManager, UwbMetrics uwbMetrics, UwbAdvertiseManager uwbAdvertiseManager, UwbSessionNotificationManager uwbSessionNotificationManager, UwbInjector uwbInjector, AlarmManager alarmManager, ActivityManager activityManager, Looper serviceLooper)169 public UwbSessionManager( 170 UwbConfigurationManager uwbConfigurationManager, 171 NativeUwbManager nativeUwbManager, UwbMetrics uwbMetrics, 172 UwbAdvertiseManager uwbAdvertiseManager, 173 UwbSessionNotificationManager uwbSessionNotificationManager, 174 UwbInjector uwbInjector, AlarmManager alarmManager, ActivityManager activityManager, 175 Looper serviceLooper) { 176 mNativeUwbManager = nativeUwbManager; 177 mNativeUwbManager.setSessionListener(this); 178 mUwbMetrics = uwbMetrics; 179 mAdvertiseManager = uwbAdvertiseManager; 180 mConfigurationManager = uwbConfigurationManager; 181 mSessionNotificationManager = uwbSessionNotificationManager; 182 mUwbInjector = uwbInjector; 183 mAlarmManager = alarmManager; 184 mActivityManager = activityManager; 185 mLooper = serviceLooper; 186 mEventTask = new EventTask(serviceLooper); 187 registerUidImportanceTransitions(); 188 } 189 isRangeDataNtfConfigEnableDisableSupported()190 private boolean isRangeDataNtfConfigEnableDisableSupported() { 191 if (mIsRangeDataNtfConfigEnableDisableSupported == null) { 192 String defaultChipId = mUwbInjector.getMultichipData().getDefaultChipId(); 193 GenericSpecificationParams specificationParams = 194 mUwbInjector.getUwbServiceCore().getCachedSpecificationParams(defaultChipId); 195 if (specificationParams == null) return false; 196 EnumSet<FiraParams.RangeDataNtfConfigCapabilityFlag> supportedRangeDataNtfConfigs = 197 specificationParams.getFiraSpecificationParams() 198 .getRangeDataNtfConfigCapabilities(); 199 mIsRangeDataNtfConfigEnableDisableSupported = 200 supportedRangeDataNtfConfigs.containsAll(EnumSet.of( 201 HAS_RANGE_DATA_NTF_CONFIG_DISABLE, 202 HAS_RANGE_DATA_NTF_CONFIG_ENABLE)); 203 } 204 return mIsRangeDataNtfConfigEnableDisableSupported; 205 } 206 207 @Override onUidImportance(final int uid, final int importance)208 public void onUidImportance(final int uid, final int importance) { 209 Handler handler = new Handler(mLooper); 210 handler.post(() -> { 211 List<UwbSession> uwbSessions = mNonPrivilegedUidToFiraSessionsTable.get(uid); 212 // Not a uid in the watch list 213 if (uwbSessions == null) return; 214 boolean newModeHasNonPrivilegedFgApp = 215 UwbInjector.isForegroundAppOrServiceImportance(importance); 216 for (UwbSession uwbSession : uwbSessions) { 217 // already at correct state. 218 if (newModeHasNonPrivilegedFgApp == uwbSession.hasNonPrivilegedFgApp()) { 219 continue; 220 } 221 uwbSession.setHasNonPrivilegedFgApp(newModeHasNonPrivilegedFgApp); 222 int sessionId = uwbSession.getSessionId(); 223 Log.i(TAG, "App state change for session " + sessionId + ". IsFg: " 224 + newModeHasNonPrivilegedFgApp); 225 // Reconfigure the session based on the new fg/bg state if 226 // NtfConfigEnableDisable is supported. 227 if (isRangeDataNtfConfigEnableDisableSupported()) { 228 Log.i(TAG, "Session " + sessionId 229 + " reconfiguring ntf control due to app state change"); 230 uwbSession.reconfigureFiraSessionOnFgStateChange(); 231 } 232 // Recalculate session priority based on the new fg/bg state. 233 if (!uwbSession.mSessionPriorityOverride) { 234 int newSessionPriority = uwbSession.calculateSessionPriority(); 235 Log.i(TAG, "Session " + sessionId 236 + " recalculating session priority, new priority: " 237 + newSessionPriority); 238 uwbSession.setStackSessionPriority(newSessionPriority); 239 } 240 } 241 }); 242 } 243 244 // Detect UIDs going foreground/background registerUidImportanceTransitions()245 private void registerUidImportanceTransitions() { 246 mActivityManager.addOnUidImportanceListener( 247 UwbSessionManager.this, IMPORTANCE_FOREGROUND_SERVICE); 248 } 249 hasAllRangingResultError(@onNull UwbRangingData rangingData)250 private static boolean hasAllRangingResultError(@NonNull UwbRangingData rangingData) { 251 if (rangingData.getRangingMeasuresType() 252 == UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY) { 253 for (UwbTwoWayMeasurement measure : rangingData.getRangingTwoWayMeasures()) { 254 if (measure.isStatusCodeOk()) { 255 return false; 256 } 257 } 258 } else if (rangingData.getRangingMeasuresType() 259 == UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA) { 260 UwbOwrAoaMeasurement measure = rangingData.getRangingOwrAoaMeasure(); 261 if (measure.getRangingStatus() == UwbUciConstants.STATUS_CODE_OK) { 262 return false; 263 } 264 } 265 return true; 266 } 267 268 @Override onRangeDataNotificationReceived(UwbRangingData rangingData)269 public void onRangeDataNotificationReceived(UwbRangingData rangingData) { 270 Trace.beginSection("UWB#onRangeDataNotificationReceived"); 271 long sessionId = rangingData.getSessionId(); 272 UwbSession uwbSession = getUwbSession((int) sessionId); 273 if (uwbSession != null) { 274 // TODO: b/268065070 Include UWB logs for both filtered and unfiltered data. 275 mSessionNotificationManager.onRangingResult(uwbSession, rangingData); 276 processRangeData(rangingData, uwbSession); 277 if (mUwbInjector.getDeviceConfigFacade().isRangingErrorStreakTimerEnabled() 278 && uwbSession.mRangingErrorStreakTimeoutMs 279 != UwbSession.RANGING_RESULT_ERROR_NO_TIMEOUT) { 280 if (hasAllRangingResultError(rangingData)) { 281 uwbSession.startRangingResultErrorStreakTimerIfNotSet(); 282 } else { 283 uwbSession.stopRangingResultErrorStreakTimerIfSet(); 284 } 285 } 286 } else { 287 Log.i(TAG, "Session is not initialized or Ranging Data is Null"); 288 } 289 Trace.endSection(); 290 } 291 292 /* Notification of received data over UWB to Application*/ 293 @Override onDataReceived( long sessionId, int status, long sequenceNum, byte[] address, int sourceEndPoint, int destEndPoint, byte[] data)294 public void onDataReceived( 295 long sessionId, int status, long sequenceNum, 296 byte[] address, int sourceEndPoint, int destEndPoint, byte[] data) { 297 Log.d(TAG, "onDataReceived(): Received data packet - " 298 + "Address: " + UwbUtil.toHexString(address) 299 + ", Data: " + UwbUtil.toHexString(data) 300 + ", sessionId: " + sessionId 301 + ", status: " + status 302 + ", sequenceNum: " + sequenceNum); 303 304 UwbSession uwbSession = getUwbSession((int) sessionId); 305 if (uwbSession == null) { 306 Log.e(TAG, "onDataReceived(): Received data for unknown sessionId = " + sessionId); 307 return; 308 } 309 310 // Size of address in the UCI Packet for DATA_MESSAGE_RCV is always expected to be 8 311 // (EXTENDED_ADDRESS_BYTE_LENGTH). It can contain the MacAddress in short format however 312 // (2 LSB with MacAddress, 6 MSB zeroed out). 313 if (address.length != UWB_DEVICE_EXT_MAC_ADDRESS_LEN) { 314 Log.e(TAG, "onDataReceived(): Received data for sessionId = " + sessionId 315 + ", with unexpected MacAddress length = " + address.length); 316 return; 317 } 318 mUwbMetrics.logDataRx(uwbSession, status); 319 320 Long longAddress = macAddressByteArrayToLong(address); 321 UwbAddress uwbAddress = UwbAddress.fromBytes(address); 322 323 // When the data packet is received on a non OWR-for-AoA ranging session, send it to the 324 // higher layer. For the OWR-for-AoA ranging session, the data packet is only sent when the 325 // received SESSION_INFO_NTF indicate this Observer device is pointing to an Advertiser. 326 if (uwbSession.getRangingRoundUsage() != ROUND_USAGE_OWR_AOA_MEASUREMENT) { 327 mSessionNotificationManager.onDataReceived( 328 uwbSession, uwbAddress, new PersistableBundle(), data); 329 return; 330 } 331 332 ReceivedDataInfo info = new ReceivedDataInfo(); 333 info.sessionId = sessionId; 334 info.status = status; 335 info.sequenceNum = sequenceNum; 336 info.address = longAddress; 337 info.sourceEndPoint = sourceEndPoint; 338 info.destEndPoint = destEndPoint; 339 info.payload = data; 340 341 uwbSession.addReceivedDataInfo(info); 342 } 343 344 /* Notification of data send status */ 345 @Override onDataSendStatus( long sessionId, int dataTransferStatus, long sequenceNum)346 public void onDataSendStatus( 347 long sessionId, int dataTransferStatus, long sequenceNum) { 348 Log.d(TAG, "onDataSendStatus(): Received data send status - " 349 + ", sessionId: " + sessionId 350 + ", status: " + dataTransferStatus 351 + ", sequenceNum: " + sequenceNum); 352 353 UwbSession uwbSession = getUwbSession((int) sessionId); 354 if (uwbSession == null) { 355 Log.e(TAG, "onDataSendStatus(): Received data send status for unknown sessionId = " 356 + sessionId); 357 return; 358 } 359 360 SendDataInfo sendDataInfo = uwbSession.getSendDataInfo(sequenceNum); 361 if (sendDataInfo == null) { 362 Log.e(TAG, "onDataSendStatus(): No SendDataInfo found for data packet (sessionId = " 363 + sessionId + ", sequenceNum = " + sequenceNum + ")"); 364 return; 365 } 366 367 // A note on status - earlier spec versions had the same status value (0x1) as an error, 368 // the code is written as per recent spec versions (v2.0.0_0.0.9r0). 369 if (dataTransferStatus == UwbUciConstants.STATUS_CODE_DATA_TRANSFER_REPETITION_OK 370 || dataTransferStatus == UwbUciConstants.STATUS_CODE_DATA_TRANSFER_OK) { 371 mSessionNotificationManager.onDataSent( 372 uwbSession, sendDataInfo.remoteDeviceAddress, sendDataInfo.params); 373 } else { 374 mSessionNotificationManager.onDataSendFailed( 375 uwbSession, sendDataInfo.remoteDeviceAddress, dataTransferStatus, 376 sendDataInfo.params); 377 } 378 // TODO(b/274711916): When Data Repetition during Data Packet Tx flow is implemented 379 // change here to remove the sendDataInfo only after all the copies of the packet have 380 // been sent. 381 uwbSession.removeSendDataInfo(sequenceNum); 382 } 383 384 /** Updates pose information if the session is using an ApplicationPoseSource */ updatePose(SessionHandle sessionHandle, PersistableBundle params)385 public void updatePose(SessionHandle sessionHandle, PersistableBundle params) { 386 int sessionId = getSessionId(sessionHandle); 387 UwbSession uwbSession = getUwbSession(sessionId); 388 389 if (uwbSession == null) { 390 // Session doesn't exist yet/anymore. 391 return; 392 } 393 394 uwbSession.updatePose(FiraPoseUpdateParams.fromBundle(params)); 395 } 396 397 @VisibleForTesting 398 static final class ReceivedDataInfo { 399 public long sessionId; 400 public int status; 401 public long sequenceNum; 402 public long address; 403 public int sourceEndPoint; 404 public int destEndPoint; 405 public byte[] payload; 406 } 407 408 @Override onMulticastListUpdateNotificationReceived( UwbMulticastListUpdateStatus multicastListUpdateStatus)409 public void onMulticastListUpdateNotificationReceived( 410 UwbMulticastListUpdateStatus multicastListUpdateStatus) { 411 Log.d(TAG, "onMulticastListUpdateNotificationReceived"); 412 UwbSession uwbSession = getUwbSession((int) multicastListUpdateStatus.getSessionId()); 413 if (uwbSession == null) { 414 Log.d(TAG, "onMulticastListUpdateNotificationReceived - invalid session"); 415 return; 416 } 417 uwbSession.setMulticastListUpdateStatus(multicastListUpdateStatus); 418 synchronized (uwbSession.getWaitObj()) { 419 uwbSession.getWaitObj().blockingNotify(); 420 } 421 } 422 423 @Override onSessionStatusNotificationReceived(long sessionId, int state, int reasonCode)424 public void onSessionStatusNotificationReceived(long sessionId, int state, int reasonCode) { 425 Log.i(TAG, "onSessionStatusNotificationReceived - Session ID : " + sessionId + ", state : " 426 + UwbSessionNotificationHelper.getSessionStateString(state) 427 + ", reasonCode:" + reasonCode); 428 UwbSession uwbSession = getUwbSession((int) sessionId); 429 430 if (uwbSession == null) { 431 Log.d(TAG, "onSessionStatusNotificationReceived - invalid session"); 432 return; 433 } 434 if (mUwbInjector.getUwbServiceCore().isOemExtensionCbRegistered()) { 435 PersistableBundle sessionStatusBundle = new SessionStatus.Builder() 436 .setSessionId(sessionId) 437 .setState(state) 438 .setReasonCode(reasonCode) 439 .setAppPackageName(uwbSession.getAttributionSource().getPackageName()) 440 .setSessiontoken(mSessionTokenMap.getOrDefault(uwbSession.getSessionId(), 0)) 441 .build() 442 .toBundle(); 443 try { 444 mUwbInjector.getUwbServiceCore().getOemExtensionCallback() 445 .onSessionStatusNotificationReceived(sessionStatusBundle); 446 } catch (RemoteException e) { 447 Log.e(TAG, "Failed to send vendor notification", e); 448 } 449 } 450 int prevState = uwbSession.getSessionState(); 451 setCurrentSessionState((int) sessionId, state); 452 453 if ((uwbSession.getOperationType() == SESSION_ON_DEINIT 454 && state == UwbUciConstants.UWB_SESSION_STATE_IDLE) 455 || (uwbSession.getOperationType() == SESSION_STOP_RANGING 456 && state == UwbUciConstants.UWB_SESSION_STATE_IDLE 457 && reasonCode != REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS)) { 458 Log.d(TAG, "Session status NTF is received due to in-band session state change"); 459 } else { 460 synchronized (uwbSession.getWaitObj()) { 461 uwbSession.getWaitObj().blockingNotify(); 462 } 463 } 464 465 //TODO : process only error handling in this switch function, b/218921154 466 switch (state) { 467 case UwbUciConstants.UWB_SESSION_STATE_IDLE: 468 if (prevState == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) { 469 // If session was stopped explicitly, then the onStopped() is sent from 470 // stopRanging method. 471 if (reasonCode != REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS) { 472 mSessionNotificationManager.onRangingStoppedWithUciReasonCode( 473 uwbSession, reasonCode); 474 mUwbMetrics.longRangingStopEvent(uwbSession); 475 } 476 } else if (prevState == UwbUciConstants.UWB_SESSION_STATE_IDLE) { 477 //mSessionNotificationManager.onRangingReconfigureFailed( 478 // uwbSession, reasonCode); 479 } 480 break; 481 case UwbUciConstants.UWB_SESSION_STATE_DEINIT: 482 mEventTask.execute(SESSION_ON_DEINIT, uwbSession); 483 break; 484 default: 485 break; 486 } 487 } 488 setAppConfigurations(UwbSession uwbSession)489 private int setAppConfigurations(UwbSession uwbSession) { 490 int status = mConfigurationManager.setAppConfigurations(uwbSession.getSessionId(), 491 uwbSession.getParams(), uwbSession.getChipId()); 492 if (status == UwbUciConstants.STATUS_CODE_OK 493 && mUwbInjector.getUwbServiceCore().isOemExtensionCbRegistered()) { 494 try { 495 status = mUwbInjector.getUwbServiceCore().getOemExtensionCallback() 496 .onSessionConfigurationReceived(uwbSession.getParams().toBundle()); 497 } catch (RemoteException e) { 498 Log.e(TAG, "Failed to send vendor notification", e); 499 } 500 } 501 return status; 502 } 503 initSession(AttributionSource attributionSource, SessionHandle sessionHandle, int sessionId, byte sessionType, String protocolName, Params params, IUwbRangingCallbacks rangingCallbacks, String chipId)504 public synchronized void initSession(AttributionSource attributionSource, 505 SessionHandle sessionHandle, int sessionId, byte sessionType, String protocolName, 506 Params params, IUwbRangingCallbacks rangingCallbacks, String chipId) 507 throws RemoteException { 508 Log.i(TAG, "initSession() - sessionId: " + sessionId + ", sessionHandle: " + sessionHandle 509 + ", sessionType: " + sessionType); 510 UwbSession uwbSession = createUwbSession(attributionSource, sessionHandle, sessionId, 511 sessionType, protocolName, params, rangingCallbacks, chipId); 512 // Check the attribution source chain to ensure that there are no 3p apps which are not in 513 // fg which can receive the ranging results. 514 AttributionSource nonPrivilegedAppAttrSource = 515 uwbSession.getAnyNonPrivilegedAppInAttributionSource(); 516 if (nonPrivilegedAppAttrSource != null) { 517 Log.d(TAG, "Found a 3p app/service in the attribution source of request: " 518 + nonPrivilegedAppAttrSource); 519 // TODO(b/211445008): Move this operation to uwb thread. 520 long identity = Binder.clearCallingIdentity(); 521 boolean hasNonPrivilegedFgApp = mUwbInjector.isForegroundAppOrService( 522 nonPrivilegedAppAttrSource.getUid(), 523 nonPrivilegedAppAttrSource.getPackageName()); 524 Binder.restoreCallingIdentity(identity); 525 uwbSession.setHasNonPrivilegedFgApp(hasNonPrivilegedFgApp); 526 if (!hasNonPrivilegedFgApp) { 527 if (!mUwbInjector.getDeviceConfigFacade().isBackgroundRangingEnabled()) { 528 Log.e(TAG, "openRanging - System policy disallows for non fg 3p apps"); 529 rangingCallbacks.onRangingOpenFailed(sessionHandle, 530 RangingChangeReason.SYSTEM_POLICY, new PersistableBundle()); 531 return; 532 } else { 533 Log.d(TAG, "openRanging - System policy allows for non fg 3p apps"); 534 } 535 } 536 } 537 if (isExistedSession(sessionHandle) || isExistedSession(sessionId)) { 538 Log.i(TAG, "Duplicated session. SessionHandle: " + sessionHandle + ", SessionId: " 539 + sessionId); 540 rangingCallbacks.onRangingOpenFailed(sessionHandle, RangingChangeReason.BAD_PARAMETERS, 541 UwbSessionNotificationHelper.convertUciStatusToParam(protocolName, 542 UwbUciConstants.STATUS_CODE_ERROR_SESSION_DUPLICATE)); 543 mUwbMetrics.logRangingInitEvent(uwbSession, 544 UwbUciConstants.STATUS_CODE_ERROR_SESSION_DUPLICATE); 545 return; 546 } 547 548 boolean maxSessionsExceeded = false; 549 // TODO: getCccSessionCount and getFiraSessionCount should be chip specific 550 if (protocolName.equals(CccParams.PROTOCOL_NAME) 551 && getCccSessionCount() >= getMaxCccSessionsNumber(chipId)) { 552 Log.i(TAG, "Max CCC Sessions Exceeded"); 553 // All CCC sessions have the same priority so there's no point in trying to make space 554 // if max sessions are already reached. 555 maxSessionsExceeded = true; 556 } else if (protocolName.equals(FiraParams.PROTOCOL_NAME) 557 && getFiraSessionCount() >= getMaxFiraSessionsNumber(chipId)) { 558 Log.i(TAG, "Max Fira Sessions Exceeded"); 559 maxSessionsExceeded = !tryMakeSpaceForFiraSession( 560 uwbSession.getStackSessionPriority()); 561 } 562 if (maxSessionsExceeded) { 563 rangingCallbacks.onRangingOpenFailed(sessionHandle, 564 RangingChangeReason.MAX_SESSIONS_REACHED, 565 UwbSessionNotificationHelper.convertUciStatusToParam(protocolName, 566 UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED)); 567 mUwbMetrics.logRangingInitEvent(uwbSession, 568 UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED); 569 return; 570 } 571 572 try { 573 uwbSession.getBinder().linkToDeath(uwbSession, 0); 574 } catch (RemoteException e) { 575 uwbSession.binderDied(); 576 Log.e(TAG, "linkToDeath fail - sessionID : " + uwbSession.getSessionId()); 577 rangingCallbacks.onRangingOpenFailed(sessionHandle, RangingChangeReason.UNKNOWN, 578 UwbSessionNotificationHelper.convertUciStatusToParam(protocolName, 579 UwbUciConstants.STATUS_CODE_FAILED)); 580 mUwbMetrics.logRangingInitEvent(uwbSession, 581 UwbUciConstants.STATUS_CODE_FAILED); 582 removeSession(uwbSession); 583 return; 584 } 585 586 mSessionTable.put(sessionHandle, uwbSession); 587 addToNonPrivilegedUidToFiraSessionTableIfNecessary(uwbSession); 588 mEventTask.execute(SESSION_OPEN_RANGING, uwbSession); 589 return; 590 } 591 tryMakeSpaceForFiraSession(int priorityThreshold)592 private boolean tryMakeSpaceForFiraSession(int priorityThreshold) { 593 Optional<UwbSession> lowestPrioritySession = getSessionWithLowestPriorityByProtocol( 594 FiraParams.PROTOCOL_NAME); 595 if (!lowestPrioritySession.isPresent()) { 596 Log.w(TAG, 597 "New session blocked by max sessions exceeded, but list of sessions is " 598 + "empty"); 599 return false; 600 } 601 if (lowestPrioritySession.get().getStackSessionPriority() < priorityThreshold) { 602 return deInitDueToLowPriority(lowestPrioritySession.get().getSessionHandle()); 603 } 604 return false; 605 } 606 607 // TODO: use UwbInjector. 608 @VisibleForTesting createUwbSession(AttributionSource attributionSource, SessionHandle sessionHandle, int sessionId, byte sessionType, String protocolName, Params params, IUwbRangingCallbacks iUwbRangingCallbacks, String chipId)609 UwbSession createUwbSession(AttributionSource attributionSource, SessionHandle sessionHandle, 610 int sessionId, byte sessionType, String protocolName, Params params, 611 IUwbRangingCallbacks iUwbRangingCallbacks, String chipId) { 612 return new UwbSession(attributionSource, sessionHandle, sessionId, sessionType, 613 protocolName, params, iUwbRangingCallbacks, chipId); 614 } 615 deInitSession(SessionHandle sessionHandle)616 public synchronized void deInitSession(SessionHandle sessionHandle) { 617 if (!isExistedSession(sessionHandle)) { 618 Log.i(TAG, "Not initialized session ID"); 619 return; 620 } 621 622 int sessionId = getSessionId(sessionHandle); 623 Log.i(TAG, "deinitSession() - sessionId: " + sessionId 624 + ", sessionHandle: " + sessionHandle); 625 UwbSession uwbSession = getUwbSession(sessionId); 626 mEventTask.execute(SESSION_DEINIT, uwbSession, STATUS_CODE_OK); 627 return; 628 } 629 630 /** 631 * Logs and executes session de-init task with low priority being sent as the reason in 632 * ranging closed callback. 633 */ deInitDueToLowPriority(SessionHandle sessionHandle)634 private synchronized boolean deInitDueToLowPriority(SessionHandle sessionHandle) { 635 int sessionId = getSessionId(sessionHandle); 636 if (!isExistedSession(sessionHandle)) { 637 Log.w(TAG, "Session " + sessionId + " expected to exist but not found. " 638 + "Failed to de-initialize low priority session."); 639 return false; 640 } 641 642 Log.i(TAG, "deInitDueToLowPriority() - sessionId: " + sessionId 643 + ", sessionHandle: " + sessionHandle); 644 UwbSession uwbSession = getUwbSession(sessionId); 645 mEventTask.execute(SESSION_DEINIT, uwbSession, 646 UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED); 647 return true; 648 } 649 startRanging(SessionHandle sessionHandle, @Nullable Params params)650 public synchronized void startRanging(SessionHandle sessionHandle, @Nullable Params params) { 651 if (!isExistedSession(sessionHandle)) { 652 Log.i(TAG, "Not initialized session ID"); 653 return; 654 } 655 656 int sessionId = getSessionId(sessionHandle); 657 Log.i(TAG, "startRanging() - sessionId: " + sessionId 658 + ", sessionHandle: " + sessionHandle); 659 660 UwbSession uwbSession = getUwbSession(sessionId); 661 662 int currentSessionState = getCurrentSessionState(sessionId); 663 if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_IDLE) { 664 if (uwbSession.getProtocolName().equals(CccParams.PROTOCOL_NAME) 665 && params instanceof CccStartRangingParams) { 666 CccStartRangingParams rangingStartParams = (CccStartRangingParams) params; 667 Log.i(TAG, "startRanging() - update RAN multiplier: " 668 + rangingStartParams.getRanMultiplier()); 669 // Need to update the RAN multiplier from the CccStartRangingParams for CCC session. 670 uwbSession.updateCccParamsOnStart(rangingStartParams); 671 } 672 if (uwbSession.getProtocolName().equals(FiraParams.PROTOCOL_NAME)) { 673 // Need to update session priority if it changed. 674 uwbSession.updateFiraParamsOnStartIfChanged(); 675 } 676 mEventTask.execute(SESSION_START_RANGING, uwbSession); 677 } else if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) { 678 Log.i(TAG, "session is already ranging"); 679 mSessionNotificationManager.onRangingStartFailed( 680 uwbSession, UwbUciConstants.STATUS_CODE_REJECTED); 681 } else { 682 Log.i(TAG, "session can't start ranging"); 683 mSessionNotificationManager.onRangingStartFailed( 684 uwbSession, UwbUciConstants.STATUS_CODE_FAILED); 685 mUwbMetrics.longRangingStartEvent(uwbSession, UwbUciConstants.STATUS_CODE_FAILED); 686 } 687 } 688 stopRangingInternal(SessionHandle sessionHandle, boolean triggeredBySystemPolicy)689 private synchronized void stopRangingInternal(SessionHandle sessionHandle, 690 boolean triggeredBySystemPolicy) { 691 if (!isExistedSession(sessionHandle)) { 692 Log.i(TAG, "Not initialized session ID"); 693 return; 694 } 695 696 int sessionId = getSessionId(sessionHandle); 697 Log.i(TAG, "stopRanging() - sessionId: " + sessionId 698 + ", sessionHandle: " + sessionHandle); 699 700 UwbSession uwbSession = getUwbSession(sessionId); 701 int currentSessionState = getCurrentSessionState(sessionId); 702 if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) { 703 mEventTask.execute(SESSION_STOP_RANGING, uwbSession, triggeredBySystemPolicy ? 1 : 0); 704 } else if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_IDLE) { 705 Log.i(TAG, "session is already idle state"); 706 mSessionNotificationManager.onRangingStopped(uwbSession, 707 UwbUciConstants.STATUS_CODE_OK); 708 mUwbMetrics.longRangingStopEvent(uwbSession); 709 } else { 710 mSessionNotificationManager.onRangingStopFailed(uwbSession, 711 UwbUciConstants.STATUS_CODE_REJECTED); 712 Log.i(TAG, "Not active session ID"); 713 } 714 } 715 stopRanging(SessionHandle sessionHandle)716 public synchronized void stopRanging(SessionHandle sessionHandle) { 717 stopRangingInternal(sessionHandle, false /* triggeredBySystemPolicy */); 718 } 719 720 /** 721 * Get the UwbSession corresponding to the given UWB Session ID. This API returns {@code null} 722 * when the UWB session is not found. 723 */ 724 @Nullable getUwbSession(int sessionId)725 public UwbSession getUwbSession(int sessionId) { 726 return mSessionTable.values() 727 .stream() 728 .filter(v -> v.getSessionId() == sessionId) 729 .findAny() 730 .orElse(null); 731 } 732 733 /** 734 * Get the Uwb Session ID corresponding to the given UWB Session Handle. This API returns 735 * {@code null} when the UWB session ID is not found. 736 */ 737 @Nullable getSessionId(SessionHandle sessionHandle)738 public Integer getSessionId(SessionHandle sessionHandle) { 739 UwbSession session = mSessionTable.get(sessionHandle); 740 if (session == null) return null; 741 return session.getSessionId(); 742 } 743 getActiveSessionCount()744 private int getActiveSessionCount() { 745 return Math.toIntExact( 746 mSessionTable.values() 747 .stream() 748 .filter(v -> v.getSessionState() == UwbUciConstants.DEVICE_STATE_ACTIVE) 749 .count() 750 ); 751 } 752 processRangeData(UwbRangingData rangingData, UwbSession uwbSession)753 private void processRangeData(UwbRangingData rangingData, UwbSession uwbSession) { 754 if (rangingData.getRangingMeasuresType() 755 != UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA) { 756 return; 757 } 758 759 if (!isValidUwbSessionForOwrAoaRanging(uwbSession)) { 760 return; 761 } 762 763 // Record the OWR Aoa Measurement from the RANGE_DATA_NTF. 764 UwbOwrAoaMeasurement uwbOwrAoaMeasurement = rangingData.getRangingOwrAoaMeasure(); 765 mAdvertiseManager.updateAdvertiseTarget(uwbOwrAoaMeasurement); 766 767 byte[] macAddressBytes = getValidMacAddressFromOwrAoaMeasurement( 768 rangingData, uwbOwrAoaMeasurement); 769 if (macAddressBytes == null) { 770 Log.i(TAG, "OwR Aoa UwbSession: Invalid MacAddress for remote device"); 771 return; 772 } 773 774 boolean advertisePointingResult = mAdvertiseManager.isPointedTarget(macAddressBytes); 775 if (mUwbInjector.getUwbServiceCore().isOemExtensionCbRegistered()) { 776 try { 777 PersistableBundle pointedTargetBundle = new AdvertisePointedTarget.Builder() 778 .setMacAddress(macAddressBytes) 779 .setAdvertisePointingResult(advertisePointingResult) 780 .build() 781 .toBundle(); 782 783 advertisePointingResult = mUwbInjector 784 .getUwbServiceCore() 785 .getOemExtensionCallback() 786 .onCheckPointedTarget(pointedTargetBundle); 787 } catch (RemoteException e) { 788 e.printStackTrace(); 789 } 790 } 791 792 if (advertisePointingResult) { 793 // Use a loop to notify all the received application data payload(s) (in sequence number 794 // order) for this OWR AOA ranging session. 795 long macAddress = macAddressByteArrayToLong(macAddressBytes); 796 UwbAddress uwbAddress = UwbAddress.fromBytes(macAddressBytes); 797 798 List<ReceivedDataInfo> receivedDataInfoList = uwbSession.getAllReceivedDataInfo( 799 macAddress); 800 if (receivedDataInfoList.isEmpty()) { 801 Log.i(TAG, "OwR Aoa UwbSession: Application Payload data not found for" 802 + " MacAddress = " + UwbUtil.toHexString(macAddress)); 803 return; 804 } 805 806 receivedDataInfoList.stream().forEach(r -> 807 mSessionNotificationManager.onDataReceived( 808 uwbSession, uwbAddress, new PersistableBundle(), r.payload)); 809 mUwbMetrics.logDataToUpperLayer(uwbSession, receivedDataInfoList.size()); 810 mAdvertiseManager.removeAdvertiseTarget(macAddress); 811 } 812 } 813 814 @Nullable getValidMacAddressFromOwrAoaMeasurement(UwbRangingData rangingData, UwbOwrAoaMeasurement uwbOwrAoaMeasurement)815 private byte[] getValidMacAddressFromOwrAoaMeasurement(UwbRangingData rangingData, 816 UwbOwrAoaMeasurement uwbOwrAoaMeasurement) { 817 byte[] macAddress = uwbOwrAoaMeasurement.getMacAddress(); 818 if (rangingData.getMacAddressMode() == MAC_ADDRESSING_MODE_SHORT) { 819 return (macAddress.length == UWB_DEVICE_SHORT_MAC_ADDRESS_LEN) ? macAddress : null; 820 } else if (rangingData.getMacAddressMode() == MAC_ADDRESSING_MODE_EXTENDED) { 821 return (macAddress.length == UWB_DEVICE_EXT_MAC_ADDRESS_LEN) ? macAddress : null; 822 } 823 return null; 824 } 825 isExistedSession(SessionHandle sessionHandle)826 public boolean isExistedSession(SessionHandle sessionHandle) { 827 return (getSessionId(sessionHandle) != null); 828 } 829 isExistedSession(int sessionId)830 public boolean isExistedSession(int sessionId) { 831 return getUwbSession(sessionId) != null; 832 } 833 stopAllRanging()834 public void stopAllRanging() { 835 Log.d(TAG, "stopAllRanging()"); 836 for (UwbSession uwbSession : mSessionTable.values()) { 837 int status = mNativeUwbManager.stopRanging(uwbSession.getSessionId(), 838 uwbSession.getChipId()); 839 840 if (status != UwbUciConstants.STATUS_CODE_OK) { 841 Log.i(TAG, "stopAllRanging() - Session " + uwbSession.getSessionId() 842 + " is failed to stop ranging"); 843 } else { 844 mUwbMetrics.longRangingStopEvent(uwbSession); 845 uwbSession.setSessionState(UwbUciConstants.UWB_SESSION_STATE_IDLE); 846 } 847 } 848 } 849 deinitAllSession()850 public synchronized void deinitAllSession() { 851 Log.d(TAG, "deinitAllSession()"); 852 for (UwbSession uwbSession : mSessionTable.values()) { 853 handleOnDeInit(uwbSession); 854 } 855 856 // Not resetting chip on UWB toggle off. 857 // mNativeUwbManager.deviceReset(UwbUciConstants.UWBS_RESET); 858 } 859 handleOnDeInit(UwbSession uwbSession)860 public synchronized void handleOnDeInit(UwbSession uwbSession) { 861 if (!isExistedSession(uwbSession.getSessionHandle())) { 862 Log.i(TAG, "onDeinit - Ignoring already deleted session " 863 + uwbSession.getSessionId()); 864 return; 865 } 866 Log.d(TAG, "onDeinit: " + uwbSession.getSessionId()); 867 mSessionNotificationManager.onRangingClosedWithApiReasonCode(uwbSession, 868 RangingChangeReason.SYSTEM_POLICY); 869 mUwbMetrics.logRangingCloseEvent(uwbSession, UwbUciConstants.STATUS_CODE_OK); 870 871 // Reset all UWB session timers when the session is de-init. 872 uwbSession.stopTimers(); 873 removeSession(uwbSession); 874 } 875 setCurrentSessionState(int sessionId, int state)876 public void setCurrentSessionState(int sessionId, int state) { 877 UwbSession uwbSession = getUwbSession(sessionId); 878 if (uwbSession != null) { 879 uwbSession.setSessionState(state); 880 } 881 } 882 getCurrentSessionState(int sessionId)883 public int getCurrentSessionState(int sessionId) { 884 UwbSession uwbSession = getUwbSession(sessionId); 885 if (uwbSession != null) { 886 return uwbSession.getSessionState(); 887 } 888 return UwbUciConstants.UWB_SESSION_STATE_ERROR; 889 } 890 getSessionCount()891 public int getSessionCount() { 892 return mSessionTable.size(); 893 } 894 getCccSessionCount()895 public long getCccSessionCount() { 896 return mSessionTable.values().stream().filter( 897 s -> s.mProtocolName.equals(CccParams.PROTOCOL_NAME)).count(); 898 } 899 getFiraSessionCount()900 public long getFiraSessionCount() { 901 return mSessionTable.values().stream().filter( 902 s -> s.mProtocolName.equals(FiraParams.PROTOCOL_NAME)).count(); 903 } 904 905 /** Returns max number of CCC sessions possible on given chip */ getMaxCccSessionsNumber(String chipId)906 public long getMaxCccSessionsNumber(String chipId) { 907 GenericSpecificationParams params = 908 mUwbInjector.getUwbServiceCore().getCachedSpecificationParams(chipId); 909 if (params != null && params.getCccSpecificationParams() != null) { 910 return params.getCccSpecificationParams().getMaxRangingSessionNumber(); 911 } else { 912 // specification params are empty, return the default CCC max sessions value 913 return CccSpecificationParams.DEFAULT_MAX_RANGING_SESSIONS_NUMBER; 914 } 915 } 916 917 /** Returns max number of Fira sessions possible on given chip */ getMaxFiraSessionsNumber(String chipId)918 public long getMaxFiraSessionsNumber(String chipId) { 919 GenericSpecificationParams params = 920 mUwbInjector.getUwbServiceCore().getCachedSpecificationParams(chipId); 921 if (params != null && params.getFiraSpecificationParams() != null) { 922 return params.getFiraSpecificationParams().getMaxRangingSessionNumber(); 923 } else { 924 // specification params are empty, return the default Fira max sessions value 925 return FiraSpecificationParams.DEFAULT_MAX_RANGING_SESSIONS_NUMBER; 926 } 927 } 928 929 /** Gets the session with the lowest session priority among all sessions with given protocol. */ getSessionWithLowestPriorityByProtocol(String protocolName)930 public Optional<UwbSession> getSessionWithLowestPriorityByProtocol(String protocolName) { 931 return mSessionTable.values().stream().filter( 932 s -> s.mProtocolName.equals(protocolName)).min( 933 Comparator.comparingInt(UwbSession::getStackSessionPriority)); 934 } 935 getSessionIdSet()936 public Set<Integer> getSessionIdSet() { 937 return mSessionTable.values() 938 .stream() 939 .map(v -> v.getSessionId()) 940 .collect(Collectors.toSet()); 941 } 942 reconfigureInternal(SessionHandle sessionHandle, @Nullable Params params, boolean triggeredByFgStateChange)943 private synchronized int reconfigureInternal(SessionHandle sessionHandle, 944 @Nullable Params params, boolean triggeredByFgStateChange) { 945 int status = UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST; 946 if (!isExistedSession(sessionHandle)) { 947 Log.i(TAG, "Not initialized session ID"); 948 return status; 949 } 950 int sessionId = getSessionId(sessionHandle); 951 Log.i(TAG, "reconfigure() - Session ID : " + sessionId); 952 UwbSession uwbSession = getUwbSession(sessionId); 953 if (uwbSession.getProtocolName().equals(FiraParams.PROTOCOL_NAME) 954 && params instanceof FiraRangingReconfigureParams) { 955 FiraRangingReconfigureParams rangingReconfigureParams = 956 (FiraRangingReconfigureParams) params; 957 Log.i(TAG, "reconfigure() - update reconfigure params: " 958 + rangingReconfigureParams); 959 // Do not update mParams if this was triggered by framework. 960 if (!triggeredByFgStateChange) { 961 uwbSession.updateFiraParamsOnReconfigure(rangingReconfigureParams); 962 } 963 } 964 mEventTask.execute(SESSION_RECONFIG_RANGING, 965 new ReconfigureEventParams(uwbSession, params, triggeredByFgStateChange)); 966 return 0; 967 } 968 reconfigure(SessionHandle sessionHandle, @Nullable Params params)969 public synchronized int reconfigure(SessionHandle sessionHandle, @Nullable Params params) { 970 return reconfigureInternal(sessionHandle, params, false /* triggeredByFgStateChange */); 971 } 972 973 /** Send the payload data to a remote device in the UWB session */ sendData(SessionHandle sessionHandle, UwbAddress remoteDeviceAddress, PersistableBundle params, byte[] data)974 public synchronized void sendData(SessionHandle sessionHandle, UwbAddress remoteDeviceAddress, 975 PersistableBundle params, byte[] data) { 976 SendDataInfo info = new SendDataInfo(); 977 info.sessionHandle = sessionHandle; 978 info.remoteDeviceAddress = remoteDeviceAddress; 979 info.params = params; 980 info.data = data; 981 982 mEventTask.execute(SESSION_SEND_DATA, info); 983 } 984 985 private static final class SendDataInfo { 986 public SessionHandle sessionHandle; 987 public UwbAddress remoteDeviceAddress; 988 public PersistableBundle params; 989 public byte[] data; 990 } 991 992 private static final class RangingRoundsUpdateDtTagInfo { 993 public SessionHandle sessionHandle; 994 public PersistableBundle params; 995 } 996 997 /** DT Tag ranging round update */ rangingRoundsUpdateDtTag(SessionHandle sessionHandle, PersistableBundle bundle)998 public void rangingRoundsUpdateDtTag(SessionHandle sessionHandle, 999 PersistableBundle bundle) { 1000 RangingRoundsUpdateDtTagInfo info = new RangingRoundsUpdateDtTagInfo(); 1001 info.sessionHandle = sessionHandle; 1002 info.params = bundle; 1003 1004 mEventTask.execute(SESSION_UPDATE_DT_TAG_RANGING_ROUNDS, info); 1005 } 1006 1007 /** Query Max Application data size for the given UWB Session */ queryMaxDataSizeBytes(SessionHandle sessionHandle)1008 public synchronized int queryMaxDataSizeBytes(SessionHandle sessionHandle) { 1009 if (!isExistedSession(sessionHandle)) { 1010 throw new IllegalStateException("Not initialized session ID"); 1011 } 1012 1013 int sessionId = getSessionId(sessionHandle); 1014 UwbSession uwbSession = getUwbSession(sessionId); 1015 if (uwbSession == null) { 1016 throw new IllegalStateException("UwbSession not found"); 1017 } 1018 1019 synchronized (uwbSession.getWaitObj()) { 1020 return mNativeUwbManager.queryMaxDataSizeBytes(uwbSession.getSessionId(), 1021 uwbSession.getChipId()); 1022 } 1023 } 1024 1025 /** Handle ranging rounds update for DT Tag */ handleRangingRoundsUpdateDtTag(RangingRoundsUpdateDtTagInfo info)1026 public void handleRangingRoundsUpdateDtTag(RangingRoundsUpdateDtTagInfo info) { 1027 SessionHandle sessionHandle = info.sessionHandle; 1028 Integer sessionId = getSessionId(sessionHandle); 1029 if (sessionId == null) { 1030 Log.i(TAG, "UwbSessionId not found"); 1031 return; 1032 } 1033 UwbSession uwbSession = getUwbSession(sessionId); 1034 if (uwbSession == null) { 1035 Log.i(TAG, "UwbSession not found"); 1036 return; 1037 } 1038 DlTDoARangingRoundsUpdate dlTDoARangingRoundsUpdate = DlTDoARangingRoundsUpdate 1039 .fromBundle(info.params); 1040 1041 if (dlTDoARangingRoundsUpdate.getSessionId() != getSessionId(sessionHandle)) { 1042 throw new IllegalArgumentException("Wrong session ID"); 1043 } 1044 1045 FutureTask<DtTagUpdateRangingRoundsStatus> rangingRoundsUpdateTask = new FutureTask<>( 1046 () -> { 1047 synchronized (uwbSession.getWaitObj()) { 1048 return mNativeUwbManager.sessionUpdateDtTagRangingRounds( 1049 (int) dlTDoARangingRoundsUpdate.getSessionId(), 1050 dlTDoARangingRoundsUpdate.getNoOfRangingRounds(), 1051 dlTDoARangingRoundsUpdate.getRangingRoundIndexes(), 1052 uwbSession.getChipId()); 1053 } 1054 } 1055 ); 1056 1057 DtTagUpdateRangingRoundsStatus status = null; 1058 ExecutorService executor = Executors.newSingleThreadExecutor(); 1059 executor.submit(rangingRoundsUpdateTask); 1060 try { 1061 status = rangingRoundsUpdateTask.get(IUwbAdapter 1062 .RANGING_ROUNDS_UPDATE_DT_TAG_THRESHOLD_MS, TimeUnit.MILLISECONDS); 1063 } catch (TimeoutException e) { 1064 Log.i(TAG, "Failed to update ranging rounds for Dt tag - status : TIMEOUT"); 1065 executor.shutdownNow(); 1066 } catch (InterruptedException | ExecutionException e) { 1067 e.printStackTrace(); 1068 } 1069 // Native stack returns null if unsuccessful 1070 if (status == null) { 1071 status = new DtTagUpdateRangingRoundsStatus( 1072 UwbUciConstants.STATUS_CODE_ERROR_ROUND_INDEX_NOT_ACTIVATED, 1073 0, 1074 new byte[]{}); 1075 } 1076 PersistableBundle params = new DlTDoARangingRoundsUpdateStatus.Builder() 1077 .setStatus(status.getStatus()) 1078 .setNoOfRangingRounds(status.getNoOfRangingRounds()) 1079 .setRangingRoundIndexes(status.getRangingRoundIndexes()) 1080 .build() 1081 .toBundle(); 1082 mSessionNotificationManager.onRangingRoundsUpdateStatus(uwbSession, params); 1083 } 1084 removeSession(UwbSession uwbSession)1085 void removeSession(UwbSession uwbSession) { 1086 if (uwbSession != null) { 1087 uwbSession.getBinder().unlinkToDeath(uwbSession, 0); 1088 removeAdvertiserData(uwbSession); 1089 uwbSession.close(); 1090 removeFromNonPrivilegedUidToFiraSessionTableIfNecessary(uwbSession); 1091 mSessionTokenMap.remove(uwbSession.getSessionId()); 1092 mSessionTable.remove(uwbSession.getSessionHandle()); 1093 mDbgRecentlyClosedSessions.add(uwbSession); 1094 } 1095 } 1096 removeAdvertiserData(UwbSession uwbSession)1097 private void removeAdvertiserData(UwbSession uwbSession) { 1098 for (long remoteMacAddress : uwbSession.getRemoteMacAddressList()) { 1099 mAdvertiseManager.removeAdvertiseTarget(remoteMacAddress); 1100 } 1101 } 1102 addToNonPrivilegedUidToFiraSessionTableIfNecessary(@onNull UwbSession uwbSession)1103 void addToNonPrivilegedUidToFiraSessionTableIfNecessary(@NonNull UwbSession uwbSession) { 1104 if (uwbSession.getSessionType() == UwbUciConstants.SESSION_TYPE_RANGING) { 1105 AttributionSource nonPrivilegedAppAttrSource = 1106 uwbSession.getAnyNonPrivilegedAppInAttributionSource(); 1107 if (nonPrivilegedAppAttrSource != null) { 1108 Log.d(TAG, "Detected start of non privileged FIRA session from " 1109 + nonPrivilegedAppAttrSource); 1110 List<UwbSession> sessions = mNonPrivilegedUidToFiraSessionsTable.computeIfAbsent( 1111 nonPrivilegedAppAttrSource.getUid(), v -> new ArrayList<>()); 1112 sessions.add(uwbSession); 1113 } 1114 } 1115 } 1116 removeFromNonPrivilegedUidToFiraSessionTableIfNecessary(@onNull UwbSession uwbSession)1117 void removeFromNonPrivilegedUidToFiraSessionTableIfNecessary(@NonNull UwbSession uwbSession) { 1118 if (uwbSession.getSessionType() == UwbUciConstants.SESSION_TYPE_RANGING) { 1119 AttributionSource nonPrivilegedAppAttrSource = 1120 uwbSession.getAnyNonPrivilegedAppInAttributionSource(); 1121 if (nonPrivilegedAppAttrSource != null) { 1122 Log.d(TAG, "Detected end of non privileged FIRA session from " 1123 + nonPrivilegedAppAttrSource); 1124 List<UwbSession> sessions = mNonPrivilegedUidToFiraSessionsTable.get( 1125 nonPrivilegedAppAttrSource.getUid()); 1126 if (sessions == null) { 1127 Log.wtf(TAG, "No sessions found for uid: " 1128 + nonPrivilegedAppAttrSource.getUid()); 1129 return; 1130 } 1131 sessions.remove(uwbSession); 1132 if (sessions.isEmpty()) { 1133 mNonPrivilegedUidToFiraSessionsTable.remove( 1134 nonPrivilegedAppAttrSource.getUid()); 1135 } 1136 } 1137 } 1138 } 1139 1140 private static class ReconfigureEventParams { 1141 public final UwbSession uwbSession; 1142 public final Params params; 1143 public final boolean triggeredByFgStateChange; 1144 ReconfigureEventParams(UwbSession uwbSession, Params params, boolean triggeredByFgStateChange)1145 ReconfigureEventParams(UwbSession uwbSession, Params params, 1146 boolean triggeredByFgStateChange) { 1147 this.uwbSession = uwbSession; 1148 this.params = params; 1149 this.triggeredByFgStateChange = triggeredByFgStateChange; 1150 } 1151 } 1152 1153 private class EventTask extends Handler { 1154 EventTask(Looper looper)1155 EventTask(Looper looper) { 1156 super(looper); 1157 } 1158 1159 @Override handleMessage(Message msg)1160 public void handleMessage(Message msg) { 1161 int type = msg.what; 1162 switch (type) { 1163 case SESSION_OPEN_RANGING: { 1164 UwbSession uwbSession = (UwbSession) msg.obj; 1165 handleOpenRanging(uwbSession); 1166 break; 1167 } 1168 1169 case SESSION_START_RANGING: { 1170 UwbSession uwbSession = (UwbSession) msg.obj; 1171 handleStartRanging(uwbSession); 1172 break; 1173 } 1174 1175 case SESSION_STOP_RANGING: { 1176 UwbSession uwbSession = (UwbSession) msg.obj; 1177 boolean triggeredBySystemPolicy = msg.arg1 == 1; 1178 handleStopRanging(uwbSession, triggeredBySystemPolicy); 1179 break; 1180 } 1181 1182 case SESSION_RECONFIG_RANGING: { 1183 Log.d(TAG, "SESSION_RECONFIG_RANGING"); 1184 ReconfigureEventParams params = (ReconfigureEventParams) msg.obj; 1185 handleReconfigure( 1186 params.uwbSession, params.params, params.triggeredByFgStateChange); 1187 break; 1188 } 1189 1190 case SESSION_DEINIT: { 1191 UwbSession uwbSession = (UwbSession) msg.obj; 1192 int reason = msg.arg1; 1193 handleDeInitWithReason(uwbSession, reason); 1194 break; 1195 } 1196 1197 case SESSION_ON_DEINIT: { 1198 UwbSession uwbSession = (UwbSession) msg.obj; 1199 handleOnDeInit(uwbSession); 1200 break; 1201 } 1202 1203 case SESSION_SEND_DATA: { 1204 Log.d(TAG, "SESSION_SEND_DATA"); 1205 SendDataInfo info = (SendDataInfo) msg.obj; 1206 handleSendData(info); 1207 break; 1208 } 1209 1210 case SESSION_UPDATE_DT_TAG_RANGING_ROUNDS: { 1211 Log.d(TAG, "SESSION_UPDATE_DT_TAG_RANGING_ROUNDS"); 1212 RangingRoundsUpdateDtTagInfo info = (RangingRoundsUpdateDtTagInfo) msg.obj; 1213 handleRangingRoundsUpdateDtTag(info); 1214 break; 1215 } 1216 1217 default: { 1218 Log.d(TAG, "EventTask : Undefined Task"); 1219 break; 1220 } 1221 } 1222 } 1223 execute(int task, Object obj)1224 public void execute(int task, Object obj) { 1225 Message msg = mEventTask.obtainMessage(); 1226 msg.what = task; 1227 msg.obj = obj; 1228 this.sendMessage(msg); 1229 } 1230 execute(int task, Object obj, int arg1)1231 public void execute(int task, Object obj, int arg1) { 1232 Message msg = mEventTask.obtainMessage(); 1233 msg.what = task; 1234 msg.obj = obj; 1235 msg.arg1 = arg1; 1236 this.sendMessage(msg); 1237 } 1238 handleOpenRanging(UwbSession uwbSession)1239 private void handleOpenRanging(UwbSession uwbSession) { 1240 Trace.beginSection("UWB#handleOpenRanging"); 1241 // TODO(b/211445008): Consolidate to a single uwb thread. 1242 FutureTask<Integer> initSessionTask = new FutureTask<>( 1243 () -> { 1244 int status = UwbUciConstants.STATUS_CODE_FAILED; 1245 synchronized (uwbSession.getWaitObj()) { 1246 uwbSession.setOperationType(OPERATION_TYPE_INIT_SESSION); 1247 status = mNativeUwbManager.initSession( 1248 uwbSession.getSessionId(), 1249 uwbSession.getSessionType(), 1250 uwbSession.getChipId()); 1251 if (status != UwbUciConstants.STATUS_CODE_OK) { 1252 return status; 1253 } 1254 mSessionTokenMap.put(uwbSession.getSessionId(), mNativeUwbManager 1255 .getSessionToken(uwbSession.getSessionId(), 1256 uwbSession.getChipId())); 1257 uwbSession.getWaitObj().blockingWait(); 1258 status = UwbUciConstants.STATUS_CODE_FAILED; 1259 if (uwbSession.getSessionState() 1260 == UwbUciConstants.UWB_SESSION_STATE_INIT) { 1261 status = UwbSessionManager.this.setAppConfigurations(uwbSession); 1262 if (status != UwbUciConstants.STATUS_CODE_OK) { 1263 return status; 1264 } 1265 1266 uwbSession.getWaitObj().blockingWait(); 1267 status = UwbUciConstants.STATUS_CODE_FAILED; 1268 if (uwbSession.getSessionState() 1269 == UwbUciConstants.UWB_SESSION_STATE_IDLE) { 1270 mSessionNotificationManager.onRangingOpened(uwbSession); 1271 status = UwbUciConstants.STATUS_CODE_OK; 1272 } else { 1273 status = UwbUciConstants.STATUS_CODE_FAILED; 1274 } 1275 return status; 1276 } 1277 return status; 1278 } 1279 }); 1280 1281 int status = UwbUciConstants.STATUS_CODE_FAILED; 1282 try { 1283 status = mUwbInjector.runTaskOnSingleThreadExecutor(initSessionTask, 1284 IUwbAdapter.RANGING_SESSION_OPEN_THRESHOLD_MS); 1285 } catch (TimeoutException e) { 1286 Log.i(TAG, "Failed to initialize session - status : TIMEOUT"); 1287 } catch (InterruptedException e) { 1288 e.printStackTrace(); 1289 } catch (ExecutionException e) { 1290 e.printStackTrace(); 1291 } 1292 1293 mUwbMetrics.logRangingInitEvent(uwbSession, status); 1294 if (status != UwbUciConstants.STATUS_CODE_OK) { 1295 Log.i(TAG, "Failed to initialize session - status : " + status); 1296 mSessionNotificationManager.onRangingOpenFailed(uwbSession, status); 1297 uwbSession.setOperationType(SESSION_ON_DEINIT); 1298 mNativeUwbManager.deInitSession(uwbSession.getSessionId(), uwbSession.getChipId()); 1299 removeSession(uwbSession); 1300 } 1301 Log.i(TAG, "sessionInit() : finish - sessionId : " + uwbSession.getSessionId()); 1302 Trace.endSection(); 1303 } 1304 handleStartRanging(UwbSession uwbSession)1305 private void handleStartRanging(UwbSession uwbSession) { 1306 Trace.beginSection("UWB#handleStartRanging"); 1307 // TODO(b/211445008): Consolidate to a single uwb thread. 1308 FutureTask<Integer> startRangingTask = new FutureTask<>( 1309 () -> { 1310 int status = UwbUciConstants.STATUS_CODE_FAILED; 1311 synchronized (uwbSession.getWaitObj()) { 1312 if (uwbSession.getNeedsAppConfigUpdate()) { 1313 uwbSession.resetNeedsAppConfigUpdate(); 1314 status = mConfigurationManager.setAppConfigurations( 1315 uwbSession.getSessionId(), 1316 uwbSession.getParams(), uwbSession.getChipId()); 1317 if (status != UwbUciConstants.STATUS_CODE_OK) { 1318 mSessionNotificationManager.onRangingStartFailed( 1319 uwbSession, status); 1320 return status; 1321 } 1322 } 1323 1324 uwbSession.setOperationType(SESSION_START_RANGING); 1325 status = mNativeUwbManager.startRanging(uwbSession.getSessionId(), 1326 uwbSession.getChipId()); 1327 if (status != UwbUciConstants.STATUS_CODE_OK) { 1328 mSessionNotificationManager.onRangingStartFailed( 1329 uwbSession, status); 1330 return status; 1331 } 1332 uwbSession.getWaitObj().blockingWait(); 1333 if (uwbSession.getSessionState() 1334 == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) { 1335 // TODO: Ensure |rangingStartedParams| is valid for FIRA sessions 1336 // as well. 1337 Params rangingStartedParams = uwbSession.getParams(); 1338 // For CCC sessions, retrieve the app configs 1339 if (uwbSession.getProtocolName().equals(CccParams.PROTOCOL_NAME)) { 1340 Pair<Integer, CccRangingStartedParams> statusAndParams = 1341 mConfigurationManager.getAppConfigurations( 1342 uwbSession.getSessionId(), 1343 CccParams.PROTOCOL_NAME, 1344 new byte[0], 1345 CccRangingStartedParams.class, 1346 uwbSession.getChipId()); 1347 if (statusAndParams.first != UwbUciConstants.STATUS_CODE_OK) { 1348 Log.e(TAG, "Failed to get CCC ranging started params"); 1349 } 1350 rangingStartedParams = statusAndParams.second; 1351 } 1352 mSessionNotificationManager.onRangingStarted( 1353 uwbSession, rangingStartedParams); 1354 } else { 1355 status = UwbUciConstants.STATUS_CODE_FAILED; 1356 mSessionNotificationManager.onRangingStartFailed(uwbSession, 1357 status); 1358 } 1359 } 1360 return status; 1361 }); 1362 int status = UwbUciConstants.STATUS_CODE_FAILED; 1363 try { 1364 status = mUwbInjector.runTaskOnSingleThreadExecutor(startRangingTask, 1365 IUwbAdapter.RANGING_SESSION_START_THRESHOLD_MS); 1366 } catch (TimeoutException e) { 1367 Log.i(TAG, "Failed to Start Ranging - status : TIMEOUT"); 1368 mSessionNotificationManager.onRangingStartFailed( 1369 uwbSession, UwbUciConstants.STATUS_CODE_FAILED); 1370 } catch (InterruptedException e) { 1371 e.printStackTrace(); 1372 } catch (ExecutionException e) { 1373 e.printStackTrace(); 1374 } 1375 mUwbMetrics.longRangingStartEvent(uwbSession, status); 1376 Trace.endSection(); 1377 } 1378 handleStopRanging(UwbSession uwbSession, boolean triggeredBySystemPolicy)1379 private void handleStopRanging(UwbSession uwbSession, boolean triggeredBySystemPolicy) { 1380 Trace.beginSection("UWB#handleStopRanging"); 1381 // TODO(b/211445008): Consolidate to a single uwb thread. 1382 FutureTask<Integer> stopRangingTask = new FutureTask<>( 1383 () -> { 1384 int status = UwbUciConstants.STATUS_CODE_FAILED; 1385 synchronized (uwbSession.getWaitObj()) { 1386 uwbSession.setOperationType(SESSION_STOP_RANGING); 1387 status = mNativeUwbManager.stopRanging(uwbSession.getSessionId(), 1388 uwbSession.getChipId()); 1389 if (status != UwbUciConstants.STATUS_CODE_OK) { 1390 mSessionNotificationManager.onRangingStopFailed(uwbSession, status); 1391 return status; 1392 } 1393 uwbSession.getWaitObj().blockingWait(); 1394 if (uwbSession.getSessionState() 1395 == UwbUciConstants.UWB_SESSION_STATE_IDLE) { 1396 int apiReasonCode = triggeredBySystemPolicy 1397 ? RangingChangeReason.SYSTEM_POLICY 1398 : RangingChangeReason.LOCAL_API; 1399 mSessionNotificationManager.onRangingStoppedWithApiReasonCode( 1400 uwbSession, apiReasonCode); 1401 } else { 1402 status = UwbUciConstants.STATUS_CODE_FAILED; 1403 mSessionNotificationManager.onRangingStopFailed(uwbSession, 1404 status); 1405 } 1406 } 1407 return status; 1408 }); 1409 1410 1411 int status = UwbUciConstants.STATUS_CODE_FAILED; 1412 int timeoutMs = IUwbAdapter.RANGING_SESSION_START_THRESHOLD_MS; 1413 if (uwbSession.getProtocolName().equals(PROTOCOL_NAME)) { 1414 int minTimeoutNecessary = uwbSession.getCurrentFiraRangingIntervalMs() * 4; 1415 timeoutMs = timeoutMs > minTimeoutNecessary ? timeoutMs : minTimeoutNecessary; 1416 } 1417 Log.v(TAG, "Stop timeout: " + timeoutMs); 1418 try { 1419 status = mUwbInjector.runTaskOnSingleThreadExecutor(stopRangingTask, timeoutMs); 1420 } catch (TimeoutException e) { 1421 Log.i(TAG, "Failed to Stop Ranging - status : TIMEOUT"); 1422 mSessionNotificationManager.onRangingStopFailed( 1423 uwbSession, UwbUciConstants.STATUS_CODE_FAILED); 1424 } catch (InterruptedException e) { 1425 e.printStackTrace(); 1426 } catch (ExecutionException e) { 1427 e.printStackTrace(); 1428 } 1429 if (status != UwbUciConstants.STATUS_CODE_FAILED) { 1430 mUwbMetrics.longRangingStopEvent(uwbSession); 1431 } 1432 // Reset all UWB session timers when the session is stopped. 1433 uwbSession.stopTimers(); 1434 removeAdvertiserData(uwbSession); 1435 Trace.endSection(); 1436 } 1437 handleReconfigure(UwbSession uwbSession, @Nullable Params param, boolean triggeredByFgStateChange)1438 private void handleReconfigure(UwbSession uwbSession, @Nullable Params param, 1439 boolean triggeredByFgStateChange) { 1440 if (!(param instanceof FiraRangingReconfigureParams)) { 1441 Log.e(TAG, "Invalid reconfigure params: " + param); 1442 mSessionNotificationManager.onRangingReconfigureFailed( 1443 uwbSession, UwbUciConstants.STATUS_CODE_INVALID_PARAM); 1444 return; 1445 } 1446 Trace.beginSection("UWB#handleReconfigure"); 1447 FiraRangingReconfigureParams rangingReconfigureParams = 1448 (FiraRangingReconfigureParams) param; 1449 // TODO(b/211445008): Consolidate to a single uwb thread. 1450 FutureTask<Integer> cmdTask = new FutureTask<>( 1451 () -> { 1452 int status = UwbUciConstants.STATUS_CODE_FAILED; 1453 synchronized (uwbSession.getWaitObj()) { 1454 // Handle SESSION_UPDATE_CONTROLLER_MULTICAST_LIST_CMD 1455 UwbAddress[] addrList = rangingReconfigureParams.getAddressList(); 1456 Integer action = rangingReconfigureParams.getAction(); 1457 // Action will indicate if this is a controlee add/remove. 1458 // if null, it's a session configuration change. 1459 if (action != null) { 1460 if (addrList == null) { 1461 Log.e(TAG, 1462 "Multicast update missing the address list."); 1463 return status; 1464 } 1465 int dstAddressListSize = addrList.length; 1466 List<byte[]> dstAddressList = new ArrayList<>(); 1467 for (UwbAddress address : addrList) { 1468 dstAddressList.add(getComputedMacAddress(address)); 1469 } 1470 int[] subSessionIdList; 1471 if (!ArrayUtils.isEmpty( 1472 rangingReconfigureParams.getSubSessionIdList())) { 1473 subSessionIdList = 1474 rangingReconfigureParams.getSubSessionIdList(); 1475 } else { 1476 // Set to 0's for the UCI stack. 1477 subSessionIdList = new int[dstAddressListSize]; 1478 } 1479 boolean isV2 = action 1480 == P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_16_BYTE 1481 || action 1482 == P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_32_BYTE; 1483 status = mNativeUwbManager.controllerMulticastListUpdate( 1484 uwbSession.getSessionId(), 1485 action, 1486 subSessionIdList.length, 1487 ArrayUtils.toPrimitive(dstAddressList), 1488 subSessionIdList, 1489 isV2 ? rangingReconfigureParams 1490 .getSubSessionKeyList() : null, 1491 uwbSession.getChipId()); 1492 if (status != UwbUciConstants.STATUS_CODE_OK) { 1493 Log.e(TAG, "Unable to update controller multicast list."); 1494 if (isMulticastActionAdd(action)) { 1495 mSessionNotificationManager.onControleeAddFailed( 1496 uwbSession, status); 1497 } else if (action == MULTICAST_LIST_UPDATE_ACTION_DELETE) { 1498 mSessionNotificationManager.onControleeRemoveFailed( 1499 uwbSession, status); 1500 } 1501 return status; 1502 } 1503 1504 uwbSession.getWaitObj().blockingWait(); 1505 1506 UwbMulticastListUpdateStatus multicastList = 1507 uwbSession.getMulticastListUpdateStatus(); 1508 1509 if (multicastList == null) { 1510 Log.e(TAG, "Confirmed controller multicast list is empty!"); 1511 return status; 1512 } 1513 1514 for (int i = 0; i < multicastList.getNumOfControlee(); i++) { 1515 int actionStatus = multicastList.getStatus()[i]; 1516 if (actionStatus == UwbUciConstants.STATUS_CODE_OK) { 1517 if (isMulticastActionAdd(action)) { 1518 uwbSession.addControlee( 1519 multicastList.getControleeUwbAddresses()[i]); 1520 mSessionNotificationManager.onControleeAdded( 1521 uwbSession); 1522 } else if (action == MULTICAST_LIST_UPDATE_ACTION_DELETE) { 1523 uwbSession.removeControlee( 1524 multicastList.getControleeUwbAddresses()[i]); 1525 mSessionNotificationManager.onControleeRemoved( 1526 uwbSession); 1527 } 1528 } 1529 else { 1530 status = actionStatus; 1531 if (isMulticastActionAdd(action)) { 1532 mSessionNotificationManager.onControleeAddFailed( 1533 uwbSession, actionStatus); 1534 } else if (action == MULTICAST_LIST_UPDATE_ACTION_DELETE) { 1535 mSessionNotificationManager.onControleeRemoveFailed( 1536 uwbSession, actionStatus); 1537 } 1538 } 1539 } 1540 } else { 1541 // setAppConfigurations only applies to config changes, 1542 // not controlee list changes 1543 status = mConfigurationManager.setAppConfigurations( 1544 uwbSession.getSessionId(), param, uwbSession.getChipId()); 1545 } 1546 if (status == UwbUciConstants.STATUS_CODE_OK) { 1547 // only call this if all controlees succeeded otherwise the 1548 // fail status cause a onRangingReconfigureFailed later. 1549 if (!triggeredByFgStateChange) { 1550 mSessionNotificationManager.onRangingReconfigured(uwbSession); 1551 } 1552 } 1553 Log.d(TAG, "Multicast update status: " + status); 1554 return status; 1555 } 1556 }); 1557 int status = UwbUciConstants.STATUS_CODE_FAILED; 1558 try { 1559 status = mUwbInjector.runTaskOnSingleThreadExecutor(cmdTask, 1560 IUwbAdapter.RANGING_SESSION_OPEN_THRESHOLD_MS); 1561 } catch (TimeoutException e) { 1562 Log.i(TAG, "Failed to Reconfigure - status : TIMEOUT"); 1563 } catch (InterruptedException e) { 1564 e.printStackTrace(); 1565 } catch (ExecutionException e) { 1566 e.printStackTrace(); 1567 } 1568 if (status != UwbUciConstants.STATUS_CODE_OK) { 1569 Log.i(TAG, "Failed to Reconfigure : " + status); 1570 if (!triggeredByFgStateChange) { 1571 mSessionNotificationManager.onRangingReconfigureFailed(uwbSession, status); 1572 } 1573 } 1574 Trace.endSection(); 1575 } 1576 isMulticastActionAdd(Integer action)1577 private boolean isMulticastActionAdd(Integer action) { 1578 return action == MULTICAST_LIST_UPDATE_ACTION_ADD 1579 || action == P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_16_BYTE 1580 || action == P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_32_BYTE; 1581 } 1582 handleDeInitWithReason(UwbSession uwbSession, int reason)1583 private void handleDeInitWithReason(UwbSession uwbSession, int reason) { 1584 Trace.beginSection("UWB#handleDeInitWithReason"); 1585 // TODO(b/211445008): Consolidate to a single uwb thread. 1586 FutureTask<Integer> deInitTask = new FutureTask<>( 1587 (Callable<Integer>) () -> { 1588 int status = UwbUciConstants.STATUS_CODE_FAILED; 1589 synchronized (uwbSession.getWaitObj()) { 1590 status = mNativeUwbManager.deInitSession(uwbSession.getSessionId(), 1591 uwbSession.getChipId()); 1592 if (status != UwbUciConstants.STATUS_CODE_OK) { 1593 mSessionNotificationManager.onRangingClosed(uwbSession, status); 1594 return status; 1595 } 1596 uwbSession.getWaitObj().blockingWait(); 1597 Log.i(TAG, "onRangingClosed - status : " + status); 1598 mSessionNotificationManager.onRangingClosed(uwbSession, 1599 reason); 1600 } 1601 return status; 1602 }); 1603 1604 int status = UwbUciConstants.STATUS_CODE_FAILED; 1605 try { 1606 status = mUwbInjector.runTaskOnSingleThreadExecutor(deInitTask, 1607 IUwbAdapter.RANGING_SESSION_CLOSE_THRESHOLD_MS); 1608 } catch (TimeoutException e) { 1609 Log.i(TAG, "Failed to Stop Ranging - status : TIMEOUT"); 1610 mSessionNotificationManager.onRangingClosed(uwbSession, status); 1611 } catch (InterruptedException | ExecutionException e) { 1612 e.printStackTrace(); 1613 } 1614 mUwbMetrics.logRangingCloseEvent(uwbSession, status); 1615 1616 // Reset all UWB session timers when the session is de-initialized (ie, closed). 1617 uwbSession.stopTimers(); 1618 removeSession(uwbSession); 1619 Log.i(TAG, "deinit finish : status :" + status); 1620 Trace.endSection(); 1621 } 1622 handleSendData(SendDataInfo sendDataInfo)1623 private void handleSendData(SendDataInfo sendDataInfo) { 1624 int status = UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST; 1625 SessionHandle sessionHandle = sendDataInfo.sessionHandle; 1626 if (sessionHandle == null) { 1627 Log.i(TAG, "Not present sessionHandle"); 1628 mSessionNotificationManager.onDataSendFailed( 1629 null, sendDataInfo.remoteDeviceAddress, status, sendDataInfo.params); 1630 return; 1631 } 1632 1633 Integer sessionId = getSessionId(sessionHandle); 1634 if (sessionId == null) { 1635 Log.i(TAG, "UwbSessionId not found"); 1636 mSessionNotificationManager.onDataSendFailed( 1637 null, sendDataInfo.remoteDeviceAddress, status, sendDataInfo.params); 1638 return; 1639 } 1640 1641 // TODO(b/256675656): Check if there is race condition between uwbSession being 1642 // retrieved here and used below (and similar for uwbSession being stored in the 1643 // mLooper message and being used during processing for all other message types). 1644 UwbSession uwbSession = getUwbSession(sessionId); 1645 if (uwbSession == null) { 1646 Log.i(TAG, "UwbSession not found"); 1647 mSessionNotificationManager.onDataSendFailed( 1648 null, sendDataInfo.remoteDeviceAddress, status, sendDataInfo.params); 1649 return; 1650 } 1651 1652 // TODO(b/211445008): Consolidate to a single uwb thread. 1653 FutureTask<Integer> sendDataTask = new FutureTask<>((Callable<Integer>) () -> { 1654 int sendDataStatus = UwbUciConstants.STATUS_CODE_FAILED; 1655 synchronized (uwbSession.getWaitObj()) { 1656 if (!isValidUwbSessionForApplicationDataTransfer(uwbSession)) { 1657 sendDataStatus = UwbUciConstants.STATUS_CODE_FAILED; 1658 Log.i(TAG, "UwbSession not in active state"); 1659 mSessionNotificationManager.onDataSendFailed( 1660 uwbSession, sendDataInfo.remoteDeviceAddress, sendDataStatus, 1661 sendDataInfo.params); 1662 return sendDataStatus; 1663 } 1664 if (!isValidSendDataInfo(sendDataInfo)) { 1665 sendDataStatus = UwbUciConstants.STATUS_CODE_INVALID_PARAM; 1666 mSessionNotificationManager.onDataSendFailed( 1667 uwbSession, sendDataInfo.remoteDeviceAddress, sendDataStatus, 1668 sendDataInfo.params); 1669 return sendDataStatus; 1670 } 1671 1672 // Get the UCI sequence number for this data packet, and store it. 1673 byte sequenceNum = uwbSession.getAndIncrementDataSndSequenceNumber(); 1674 uwbSession.addSendDataInfo(sequenceNum, sendDataInfo); 1675 1676 sendDataStatus = mNativeUwbManager.sendData( 1677 uwbSession.getSessionId(), 1678 DataTypeConversionUtil.convertShortMacAddressBytesToExtended( 1679 sendDataInfo.remoteDeviceAddress.toBytes()), 1680 UwbUciConstants.UWB_DESTINATION_END_POINT_HOST, sequenceNum, 1681 sendDataInfo.data, uwbSession.getChipId()); 1682 mUwbMetrics.logDataTx(uwbSession, sendDataStatus); 1683 if (sendDataStatus != STATUS_CODE_OK) { 1684 Log.e(TAG, "MSG_SESSION_SEND_DATA error status: " + sendDataStatus 1685 + " for data packet sessionId: " + sessionId 1686 + ", sequence number: " + sequenceNum); 1687 mSessionNotificationManager.onDataSendFailed( 1688 uwbSession, sendDataInfo.remoteDeviceAddress, sendDataStatus, 1689 sendDataInfo.params); 1690 uwbSession.removeSendDataInfo(sequenceNum); 1691 } 1692 return sendDataStatus; 1693 } 1694 }); 1695 1696 status = UwbUciConstants.STATUS_CODE_FAILED; 1697 try { 1698 status = mUwbInjector.runTaskOnSingleThreadExecutor(sendDataTask, 1699 IUwbAdapter.RANGING_SESSION_OPEN_THRESHOLD_MS); 1700 } catch (TimeoutException e) { 1701 Log.i(TAG, "Failed to Send data - status : TIMEOUT"); 1702 mSessionNotificationManager.onDataSendFailed(uwbSession, 1703 sendDataInfo.remoteDeviceAddress, status, sendDataInfo.params); 1704 } catch (InterruptedException | ExecutionException e) { 1705 e.printStackTrace(); 1706 } 1707 } 1708 } 1709 isValidUwbSessionForOwrAoaRanging(UwbSession uwbSession)1710 private boolean isValidUwbSessionForOwrAoaRanging(UwbSession uwbSession) { 1711 Params params = uwbSession.getParams(); 1712 if (params instanceof FiraOpenSessionParams) { 1713 FiraOpenSessionParams firaParams = (FiraOpenSessionParams) params; 1714 if (firaParams.getRangingRoundUsage() != ROUND_USAGE_OWR_AOA_MEASUREMENT) { 1715 Log.i(TAG, "OwR Aoa UwbSession: Invalid ranging round usage value = " 1716 + firaParams.getRangingRoundUsage()); 1717 return false; 1718 } 1719 if (firaParams.getDeviceRole() != RANGING_DEVICE_ROLE_OBSERVER) { 1720 Log.i(TAG, "OwR Aoa UwbSession: Invalid device role value = " 1721 + firaParams.getDeviceRole()); 1722 return false; 1723 } 1724 return true; 1725 } 1726 return false; 1727 } 1728 isValidUwbSessionForApplicationDataTransfer(UwbSession uwbSession)1729 private boolean isValidUwbSessionForApplicationDataTransfer(UwbSession uwbSession) { 1730 // The session state must be SESSION_STATE_ACTIVE, as that's required to transmit or receive 1731 // application data. 1732 return uwbSession != null && uwbSession.getSessionState() == UWB_SESSION_STATE_ACTIVE; 1733 } 1734 isValidSendDataInfo(SendDataInfo sendDataInfo)1735 private boolean isValidSendDataInfo(SendDataInfo sendDataInfo) { 1736 if (sendDataInfo.data == null) { 1737 return false; 1738 } 1739 1740 if (sendDataInfo.remoteDeviceAddress == null) { 1741 return false; 1742 } 1743 1744 if (sendDataInfo.remoteDeviceAddress.size() 1745 > UwbUciConstants.UWB_DEVICE_EXT_MAC_ADDRESS_LEN) { 1746 return false; 1747 } 1748 return true; 1749 } 1750 1751 /** Represents a UWB session */ 1752 public class UwbSession implements IBinder.DeathRecipient, Closeable { 1753 @VisibleForTesting 1754 public static final long RANGING_RESULT_ERROR_NO_TIMEOUT = 0; 1755 private static final String RANGING_RESULT_ERROR_STREAK_TIMER_TAG = 1756 "UwbSessionRangingResultError"; 1757 private static final long NON_PRIVILEGED_BG_APP_TIMEOUT_MS = 120_000; 1758 @VisibleForTesting 1759 public static final String NON_PRIVILEGED_BG_APP_TIMER_TAG = 1760 "UwbSessionNonPrivilegedBgAppError"; 1761 @VisibleForTesting 1762 static final int CCC_SESSION_PRIORITY = 80; 1763 @VisibleForTesting 1764 static final int SYSTEM_APP_SESSION_PRIORITY = 70; 1765 @VisibleForTesting 1766 static final int FG_SESSION_PRIORITY = 60; 1767 // Default session priority value needs to be different from other session priority buckets, 1768 // so we can detect overrides from the shell or System API. 1769 @VisibleForTesting 1770 static final int DEFAULT_SESSION_PRIORITY = 50; 1771 @VisibleForTesting 1772 static final int BG_SESSION_PRIORITY = 40; 1773 1774 private final AttributionSource mAttributionSource; 1775 private final SessionHandle mSessionHandle; 1776 private final int mSessionId; 1777 private final byte mSessionType; 1778 private final int mRangingRoundUsage; 1779 private final IUwbRangingCallbacks mIUwbRangingCallbacks; 1780 private final String mProtocolName; 1781 private final IBinder mIBinder; 1782 private final WaitObj mWaitObj; 1783 private boolean mAcquiredDefaultPose = false; 1784 private Params mParams; 1785 private int mSessionState; 1786 // Session priority as tracked by the UWB stack that changes based on the requesting 1787 // app/service bg/fg state changes. Note, it will differ from the Fira SESSION_PRIORITY 1788 // param given to UWBS if the state changed after the session became active. 1789 private int mStackSessionPriority; 1790 private boolean mSessionPriorityOverride = false; 1791 private boolean mNeedsAppConfigUpdate = false; 1792 private UwbMulticastListUpdateStatus mMulticastListUpdateStatus; 1793 private final int mProfileType; 1794 private AlarmManager.OnAlarmListener mRangingResultErrorStreakTimerListener; 1795 private AlarmManager.OnAlarmListener mNonPrivilegedBgAppTimerListener; 1796 private int mOperationType = OPERATION_TYPE_INIT_SESSION; 1797 private final String mChipId; 1798 private boolean mHasNonPrivilegedFgApp = false; 1799 private long mRangingErrorStreakTimeoutMs = RANGING_RESULT_ERROR_NO_TIMEOUT; 1800 // Use a Map<RemoteMacAddress, SortedMap<SequenceNumber, ReceivedDataInfo>> to store all 1801 // the Application payload data packets received in this (active) UWB Session. 1802 // - The outer key (RemoteMacAddress) is used to identify the Advertiser device that sends 1803 // the data (there can be multiple advertisers in the same UWB session). 1804 // - The inner key (SequenceNumber) is used to ensure we don't store duplicate packets, 1805 // and notify them to the higher layers in-order. 1806 // TODO(b/270068278): Change the type of SequenceNumber from Long to Integer everywhere. 1807 private final ConcurrentHashMap<Long, SortedMap<Long, ReceivedDataInfo>> 1808 mReceivedDataInfoMap; 1809 private IPoseSource mPoseSource; 1810 1811 // Store the UCI sequence number for the next Data packet (to be sent to UWBS). 1812 private byte mDataSndSequenceNumber; 1813 // Store a Map<SequenceNumber, SendDataInfo>, for every Data packet (sent to UWBS). It's 1814 // used when the corresponding DataTransferStatusNtf is received (from UWBS). 1815 private final ConcurrentHashMap<Long, SendDataInfo> mSendDataInfoMap; 1816 1817 @VisibleForTesting 1818 public List<UwbControlee> mControleeList; 1819 UwbSession(AttributionSource attributionSource, SessionHandle sessionHandle, int sessionId, byte sessionType, String protocolName, Params params, IUwbRangingCallbacks iUwbRangingCallbacks, String chipId)1820 UwbSession(AttributionSource attributionSource, SessionHandle sessionHandle, int sessionId, 1821 byte sessionType, String protocolName, Params params, 1822 IUwbRangingCallbacks iUwbRangingCallbacks, String chipId) { 1823 this.mAttributionSource = attributionSource; 1824 this.mSessionHandle = sessionHandle; 1825 this.mSessionId = sessionId; 1826 this.mSessionType = sessionType; 1827 this.mProtocolName = protocolName; 1828 this.mIUwbRangingCallbacks = iUwbRangingCallbacks; 1829 this.mIBinder = iUwbRangingCallbacks.asBinder(); 1830 this.mSessionState = UwbUciConstants.UWB_SESSION_STATE_DEINIT; 1831 this.mStackSessionPriority = calculateSessionPriority(); 1832 this.mParams = params; 1833 this.mWaitObj = new WaitObj(); 1834 this.mProfileType = convertProtolNameToProfileType(protocolName); 1835 this.mChipId = chipId; 1836 1837 if (params instanceof FiraOpenSessionParams) { 1838 FiraOpenSessionParams firaParams = (FiraOpenSessionParams) params; 1839 1840 this.mRangingRoundUsage = firaParams.getRangingRoundUsage(); 1841 1842 // Set up pose sources before we start creating UwbControlees. 1843 switch (firaParams.getFilterType()) { 1844 case FILTER_TYPE_DEFAULT: 1845 this.mPoseSource = mUwbInjector.acquirePoseSource(); 1846 this.mAcquiredDefaultPose = true; 1847 break; 1848 case FILTER_TYPE_APPLICATION: 1849 this.mPoseSource = new ApplicationPoseSource(); 1850 break; 1851 } 1852 1853 if (firaParams.getDestAddressList() != null) { 1854 // Set up list of all controlees involved. 1855 mControleeList = firaParams.getDestAddressList().stream() 1856 .map(addr -> new UwbControlee(addr, createFilterEngine(), mUwbInjector)) 1857 .collect(Collectors.toList()); 1858 } 1859 mRangingErrorStreakTimeoutMs = firaParams 1860 .getRangingErrorStreakTimeoutMs(); 1861 1862 // Add stack calculated session priority to Fira open session params. The stack 1863 // session priority might change later based on fg/bg state changes, but the 1864 // SESSION_PRIORITY given to the UWBS on open session will stay the same since 1865 // UWBS doesn't support reconfiguring session priority while the session is active. 1866 // In case the session stops being active, session priority will update on next 1867 // start ranging call. 1868 if (firaParams.getSessionPriority() != DEFAULT_SESSION_PRIORITY) { 1869 mSessionPriorityOverride = true; 1870 mStackSessionPriority = firaParams.getSessionPriority(); 1871 } else { 1872 mParams = firaParams.toBuilder().setSessionPriority( 1873 mStackSessionPriority).build(); 1874 } 1875 } else { 1876 this.mRangingRoundUsage = -1; 1877 } 1878 1879 this.mReceivedDataInfoMap = new ConcurrentHashMap<>(); 1880 this.mDataSndSequenceNumber = 0; 1881 this.mSendDataInfoMap = new ConcurrentHashMap<>(); 1882 } 1883 1884 /** 1885 * Calculates the priority of the session based on the protocol type and the originating 1886 * app/service requesting the session. 1887 * 1888 * Session priority ranking order (from highest to lowest priority): 1889 * 1. Any CCC session 1890 * 2. Any System app/service 1891 * 3. Other apps/services running in Foreground 1892 * 4. Other apps/services running in Background 1893 */ calculateSessionPriority()1894 public int calculateSessionPriority() { 1895 if (mProtocolName.equals(CccParams.PROTOCOL_NAME)) { 1896 return CCC_SESSION_PRIORITY; 1897 } 1898 AttributionSource nonPrivilegedAppAttrSource = 1899 this.getAnyNonPrivilegedAppInAttributionSource(); 1900 if (nonPrivilegedAppAttrSource == null) { 1901 return SYSTEM_APP_SESSION_PRIORITY; 1902 } 1903 long identity = Binder.clearCallingIdentity(); 1904 boolean isFgAppOrService = mUwbInjector.isForegroundAppOrService( 1905 nonPrivilegedAppAttrSource.getUid(), 1906 nonPrivilegedAppAttrSource.getPackageName()); 1907 Binder.restoreCallingIdentity(identity); 1908 if (isFgAppOrService) { 1909 return FG_SESSION_PRIORITY; 1910 } 1911 return BG_SESSION_PRIORITY; 1912 } 1913 isPrivilegedApp(int uid, String packageName)1914 private boolean isPrivilegedApp(int uid, String packageName) { 1915 return mUwbInjector.isSystemApp(uid, packageName) 1916 || mUwbInjector.isAppSignedWithPlatformKey(uid); 1917 } 1918 1919 /** 1920 * Check the attribution source chain to check if there are any 3p apps. 1921 * @return AttributionSource of first non-system app found in the chain, null otherwise. 1922 */ 1923 @Nullable getAnyNonPrivilegedAppInAttributionSource()1924 public AttributionSource getAnyNonPrivilegedAppInAttributionSource() { 1925 // Iterate attribution source chain to ensure that there is no non-fg 3p app in the 1926 // request. 1927 AttributionSource attributionSource = mAttributionSource; 1928 while (attributionSource != null) { 1929 int uid = attributionSource.getUid(); 1930 String packageName = attributionSource.getPackageName(); 1931 if (!isPrivilegedApp(uid, packageName)) { 1932 return attributionSource; 1933 } 1934 attributionSource = attributionSource.getNext(); 1935 } 1936 return null; 1937 } 1938 1939 /** 1940 * Gets the list of controlees active under this session. 1941 */ getControleeList()1942 public List<UwbControlee> getControleeList() { 1943 return Collections.unmodifiableList(mControleeList); 1944 } 1945 1946 /** 1947 * Store a ReceivedDataInfo for the UwbSession. If we already have stored data from the 1948 * same advertiser and with the same sequence number, this is a no-op. 1949 */ addReceivedDataInfo(ReceivedDataInfo receivedDataInfo)1950 public void addReceivedDataInfo(ReceivedDataInfo receivedDataInfo) { 1951 SortedMap<Long, ReceivedDataInfo> innerMap = mReceivedDataInfoMap.get( 1952 receivedDataInfo.address); 1953 if (innerMap == null) { 1954 innerMap = new TreeMap<>(); 1955 mReceivedDataInfoMap.put(receivedDataInfo.address, innerMap); 1956 } 1957 1958 // Check if the sorted InnerMap has reached the max number of Rx packets we want to 1959 // store; if so we drop the smallest (sequence number) packet between the new received 1960 // packet and the stored packets. 1961 int maxRxPacketsToStore = 1962 mUwbInjector.getDeviceConfigFacade().getRxDataMaxPacketsToStore(); 1963 if (innerMap.size() < maxRxPacketsToStore) { 1964 innerMap.putIfAbsent(receivedDataInfo.sequenceNum, receivedDataInfo); 1965 } else if (innerMap.size() == maxRxPacketsToStore) { 1966 Long smallestStoredSequenceNumber = innerMap.firstKey(); 1967 if (smallestStoredSequenceNumber < receivedDataInfo.sequenceNum 1968 && !innerMap.containsKey(receivedDataInfo.sequenceNum)) { 1969 innerMap.remove(smallestStoredSequenceNumber); 1970 innerMap.putIfAbsent(receivedDataInfo.sequenceNum, receivedDataInfo); 1971 } 1972 } 1973 } 1974 1975 /** 1976 * Return all the ReceivedDataInfo from the given remote device, in sequence number order. 1977 * This method also removes the returned packets from the Map, so the same packet will 1978 * not be returned again (in a future call). 1979 */ getAllReceivedDataInfo(long macAddress)1980 public List<ReceivedDataInfo> getAllReceivedDataInfo(long macAddress) { 1981 SortedMap<Long, ReceivedDataInfo> innerMap = mReceivedDataInfoMap.get(macAddress); 1982 if (innerMap == null) { 1983 // No stored ReceivedDataInfo(s) for the address. 1984 return List.of(); 1985 } 1986 1987 List<ReceivedDataInfo> receivedDataInfoList = new ArrayList<>(innerMap.values()); 1988 innerMap.clear(); 1989 return receivedDataInfoList; 1990 } 1991 clearReceivedDataInfo()1992 private void clearReceivedDataInfo() { 1993 for (long macAddress : getRemoteMacAddressList()) { 1994 SortedMap<Long, ReceivedDataInfo> innerMap = mReceivedDataInfoMap.get(macAddress); 1995 innerMap.clear(); 1996 } 1997 mReceivedDataInfoMap.clear(); 1998 } 1999 2000 /** 2001 * Get (and increment) the UCI sequence number for the next Data packet to be sent to UWBS. 2002 */ getAndIncrementDataSndSequenceNumber()2003 public byte getAndIncrementDataSndSequenceNumber() { 2004 return mDataSndSequenceNumber++; 2005 } 2006 2007 /** 2008 * Store a SendDataInfo for a UCI Data packet sent to UWBS. 2009 */ addSendDataInfo(long sequenceNumber, SendDataInfo sendDataInfo)2010 public void addSendDataInfo(long sequenceNumber, SendDataInfo sendDataInfo) { 2011 mSendDataInfoMap.put(sequenceNumber, sendDataInfo); 2012 } 2013 2014 /** 2015 * Remove the SendDataInfo for a UCI packet from the current UWB Session. 2016 */ removeSendDataInfo(long sequenceNumber)2017 public void removeSendDataInfo(long sequenceNumber) { 2018 mSendDataInfoMap.remove(sequenceNumber); 2019 } 2020 2021 /** 2022 * Get the SendDataInfo for a UCI packet from the current UWB Session. 2023 */ 2024 @Nullable getSendDataInfo(long sequenceNumber)2025 public SendDataInfo getSendDataInfo(long sequenceNumber) { 2026 return mSendDataInfoMap.get(sequenceNumber); 2027 } 2028 2029 /** 2030 * Adds a Controlee to the session. This should only be called to reflect 2031 * the state of the native UWB interface. 2032 * @param address The UWB address of the Controlee to add. 2033 */ addControlee(UwbAddress address)2034 public void addControlee(UwbAddress address) { 2035 if (mControleeList != null 2036 && mControleeList.stream().noneMatch(e -> e.getUwbAddress().equals(address))) { 2037 mControleeList.add(new UwbControlee(address, createFilterEngine(), mUwbInjector)); 2038 } 2039 } 2040 2041 /** 2042 * Fetches a {@link UwbControlee} object by {@link UwbAddress}. 2043 * @param address The UWB address of the Controlee to find. 2044 * @return The matching {@link UwbControlee}, or null if not found. 2045 */ getControlee(UwbAddress address)2046 public UwbControlee getControlee(UwbAddress address) { 2047 UwbControlee result = mControleeList 2048 .stream() 2049 .filter(e -> e.getUwbAddress().equals(address)) 2050 .findFirst() 2051 .orElse(null); 2052 if (result == null) { 2053 Log.d(TAG, "Failure to find controlee " + address); 2054 } 2055 return result; 2056 } 2057 2058 /** 2059 * Removes a Controlee from the session. This should only be called to reflect 2060 * the state of the native UWB interface. 2061 * @param address The UWB address of the Controlee to remove. 2062 */ removeControlee(UwbAddress address)2063 public void removeControlee(UwbAddress address) { 2064 if (mControleeList != null) { 2065 for (UwbControlee controlee : mControleeList) { 2066 if (controlee.getUwbAddress().equals(address)) { 2067 controlee.close(); 2068 mControleeList.remove(controlee); 2069 break; 2070 } 2071 } 2072 } 2073 } 2074 getAttributionSource()2075 public AttributionSource getAttributionSource() { 2076 return this.mAttributionSource; 2077 } 2078 getSessionId()2079 public int getSessionId() { 2080 return this.mSessionId; 2081 } 2082 getSessionType()2083 public byte getSessionType() { 2084 return this.mSessionType; 2085 } 2086 getRangingRoundUsage()2087 public int getRangingRoundUsage() { 2088 return this.mRangingRoundUsage; 2089 } 2090 getChipId()2091 public String getChipId() { 2092 return this.mChipId; 2093 } 2094 getSessionHandle()2095 public SessionHandle getSessionHandle() { 2096 return this.mSessionHandle; 2097 } 2098 getParams()2099 public Params getParams() { 2100 return this.mParams; 2101 } 2102 updateCccParamsOnStart(CccStartRangingParams rangingStartParams)2103 public void updateCccParamsOnStart(CccStartRangingParams rangingStartParams) { 2104 // Need to update the RAN multiplier and initiation time 2105 // from the CccStartRangingParams for CCC session. 2106 CccOpenRangingParams newParams = 2107 new CccOpenRangingParams.Builder((CccOpenRangingParams) mParams) 2108 .setRanMultiplier(rangingStartParams.getRanMultiplier()) 2109 .setInitiationTimeMs(rangingStartParams.getInitiationTimeMs()) 2110 .build(); 2111 this.mParams = newParams; 2112 this.mNeedsAppConfigUpdate = true; 2113 } 2114 2115 /** 2116 * Checks if session priority of the current session changed from the initial value, if so 2117 * updates the session priority param and marks session for needed app config update. 2118 */ updateFiraParamsOnStartIfChanged()2119 public void updateFiraParamsOnStartIfChanged() { 2120 // Need to check if session priority changed and update if it did 2121 FiraOpenSessionParams firaOpenSessionParams = (FiraOpenSessionParams) mParams; 2122 if (mStackSessionPriority != firaOpenSessionParams.getSessionPriority()) { 2123 this.mParams = ((FiraOpenSessionParams) mParams).toBuilder().setSessionPriority( 2124 mStackSessionPriority).build(); 2125 this.mNeedsAppConfigUpdate = true; 2126 } 2127 } 2128 updateFiraParamsOnReconfigure(FiraRangingReconfigureParams reconfigureParams)2129 public void updateFiraParamsOnReconfigure(FiraRangingReconfigureParams reconfigureParams) { 2130 // Need to update the reconfigure params from the FiraRangingReconfigureParams for 2131 // FiRa session. 2132 FiraOpenSessionParams.Builder newParamsBuilder = 2133 new FiraOpenSessionParams.Builder((FiraOpenSessionParams) mParams); 2134 if (reconfigureParams.getBlockStrideLength() != null) { 2135 newParamsBuilder.setBlockStrideLength(reconfigureParams.getBlockStrideLength()); 2136 } 2137 if (reconfigureParams.getRangeDataNtfConfig() != null) { 2138 newParamsBuilder.setRangeDataNtfConfig(reconfigureParams.getRangeDataNtfConfig()); 2139 } 2140 if (reconfigureParams.getRangeDataProximityNear() != null) { 2141 newParamsBuilder.setRangeDataNtfProximityNear( 2142 reconfigureParams.getRangeDataProximityNear()); 2143 } 2144 if (reconfigureParams.getRangeDataProximityFar() != null) { 2145 newParamsBuilder.setRangeDataNtfProximityFar( 2146 reconfigureParams.getRangeDataProximityFar()); 2147 } 2148 if (reconfigureParams.getRangeDataAoaAzimuthLower() != null) { 2149 newParamsBuilder.setRangeDataNtfAoaAzimuthLower( 2150 reconfigureParams.getRangeDataAoaAzimuthLower()); 2151 } 2152 if (reconfigureParams.getRangeDataAoaAzimuthUpper() != null) { 2153 newParamsBuilder.setRangeDataNtfAoaAzimuthUpper( 2154 reconfigureParams.getRangeDataAoaAzimuthUpper()); 2155 } 2156 if (reconfigureParams.getRangeDataAoaElevationLower() != null) { 2157 newParamsBuilder.setRangeDataNtfAoaElevationLower( 2158 reconfigureParams.getRangeDataAoaElevationLower()); 2159 } 2160 if (reconfigureParams.getRangeDataAoaElevationUpper() != null) { 2161 newParamsBuilder.setRangeDataNtfAoaElevationUpper( 2162 reconfigureParams.getRangeDataAoaElevationUpper()); 2163 } 2164 this.mParams = newParamsBuilder.build(); 2165 } 2166 getCurrentFiraRangingIntervalMs()2167 public int getCurrentFiraRangingIntervalMs() { 2168 FiraOpenSessionParams firaOpenSessionParams = (FiraOpenSessionParams) mParams; 2169 return firaOpenSessionParams.getRangingIntervalMs() 2170 * (firaOpenSessionParams.getBlockStrideLength() + 1); 2171 } 2172 getProtocolName()2173 public String getProtocolName() { 2174 return this.mProtocolName; 2175 } 2176 getIUwbRangingCallbacks()2177 public IUwbRangingCallbacks getIUwbRangingCallbacks() { 2178 return this.mIUwbRangingCallbacks; 2179 } 2180 getSessionState()2181 public int getSessionState() { 2182 return this.mSessionState; 2183 } 2184 setSessionState(int state)2185 public void setSessionState(int state) { 2186 this.mSessionState = state; 2187 } 2188 getStackSessionPriority()2189 public int getStackSessionPriority() { 2190 return this.mStackSessionPriority; 2191 } 2192 setStackSessionPriority(int priority)2193 public void setStackSessionPriority(int priority) { 2194 this.mStackSessionPriority = priority; 2195 } 2196 getNeedsAppConfigUpdate()2197 public boolean getNeedsAppConfigUpdate() { 2198 return this.mNeedsAppConfigUpdate; 2199 } 2200 2201 /** Reset the needsAppConfigUpdate flag to false. */ resetNeedsAppConfigUpdate()2202 public void resetNeedsAppConfigUpdate() { 2203 this.mNeedsAppConfigUpdate = false; 2204 } 2205 getRemoteMacAddressList()2206 public Set<Long> getRemoteMacAddressList() { 2207 return mReceivedDataInfoMap.keySet(); 2208 } 2209 setMulticastListUpdateStatus( UwbMulticastListUpdateStatus multicastListUpdateStatus)2210 public void setMulticastListUpdateStatus( 2211 UwbMulticastListUpdateStatus multicastListUpdateStatus) { 2212 mMulticastListUpdateStatus = multicastListUpdateStatus; 2213 } 2214 getMulticastListUpdateStatus()2215 public UwbMulticastListUpdateStatus getMulticastListUpdateStatus() { 2216 return mMulticastListUpdateStatus; 2217 } 2218 convertProtolNameToProfileType(String protocolName)2219 private int convertProtolNameToProfileType(String protocolName) { 2220 if (protocolName.equals(FiraParams.PROTOCOL_NAME)) { 2221 return UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__FIRA; 2222 } else if (protocolName.equals(CccParams.PROTOCOL_NAME)) { 2223 return UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__CCC; 2224 } else { 2225 return UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__CUSTOMIZED; 2226 } 2227 } 2228 getProfileType()2229 public int getProfileType() { 2230 return mProfileType; 2231 } 2232 getParallelSessionCount()2233 public int getParallelSessionCount() { 2234 if (mSessionTable.containsKey(mSessionHandle)) { 2235 return getSessionCount() - 1; 2236 } 2237 return getSessionCount(); 2238 } 2239 getBinder()2240 public IBinder getBinder() { 2241 return mIBinder; 2242 } 2243 getWaitObj()2244 public WaitObj getWaitObj() { 2245 return mWaitObj; 2246 } 2247 hasNonPrivilegedFgApp()2248 public boolean hasNonPrivilegedFgApp() { 2249 return mHasNonPrivilegedFgApp; 2250 } 2251 setHasNonPrivilegedFgApp(boolean hasNonPrivilegedFgApp)2252 public void setHasNonPrivilegedFgApp(boolean hasNonPrivilegedFgApp) { 2253 mHasNonPrivilegedFgApp = hasNonPrivilegedFgApp; 2254 } 2255 2256 /** 2257 * Starts a timer to detect if the error streak is longer than 2258 * {@link UwbSession#mRangingErrorStreakTimeoutMs }. 2259 */ startRangingResultErrorStreakTimerIfNotSet()2260 public void startRangingResultErrorStreakTimerIfNotSet() { 2261 // Start a timer on first failure to detect continuous failures. 2262 if (mRangingResultErrorStreakTimerListener == null) { 2263 mRangingResultErrorStreakTimerListener = () -> { 2264 Log.w(TAG, "Continuous errors or no ranging results detected for " 2265 + mRangingErrorStreakTimeoutMs + " ms." 2266 + " Stopping session"); 2267 stopRangingInternal(mSessionHandle, true /* triggeredBySystemPolicy */); 2268 }; 2269 Log.v(TAG, "Starting error timer for " 2270 + mRangingErrorStreakTimeoutMs + " ms."); 2271 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2272 mUwbInjector.getElapsedSinceBootMillis() 2273 + mRangingErrorStreakTimeoutMs, 2274 RANGING_RESULT_ERROR_STREAK_TIMER_TAG, 2275 mRangingResultErrorStreakTimerListener, mEventTask); 2276 } 2277 } 2278 stopRangingResultErrorStreakTimerIfSet()2279 public void stopRangingResultErrorStreakTimerIfSet() { 2280 // Cancel error streak timer on any success. 2281 if (mRangingResultErrorStreakTimerListener != null) { 2282 mAlarmManager.cancel(mRangingResultErrorStreakTimerListener); 2283 mRangingResultErrorStreakTimerListener = null; 2284 } 2285 } 2286 2287 /** 2288 * Starts a timer to detect if the app that started the UWB session is in the background 2289 * for longer than {@link UwbSession#NON_PRIVILEGED_BG_APP_TIMEOUT_MS}. 2290 */ startNonPrivilegedBgAppTimerIfNotSet()2291 private void startNonPrivilegedBgAppTimerIfNotSet() { 2292 // Start a timer when the non-privileged app goes into the background. 2293 if (mNonPrivilegedBgAppTimerListener == null) { 2294 mNonPrivilegedBgAppTimerListener = () -> { 2295 Log.w(TAG, "Non-privileged app in background for longer than timeout - " 2296 + " Stopping session"); 2297 stopRangingInternal(mSessionHandle, true /* triggeredBySystemPolicy */); 2298 }; 2299 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2300 mUwbInjector.getElapsedSinceBootMillis() 2301 + NON_PRIVILEGED_BG_APP_TIMEOUT_MS, 2302 NON_PRIVILEGED_BG_APP_TIMER_TAG, 2303 mNonPrivilegedBgAppTimerListener, mEventTask); 2304 } 2305 } 2306 stopNonPrivilegedBgAppTimerIfSet()2307 private void stopNonPrivilegedBgAppTimerIfSet() { 2308 // Stop the timer when the non-privileged app goes into the foreground. 2309 if (mNonPrivilegedBgAppTimerListener != null) { 2310 mAlarmManager.cancel(mNonPrivilegedBgAppTimerListener); 2311 mNonPrivilegedBgAppTimerListener = null; 2312 } 2313 } 2314 stopTimers()2315 private void stopTimers() { 2316 // Reset any stored error streak or non-privileged background app timestamps. 2317 stopRangingResultErrorStreakTimerIfSet(); 2318 stopNonPrivilegedBgAppTimerIfSet(); 2319 } 2320 reconfigureFiraSessionOnFgStateChange()2321 public void reconfigureFiraSessionOnFgStateChange() { 2322 // Reconfigure the session to change notification control when the app transitions 2323 // from fg to bg and vice versa. 2324 FiraRangingReconfigureParams.Builder builder = 2325 new FiraRangingReconfigureParams.Builder(); 2326 // If app is in fg, use the configured ntf control, else disable. 2327 if (mHasNonPrivilegedFgApp) { 2328 FiraOpenSessionParams params = (FiraOpenSessionParams) mParams; 2329 builder.setRangeDataNtfConfig(params.getRangeDataNtfConfig()) 2330 .setRangeDataProximityNear(params.getRangeDataNtfProximityNear()) 2331 .setRangeDataProximityFar(params.getRangeDataNtfProximityFar()); 2332 } else { 2333 builder.setRangeDataNtfConfig(FiraParams.RANGE_DATA_NTF_CONFIG_DISABLE); 2334 } 2335 FiraRangingReconfigureParams reconfigureParams = builder.build(); 2336 reconfigureInternal( 2337 mSessionHandle, reconfigureParams, true /* triggeredByFgStateChange */); 2338 2339 if (!mUwbInjector.getDeviceConfigFacade().isBackgroundRangingEnabled()) { 2340 Log.d(TAG, "reconfigureFiraSessionOnFgStateChange - System policy disallows for " 2341 + "non fg 3p apps"); 2342 // When a non-privileged app goes into the background, start a timer (that will stop 2343 // the ranging session). If the app goes back into the foreground, the timer will 2344 // get reset (but any stopped UWB session will not be auto-resumed). 2345 if (!mHasNonPrivilegedFgApp) { 2346 startNonPrivilegedBgAppTimerIfNotSet(); 2347 } else { 2348 stopNonPrivilegedBgAppTimerIfSet(); 2349 } 2350 } else { 2351 Log.d(TAG, "reconfigureFiraSessionOnFgStateChange - System policy allows for " 2352 + "non fg 3p apps"); 2353 } 2354 } 2355 getOperationType()2356 public int getOperationType() { 2357 return mOperationType; 2358 } 2359 setOperationType(int type)2360 public void setOperationType(int type) { 2361 mOperationType = type; 2362 } 2363 2364 /** Creates a filter engine based on the device configuration. */ createFilterEngine()2365 public UwbFilterEngine createFilterEngine() { 2366 if (mParams instanceof FiraOpenSessionParams) { 2367 FiraOpenSessionParams firaParams = (FiraOpenSessionParams) mParams; 2368 if (firaParams.getFilterType() == FILTER_TYPE_NONE) { 2369 return null; /* Bail early. App requested no engine. */ 2370 } 2371 } 2372 2373 return mUwbInjector.createFilterEngine(mPoseSource); 2374 } 2375 2376 /** Updates the pose information if an ApplicationPoseSource is being used. */ updatePose(FiraPoseUpdateParams updateParams)2377 public void updatePose(FiraPoseUpdateParams updateParams) { 2378 if (mPoseSource instanceof ApplicationPoseSource) { 2379 ApplicationPoseSource aps = (ApplicationPoseSource) mPoseSource; 2380 aps.applyPose(updateParams.getPoseInfo()); 2381 } else { 2382 throw new IllegalStateException("Session not configured for application poses."); 2383 } 2384 } 2385 2386 @Override binderDied()2387 public void binderDied() { 2388 Log.i(TAG, "binderDied : getSessionId is getSessionId() " + getSessionId()); 2389 2390 synchronized (UwbSessionManager.this) { 2391 int status = mNativeUwbManager.deInitSession(getSessionId(), getChipId()); 2392 mUwbMetrics.logRangingCloseEvent(this, status); 2393 if (status == UwbUciConstants.STATUS_CODE_OK) { 2394 removeSession(this); 2395 Log.i(TAG, 2396 "binderDied : Fira/CCC Session counts currently are " 2397 + getFiraSessionCount() 2398 + "/" + getCccSessionCount()); 2399 } else { 2400 Log.e(TAG, 2401 "binderDied : sessionDeinit Failure because of NativeSessionDeinit " 2402 + "Error"); 2403 } 2404 } 2405 } 2406 2407 /** 2408 * Cleans up resources held by this object. 2409 */ close()2410 public void close() { 2411 if (this.mAcquiredDefaultPose) { 2412 if (mControleeList != null) { 2413 for (UwbControlee controlee : mControleeList) { 2414 controlee.close(); 2415 } 2416 mControleeList.clear(); 2417 } 2418 2419 this.mAcquiredDefaultPose = false; 2420 mUwbInjector.releasePoseSource(); 2421 } 2422 2423 mSendDataInfoMap.clear(); 2424 clearReceivedDataInfo(); 2425 } 2426 2427 /** 2428 * Gets the pose source for this session. This may be the default pose source provided 2429 * by UwbInjector.java when the session was created, or a specialized pose source later 2430 * requested by the application. 2431 */ getPoseSource()2432 public IPoseSource getPoseSource() { 2433 return mPoseSource; 2434 } 2435 2436 @Override toString()2437 public String toString() { 2438 return "UwbSession: { Session Id: " + getSessionId() 2439 + ", Handle: " + getSessionHandle() 2440 + ", Protocol: " + getProtocolName() 2441 + ", State: " + getSessionState() 2442 + ", Data Send Sequence Number: " + mDataSndSequenceNumber 2443 + ", Params: " + getParams() 2444 + ", AttributionSource: " + getAttributionSource() 2445 + " }"; 2446 } 2447 } 2448 2449 // TODO: refactor the async operation flow. 2450 // Wrapper for unit test. 2451 @VisibleForTesting 2452 static class WaitObj { WaitObj()2453 WaitObj() { 2454 } 2455 blockingWait()2456 void blockingWait() throws InterruptedException { 2457 wait(); 2458 } 2459 blockingNotify()2460 void blockingNotify() { 2461 notify(); 2462 } 2463 } 2464 2465 /** 2466 * Dump the UWB session manager debug info 2467 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)2468 public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2469 pw.println("---- Dump of UwbSessionManager ----"); 2470 pw.println("Active sessions: "); 2471 for (UwbSession uwbSession : mSessionTable.values()) { 2472 pw.println(uwbSession); 2473 } 2474 pw.println("Recently closed sessions: "); 2475 for (UwbSession uwbSession: mDbgRecentlyClosedSessions.getEntries()) { 2476 pw.println(uwbSession); 2477 } 2478 List<Integer> nonPrivilegedSessionIds = 2479 mNonPrivilegedUidToFiraSessionsTable.entrySet() 2480 .stream() 2481 .map(e -> e.getValue() 2482 .stream() 2483 .map(UwbSession::getSessionId) 2484 .collect(Collectors.toList())) 2485 .flatMap(Collection::stream) 2486 .collect(Collectors.toList()); 2487 pw.println("Non Privileged Fira Session Ids: " + nonPrivilegedSessionIds); 2488 pw.println("---- Dump of UwbSessionManager ----"); 2489 } 2490 getComputedMacAddress(UwbAddress address)2491 private static byte[] getComputedMacAddress(UwbAddress address) { 2492 if (!SdkLevel.isAtLeastU()) { 2493 return TlvUtil.getReverseBytes(address.toBytes()); 2494 } 2495 return address.toBytes(); 2496 } 2497 } 2498