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 17 package com.android.server.uwb; 18 19 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; 20 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; 21 import static android.uwb.UwbAddress.SHORT_ADDRESS_BYTE_LENGTH; 22 23 import static com.google.uwb.support.ccc.CccParams.CHAPS_PER_SLOT_3; 24 import static com.google.uwb.support.ccc.CccParams.HOPPING_CONFIG_MODE_ADAPTIVE; 25 import static com.google.uwb.support.ccc.CccParams.HOPPING_CONFIG_MODE_CONTINUOUS; 26 import static com.google.uwb.support.ccc.CccParams.HOPPING_SEQUENCE_AES; 27 import static com.google.uwb.support.ccc.CccParams.HOPPING_SEQUENCE_DEFAULT; 28 import static com.google.uwb.support.ccc.CccParams.PULSE_SHAPE_SYMMETRICAL_ROOT_RAISED_COSINE; 29 import static com.google.uwb.support.ccc.CccParams.SLOTS_PER_ROUND_6; 30 import static com.google.uwb.support.ccc.CccParams.UWB_CHANNEL_9; 31 import static com.google.uwb.support.fira.FiraParams.AOA_RESULT_REQUEST_MODE_NO_AOA_REPORT; 32 import static com.google.uwb.support.fira.FiraParams.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS; 33 import static com.google.uwb.support.fira.FiraParams.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS_AZIMUTH_ONLY; 34 import static com.google.uwb.support.fira.FiraParams.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS_ELEVATION_ONLY; 35 import static com.google.uwb.support.fira.FiraParams.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS_INTERLEAVED; 36 import static com.google.uwb.support.fira.FiraParams.BPRF_PHR_DATA_RATE_6M81; 37 import static com.google.uwb.support.fira.FiraParams.BPRF_PHR_DATA_RATE_850K; 38 import static com.google.uwb.support.fira.FiraParams.HOPPING_MODE_DISABLE; 39 import static com.google.uwb.support.fira.FiraParams.MULTICAST_LIST_UPDATE_ACTION_ADD; 40 import static com.google.uwb.support.fira.FiraParams.MULTICAST_LIST_UPDATE_ACTION_DELETE; 41 import static com.google.uwb.support.fira.FiraParams.MULTI_NODE_MODE_MANY_TO_MANY; 42 import static com.google.uwb.support.fira.FiraParams.MULTI_NODE_MODE_ONE_TO_MANY; 43 import static com.google.uwb.support.fira.FiraParams.MULTI_NODE_MODE_UNICAST; 44 import static com.google.uwb.support.fira.FiraParams.PRF_MODE_BPRF; 45 import static com.google.uwb.support.fira.FiraParams.PRF_MODE_HPRF; 46 import static com.google.uwb.support.fira.FiraParams.PSDU_DATA_RATE_27M2; 47 import static com.google.uwb.support.fira.FiraParams.PSDU_DATA_RATE_31M2; 48 import static com.google.uwb.support.fira.FiraParams.PSDU_DATA_RATE_6M81; 49 import static com.google.uwb.support.fira.FiraParams.PSDU_DATA_RATE_7M80; 50 import static com.google.uwb.support.fira.FiraParams.RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY_LEVEL_TRIG; 51 import static com.google.uwb.support.fira.FiraParams.RANGING_DEVICE_DT_TAG; 52 import static com.google.uwb.support.fira.FiraParams.RANGING_DEVICE_ROLE_INITIATOR; 53 import static com.google.uwb.support.fira.FiraParams.RANGING_DEVICE_ROLE_RESPONDER; 54 import static com.google.uwb.support.fira.FiraParams.RANGING_DEVICE_TYPE_CONTROLEE; 55 import static com.google.uwb.support.fira.FiraParams.RANGING_DEVICE_TYPE_CONTROLLER; 56 import static com.google.uwb.support.fira.FiraParams.RANGING_DEVICE_TYPE_DT_TAG; 57 import static com.google.uwb.support.fira.FiraParams.RANGING_ROUND_USAGE_DL_TDOA; 58 import static com.google.uwb.support.fira.FiraParams.RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE; 59 import static com.google.uwb.support.fira.FiraParams.RANGING_ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE; 60 import static com.google.uwb.support.fira.FiraParams.RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE; 61 import static com.google.uwb.support.fira.FiraParams.RANGING_ROUND_USAGE_SS_TWR_NON_DEFERRED_MODE; 62 import static com.google.uwb.support.fira.FiraParams.RFRAME_CONFIG_SP0; 63 import static com.google.uwb.support.fira.FiraParams.RFRAME_CONFIG_SP1; 64 import static com.google.uwb.support.fira.FiraParams.SFD_ID_VALUE_2; 65 import static com.google.uwb.support.fira.FiraParams.STS_CONFIG_PROVISIONED; 66 import static com.google.uwb.support.fira.FiraParams.STS_CONFIG_STATIC; 67 import static com.google.uwb.support.radar.RadarParams.BITS_PER_SAMPLES_32; 68 import static com.google.uwb.support.radar.RadarParams.NUMBER_OF_BURSTS_DEFAULT; 69 import static com.google.uwb.support.radar.RadarParams.PREAMBLE_DURATION_T128_SYMBOLS; 70 import static com.google.uwb.support.radar.RadarParams.RADAR_DATA_TYPE_RADAR_SWEEP_SAMPLES; 71 import static com.google.uwb.support.radar.RadarParams.SAMPLES_PER_SWEEP_DEFAULT; 72 import static com.google.uwb.support.radar.RadarParams.SESSION_PRIORITY_DEFAULT; 73 import static com.google.uwb.support.radar.RadarParams.SWEEP_OFFSET_DEFAULT; 74 75 import static java.util.concurrent.TimeUnit.MILLISECONDS; 76 77 import android.annotation.NonNull; 78 import android.content.AttributionSource; 79 import android.content.Context; 80 import android.content.pm.PackageManager; 81 import android.os.Binder; 82 import android.os.Handler; 83 import android.os.Looper; 84 import android.os.PersistableBundle; 85 import android.os.Process; 86 import android.os.RemoteException; 87 import android.util.ArrayMap; 88 import android.util.Log; 89 import android.util.Pair; 90 import android.uwb.IUwbRangingCallbacks; 91 import android.uwb.RangingReport; 92 import android.uwb.SessionHandle; 93 import android.uwb.UwbAddress; 94 import android.uwb.UwbManager; 95 96 import androidx.annotation.Nullable; 97 98 import com.android.internal.annotations.VisibleForTesting; 99 import com.android.modules.utils.BasicShellCommandHandler; 100 import com.android.server.uwb.jni.NativeUwbManager; 101 import com.android.server.uwb.util.ArrayUtils; 102 103 import com.google.common.io.BaseEncoding; 104 import com.google.uwb.support.base.Params; 105 import com.google.uwb.support.ccc.CccOpenRangingParams; 106 import com.google.uwb.support.ccc.CccParams; 107 import com.google.uwb.support.ccc.CccPulseShapeCombo; 108 import com.google.uwb.support.ccc.CccStartRangingParams; 109 import com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdate; 110 import com.google.uwb.support.fira.FiraOpenSessionParams; 111 import com.google.uwb.support.fira.FiraParams; 112 import com.google.uwb.support.fira.FiraRangingReconfigureParams; 113 import com.google.uwb.support.generic.GenericSpecificationParams; 114 import com.google.uwb.support.radar.RadarOpenSessionParams; 115 116 import java.io.PrintWriter; 117 import java.nio.ByteBuffer; 118 import java.util.ArrayDeque; 119 import java.util.ArrayList; 120 import java.util.Arrays; 121 import java.util.List; 122 import java.util.Map; 123 import java.util.concurrent.CancellationException; 124 import java.util.concurrent.CompletableFuture; 125 import java.util.concurrent.ExecutionException; 126 import java.util.concurrent.FutureTask; 127 import java.util.concurrent.TimeoutException; 128 129 /** 130 * Interprets and executes 'adb shell cmd uwb [args]'. 131 * 132 * To add new commands: 133 * - onCommand: Add a case "<command>" execute. Return a 0 134 * if command executed successfully. 135 * - onHelp: add a description string. 136 * 137 * Permissions: currently root permission is required for some commands. Others will 138 * enforce the corresponding API permissions. 139 */ 140 public class UwbShellCommand extends BasicShellCommandHandler { 141 @VisibleForTesting 142 public static String SHELL_PACKAGE_NAME = "com.android.shell"; 143 private static final long RANGE_CTL_TIMEOUT_MILLIS = 10_000; 144 private static final int RSSI_FLAG = 1; 145 private static final int AOA_FLAG = 1 << 1; 146 private static final int CIR_FLAG = 1 << 2; 147 private static final int SEGMENT_METRICS_FLAG = 1 << 5; 148 private static final int CMD_TIMEOUT_MS = 10_000; 149 150 // These don't require root access. 151 // However, these do perform permission checks in the corresponding UwbService methods. 152 private static final String[] NON_PRIVILEGED_COMMANDS = { 153 "help", 154 "status", 155 "get-country-code", 156 "get-log-mode", 157 "enable-uwb", 158 "disable-uwb", 159 "enable-uwb-hw", 160 "disable-uwb-hw", 161 "simulate-app-state-change", 162 "start-fira-ranging-session", 163 "start-ccc-ranging-session", 164 "start-radar-session", 165 "reconfigure-fira-ranging-session", 166 "get-ranging-session-reports", 167 "get-all-ranging-session-reports", 168 "stop-ranging-session", 169 "stop-radar-session", 170 "stop-all-ranging-sessions", 171 "stop-all-radar-sessions", 172 "get-specification-info", 173 "enable-diagnostics-notification", 174 "disable-diagnostics-notification", 175 "take-bugreport", 176 }; 177 178 @VisibleForTesting 179 public static final FiraOpenSessionParams.Builder DEFAULT_FIRA_OPEN_SESSION_PARAMS = 180 new FiraOpenSessionParams.Builder() 181 .setProtocolVersion(FiraParams.PROTOCOL_VERSION_1_1) 182 .setSessionId(1) 183 .setSessionType(FiraParams.SESSION_TYPE_RANGING) 184 .setChannelNumber(9) 185 .setDeviceType(RANGING_DEVICE_TYPE_CONTROLLER) 186 .setDeviceRole(RANGING_DEVICE_ROLE_INITIATOR) 187 .setDeviceAddress(UwbAddress.fromBytes(new byte[] { 0x4, 0x6})) 188 .setDestAddressList(Arrays.asList(UwbAddress.fromBytes(new byte[] { 0x4, 0x6}))) 189 .setMultiNodeMode(MULTI_NODE_MODE_UNICAST) 190 .setRangingRoundUsage(RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE) 191 .setVendorId(new byte[]{0x8, 0x7}) 192 .setStaticStsIV(new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}); 193 194 @VisibleForTesting 195 public static final CccOpenRangingParams.Builder DEFAULT_CCC_OPEN_RANGING_PARAMS = 196 new CccOpenRangingParams.Builder() 197 .setProtocolVersion(CccParams.PROTOCOL_VERSION_1_0) 198 .setUwbConfig(CccParams.UWB_CONFIG_0) 199 .setPulseShapeCombo( 200 new CccPulseShapeCombo( 201 PULSE_SHAPE_SYMMETRICAL_ROOT_RAISED_COSINE, 202 PULSE_SHAPE_SYMMETRICAL_ROOT_RAISED_COSINE)) 203 .setSessionId(1) 204 .setRanMultiplier(4) 205 .setChannel(UWB_CHANNEL_9) 206 .setNumChapsPerSlot(CHAPS_PER_SLOT_3) 207 .setNumResponderNodes(1) 208 .setNumSlotsPerRound(SLOTS_PER_ROUND_6) 209 .setSyncCodeIndex(1) 210 .setHoppingConfigMode(HOPPING_MODE_DISABLE) 211 .setHoppingSequence(HOPPING_SEQUENCE_DEFAULT); 212 @VisibleForTesting 213 public static final RadarOpenSessionParams.Builder DEFAULT_RADAR_OPEN_SESSION_PARAMS = 214 new RadarOpenSessionParams.Builder() 215 .setSessionId(1) 216 .setBurstPeriod(100) 217 .setSweepPeriod(3000) 218 .setSweepsPerBurst(16) 219 .setSamplesPerSweep(SAMPLES_PER_SWEEP_DEFAULT) 220 .setChannelNumber(FiraParams.UWB_CHANNEL_9) 221 .setSweepOffset(SWEEP_OFFSET_DEFAULT) 222 .setRframeConfig(RFRAME_CONFIG_SP0) 223 .setPreambleDuration(PREAMBLE_DURATION_T128_SYMBOLS) 224 .setPreambleCodeIndex(25) 225 .setSessionPriority(SESSION_PRIORITY_DEFAULT) 226 .setBitsPerSample(BITS_PER_SAMPLES_32) 227 .setPrfMode(PRF_MODE_HPRF) 228 .setNumberOfBursts(NUMBER_OF_BURSTS_DEFAULT) 229 .setRadarDataType(RADAR_DATA_TYPE_RADAR_SWEEP_SAMPLES); 230 231 private static final Map<Integer, SessionInfo> sSessionIdToInfo = new ArrayMap<>(); 232 private static int sSessionHandleIdNext = 0; 233 234 private final UwbInjector mUwbInjector; 235 private final UwbServiceImpl mUwbService; 236 private final UwbServiceCore mUwbServiceCore; 237 private final UwbCountryCode mUwbCountryCode; 238 private final UciLogModeStore mUciLogModeStore; 239 private final NativeUwbManager mNativeUwbManager; 240 private final UwbDiagnostics mUwbDiagnostics; 241 private final DeviceConfigFacade mDeviceConfig; 242 private final Looper mLooper; 243 private final Context mContext; 244 UwbShellCommand(UwbInjector uwbInjector, UwbServiceImpl uwbService, Context context)245 UwbShellCommand(UwbInjector uwbInjector, UwbServiceImpl uwbService, Context context) { 246 mUwbInjector = uwbInjector; 247 mUwbService = uwbService; 248 mContext = context; 249 mUwbCountryCode = uwbInjector.getUwbCountryCode(); 250 mUciLogModeStore = uwbInjector.getUciLogModeStore(); 251 mNativeUwbManager = uwbInjector.getNativeUwbManager(); 252 mUwbServiceCore = uwbInjector.getUwbServiceCore(); 253 mUwbDiagnostics = uwbInjector.getUwbDiagnostics(); 254 mDeviceConfig = uwbInjector.getDeviceConfigFacade(); 255 mLooper = uwbInjector.getUwbServiceLooper(); 256 } 257 bundleToString(@ullable PersistableBundle bundle)258 private static String bundleToString(@Nullable PersistableBundle bundle) { 259 if (bundle != null) { 260 // Need to defuse any local bundles before printing. Use isEmpty() triggers unparcel. 261 bundle.isEmpty(); 262 return bundle.toString(); 263 } else { 264 return "null"; 265 } 266 } 267 268 private static final class UwbRangingCallbacks extends IUwbRangingCallbacks.Stub { 269 private final SessionInfo mSessionInfo; 270 private final PrintWriter mPw; 271 private final CompletableFuture mRangingOpenedFuture; 272 private final CompletableFuture mRangingStartedFuture; 273 private final CompletableFuture mRangingStoppedFuture; 274 private final CompletableFuture mRangingClosedFuture; 275 private final CompletableFuture mRangingReconfiguredFuture; 276 UwbRangingCallbacks(@onNull SessionInfo sessionInfo, @NonNull PrintWriter pw, @NonNull CompletableFuture rangingOpenedFuture, @NonNull CompletableFuture rangingStartedFuture, @NonNull CompletableFuture rangingStoppedFuture, @NonNull CompletableFuture rangingClosedFuture, @NonNull CompletableFuture rangingReconfiguredFuture)277 UwbRangingCallbacks(@NonNull SessionInfo sessionInfo, @NonNull PrintWriter pw, 278 @NonNull CompletableFuture rangingOpenedFuture, 279 @NonNull CompletableFuture rangingStartedFuture, 280 @NonNull CompletableFuture rangingStoppedFuture, 281 @NonNull CompletableFuture rangingClosedFuture, 282 @NonNull CompletableFuture rangingReconfiguredFuture) { 283 mSessionInfo = sessionInfo; 284 mPw = pw; 285 mRangingOpenedFuture = rangingOpenedFuture; 286 mRangingStartedFuture = rangingStartedFuture; 287 mRangingStoppedFuture = rangingStoppedFuture; 288 mRangingClosedFuture = rangingClosedFuture; 289 mRangingReconfiguredFuture = rangingReconfiguredFuture; 290 } 291 onRangingOpened(SessionHandle sessionHandle)292 public void onRangingOpened(SessionHandle sessionHandle) { 293 mPw.println("Ranging session opened"); 294 mRangingOpenedFuture.complete(true); 295 } 296 onRangingOpenFailed(SessionHandle sessionHandle, int reason, PersistableBundle params)297 public void onRangingOpenFailed(SessionHandle sessionHandle, int reason, 298 PersistableBundle params) { 299 mPw.println("Ranging session open failed with reason: " + reason + " and params: " 300 + bundleToString(params)); 301 mRangingOpenedFuture.complete(false); 302 } 303 onRangingStarted(SessionHandle sessionHandle, PersistableBundle params)304 public void onRangingStarted(SessionHandle sessionHandle, PersistableBundle params) { 305 mPw.println("Ranging session started with params: " + bundleToString(params)); 306 mRangingStartedFuture.complete(true); 307 } 308 onRangingStartFailed(SessionHandle sessionHandle, int reason, PersistableBundle params)309 public void onRangingStartFailed(SessionHandle sessionHandle, int reason, 310 PersistableBundle params) { 311 mPw.println("Ranging session start failed with reason: " + reason + " and params: " 312 + bundleToString(params)); 313 mRangingStartedFuture.complete(false); 314 } 315 onRangingReconfigured(SessionHandle sessionHandle, PersistableBundle params)316 public void onRangingReconfigured(SessionHandle sessionHandle, PersistableBundle params) { 317 mPw.println("Ranging reconfigured with params: " + bundleToString(params)); 318 mRangingReconfiguredFuture.complete(true); 319 } 320 onRangingReconfigureFailed(SessionHandle sessionHandle, int reason, PersistableBundle params)321 public void onRangingReconfigureFailed(SessionHandle sessionHandle, int reason, 322 PersistableBundle params) { 323 mPw.println("Ranging reconfigure failed with reason: " + reason + " and params: " 324 + bundleToString(params)); 325 mRangingReconfiguredFuture.complete(true); 326 327 } 328 onRangingStopped(SessionHandle sessionHandle, int reason, PersistableBundle params)329 public void onRangingStopped(SessionHandle sessionHandle, int reason, 330 PersistableBundle params) { 331 mPw.println("Ranging session stopped with reason: " + reason + " and params: " 332 + bundleToString(params)); 333 mRangingStoppedFuture.complete(true); 334 } 335 onRangingStopFailed(SessionHandle sessionHandle, int reason, PersistableBundle params)336 public void onRangingStopFailed(SessionHandle sessionHandle, int reason, 337 PersistableBundle params) { 338 mPw.println("Ranging session stop failed with reason: " + reason + " and params: " 339 + bundleToString(params)); 340 mRangingStoppedFuture.complete(false); 341 } 342 onRangingClosed(SessionHandle sessionHandle, int reason, PersistableBundle params)343 public void onRangingClosed(SessionHandle sessionHandle, int reason, 344 PersistableBundle params) { 345 mPw.println("Ranging session closed with reason: " + reason + " and params: " 346 + bundleToString(params)); 347 sSessionIdToInfo.remove(mSessionInfo.sessionId); 348 mRangingClosedFuture.complete(true); 349 } 350 onRangingResult(SessionHandle sessionHandle, RangingReport rangingReport)351 public void onRangingResult(SessionHandle sessionHandle, RangingReport rangingReport) { 352 mPw.println("Ranging Result: " + rangingReport); 353 mSessionInfo.addRangingReport(rangingReport); 354 } 355 onControleeAdded(SessionHandle sessionHandle, PersistableBundle params)356 public void onControleeAdded(SessionHandle sessionHandle, PersistableBundle params) {} 357 onControleeAddFailed(SessionHandle sessionHandle, int reason, PersistableBundle params)358 public void onControleeAddFailed(SessionHandle sessionHandle, int reason, 359 PersistableBundle params) {} 360 onControleeRemoved(SessionHandle sessionHandle, PersistableBundle params)361 public void onControleeRemoved(SessionHandle sessionHandle, PersistableBundle params) {} 362 onControleeRemoveFailed(SessionHandle sessionHandle, int reason, PersistableBundle params)363 public void onControleeRemoveFailed(SessionHandle sessionHandle, int reason, 364 PersistableBundle params) {} 365 onRangingPaused(SessionHandle sessionHandle, PersistableBundle params)366 public void onRangingPaused(SessionHandle sessionHandle, PersistableBundle params) {} 367 onRangingPauseFailed(SessionHandle sessionHandle, int reason, PersistableBundle params)368 public void onRangingPauseFailed(SessionHandle sessionHandle, int reason, 369 PersistableBundle params) {} 370 onRangingResumed(SessionHandle sessionHandle, PersistableBundle params)371 public void onRangingResumed(SessionHandle sessionHandle, PersistableBundle params) {} 372 onRangingResumeFailed(SessionHandle sessionHandle, int reason, PersistableBundle params)373 public void onRangingResumeFailed(SessionHandle sessionHandle, int reason, 374 PersistableBundle params) {} 375 onDataSent(SessionHandle sessionHandle, UwbAddress uwbAddress, PersistableBundle params)376 public void onDataSent(SessionHandle sessionHandle, UwbAddress uwbAddress, 377 PersistableBundle params) {} 378 onDataSendFailed(SessionHandle sessionHandle, UwbAddress uwbAddress, int reason, PersistableBundle params)379 public void onDataSendFailed(SessionHandle sessionHandle, UwbAddress uwbAddress, int reason, 380 PersistableBundle params) {} 381 onDataTransferPhaseConfigured(SessionHandle sessionHandle, PersistableBundle params)382 public void onDataTransferPhaseConfigured(SessionHandle sessionHandle, 383 PersistableBundle params) { 384 } 385 onDataTransferPhaseConfigFailed(SessionHandle sessionHandle, int reason, PersistableBundle params)386 public void onDataTransferPhaseConfigFailed(SessionHandle sessionHandle, int reason, 387 PersistableBundle params) {} 388 onDataReceived(SessionHandle sessionHandle, UwbAddress uwbAddress, PersistableBundle params, byte[] data)389 public void onDataReceived(SessionHandle sessionHandle, UwbAddress uwbAddress, 390 PersistableBundle params, byte[] data) {} 391 onDataReceiveFailed(SessionHandle sessionHandle, UwbAddress uwbAddress, int reason, PersistableBundle params)392 public void onDataReceiveFailed(SessionHandle sessionHandle, UwbAddress uwbAddress, 393 int reason, PersistableBundle params) {} 394 onServiceDiscovered(SessionHandle sessionHandle, PersistableBundle params)395 public void onServiceDiscovered(SessionHandle sessionHandle, PersistableBundle params) {} 396 onServiceConnected(SessionHandle sessionHandle, PersistableBundle params)397 public void onServiceConnected(SessionHandle sessionHandle, PersistableBundle params) {} 398 onRangingRoundsUpdateDtTagStatus(SessionHandle sessionHandle, PersistableBundle params)399 public void onRangingRoundsUpdateDtTagStatus(SessionHandle sessionHandle, 400 PersistableBundle params) {} 401 onHybridSessionControllerConfigured(SessionHandle sessionHandle, PersistableBundle parameters)402 public void onHybridSessionControllerConfigured(SessionHandle sessionHandle, 403 PersistableBundle parameters) {} 404 onHybridSessionControllerConfigurationFailed(SessionHandle sessionHandle, int reason, PersistableBundle parameters)405 public void onHybridSessionControllerConfigurationFailed(SessionHandle sessionHandle, 406 int reason, PersistableBundle parameters) {} 407 onHybridSessionControleeConfigured(SessionHandle sessionHandle, PersistableBundle parameters)408 public void onHybridSessionControleeConfigured(SessionHandle sessionHandle, 409 PersistableBundle parameters) {} 410 onHybridSessionControleeConfigurationFailed(SessionHandle sessionHandle, int reason, PersistableBundle parameters)411 public void onHybridSessionControleeConfigurationFailed(SessionHandle sessionHandle, 412 int reason, PersistableBundle parameters) {} 413 } 414 415 416 private class SessionInfo { 417 private static final int LAST_NUM_RANGING_REPORTS = 20; 418 419 public final SessionHandle sessionHandle; 420 public final int sessionId; 421 public final Params openRangingParams; 422 public final UwbRangingCallbacks uwbRangingCbs; 423 public final boolean isRadarSession; 424 public final ArrayDeque<RangingReport> lastRangingReports = 425 new ArrayDeque<>(LAST_NUM_RANGING_REPORTS); 426 427 public final CompletableFuture<Boolean> rangingOpenedFuture = new CompletableFuture<>(); 428 public final CompletableFuture<Boolean> rangingStartedFuture = new CompletableFuture<>(); 429 public final CompletableFuture<Boolean> rangingStoppedFuture = new CompletableFuture<>(); 430 public final CompletableFuture<Boolean> rangingClosedFuture = new CompletableFuture<>(); 431 public final CompletableFuture<Boolean> rangingReconfiguredFuture = 432 new CompletableFuture<>(); 433 SessionInfo(int sessionId, SessionHandle sessionHandle, @NonNull Params openRangingParams, @NonNull PrintWriter pw, boolean isRadarSession)434 SessionInfo(int sessionId, SessionHandle sessionHandle, @NonNull Params openRangingParams, 435 @NonNull PrintWriter pw, boolean isRadarSession) { 436 this.sessionId = sessionId; 437 this.sessionHandle = sessionHandle; 438 this.openRangingParams = openRangingParams; 439 this.isRadarSession = isRadarSession; 440 uwbRangingCbs = new UwbRangingCallbacks(this, pw, rangingOpenedFuture, 441 rangingStartedFuture, rangingStoppedFuture, rangingClosedFuture, 442 rangingReconfiguredFuture); 443 } 444 addRangingReport(@onNull RangingReport rangingReport)445 public void addRangingReport(@NonNull RangingReport rangingReport) { 446 if (lastRangingReports.size() == LAST_NUM_RANGING_REPORTS) { 447 lastRangingReports.remove(); 448 } 449 lastRangingReports.add(rangingReport); 450 } 451 } 452 buildFiraOpenSessionParams( GenericSpecificationParams specificationParams)453 private Pair<FiraOpenSessionParams, Boolean> buildFiraOpenSessionParams( 454 GenericSpecificationParams specificationParams) { 455 FiraOpenSessionParams.Builder builder = 456 new FiraOpenSessionParams.Builder(DEFAULT_FIRA_OPEN_SESSION_PARAMS); 457 boolean shouldBlockCall = false; 458 boolean interleavingEnabled = false; 459 boolean aoaResultReqEnabled = false; 460 String option = getNextOption(); 461 while (option != null) { 462 if (option.equals("-b")) { 463 shouldBlockCall = true; 464 } 465 if (option.equals("-i")) { 466 builder.setSessionId(Integer.parseInt(getNextArgRequired())); 467 } 468 if (option.equals("-c")) { 469 builder.setChannelNumber(Integer.parseInt(getNextArgRequired())); 470 } 471 if (option.equals("-t")) { 472 String type = getNextArgRequired(); 473 if (type.equals("controller")) { 474 builder.setDeviceType(RANGING_DEVICE_TYPE_CONTROLLER); 475 } else if (type.equals("controlee")) { 476 builder.setDeviceType(RANGING_DEVICE_TYPE_CONTROLEE); 477 } else { 478 throw new IllegalArgumentException("Unknown device type: " + type); 479 } 480 } 481 if (option.equals("-r")) { 482 String role = getNextArgRequired(); 483 if (role.equals("initiator")) { 484 builder.setDeviceRole(RANGING_DEVICE_ROLE_INITIATOR); 485 } else if (role.equals("responder")) { 486 builder.setDeviceRole(RANGING_DEVICE_ROLE_RESPONDER); 487 } else { 488 throw new IllegalArgumentException("Unknown device role: " + role); 489 } 490 } 491 if (option.equals("-a")) { 492 builder.setDeviceAddress( 493 UwbAddress.fromBytes( 494 ByteBuffer.allocate(SHORT_ADDRESS_BYTE_LENGTH) 495 .putShort(Short.parseShort(getNextArgRequired())) 496 .array())); 497 } 498 if (option.equals("-d")) { 499 String[] destAddressesString = getNextArgRequired().split(","); 500 List<UwbAddress> destAddresses = new ArrayList<>(); 501 for (String destAddressString : destAddressesString) { 502 destAddresses.add(UwbAddress.fromBytes( 503 ByteBuffer.allocate(SHORT_ADDRESS_BYTE_LENGTH) 504 .putShort(Short.parseShort(destAddressString)) 505 .array())); 506 } 507 builder.setDestAddressList(destAddresses); 508 builder.setMultiNodeMode(destAddresses.size() > 1 509 ? MULTI_NODE_MODE_ONE_TO_MANY 510 : MULTI_NODE_MODE_UNICAST); 511 } 512 if (option.equals("-m")) { 513 String mode = getNextArgRequired(); 514 if (mode.equals("unicast")) { 515 builder.setMultiNodeMode(MULTI_NODE_MODE_UNICAST); 516 } else if (mode.equals("one-to-many")) { 517 builder.setMultiNodeMode(MULTI_NODE_MODE_ONE_TO_MANY); 518 } else if (mode.equals("many-to-many")) { 519 builder.setMultiNodeMode(MULTI_NODE_MODE_MANY_TO_MANY); 520 } else { 521 throw new IllegalArgumentException("Unknown multi-node mode: " + mode); 522 } 523 } 524 if (option.equals("-u")) { 525 String usage = getNextArgRequired(); 526 if (usage.equals("ds-twr")) { 527 builder.setRangingRoundUsage(RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE); 528 } else if (usage.equals("ss-twr")) { 529 builder.setRangingRoundUsage(RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE); 530 } else if (usage.equals("ds-twr-non-deferred")) { 531 builder.setRangingRoundUsage(RANGING_ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE); 532 } else if (usage.equals("ss-twr-non-deferred")) { 533 builder.setRangingRoundUsage(RANGING_ROUND_USAGE_SS_TWR_NON_DEFERRED_MODE); 534 } else { 535 throw new IllegalArgumentException("Unknown round usage: " + usage); 536 } 537 } 538 if (option.equals("-l")) { 539 builder.setRangingIntervalMs(Integer.parseInt(getNextArgRequired())); 540 } 541 if (option.equals("-s")) { 542 builder.setSlotsPerRangingRound(Integer.parseInt(getNextArgRequired())); 543 } 544 if (option.equals("-x")) { 545 String[] rangeDataNtfProximityString = getNextArgRequired().split(","); 546 if (rangeDataNtfProximityString.length != 2) { 547 throw new IllegalArgumentException("Unexpected range data ntf proximity range:" 548 + Arrays.toString(rangeDataNtfProximityString) 549 + " expected to be <proximity-near-cm, proximity-far-cm>"); 550 } 551 int rangeDataNtfProximityNearCm = Integer.parseInt(rangeDataNtfProximityString[0]); 552 int rangeDataNtfProximityFarCm = Integer.parseInt(rangeDataNtfProximityString[1]); 553 // Enable range data ntf while inside proximity range 554 builder.setRangeDataNtfConfig(RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY_LEVEL_TRIG); 555 builder.setRangeDataNtfProximityNear(rangeDataNtfProximityNearCm); 556 builder.setRangeDataNtfProximityFar(rangeDataNtfProximityFarCm); 557 } 558 if (option.equals("-R")) { 559 // enable / disable range data NTFs 560 // range-data-notification 561 String range_data_ntf = getNextArgRequired(); 562 if (range_data_ntf.equals("disabled")) { 563 builder.setRangeDataNtfConfig(FiraParams.RANGE_DATA_NTF_CONFIG_DISABLE); 564 } else if (range_data_ntf.equals("enabled")) { 565 builder.setRangeDataNtfConfig(FiraParams.RANGE_DATA_NTF_CONFIG_ENABLE); 566 } else { 567 throw new IllegalArgumentException("Unknown range data ntf setting: " 568 + range_data_ntf); 569 } 570 } 571 if (option.equals("-z")) { 572 String[] interleaveRatioString = getNextArgRequired().split(","); 573 if (interleaveRatioString.length != 3) { 574 throw new IllegalArgumentException("Unexpected interleaving ratio: " 575 + Arrays.toString(interleaveRatioString) 576 + " expected to be <numRange, numAoaAzimuth, numAoaElevation>"); 577 } 578 int numOfRangeMsrmts = Integer.parseInt(interleaveRatioString[0]); 579 int numOfAoaAzimuthMrmts = Integer.parseInt(interleaveRatioString[1]); 580 int numOfAoaElevationMrmts = Integer.parseInt(interleaveRatioString[2]); 581 // Set to interleaving mode 582 builder.setAoaResultRequest(AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS_INTERLEAVED); 583 builder.setMeasurementFocusRatio( 584 numOfRangeMsrmts, 585 numOfAoaAzimuthMrmts, 586 numOfAoaElevationMrmts); 587 interleavingEnabled = true; 588 } 589 if (option.equals("-e")) { 590 String aoaType = getNextArgRequired(); 591 if (aoaType.equals("none")) { 592 builder.setAoaResultRequest(AOA_RESULT_REQUEST_MODE_NO_AOA_REPORT); 593 } else if (aoaType.equals("enabled")) { 594 builder.setAoaResultRequest(AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS); 595 } else if (aoaType.equals("azimuth-only")) { 596 builder.setAoaResultRequest( 597 AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS_AZIMUTH_ONLY); 598 } else if (aoaType.equals("elevation-only")) { 599 builder.setAoaResultRequest( 600 AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS_ELEVATION_ONLY); 601 } else { 602 throw new IllegalArgumentException("Unknown aoa type: " + aoaType); 603 } 604 aoaResultReqEnabled = true; 605 } 606 if (option.equals("-f")) { 607 String[] resultReportConfigs = getNextArgRequired().split(","); 608 for (String resultReportConfig : resultReportConfigs) { 609 if (resultReportConfig.equals("tof")) { 610 builder.setHasTimeOfFlightReport(true); 611 } else if (resultReportConfig.equals("azimuth")) { 612 builder.setHasAngleOfArrivalAzimuthReport(true); 613 } else if (resultReportConfig.equals("elevation")) { 614 builder.setHasAngleOfArrivalElevationReport(true); 615 } else if (resultReportConfig.equals("aoa-fom")) { 616 builder.setHasAngleOfArrivalFigureOfMeritReport(true); 617 } else { 618 throw new IllegalArgumentException("Unknown result report config: " 619 + resultReportConfig); 620 } 621 } 622 } 623 if (option.equals("-g")) { 624 String staticSTSIV = getNextArgRequired(); 625 if (staticSTSIV.length() == 12) { 626 builder.setStaticStsIV(BaseEncoding.base16().decode(staticSTSIV.toUpperCase())); 627 } else { 628 throw new IllegalArgumentException("staticSTSIV expecting 6 bytes"); 629 } 630 } 631 if (option.equals("-v")) { 632 String vendorId = getNextArgRequired(); 633 if (vendorId.length() == 4) { 634 builder.setVendorId(BaseEncoding.base16().decode(vendorId.toUpperCase())); 635 } else { 636 throw new IllegalArgumentException("vendorId expecting 2 bytes"); 637 } 638 } 639 if (option.equals("-h")) { 640 int slotDurationRstu = Integer.parseInt(getNextArgRequired()); 641 builder.setSlotDurationRstu(slotDurationRstu); 642 } 643 if (option.equals("-w")) { 644 boolean hasRangingResultReportMessage = 645 getNextArgRequiredTrueOrFalse("enabled", "disabled"); 646 builder.setHasRangingResultReportMessage(hasRangingResultReportMessage); 647 } 648 if (option.equals("-y")) { 649 boolean hoppingEnabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); 650 builder.setHoppingMode(hoppingEnabled ? 1 : 0); 651 } 652 if (option.equals("-p")) { 653 int preambleCodeIndex = Integer.parseInt(getNextArgRequired()); 654 builder.setPreambleCodeIndex(preambleCodeIndex); 655 } 656 if (option.equals("-o")) { 657 String stsConfigType = getNextArgRequired(); 658 if (stsConfigType.equals("static")) { 659 builder.setStsConfig(STS_CONFIG_STATIC); 660 } else if (stsConfigType.equals("provisioned")) { 661 builder.setStsConfig(STS_CONFIG_PROVISIONED); 662 } else { 663 throw new IllegalArgumentException("unknown sts config type"); 664 } 665 } 666 if (option.equals("-n")) { 667 String sessionKey = getNextArgRequired(); 668 if (sessionKey.length() == 32 || sessionKey.length() == 64) { 669 builder.setSessionKey(BaseEncoding.base16().decode(sessionKey)); 670 } else { 671 throw new IllegalArgumentException("sessionKey expecting 16 or 32 bytes"); 672 } 673 } 674 if (option.equals("-k")) { 675 String subSessionKey = getNextArgRequired(); 676 if (subSessionKey.length() == 32 || subSessionKey.length() == 64) { 677 builder.setSubsessionKey(BaseEncoding.base16().decode(subSessionKey)); 678 } else { 679 throw new IllegalArgumentException(("subSessionKey expecting 16 or 32 bytes")); 680 } 681 } 682 if (option.equals("-j")) { 683 int errorStreakTimeoutMs = Integer.parseInt(getNextArgRequired()); 684 builder.setRangingErrorStreakTimeoutMs(errorStreakTimeoutMs); 685 } 686 if (option.equals("-q")) { 687 int sessionPriority = Integer.parseInt(getNextArgRequired()); 688 if (sessionPriority < 1 || sessionPriority > 100 || sessionPriority == 50) { 689 throw new IllegalArgumentException( 690 "sessionPriority expecting value between 1-49 or 51-100. 50 is " 691 + "reserved for default and has no effect."); 692 } 693 builder.setSessionPriority(sessionPriority); 694 } 695 if (option.equals("-P")) { 696 String prfMode = getNextArgRequired(); 697 if (prfMode.equals("bprf")) { 698 builder.setPrfMode(PRF_MODE_BPRF); 699 } else if (prfMode.equals("hprf")) { 700 builder.setPrfMode(PRF_MODE_HPRF); 701 } else { 702 throw new IllegalArgumentException("Wrong arguments for prmMode"); 703 } 704 } 705 if (option.equals("-D")) { 706 String psduDataRate = getNextArgRequired(); 707 if (psduDataRate.equals("6m81")) { 708 builder.setPsduDataRate(PSDU_DATA_RATE_6M81); 709 } else if (psduDataRate.equals("7m80")) { 710 builder.setPsduDataRate(PSDU_DATA_RATE_7M80); 711 } else if (psduDataRate.equals("27m2")) { 712 builder.setPsduDataRate(PSDU_DATA_RATE_27M2); 713 } else if (psduDataRate.equals("31m2")) { 714 builder.setPsduDataRate(PSDU_DATA_RATE_31M2); 715 } else { 716 throw new IllegalArgumentException("Wrong arguments for psduDataRate"); 717 } 718 } 719 if (option.equals("-B")) { 720 String bprfPhrDataRate = getNextArgRequired(); 721 if (bprfPhrDataRate.equals("850k")) { 722 builder.setBprfPhrDataRate(BPRF_PHR_DATA_RATE_850K); 723 } else if (bprfPhrDataRate.equals("6m81")) { 724 builder.setBprfPhrDataRate(BPRF_PHR_DATA_RATE_6M81); 725 } else { 726 throw new IllegalArgumentException("Wrong arguments for bprfPhrDataRate"); 727 } 728 } 729 if (option.equals("-A")) { 730 builder.setIsTxAdaptivePayloadPowerEnabled( 731 getNextArgRequiredTrueOrFalse("enabled", "disabled")); 732 } 733 if (option.equals("-S")) { 734 int sfd_id = Integer.parseInt(getNextArgRequired()); 735 if (sfd_id < 0 || sfd_id > 4) { 736 throw new IllegalArgumentException("SFD_ID should be in range 0-4"); 737 } 738 builder.setSfdId(sfd_id); 739 } 740 option = getNextOption(); 741 } 742 if (aoaResultReqEnabled && interleavingEnabled) { 743 throw new IllegalArgumentException( 744 "Both interleaving (-z) and aoa result req (-e) cannot be specified"); 745 } 746 // Enable rssi reporting if device supports it. 747 if (specificationParams.getFiraSpecificationParams().hasRssiReportingSupport()) { 748 builder.setIsRssiReportingEnabled(true); 749 } 750 // TODO: Add remaining params if needed. 751 return Pair.create(builder.build(), shouldBlockCall); 752 } 753 startFiraRangingSession(PrintWriter pw)754 private void startFiraRangingSession(PrintWriter pw) throws Exception { 755 GenericSpecificationParams specificationParams = 756 mUwbServiceCore.getCachedSpecificationParams(mUwbService.getDefaultChipId()); 757 Pair<FiraOpenSessionParams, Boolean> firaOpenSessionParams = 758 buildFiraOpenSessionParams(specificationParams); 759 startRangingSession( 760 firaOpenSessionParams.first, null, firaOpenSessionParams.first.getSessionId(), 761 firaOpenSessionParams.second, pw); 762 } 763 startDlTDoaRangingSession(PrintWriter pw)764 private void startDlTDoaRangingSession(PrintWriter pw) throws Exception { 765 FiraOpenSessionParams.Builder builder = new FiraOpenSessionParams.Builder() 766 .setProtocolVersion(FiraParams.PROTOCOL_VERSION_1_1) 767 .setSessionId(1) 768 .setSessionType(FiraParams.SESSION_TYPE_RANGING) 769 .setSfdId(SFD_ID_VALUE_2) 770 .setDeviceType(RANGING_DEVICE_TYPE_DT_TAG) 771 .setDeviceRole(RANGING_DEVICE_DT_TAG) 772 .setDeviceAddress(UwbAddress.fromBytes(new byte[] { 0x4, 0x6})) 773 .setMultiNodeMode(MULTI_NODE_MODE_ONE_TO_MANY) 774 .setRangingRoundUsage(RANGING_ROUND_USAGE_DL_TDOA) 775 .setVendorId(new byte[]{0x8, 0x7}) 776 .setRframeConfig(RFRAME_CONFIG_SP1) 777 .setStaticStsIV(new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}); 778 779 String option = getNextOption(); 780 while (option != null) { 781 if (option.equals("-i")) { 782 builder.setSessionId(Integer.parseInt(getNextArgRequired())); 783 } 784 option = getNextOption(); 785 } 786 FiraOpenSessionParams firaOpenSessionParams = builder.build(); 787 startRangingSession( 788 firaOpenSessionParams, null, firaOpenSessionParams.getSessionId(), 789 true, pw); 790 } 791 buildCccOpenRangingParams()792 private Pair<CccOpenRangingParams, Boolean> buildCccOpenRangingParams() { 793 CccOpenRangingParams.Builder builder = 794 new CccOpenRangingParams.Builder(DEFAULT_CCC_OPEN_RANGING_PARAMS); 795 boolean shouldBlockCall = false; 796 String option = getNextOption(); 797 while (option != null) { 798 if (option.equals("-b")) { 799 shouldBlockCall = true; 800 } 801 if (option.equals("-u")) { 802 builder.setUwbConfig(Integer.parseInt(getNextArgRequired())); 803 } 804 if (option.equals("-p")) { 805 String[] pulseComboString = getNextArgRequired().split(","); 806 if (pulseComboString.length != 2) { 807 throw new IllegalArgumentException("Erroneous pulse combo: " 808 + Arrays.toString(pulseComboString)); 809 } 810 builder.setPulseShapeCombo(new CccPulseShapeCombo( 811 Integer.parseInt(pulseComboString[0]), 812 Integer.parseInt(pulseComboString[1]))); 813 } 814 if (option.equals("-i")) { 815 builder.setSessionId(Integer.parseInt(getNextArgRequired())); 816 } 817 if (option.equals("-r")) { 818 builder.setRanMultiplier(Integer.parseInt(getNextArgRequired())); 819 } 820 if (option.equals("-c")) { 821 builder.setChannel(Integer.parseInt(getNextArgRequired())); 822 } 823 if (option.equals("-m")) { 824 builder.setNumChapsPerSlot(Integer.parseInt(getNextArgRequired())); 825 } 826 if (option.equals("-n")) { 827 builder.setNumResponderNodes(Integer.parseInt(getNextArgRequired())); 828 } 829 if (option.equals("-o")) { 830 builder.setNumSlotsPerRound(Integer.parseInt(getNextArgRequired())); 831 } 832 if (option.equals("-s")) { 833 builder.setSyncCodeIndex(Integer.parseInt(getNextArgRequired())); 834 } 835 if (option.equals("-h")) { 836 String hoppingConfigMode = getNextArgRequired(); 837 if (hoppingConfigMode.equals("none")) { 838 builder.setHoppingConfigMode(HOPPING_MODE_DISABLE); 839 } else if (hoppingConfigMode.equals("continuous")) { 840 builder.setHoppingConfigMode(HOPPING_CONFIG_MODE_CONTINUOUS); 841 } else if (hoppingConfigMode.equals("adaptive")) { 842 builder.setHoppingConfigMode(HOPPING_CONFIG_MODE_ADAPTIVE); 843 } else { 844 throw new IllegalArgumentException("Unknown hopping config mode: " 845 + hoppingConfigMode); 846 } 847 } 848 if (option.equals("-a")) { 849 String hoppingSequence = getNextArgRequired(); 850 if (hoppingSequence.equals("default")) { 851 builder.setHoppingSequence(HOPPING_SEQUENCE_DEFAULT); 852 } else if (hoppingSequence.equals("aes")) { 853 builder.setHoppingSequence(HOPPING_SEQUENCE_AES); 854 } else { 855 throw new IllegalArgumentException("Unknown hopping sequence: " 856 + hoppingSequence); 857 } 858 } 859 option = getNextOption(); 860 } 861 // TODO: Add remaining params if needed. 862 return Pair.create(builder.build(), shouldBlockCall); 863 } 864 startCccRangingSession(PrintWriter pw)865 private void startCccRangingSession(PrintWriter pw) throws Exception { 866 Pair<CccOpenRangingParams, Boolean> cccOpenRangingParamsAndBlocking = 867 buildCccOpenRangingParams(); 868 CccOpenRangingParams cccOpenRangingParams = cccOpenRangingParamsAndBlocking.first; 869 CccStartRangingParams cccStartRangingParams = new CccStartRangingParams.Builder() 870 .setSessionId(cccOpenRangingParams.getSessionId()) 871 .setRanMultiplier(cccOpenRangingParams.getRanMultiplier()) 872 .setInitiationTimeMs(cccOpenRangingParams.getInitiationTimeMs()) 873 .build(); 874 startRangingSession( 875 cccOpenRangingParams, cccStartRangingParams, cccOpenRangingParams.getSessionId(), 876 cccOpenRangingParamsAndBlocking.second, pw); 877 } 878 startRangingSession(@onNull Params openRangingSessionParams, @Nullable Params startRangingSessionParams, int sessionId, boolean shouldBlockCall, @NonNull PrintWriter pw)879 private void startRangingSession(@NonNull Params openRangingSessionParams, 880 @Nullable Params startRangingSessionParams, int sessionId, 881 boolean shouldBlockCall, @NonNull PrintWriter pw) throws Exception { 882 if (sSessionIdToInfo.containsKey(sessionId)) { 883 pw.println("Session with session ID: " + sessionId 884 + " already ongoing. Stop that session before you start a new session"); 885 return; 886 } 887 AttributionSource attributionSource = new AttributionSource.Builder(Process.SHELL_UID) 888 .setPackageName(SHELL_PACKAGE_NAME) 889 .build(); 890 SessionHandle sessionHandle = 891 new SessionHandle(sSessionHandleIdNext++, attributionSource, Process.myPid()); 892 SessionInfo sessionInfo = 893 new SessionInfo(sessionId, sessionHandle, openRangingSessionParams, pw, false); 894 mUwbService.openRanging( 895 attributionSource, 896 sessionInfo.sessionHandle, 897 sessionInfo.uwbRangingCbs, 898 openRangingSessionParams.toBundle(), 899 null); 900 boolean openCompleted = false; 901 try { 902 openCompleted = sessionInfo.rangingOpenedFuture.get( 903 RANGE_CTL_TIMEOUT_MILLIS, MILLISECONDS); 904 } catch (InterruptedException | CancellationException | TimeoutException 905 | ExecutionException e) { 906 } 907 if (!openCompleted) { 908 pw.println("Failed to open ranging session. Aborting!"); 909 return; 910 } 911 pw.println("Ranging session opened with params: " 912 + bundleToString(openRangingSessionParams.toBundle())); 913 sSessionIdToInfo.put(sessionId, sessionInfo); 914 915 if (openRangingSessionParams instanceof FiraOpenSessionParams 916 && ((FiraOpenSessionParams) openRangingSessionParams).getDeviceRole() 917 == RANGING_DEVICE_DT_TAG) { 918 DlTDoARangingRoundsUpdate rangingRounds = new DlTDoARangingRoundsUpdate.Builder() 919 .setSessionId(sessionId) 920 .setNoOfRangingRounds(1) 921 .setRangingRoundIndexes(new byte[]{0}) 922 .build(); 923 mUwbService.updateRangingRoundsDtTag(sessionInfo.sessionHandle, 924 rangingRounds.toBundle()); 925 boolean setRangingRounds = false; 926 try { 927 setRangingRounds = sessionInfo.rangingOpenedFuture.get( 928 RANGE_CTL_TIMEOUT_MILLIS, MILLISECONDS); 929 } catch (InterruptedException | CancellationException | TimeoutException 930 | ExecutionException e) { 931 } 932 if (!setRangingRounds) { 933 pw.println("Failed to set ranging rounds for DT tag"); 934 return; 935 } 936 } 937 mUwbService.startRanging( 938 sessionInfo.sessionHandle, 939 startRangingSessionParams != null 940 ? startRangingSessionParams.toBundle() 941 : new PersistableBundle()); 942 boolean startCompleted = false; 943 try { 944 startCompleted = sessionInfo.rangingStartedFuture.get( 945 RANGE_CTL_TIMEOUT_MILLIS, MILLISECONDS); 946 } catch (InterruptedException | CancellationException | TimeoutException 947 | ExecutionException e) { 948 } 949 if (!startCompleted) { 950 pw.println("Failed to start ranging session. Aborting!"); 951 return; 952 } 953 pw.println("Ranging session started for sessionId: " + sessionId); 954 while (shouldBlockCall) { 955 Thread.sleep(RANGE_CTL_TIMEOUT_MILLIS); 956 } 957 } 958 stopRangingSession(PrintWriter pw)959 private void stopRangingSession(PrintWriter pw) throws RemoteException { 960 int sessionId = Integer.parseInt(getNextArgRequired()); 961 stopRangingSession(pw, sessionId); 962 } 963 stopRangingSession(PrintWriter pw, int sessionId)964 private void stopRangingSession(PrintWriter pw, int sessionId) throws RemoteException { 965 SessionInfo sessionInfo = sSessionIdToInfo.get(sessionId); 966 if (sessionInfo == null) { 967 pw.println("No active session with session ID: " + sessionId + " found"); 968 return; 969 } 970 mUwbService.stopRanging(sessionInfo.sessionHandle); 971 boolean stopCompleted = false; 972 try { 973 stopCompleted = sessionInfo.rangingStoppedFuture.get( 974 RANGE_CTL_TIMEOUT_MILLIS, MILLISECONDS); 975 } catch (InterruptedException | CancellationException | TimeoutException 976 | ExecutionException e) { 977 } 978 if (!stopCompleted) { 979 pw.println("Failed to stop ranging session. Aborting!"); 980 return; 981 } 982 pw.println("Ranging session stopped"); 983 984 mUwbService.closeRanging(sessionInfo.sessionHandle); 985 boolean closeCompleted = false; 986 try { 987 closeCompleted = sessionInfo.rangingClosedFuture.get( 988 RANGE_CTL_TIMEOUT_MILLIS, MILLISECONDS); 989 } catch (InterruptedException | CancellationException | TimeoutException 990 | ExecutionException e) { 991 } 992 if (!closeCompleted) { 993 pw.println("Failed to close ranging session. Aborting!"); 994 return; 995 } 996 pw.println("Ranging session closed"); 997 } 998 buildFiraReconfigureParams()999 private FiraRangingReconfigureParams buildFiraReconfigureParams() { 1000 FiraRangingReconfigureParams.Builder builder = 1001 new FiraRangingReconfigureParams.Builder(); 1002 String option = getNextOption(); 1003 while (option != null) { 1004 if (option.equals("-a")) { 1005 String action = getNextArgRequired(); 1006 if (action.equals("add")) { 1007 builder.setAction(MULTICAST_LIST_UPDATE_ACTION_ADD); 1008 } else if (action.equals("delete")) { 1009 builder.setAction(MULTICAST_LIST_UPDATE_ACTION_DELETE); 1010 } else { 1011 throw new IllegalArgumentException("Unexpected action " + action); 1012 } 1013 } 1014 if (option.equals("-d")) { 1015 String[] destAddressesString = getNextArgRequired().split(","); 1016 List<UwbAddress> destAddresses = new ArrayList<>(); 1017 for (String destAddressString : destAddressesString) { 1018 destAddresses.add(UwbAddress.fromBytes( 1019 ByteBuffer.allocate(SHORT_ADDRESS_BYTE_LENGTH) 1020 .putShort(Short.parseShort(destAddressString)) 1021 .array())); 1022 } 1023 builder.setAddressList(destAddresses.toArray(new UwbAddress[0])); 1024 } 1025 if (option.equals("-s")) { 1026 String[] subSessionIdsString = getNextArgRequired().split(","); 1027 List<Integer> subSessionIds = new ArrayList<>(); 1028 for (String subSessionIdString : subSessionIdsString) { 1029 subSessionIds.add(Integer.parseInt(subSessionIdString)); 1030 } 1031 builder.setSubSessionIdList(subSessionIds.stream().mapToInt(s -> s).toArray()); 1032 } 1033 if (option.equals("-b")) { 1034 int blockStrideLength = Integer.parseInt(getNextArgRequired()); 1035 builder.setBlockStrideLength(blockStrideLength); 1036 } 1037 if (option.equals("-c")) { 1038 int rangeDataNtfConfig = Integer.parseInt(getNextArgRequired()); 1039 builder.setRangeDataNtfConfig(rangeDataNtfConfig); 1040 } 1041 if (option.equals("-n")) { 1042 int proximityNear = Integer.parseInt(getNextArgRequired()); 1043 builder.setRangeDataProximityNear(proximityNear); 1044 } 1045 if (option.equals("-f")) { 1046 int proximityFar = Integer.parseInt(getNextArgRequired()); 1047 builder.setRangeDataProximityFar(proximityFar); 1048 } 1049 option = getNextOption(); 1050 } 1051 // TODO: Add remaining params if needed. 1052 return builder.build(); 1053 } 1054 reconfigureFiraRangingSession(PrintWriter pw)1055 private void reconfigureFiraRangingSession(PrintWriter pw) throws RemoteException { 1056 int sessionId = Integer.parseInt(getNextArgRequired()); 1057 SessionInfo sessionInfo = sSessionIdToInfo.get(sessionId); 1058 if (sessionInfo == null) { 1059 pw.println("No active session with session ID: " + sessionId + " found"); 1060 return; 1061 } 1062 FiraRangingReconfigureParams params = buildFiraReconfigureParams(); 1063 1064 mUwbService.reconfigureRanging(sessionInfo.sessionHandle, params.toBundle()); 1065 boolean reconfigureCompleted = false; 1066 try { 1067 reconfigureCompleted = sessionInfo.rangingReconfiguredFuture.get( 1068 RANGE_CTL_TIMEOUT_MILLIS, MILLISECONDS); 1069 } catch (InterruptedException | CancellationException | TimeoutException 1070 | ExecutionException e) { 1071 } 1072 if (!reconfigureCompleted) { 1073 pw.println("Failed to reconfigure ranging session. Aborting!"); 1074 return; 1075 } 1076 pw.println("Ranging session reconfigured"); 1077 } 1078 runTaskOnSingleThreadExecutor(FutureTask<Integer> task)1079 private int runTaskOnSingleThreadExecutor(FutureTask<Integer> task) { 1080 try { 1081 return mUwbInjector.runTaskOnSingleThreadExecutor(task, CMD_TIMEOUT_MS); 1082 } catch (TimeoutException | InterruptedException | ExecutionException e) { 1083 Log.e(TAG, "Failed to send command", e); 1084 } 1085 return -1; 1086 } 1087 buildRadarOpenSessionParams()1088 private Pair<RadarOpenSessionParams, Boolean> buildRadarOpenSessionParams() { 1089 RadarOpenSessionParams.Builder builder = 1090 new RadarOpenSessionParams.Builder(DEFAULT_RADAR_OPEN_SESSION_PARAMS); 1091 boolean shouldBlockCall = false; 1092 1093 for (String option = getNextOption(); option != null; option = getNextOption()) { 1094 switch (option) { 1095 case "-b": 1096 shouldBlockCall = true; 1097 break; 1098 case "-i": 1099 builder.setSessionId(Integer.parseInt(getNextArgRequired())); 1100 break; 1101 case "-c": 1102 builder.setChannelNumber(Integer.parseInt(getNextArgRequired())); 1103 break; 1104 case "-s": 1105 builder.setSweepPeriod(Integer.parseInt(getNextArgRequired())); 1106 break; 1107 case "-u": 1108 builder.setSweepsPerBurst(Integer.parseInt(getNextArgRequired())); 1109 break; 1110 case "-e": 1111 builder.setSamplesPerSweep(Integer.parseInt(getNextArgRequired())); 1112 break; 1113 case "-o": 1114 builder.setSweepOffset(Integer.parseInt(getNextArgRequired())); 1115 break; 1116 case "-r": 1117 builder.setRframeConfig(Integer.parseInt(getNextArgRequired())); 1118 break; 1119 case "-t": 1120 builder.setPreambleDuration(Integer.parseInt(getNextArgRequired())); 1121 break; 1122 case "-d": 1123 builder.setPreambleCodeIndex(Integer.parseInt(getNextArgRequired())); 1124 break; 1125 case "-x": 1126 builder.setSessionPriority(Integer.parseInt(getNextArgRequired())); 1127 break; 1128 case "-p": 1129 builder.setBitsPerSample(Integer.parseInt(getNextArgRequired())); 1130 break; 1131 case "-m": 1132 builder.setPrfMode(Integer.parseInt(getNextArgRequired())); 1133 break; 1134 case "-n": 1135 builder.setNumberOfBursts(Integer.parseInt(getNextArgRequired())); 1136 break; 1137 } 1138 } 1139 return Pair.create(builder.build(), shouldBlockCall); 1140 } 1141 startRadarSession(PrintWriter pw)1142 private void startRadarSession(PrintWriter pw) throws Exception { 1143 Pair<RadarOpenSessionParams, Boolean> radarOpenSessionParamsAndBlocking = 1144 buildRadarOpenSessionParams(); 1145 RadarOpenSessionParams radarOpenSessionParams = radarOpenSessionParamsAndBlocking.first; 1146 int sessionId = radarOpenSessionParams.getSessionId(); 1147 1148 if (sSessionIdToInfo.containsKey(sessionId)) { 1149 pw.println("Session with session ID: " + sessionId 1150 + " already ongoing. Stop that session before you start a new session"); 1151 return; 1152 } 1153 AttributionSource attributionSource = new AttributionSource.Builder(Process.SHELL_UID) 1154 .setPackageName(SHELL_PACKAGE_NAME) 1155 .build(); 1156 SessionHandle sessionHandle = 1157 new SessionHandle(sSessionHandleIdNext++, attributionSource, Process.myPid()); 1158 SessionInfo sessionInfo = 1159 new SessionInfo(sessionId, sessionHandle, radarOpenSessionParams, pw, true); 1160 mUwbService.openRanging( 1161 attributionSource, 1162 sessionInfo.sessionHandle, 1163 sessionInfo.uwbRangingCbs, 1164 radarOpenSessionParams.toBundle(), 1165 null); 1166 boolean openCompleted = false; 1167 try { 1168 openCompleted = sessionInfo.rangingOpenedFuture.get( 1169 RANGE_CTL_TIMEOUT_MILLIS, MILLISECONDS); 1170 } catch (InterruptedException | CancellationException | TimeoutException 1171 | ExecutionException e) { 1172 } 1173 if (!openCompleted) { 1174 pw.println("Failed to open radar session. Aborting!"); 1175 return; 1176 } 1177 pw.println("Radar session opened with params: " 1178 + bundleToString(radarOpenSessionParams.toBundle())); 1179 1180 mUwbService.startRanging(sessionInfo.sessionHandle, new PersistableBundle()); 1181 boolean startCompleted = false; 1182 try { 1183 startCompleted = sessionInfo.rangingStartedFuture.get( 1184 RANGE_CTL_TIMEOUT_MILLIS, MILLISECONDS); 1185 } catch (InterruptedException | CancellationException | TimeoutException 1186 | ExecutionException e) { 1187 } 1188 if (!startCompleted) { 1189 pw.println("Failed to start radar session. Aborting!"); 1190 return; 1191 } 1192 pw.println("Radar session started for sessionId: " + sessionId); 1193 sSessionIdToInfo.put(sessionId, sessionInfo); 1194 while (radarOpenSessionParamsAndBlocking.second) { 1195 Thread.sleep(RANGE_CTL_TIMEOUT_MILLIS); 1196 } 1197 } 1198 1199 @Override onCommand(String cmd)1200 public int onCommand(String cmd) { 1201 // Treat no command as help command. 1202 if (cmd == null || cmd.equals("")) { 1203 cmd = "help"; 1204 } 1205 // Explicit exclusion from root permission 1206 if (ArrayUtils.indexOf(NON_PRIVILEGED_COMMANDS, cmd) == -1) { 1207 final int uid = Binder.getCallingUid(); 1208 if (uid != Process.ROOT_UID) { 1209 throw new SecurityException( 1210 "Uid " + uid + " does not have access to " + cmd + " uwb command " 1211 + "(or such command doesn't exist)"); 1212 } 1213 } 1214 1215 final PrintWriter pw = getOutPrintWriter(); 1216 try { 1217 switch (cmd) { 1218 case "force-country-code": { 1219 boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); 1220 FutureTask<Integer> task; 1221 if (enabled) { 1222 String countryCode = getNextArgRequired(); 1223 if (!UwbCountryCode.isValid(countryCode)) { 1224 pw.println("Invalid argument: Country code must be a 2-Character" 1225 + " alphanumeric code. But got countryCode " + countryCode 1226 + " instead"); 1227 return -1; 1228 } 1229 task = new FutureTask<>(() -> { 1230 mUwbCountryCode.setOverrideCountryCode(countryCode); 1231 return 0; 1232 }); 1233 } else { 1234 task = new FutureTask<>(() -> { 1235 mUwbCountryCode.clearOverrideCountryCode(); 1236 return 0; 1237 }); 1238 } 1239 return runTaskOnSingleThreadExecutor(task); 1240 } 1241 case "get-country-code": 1242 pw.println("Uwb Country Code = " + mUwbCountryCode.getCountryCode()); 1243 return 0; 1244 case "simulate-app-state-change": { 1245 String appPackageName = getNextArgRequired(); 1246 String nextArg = getNextArg(); 1247 if (nextArg != null) { 1248 boolean isFg = argTrueOrFalse(nextArg, "foreground", "background"); 1249 int importance = isFg ? IMPORTANCE_FOREGROUND : IMPORTANCE_BACKGROUND; 1250 int uid = 0; 1251 try { 1252 uid = mContext.getPackageManager().getApplicationInfo( 1253 appPackageName, 0).uid; 1254 } catch (PackageManager.NameNotFoundException e) { 1255 pw.println("Unable to find package name: " + appPackageName); 1256 return -1; 1257 } 1258 mUwbInjector.setOverridePackageImportance(appPackageName, importance); 1259 mUwbInjector.getUwbSessionManager().onUidImportance(uid, importance); 1260 } else { 1261 mUwbInjector.resetOverridePackageImportance(appPackageName); 1262 } 1263 return 0; 1264 } 1265 case "set-log-mode": { 1266 String logMode = getNextArgRequired(); 1267 if (!UciLogModeStore.isValid(logMode)) { 1268 pw.println("Invalid argument: Log mode must be one of the following:" 1269 + " Disabled, Filtered, or Unfiltered. But got log mode " + logMode 1270 + " instead"); 1271 return -1; 1272 } 1273 mUciLogModeStore.storeMode(logMode); 1274 if (!mNativeUwbManager.setLogMode(logMode)) { 1275 pw.println("Failed to set log mode. " + logMode 1276 + " log mode will be set on next UWB restart"); 1277 return -1; 1278 } 1279 return 0; 1280 } 1281 case "get-log-mode": 1282 pw.println("UWB Log Mode = " + mUciLogModeStore.getMode()); 1283 return 0; 1284 case "status": 1285 printStatus(pw); 1286 return 0; 1287 case "enable-uwb": 1288 mUwbService.setEnabled(true); 1289 return 0; 1290 case "disable-uwb": 1291 mUwbService.setEnabled(false); 1292 return 0; 1293 case "enable-uwb-hw": { 1294 AttributionSource attributionSource = new AttributionSource.Builder( 1295 Process.SHELL_UID) 1296 .setPackageName(SHELL_PACKAGE_NAME) 1297 .build(); 1298 mUwbService.requestHwEnabled(true, attributionSource, new Binder()); 1299 return 0; 1300 } 1301 case "disable-uwb-hw": { 1302 AttributionSource attributionSource = new AttributionSource.Builder( 1303 Process.SHELL_UID) 1304 .setPackageName(SHELL_PACKAGE_NAME) 1305 .build(); 1306 mUwbService.requestHwEnabled(false, attributionSource, new Binder()); 1307 return 0; 1308 } 1309 case "start-dl-tdoa-ranging-session": 1310 startDlTDoaRangingSession(pw); 1311 return 0; 1312 case "start-fira-ranging-session": 1313 startFiraRangingSession(pw); 1314 return 0; 1315 case "start-ccc-ranging-session": 1316 startCccRangingSession(pw); 1317 return 0; 1318 case "start-radar-session": 1319 startRadarSession(pw); 1320 return 0; 1321 case "reconfigure-fira-ranging-session": 1322 reconfigureFiraRangingSession(pw); 1323 return 0; 1324 case "get-ranging-session-reports": { 1325 int sessionId = Integer.parseInt(getNextArgRequired()); 1326 SessionInfo sessionInfo = sSessionIdToInfo.get(sessionId); 1327 if (sessionInfo == null) { 1328 pw.println("No active session with session ID: " + sessionId + " found"); 1329 return -1; 1330 } 1331 pw.println("Last Ranging results:"); 1332 for (RangingReport rangingReport : sessionInfo.lastRangingReports) { 1333 pw.println(rangingReport); 1334 } 1335 return 0; 1336 } 1337 case "get-all-ranging-session-reports": { 1338 for (SessionInfo sessionInfo: sSessionIdToInfo.values()) { 1339 pw.println("Last Ranging results for sessionId " + sessionInfo.sessionId 1340 + ":"); 1341 for (RangingReport rangingReport : sessionInfo.lastRangingReports) { 1342 pw.println(rangingReport); 1343 } 1344 } 1345 return 0; 1346 } 1347 case "stop-ranging-session": 1348 case "stop-radar-session": 1349 stopRangingSession(pw); 1350 return 0; 1351 case "stop-all-ranging-sessions": { 1352 for (int sessionId : new ArrayList<>(sSessionIdToInfo.keySet())) { 1353 if (!sSessionIdToInfo.get(sessionId).isRadarSession) { 1354 stopRangingSession(pw, sessionId); 1355 } 1356 } 1357 return 0; 1358 } 1359 case "stop-all-radar-sessions": { 1360 for (int sessionId : new ArrayList<>(sSessionIdToInfo.keySet())) { 1361 if (sSessionIdToInfo.get(sessionId).isRadarSession) { 1362 stopRangingSession(pw, sessionId); 1363 } 1364 } 1365 return 0; 1366 } 1367 case "get-specification-info": { 1368 PersistableBundle bundle = mUwbService.getSpecificationInfo(null); 1369 pw.println("Specification info: " + bundleToString(bundle)); 1370 return 0; 1371 } 1372 case "get-power-stats": { 1373 PersistableBundle bundle = mUwbService.getSpecificationInfo(null); 1374 GenericSpecificationParams params = 1375 GenericSpecificationParams.fromBundle(bundle); 1376 if (params == null) { 1377 pw.println("Spec info is empty"); 1378 return -1; 1379 } 1380 if (params.hasPowerStatsSupport()) { 1381 pw.println(mNativeUwbManager.getPowerStats(mUwbService.getDefaultChipId())); 1382 } else { 1383 pw.println("power stats query is not supported"); 1384 } 1385 return 0; 1386 } 1387 case "enable-diagnostics-notification": { 1388 byte diagramFrameReportsFlags = 0; 1389 String option = getNextOption(); 1390 while (option != null) { 1391 if (option.equals("-r")) { 1392 diagramFrameReportsFlags |= RSSI_FLAG; 1393 } 1394 if (option.equals("-a")) { 1395 diagramFrameReportsFlags |= AOA_FLAG; 1396 } 1397 if (option.equals("-c")) { 1398 diagramFrameReportsFlags |= CIR_FLAG; 1399 } 1400 if (option.equals("-s")) { 1401 diagramFrameReportsFlags |= SEGMENT_METRICS_FLAG; 1402 } 1403 option = getNextOption(); 1404 } 1405 mUwbServiceCore.enableDiagnostics(true, diagramFrameReportsFlags); 1406 return 0; 1407 } 1408 case "disable-diagnostics-notification": { 1409 mUwbServiceCore.enableDiagnostics(false, (byte) 0); 1410 return 0; 1411 } 1412 case "take-bugreport": { 1413 new Handler(mLooper).post(() -> { 1414 if (mDeviceConfig.isDeviceErrorBugreportEnabled()) { 1415 mUwbDiagnostics.takeBugReport("Uwb bugreport test"); 1416 } 1417 }); 1418 return 0; 1419 } 1420 default: 1421 return handleDefaultCommands(cmd); 1422 } 1423 } catch (IllegalArgumentException e) { 1424 pw.println("Invalid args for " + cmd + ": "); 1425 e.printStackTrace(pw); 1426 return -1; 1427 } catch (Exception e) { 1428 pw.println("Exception while executing UwbShellCommand" + cmd + ": "); 1429 e.printStackTrace(pw); 1430 return -1; 1431 } 1432 } 1433 argTrueOrFalse(String arg, String trueString, String falseString)1434 private static boolean argTrueOrFalse(String arg, String trueString, String falseString) { 1435 if (trueString.equals(arg)) { 1436 return true; 1437 } else if (falseString.equals(arg)) { 1438 return false; 1439 } else { 1440 throw new IllegalArgumentException("Expected '" + trueString + "' or '" + falseString 1441 + "' as next arg but got '" + arg + "'"); 1442 } 1443 1444 } 1445 getNextArgRequiredTrueOrFalse(String trueString, String falseString)1446 private boolean getNextArgRequiredTrueOrFalse(String trueString, String falseString) 1447 throws IllegalArgumentException { 1448 String nextArg = getNextArgRequired(); 1449 return argTrueOrFalse(nextArg, trueString, falseString); 1450 } 1451 printStatus(PrintWriter pw)1452 private void printStatus(PrintWriter pw) throws RemoteException { 1453 int adapterState = mUwbService.getAdapterState(); 1454 boolean uwbEnabled = adapterState != UwbManager.AdapterStateCallback.STATE_DISABLED; 1455 boolean uwbHwIdle = adapterState == UwbManager.AdapterStateCallback.STATE_ENABLED_HW_IDLE; 1456 String status; 1457 if (uwbHwIdle) { 1458 status = "enabled by user, but no clients have voted to enable hw"; 1459 } else if (uwbEnabled) { 1460 status = "enabled"; 1461 } else { 1462 status = "disabled"; 1463 } 1464 pw.println("Uwb is " + status); 1465 } 1466 onHelpNonPrivileged(PrintWriter pw)1467 private void onHelpNonPrivileged(PrintWriter pw) { 1468 pw.println(" status"); 1469 pw.println(" Gets status of UWB stack"); 1470 pw.println(" get-country-code"); 1471 pw.println(" Gets country code as a two-letter string"); 1472 pw.println(" get-log-mode"); 1473 pw.println(" Get the log mode for UCI packet capturing"); 1474 pw.println(" enable-uwb"); 1475 pw.println(" Toggle UWB on"); 1476 pw.println(" disable-uwb"); 1477 pw.println(" Toggle UWB off"); 1478 pw.println(" enable-uwb-hw"); 1479 pw.println(" If 'hw_idle_turn_off_enabled' feature is enabled, vote for UWB on"); 1480 pw.println(" disable-uwb-hw"); 1481 pw.println(" If 'hw_idle_turn_off_enabled' feature is enabled, vote for UWB off"); 1482 pw.println(" start-fira-ranging-session" 1483 + " [-b](blocking call)" 1484 + " [-i <sessionId>](session-id)" 1485 + " [-c <channel>](channel)" 1486 + " [-t controller|controlee](device-type)" 1487 + " [-r initiator|responder](device-role)" 1488 + " [-a <deviceAddress>](device-address)" 1489 + " [-d <destAddress-1, destAddress-2,...>](dest-addresses)" 1490 + " [-m <unicast|one-to-many|many-to-many>](multi-node mode)" 1491 + " [-u ds-twr|ss-twr|ds-twr-non-deferred|ss-twr-non-deferred](round-usage)" 1492 + " [-l <ranging-interval-ms>](ranging-interval-ms)" 1493 + " [-s <slots-per-ranging-round>](slots-per-ranging-round)" 1494 + " [-x <proximity-near-cm, proximity-far-cm>](range-data-ntf-proximity)" 1495 + " [-z <numRangeMrmts, numAoaAzimuthMrmts, numAoaElevationMrmts>" 1496 + "(interleaving-ratio)" 1497 + " [-e none|enabled|azimuth-only|elevation-only](aoa type)" 1498 + " [-f <tof,azimuth,elevation,aoa-fom>(result-report-config)" 1499 + " [-g <staticStsIV>(staticStsIV 6-bytes)" 1500 + " [-v <staticStsVendorId>(staticStsVendorId 2-bytes)" 1501 + " [-w enabled|disabled](has-result-report-phase)" 1502 + " [-y enabled|disabled](hopping-mode, default = disabled)" 1503 + " [-p <preamble-code-index>](preamble-code-index, default = 10)" 1504 + " [-h <slot-duration-rstu>(slot-duration-rstu, default=2400)" 1505 + " [-o static|provisioned](sts-config-type)" 1506 + " [-n <sessionKey>](sessionKey 16 or 32 bytes)" 1507 + " [-k <subSessionKey>](subSessionKey 16 or 32 bytes)" 1508 + " [-j <errorStreakTimeoutMs>](error streak timeout in millis, default=30000)" 1509 + " [-q <sessionPriority>](sessionPriority 1-49 or 51-100)" 1510 + " [-P bprf|hprf](prfMode)" 1511 + " [-D 6m81|7m80|27m2|31m2](psduDataRate)" 1512 + " [-B 850k|6m81](bprfPhrDataRate)" 1513 + " [-A enabled|disabled](TX adaptive power, default = disabled)" 1514 + " [-S <sfd_id>](sfd_id 0-4, default = 2)" 1515 + " [-R enabled|disabled](range-data-notification)"); 1516 pw.println(" Starts a FIRA ranging session with the provided params." 1517 + " Note: default behavior is to cache the latest ranging reports which can be" 1518 + " retrieved using |get-ranging-session-reports|"); 1519 pw.println(" start-dl-tdoa-ranging-session" 1520 + " [-i <sessionId>](session-id)"); 1521 pw.println(" Starts a FIRA Dl-TDoA ranging session for DT-Tag"); 1522 pw.println(" start-ccc-ranging-session" 1523 + " [-b](blocking call)" 1524 + " Ranging reports will be displayed on screen)" 1525 + " [-u 0|1](uwb-config)" 1526 + " [-p <tx>,<rx>](pulse-shape-combo)" 1527 + " [-i <sessionId>](session-id)" 1528 + " [-r <ran_multiplier>](ran-multiplier)" 1529 + " [-c <channel>](channel)" 1530 + " [-m <num-chaps-per-slot>](num-chaps-per-slot)" 1531 + " [-n <num-responder-nodes>](num-responder-nodes)" 1532 + " [-o <num-slots-per-round>](num-slots-per-round)" 1533 + " [-s <sync-code-index>](sync-code-index)" 1534 + " [-h none|continuous|adaptive](hopping-config-mode)" 1535 + " [-a default|aes](hopping-sequence)"); 1536 pw.println(" Starts a CCC ranging session with the provided params." 1537 + " Note: default behavior is to cache the latest ranging reports which can be" 1538 + " retrieved using |get-ranging-session-reports|"); 1539 pw.println(" start-radar-session" 1540 + " [-b](blocking call)" 1541 + " Radar data will be displayed on screen)" 1542 + " [-i <sessionId>](session-id)" 1543 + " [-c <channel>](channel)" 1544 + " [-s <sweepPeriod>](sweep-period)" 1545 + " [-u <sweepsPerBurst>](sweeps-per-burst)" 1546 + " [-e <samplesPerSweep>](samples-per-sweep)" 1547 + " [-p <bitsPerSample>](bits-per-sample)" 1548 + " [-o <sweepOffset>](sweep-offset)" 1549 + " [-r <rframeConfig>](rframe-config)" 1550 + " [-t <preambleDuration>](preamble-duration)" 1551 + " [-d <preambleCodeIndex>](preamble-code-index)" 1552 + " [-x <sessionPriority>](session-priority)" 1553 + " [-m <prfMode>](prf-mode)" 1554 + " [-n <numberOfBursts>](number-of-bursts)"); 1555 pw.println(" Starts a Radar session with the provided params defined in the radar UCI" 1556 + " spec."); 1557 pw.println(" reconfigure-fira-ranging-session" 1558 + " <sessionId>" 1559 + " [-a add|delete](action)" 1560 + " [-d <destAddress-1, destAddress-2,...>](dest-addresses)" 1561 + " [-s <subSessionId-1, subSessionId-2,...>](sub-sessionIds)" 1562 + " [-b <block-striding>](block-striding)" 1563 + " [-c <range-data-ntf-cfg>](range-data-ntf-cfg)" 1564 + " [-n <proximity-near>(proximity-near)" 1565 + " [-f <proximity-far>](proximity-far)"); 1566 pw.println(" get-ranging-session-reports <sessionId>"); 1567 pw.println(" Displays latest cached ranging reports for an ongoing ranging session"); 1568 pw.println(" get-all-ranging-session-reports"); 1569 pw.println(" Displays latest cached ranging reports for all ongoing ranging session"); 1570 pw.println(" stop-ranging-session <sessionId>"); 1571 pw.println(" Stops an ongoing ranging session"); 1572 pw.println(" stop-radar-session <sessionId>"); 1573 pw.println(" Stops an ongoing radar session"); 1574 pw.println(" stop-all-ranging-sessions"); 1575 pw.println(" Stops all ongoing ranging sessions"); 1576 pw.println(" stop-all-radar-sessions"); 1577 pw.println(" Stops all ongoing radar sessions"); 1578 pw.println(" get-specification-info"); 1579 pw.println(" Gets specification info from uwb chip"); 1580 pw.println(" enable-diagnostics-notification" 1581 + " [-r](enable rssi)" 1582 + " [-a](enable aoa)" 1583 + " [-c](enable cir)" 1584 + " [-s](enable segment metrics)"); 1585 pw.println(" Enable vendor diagnostics notification"); 1586 pw.println(" disable-diagnostics-notification"); 1587 pw.println(" Disable vendor diagnostics notification"); 1588 pw.println(" take-bugreport"); 1589 pw.println(" take bugreport through betterBug or alternatively bugreport manager"); 1590 pw.println(" simulate-app-state-change <package-name> foreground|background"); 1591 pw.println(" Simulate app moving to foreground/background to test stack handling"); 1592 } 1593 onHelpPrivileged(PrintWriter pw)1594 private void onHelpPrivileged(PrintWriter pw) { 1595 pw.println(" force-country-code enabled <two-letter code> | disabled "); 1596 pw.println(" Sets country code to <two-letter code> or left for normal value"); 1597 pw.println(" get-power-stats"); 1598 pw.println(" Get power stats"); 1599 pw.println(" set-log-mode disabled|filtered|unfiltered"); 1600 pw.println(" Sets the log mode for UCI packet capturing"); 1601 } 1602 1603 @Override onHelp()1604 public void onHelp() { 1605 final PrintWriter pw = getOutPrintWriter(); 1606 pw.println("UWB (ultra wide-band) commands:"); 1607 pw.println(" help or -h"); 1608 pw.println(" Print this help text."); 1609 onHelpNonPrivileged(pw); 1610 if (Binder.getCallingUid() == Process.ROOT_UID) { 1611 onHelpPrivileged(pw); 1612 } 1613 pw.println(); 1614 } 1615 1616 @VisibleForTesting reset()1617 public void reset() { 1618 sSessionHandleIdNext = 0; 1619 sSessionIdToInfo.clear(); 1620 } 1621 } 1622