1 /* 2 * Copyright 2020 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 android.uwb; 18 19 import android.Manifest; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.os.Binder; 25 import android.os.PersistableBundle; 26 import android.os.RemoteException; 27 import android.util.Log; 28 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.util.concurrent.Executor; 32 33 /** 34 * This class provides a way to control an active UWB ranging session. 35 * <p>It also defines the required {@link RangingSession.Callback} that must be implemented 36 * in order to be notified of UWB ranging results and status events related to the 37 * {@link RangingSession}. 38 * 39 * <p>To get an instance of {@link RangingSession}, first use 40 * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} to request to open a 41 * session. Once the session is opened, a {@link RangingSession} object is provided through 42 * {@link RangingSession.Callback#onOpened(RangingSession)}. If opening a session fails, the failure 43 * is reported through {@link RangingSession.Callback#onOpenFailed(int, PersistableBundle)} with the 44 * failure reason. 45 * 46 * @hide 47 */ 48 @SystemApi 49 public final class RangingSession implements AutoCloseable { 50 private static final String TAG = "Uwb.RangingSession"; 51 private final SessionHandle mSessionHandle; 52 private final IUwbAdapter mAdapter; 53 private final Executor mExecutor; 54 private final Callback mCallback; 55 56 private enum State { 57 /** 58 * The state of the {@link RangingSession} until 59 * {@link RangingSession.Callback#onOpened(RangingSession)} is invoked 60 */ 61 INIT, 62 63 /** 64 * The {@link RangingSession} is initialized and ready to begin ranging 65 */ 66 IDLE, 67 68 /** 69 * The {@link RangingSession} is actively ranging 70 */ 71 ACTIVE, 72 73 /** 74 * The {@link RangingSession} is closed and may not be used for ranging. 75 */ 76 CLOSED 77 } 78 79 private State mState = State.INIT; 80 81 /** 82 * Interface for receiving {@link RangingSession} events 83 */ 84 public interface Callback { 85 /** 86 * @hide 87 */ 88 @Retention(RetentionPolicy.SOURCE) 89 @IntDef(value = { 90 REASON_UNKNOWN, 91 REASON_LOCAL_REQUEST, 92 REASON_REMOTE_REQUEST, 93 REASON_BAD_PARAMETERS, 94 REASON_GENERIC_ERROR, 95 REASON_MAX_SESSIONS_REACHED, 96 REASON_SYSTEM_POLICY, 97 REASON_PROTOCOL_SPECIFIC_ERROR}) 98 @interface Reason {} 99 100 /** 101 * Indicates that the session was closed or failed to open due to an unknown reason 102 */ 103 int REASON_UNKNOWN = 0; 104 105 /** 106 * Indicates that the session was closed or failed to open because 107 * {@link AutoCloseable#close()} or {@link RangingSession#close()} was called 108 */ 109 int REASON_LOCAL_REQUEST = 1; 110 111 /** 112 * Indicates that the session was closed or failed to open due to an explicit request from 113 * the remote device. 114 */ 115 int REASON_REMOTE_REQUEST = 2; 116 117 /** 118 * Indicates that the session was closed or failed to open due to erroneous parameters 119 */ 120 int REASON_BAD_PARAMETERS = 3; 121 122 /** 123 * Indicates an error on this device besides the error code already listed 124 */ 125 int REASON_GENERIC_ERROR = 4; 126 127 /** 128 * Indicates that the number of currently open sessions is equal to 129 * {@link UwbManager#getMaxSimultaneousSessions()} and additional sessions may not be 130 * opened. 131 */ 132 int REASON_MAX_SESSIONS_REACHED = 5; 133 134 /** 135 * Indicates that the local system policy caused the change, such 136 * as privacy policy, power management policy, permissions, and more. 137 */ 138 int REASON_SYSTEM_POLICY = 6; 139 140 /** 141 * Indicates a protocol specific error. The associated {@link PersistableBundle} should be 142 * consulted for additional information. 143 */ 144 int REASON_PROTOCOL_SPECIFIC_ERROR = 7; 145 146 /** 147 * Invoked when {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} 148 * is successful 149 * 150 * @param session the newly opened {@link RangingSession} 151 */ onOpened(@onNull RangingSession session)152 void onOpened(@NonNull RangingSession session); 153 154 /** 155 * Invoked if {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}} 156 * fails 157 * 158 * @param reason the failure reason 159 * @param params protocol specific parameters 160 */ onOpenFailed(@eason int reason, @NonNull PersistableBundle params)161 void onOpenFailed(@Reason int reason, @NonNull PersistableBundle params); 162 163 /** 164 * Invoked when {@link RangingSession#start(PersistableBundle)} is successful 165 * @param sessionInfo session specific parameters from the lower layers 166 */ onStarted(@onNull PersistableBundle sessionInfo)167 void onStarted(@NonNull PersistableBundle sessionInfo); 168 169 /** 170 * Invoked when {@link RangingSession#start(PersistableBundle)} fails 171 * 172 * @param reason the failure reason 173 * @param params protocol specific parameters 174 */ onStartFailed(@eason int reason, @NonNull PersistableBundle params)175 void onStartFailed(@Reason int reason, @NonNull PersistableBundle params); 176 177 /** 178 * Invoked when a request to reconfigure the session succeeds 179 * 180 * @param params the updated ranging configuration 181 */ onReconfigured(@onNull PersistableBundle params)182 void onReconfigured(@NonNull PersistableBundle params); 183 184 /** 185 * Invoked when a request to reconfigure the session fails 186 * 187 * @param reason reason the session failed to be reconfigured 188 * @param params protocol specific failure reasons 189 */ onReconfigureFailed(@eason int reason, @NonNull PersistableBundle params)190 void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params); 191 192 /** 193 * Invoked when a request to stop the session succeeds 194 * 195 * @param reason reason for the session stop 196 * @param parameters protocol specific parameters related to the stop reason 197 */ onStopped(@eason int reason, @NonNull PersistableBundle parameters)198 void onStopped(@Reason int reason, @NonNull PersistableBundle parameters); 199 200 /** 201 * Invoked when a request to stop the session fails 202 * 203 * @param reason reason the session failed to be stopped 204 * @param params protocol specific failure reasons 205 */ onStopFailed(@eason int reason, @NonNull PersistableBundle params)206 void onStopFailed(@Reason int reason, @NonNull PersistableBundle params); 207 208 /** 209 * Invoked when session is either closed spontaneously, or per user request via 210 * {@link RangingSession#close()} or {@link AutoCloseable#close()}. 211 * 212 * @param reason reason for the session closure 213 * @param parameters protocol specific parameters related to the close reason 214 */ onClosed(@eason int reason, @NonNull PersistableBundle parameters)215 void onClosed(@Reason int reason, @NonNull PersistableBundle parameters); 216 217 /** 218 * Called once per ranging interval even when a ranging measurement fails 219 * 220 * @param rangingReport ranging report for this interval's measurements 221 */ onReportReceived(@onNull RangingReport rangingReport)222 void onReportReceived(@NonNull RangingReport rangingReport); 223 } 224 225 /** 226 * @hide 227 */ RangingSession(Executor executor, Callback callback, IUwbAdapter adapter, SessionHandle sessionHandle)228 public RangingSession(Executor executor, Callback callback, IUwbAdapter adapter, 229 SessionHandle sessionHandle) { 230 mState = State.INIT; 231 mExecutor = executor; 232 mCallback = callback; 233 mAdapter = adapter; 234 mSessionHandle = sessionHandle; 235 } 236 237 /** 238 * @hide 239 */ isOpen()240 public boolean isOpen() { 241 return mState == State.IDLE || mState == State.ACTIVE; 242 } 243 244 /** 245 * Begins ranging for the session. 246 * 247 * <p>On successfully starting a ranging session, 248 * {@link RangingSession.Callback#onStarted(PersistableBundle)} is invoked. 249 * 250 * <p>On failure to start the session, 251 * {@link RangingSession.Callback#onStartFailed(int, PersistableBundle)} is invoked. 252 * 253 * @param params configuration parameters for starting the session 254 */ 255 @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) start(@onNull PersistableBundle params)256 public void start(@NonNull PersistableBundle params) { 257 if (mState != State.IDLE) { 258 throw new IllegalStateException(); 259 } 260 261 try { 262 mAdapter.startRanging(mSessionHandle, params); 263 } catch (RemoteException e) { 264 throw e.rethrowFromSystemServer(); 265 } 266 } 267 268 /** 269 * Attempts to reconfigure the session with the given parameters 270 * <p>This call may be made when the session is open. 271 * 272 * <p>On successfully reconfiguring the session 273 * {@link RangingSession.Callback#onReconfigured(PersistableBundle)} is invoked. 274 * 275 * <p>On failure to reconfigure the session, 276 * {@link RangingSession.Callback#onReconfigureFailed(int, PersistableBundle)} is invoked. 277 * 278 * @param params the parameters to reconfigure and their new values 279 */ 280 @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) reconfigure(@onNull PersistableBundle params)281 public void reconfigure(@NonNull PersistableBundle params) { 282 if (mState != State.ACTIVE && mState != State.IDLE) { 283 throw new IllegalStateException(); 284 } 285 286 try { 287 mAdapter.reconfigureRanging(mSessionHandle, params); 288 } catch (RemoteException e) { 289 throw e.rethrowFromSystemServer(); 290 } 291 } 292 293 /** 294 * Stops actively ranging 295 * 296 * <p>A session that has been stopped may be resumed by calling 297 * {@link RangingSession#start(PersistableBundle)} without the need to open a new session. 298 * 299 * <p>Stopping a {@link RangingSession} is useful when the lower layers should not discard 300 * the parameters of the session, or when a session needs to be able to be resumed quickly. 301 * 302 * <p>If the {@link RangingSession} is no longer needed, use {@link RangingSession#close()} to 303 * completely close the session and allow lower layers of the stack to perform necessarily 304 * cleanup. 305 * 306 * <p>Stopped sessions may be closed by the system at any time. In such a case, 307 * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} is invoked. 308 * 309 * <p>On failure to stop the session, 310 * {@link RangingSession.Callback#onStopFailed(int, PersistableBundle)} is invoked. 311 */ 312 @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) stop()313 public void stop() { 314 if (mState != State.ACTIVE) { 315 throw new IllegalStateException(); 316 } 317 318 try { 319 mAdapter.stopRanging(mSessionHandle); 320 } catch (RemoteException e) { 321 throw e.rethrowFromSystemServer(); 322 } 323 } 324 325 /** 326 * Close the ranging session 327 * 328 * <p>After calling this function, in order resume ranging, a new {@link RangingSession} must 329 * be opened by calling 330 * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}. 331 * 332 * <p>If this session is currently ranging, it will stop and close the session. 333 * <p>If the session is in the process of being opened, it will attempt to stop the session from 334 * being opened. 335 * <p>If the session is already closed, the registered 336 * {@link Callback#onClosed(int, PersistableBundle)} callback will still be invoked. 337 * 338 * <p>{@link Callback#onClosed(int, PersistableBundle)} will be invoked using the same callback 339 * object given to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} 340 * when the {@link RangingSession} was opened. The callback will be invoked after each call to 341 * {@link #close()}, even if the {@link RangingSession} is already closed. 342 */ 343 @Override 344 @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) close()345 public void close() { 346 if (mState == State.CLOSED) { 347 mExecutor.execute(() -> mCallback.onClosed( 348 Callback.REASON_LOCAL_REQUEST, new PersistableBundle())); 349 return; 350 } 351 352 try { 353 mAdapter.closeRanging(mSessionHandle); 354 } catch (RemoteException e) { 355 throw e.rethrowFromSystemServer(); 356 } 357 } 358 359 /** 360 * @hide 361 */ onRangingOpened()362 public void onRangingOpened() { 363 if (mState == State.CLOSED) { 364 Log.w(TAG, "onRangingOpened invoked for a closed session"); 365 return; 366 } 367 368 mState = State.IDLE; 369 executeCallback(() -> mCallback.onOpened(this)); 370 } 371 372 /** 373 * @hide 374 */ onRangingOpenFailed(@allback.Reason int reason, @NonNull PersistableBundle params)375 public void onRangingOpenFailed(@Callback.Reason int reason, 376 @NonNull PersistableBundle params) { 377 if (mState == State.CLOSED) { 378 Log.w(TAG, "onRangingOpenFailed invoked for a closed session"); 379 return; 380 } 381 382 mState = State.CLOSED; 383 executeCallback(() -> mCallback.onOpenFailed(reason, params)); 384 } 385 386 /** 387 * @hide 388 */ onRangingStarted(@onNull PersistableBundle parameters)389 public void onRangingStarted(@NonNull PersistableBundle parameters) { 390 if (mState == State.CLOSED) { 391 Log.w(TAG, "onRangingStarted invoked for a closed session"); 392 return; 393 } 394 395 mState = State.ACTIVE; 396 executeCallback(() -> mCallback.onStarted(parameters)); 397 } 398 399 /** 400 * @hide 401 */ onRangingStartFailed(@allback.Reason int reason, @NonNull PersistableBundle params)402 public void onRangingStartFailed(@Callback.Reason int reason, 403 @NonNull PersistableBundle params) { 404 if (mState == State.CLOSED) { 405 Log.w(TAG, "onRangingStartFailed invoked for a closed session"); 406 return; 407 } 408 409 executeCallback(() -> mCallback.onStartFailed(reason, params)); 410 } 411 412 /** 413 * @hide 414 */ onRangingReconfigured(@onNull PersistableBundle params)415 public void onRangingReconfigured(@NonNull PersistableBundle params) { 416 if (mState == State.CLOSED) { 417 Log.w(TAG, "onRangingReconfigured invoked for a closed session"); 418 return; 419 } 420 421 executeCallback(() -> mCallback.onReconfigured(params)); 422 } 423 424 /** 425 * @hide 426 */ onRangingReconfigureFailed(@allback.Reason int reason, @NonNull PersistableBundle params)427 public void onRangingReconfigureFailed(@Callback.Reason int reason, 428 @NonNull PersistableBundle params) { 429 if (mState == State.CLOSED) { 430 Log.w(TAG, "onRangingReconfigureFailed invoked for a closed session"); 431 return; 432 } 433 434 executeCallback(() -> mCallback.onReconfigureFailed(reason, params)); 435 } 436 437 /** 438 * @hide 439 */ onRangingStopped(@allback.Reason int reason, @NonNull PersistableBundle params)440 public void onRangingStopped(@Callback.Reason int reason, 441 @NonNull PersistableBundle params) { 442 if (mState == State.CLOSED) { 443 Log.w(TAG, "onRangingStopped invoked for a closed session"); 444 return; 445 } 446 447 mState = State.IDLE; 448 executeCallback(() -> mCallback.onStopped(reason, params)); 449 } 450 451 /** 452 * @hide 453 */ onRangingStopFailed(@allback.Reason int reason, @NonNull PersistableBundle params)454 public void onRangingStopFailed(@Callback.Reason int reason, 455 @NonNull PersistableBundle params) { 456 if (mState == State.CLOSED) { 457 Log.w(TAG, "onRangingStopFailed invoked for a closed session"); 458 return; 459 } 460 461 executeCallback(() -> mCallback.onStopFailed(reason, params)); 462 } 463 464 /** 465 * @hide 466 */ onRangingClosed(@allback.Reason int reason, @NonNull PersistableBundle parameters)467 public void onRangingClosed(@Callback.Reason int reason, 468 @NonNull PersistableBundle parameters) { 469 mState = State.CLOSED; 470 executeCallback(() -> mCallback.onClosed(reason, parameters)); 471 } 472 473 /** 474 * @hide 475 */ onRangingResult(@onNull RangingReport report)476 public void onRangingResult(@NonNull RangingReport report) { 477 if (!isOpen()) { 478 Log.w(TAG, "onRangingResult invoked for non-open session"); 479 return; 480 } 481 482 executeCallback(() -> mCallback.onReportReceived(report)); 483 } 484 485 /** 486 * @hide 487 */ executeCallback(@onNull Runnable runnable)488 private void executeCallback(@NonNull Runnable runnable) { 489 final long identity = Binder.clearCallingIdentity(); 490 try { 491 mExecutor.execute(runnable); 492 } finally { 493 Binder.restoreCallingIdentity(identity); 494 } 495 } 496 } 497