diff options
| -rw-r--r-- | core/java/android/uwb/IUwbAdapter.aidl | 84 | ||||
| -rw-r--r-- | core/java/android/uwb/IUwbRangingCallbacks.aidl | 67 | ||||
| -rw-r--r-- | core/java/android/uwb/RangingChangeReason.aidl (renamed from core/java/android/uwb/CloseReason.aidl) | 25 | ||||
| -rw-r--r-- | core/java/android/uwb/RangingManager.java | 154 | ||||
| -rw-r--r-- | core/java/android/uwb/RangingSession.java | 363 | ||||
| -rw-r--r-- | core/java/android/uwb/StartFailureReason.aidl | 52 | ||||
| -rw-r--r-- | core/java/android/uwb/UwbManager.java | 14 | ||||
| -rw-r--r-- | core/tests/uwbtests/src/android/uwb/RangingManagerTest.java | 166 | ||||
| -rw-r--r-- | core/tests/uwbtests/src/android/uwb/RangingSessionTest.java | 244 |
9 files changed, 861 insertions, 308 deletions
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl index 2c8b2e462510..b9c55081a103 100644 --- a/core/java/android/uwb/IUwbAdapter.aidl +++ b/core/java/android/uwb/IUwbAdapter.aidl @@ -119,42 +119,96 @@ interface IUwbAdapter { PersistableBundle getSpecificationInfo(); /** - * Request to start a new ranging session + * Request to open a new ranging session * - * This function must return before calling IUwbAdapterCallbacks - * #onRangingStarted, #onRangingClosed, or #onRangingResult. + * This function must return before calling any functions in + * IUwbAdapterCallbacks. * - * A ranging session does not need to be started before returning. + * This function does not start the ranging session, but all necessary + * components must be initialized and ready to start a new ranging + * session prior to calling IUwbAdapterCallback#onRangingOpened. * - * IUwbAdapterCallbacks#onRangingStarted must be called within - * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called - * if the ranging session is scheduled to start successfully. + * IUwbAdapterCallbacks#onRangingOpened must be called within + * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being + * called if the ranging session is opened successfully. * - * IUwbAdapterCallbacks#onRangingStartFailed must be called within - * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called - * if the ranging session fails to be scheduled to start successfully. + * IUwbAdapterCallbacks#onRangingOpenFailed must be called within + * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being called + * if the ranging session fails to be opened. * * @param rangingCallbacks the callbacks used to deliver ranging information * @param parameters the configuration to use for ranging * @return a SessionHandle used to identify this ranging request */ - SessionHandle startRanging(in IUwbRangingCallbacks rangingCallbacks, - in PersistableBundle parameters); + SessionHandle openRanging(in IUwbRangingCallbacks rangingCallbacks, + in PersistableBundle parameters); + + /** + * Request to start ranging + * + * IUwbAdapterCallbacks#onRangingStarted must be called within + * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being + * called if the ranging session starts successfully. + * + * IUwbAdapterCallbacks#onRangingStartFailed must be called within + * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being + * called if the ranging session fails to be started. + * + * @param sessionHandle the session handle to start ranging for + * @param parameters additional configuration required to start ranging + */ + void startRanging(in SessionHandle sessionHandle, + in PersistableBundle parameters); + + /** + * Request to reconfigure ranging + * + * IUwbAdapterCallbacks#onRangingReconfigured must be called after + * successfully reconfiguring the session. + * + * IUwbAdapterCallbacks#onRangingReconfigureFailed must be called after + * failing to reconfigure the session. + * + * A session must not be modified by a failed call to #reconfigureRanging. + * + * @param sessionHandle the session handle to start ranging for + * @param parameters the parameters to reconfigure and their new values + */ + void reconfigureRanging(in SessionHandle sessionHandle, + in PersistableBundle parameters); + + /** + * Request to stop ranging + * + * IUwbAdapterCallbacks#onRangingStopped must be called after + * successfully stopping the session. + * + * IUwbAdapterCallbacks#onRangingStopFailed must be called after failing + * to stop the session. + * + * @param sessionHandle the session handle to stop ranging for + */ + void stopRanging(in SessionHandle sessionHandle); /** - * Stop and close ranging for the session associated with the given handle + * Close ranging for the session associated with the given handle * * Calling with an invalid handle or a handle that has already been closed * is a no-op. * * IUwbAdapterCallbacks#onRangingClosed must be called within - * RANGING_SESSION_CLOSE_THRESHOLD_MS of #stopRanging being called. + * RANGING_SESSION_CLOSE_THRESHOLD_MS of #closeRanging being called. * - * @param sessionHandle the session handle to stop ranging for + * @param sessionHandle the session handle to close ranging for */ void closeRanging(in SessionHandle sessionHandle); /** + * The maximum allowed time to open a ranging session. + */ + const int RANGING_SESSION_OPEN_THRESHOLD_MS = 3000; // Value TBD + + /** * The maximum allowed time to start a ranging session. */ const int RANGING_SESSION_START_THRESHOLD_MS = 3000; // Value TBD diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl index 1fc3bfd818c3..f71f3ff7ad44 100644 --- a/core/java/android/uwb/IUwbRangingCallbacks.aidl +++ b/core/java/android/uwb/IUwbRangingCallbacks.aidl @@ -17,16 +17,33 @@ package android.uwb; import android.os.PersistableBundle; -import android.uwb.CloseReason; +import android.uwb.RangingChangeReason; import android.uwb.RangingReport; import android.uwb.SessionHandle; -import android.uwb.StartFailureReason; /** * @hide */ interface IUwbRangingCallbacks { /** + * Called when the ranging session has been opened + * + * @param sessionHandle the session the callback is being invoked for + */ + void onRangingOpened(in SessionHandle sessionHandle); + + /** + * Called when a ranging session fails to start + * + * @param sessionHandle the session the callback is being invoked for + * @param reason the reason the session failed to start + * @param parameters protocol specific parameters + */ + void onRangingOpenFailed(in SessionHandle sessionHandle, + RangingChangeReason reason, + in PersistableBundle parameters); + + /** * Called when ranging has started * * May output parameters generated by the lower layers that must be sent to the @@ -47,8 +64,49 @@ interface IUwbRangingCallbacks { * @param reason the reason the session failed to start * @param parameters protocol specific parameters */ - void onRangingStartFailed(in SessionHandle sessionHandle, StartFailureReason reason, + void onRangingStartFailed(in SessionHandle sessionHandle, + RangingChangeReason reason, in PersistableBundle parameters); + + /** + * Called when ranging has been reconfigured + * + * @param sessionHandle the session the callback is being invoked for + * @param parameters the updated ranging configuration + */ + void onRangingReconfigured(in SessionHandle sessionHandle, + in PersistableBundle parameters); + + /** + * Called when a ranging session fails to be reconfigured + * + * @param sessionHandle the session the callback is being invoked for + * @param reason the reason the session failed to reconfigure + * @param parameters protocol specific parameters + */ + void onRangingReconfigureFailed(in SessionHandle sessionHandle, + RangingChangeReason reason, + in PersistableBundle parameters); + + /** + * Called when the ranging session has been stopped + * + * @param sessionHandle the session the callback is being invoked for + */ + + void onRangingStopped(in SessionHandle sessionHandle); + + /** + * Called when a ranging session fails to stop + * + * @param sessionHandle the session the callback is being invoked for + * @param reason the reason the session failed to stop + * @param parameters protocol specific parameters + */ + void onRangingStopFailed(in SessionHandle sessionHandle, + RangingChangeReason reason, + in PersistableBundle parameters); + /** * Called when a ranging session is closed * @@ -56,7 +114,8 @@ interface IUwbRangingCallbacks { * @param reason the reason the session was closed * @param parameters protocol specific parameters */ - void onRangingClosed(in SessionHandle sessionHandle, CloseReason reason, + void onRangingClosed(in SessionHandle sessionHandle, + RangingChangeReason reason, in PersistableBundle parameters); /** diff --git a/core/java/android/uwb/CloseReason.aidl b/core/java/android/uwb/RangingChangeReason.aidl index bef129e2c1c7..19d4b3949d07 100644 --- a/core/java/android/uwb/CloseReason.aidl +++ b/core/java/android/uwb/RangingChangeReason.aidl @@ -20,39 +20,44 @@ package android.uwb; * @hide */ @Backing(type="int") -enum CloseReason { +enum RangingChangeReason { /** * Unknown reason */ UNKNOWN, /** - * A local API call triggered the close, such as a call to - * IUwbAdapter.stopRanging. + * A local API call triggered the change, such as a call to + * IUwbAdapter.closeRanging. */ LOCAL_API, /** - * The maximum number of sessions has been reached. This error may be generated - * for an active session if a higher priority session begins. + * The maximum number of sessions has been reached. This may be generated for + * an active session if a higher priority session begins. */ MAX_SESSIONS_REACHED, /** - * The system state has changed resulting in the session ending (e.g. the user - * disables UWB, or the user's locale changes and an active channel is no longer - * permitted to be used). + * The system state has changed resulting in the session changing (e.g. the + * user disables UWB, or the user's locale changes and an active channel is no + * longer permitted to be used). */ SYSTEM_POLICY, /** - * The remote device has requested to terminate the session + * The remote device has requested to change the session */ REMOTE_REQUEST, /** - * The session was closed for a protocol specific reason + * The session changed for a protocol specific reason */ PROTOCOL_SPECIFIC, + + /** + * The provided parameters were invalid + */ + BAD_PARAMETERS, } diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java index a9bf4abe566a..5ac95d49c1bb 100644 --- a/core/java/android/uwb/RangingManager.java +++ b/core/java/android/uwb/RangingManager.java @@ -50,7 +50,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { @NonNull RangingSession.Callback callbacks) { SessionHandle sessionHandle; try { - sessionHandle = mAdapter.startRanging(this, params); + sessionHandle = mAdapter.openRanging(this, params); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -59,7 +59,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { if (hasSession(sessionHandle)) { Log.w(TAG, "Newly created session unexpectedly reuses an active SessionHandle"); executor.execute(() -> callbacks.onClosed( - RangingSession.Callback.CLOSE_REASON_LOCAL_GENERIC_ERROR, + RangingSession.Callback.REASON_GENERIC_ERROR, new PersistableBundle())); } @@ -75,6 +75,67 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { } @Override + public void onRangingOpened(SessionHandle sessionHandle) { + synchronized (this) { + if (!hasSession(sessionHandle)) { + Log.w(TAG, + "onRangingOpened - received unexpected SessionHandle: " + sessionHandle); + return; + } + + RangingSession session = mRangingSessionTable.get(sessionHandle); + session.onRangingOpened(); + } + } + + @Override + public void onRangingOpenFailed(SessionHandle sessionHandle, @RangingChangeReason int reason, + PersistableBundle parameters) { + synchronized (this) { + if (!hasSession(sessionHandle)) { + Log.w(TAG, + "onRangingOpened - received unexpected SessionHandle: " + sessionHandle); + return; + } + + RangingSession session = mRangingSessionTable.get(sessionHandle); + session.onRangingOpenFailed(convertToReason(reason), parameters); + mRangingSessionTable.remove(sessionHandle); + } + } + + @Override + public void onRangingReconfigured(SessionHandle sessionHandle, PersistableBundle parameters) { + synchronized (this) { + if (!hasSession(sessionHandle)) { + Log.w(TAG, + "onRangingReconfigured - received unexpected SessionHandle: " + + sessionHandle); + return; + } + + RangingSession session = mRangingSessionTable.get(sessionHandle); + session.onRangingReconfigured(parameters); + } + } + + @Override + public void onRangingReconfigureFailed(SessionHandle sessionHandle, + @RangingChangeReason int reason, PersistableBundle params) { + synchronized (this) { + if (!hasSession(sessionHandle)) { + Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: " + + sessionHandle); + return; + } + + RangingSession session = mRangingSessionTable.get(sessionHandle); + session.onRangingReconfigureFailed(convertToReason(reason), params); + } + } + + + @Override public void onRangingStarted(SessionHandle sessionHandle, PersistableBundle parameters) { synchronized (this) { if (!hasSession(sessionHandle)) { @@ -89,7 +150,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { } @Override - public void onRangingStartFailed(SessionHandle sessionHandle, int reason, + public void onRangingStartFailed(SessionHandle sessionHandle, @RangingChangeReason int reason, PersistableBundle params) { synchronized (this) { if (!hasSession(sessionHandle)) { @@ -99,13 +160,42 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { } RangingSession session = mRangingSessionTable.get(sessionHandle); - session.onRangingClosed(convertStartFailureToCloseReason(reason), params); - mRangingSessionTable.remove(sessionHandle); + session.onRangingStartFailed(convertToReason(reason), params); + } + } + + @Override + public void onRangingStopped(SessionHandle sessionHandle) { + synchronized (this) { + if (!hasSession(sessionHandle)) { + Log.w(TAG, "onRangingStopped - received unexpected SessionHandle: " + + sessionHandle); + return; + } + + RangingSession session = mRangingSessionTable.get(sessionHandle); + session.onRangingStopped(); } } @Override - public void onRangingClosed(SessionHandle sessionHandle, int reason, PersistableBundle params) { + public void onRangingStopFailed(SessionHandle sessionHandle, @RangingChangeReason int reason, + PersistableBundle parameters) { + synchronized (this) { + if (!hasSession(sessionHandle)) { + Log.w(TAG, "onRangingStopFailed - received unexpected SessionHandle: " + + sessionHandle); + return; + } + + RangingSession session = mRangingSessionTable.get(sessionHandle); + session.onRangingStopFailed(convertToReason(reason), parameters); + } + } + + @Override + public void onRangingClosed(SessionHandle sessionHandle, @RangingChangeReason int reason, + PersistableBundle params) { synchronized (this) { if (!hasSession(sessionHandle)) { Log.w(TAG, "onRangingClosed - received unexpected SessionHandle: " + sessionHandle); @@ -113,7 +203,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { } RangingSession session = mRangingSessionTable.get(sessionHandle); - session.onRangingClosed(convertToCloseReason(reason), params); + session.onRangingClosed(convertToReason(reason), params); mRangingSessionTable.remove(sessionHandle); } } @@ -131,48 +221,30 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { } } - @RangingSession.Callback.CloseReason - private static int convertToCloseReason(@CloseReason int reason) { + @RangingSession.Callback.Reason + private static int convertToReason(@RangingChangeReason int reason) { switch (reason) { - case CloseReason.LOCAL_API: - return RangingSession.Callback.CLOSE_REASON_LOCAL_CLOSE_API; - - case CloseReason.MAX_SESSIONS_REACHED: - return RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED; - - case CloseReason.SYSTEM_POLICY: - return RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY; + case RangingChangeReason.LOCAL_API: + return RangingSession.Callback.REASON_LOCAL_REQUEST; - case CloseReason.REMOTE_REQUEST: - return RangingSession.Callback.CLOSE_REASON_REMOTE_REQUEST; + case RangingChangeReason.MAX_SESSIONS_REACHED: + return RangingSession.Callback.REASON_MAX_SESSIONS_REACHED; - case CloseReason.PROTOCOL_SPECIFIC: - return RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC; - - case CloseReason.UNKNOWN: - default: - return RangingSession.Callback.CLOSE_REASON_UNKNOWN; - } - } - - @RangingSession.Callback.CloseReason - private static int convertStartFailureToCloseReason(@StartFailureReason int reason) { - switch (reason) { - case StartFailureReason.BAD_PARAMETERS: - return RangingSession.Callback.CLOSE_REASON_LOCAL_BAD_PARAMETERS; + case RangingChangeReason.SYSTEM_POLICY: + return RangingSession.Callback.REASON_SYSTEM_POLICY; - case StartFailureReason.MAX_SESSIONS_REACHED: - return RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED; + case RangingChangeReason.REMOTE_REQUEST: + return RangingSession.Callback.REASON_REMOTE_REQUEST; - case StartFailureReason.SYSTEM_POLICY: - return RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY; + case RangingChangeReason.PROTOCOL_SPECIFIC: + return RangingSession.Callback.REASON_PROTOCOL_SPECIFIC_ERROR; - case StartFailureReason.PROTOCOL_SPECIFIC: - return RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC; + case RangingChangeReason.BAD_PARAMETERS: + return RangingSession.Callback.REASON_BAD_PARAMETERS; - case StartFailureReason.UNKNOWN: + case RangingChangeReason.UNKNOWN: default: - return RangingSession.Callback.CLOSE_REASON_UNKNOWN; + return RangingSession.Callback.REASON_UNKNOWN; } } } diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java index b0dbd85c0812..0f87af415825 100644 --- a/core/java/android/uwb/RangingSession.java +++ b/core/java/android/uwb/RangingSession.java @@ -36,9 +36,9 @@ import java.util.concurrent.Executor; * <p>To get an instance of {@link RangingSession}, first use * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} to request to open a * session. Once the session is opened, a {@link RangingSession} object is provided through - * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)}. If opening a - * session fails, the failure is reported through - * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} with the failure reason. + * {@link RangingSession.Callback#onOpened(RangingSession)}. If opening a session fails, the failure + * is reported through {@link RangingSession.Callback#onOpenFailed(int, PersistableBundle)} with the + * failure reason. * * @hide */ @@ -50,103 +50,162 @@ public final class RangingSession implements AutoCloseable { private final Callback mCallback; private enum State { + /** + * The state of the {@link RangingSession} until + * {@link RangingSession.Callback#onOpened(RangingSession)} is invoked + */ INIT, - OPEN, - CLOSED, + + /** + * The {@link RangingSession} is initialized and ready to begin ranging + */ + IDLE, + + /** + * The {@link RangingSession} is actively ranging + */ + ACTIVE, + + /** + * The {@link RangingSession} is closed and may not be used for ranging. + */ + CLOSED } - private State mState; + private State mState = State.INIT; /** * Interface for receiving {@link RangingSession} events */ public interface Callback { /** - * Invoked when {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} - * is successful - * - * @param session the newly opened {@link RangingSession} - * @param sessionInfo session specific parameters from lower layers - */ - void onOpenSuccess(@NonNull RangingSession session, @NonNull PersistableBundle sessionInfo); - - /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { - CLOSE_REASON_UNKNOWN, - CLOSE_REASON_LOCAL_CLOSE_API, - CLOSE_REASON_LOCAL_BAD_PARAMETERS, - CLOSE_REASON_LOCAL_GENERIC_ERROR, - CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED, - CLOSE_REASON_LOCAL_SYSTEM_POLICY, - CLOSE_REASON_REMOTE_GENERIC_ERROR, - CLOSE_REASON_REMOTE_REQUEST}) - @interface CloseReason {} + REASON_UNKNOWN, + REASON_LOCAL_REQUEST, + REASON_REMOTE_REQUEST, + REASON_BAD_PARAMETERS, + REASON_GENERIC_ERROR, + REASON_MAX_SESSIONS_REACHED, + REASON_SYSTEM_POLICY, + REASON_PROTOCOL_SPECIFIC_ERROR}) + @interface Reason {} /** * Indicates that the session was closed or failed to open due to an unknown reason */ - int CLOSE_REASON_UNKNOWN = 0; + int REASON_UNKNOWN = 0; /** * Indicates that the session was closed or failed to open because * {@link AutoCloseable#close()} or {@link RangingSession#close()} was called */ - int CLOSE_REASON_LOCAL_CLOSE_API = 1; + int REASON_LOCAL_REQUEST = 1; /** - * Indicates that the session failed to open due to erroneous parameters passed - * to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} + * Indicates that the session was closed or failed to open due to an explicit request from + * the remote device. */ - int CLOSE_REASON_LOCAL_BAD_PARAMETERS = 2; + int REASON_REMOTE_REQUEST = 2; /** - * Indicates that the session was closed due to some local error on this device besides the - * error code already listed + * Indicates that the session was closed or failed to open due to erroneous parameters */ - int CLOSE_REASON_LOCAL_GENERIC_ERROR = 3; + int REASON_BAD_PARAMETERS = 3; /** - * Indicates that the session failed to open because the number of currently open sessions - * is equal to {@link UwbManager#getMaxSimultaneousSessions()} + * Indicates an error on this device besides the error code already listed */ - int CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED = 4; + int REASON_GENERIC_ERROR = 4; /** - * Indicates that the session was closed or failed to open due to local system policy, such + * Indicates that the number of currently open sessions is equal to + * {@link UwbManager#getMaxSimultaneousSessions()} and additional sessions may not be + * opened. + */ + int REASON_MAX_SESSIONS_REACHED = 5; + + /** + * Indicates that the local system policy caused the change, such * as privacy policy, power management policy, permissions, and more. */ - int CLOSE_REASON_LOCAL_SYSTEM_POLICY = 5; + int REASON_SYSTEM_POLICY = 6; /** - * Indicates that the session was closed or failed to open due to an error with the remote - * device besides error codes already listed. + * Indicates a protocol specific error. The associated {@link PersistableBundle} should be + * consulted for additional information. */ - int CLOSE_REASON_REMOTE_GENERIC_ERROR = 6; + int REASON_PROTOCOL_SPECIFIC_ERROR = 7; /** - * Indicates that the session was closed or failed to open due to an explicit request from - * the remote device. + * Invoked when {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} + * is successful + * + * @param session the newly opened {@link RangingSession} + */ + void onOpened(@NonNull RangingSession session); + + /** + * Invoked if {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}} + * fails + * + * @param reason the failure reason + * @param params protocol specific parameters + */ + void onOpenFailed(@Reason int reason, @NonNull PersistableBundle params); + + /** + * Invoked when {@link RangingSession#start(PersistableBundle)} is successful + * @param sessionInfo session specific parameters from the lower layers + */ + void onStarted(@NonNull PersistableBundle sessionInfo); + + /** + * Invoked when {@link RangingSession#start(PersistableBundle)} fails + * + * @param reason the failure reason + * @param params protocol specific parameters + */ + void onStartFailed(@Reason int reason, @NonNull PersistableBundle params); + + /** + * Invoked when a request to reconfigure the session succeeds + * + * @param params the updated ranging configuration + */ + void onReconfigured(@NonNull PersistableBundle params); + + /** + * Invoked when a request to reconfigure the session fails + * + * @param reason reason the session failed to be reconfigured + * @param params protocol specific failure reasons */ - int CLOSE_REASON_REMOTE_REQUEST = 7; + void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params); /** - * Indicates that the session was closed for a protocol specific reason. The associated - * {@link PersistableBundle} should be consulted for additional information. + * Invoked when a request to stop the session succeeds */ - int CLOSE_REASON_PROTOCOL_SPECIFIC = 8; + void onStopped(); /** + * Invoked when a request to stop the session fails + * + * @param reason reason the session failed to be stopped + * @param params protocol specific failure reasons + */ + void onStopFailed(@Reason int reason, @NonNull PersistableBundle params); + + /** * Invoked when session is either closed spontaneously, or per user request via - * {@link RangingSession#close()} or {@link AutoCloseable#close()}, or when session failed - * to open. + * {@link RangingSession#close()} or {@link AutoCloseable#close()}. * * @param reason reason for the session closure * @param parameters protocol specific parameters related to the close reason */ - void onClosed(@CloseReason int reason, @NonNull PersistableBundle parameters); + void onClosed(@Reason int reason, @NonNull PersistableBundle parameters); /** * Called once per ranging interval even when a ranging measurement fails @@ -172,12 +231,95 @@ public final class RangingSession implements AutoCloseable { * @hide */ public boolean isOpen() { - return mState == State.OPEN; + return mState == State.IDLE || mState == State.ACTIVE; + } + + /** + * Begins ranging for the session. + * + * <p>On successfully starting a ranging session, + * {@link RangingSession.Callback#onStarted(PersistableBundle)} is invoked. + * + * <p>On failure to start the session, + * {@link RangingSession.Callback#onStartFailed(int, PersistableBundle)} is invoked. + * + * @param params configuration parameters for starting the session + */ + public void start(@NonNull PersistableBundle params) { + if (mState != State.IDLE) { + throw new IllegalStateException(); + } + + try { + mAdapter.startRanging(mSessionHandle, params); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Attempts to reconfigure the session with the given parameters + * <p>This call may be made when the session is open. + * + * <p>On successfully reconfiguring the session + * {@link RangingSession.Callback#onReconfigured(PersistableBundle)} is invoked. + * + * <p>On failure to reconfigure the session, + * {@link RangingSession.Callback#onReconfigureFailed(int, PersistableBundle)} is invoked. + * + * @param params the parameters to reconfigure and their new values + */ + public void reconfigure(@NonNull PersistableBundle params) { + if (mState != State.ACTIVE && mState != State.IDLE) { + throw new IllegalStateException(); + } + + try { + mAdapter.reconfigureRanging(mSessionHandle, params); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Stops actively ranging + * + * <p>A session that has been stopped may be resumed by calling + * {@link RangingSession#start(PersistableBundle)} without the need to open a new session. + * + * <p>Stopping a {@link RangingSession} is useful when the lower layers should not discard + * the parameters of the session, or when a session needs to be able to be resumed quickly. + * + * <p>If the {@link RangingSession} is no longer needed, use {@link RangingSession#close()} to + * completely close the session and allow lower layers of the stack to perform necessarily + * cleanup. + * + * <p>Stopped sessions may be closed by the system at any time. In such a case, + * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} is invoked. + * + * <p>On failure to stop the session, + * {@link RangingSession.Callback#onStopFailed(int, PersistableBundle)} is invoked. + */ + public void stop() { + if (mState != State.ACTIVE) { + throw new IllegalStateException(); + } + + try { + mAdapter.stopRanging(mSessionHandle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** * Close the ranging session - * <p>If this session is currently open, it will close and stop the session. + * + * <p>After calling this function, in order resume ranging, a new {@link RangingSession} must + * be opened by calling + * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}. + * + * <p>If this session is currently ranging, it will stop and close the session. * <p>If the session is in the process of being opened, it will attempt to stop the session from * being opened. * <p>If the session is already closed, the registered @@ -192,7 +334,7 @@ public final class RangingSession implements AutoCloseable { public void close() { if (mState == State.CLOSED) { mExecutor.execute(() -> mCallback.onClosed( - Callback.CLOSE_REASON_LOCAL_CLOSE_API, new PersistableBundle())); + Callback.REASON_LOCAL_REQUEST, new PersistableBundle())); return; } @@ -206,32 +348,114 @@ public final class RangingSession implements AutoCloseable { /** * @hide */ + public void onRangingOpened() { + if (mState == State.CLOSED) { + Log.w(TAG, "onRangingOpened invoked for a closed session"); + return; + } + + mState = State.IDLE; + executeCallback(() -> mCallback.onOpened(this)); + } + + /** + * @hide + */ + public void onRangingOpenFailed(@Callback.Reason int reason, + @NonNull PersistableBundle params) { + if (mState == State.CLOSED) { + Log.w(TAG, "onRangingOpenFailed invoked for a closed session"); + return; + } + + mState = State.CLOSED; + executeCallback(() -> mCallback.onOpenFailed(reason, params)); + } + + /** + * @hide + */ public void onRangingStarted(@NonNull PersistableBundle parameters) { if (mState == State.CLOSED) { Log.w(TAG, "onRangingStarted invoked for a closed session"); return; } - mState = State.OPEN; - final long identity = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onOpenSuccess(this, parameters)); - } finally { - Binder.restoreCallingIdentity(identity); + mState = State.ACTIVE; + executeCallback(() -> mCallback.onStarted(parameters)); + } + + /** + * @hide + */ + public void onRangingStartFailed(@Callback.Reason int reason, + @NonNull PersistableBundle params) { + if (mState == State.CLOSED) { + Log.w(TAG, "onRangingStartFailed invoked for a closed session"); + return; } + + executeCallback(() -> mCallback.onStartFailed(reason, params)); } /** * @hide */ - public void onRangingClosed(@Callback.CloseReason int reason, PersistableBundle parameters) { - mState = State.CLOSED; - final long identity = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onClosed(reason, parameters)); - } finally { - Binder.restoreCallingIdentity(identity); + public void onRangingReconfigured(@NonNull PersistableBundle params) { + if (mState == State.CLOSED) { + Log.w(TAG, "onRangingReconfigured invoked for a closed session"); + return; + } + + executeCallback(() -> mCallback.onReconfigured(params)); + } + + /** + * @hide + */ + public void onRangingReconfigureFailed(@Callback.Reason int reason, + @NonNull PersistableBundle params) { + if (mState == State.CLOSED) { + Log.w(TAG, "onRangingReconfigureFailed invoked for a closed session"); + return; + } + + executeCallback(() -> mCallback.onReconfigureFailed(reason, params)); + } + + /** + * @hide + */ + public void onRangingStopped() { + if (mState == State.CLOSED) { + Log.w(TAG, "onRangingStopped invoked for a closed session"); + return; } + + mState = State.IDLE; + executeCallback(() -> mCallback.onStopped()); + } + + /** + * @hide + */ + public void onRangingStopFailed(@Callback.Reason int reason, + @NonNull PersistableBundle params) { + if (mState == State.CLOSED) { + Log.w(TAG, "onRangingStopFailed invoked for a closed session"); + return; + } + + executeCallback(() -> mCallback.onStopFailed(reason, params)); + } + + /** + * @hide + */ + public void onRangingClosed(@Callback.Reason int reason, + @NonNull PersistableBundle parameters) { + mState = State.CLOSED; + executeCallback(() -> mCallback.onClosed(reason, parameters)); } /** @@ -243,9 +467,16 @@ public final class RangingSession implements AutoCloseable { return; } + executeCallback(() -> mCallback.onReportReceived(report)); + } + + /** + * @hide + */ + private void executeCallback(@NonNull Runnable runnable) { final long identity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> mCallback.onReportReceived(report)); + mExecutor.execute(runnable); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/core/java/android/uwb/StartFailureReason.aidl b/core/java/android/uwb/StartFailureReason.aidl deleted file mode 100644 index 4d9c962f529b..000000000000 --- a/core/java/android/uwb/StartFailureReason.aidl +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.uwb; - -/** - * @hide - */ -@Backing(type="int") -enum StartFailureReason { - /** - * Unknown start failure reason - */ - UNKNOWN, - - /** - * The provided parameters were invalid and ranging could not start - */ - BAD_PARAMETERS, - - /** - * The maximum number of sessions has been reached. This error may be generated - * for an active session if a higher priority session begins. - */ - MAX_SESSIONS_REACHED, - - /** - * The system state has changed resulting in the session ending (e.g. the user - * disables UWB, or the user's locale changes and an active channel is no longer - * permitted to be used). - */ - SYSTEM_POLICY, - - /** - * The session could not start because of a protocol specific reason. - */ - PROTOCOL_SPECIFIC, -} - diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index f4d801868e18..15ee5b5f22eb 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -369,13 +369,13 @@ public final class UwbManager { /** * Open a {@link RangingSession} with the given parameters - * <p>This function is asynchronous and will return before ranging begins. The - * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)} function is - * called with a {@link RangingSession} object used to control ranging when the session is - * successfully opened. + * <p>The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a + * {@link RangingSession} object used to control ranging when the session is successfully + * opened. * - * <p>If a session cannot be opened, then {@link RangingSession.Callback#onClosed(int)} will be - * invoked with the appropriate {@link RangingSession.Callback.CloseReason}. + * <p>If a session cannot be opened, then + * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} will be invoked with the + * appropriate {@link RangingSession.Callback.Reason}. * * <p>An open {@link RangingSession} will be automatically closed if client application process * dies. @@ -391,7 +391,7 @@ public final class UwbManager { * @return an {@link AutoCloseable} that is able to be used to close or cancel the opening of a * {@link RangingSession} that has been requested through {@link #openRangingSession} * but has not yet been made available by - * {@link RangingSession.Callback#onOpenSuccess}. + * {@link RangingSession.Callback#onOpened(RangingSession)}. */ @NonNull public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters, diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java index 6df1c3ed220f..c01bb75c32aa 100644 --- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java +++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java @@ -45,14 +45,14 @@ public class RangingManagerTest { private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class); private static final Executor EXECUTOR = UwbTestUtils.getExecutor(); private static final PersistableBundle PARAMS = new PersistableBundle(); - private static final @CloseReason int CLOSE_REASON = CloseReason.UNKNOWN; + private static final @RangingChangeReason int REASON = RangingChangeReason.UNKNOWN; @Test - public void testOpenSession_StartRangingInvoked() throws RemoteException { + public void testOpenSession_OpenRangingInvoked() throws RemoteException { RangingManager rangingManager = new RangingManager(ADAPTER); RangingSession.Callback callback = mock(RangingSession.Callback.class); rangingManager.openSession(PARAMS, EXECUTOR, callback); - verify(ADAPTER, times(1)).startRanging(eq(rangingManager), eq(PARAMS)); + verify(ADAPTER, times(1)).openRanging(eq(rangingManager), eq(PARAMS)); } @Test @@ -60,7 +60,7 @@ public class RangingManagerTest { RangingManager rangingManager = new RangingManager(ADAPTER); RangingSession.Callback callback = mock(RangingSession.Callback.class); SessionHandle handle = new SessionHandle(1); - when(ADAPTER.startRanging(any(), any())).thenReturn(handle); + when(ADAPTER.openRanging(any(), any())).thenReturn(handle); rangingManager.openSession(PARAMS, EXECUTOR, callback); @@ -73,34 +73,34 @@ public class RangingManagerTest { } @Test - public void testOnRangingStarted_ValidSessionHandle() throws RemoteException { + public void testOnRangingOpened_ValidSessionHandle() throws RemoteException { RangingManager rangingManager = new RangingManager(ADAPTER); RangingSession.Callback callback = mock(RangingSession.Callback.class); SessionHandle handle = new SessionHandle(1); - when(ADAPTER.startRanging(any(), any())).thenReturn(handle); + when(ADAPTER.openRanging(any(), any())).thenReturn(handle); rangingManager.openSession(PARAMS, EXECUTOR, callback); - rangingManager.onRangingStarted(handle, PARAMS); - verify(callback, times(1)).onOpenSuccess(any(), any()); + rangingManager.onRangingOpened(handle); + verify(callback, times(1)).onOpened(any()); } @Test - public void testOnRangingStarted_InvalidSessionHandle() throws RemoteException { + public void testOnRangingOpened_InvalidSessionHandle() throws RemoteException { RangingManager rangingManager = new RangingManager(ADAPTER); RangingSession.Callback callback = mock(RangingSession.Callback.class); - rangingManager.onRangingStarted(new SessionHandle(2), PARAMS); - verify(callback, times(0)).onOpenSuccess(any(), any()); + rangingManager.onRangingOpened(new SessionHandle(2)); + verify(callback, times(0)).onOpened(any()); } @Test - public void testOnRangingStarted_MultipleSessionsRegistered() throws RemoteException { + public void testOnRangingOpened_MultipleSessionsRegistered() throws RemoteException { SessionHandle sessionHandle1 = new SessionHandle(1); SessionHandle sessionHandle2 = new SessionHandle(2); RangingSession.Callback callback1 = mock(RangingSession.Callback.class); RangingSession.Callback callback2 = mock(RangingSession.Callback.class); - when(ADAPTER.startRanging(any(), any())) + when(ADAPTER.openRanging(any(), any())) .thenReturn(sessionHandle1) .thenReturn(sessionHandle2); @@ -108,25 +108,50 @@ public class RangingManagerTest { rangingManager.openSession(PARAMS, EXECUTOR, callback1); rangingManager.openSession(PARAMS, EXECUTOR, callback2); - rangingManager.onRangingStarted(sessionHandle1, PARAMS); - verify(callback1, times(1)).onOpenSuccess(any(), any()); - verify(callback2, times(0)).onOpenSuccess(any(), any()); + rangingManager.onRangingOpened(sessionHandle1); + verify(callback1, times(1)).onOpened(any()); + verify(callback2, times(0)).onOpened(any()); - rangingManager.onRangingStarted(sessionHandle2, PARAMS); - verify(callback1, times(1)).onOpenSuccess(any(), any()); - verify(callback2, times(1)).onOpenSuccess(any(), any()); + rangingManager.onRangingOpened(sessionHandle2); + verify(callback1, times(1)).onOpened(any()); + verify(callback2, times(1)).onOpened(any()); } @Test - public void testOnRangingClosed_OnRangingClosedCalled() throws RemoteException { + public void testCorrectCallbackInvoked() throws RemoteException { RangingManager rangingManager = new RangingManager(ADAPTER); RangingSession.Callback callback = mock(RangingSession.Callback.class); SessionHandle handle = new SessionHandle(1); - when(ADAPTER.startRanging(any(), any())).thenReturn(handle); + when(ADAPTER.openRanging(any(), any())).thenReturn(handle); + rangingManager.openSession(PARAMS, EXECUTOR, callback); + rangingManager.onRangingOpened(handle); + verify(callback, times(1)).onOpened(any()); + + rangingManager.onRangingStarted(handle, PARAMS); + verify(callback, times(1)).onStarted(eq(PARAMS)); + + rangingManager.onRangingStartFailed(handle, REASON, PARAMS); + verify(callback, times(1)).onStartFailed(eq(REASON), eq(PARAMS)); + + RangingReport report = UwbTestUtils.getRangingReports(1); + rangingManager.onRangingResult(handle, report); + verify(callback, times(1)).onReportReceived(eq(report)); - rangingManager.onRangingClosed(handle, CLOSE_REASON, PARAMS); - verify(callback, times(1)).onClosed(anyInt(), any()); + rangingManager.onRangingReconfigured(handle, PARAMS); + verify(callback, times(1)).onReconfigured(eq(PARAMS)); + + rangingManager.onRangingReconfigureFailed(handle, REASON, PARAMS); + verify(callback, times(1)).onReconfigureFailed(eq(REASON), eq(PARAMS)); + + rangingManager.onRangingStopped(handle); + verify(callback, times(1)).onStopped(); + + rangingManager.onRangingStopFailed(handle, REASON, PARAMS); + verify(callback, times(1)).onStopFailed(eq(REASON), eq(PARAMS)); + + rangingManager.onRangingClosed(handle, REASON, PARAMS); + verify(callback, times(1)).onClosed(eq(REASON), eq(PARAMS)); } @Test @@ -138,7 +163,7 @@ public class RangingManagerTest { RangingSession.Callback callback1 = mock(RangingSession.Callback.class); RangingSession.Callback callback2 = mock(RangingSession.Callback.class); - when(ADAPTER.startRanging(any(), any())) + when(ADAPTER.openRanging(any(), any())) .thenReturn(sessionHandle1) .thenReturn(sessionHandle2); @@ -146,37 +171,23 @@ public class RangingManagerTest { rangingManager.openSession(PARAMS, EXECUTOR, callback1); rangingManager.openSession(PARAMS, EXECUTOR, callback2); - rangingManager.onRangingClosed(sessionHandle1, CLOSE_REASON, PARAMS); + rangingManager.onRangingClosed(sessionHandle1, REASON, PARAMS); verify(callback1, times(1)).onClosed(anyInt(), any()); verify(callback2, times(0)).onClosed(anyInt(), any()); - rangingManager.onRangingClosed(sessionHandle2, CLOSE_REASON, PARAMS); + rangingManager.onRangingClosed(sessionHandle2, REASON, PARAMS); verify(callback1, times(1)).onClosed(anyInt(), any()); verify(callback2, times(1)).onClosed(anyInt(), any()); } @Test - public void testOnRangingReport_OnReportReceived() throws RemoteException { - RangingManager rangingManager = new RangingManager(ADAPTER); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - SessionHandle handle = new SessionHandle(1); - when(ADAPTER.startRanging(any(), any())).thenReturn(handle); - rangingManager.openSession(PARAMS, EXECUTOR, callback); - rangingManager.onRangingStarted(handle, PARAMS); - - RangingReport report = UwbTestUtils.getRangingReports(1); - rangingManager.onRangingResult(handle, report); - verify(callback, times(1)).onReportReceived(eq(report)); - } - - @Test public void testOnRangingReport_MultipleSessionsRegistered() throws RemoteException { SessionHandle sessionHandle1 = new SessionHandle(1); SessionHandle sessionHandle2 = new SessionHandle(2); RangingSession.Callback callback1 = mock(RangingSession.Callback.class); RangingSession.Callback callback2 = mock(RangingSession.Callback.class); - when(ADAPTER.startRanging(any(), any())) + when(ADAPTER.openRanging(any(), any())) .thenReturn(sessionHandle1) .thenReturn(sessionHandle2); @@ -196,65 +207,54 @@ public class RangingManagerTest { } @Test - public void testOnClose_Reasons() throws RemoteException { - runOnClose_Reason(CloseReason.LOCAL_API, - RangingSession.Callback.CLOSE_REASON_LOCAL_CLOSE_API); + public void testReasons() throws RemoteException { + runReason(RangingChangeReason.LOCAL_API, + RangingSession.Callback.REASON_LOCAL_REQUEST); + + runReason(RangingChangeReason.MAX_SESSIONS_REACHED, + RangingSession.Callback.REASON_MAX_SESSIONS_REACHED); - runOnClose_Reason(CloseReason.MAX_SESSIONS_REACHED, - RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED); + runReason(RangingChangeReason.PROTOCOL_SPECIFIC, + RangingSession.Callback.REASON_PROTOCOL_SPECIFIC_ERROR); - runOnClose_Reason(CloseReason.PROTOCOL_SPECIFIC, - RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC); + runReason(RangingChangeReason.REMOTE_REQUEST, + RangingSession.Callback.REASON_REMOTE_REQUEST); - runOnClose_Reason(CloseReason.REMOTE_REQUEST, - RangingSession.Callback.CLOSE_REASON_REMOTE_REQUEST); + runReason(RangingChangeReason.SYSTEM_POLICY, + RangingSession.Callback.REASON_SYSTEM_POLICY); - runOnClose_Reason(CloseReason.SYSTEM_POLICY, - RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY); + runReason(RangingChangeReason.BAD_PARAMETERS, + RangingSession.Callback.REASON_BAD_PARAMETERS); - runOnClose_Reason(CloseReason.UNKNOWN, - RangingSession.Callback.CLOSE_REASON_UNKNOWN); + runReason(RangingChangeReason.UNKNOWN, + RangingSession.Callback.REASON_UNKNOWN); } - private void runOnClose_Reason(@CloseReason int reasonIn, - @RangingSession.Callback.CloseReason int reasonOut) throws RemoteException { + private void runReason(@RangingChangeReason int reasonIn, + @RangingSession.Callback.Reason int reasonOut) throws RemoteException { RangingManager rangingManager = new RangingManager(ADAPTER); RangingSession.Callback callback = mock(RangingSession.Callback.class); SessionHandle handle = new SessionHandle(1); - when(ADAPTER.startRanging(any(), any())).thenReturn(handle); + when(ADAPTER.openRanging(any(), any())).thenReturn(handle); rangingManager.openSession(PARAMS, EXECUTOR, callback); - rangingManager.onRangingClosed(handle, reasonIn, PARAMS); - verify(callback, times(1)).onClosed(eq(reasonOut), eq(PARAMS)); - } - - @Test - public void testStartFailureReasons() throws RemoteException { - runOnRangingStartFailed_Reason(StartFailureReason.BAD_PARAMETERS, - RangingSession.Callback.CLOSE_REASON_LOCAL_BAD_PARAMETERS); + rangingManager.onRangingOpenFailed(handle, reasonIn, PARAMS); + verify(callback, times(1)).onOpenFailed(eq(reasonOut), eq(PARAMS)); - runOnRangingStartFailed_Reason(StartFailureReason.MAX_SESSIONS_REACHED, - RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED); + // Open a new session + rangingManager.openSession(PARAMS, EXECUTOR, callback); + rangingManager.onRangingOpened(handle); - runOnRangingStartFailed_Reason(StartFailureReason.PROTOCOL_SPECIFIC, - RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC); + rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS); + verify(callback, times(1)).onStartFailed(eq(reasonOut), eq(PARAMS)); - runOnRangingStartFailed_Reason(StartFailureReason.SYSTEM_POLICY, - RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY); + rangingManager.onRangingReconfigureFailed(handle, reasonIn, PARAMS); + verify(callback, times(1)).onReconfigureFailed(eq(reasonOut), eq(PARAMS)); - runOnRangingStartFailed_Reason(StartFailureReason.UNKNOWN, - RangingSession.Callback.CLOSE_REASON_UNKNOWN); - } - - private void runOnRangingStartFailed_Reason(@StartFailureReason int reasonIn, - @RangingSession.Callback.CloseReason int reasonOut) throws RemoteException { - RangingManager rangingManager = new RangingManager(ADAPTER); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - SessionHandle handle = new SessionHandle(1); - when(ADAPTER.startRanging(any(), any())).thenReturn(handle); - rangingManager.openSession(PARAMS, EXECUTOR, callback); + rangingManager.onRangingStopFailed(handle, reasonIn, PARAMS); + verify(callback, times(1)).onStopFailed(eq(reasonOut), eq(PARAMS)); - rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS); + rangingManager.onRangingClosed(handle, reasonIn, PARAMS); verify(callback, times(1)).onClosed(eq(reasonOut), eq(PARAMS)); } } diff --git a/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java b/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java index 702c68ebc9de..e5eea26f5d11 100644 --- a/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java +++ b/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java @@ -19,9 +19,11 @@ package android.uwb; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -34,6 +36,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.util.concurrent.Executor; @@ -43,47 +47,48 @@ import java.util.concurrent.Executor; @SmallTest @RunWith(AndroidJUnit4.class) public class RangingSessionTest { - private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class); private static final Executor EXECUTOR = UwbTestUtils.getExecutor(); private static final PersistableBundle PARAMS = new PersistableBundle(); - private static final @RangingSession.Callback.CloseReason int CLOSE_REASON = - RangingSession.Callback.CLOSE_REASON_LOCAL_GENERIC_ERROR; + private static final @RangingSession.Callback.Reason int REASON = + RangingSession.Callback.REASON_GENERIC_ERROR; @Test - public void testOnRangingStarted_OnOpenSuccessCalled() { + public void testOnRangingOpened_OnOpenSuccessCalled() { SessionHandle handle = new SessionHandle(123); RangingSession.Callback callback = mock(RangingSession.Callback.class); - RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); verifyOpenState(session, false); - session.onRangingStarted(PARAMS); + session.onRangingOpened(); verifyOpenState(session, true); // Verify that the onOpenSuccess callback was invoked - verify(callback, times(1)).onOpenSuccess(eq(session), any()); + verify(callback, times(1)).onOpened(eq(session)); verify(callback, times(0)).onClosed(anyInt(), any()); } @Test - public void testOnRangingStarted_CannotOpenClosedSession() { + public void testOnRangingOpened_CannotOpenClosedSession() { SessionHandle handle = new SessionHandle(123); RangingSession.Callback callback = mock(RangingSession.Callback.class); - RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - session.onRangingStarted(PARAMS); + session.onRangingOpened(); verifyOpenState(session, true); - verify(callback, times(1)).onOpenSuccess(eq(session), any()); + verify(callback, times(1)).onOpened(eq(session)); verify(callback, times(0)).onClosed(anyInt(), any()); - session.onRangingClosed(CLOSE_REASON, PARAMS); + session.onRangingClosed(REASON, PARAMS); verifyOpenState(session, false); - verify(callback, times(1)).onOpenSuccess(eq(session), any()); + verify(callback, times(1)).onOpened(eq(session)); verify(callback, times(1)).onClosed(anyInt(), any()); // Now invoke the ranging started callback and ensure the session remains closed - session.onRangingStarted(PARAMS); + session.onRangingOpened(); verifyOpenState(session, false); - verify(callback, times(1)).onOpenSuccess(eq(session), any()); + verify(callback, times(1)).onOpened(eq(session)); verify(callback, times(1)).onClosed(anyInt(), any()); } @@ -91,27 +96,30 @@ public class RangingSessionTest { public void testOnRangingClosed_OnClosedCalledWhenSessionNotOpen() { SessionHandle handle = new SessionHandle(123); RangingSession.Callback callback = mock(RangingSession.Callback.class); - RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); verifyOpenState(session, false); - session.onRangingClosed(CLOSE_REASON, PARAMS); + session.onRangingClosed(REASON, PARAMS); verifyOpenState(session, false); // Verify that the onOpenSuccess callback was invoked - verify(callback, times(0)).onOpenSuccess(eq(session), any()); + verify(callback, times(0)).onOpened(eq(session)); verify(callback, times(1)).onClosed(anyInt(), any()); } - @Test public void testOnRangingClosed_OnClosedCalled() { + @Test + public void testOnRangingClosed_OnClosedCalled() { SessionHandle handle = new SessionHandle(123); RangingSession.Callback callback = mock(RangingSession.Callback.class); - RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); session.onRangingStarted(PARAMS); - session.onRangingClosed(CLOSE_REASON, PARAMS); + session.onRangingClosed(REASON, PARAMS); verify(callback, times(1)).onClosed(anyInt(), any()); verifyOpenState(session, false); - session.onRangingClosed(CLOSE_REASON, PARAMS); + session.onRangingClosed(REASON, PARAMS); verify(callback, times(2)).onClosed(anyInt(), any()); } @@ -119,7 +127,8 @@ public class RangingSessionTest { public void testOnRangingResult_OnReportReceivedCalled() { SessionHandle handle = new SessionHandle(123); RangingSession.Callback callback = mock(RangingSession.Callback.class); - RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); verifyOpenState(session, false); session.onRangingStarted(PARAMS); @@ -131,11 +140,83 @@ public class RangingSessionTest { } @Test - public void testClose() throws RemoteException { + public void testStart_CannotStartIfAlreadyStarted() throws RemoteException { SessionHandle handle = new SessionHandle(123); RangingSession.Callback callback = mock(RangingSession.Callback.class); - RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); + doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); + session.onRangingOpened(); + + session.start(PARAMS); + verify(callback, times(1)).onStarted(any()); + + // Calling start again should throw an illegal state + verifyThrowIllegalState(() -> session.start(PARAMS)); + verify(callback, times(1)).onStarted(any()); + } + + @Test + public void testStop_CannotStopIfAlreadyStopped() throws RemoteException { + SessionHandle handle = new SessionHandle(123); + RangingSession.Callback callback = mock(RangingSession.Callback.class); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); + doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); + doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any()); + session.onRangingOpened(); + session.start(PARAMS); + + verifyNoThrowIllegalState(session::stop); + verify(callback, times(1)).onStopped(); + + // Calling stop again should throw an illegal state + verifyThrowIllegalState(session::stop); + verify(callback, times(1)).onStopped(); + } + + @Test + public void testReconfigure_OnlyWhenOpened() throws RemoteException { + SessionHandle handle = new SessionHandle(123); + RangingSession.Callback callback = mock(RangingSession.Callback.class); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); + doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); + doAnswer(new ReconfigureAnswer(session)).when(adapter).reconfigureRanging(any(), any()); + + verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); + verify(callback, times(0)).onReconfigured(any()); + verifyOpenState(session, false); + + session.onRangingOpened(); + verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); + verify(callback, times(1)).onReconfigured(any()); + verifyOpenState(session, true); + session.onRangingStarted(PARAMS); + verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); + verify(callback, times(2)).onReconfigured(any()); + verifyOpenState(session, true); + + session.onRangingStopped(); + verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); + verify(callback, times(3)).onReconfigured(any()); + verifyOpenState(session, true); + + + session.onRangingClosed(REASON, PARAMS); + verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); + verify(callback, times(3)).onReconfigured(any()); + verifyOpenState(session, false); + } + + @Test + public void testClose_NoCallbackUntilInvoked() throws RemoteException { + SessionHandle handle = new SessionHandle(123); + RangingSession.Callback callback = mock(RangingSession.Callback.class); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); + session.onRangingOpened(); // Calling close multiple times should invoke closeRanging until the session receives // the onClosed callback. @@ -143,7 +224,7 @@ public class RangingSessionTest { for (int i = 1; i <= totalCallsBeforeOnRangingClosed; i++) { session.close(); verifyOpenState(session, true); - verify(ADAPTER, times(i)).closeRanging(handle); + verify(adapter, times(i)).closeRanging(handle); verify(callback, times(0)).onClosed(anyInt(), any()); } @@ -151,18 +232,47 @@ public class RangingSessionTest { // the session's close. final int totalCallsAfterOnRangingClosed = 2; for (int i = 1; i <= totalCallsAfterOnRangingClosed; i++) { - session.onRangingClosed(CLOSE_REASON, PARAMS); + session.onRangingClosed(REASON, PARAMS); verifyOpenState(session, false); - verify(ADAPTER, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle); + verify(adapter, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle); verify(callback, times(i)).onClosed(anyInt(), any()); } } @Test + public void testClose_OnClosedCalled() throws RemoteException { + SessionHandle handle = new SessionHandle(123); + RangingSession.Callback callback = mock(RangingSession.Callback.class); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); + doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any()); + session.onRangingOpened(); + + session.close(); + verify(callback, times(1)).onClosed(anyInt(), any()); + } + + @Test + public void testClose_CannotInteractFurther() throws RemoteException { + SessionHandle handle = new SessionHandle(123); + RangingSession.Callback callback = mock(RangingSession.Callback.class); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); + doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any()); + session.close(); + + verifyThrowIllegalState(() -> session.start(PARAMS)); + verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); + verifyThrowIllegalState(() -> session.stop()); + verifyNoThrowIllegalState(() -> session.close()); + } + + @Test public void testOnRangingResult_OnReportReceivedCalledWhenOpen() { SessionHandle handle = new SessionHandle(123); RangingSession.Callback callback = mock(RangingSession.Callback.class); - RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); assertFalse(session.isOpen()); session.onRangingStarted(PARAMS); @@ -178,7 +288,8 @@ public class RangingSessionTest { public void testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen() { SessionHandle handle = new SessionHandle(123); RangingSession.Callback callback = mock(RangingSession.Callback.class); - RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle); + IUwbAdapter adapter = mock(IUwbAdapter.class); + RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); assertFalse(session.isOpen()); @@ -191,4 +302,77 @@ public class RangingSessionTest { private void verifyOpenState(RangingSession session, boolean expected) { assertEquals(expected, session.isOpen()); } + + private void verifyThrowIllegalState(Runnable runnable) { + try { + runnable.run(); + fail(); + } catch (IllegalStateException e) { + // Pass + } + } + + private void verifyNoThrowIllegalState(Runnable runnable) { + try { + runnable.run(); + } catch (IllegalStateException e) { + fail(); + } + } + + abstract class AdapterAnswer implements Answer { + protected RangingSession mSession; + + protected AdapterAnswer(RangingSession session) { + mSession = session; + } + } + + class StartAnswer extends AdapterAnswer { + StartAnswer(RangingSession session) { + super(session); + } + + @Override + public Object answer(InvocationOnMock invocation) { + mSession.onRangingStarted(PARAMS); + return null; + } + } + + class ReconfigureAnswer extends AdapterAnswer { + ReconfigureAnswer(RangingSession session) { + super(session); + } + + @Override + public Object answer(InvocationOnMock invocation) { + mSession.onRangingReconfigured(PARAMS); + return null; + } + } + + class StopAnswer extends AdapterAnswer { + StopAnswer(RangingSession session) { + super(session); + } + + @Override + public Object answer(InvocationOnMock invocation) { + mSession.onRangingStopped(); + return null; + } + } + + class CloseAnswer extends AdapterAnswer { + CloseAnswer(RangingSession session) { + super(session); + } + + @Override + public Object answer(InvocationOnMock invocation) { + mSession.onRangingClosed(REASON, PARAMS); + return null; + } + } } |