Merge "Add control functions to RangingSession" am: 022ba65d34

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1532667

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ib0277c191acd5ed2b27f850b57e1d4cb4b6966c4
This commit is contained in:
Treehugger Robot
2021-01-08 22:04:30 +00:00
committed by Automerger Merge Worker
9 changed files with 882 additions and 329 deletions

View File

@@ -119,41 +119,95 @@ interface IUwbAdapter {
PersistableBundle getSpecificationInfo(); PersistableBundle getSpecificationInfo();
/** /**
* Request to start a new ranging session * Request to open a new ranging session
* *
* This function must return before calling IUwbAdapterCallbacks * This function must return before calling any functions in
* #onRangingStarted, #onRangingClosed, or #onRangingResult. * 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 * IUwbAdapterCallbacks#onRangingOpened must be called within
* RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being
* if the ranging session is scheduled to start successfully. * called if the ranging session is opened successfully.
* *
* IUwbAdapterCallbacks#onRangingStartFailed must be called within * IUwbAdapterCallbacks#onRangingOpenFailed must be called within
* RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being called
* if the ranging session fails to be scheduled to start successfully. * if the ranging session fails to be opened.
* *
* @param rangingCallbacks the callbacks used to deliver ranging information * @param rangingCallbacks the callbacks used to deliver ranging information
* @param parameters the configuration to use for ranging * @param parameters the configuration to use for ranging
* @return a SessionHandle used to identify this ranging request * @return a SessionHandle used to identify this ranging request
*/ */
SessionHandle startRanging(in IUwbRangingCallbacks rangingCallbacks, SessionHandle openRanging(in IUwbRangingCallbacks rangingCallbacks,
in PersistableBundle parameters); in PersistableBundle parameters);
/** /**
* Stop and close ranging for the session associated with the given handle * 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);
/**
* Close ranging for the session associated with the given handle
* *
* Calling with an invalid handle or a handle that has already been closed * Calling with an invalid handle or a handle that has already been closed
* is a no-op. * is a no-op.
* *
* IUwbAdapterCallbacks#onRangingClosed must be called within * 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); 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. * The maximum allowed time to start a ranging session.
*/ */

View File

@@ -17,15 +17,32 @@
package android.uwb; package android.uwb;
import android.os.PersistableBundle; import android.os.PersistableBundle;
import android.uwb.CloseReason; import android.uwb.RangingChangeReason;
import android.uwb.RangingReport; import android.uwb.RangingReport;
import android.uwb.SessionHandle; import android.uwb.SessionHandle;
import android.uwb.StartFailureReason;
/** /**
* @hide * @hide
*/ */
interface IUwbRangingCallbacks { 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 * Called when ranging has started
* *
@@ -47,8 +64,49 @@ interface IUwbRangingCallbacks {
* @param reason the reason the session failed to start * @param reason the reason the session failed to start
* @param parameters protocol specific parameters * @param parameters protocol specific parameters
*/ */
void onRangingStartFailed(in SessionHandle sessionHandle, StartFailureReason reason, void onRangingStartFailed(in SessionHandle sessionHandle,
RangingChangeReason reason,
in PersistableBundle parameters); 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 * Called when a ranging session is closed
* *
@@ -56,7 +114,8 @@ interface IUwbRangingCallbacks {
* @param reason the reason the session was closed * @param reason the reason the session was closed
* @param parameters protocol specific parameters * @param parameters protocol specific parameters
*/ */
void onRangingClosed(in SessionHandle sessionHandle, CloseReason reason, void onRangingClosed(in SessionHandle sessionHandle,
RangingChangeReason reason,
in PersistableBundle parameters); in PersistableBundle parameters);
/** /**

View File

@@ -20,39 +20,44 @@ package android.uwb;
* @hide * @hide
*/ */
@Backing(type="int") @Backing(type="int")
enum CloseReason { enum RangingChangeReason {
/** /**
* Unknown reason * Unknown reason
*/ */
UNKNOWN, UNKNOWN,
/** /**
* A local API call triggered the close, such as a call to * A local API call triggered the change, such as a call to
* IUwbAdapter.stopRanging. * IUwbAdapter.closeRanging.
*/ */
LOCAL_API, LOCAL_API,
/** /**
* The maximum number of sessions has been reached. This error may be generated * The maximum number of sessions has been reached. This may be generated for
* for an active session if a higher priority session begins. * an active session if a higher priority session begins.
*/ */
MAX_SESSIONS_REACHED, MAX_SESSIONS_REACHED,
/** /**
* The system state has changed resulting in the session ending (e.g. the user * The system state has changed resulting in the session changing (e.g. the
* disables UWB, or the user's locale changes and an active channel is no longer * user disables UWB, or the user's locale changes and an active channel is no
* permitted to be used). * longer permitted to be used).
*/ */
SYSTEM_POLICY, SYSTEM_POLICY,
/** /**
* The remote device has requested to terminate the session * The remote device has requested to change the session
*/ */
REMOTE_REQUEST, REMOTE_REQUEST,
/** /**
* The session was closed for a protocol specific reason * The session changed for a protocol specific reason
*/ */
PROTOCOL_SPECIFIC, PROTOCOL_SPECIFIC,
/**
* The provided parameters were invalid
*/
BAD_PARAMETERS,
} }

View File

@@ -50,7 +50,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
@NonNull RangingSession.Callback callbacks) { @NonNull RangingSession.Callback callbacks) {
SessionHandle sessionHandle; SessionHandle sessionHandle;
try { try {
sessionHandle = mAdapter.startRanging(this, params); sessionHandle = mAdapter.openRanging(this, params);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
@@ -59,7 +59,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
if (hasSession(sessionHandle)) { if (hasSession(sessionHandle)) {
Log.w(TAG, "Newly created session unexpectedly reuses an active SessionHandle"); Log.w(TAG, "Newly created session unexpectedly reuses an active SessionHandle");
executor.execute(() -> callbacks.onClosed( executor.execute(() -> callbacks.onClosed(
RangingSession.Callback.CLOSE_REASON_LOCAL_GENERIC_ERROR, RangingSession.Callback.REASON_GENERIC_ERROR,
new PersistableBundle())); new PersistableBundle()));
} }
@@ -74,6 +74,67 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
return mRangingSessionTable.containsKey(sessionHandle); return mRangingSessionTable.containsKey(sessionHandle);
} }
@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 @Override
public void onRangingStarted(SessionHandle sessionHandle, PersistableBundle parameters) { public void onRangingStarted(SessionHandle sessionHandle, PersistableBundle parameters) {
synchronized (this) { synchronized (this) {
@@ -89,7 +150,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
} }
@Override @Override
public void onRangingStartFailed(SessionHandle sessionHandle, int reason, public void onRangingStartFailed(SessionHandle sessionHandle, @RangingChangeReason int reason,
PersistableBundle params) { PersistableBundle params) {
synchronized (this) { synchronized (this) {
if (!hasSession(sessionHandle)) { if (!hasSession(sessionHandle)) {
@@ -99,13 +160,42 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
} }
RangingSession session = mRangingSessionTable.get(sessionHandle); RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingClosed(convertStartFailureToCloseReason(reason), params); session.onRangingStartFailed(convertToReason(reason), params);
mRangingSessionTable.remove(sessionHandle);
} }
} }
@Override @Override
public void onRangingClosed(SessionHandle sessionHandle, int reason, PersistableBundle params) { 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 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) { synchronized (this) {
if (!hasSession(sessionHandle)) { if (!hasSession(sessionHandle)) {
Log.w(TAG, "onRangingClosed - received unexpected SessionHandle: " + 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); RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingClosed(convertToCloseReason(reason), params); session.onRangingClosed(convertToReason(reason), params);
mRangingSessionTable.remove(sessionHandle); mRangingSessionTable.remove(sessionHandle);
} }
} }
@@ -131,48 +221,30 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
} }
} }
@RangingSession.Callback.CloseReason @RangingSession.Callback.Reason
private static int convertToCloseReason(@CloseReason int reason) { private static int convertToReason(@RangingChangeReason int reason) {
switch (reason) { switch (reason) {
case CloseReason.LOCAL_API: case RangingChangeReason.LOCAL_API:
return RangingSession.Callback.CLOSE_REASON_LOCAL_CLOSE_API; return RangingSession.Callback.REASON_LOCAL_REQUEST;
case CloseReason.MAX_SESSIONS_REACHED: case RangingChangeReason.MAX_SESSIONS_REACHED:
return RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED; return RangingSession.Callback.REASON_MAX_SESSIONS_REACHED;
case CloseReason.SYSTEM_POLICY: case RangingChangeReason.SYSTEM_POLICY:
return RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY; return RangingSession.Callback.REASON_SYSTEM_POLICY;
case CloseReason.REMOTE_REQUEST: case RangingChangeReason.REMOTE_REQUEST:
return RangingSession.Callback.CLOSE_REASON_REMOTE_REQUEST; return RangingSession.Callback.REASON_REMOTE_REQUEST;
case CloseReason.PROTOCOL_SPECIFIC: case RangingChangeReason.PROTOCOL_SPECIFIC:
return RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC; return RangingSession.Callback.REASON_PROTOCOL_SPECIFIC_ERROR;
case CloseReason.UNKNOWN: case RangingChangeReason.BAD_PARAMETERS:
return RangingSession.Callback.REASON_BAD_PARAMETERS;
case RangingChangeReason.UNKNOWN:
default: default:
return RangingSession.Callback.CLOSE_REASON_UNKNOWN; return RangingSession.Callback.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 StartFailureReason.MAX_SESSIONS_REACHED:
return RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED;
case StartFailureReason.SYSTEM_POLICY:
return RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY;
case StartFailureReason.PROTOCOL_SPECIFIC:
return RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC;
case StartFailureReason.UNKNOWN:
default:
return RangingSession.Callback.CLOSE_REASON_UNKNOWN;
} }
} }
} }

View File

@@ -36,9 +36,9 @@ import java.util.concurrent.Executor;
* <p>To get an instance of {@link RangingSession}, first use * <p>To get an instance of {@link RangingSession}, first use
* {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} to request to open a * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} to request to open a
* session. Once the session is opened, a {@link RangingSession} object is provided through * session. Once the session is opened, a {@link RangingSession} object is provided through
* {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)}. If opening a * {@link RangingSession.Callback#onOpened(RangingSession)}. If opening a session fails, the failure
* session fails, the failure is reported through * is reported through {@link RangingSession.Callback#onOpenFailed(int, PersistableBundle)} with the
* {@link RangingSession.Callback#onClosed(int, PersistableBundle)} with the failure reason. * failure reason.
* *
* @hide * @hide
*/ */
@@ -50,103 +50,162 @@ public final class RangingSession implements AutoCloseable {
private final Callback mCallback; private final Callback mCallback;
private enum State { private enum State {
/**
* The state of the {@link RangingSession} until
* {@link RangingSession.Callback#onOpened(RangingSession)} is invoked
*/
INIT, 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 * Interface for receiving {@link RangingSession} events
*/ */
public interface Callback { 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 * @hide
*/ */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef(value = { @IntDef(value = {
CLOSE_REASON_UNKNOWN, REASON_UNKNOWN,
CLOSE_REASON_LOCAL_CLOSE_API, REASON_LOCAL_REQUEST,
CLOSE_REASON_LOCAL_BAD_PARAMETERS, REASON_REMOTE_REQUEST,
CLOSE_REASON_LOCAL_GENERIC_ERROR, REASON_BAD_PARAMETERS,
CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED, REASON_GENERIC_ERROR,
CLOSE_REASON_LOCAL_SYSTEM_POLICY, REASON_MAX_SESSIONS_REACHED,
CLOSE_REASON_REMOTE_GENERIC_ERROR, REASON_SYSTEM_POLICY,
CLOSE_REASON_REMOTE_REQUEST}) REASON_PROTOCOL_SPECIFIC_ERROR})
@interface CloseReason {} @interface Reason {}
/** /**
* Indicates that the session was closed or failed to open due to an unknown 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 * Indicates that the session was closed or failed to open because
* {@link AutoCloseable#close()} or {@link RangingSession#close()} was called * {@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)}
*/
int CLOSE_REASON_LOCAL_BAD_PARAMETERS = 2;
/**
* Indicates that the session was closed due to some local error on this device besides the
* error code already listed
*/
int CLOSE_REASON_LOCAL_GENERIC_ERROR = 3;
/**
* Indicates that the session failed to open because the number of currently open sessions
* is equal to {@link UwbManager#getMaxSimultaneousSessions()}
*/
int CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED = 4;
/**
* Indicates that the session was closed or failed to open due to local system policy, such
* as privacy policy, power management policy, permissions, and more.
*/
int CLOSE_REASON_LOCAL_SYSTEM_POLICY = 5;
/**
* Indicates that the session was closed or failed to open due to an error with the remote
* device besides error codes already listed.
*/
int CLOSE_REASON_REMOTE_GENERIC_ERROR = 6;
/** /**
* Indicates that the session was closed or failed to open due to an explicit request from * Indicates that the session was closed or failed to open due to an explicit request from
* the remote device. * the remote device.
*/ */
int CLOSE_REASON_REMOTE_REQUEST = 7; int REASON_REMOTE_REQUEST = 2;
/** /**
* Indicates that the session was closed for a protocol specific reason. The associated * Indicates that the session was closed or failed to open due to erroneous parameters
* {@link PersistableBundle} should be consulted for additional information.
*/ */
int CLOSE_REASON_PROTOCOL_SPECIFIC = 8; int REASON_BAD_PARAMETERS = 3;
/** /**
* Indicates an error on this device besides the error code already listed
*/
int REASON_GENERIC_ERROR = 4;
/**
* 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 REASON_SYSTEM_POLICY = 6;
/**
* Indicates a protocol specific error. The associated {@link PersistableBundle} should be
* consulted for additional information.
*/
int REASON_PROTOCOL_SPECIFIC_ERROR = 7;
/**
* 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
*/
void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params);
/**
* Invoked when a request to stop the session succeeds
*/
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 * Invoked when session is either closed spontaneously, or per user request via
* {@link RangingSession#close()} or {@link AutoCloseable#close()}, or when session failed * {@link RangingSession#close()} or {@link AutoCloseable#close()}.
* to open.
* *
* @param reason reason for the session closure * @param reason reason for the session closure
* @param parameters protocol specific parameters related to the close reason * @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 * Called once per ranging interval even when a ranging measurement fails
@@ -172,12 +231,95 @@ public final class RangingSession implements AutoCloseable {
* @hide * @hide
*/ */
public boolean isOpen() { 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 * 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 * <p>If the session is in the process of being opened, it will attempt to stop the session from
* being opened. * being opened.
* <p>If the session is already closed, the registered * <p>If the session is already closed, the registered
@@ -192,7 +334,7 @@ public final class RangingSession implements AutoCloseable {
public void close() { public void close() {
if (mState == State.CLOSED) { if (mState == State.CLOSED) {
mExecutor.execute(() -> mCallback.onClosed( mExecutor.execute(() -> mCallback.onClosed(
Callback.CLOSE_REASON_LOCAL_CLOSE_API, new PersistableBundle())); Callback.REASON_LOCAL_REQUEST, new PersistableBundle()));
return; return;
} }
@@ -203,6 +345,33 @@ 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 * @hide
*/ */
@@ -212,26 +381,81 @@ public final class RangingSession implements AutoCloseable {
return; return;
} }
mState = State.OPEN; mState = State.ACTIVE;
final long identity = Binder.clearCallingIdentity(); executeCallback(() -> mCallback.onStarted(parameters));
try {
mExecutor.execute(() -> mCallback.onOpenSuccess(this, parameters));
} finally {
Binder.restoreCallingIdentity(identity);
}
} }
/** /**
* @hide * @hide
*/ */
public void onRangingClosed(@Callback.CloseReason int reason, PersistableBundle parameters) { public void onRangingStartFailed(@Callback.Reason int reason,
mState = State.CLOSED; @NonNull PersistableBundle params) {
final long identity = Binder.clearCallingIdentity(); if (mState == State.CLOSED) {
try { Log.w(TAG, "onRangingStartFailed invoked for a closed session");
mExecutor.execute(() -> mCallback.onClosed(reason, parameters)); return;
} finally {
Binder.restoreCallingIdentity(identity);
} }
executeCallback(() -> mCallback.onStartFailed(reason, params));
}
/**
* @hide
*/
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; return;
} }
executeCallback(() -> mCallback.onReportReceived(report));
}
/**
* @hide
*/
private void executeCallback(@NonNull Runnable runnable) {
final long identity = Binder.clearCallingIdentity(); final long identity = Binder.clearCallingIdentity();
try { try {
mExecutor.execute(() -> mCallback.onReportReceived(report)); mExecutor.execute(runnable);
} finally { } finally {
Binder.restoreCallingIdentity(identity); Binder.restoreCallingIdentity(identity);
} }

View File

@@ -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,
}

View File

@@ -369,13 +369,13 @@ public final class UwbManager {
/** /**
* Open a {@link RangingSession} with the given parameters * Open a {@link RangingSession} with the given parameters
* <p>This function is asynchronous and will return before ranging begins. The * <p>The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a
* {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)} function is * {@link RangingSession} object used to control ranging when the session is successfully
* called with a {@link RangingSession} object used to control ranging when the session is * opened.
* successfully opened.
* *
* <p>If a session cannot be opened, then {@link RangingSession.Callback#onClosed(int)} will be * <p>If a session cannot be opened, then
* invoked with the appropriate {@link RangingSession.Callback.CloseReason}. * {@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 * <p>An open {@link RangingSession} will be automatically closed if client application process
* dies. * 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 * @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} * {@link RangingSession} that has been requested through {@link #openRangingSession}
* but has not yet been made available by * but has not yet been made available by
* {@link RangingSession.Callback#onOpenSuccess}. * {@link RangingSession.Callback#onOpened(RangingSession)}.
*/ */
@NonNull @NonNull
public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters, public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters,

View File

@@ -45,14 +45,14 @@ public class RangingManagerTest {
private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class); private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class);
private static final Executor EXECUTOR = UwbTestUtils.getExecutor(); private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
private static final PersistableBundle PARAMS = new PersistableBundle(); 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 @Test
public void testOpenSession_StartRangingInvoked() throws RemoteException { public void testOpenSession_OpenRangingInvoked() throws RemoteException {
RangingManager rangingManager = new RangingManager(ADAPTER); RangingManager rangingManager = new RangingManager(ADAPTER);
RangingSession.Callback callback = mock(RangingSession.Callback.class); RangingSession.Callback callback = mock(RangingSession.Callback.class);
rangingManager.openSession(PARAMS, EXECUTOR, callback); rangingManager.openSession(PARAMS, EXECUTOR, callback);
verify(ADAPTER, times(1)).startRanging(eq(rangingManager), eq(PARAMS)); verify(ADAPTER, times(1)).openRanging(eq(rangingManager), eq(PARAMS));
} }
@Test @Test
@@ -60,7 +60,7 @@ public class RangingManagerTest {
RangingManager rangingManager = new RangingManager(ADAPTER); RangingManager rangingManager = new RangingManager(ADAPTER);
RangingSession.Callback callback = mock(RangingSession.Callback.class); RangingSession.Callback callback = mock(RangingSession.Callback.class);
SessionHandle handle = new SessionHandle(1); 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.openSession(PARAMS, EXECUTOR, callback);
@@ -73,34 +73,34 @@ public class RangingManagerTest {
} }
@Test @Test
public void testOnRangingStarted_ValidSessionHandle() throws RemoteException { public void testOnRangingOpened_ValidSessionHandle() throws RemoteException {
RangingManager rangingManager = new RangingManager(ADAPTER); RangingManager rangingManager = new RangingManager(ADAPTER);
RangingSession.Callback callback = mock(RangingSession.Callback.class); RangingSession.Callback callback = mock(RangingSession.Callback.class);
SessionHandle handle = new SessionHandle(1); 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.openSession(PARAMS, EXECUTOR, callback);
rangingManager.onRangingStarted(handle, PARAMS); rangingManager.onRangingOpened(handle);
verify(callback, times(1)).onOpenSuccess(any(), any()); verify(callback, times(1)).onOpened(any());
} }
@Test @Test
public void testOnRangingStarted_InvalidSessionHandle() throws RemoteException { public void testOnRangingOpened_InvalidSessionHandle() throws RemoteException {
RangingManager rangingManager = new RangingManager(ADAPTER); RangingManager rangingManager = new RangingManager(ADAPTER);
RangingSession.Callback callback = mock(RangingSession.Callback.class); RangingSession.Callback callback = mock(RangingSession.Callback.class);
rangingManager.onRangingStarted(new SessionHandle(2), PARAMS); rangingManager.onRangingOpened(new SessionHandle(2));
verify(callback, times(0)).onOpenSuccess(any(), any()); verify(callback, times(0)).onOpened(any());
} }
@Test @Test
public void testOnRangingStarted_MultipleSessionsRegistered() throws RemoteException { public void testOnRangingOpened_MultipleSessionsRegistered() throws RemoteException {
SessionHandle sessionHandle1 = new SessionHandle(1); SessionHandle sessionHandle1 = new SessionHandle(1);
SessionHandle sessionHandle2 = new SessionHandle(2); SessionHandle sessionHandle2 = new SessionHandle(2);
RangingSession.Callback callback1 = mock(RangingSession.Callback.class); RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
RangingSession.Callback callback2 = mock(RangingSession.Callback.class); RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
when(ADAPTER.startRanging(any(), any())) when(ADAPTER.openRanging(any(), any()))
.thenReturn(sessionHandle1) .thenReturn(sessionHandle1)
.thenReturn(sessionHandle2); .thenReturn(sessionHandle2);
@@ -108,25 +108,50 @@ public class RangingManagerTest {
rangingManager.openSession(PARAMS, EXECUTOR, callback1); rangingManager.openSession(PARAMS, EXECUTOR, callback1);
rangingManager.openSession(PARAMS, EXECUTOR, callback2); rangingManager.openSession(PARAMS, EXECUTOR, callback2);
rangingManager.onRangingStarted(sessionHandle1, PARAMS); rangingManager.onRangingOpened(sessionHandle1);
verify(callback1, times(1)).onOpenSuccess(any(), any()); verify(callback1, times(1)).onOpened(any());
verify(callback2, times(0)).onOpenSuccess(any(), any()); verify(callback2, times(0)).onOpened(any());
rangingManager.onRangingStarted(sessionHandle2, PARAMS); rangingManager.onRangingOpened(sessionHandle2);
verify(callback1, times(1)).onOpenSuccess(any(), any()); verify(callback1, times(1)).onOpened(any());
verify(callback2, times(1)).onOpenSuccess(any(), any()); verify(callback2, times(1)).onOpened(any());
} }
@Test @Test
public void testOnRangingClosed_OnRangingClosedCalled() throws RemoteException { public void testCorrectCallbackInvoked() throws RemoteException {
RangingManager rangingManager = new RangingManager(ADAPTER); RangingManager rangingManager = new RangingManager(ADAPTER);
RangingSession.Callback callback = mock(RangingSession.Callback.class); RangingSession.Callback callback = mock(RangingSession.Callback.class);
SessionHandle handle = new SessionHandle(1); 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, CLOSE_REASON, PARAMS); rangingManager.openSession(PARAMS, EXECUTOR, callback);
verify(callback, times(1)).onClosed(anyInt(), any()); 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.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 @Test
@@ -138,7 +163,7 @@ public class RangingManagerTest {
RangingSession.Callback callback1 = mock(RangingSession.Callback.class); RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
RangingSession.Callback callback2 = mock(RangingSession.Callback.class); RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
when(ADAPTER.startRanging(any(), any())) when(ADAPTER.openRanging(any(), any()))
.thenReturn(sessionHandle1) .thenReturn(sessionHandle1)
.thenReturn(sessionHandle2); .thenReturn(sessionHandle2);
@@ -146,29 +171,15 @@ public class RangingManagerTest {
rangingManager.openSession(PARAMS, EXECUTOR, callback1); rangingManager.openSession(PARAMS, EXECUTOR, callback1);
rangingManager.openSession(PARAMS, EXECUTOR, callback2); rangingManager.openSession(PARAMS, EXECUTOR, callback2);
rangingManager.onRangingClosed(sessionHandle1, CLOSE_REASON, PARAMS); rangingManager.onRangingClosed(sessionHandle1, REASON, PARAMS);
verify(callback1, times(1)).onClosed(anyInt(), any()); verify(callback1, times(1)).onClosed(anyInt(), any());
verify(callback2, times(0)).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(callback1, times(1)).onClosed(anyInt(), any());
verify(callback2, 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 @Test
public void testOnRangingReport_MultipleSessionsRegistered() throws RemoteException { public void testOnRangingReport_MultipleSessionsRegistered() throws RemoteException {
SessionHandle sessionHandle1 = new SessionHandle(1); SessionHandle sessionHandle1 = new SessionHandle(1);
@@ -176,7 +187,7 @@ public class RangingManagerTest {
RangingSession.Callback callback1 = mock(RangingSession.Callback.class); RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
RangingSession.Callback callback2 = mock(RangingSession.Callback.class); RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
when(ADAPTER.startRanging(any(), any())) when(ADAPTER.openRanging(any(), any()))
.thenReturn(sessionHandle1) .thenReturn(sessionHandle1)
.thenReturn(sessionHandle2); .thenReturn(sessionHandle2);
@@ -196,65 +207,54 @@ public class RangingManagerTest {
} }
@Test @Test
public void testOnClose_Reasons() throws RemoteException { public void testReasons() throws RemoteException {
runOnClose_Reason(CloseReason.LOCAL_API, runReason(RangingChangeReason.LOCAL_API,
RangingSession.Callback.CLOSE_REASON_LOCAL_CLOSE_API); RangingSession.Callback.REASON_LOCAL_REQUEST);
runOnClose_Reason(CloseReason.MAX_SESSIONS_REACHED, runReason(RangingChangeReason.MAX_SESSIONS_REACHED,
RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED); RangingSession.Callback.REASON_MAX_SESSIONS_REACHED);
runOnClose_Reason(CloseReason.PROTOCOL_SPECIFIC, runReason(RangingChangeReason.PROTOCOL_SPECIFIC,
RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC); RangingSession.Callback.REASON_PROTOCOL_SPECIFIC_ERROR);
runOnClose_Reason(CloseReason.REMOTE_REQUEST, runReason(RangingChangeReason.REMOTE_REQUEST,
RangingSession.Callback.CLOSE_REASON_REMOTE_REQUEST); RangingSession.Callback.REASON_REMOTE_REQUEST);
runOnClose_Reason(CloseReason.SYSTEM_POLICY, runReason(RangingChangeReason.SYSTEM_POLICY,
RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY); RangingSession.Callback.REASON_SYSTEM_POLICY);
runOnClose_Reason(CloseReason.UNKNOWN, runReason(RangingChangeReason.BAD_PARAMETERS,
RangingSession.Callback.CLOSE_REASON_UNKNOWN); RangingSession.Callback.REASON_BAD_PARAMETERS);
runReason(RangingChangeReason.UNKNOWN,
RangingSession.Callback.REASON_UNKNOWN);
} }
private void runOnClose_Reason(@CloseReason int reasonIn, private void runReason(@RangingChangeReason int reasonIn,
@RangingSession.Callback.CloseReason int reasonOut) throws RemoteException { @RangingSession.Callback.Reason int reasonOut) throws RemoteException {
RangingManager rangingManager = new RangingManager(ADAPTER); RangingManager rangingManager = new RangingManager(ADAPTER);
RangingSession.Callback callback = mock(RangingSession.Callback.class); RangingSession.Callback callback = mock(RangingSession.Callback.class);
SessionHandle handle = new SessionHandle(1); 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.openSession(PARAMS, EXECUTOR, callback);
rangingManager.onRangingOpenFailed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onOpenFailed(eq(reasonOut), eq(PARAMS));
// Open a new session
rangingManager.openSession(PARAMS, EXECUTOR, callback);
rangingManager.onRangingOpened(handle);
rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onStartFailed(eq(reasonOut), eq(PARAMS));
rangingManager.onRangingReconfigureFailed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onReconfigureFailed(eq(reasonOut), eq(PARAMS));
rangingManager.onRangingStopFailed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onStopFailed(eq(reasonOut), eq(PARAMS));
rangingManager.onRangingClosed(handle, reasonIn, PARAMS); rangingManager.onRangingClosed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onClosed(eq(reasonOut), eq(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);
runOnRangingStartFailed_Reason(StartFailureReason.MAX_SESSIONS_REACHED,
RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED);
runOnRangingStartFailed_Reason(StartFailureReason.PROTOCOL_SPECIFIC,
RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC);
runOnRangingStartFailed_Reason(StartFailureReason.SYSTEM_POLICY,
RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY);
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.onRangingStartFailed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onClosed(eq(reasonOut), eq(PARAMS));
}
} }

View File

@@ -19,9 +19,11 @@ package android.uwb;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -34,6 +36,8 @@ import androidx.test.filters.SmallTest;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -43,47 +47,48 @@ import java.util.concurrent.Executor;
@SmallTest @SmallTest
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class RangingSessionTest { public class RangingSessionTest {
private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class);
private static final Executor EXECUTOR = UwbTestUtils.getExecutor(); private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
private static final PersistableBundle PARAMS = new PersistableBundle(); private static final PersistableBundle PARAMS = new PersistableBundle();
private static final @RangingSession.Callback.CloseReason int CLOSE_REASON = private static final @RangingSession.Callback.Reason int REASON =
RangingSession.Callback.CLOSE_REASON_LOCAL_GENERIC_ERROR; RangingSession.Callback.REASON_GENERIC_ERROR;
@Test @Test
public void testOnRangingStarted_OnOpenSuccessCalled() { public void testOnRangingOpened_OnOpenSuccessCalled() {
SessionHandle handle = new SessionHandle(123); SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class); 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); verifyOpenState(session, false);
session.onRangingStarted(PARAMS); session.onRangingOpened();
verifyOpenState(session, true); verifyOpenState(session, true);
// Verify that the onOpenSuccess callback was invoked // 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()); verify(callback, times(0)).onClosed(anyInt(), any());
} }
@Test @Test
public void testOnRangingStarted_CannotOpenClosedSession() { public void testOnRangingOpened_CannotOpenClosedSession() {
SessionHandle handle = new SessionHandle(123); SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class); 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); 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()); verify(callback, times(0)).onClosed(anyInt(), any());
session.onRangingClosed(CLOSE_REASON, PARAMS); session.onRangingClosed(REASON, PARAMS);
verifyOpenState(session, false); 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()); verify(callback, times(1)).onClosed(anyInt(), any());
// Now invoke the ranging started callback and ensure the session remains closed // Now invoke the ranging started callback and ensure the session remains closed
session.onRangingStarted(PARAMS); session.onRangingOpened();
verifyOpenState(session, false); 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()); verify(callback, times(1)).onClosed(anyInt(), any());
} }
@@ -91,27 +96,30 @@ public class RangingSessionTest {
public void testOnRangingClosed_OnClosedCalledWhenSessionNotOpen() { public void testOnRangingClosed_OnClosedCalledWhenSessionNotOpen() {
SessionHandle handle = new SessionHandle(123); SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class); 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); verifyOpenState(session, false);
session.onRangingClosed(CLOSE_REASON, PARAMS); session.onRangingClosed(REASON, PARAMS);
verifyOpenState(session, false); verifyOpenState(session, false);
// Verify that the onOpenSuccess callback was invoked // 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()); verify(callback, times(1)).onClosed(anyInt(), any());
} }
@Test public void testOnRangingClosed_OnClosedCalled() { @Test
public void testOnRangingClosed_OnClosedCalled() {
SessionHandle handle = new SessionHandle(123); SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class); 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.onRangingStarted(PARAMS);
session.onRangingClosed(CLOSE_REASON, PARAMS); session.onRangingClosed(REASON, PARAMS);
verify(callback, times(1)).onClosed(anyInt(), any()); verify(callback, times(1)).onClosed(anyInt(), any());
verifyOpenState(session, false); verifyOpenState(session, false);
session.onRangingClosed(CLOSE_REASON, PARAMS); session.onRangingClosed(REASON, PARAMS);
verify(callback, times(2)).onClosed(anyInt(), any()); verify(callback, times(2)).onClosed(anyInt(), any());
} }
@@ -119,7 +127,8 @@ public class RangingSessionTest {
public void testOnRangingResult_OnReportReceivedCalled() { public void testOnRangingResult_OnReportReceivedCalled() {
SessionHandle handle = new SessionHandle(123); SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class); 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); verifyOpenState(session, false);
session.onRangingStarted(PARAMS); session.onRangingStarted(PARAMS);
@@ -131,11 +140,83 @@ public class RangingSessionTest {
} }
@Test @Test
public void testClose() throws RemoteException { public void testStart_CannotStartIfAlreadyStarted() throws RemoteException {
SessionHandle handle = new SessionHandle(123); SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class); 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); 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 // Calling close multiple times should invoke closeRanging until the session receives
// the onClosed callback. // the onClosed callback.
@@ -143,7 +224,7 @@ public class RangingSessionTest {
for (int i = 1; i <= totalCallsBeforeOnRangingClosed; i++) { for (int i = 1; i <= totalCallsBeforeOnRangingClosed; i++) {
session.close(); session.close();
verifyOpenState(session, true); verifyOpenState(session, true);
verify(ADAPTER, times(i)).closeRanging(handle); verify(adapter, times(i)).closeRanging(handle);
verify(callback, times(0)).onClosed(anyInt(), any()); verify(callback, times(0)).onClosed(anyInt(), any());
} }
@@ -151,18 +232,47 @@ public class RangingSessionTest {
// the session's close. // the session's close.
final int totalCallsAfterOnRangingClosed = 2; final int totalCallsAfterOnRangingClosed = 2;
for (int i = 1; i <= totalCallsAfterOnRangingClosed; i++) { for (int i = 1; i <= totalCallsAfterOnRangingClosed; i++) {
session.onRangingClosed(CLOSE_REASON, PARAMS); session.onRangingClosed(REASON, PARAMS);
verifyOpenState(session, false); verifyOpenState(session, false);
verify(ADAPTER, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle); verify(adapter, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle);
verify(callback, times(i)).onClosed(anyInt(), any()); 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 @Test
public void testOnRangingResult_OnReportReceivedCalledWhenOpen() { public void testOnRangingResult_OnReportReceivedCalledWhenOpen() {
SessionHandle handle = new SessionHandle(123); SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class); 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()); assertFalse(session.isOpen());
session.onRangingStarted(PARAMS); session.onRangingStarted(PARAMS);
@@ -178,7 +288,8 @@ public class RangingSessionTest {
public void testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen() { public void testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen() {
SessionHandle handle = new SessionHandle(123); SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class); 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()); assertFalse(session.isOpen());
@@ -191,4 +302,77 @@ public class RangingSessionTest {
private void verifyOpenState(RangingSession session, boolean expected) { private void verifyOpenState(RangingSession session, boolean expected) {
assertEquals(expected, session.isOpen()); 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;
}
}
} }