summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-13 17:29:00 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-13 17:29:00 +0000
commit31ea91e41f513d708c766872c724055f65152caf (patch)
tree4a86a14d14c39357949158cc7d79580171607dcb
parent4dd015fee647e4cb6666018a41e797dec74ba42f (diff)
parentee34bb56f472e0c69222d0ea7ca319df23373e38 (diff)
downloadUwb-aml_adb_340912530.tar.gz
Snap for 10491609 from ee34bb56f472e0c69222d0ea7ca319df23373e38 to mainline-adbd-releaseaml_adb_340912530aml_adb_340912350aml_adb_340912200aml_adb_340912000android14-mainline-adbd-release
Change-Id: I68a02677c25ac98c1ccfb4bbcd23af2987604045
-rw-r--r--androidx_backend/src/androidx/core/uwb/backend/impl/internal/RangingDevice.java2
-rw-r--r--docs/FiRa_CRs_Android.csv4
-rw-r--r--framework/java/android/uwb/IUwbAdapter.aidl23
-rw-r--r--framework/java/android/uwb/RangingSession.java35
-rw-r--r--framework/tests/src/android/uwb/RangingSessionTest.java35
-rw-r--r--service/java/com/android/server/uwb/UwbServiceCore.java40
-rw-r--r--service/java/com/android/server/uwb/UwbServiceImpl.java10
-rw-r--r--service/java/com/android/server/uwb/UwbSessionManager.java102
-rw-r--r--service/java/com/android/server/uwb/UwbSessionNotificationHelper.java4
-rw-r--r--service/java/com/android/server/uwb/UwbTestUtils.java2
-rw-r--r--service/java/com/android/server/uwb/data/UwbUciConstants.java4
-rw-r--r--service/java/com/android/server/uwb/jni/NativeUwbManager.java22
-rw-r--r--service/java/com/android/server/uwb/params/FiraEncoder.java46
-rw-r--r--service/support_lib/src/com/google/uwb/support/fira/FiraHybridSessionConfig.java199
-rw-r--r--service/support_lib/src/com/google/uwb/support/fira/FiraOpenSessionParams.java63
-rw-r--r--service/support_lib/src/com/google/uwb/support/fira/FiraParams.java11
-rw-r--r--service/tests/src/com/android/server/uwb/UwbServiceCoreTest.java59
-rw-r--r--service/tests/src/com/android/server/uwb/UwbServiceImplTest.java15
-rw-r--r--service/tests/src/com/android/server/uwb/UwbSessionManagerTest.java140
-rw-r--r--service/tests/src/com/android/server/uwb/UwbSessionNotificationManagerTest.java6
-rw-r--r--service/tests/src/com/android/server/uwb/params/FiraEncoderTest.java58
-rw-r--r--service/uci/jni/src/uci_jni_android_new.rs80
22 files changed, 904 insertions, 56 deletions
diff --git a/androidx_backend/src/androidx/core/uwb/backend/impl/internal/RangingDevice.java b/androidx_backend/src/androidx/core/uwb/backend/impl/internal/RangingDevice.java
index 4ec07062..8df05baa 100644
--- a/androidx_backend/src/androidx/core/uwb/backend/impl/internal/RangingDevice.java
+++ b/androidx_backend/src/androidx/core/uwb/backend/impl/internal/RangingDevice.java
@@ -56,7 +56,7 @@ public abstract class RangingDevice {
private static final int SESSION_ID_UNSET = 0;
/** Timeout value after ranging start call */
- private static final int RANGING_START_TIMEOUT_MILLIS = 3000;
+ private static final int RANGING_START_TIMEOUT_MILLIS = 3100;
protected final UwbManager mUwbManager;
diff --git a/docs/FiRa_CRs_Android.csv b/docs/FiRa_CRs_Android.csv
index 11e7469d..9475f1b0 100644
--- a/docs/FiRa_CRs_Android.csv
+++ b/docs/FiRa_CRs_Android.csv
@@ -29,7 +29,7 @@ CR326,YES,
CR327,NA,Marked as N/A (MAC)
CR328,NO,
CR329,NO,
-CR330,NO,
+CR330,YES,
CR337,NA,CR-337 seems to have been withdrawn
CR338,NA,Marked as N/A (MAC)
CR340,NA,Marked as N/A (MAC)
@@ -85,7 +85,7 @@ CR432,NA,Marked as N/A (MAC)
CR433,YES,
CR435,YES,
CR436,NA,
-CR437,NO,
+CR437,YES,
CR438,YES,
CR441,NO,
CR442,YES,
diff --git a/framework/java/android/uwb/IUwbAdapter.aidl b/framework/java/android/uwb/IUwbAdapter.aidl
index 3228f5c5..a17e44d0 100644
--- a/framework/java/android/uwb/IUwbAdapter.aidl
+++ b/framework/java/android/uwb/IUwbAdapter.aidl
@@ -355,6 +355,29 @@ interface IUwbAdapter {
int sendVendorUciMessage(int mt, int gid, int oid, in byte[] payload);
+ /**
+ * @hide
+ * Sets the Hybrid UWB Session Configuration
+ *
+ * @param SessionHandle Primary session handle
+ * @param params protocol specific parameters to initiate the hybrid session
+ * @return HUS configuration status code
+ * <p>{@link UwbUciConstants#STATUS_CODE_OK} UWBS successfully processes the command
+ *
+ * <p>{@link UwbUciConstants#STATUS_CODE_FAILED} Intended operation is failed to complete
+ *
+ * <p>{@link UwbUciConstants#STATUS_CODE_ERROR_SESSION_NOT_EXIST} Primary session or
+ * secondary session is not existing or not created
+ *
+ * <p>{@link UwbUciConstants#STATUS_CODE_ERROR_SESSION_NOT_CONFIGURED} Primary session or
+ * secondary session has not been configured (i.e. SESSION_STATE_IDLE)
+ *
+ * <p>{@link UwbUciConstants#STATUS_CODE_ERROR_SESSION_DUPLICATE} Session Handle in phase
+ * list is repeated
+ */
+ int setHybridSessionConfiguration(in SessionHandle sessionHandle,
+ in PersistableBundle params);
+
void updateRangingRoundsDtTag(in SessionHandle sessionHandle, in PersistableBundle parameters);
void getUwbActivityEnergyInfoAsync(in IOnUwbActivityEnergyInfoListener listener);
diff --git a/framework/java/android/uwb/RangingSession.java b/framework/java/android/uwb/RangingSession.java
index a031c28e..b1ab55d9 100644
--- a/framework/java/android/uwb/RangingSession.java
+++ b/framework/java/android/uwb/RangingSession.java
@@ -800,6 +800,41 @@ public final class RangingSession implements AutoCloseable {
/**
* @hide
+ * Sets the Hybrid UWB Session Configuration
+ * <p>
+ * Requires the {@link android.Manifest.permission#UWB_PRIVILEGED} permission
+ *
+ * @param params protocol specific parameters to initiate the hybrid session
+ * @return HUS configuration status code
+ * <p>{@link UwbUciConstants#STATUS_CODE_OK} UWBS successfully processes the command
+ *
+ * <p>{@link UwbUciConstants#STATUS_CODE_FAILED} Intended operation is failed to complete
+ *
+ * <p>{@link UwbUciConstants#STATUS_CODE_ERROR_SESSION_NOT_EXIST} Primary session or
+ * secondary session is not existing or not created
+ *
+ * <p>{@link UwbUciConstants#STATUS_CODE_ERROR_SESSION_NOT_CONFIGURED} Primary session or
+ * secondary session has not been configured (i.e. SESSION_STATE_IDLE)
+ *
+ * <p>{@link UwbUciConstants#STATUS_CODE_ERROR_SESSION_DUPLICATE} Session Handle in phase
+ * list is repeated
+ * @throws RemoteException if a remote error occurred
+ */
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
+ public int setHybridSessionConfiguration(@NonNull PersistableBundle params) {
+ if (!isOpen()) {
+ throw new IllegalStateException("Ranging session is not open");
+ }
+
+ try {
+ return mAdapter.setHybridSessionConfiguration(mSessionHandle, params);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
*/
public void onRangingOpened() {
if (mState == State.CLOSED) {
diff --git a/framework/tests/src/android/uwb/RangingSessionTest.java b/framework/tests/src/android/uwb/RangingSessionTest.java
index 4673721d..4234adfe 100644
--- a/framework/tests/src/android/uwb/RangingSessionTest.java
+++ b/framework/tests/src/android/uwb/RangingSessionTest.java
@@ -71,6 +71,7 @@ public class RangingSessionTest {
private static final int HANDLE_ID = 12;
private static final int PID = Process.myPid();
private static final int MAX_DATA_SIZE = 100;
+ public static final int STATUS_OK = 0;
@Test
public void testOnRangingOpened_OnOpenSuccessCalled() {
@@ -533,6 +534,40 @@ public class RangingSessionTest {
}
@Test
+ public void testSetHybridSessionConfiguration() throws RemoteException {
+ assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
+ SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
+
+ // Confirm that setHybridSessionConfiguration() throws an IllegalStateException
+ // when the ranging session is not open.
+ assertFalse(session.isOpen());
+ verifyThrowIllegalState(() -> session.setHybridSessionConfiguration(PARAMS));
+
+ // Confirm that setHybridSessionConfiguration() returns a value when the ranging
+ // session has been opened.
+ session.onRangingOpened();
+ assertEquals(session.setHybridSessionConfiguration(PARAMS), STATUS_OK);
+
+ // Confirm that setHybridSessionConfiguration() returns a value when the ranging
+ // session has been started.
+ session.onRangingStarted(PARAMS);
+ assertEquals(session.setHybridSessionConfiguration(PARAMS), STATUS_OK);
+
+ // Confirm that setHybridSessionConfiguration() still returns a value, when the ranging
+ // session was stopped.
+ session.onRangingStopped(REASON, PARAMS);
+ assertEquals(session.setHybridSessionConfiguration(PARAMS), STATUS_OK);
+
+ // Confirm that setHybridSessionConfiguration() throws an IllegalStateException when the
+ // ranging session has now been closed.
+ session.onRangingClosed(REASON, PARAMS);
+ verifyThrowIllegalState(() -> session.setHybridSessionConfiguration(PARAMS));
+ }
+
+ @Test
public void testPoseUpdate() throws RemoteException {
assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
diff --git a/service/java/com/android/server/uwb/UwbServiceCore.java b/service/java/com/android/server/uwb/UwbServiceCore.java
index f7e852e0..9f5bb86c 100644
--- a/service/java/com/android/server/uwb/UwbServiceCore.java
+++ b/service/java/com/android/server/uwb/UwbServiceCore.java
@@ -199,6 +199,10 @@ public class UwbServiceCore implements INativeUwbManager.DeviceNotification,
}
private boolean isUwbEnabled() {
+ return getAdapterState() != AdapterStateCallback.STATE_DISABLED;
+ }
+
+ private boolean isUwbChipEnabled() {
synchronized (UwbServiceCore.this) {
return getInternalAdapterState() != AdapterStateCallback.STATE_DISABLED;
}
@@ -640,6 +644,24 @@ public class UwbServiceCore implements INativeUwbManager.DeviceNotification,
return getInternalAdapterState();
}
+ /**
+ * Configure a Hybrid session.
+ */
+ public int setHybridSessionConfiguration(SessionHandle sessionHandle,
+ PersistableBundle params) {
+ int status = UwbUciConstants.STATUS_CODE_FAILED;
+ if (!isUwbEnabled()) {
+ throw new IllegalStateException("Uwb is not enabled");
+ }
+ try {
+ status = mSessionManager.setHybridSessionConfiguration(sessionHandle, params);
+ return status;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to set Hybrid Session Configuration", e);
+ }
+ return status;
+ }
+
private /* @UwbManager.AdapterStateCallback.State */ int getInternalAdapterState() {
synchronized (UwbServiceCore.this) {
if (mChipIdToStateMap.isEmpty()) {
@@ -663,10 +685,10 @@ public class UwbServiceCore implements INativeUwbManager.DeviceNotification,
public synchronized void setEnabled(boolean enabled) {
int task = enabled ? TASK_ENABLE : TASK_DISABLE;
- if (enabled && isUwbEnabled()) {
- Log.w(TAG, "Uwb is already enabled");
- } else if (!enabled && !isUwbEnabled()) {
- Log.w(TAG, "Uwb is already disabled");
+ if (enabled && isUwbChipEnabled()) {
+ Log.w(TAG, "Uwb chip is already enabled");
+ } else if (!enabled && !isUwbChipEnabled()) {
+ Log.w(TAG, "Uwb chip is already disabled");
}
mUwbTask.execute(task);
@@ -826,8 +848,9 @@ public class UwbServiceCore implements INativeUwbManager.DeviceNotification,
}
private void handleEnable() {
- if (isUwbEnabled()) {
- Log.i(TAG, "UWB service is already enabled");
+ if (isUwbChipEnabled()) {
+ Log.i(TAG, "UWB chip is already enabled, notify adapter state = "
+ + getAdapterState());
return;
}
try {
@@ -890,8 +913,9 @@ public class UwbServiceCore implements INativeUwbManager.DeviceNotification,
}
private void handleDisable() {
- if (!isUwbEnabled()) {
- Log.i(TAG, "UWB service is already disabled");
+ if (!isUwbChipEnabled()) {
+ Log.i(TAG, "UWB chip is already disabled, notify adapter state = "
+ + getAdapterState());
return;
}
diff --git a/service/java/com/android/server/uwb/UwbServiceImpl.java b/service/java/com/android/server/uwb/UwbServiceImpl.java
index a1b16603..e8440184 100644
--- a/service/java/com/android/server/uwb/UwbServiceImpl.java
+++ b/service/java/com/android/server/uwb/UwbServiceImpl.java
@@ -396,6 +396,16 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
}
@Override
+ public int setHybridSessionConfiguration(SessionHandle sessionHandle,
+ PersistableBundle params) {
+ if (!SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException();
+ }
+ enforceUwbPrivilegedPermission();
+ return mUwbServiceCore.setHybridSessionConfiguration(sessionHandle, params);
+ }
+
+ @Override
public synchronized void setEnabled(boolean enabled) throws RemoteException {
enforceUwbPrivilegedPermission();
persistUwbToggleState(enabled);
diff --git a/service/java/com/android/server/uwb/UwbSessionManager.java b/service/java/com/android/server/uwb/UwbSessionManager.java
index 33ecccfe..71ed4b74 100644
--- a/service/java/com/android/server/uwb/UwbSessionManager.java
+++ b/service/java/com/android/server/uwb/UwbSessionManager.java
@@ -92,6 +92,7 @@ import com.google.uwb.support.ccc.CccSpecificationParams;
import com.google.uwb.support.ccc.CccStartRangingParams;
import com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdate;
import com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdateStatus;
+import com.google.uwb.support.fira.FiraHybridSessionConfig;
import com.google.uwb.support.fira.FiraOpenSessionParams;
import com.google.uwb.support.fira.FiraParams;
import com.google.uwb.support.fira.FiraPoseUpdateParams;
@@ -104,6 +105,8 @@ import com.google.uwb.support.oemextension.SessionStatus;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -129,6 +132,7 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification,
private static final String TAG = "UwbSessionManager";
private static final byte OPERATION_TYPE_INIT_SESSION = 0;
+ private static final int UWB_HUS_PHASE_SIZE = 8;
@VisibleForTesting
public static final int SESSION_OPEN_RANGING = 1;
@@ -1003,6 +1007,44 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification,
mEventTask.execute(SESSION_SEND_DATA, info);
}
+ /**
+ * Sets the hybrid UWB configuration
+ *
+ * @param sessionHandle : Primary session handle
+ * @param params : protocol specific parameters to initiate the hybrid
+ * session
+ * @return the status code of the operation
+ * @throws RemoteException if an error occurs during the remote call.
+ */
+ public int setHybridSessionConfiguration(SessionHandle sessionHandle, PersistableBundle params)
+ throws RemoteException {
+ if (!isExistedSession(sessionHandle)) {
+ throw new IllegalStateException("Not initialized session ID");
+ }
+
+ FiraHybridSessionConfig husConfig = FiraHybridSessionConfig.fromBundle(params);
+ int numberOfPhases = husConfig.getNumberOfPhases();
+ int sessionId = getSessionId(sessionHandle);
+
+ Log.i(TAG, "setHybridSessionConfiguration() - sessionId: " + sessionId
+ + ", sessionHandle: " + sessionHandle
+ + ", numberOfPhases: " + numberOfPhases);
+
+ ByteBuffer buffer = ByteBuffer.allocate(numberOfPhases * UWB_HUS_PHASE_SIZE);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (FiraHybridSessionConfig.FiraHybridSessionPhaseList phaseList :
+ husConfig.getPhaseList()) {
+ buffer.putInt(mNativeUwbManager.getSessionToken(phaseList.getSessionHandle(),
+ getUwbSession(sessionId).getChipId()));
+ buffer.putShort(phaseList.getStartSlotIndex());
+ buffer.putShort(phaseList.getEndSlotIndex());
+ }
+
+ return mNativeUwbManager.setHybridSessionConfiguration(sessionId, numberOfPhases,
+ husConfig.getUpdateTime(), buffer.array(), getUwbSession(sessionId).getChipId());
+ }
+
private static final class SendDataInfo {
public SessionHandle sessionHandle;
public UwbAddress remoteDeviceAddress;
@@ -1330,11 +1372,21 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification,
() -> {
int status = UwbUciConstants.STATUS_CODE_FAILED;
synchronized (uwbSession.getWaitObj()) {
+ if (uwbSession.getNeedsQueryUwbsTimestamp()) {
+ // Query the UWBS timestamp and add the relative initiation time
+ // stored in the FiraOpenSessionParams, to get the absolute
+ // initiation time to be configured.
+ long uwbsTimestamp =
+ mUwbInjector.getUwbServiceCore().queryUwbsTimestampMicros();
+ uwbSession.computeAbsoluteInitiationTime(uwbsTimestamp);
+ }
+
if (uwbSession.getNeedsAppConfigUpdate()) {
uwbSession.resetNeedsAppConfigUpdate();
status = mConfigurationManager.setAppConfigurations(
uwbSession.getSessionId(),
uwbSession.getParams(), uwbSession.getChipId());
+ uwbSession.resetAbsoluteInitiationTime();
if (status != UwbUciConstants.STATUS_CODE_OK) {
mSessionNotificationManager.onRangingStartFailed(
uwbSession, status);
@@ -1809,6 +1861,7 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification,
private int mStackSessionPriority;
private boolean mSessionPriorityOverride = false;
private boolean mNeedsAppConfigUpdate = false;
+ private boolean mNeedsQueryUwbsTimestamp = false;
private UwbMulticastListUpdateStatus mMulticastListUpdateStatus;
private final int mProfileType;
private AlarmManager.OnAlarmListener mRangingResultErrorStreakTimerListener;
@@ -2151,6 +2204,51 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification,
mStackSessionPriority).build();
this.mNeedsAppConfigUpdate = true;
}
+
+ // When the UWBS supports Fira 2.0+ and the application has not configured an absolute
+ // UWB initiation time, we must fetch the UWBS timestamp (to compute the absolute time).
+ GenericSpecificationParams specificationParams =
+ mUwbInjector.getUwbServiceCore().getCachedSpecificationParams(mChipId);
+ if (specificationParams != null
+ && specificationParams.getFiraSpecificationParams()
+ .getMinPhyVersionSupported().getMajor() >= 2
+ && firaOpenSessionParams.getAbsoluteInitiationTime() == 0) {
+ this.mNeedsQueryUwbsTimestamp = true;
+ }
+ }
+
+ /**
+ * Compute {@code FiraOpenSessionParams.absolute_initiation_time}, by doing a sum of the
+ * UWBS Timestamp (in micro-seconds) and the relative
+ * {@code FiraOpenSessionParams.initiation_time} (in milli-seconds). This method should be
+ * called only for FiRa UCI ProtocolVersion >= 2.0 devices.
+ */
+ public void computeAbsoluteInitiationTime(long uwbsTimestamp) {
+ if (this.mNeedsQueryUwbsTimestamp) {
+ FiraOpenSessionParams firaOpenSessionParams = (FiraOpenSessionParams) mParams;
+ this.mParams = ((FiraOpenSessionParams) mParams).toBuilder()
+ .setAbsoluteInitiationTime(
+ uwbsTimestamp + firaOpenSessionParams.getInitiationTime() * 1000)
+ .build();
+ this.mNeedsAppConfigUpdate = true;
+ }
+ }
+
+ /**
+ * Reset the computed {@code FiraOpenSessionParams.absolute_initiation_time}, only when it
+ * was computed and set by this class (it should not be reset when it was provided by the
+ * application}.
+ */
+ public void resetAbsoluteInitiationTime() {
+ if (this.mNeedsQueryUwbsTimestamp) {
+ FiraOpenSessionParams firaOpenSessionParams = (FiraOpenSessionParams) mParams;
+ // Reset the absolute Initiation time, so that it's re-computed if start ranging is
+ // called in the future for this UWB session.
+ this.mParams = ((FiraOpenSessionParams) mParams).toBuilder()
+ .setAbsoluteInitiationTime(0)
+ .build();
+ this.mNeedsQueryUwbsTimestamp = false;
+ }
}
public void updateFiraParamsOnReconfigure(FiraRangingReconfigureParams reconfigureParams) {
@@ -2231,6 +2329,10 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification,
this.mNeedsAppConfigUpdate = false;
}
+ public boolean getNeedsQueryUwbsTimestamp() {
+ return this.mNeedsQueryUwbsTimestamp;
+ }
+
public Set<Long> getRemoteMacAddressList() {
return mReceivedDataInfoMap.keySet();
}
diff --git a/service/java/com/android/server/uwb/UwbSessionNotificationHelper.java b/service/java/com/android/server/uwb/UwbSessionNotificationHelper.java
index 2562cecf..047bb3f2 100644
--- a/service/java/com/android/server/uwb/UwbSessionNotificationHelper.java
+++ b/service/java/com/android/server/uwb/UwbSessionNotificationHelper.java
@@ -46,6 +46,10 @@ public class UwbSessionNotificationHelper {
case UwbUciConstants.REASON_ERROR_INVALID_RANGING_INTERVAL:
case UwbUciConstants.REASON_ERROR_INVALID_STS_CONFIG:
case UwbUciConstants.REASON_ERROR_INVALID_RFRAME_CONFIG:
+ case UwbUciConstants.REASON_ERROR_HUS_NOT_ENOUGH_SLOTS:
+ case UwbUciConstants.REASON_ERROR_HUS_CFP_PHASE_TOO_SHORT:
+ case UwbUciConstants.REASON_ERROR_HUS_CAP_PHASE_TOO_SHORT:
+ case UwbUciConstants.REASON_ERROR_HUS_OTHERS:
rangingChangeReason = RangingChangeReason.BAD_PARAMETERS;
break;
case UwbUciConstants.REASON_REGULATION_UWB_OFF:
diff --git a/service/java/com/android/server/uwb/UwbTestUtils.java b/service/java/com/android/server/uwb/UwbTestUtils.java
index 7406e13e..6bd4ee03 100644
--- a/service/java/com/android/server/uwb/UwbTestUtils.java
+++ b/service/java/com/android/server/uwb/UwbTestUtils.java
@@ -88,7 +88,7 @@ public class UwbTestUtils {
private static final long TEST_CURR_RANGING_INTERVAL = 100;
private static final int TEST_RANGING_MEASURES_TYPE = RANGING_MEASUREMENT_TYPE_TWO_WAY;
private static final int TEST_MAC_ADDRESS_MODE = 1;
- private static final int TEST_STATUS = FiraParams.STATUS_CODE_OK;
+ public static final int TEST_STATUS = FiraParams.STATUS_CODE_OK;
private static final int TEST_LOS = 0;
private static final int TEST_DISTANCE = 101;
private static final float TEST_AOA_AZIMUTH = 67;
diff --git a/service/java/com/android/server/uwb/data/UwbUciConstants.java b/service/java/com/android/server/uwb/data/UwbUciConstants.java
index cd2a95a8..6511c9e3 100644
--- a/service/java/com/android/server/uwb/data/UwbUciConstants.java
+++ b/service/java/com/android/server/uwb/data/UwbUciConstants.java
@@ -69,6 +69,10 @@ public class UwbUciConstants {
public static final int REASON_ERROR_INVALID_RANGING_INTERVAL = 0x23;
public static final int REASON_ERROR_INVALID_STS_CONFIG = 0x24;
public static final int REASON_ERROR_INVALID_RFRAME_CONFIG = 0x25;
+ public static final int REASON_ERROR_HUS_NOT_ENOUGH_SLOTS = 0x26;
+ public static final int REASON_ERROR_HUS_CFP_PHASE_TOO_SHORT = 0x27;
+ public static final int REASON_ERROR_HUS_CAP_PHASE_TOO_SHORT = 0x28;
+ public static final int REASON_ERROR_HUS_OTHERS = 0x29;
/* Vendor Specific reason codes */
public static final int REASON_REGULATION_UWB_OFF =
UwbVendorReasonCodes.REASON_REGULATION_UWB_OFF;
diff --git a/service/java/com/android/server/uwb/jni/NativeUwbManager.java b/service/java/com/android/server/uwb/jni/NativeUwbManager.java
index b4454c7e..ddec1c90 100644
--- a/service/java/com/android/server/uwb/jni/NativeUwbManager.java
+++ b/service/java/com/android/server/uwb/jni/NativeUwbManager.java
@@ -464,6 +464,25 @@ public class NativeUwbManager {
}
}
+ /**
+ * Sets the Hybrid UWB Session Configuration
+ *
+ * @param sessionId : Primary session ID
+ * @param numberOfPhases : Number of secondary sessions
+ * @param updateTime : Absolute time in UWBS Time domain
+ * @param phaseList : list of secondary sessions which have been previously initialized and
+ * configured
+ * @param chipId : Identifier of UWB chip for multi-HAL devices
+ * @return Byte representing the status of the operation
+ */
+ public byte setHybridSessionConfiguration(int sessionId, int numberOfPhases, byte[] updateTime,
+ byte[] phaseList, String chipId) {
+ synchronized (mNativeLock) {
+ return nativeSetHybridSessionConfigurations(sessionId, numberOfPhases, updateTime,
+ phaseList, chipId);
+ }
+ }
+
private native byte nativeSendData(int sessionId, byte[] address,
short sequenceNum, byte[] appData, String chipId);
@@ -522,4 +541,7 @@ public class NativeUwbManager {
private native long nativeQueryUwbTimestamp(String chipId);
private native int nativeGetSessionToken(int sessionId, String chipId);
+
+ private native byte nativeSetHybridSessionConfigurations(int sessionId, int noOfPhases,
+ byte[] updateTime, byte[] phaseList, String chipId);
}
diff --git a/service/java/com/android/server/uwb/params/FiraEncoder.java b/service/java/com/android/server/uwb/params/FiraEncoder.java
index be0c011b..f348d968 100644
--- a/service/java/com/android/server/uwb/params/FiraEncoder.java
+++ b/service/java/com/android/server/uwb/params/FiraEncoder.java
@@ -117,24 +117,36 @@ public class FiraEncoder extends TlvEncoder {
tlvBufferBuilder.putInt(ConfigParam.RANGING_INTERVAL, params.getRangingIntervalMs());
}
if (deviceRole != FiraParams.RANGING_DEVICE_DT_TAG) {
- tlvBufferBuilder.putByte(ConfigParam.DEVICE_TYPE, (byte) params.getDeviceType())
- .putByte(ConfigParam.NUMBER_OF_CONTROLEES,
- (byte) params.getDestAddressList().size());
+ tlvBufferBuilder.putByte(ConfigParam.DEVICE_TYPE, (byte) params.getDeviceType());
+ }
+
+ if (isTimeScheduledTwrSession(
+ params.getScheduledMode(), params.getRangingRoundUsage())) {
if (params.getDestAddressList().size() > 0) {
ByteBuffer dstAddressList = ByteBuffer.allocate(1024);
for (UwbAddress address : params.getDestAddressList()) {
dstAddressList.put(getComputedMacAddress(address));
}
- tlvBufferBuilder.putByteArray(
- ConfigParam.DST_MAC_ADDRESS, dstAddressList.position(),
- Arrays.copyOf(dstAddressList.array(), dstAddressList.position()));
+ tlvBufferBuilder
+ .putByte(ConfigParam.NUMBER_OF_CONTROLEES,
+ (byte) params.getDestAddressList().size())
+ .putByteArray(
+ ConfigParam.DST_MAC_ADDRESS, dstAddressList.position(),
+ Arrays.copyOf(dstAddressList.array(), dstAddressList.position()));
}
}
+
if (params.getProtocolVersion().getMajor() >= 2) {
// Initiation time Changed from 4 byte field to 8 byte field in version 2.
if (deviceRole != FiraParams.RANGING_DEVICE_DT_TAG) {
- tlvBufferBuilder.putLong(ConfigParam.UWB_INITIATION_TIME,
- params.getInitiationTime());
+ // For FiRa 2.0+ device, prefer to set the Absolute UWB Initiation time.
+ if (params.getAbsoluteInitiationTime() > 0) {
+ tlvBufferBuilder.putLong(ConfigParam.UWB_INITIATION_TIME,
+ params.getAbsoluteInitiationTime());
+ } else {
+ tlvBufferBuilder.putLong(ConfigParam.UWB_INITIATION_TIME,
+ params.getInitiationTime());
+ }
}
tlvBufferBuilder.putByte(ConfigParam.LINK_LAYER_MODE, (byte) params.getLinkLayerMode())
.putByte(ConfigParam.DATA_REPETITION_COUNT,
@@ -145,10 +157,12 @@ public class FiraEncoder extends TlvEncoder {
(byte) params.getApplicationDataEndpoint());
} else {
if (deviceRole != FiraParams.RANGING_DEVICE_DT_TAG) {
- tlvBufferBuilder.putInt(ConfigParam.UWB_INITIATION_TIME,
- Math.toIntExact(params.getInitiationTime()));
+ tlvBufferBuilder
+ .putInt(ConfigParam.UWB_INITIATION_TIME,
+ Math.toIntExact(params.getInitiationTime()));
}
}
+
if ((stsConfig == FiraParams.STS_CONFIG_DYNAMIC_FOR_CONTROLEE_INDIVIDUAL_KEY)
&& (deviceType == FiraParams.RANGING_DEVICE_TYPE_CONTROLEE)) {
tlvBufferBuilder.putInt(ConfigParam.SUB_SESSION_ID, params.getSubSessionId());
@@ -234,6 +248,18 @@ public class FiraEncoder extends TlvEncoder {
return tlvBufferBuilder.build();
}
+ private boolean isTimeScheduledTwrSession(int scheduledMode, int rangingUsage) {
+ if (scheduledMode == FiraParams.TIME_SCHEDULED_RANGING) {
+ if (rangingUsage == FiraParams.RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE
+ || rangingUsage == FiraParams.RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE
+ || rangingUsage == FiraParams.RANGING_ROUND_USAGE_SS_TWR_NON_DEFERRED_MODE
+ || rangingUsage == FiraParams.RANGING_ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private byte[] getUlTdoaDeviceId(int ulTdoaDeviceIdType, byte[] ulTdoaDeviceId) {
if (ulTdoaDeviceIdType == FiraParams.UL_TDOA_DEVICE_ID_NONE) {
// Device ID not included
diff --git a/service/support_lib/src/com/google/uwb/support/fira/FiraHybridSessionConfig.java b/service/support_lib/src/com/google/uwb/support/fira/FiraHybridSessionConfig.java
new file mode 100644
index 00000000..33bb0a1a
--- /dev/null
+++ b/service/support_lib/src/com/google/uwb/support/fira/FiraHybridSessionConfig.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2023 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 com.google.uwb.support.fira;
+
+import android.os.PersistableBundle;
+
+import androidx.annotation.Nullable;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Uwb Hybrid session configuration
+ */
+public class FiraHybridSessionConfig extends FiraParams {
+ private static final int BUNDLE_VERSION_1 = 1;
+ private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_1;
+ private static final int UWB_HUS_PHASE_SIZE = 8;
+
+ private final int mNumberOfPhases;
+ private final byte[] mUpdateTime;
+ private final List<FiraHybridSessionPhaseList> mPhaseList;
+
+ public static final String KEY_BUNDLE_VERSION = "bundle_version";
+ public static final String KEY_NUMBER_OF_PHASES = "number_of_phases";
+ public static final String KEY_UPDATE_TIME = "update_time";
+ public static final String KEY_PHASE_LIST = "phase_list";
+
+ @Override
+ public int getBundleVersion() {
+ return BUNDLE_VERSION_CURRENT;
+ }
+
+ public int getNumberOfPhases() {
+ return mNumberOfPhases;
+ }
+
+ public byte[] getUpdateTime() {
+ return mUpdateTime;
+ }
+
+ public List<FiraHybridSessionPhaseList> getPhaseList() {
+ return mPhaseList;
+ }
+
+ private FiraHybridSessionConfig(int numberOfPhases, byte[] updateTime,
+ List<FiraHybridSessionPhaseList> phaseList) {
+ mNumberOfPhases = numberOfPhases;
+ mUpdateTime = updateTime;
+ mPhaseList = phaseList;
+ }
+
+ //TODO, move these utility methods to helper class
+ @Nullable
+ private static int[] byteArrayToIntArray(@Nullable byte[] bytes) {
+ if (bytes == null) {
+ return null;
+ }
+
+ int[] values = new int[bytes.length];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = bytes[i];
+ }
+ return values;
+ }
+
+ @Nullable
+ private static byte[] intArrayToByteArray(@Nullable int[] values) {
+ if (values == null) {
+ return null;
+ }
+ byte[] bytes = new byte[values.length];
+ for (int i = 0; i < values.length; i++) {
+ bytes[i] = (byte) values[i];
+ }
+ return bytes;
+ }
+
+ public PersistableBundle toBundle() {
+ PersistableBundle bundle = super.toBundle();
+ bundle.putInt(KEY_BUNDLE_VERSION, getBundleVersion());
+ bundle.putInt(KEY_NUMBER_OF_PHASES, mNumberOfPhases);
+ bundle.putIntArray(KEY_UPDATE_TIME, byteArrayToIntArray(mUpdateTime));
+
+ ByteBuffer buffer = ByteBuffer.allocate(mNumberOfPhases * UWB_HUS_PHASE_SIZE);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ for (FiraHybridSessionPhaseList phaseList : mPhaseList) {
+ buffer.putInt(phaseList.getSessionHandle());
+ buffer.putShort(phaseList.getStartSlotIndex());
+ buffer.putShort(phaseList.getEndSlotIndex());
+ }
+
+ bundle.putIntArray(KEY_PHASE_LIST, byteArrayToIntArray(buffer.array()));
+ return bundle;
+ }
+
+ public static FiraHybridSessionConfig fromBundle(PersistableBundle bundle) {
+ switch (bundle.getInt(KEY_BUNDLE_VERSION)) {
+ case BUNDLE_VERSION_1:
+ return parseVersion1(bundle);
+ default:
+ throw new IllegalArgumentException("Invalid bundle version");
+ }
+ }
+
+ private static FiraHybridSessionConfig parseVersion1(PersistableBundle bundle) {
+ FiraHybridSessionConfig.Builder builder = new FiraHybridSessionConfig.Builder();
+
+ int numberOfPhases = bundle.getInt(KEY_NUMBER_OF_PHASES);
+ builder.setNumberOfPhases(numberOfPhases);
+ builder.setUpdateTime(intArrayToByteArray(bundle.getIntArray(KEY_UPDATE_TIME)));
+
+ byte[] phaseByteArray = intArrayToByteArray(bundle.getIntArray(KEY_PHASE_LIST));
+ ByteBuffer buffer = ByteBuffer.wrap(phaseByteArray);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (int i = 0; i < numberOfPhases; i++) {
+ FiraHybridSessionPhaseList mFiraHybridSessionPhaseList = new FiraHybridSessionPhaseList(
+ buffer.getInt(),
+ buffer.getShort(),
+ buffer.getShort());
+ builder.addPhaseList(mFiraHybridSessionPhaseList);
+ }
+ return builder.build();
+ }
+
+ /** Builder */
+ public static class Builder {
+ private int mNumberOfPhases;
+ private byte[] mUpdateTime;
+ private final List<FiraHybridSessionPhaseList> mPhaseList = new ArrayList<>();
+
+ public FiraHybridSessionConfig.Builder setNumberOfPhases(int numberOfPhases) {
+ mNumberOfPhases = numberOfPhases;
+ return this;
+ }
+
+ public FiraHybridSessionConfig.Builder setUpdateTime(byte[] updateTime) {
+ mUpdateTime = updateTime;
+ return this;
+ }
+
+ public FiraHybridSessionConfig.Builder addPhaseList(FiraHybridSessionPhaseList phaseList) {
+ mPhaseList.add(phaseList);
+ return this;
+ }
+
+ public FiraHybridSessionConfig build() {
+ if (mPhaseList.size() == 0) {
+ throw new IllegalStateException("No hybrid session phase list have been set");
+ }
+ return new FiraHybridSessionConfig(
+ mNumberOfPhases,
+ mUpdateTime,
+ mPhaseList);
+ }
+ }
+
+ /** Defines parameters for hybrid session's secondary phase list */
+ public static class FiraHybridSessionPhaseList {
+ private final int mSessionHandle;
+ private final short mStartSlotIndex;
+ private final short mEndSlotIndex;
+
+ public FiraHybridSessionPhaseList(int sessionHandle, short startSlotIndex,
+ short endSlotIndex) {
+ mSessionHandle = sessionHandle;
+ mStartSlotIndex = startSlotIndex;
+ mEndSlotIndex = endSlotIndex;
+ }
+
+ public int getSessionHandle() {
+ return mSessionHandle;
+ }
+
+ public short getStartSlotIndex() {
+ return mStartSlotIndex;
+ }
+
+ public short getEndSlotIndex() {
+ return mEndSlotIndex;
+ }
+ }
+}
diff --git a/service/support_lib/src/com/google/uwb/support/fira/FiraOpenSessionParams.java b/service/support_lib/src/com/google/uwb/support/fira/FiraOpenSessionParams.java
index c5b4fdfe..06f992a8 100644
--- a/service/support_lib/src/com/google/uwb/support/fira/FiraOpenSessionParams.java
+++ b/service/support_lib/src/com/google/uwb/support/fira/FiraOpenSessionParams.java
@@ -57,8 +57,10 @@ public class FiraOpenSessionParams extends FiraParams {
private final List<UwbAddress> mDestAddressList;
// FiRa 1.0: Relative time (in milli-seconds).
- // FiRa 2.0: Absolute time in UWB time domain, as specified in CR-272 (in micro-seconds).
+ // FiRa 2.0: Relative time (in milli-seconds).
private final long mInitiationTime;
+ // FiRa 2.0: Absolute time in UWB time domain, as specified in CR-272 (in micro-seconds).
+ private final long mAbsoluteInitiationTime;
private final int mSlotDurationRstu;
private final int mSlotsPerRangingRound;
private final int mRangingIntervalMs;
@@ -159,6 +161,7 @@ public class FiraOpenSessionParams extends FiraParams {
private static final String KEY_DEVICE_ADDRESS = "device_address";
private static final String KEY_DEST_ADDRESS_LIST = "dest_address_list";
private static final String KEY_INITIATION_TIME_MS = "initiation_time_ms";
+ private static final String KEY_ABSOLUTE_INITIATION_TIME_US = "absolute_initiation_time_us";
private static final String KEY_SLOT_DURATION_RSTU = "slot_duration_rstu";
private static final String KEY_SLOTS_PER_RANGING_ROUND = "slots_per_ranging_round";
private static final String KEY_RANGING_INTERVAL_MS = "ranging_interval_ms";
@@ -261,6 +264,7 @@ public class FiraOpenSessionParams extends FiraParams {
UwbAddress deviceAddress,
List<UwbAddress> destAddressList,
long initiationTime,
+ long absoluteInitiationTime,
int slotDurationRstu,
int slotsPerRangingRound,
int rangingIntervalMs,
@@ -341,6 +345,7 @@ public class FiraOpenSessionParams extends FiraParams {
mDeviceAddress = deviceAddress;
mDestAddressList = destAddressList;
mInitiationTime = initiationTime;
+ mAbsoluteInitiationTime = absoluteInitiationTime;
mSlotDurationRstu = slotDurationRstu;
mSlotsPerRangingRound = slotsPerRangingRound;
mRangingIntervalMs = rangingIntervalMs;
@@ -459,6 +464,10 @@ public class FiraOpenSessionParams extends FiraParams {
return mInitiationTime;
}
+ public long getAbsoluteInitiationTime() {
+ return mAbsoluteInitiationTime;
+ }
+
public int getSlotDurationRstu() {
return mSlotDurationRstu;
}
@@ -812,6 +821,7 @@ public class FiraOpenSessionParams extends FiraParams {
}
bundle.putLong(KEY_INITIATION_TIME_MS, mInitiationTime);
+ bundle.putLong(KEY_ABSOLUTE_INITIATION_TIME_US, mAbsoluteInitiationTime);
bundle.putInt(KEY_SLOT_DURATION_RSTU, mSlotDurationRstu);
bundle.putInt(KEY_SLOTS_PER_RANGING_ROUND, mSlotsPerRangingRound);
bundle.putInt(KEY_RANGING_INTERVAL_MS, mRangingIntervalMs);
@@ -934,6 +944,7 @@ public class FiraOpenSessionParams extends FiraParams {
// Changed from int to long. Look for int value, if long value not found to
// maintain backwards compatibility.
.setInitiationTime(bundle.getLong(KEY_INITIATION_TIME_MS))
+ .setAbsoluteInitiationTime(bundle.getLong(KEY_ABSOLUTE_INITIATION_TIME_US))
.setSlotDurationRstu(bundle.getInt(KEY_SLOT_DURATION_RSTU))
.setSlotsPerRangingRound(bundle.getInt(KEY_SLOTS_PER_RANGING_ROUND))
.setRangingIntervalMs(bundle.getInt(KEY_RANGING_INTERVAL_MS))
@@ -1027,13 +1038,15 @@ public class FiraOpenSessionParams extends FiraParams {
.setApplicationDataEndpoint(bundle.getInt(
KEY_APPLICATION_DATA_ENDPOINT, APPLICATION_DATA_ENDPOINT_DEFAULT));
- if (builder.mDeviceRole.get() != RANGING_DEVICE_DT_TAG) {
+ if (builder.isTimeScheduledTwrSession()) {
long[] destAddresses = bundle.getLongArray(KEY_DEST_ADDRESS_LIST);
- List<UwbAddress> destAddressList = new ArrayList<>();
- for (long address : destAddresses) {
- destAddressList.add(longToUwbAddress(address, addressByteLength));
+ if (destAddresses != null) {
+ List<UwbAddress> destAddressList = new ArrayList<>();
+ for (long address : destAddresses) {
+ destAddressList.add(longToUwbAddress(address, addressByteLength));
+ }
+ builder.setDestAddressList(destAddressList);
}
- builder.setDestAddressList(destAddressList);
}
return builder.build();
}
@@ -1067,6 +1080,7 @@ public class FiraOpenSessionParams extends FiraParams {
/** UCI spec default: 0ms */
private long mInitiationTime = 0;
+ private long mAbsoluteInitiationTime = 0;
/** UCI spec default: 2400 RSTU (2 ms). */
private int mSlotDurationRstu = 2400;
@@ -1293,6 +1307,7 @@ public class FiraOpenSessionParams extends FiraParams {
mDeviceAddress = builder.mDeviceAddress;
mDestAddressList = builder.mDestAddressList;
mInitiationTime = builder.mInitiationTime;
+ mAbsoluteInitiationTime = builder.mAbsoluteInitiationTime;
mSlotDurationRstu = builder.mSlotDurationRstu;
mSlotsPerRangingRound = builder.mSlotsPerRangingRound;
mRangingIntervalMs = builder.mRangingIntervalMs;
@@ -1377,6 +1392,7 @@ public class FiraOpenSessionParams extends FiraParams {
mDeviceAddress = params.mDeviceAddress;
mDestAddressList = params.mDestAddressList;
mInitiationTime = params.mInitiationTime;
+ mAbsoluteInitiationTime = params.mAbsoluteInitiationTime;
mSlotDurationRstu = params.mSlotDurationRstu;
mSlotsPerRangingRound = params.mSlotsPerRangingRound;
mRangingIntervalMs = params.mRangingIntervalMs;
@@ -1500,14 +1516,26 @@ public class FiraOpenSessionParams extends FiraParams {
/**
* @param initiationTime UWB initiation time:
* FiRa 1.0: Relative time (in milli-seconds).
- * FiRa 2.0: Absolute time in UWB time domain, as specified in CR-272
- * (in micro-seconds).
+ * FiRa 2.0: Relative time (in milli-seconds).
+ * For a FiRa 2.0 device, the UWB Service will query the absolute UWBS timestamp
+ * and add the relative time (in milli-seconds) configured here, to compute the
+ * absolute time that will be configured in the UWB_INITIATION_TIME parameter.
*/
public FiraOpenSessionParams.Builder setInitiationTime(long initiationTime) {
mInitiationTime = initiationTime;
return this;
}
+ /**
+ * @param absoluteInitiationTime Absolute UWB initiation time (in micro-seconds). This is
+ * applicable only for FiRa 2.0+ devices, as specified in CR-272.
+ */
+ public FiraOpenSessionParams.Builder setAbsoluteInitiationTime(
+ long absoluteInitiationTime) {
+ mAbsoluteInitiationTime = absoluteInitiationTime;
+ return this;
+ }
+
public FiraOpenSessionParams.Builder setSlotDurationRstu(int slotDurationRstu) {
mSlotDurationRstu = slotDurationRstu;
return this;
@@ -1927,7 +1955,7 @@ public class FiraOpenSessionParams extends FiraParams {
// Make sure address length matches the address mode
checkArgument(mDeviceAddress != null && mDeviceAddress.size() == addressByteLength);
- if (mDeviceRole.get() != RANGING_DEVICE_DT_TAG) {
+ if (isTimeScheduledTwrSession()) {
checkNotNull(mDestAddressList);
for (UwbAddress destAddress : mDestAddressList) {
checkArgument(destAddress != null
@@ -2052,6 +2080,22 @@ public class FiraOpenSessionParams extends FiraParams {
return this;
}
+ /**
+ * Returns true when (RangingRoundUsage = 1, 2, 3, 4) and
+ * SCHEDULED_MODE == 0x01 (TIME_SCHEDULED_RANGING)
+ **/
+ public boolean isTimeScheduledTwrSession() {
+ if (mScheduledMode == FiraParams.TIME_SCHEDULED_RANGING) {
+ if (mRangingRoundUsage == RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE
+ || mRangingRoundUsage == RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE
+ || mRangingRoundUsage == RANGING_ROUND_USAGE_SS_TWR_NON_DEFERRED_MODE
+ || mRangingRoundUsage == RANGING_ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public FiraOpenSessionParams build() {
checkAddress();
checkStsConfig();
@@ -2069,6 +2113,7 @@ public class FiraOpenSessionParams extends FiraParams {
mDeviceAddress,
mDestAddressList,
mInitiationTime,
+ mAbsoluteInitiationTime,
mSlotDurationRstu,
mSlotsPerRangingRound,
mRangingIntervalMs,
diff --git a/service/support_lib/src/com/google/uwb/support/fira/FiraParams.java b/service/support_lib/src/com/google/uwb/support/fira/FiraParams.java
index 55d65ede..a35d100b 100644
--- a/service/support_lib/src/com/google/uwb/support/fira/FiraParams.java
+++ b/service/support_lib/src/com/google/uwb/support/fira/FiraParams.java
@@ -233,6 +233,7 @@ public abstract class FiraParams extends Params {
value = {
CONTENTION_BASED_RANGING,
TIME_SCHEDULED_RANGING,
+ HYBRID_SCHEDULED_RANGING,
})
public @interface SchedulingMode {}
@@ -240,6 +241,8 @@ public abstract class FiraParams extends Params {
public static final int TIME_SCHEDULED_RANGING = 1;
+ public static final int HYBRID_SCHEDULED_RANGING = 2;
+
/** Ranging Time Struct */
@IntDef(
value = {
@@ -629,6 +632,10 @@ public abstract class FiraParams extends Params {
STATE_CHANGE_REASON_CODE_ERROR_INVALID_RANGING_INTERVAL,
STATE_CHANGE_REASON_CODE_ERROR_INVALID_STS_CONFIG,
STATE_CHANGE_REASON_CODE_ERROR_INVALID_RFRAME_CONFIG,
+ STATE_CHANGE_REASON_CODE_ERROR_HUS_NOT_ENOUGH_SLOTS,
+ STATE_CHANGE_REASON_CODE_ERROR_HUS_CFP_PHASE_TOO_SHORT,
+ STATE_CHANGE_REASON_CODE_ERROR_HUS_CAP_PHASE_TOO_SHORT,
+ STATE_CHANGE_REASON_CODE_ERROR_HUS_OTHERS,
})
public @interface StateChangeReasonCode {}
@@ -640,6 +647,10 @@ public abstract class FiraParams extends Params {
public static final int STATE_CHANGE_REASON_CODE_ERROR_INVALID_RANGING_INTERVAL = 0x23;
public static final int STATE_CHANGE_REASON_CODE_ERROR_INVALID_STS_CONFIG = 0x24;
public static final int STATE_CHANGE_REASON_CODE_ERROR_INVALID_RFRAME_CONFIG = 0x25;
+ public static final int STATE_CHANGE_REASON_CODE_ERROR_HUS_NOT_ENOUGH_SLOTS = 0x26;
+ public static final int STATE_CHANGE_REASON_CODE_ERROR_HUS_CFP_PHASE_TOO_SHORT = 0x27;
+ public static final int STATE_CHANGE_REASON_CODE_ERROR_HUS_CAP_PHASE_TOO_SHORT = 0x28;
+ public static final int STATE_CHANGE_REASON_CODE_ERROR_HUS_OTHERS = 0x29;
/** Multicast controlee add/delete actions defined in UCI */
@IntDef(
diff --git a/service/tests/src/com/android/server/uwb/UwbServiceCoreTest.java b/service/tests/src/com/android/server/uwb/UwbServiceCoreTest.java
index 32c0cca6..66d1f054 100644
--- a/service/tests/src/com/android/server/uwb/UwbServiceCoreTest.java
+++ b/service/tests/src/com/android/server/uwb/UwbServiceCoreTest.java
@@ -482,11 +482,33 @@ public class UwbServiceCoreTest {
mUwbServiceCore.setEnabled(true);
mTestLooper.dispatchAll();
+ // Verify that UWB adapter state is notified as DISABLED, and future calls to
+ // getAdapterState() also return the state as DISABLED.
verify(mNativeUwbManager).doInitialize();
verify(mUwbCountryCode).setCountryCode(true);
verify(cb).onAdapterStateChanged(UwbManager.AdapterStateCallback.STATE_DISABLED,
StateChangeReason.SYSTEM_REGULATION);
assertThat(mUwbServiceCore.getAdapterState()).isEqualTo(AdapterState.STATE_DISABLED);
+
+ // Verify that a UWB ranging session cannot be opened or started.
+ SessionHandle sessionHandle = mock(SessionHandle.class);
+ IUwbRangingCallbacks rangingCallbacks = mock(IUwbRangingCallbacks.class);
+
+ try {
+ mUwbServiceCore.openRanging(
+ TEST_ATTRIBUTION_SOURCE, sessionHandle, rangingCallbacks,
+ TEST_FIRA_OPEN_SESSION_PARAMS.build().toBundle(), TEST_DEFAULT_CHIP_ID);
+ fail();
+ } catch (IllegalStateException e) {
+ // pass
+ }
+
+ try {
+ mUwbServiceCore.startRanging(sessionHandle, new PersistableBundle());
+ fail();
+ } catch (IllegalStateException e) {
+ // pass
+ }
}
// Unit test for scenario when setting the country code (during UWB Enable) fails with a generic
@@ -512,11 +534,33 @@ public class UwbServiceCoreTest {
mUwbServiceCore.setEnabled(true);
mTestLooper.dispatchAll();
+ // Verify that UWB adapter state is notified as DISABLED, and future calls to
+ // getAdapterState() also return the state as DISABLED.
verify(mNativeUwbManager).doInitialize();
verify(mUwbCountryCode).setCountryCode(true);
verify(cb).onAdapterStateChanged(UwbManager.AdapterStateCallback.STATE_DISABLED,
StateChangeReason.SYSTEM_POLICY);
assertThat(mUwbServiceCore.getAdapterState()).isEqualTo(AdapterState.STATE_DISABLED);
+
+ // Verify that a UWB ranging session cannot be opened or started.
+ SessionHandle sessionHandle = mock(SessionHandle.class);
+ IUwbRangingCallbacks rangingCallbacks = mock(IUwbRangingCallbacks.class);
+
+ try {
+ mUwbServiceCore.openRanging(
+ TEST_ATTRIBUTION_SOURCE, sessionHandle, rangingCallbacks,
+ TEST_FIRA_OPEN_SESSION_PARAMS.build().toBundle(), TEST_DEFAULT_CHIP_ID);
+ fail();
+ } catch (IllegalStateException e) {
+ // pass
+ }
+
+ try {
+ mUwbServiceCore.startRanging(sessionHandle, new PersistableBundle());
+ fail();
+ } catch (IllegalStateException e) {
+ // pass
+ }
}
@Test
@@ -542,7 +586,7 @@ public class UwbServiceCoreTest {
// Enable again. should be ignored.
enableUwb(VALID_COUNTRY_CODE);
- verifyNoMoreInteractions(mNativeUwbManager, mUwbCountryCode, cb);
+ verifyNoMoreInteractions(mNativeUwbManager, cb);
assertThat(mUwbServiceCore.getAdapterState())
.isEqualTo(AdapterState.STATE_ENABLED_INACTIVE);
}
@@ -805,7 +849,7 @@ public class UwbServiceCoreTest {
// Disable again. should be ignored.
disableUwb();
- verifyNoMoreInteractions(mNativeUwbManager, mUwbCountryCode, cb);
+ verifyNoMoreInteractions(mNativeUwbManager, cb);
assertThat(mUwbServiceCore.getAdapterState()).isEqualTo(AdapterState.STATE_DISABLED);
}
@@ -1553,6 +1597,17 @@ public class UwbServiceCoreTest {
verify(mUwbSessionManager).rangingRoundsUpdateDtTag(sessionHandle, bundle);
}
+ @Test
+ public void testHybridSessionConfiguration() throws Exception {
+ enableUwbWithCountryCodeChangedCallback();
+
+ SessionHandle sessionHandle = mock(SessionHandle.class);
+ PersistableBundle bundle = new PersistableBundle();
+ mUwbServiceCore.setHybridSessionConfiguration(sessionHandle, bundle);
+
+ verify(mUwbSessionManager).setHybridSessionConfiguration(sessionHandle, bundle);
+ }
+
public CccSpecificationParams getTestCccSpecificationParams() {
CccProtocolVersion[] protocolVersions =
new CccProtocolVersion[] {
diff --git a/service/tests/src/com/android/server/uwb/UwbServiceImplTest.java b/service/tests/src/com/android/server/uwb/UwbServiceImplTest.java
index ada41ee2..7eb8d289 100644
--- a/service/tests/src/com/android/server/uwb/UwbServiceImplTest.java
+++ b/service/tests/src/com/android/server/uwb/UwbServiceImplTest.java
@@ -24,6 +24,7 @@ import static com.android.server.uwb.UwbServiceImpl.SETTINGS_SATELLITE_MODE_ENAB
import static com.android.server.uwb.UwbServiceImpl.SETTINGS_SATELLITE_MODE_RADIOS;
import static com.android.server.uwb.UwbSettingsStore.SETTINGS_TOGGLE_STATE;
import static com.android.server.uwb.UwbTestUtils.MAX_DATA_SIZE;
+import static com.android.server.uwb.UwbTestUtils.TEST_STATUS;
import static com.google.common.truth.Truth.assertThat;
import static com.google.uwb.support.fira.FiraParams.PACS_PROFILE_SERVICE_ID;
@@ -819,6 +820,20 @@ public class UwbServiceImplTest {
}
@Test
+ public void testSetHybridSessionConfiguration() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
+ final SessionHandle sessionHandle = mock(SessionHandle.class);
+ final PersistableBundle parameters = new PersistableBundle();
+
+ when(mUwbServiceCore.setHybridSessionConfiguration(sessionHandle, parameters))
+ .thenReturn(TEST_STATUS);
+ assertThat(mUwbServiceImpl.setHybridSessionConfiguration(sessionHandle, parameters))
+ .isEqualTo(TEST_STATUS);
+
+ verify(mUwbServiceCore).setHybridSessionConfiguration(sessionHandle, parameters);
+ }
+
+ @Test
public void testGetUwbActivityEnergyInfoAsync() throws Exception {
final IOnUwbActivityEnergyInfoListener listener = mock(
IOnUwbActivityEnergyInfoListener.class);
diff --git a/service/tests/src/com/android/server/uwb/UwbSessionManagerTest.java b/service/tests/src/com/android/server/uwb/UwbSessionManagerTest.java
index 35fc8807..7fe21ecd 100644
--- a/service/tests/src/com/android/server/uwb/UwbSessionManagerTest.java
+++ b/service/tests/src/com/android/server/uwb/UwbSessionManagerTest.java
@@ -122,6 +122,7 @@ import com.google.uwb.support.ccc.CccPulseShapeCombo;
import com.google.uwb.support.ccc.CccSpecificationParams;
import com.google.uwb.support.ccc.CccStartRangingParams;
import com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdate;
+import com.google.uwb.support.fira.FiraHybridSessionConfig;
import com.google.uwb.support.fira.FiraOpenSessionParams;
import com.google.uwb.support.fira.FiraParams;
import com.google.uwb.support.fira.FiraProtocolVersion;
@@ -139,6 +140,8 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
@@ -177,7 +180,20 @@ public class UwbSessionManagerTest {
private static final short DATA_SEQUENCE_NUM_1 = 2;
private static final int DATA_TRANSMISSION_COUNT = 1;
private static final int DATA_TRANSMISSION_COUNT_3 = 3;
-
+ private static final FiraProtocolVersion FIRA_VERSION_1_0 = new FiraProtocolVersion(1, 0);
+ private static final FiraProtocolVersion FIRA_VERSION_1_1 = new FiraProtocolVersion(1, 1);
+ private static final FiraProtocolVersion FIRA_VERSION_2_0 = new FiraProtocolVersion(2, 0);
+ private static final FiraSpecificationParams FIRA_SPECIFICATION_PARAMS =
+ new FiraSpecificationParams.Builder()
+ .setMinPhyVersionSupported(FIRA_VERSION_1_0)
+ .setMaxPhyVersionSupported(FIRA_VERSION_1_1)
+ .setSupportedChannels(List.of(9))
+ .setRangeDataNtfConfigCapabilities(
+ EnumSet.of(
+ HAS_RANGE_DATA_NTF_CONFIG_DISABLE,
+ HAS_RANGE_DATA_NTF_CONFIG_ENABLE))
+ .build();
+ private static final long UWBS_TIMESTAMP = 2000000L;
private static final int HANDLE_ID = 12;
private static final int MAX_RX_DATA_PACKETS_TO_STORE = 10;
private static final int PID = Process.myPid();
@@ -229,14 +245,7 @@ public class UwbSessionManagerTest {
}).when(mUwbInjector).runTaskOnSingleThreadExecutor(any(FutureTask.class), anyInt());
mSpecificationParamsBuilder = new GenericSpecificationParams.Builder()
.setCccSpecificationParams(mCccSpecificationParams)
- .setFiraSpecificationParams(
- new FiraSpecificationParams.Builder()
- .setSupportedChannels(List.of(9))
- .setRangeDataNtfConfigCapabilities(
- EnumSet.of(
- HAS_RANGE_DATA_NTF_CONFIG_DISABLE,
- HAS_RANGE_DATA_NTF_CONFIG_ENABLE))
- .build());
+ .setFiraSpecificationParams(FIRA_SPECIFICATION_PARAMS);
when(mUwbServiceCore.getCachedSpecificationParams(any())).thenReturn(
mSpecificationParamsBuilder.build());
when(mCccSpecificationParams.getMaxRangingSessionNumber()).thenReturn(
@@ -1482,10 +1491,26 @@ public class UwbSessionManagerTest {
}
private Params setupFiraParams() {
- return setupFiraParams(FiraParams.RANGING_DEVICE_ROLE_INITIATOR, Optional.empty());
+ return setupFiraParams(FIRA_VERSION_2_0);
+ }
+
+ private Params setupFiraParams(FiraProtocolVersion firaProtocolVersion) {
+ return setupFiraParams(
+ FiraParams.RANGING_DEVICE_ROLE_INITIATOR,
+ /* rangingRoundusageOptional = */ Optional.empty(),
+ firaProtocolVersion);
+ }
+
+ private Params setupFiraParams(
+ int deviceRole,
+ Optional<Integer> rangingRoundUsageOptional) {
+ return setupFiraParams(deviceRole, rangingRoundUsageOptional, FIRA_VERSION_1_0);
}
- private Params setupFiraParams(int deviceRole, Optional<Integer> rangingRoundUsageOptional) {
+ private Params setupFiraParams(
+ int deviceRole,
+ Optional<Integer> rangingRoundUsageOptional,
+ FiraProtocolVersion firaProtocolVersion) {
FiraOpenSessionParams.Builder paramsBuilder = new FiraOpenSessionParams.Builder()
.setDeviceAddress(UwbAddress.fromBytes(new byte[] {(byte) 0x01, (byte) 0x02 }))
.setVendorId(new byte[] { (byte) 0x00, (byte) 0x01 })
@@ -1493,7 +1518,7 @@ public class UwbSessionManagerTest {
(byte) 0x04, (byte) 0x05, (byte) 0x06 })
.setDestAddressList(Arrays.asList(
UWB_DEST_ADDRESS))
- .setProtocolVersion(new FiraProtocolVersion(1, 0))
+ .setProtocolVersion(firaProtocolVersion)
.setSessionId(10)
.setSessionType(SESSION_TYPE_RANGING)
.setDeviceType(FiraParams.RANGING_DEVICE_TYPE_CONTROLLER)
@@ -2108,6 +2133,47 @@ public class UwbSessionManagerTest {
}
@Test
+ public void execStartRanging_absoluteInitiationTime() throws Exception {
+ // Setup the UWBS to return Fira version as 2.0.
+ FiraSpecificationParams firaSpecificationParams20 = new FiraSpecificationParams.Builder()
+ .setMinPhyVersionSupported(FIRA_VERSION_2_0)
+ .setMaxPhyVersionSupported(FIRA_VERSION_2_0)
+ .setSupportedChannels(FIRA_SPECIFICATION_PARAMS.getSupportedChannels())
+ .setRangeDataNtfConfigCapabilities(
+ FIRA_SPECIFICATION_PARAMS.getRangeDataNtfConfigCapabilities())
+ .build();
+ GenericSpecificationParams specificationParams = new GenericSpecificationParams.Builder()
+ .setCccSpecificationParams(mCccSpecificationParams)
+ .setFiraSpecificationParams(firaSpecificationParams20)
+ .build();
+ when(mUwbServiceCore.getCachedSpecificationParams(any())).thenReturn(specificationParams);
+
+ Params params = setupFiraParams(new FiraProtocolVersion(2, 0));
+ UwbSession uwbSession = prepareExistingUwbSession(params);
+
+ // Setup for start ranging.
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_IDLE, UwbUciConstants.UWB_SESSION_STATE_ACTIVE)
+ .when(uwbSession).getSessionState();
+ when(mNativeUwbManager.startRanging(eq(TEST_SESSION_ID), anyString()))
+ .thenReturn((byte) UwbUciConstants.STATUS_CODE_OK);
+ when(mUwbServiceCore.queryUwbsTimestampMicros()).thenReturn(UWBS_TIMESTAMP);
+ when(mUwbConfigurationManager.setAppConfigurations(anyInt(), any(), anyString()))
+ .thenReturn(UwbUciConstants.STATUS_CODE_OK);
+
+ mUwbSessionManager.startRanging(uwbSession.getSessionHandle(), params);
+ mTestLooper.dispatchAll();
+
+ // Verify that queryUwbsTimestampMicros() is called. Currently unable to verify that the
+ // FiraOpenSessionParams is changed and the absoluteInitiationTime field set in it, as
+ // equals() is not implemented.
+ verify(mUwbServiceCore).queryUwbsTimestampMicros();
+ verify(mUwbConfigurationManager).setAppConfigurations(anyInt(), any(), any());
+ verify(mUwbSessionNotificationManager).onRangingStarted(eq(uwbSession), any());
+ verify(mUwbMetrics).longRangingStartEvent(
+ eq(uwbSession), eq(UwbUciConstants.STATUS_CODE_OK));
+ }
+
+ @Test
public void execStartRanging_onRangeDataNotification() throws Exception {
UwbSession uwbSession = prepareExistingUwbSession();
// set up for start ranging
@@ -3275,6 +3341,56 @@ public class UwbSessionManagerTest {
}
@Test
+ public void testSetHybridSessionConfiguration() throws Exception {
+ UwbSession uwbSession = prepareExistingUwbSession();
+ FiraHybridSessionConfig.Builder mockFiraBuilder =
+ mock(FiraHybridSessionConfig.Builder.class);
+
+ SessionHandle sessionHandle = mock(SessionHandle.class);
+ SessionHandle sessionHandle1 = mock(SessionHandle.class);
+ SessionHandle sessionHandle2 = mock(SessionHandle.class);
+ byte[] updateTime = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ int noOfPhases = 2;
+ short startSlotIndex1 = 0x01, endSlotIndex1 = 0x34;
+ short startSlotIndex2 = 0x37, endSlotIndex2 = 0x64;
+ FiraHybridSessionConfig params = new FiraHybridSessionConfig.Builder()
+ .setNumberOfPhases(noOfPhases)
+ .setUpdateTime(updateTime)
+ .addPhaseList(new FiraHybridSessionConfig.FiraHybridSessionPhaseList(
+ sessionHandle1.getId(), startSlotIndex1, endSlotIndex1))
+ .addPhaseList(new FiraHybridSessionConfig.FiraHybridSessionPhaseList(
+ sessionHandle2.getId(), startSlotIndex2, endSlotIndex2))
+ .build();
+
+ // Setup the expected byte-array for the Hybrid configuration.
+ ByteBuffer expectedHybridConfigBytes = ByteBuffer.allocate(noOfPhases * 8);
+ expectedHybridConfigBytes.order(ByteOrder.LITTLE_ENDIAN);
+
+ expectedHybridConfigBytes.putInt(sessionHandle1.getId());
+ expectedHybridConfigBytes.putShort(startSlotIndex1);
+ expectedHybridConfigBytes.putShort(endSlotIndex1);
+ expectedHybridConfigBytes.putInt(sessionHandle2.getId());
+ expectedHybridConfigBytes.putShort(startSlotIndex2);
+ expectedHybridConfigBytes.putShort(endSlotIndex2);
+
+ when(mNativeUwbManager.setHybridSessionConfiguration(
+ eq(uwbSession.getSessionId()), eq(noOfPhases), eq(updateTime),
+ eq(expectedHybridConfigBytes.array()), anyString()))
+ .thenReturn((byte) UwbUciConstants.STATUS_CODE_OK);
+ assertThat(mUwbSessionManager.setHybridSessionConfiguration(uwbSession.getSessionHandle(),
+ params.toBundle()))
+ .isEqualTo(UwbUciConstants.STATUS_CODE_OK);
+ }
+
+ @Test
+ public void testSetHybridSessionConfiguration_whenUwbSessionDoesNotExist() throws Exception {
+ SessionHandle mockSessionHandle = mock(SessionHandle.class);
+ assertThrows(IllegalStateException.class,
+ () -> mUwbSessionManager.setHybridSessionConfiguration(mockSessionHandle,
+ mock(PersistableBundle.class)));
+ }
+
+ @Test
public void deInitSession_notExistedSession() {
doReturn(false).when(mUwbSessionManager).isExistedSession(any());
diff --git a/service/tests/src/com/android/server/uwb/UwbSessionNotificationManagerTest.java b/service/tests/src/com/android/server/uwb/UwbSessionNotificationManagerTest.java
index d89566e7..dabb74bb 100644
--- a/service/tests/src/com/android/server/uwb/UwbSessionNotificationManagerTest.java
+++ b/service/tests/src/com/android/server/uwb/UwbSessionNotificationManagerTest.java
@@ -460,7 +460,11 @@ public class UwbSessionNotificationManagerTest {
UwbUciConstants.REASON_ERROR_MAC_ADDRESS_MODE_NOT_SUPPORTED,
UwbUciConstants.REASON_ERROR_INVALID_RANGING_INTERVAL,
UwbUciConstants.REASON_ERROR_INVALID_STS_CONFIG,
- UwbUciConstants.REASON_ERROR_INVALID_RFRAME_CONFIG);
+ UwbUciConstants.REASON_ERROR_INVALID_RFRAME_CONFIG,
+ UwbUciConstants.REASON_ERROR_HUS_NOT_ENOUGH_SLOTS,
+ UwbUciConstants.REASON_ERROR_HUS_CFP_PHASE_TOO_SHORT,
+ UwbUciConstants.REASON_ERROR_HUS_CAP_PHASE_TOO_SHORT,
+ UwbUciConstants.REASON_ERROR_HUS_OTHERS);
for (int reasonCode : reasonCodes) {
clearInvocations(mIUwbRangingCallbacks);
mUwbSessionNotificationManager.onRangingStoppedWithUciReasonCode(mUwbSession,
diff --git a/service/tests/src/com/android/server/uwb/params/FiraEncoderTest.java b/service/tests/src/com/android/server/uwb/params/FiraEncoderTest.java
index ea599d3f..e3295321 100644
--- a/service/tests/src/com/android/server/uwb/params/FiraEncoderTest.java
+++ b/service/tests/src/com/android/server/uwb/params/FiraEncoderTest.java
@@ -97,6 +97,11 @@ public class FiraEncoderTest {
.setLinkLayerMode(1)
.setApplicationDataEndpoint(1);
+ private static final FiraOpenSessionParams.Builder
+ TEST_FIRA_OPEN_SESSION_PARAMS_V_2_0_ABSOLUTE_INITIATION_TIME =
+ new FiraOpenSessionParams.Builder(TEST_FIRA_OPEN_SESSION_PARAMS_V_2_0)
+ .setAbsoluteInitiationTime(20000000L);
+
private static final FiraRangingReconfigureParams.Builder TEST_FIRA_RECONFIGURE_PARAMS =
new FiraRangingReconfigureParams.Builder()
.setBlockStrideLength(6)
@@ -189,6 +194,7 @@ public class FiraEncoderTest {
private static final String DST_MAC_ADDRESS_TLV = "07020406";
private static final String UWB_INITIATION_TIME_TLV = "2B0400000000";
private static final String UWB_INITIATION_TIME_2_0_TLV = "2B08E803000000000000";
+ private static final String ABSOLUTE_UWB_INITIATION_TIME_2_0_TLV = "2B08002D310100000000";
private static final String VENDOR_ID_TLV = "27020578";
private static final String STATIC_STS_IV_TLV = "28061A5577477E7D";
private static final String RANGE_DATA_NTF_AOA_BOUND_TLV = "1D0807D59E4707D56022";
@@ -203,6 +209,7 @@ public class FiraEncoderTest {
private byte[] mFiraOpenSessionTlvUtTag;
private byte[] mFiraSessionv11TlvData;
private byte[] mFiraSessionv20TlvData;
+ private byte[] mFiraSessionv20AbsoluteInitiationTimeTlvData;
@Before
public void setUp() {
@@ -218,7 +225,7 @@ public class FiraEncoderTest {
"01010002010003010004010906020604080260090B01000C01030D01010E01010F02"
+ "00001002204E11010412010313010014010A1501021601001701011A01011B01191C"
+ "01001F01002201012301002401002501322601002901012A0200002C01002D01002E"
- + "01012F010131010032020000350101000101050101070206042B0400000000270278"
+ + "01012F0101310100320200003501010001012B0400000000270278"
+ "0528061A5577477E7D3304B004000034041E0000003803010B0A390101");
} else {
mFiraSessionv11TlvData = UwbUtil.getByteArray(RANGING_ROUND_USAGE_SS_TWR_TLV
@@ -241,7 +248,8 @@ public class FiraEncoderTest {
+ UWB_INITIATION_TIME_TLV + VENDOR_ID_TLV + STATIC_STS_IV_TLV
+ RANGE_DATA_NTF_AOA_BOUND_TLV);
- mFiraSessionv20TlvData = UwbUtil.getByteArray(RANGING_ROUND_USAGE_SS_TWR_TLV
+ mFiraSessionv20TlvData = UwbUtil.getByteArray(
+ RANGING_ROUND_USAGE_SS_TWR_TLV
+ STS_CONFIG_STATIC_TLV + MULTI_NODE_MODE_UNICAST_TLV + CHANNEL_NUMBER_TLV
+ DEVICE_MAC_ADDRESS_TLV + SLOT_DURATION_TLV + MAC_FCS_TYPE_TLV
+ RANGING_ROUND_CONTROL_TLV + AOA_RESULT_REQ_TLV
@@ -266,6 +274,32 @@ public class FiraEncoderTest {
+ VENDOR_ID_TLV + STATIC_STS_IV_TLV
+ RANGE_DATA_NTF_AOA_BOUND_TLV);
+ mFiraSessionv20AbsoluteInitiationTimeTlvData = UwbUtil.getByteArray(
+ RANGING_ROUND_USAGE_SS_TWR_TLV
+ + STS_CONFIG_STATIC_TLV + MULTI_NODE_MODE_UNICAST_TLV + CHANNEL_NUMBER_TLV
+ + DEVICE_MAC_ADDRESS_TLV + SLOT_DURATION_TLV + MAC_FCS_TYPE_TLV
+ + RANGING_ROUND_CONTROL_TLV + AOA_RESULT_REQ_TLV
+ + RANGE_DATA_NTF_CONFIG_AOA_LEVEL_TLV + RANGE_DATA_NTF_PROXIMITY_NEAR_TLV
+ + RANGE_DATA_NTF_PROXIMITY_FAR_TLV + DEVICE_ROLE_RESPONDER_TLV
+ + RFRAME_CONFIG_TLV
+ + RSSI_REPORTING_TLV + PREAMBLE_CODE_INDEX_TLV + SFD_ID_TLV
+ + PSDU_DATA_RATE_TLV + PREAMBLE_DURATION_TLV + RANGING_TIME_STRUCT_TLV
+ + SLOTS_PER_RR_TLV + TX_ADAPTIVE_PAYLOAD_POWER_TLV
+ + PRF_MODE_TLV + SCHEDULED_MODE_TIME_SCHEDULED_TLV + KEY_ROTATION_TLV
+ + KEY_ROTATION_RATE_TLV
+ + SESSION_PRIORITY_TLV + MAC_ADDRESS_MODE_TLV + NUMBER_OF_STS_SEGMENTS_TLV
+ + MAX_RR_RETRY_TLV + HOPPING_MODE_TLV + BLOCK_STRIDE_LENGTH_TLV
+ + RESULT_REPORT_CONFIG_TLV + IN_BAND_TERMINATION_ATTEMPT_COUNT_TLV
+ + BPRF_PHR_DATA_RATE_TLV + MAX_NUMBER_OF_MEASUREMENTS_TLV + STS_LENGTH_TLV
+ + RANGING_INTERVAL_TLV + DEVICE_TYPE_CONTROLLER_TLV + NUMBER_OF_CONTROLEES_TLV
+ + DST_MAC_ADDRESS_TLV + ABSOLUTE_UWB_INITIATION_TIME_2_0_TLV
+ + LINK_LAYER_MODE_CONNECTIONLESS_DATA_TLV
+ + DATA_TRANSFER_STATUS_NTF_CONFIG
+ + SESSION_DATA_TRANSFER_STATUS_NTF_CONFIG
+ + APPLICATION_DATA_ENDPOINT_SECURE_COMPONENT_TLV
+ + VENDOR_ID_TLV + STATIC_STS_IV_TLV
+ + RANGE_DATA_NTF_AOA_BOUND_TLV);
+
mFiraOpenSessionTlvUtTag = UwbUtil.getByteArray(
RANGING_ROUND_USAGE_UL_TDOA_TLV
+ STS_CONFIG_STATIC_TLV + MULTI_NODE_MODE_UNICAST_TLV + CHANNEL_NUMBER_TLV
@@ -283,7 +317,7 @@ public class FiraEncoderTest {
+ MAX_RR_RETRY_TLV + HOPPING_MODE_TLV + BLOCK_STRIDE_LENGTH_TLV
+ RESULT_REPORT_CONFIG_TLV + IN_BAND_TERMINATION_ATTEMPT_COUNT_TLV
+ BPRF_PHR_DATA_RATE_TLV + MAX_NUMBER_OF_MEASUREMENTS_TLV + STS_LENGTH_TLV
- + DEVICE_TYPE_CONTROLLER_TLV + NUMBER_OF_CONTROLEES_TLV + DST_MAC_ADDRESS_TLV
+ + DEVICE_TYPE_CONTROLLER_TLV
+ UWB_INITIATION_TIME_TLV + VENDOR_ID_TLV + STATIC_STS_IV_TLV
+ UL_TDOA_TX_INTERVAL_TLV + UL_TDOA_RANDOM_WINDOW_TLV + UL_TDOA_DEVICE_ID_TLV
+ UL_TDOA_TX_TIMESTAMP_TLV);
@@ -302,11 +336,20 @@ public class FiraEncoderTest {
// Test FiRa v2.0 Params
if (SdkLevel.isAtLeastU()) {
+ // Test the default Fira v2.0 OpenSessionParams.
params = TEST_FIRA_OPEN_SESSION_PARAMS_V_2_0.build();
tlvs = mFiraEncoder.getTlvBuffer(params);
assertThat(tlvs.getNoOfParams()).isEqualTo(49);
assertThat(tlvs.getByteArray()).isEqualTo(mFiraSessionv20TlvData);
+
+ // Test the Fira v2.0 OpenSessionParams with ABSOLUTE_INITIATION_TIME set.
+ params = TEST_FIRA_OPEN_SESSION_PARAMS_V_2_0_ABSOLUTE_INITIATION_TIME.build();
+ tlvs = mFiraEncoder.getTlvBuffer(params);
+
+ assertThat(tlvs.getNoOfParams()).isEqualTo(49);
+ assertThat(tlvs.getByteArray()).isEqualTo(mFiraSessionv20AbsoluteInitiationTimeTlvData);
+
}
}
@@ -344,7 +387,7 @@ public class FiraEncoderTest {
FiraOpenSessionParams params = TEST_FIRA_UT_TAG_OPEN_SESSION_PARAM.build();
TlvBuffer tlvs = mFiraEncoder.getTlvBuffer(params);
- assertThat(tlvs.getNoOfParams()).isEqualTo(47);
+ assertThat(tlvs.getNoOfParams()).isEqualTo(45);
assertThat(tlvs.getByteArray()).isEqualTo(mFiraOpenSessionTlvUtTag);
}
@@ -421,7 +464,7 @@ public class FiraEncoderTest {
.setDeviceRole(RANGING_DEVICE_DT_TAG)
.setDeviceAddress(UwbAddress.fromBytes(new byte[]{0x4, 0x6}))
.setDestAddressList(Arrays.asList(UwbAddress.fromBytes(
- new byte[]{0x4, 0x6})))
+ new byte[]{0x4, 0x6}))) // Should be ignored
.setMultiNodeMode(MULTI_NODE_MODE_ONE_TO_MANY)
.setRangingRoundUsage(RANGING_ROUND_USAGE_DL_TDOA)
.setRframeConfig(RFRAME_CONFIG_SP1)
@@ -463,6 +506,7 @@ public class FiraEncoderTest {
.setDeviceType(RANGING_DEVICE_TYPE_CONTROLLER)
.setDeviceRole(RANGING_DEVICE_ROLE_INITIATOR)
.setDeviceAddress(UwbAddress.fromBytes(new byte[]{0x4, 0x6}))
+ // DestAddressList should be ignored and not set in the TLV params.
.setDestAddressList(Arrays.asList(UwbAddress.fromBytes(new byte[]{0x4, 0x6})))
.setMultiNodeMode(MULTI_NODE_MODE_ONE_TO_MANY)
.setRangingRoundUsage(RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE)
@@ -519,8 +563,6 @@ public class FiraEncoderTest {
+ STS_LENGTH_TLV
+ RANGING_INTERVAL_TLV
+ DEVICE_TYPE_CONTROLLER_TLV
- + NUMBER_OF_CONTROLEES_TLV
- + DST_MAC_ADDRESS_TLV
+ UWB_INITIATION_TIME_2_0_TLV
+ LINK_LAYER_MODE_BYPASS_TLV
+ DATA_TRANSFER_STATUS_NTF_CONFIG
@@ -532,7 +574,7 @@ public class FiraEncoderTest {
+ CAP_SIZE_RANGE_DEFAULT_TLV);
TlvBuffer tlvs = mFiraEncoder.getTlvBuffer(params);
- assertThat(tlvs.getNoOfParams()).isEqualTo(50);
+ assertThat(tlvs.getNoOfParams()).isEqualTo(48);
assertThat(tlvs.getByteArray()).isEqualTo(expected_data);
}
}
diff --git a/service/uci/jni/src/uci_jni_android_new.rs b/service/uci/jni/src/uci_jni_android_new.rs
index 2d42c142..fd5bf32d 100644
--- a/service/uci/jni/src/uci_jni_android_new.rs
+++ b/service/uci/jni/src/uci_jni_android_new.rs
@@ -35,8 +35,8 @@ use jni::JNIEnv;
use log::{debug, error};
use uwb_core::error::{Error, Result};
use uwb_core::params::{
- AppConfigTlv, CountryCode, RawAppConfigTlv, RawUciMessage,
- SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse,
+ AppConfigTlv, CountryCode, PhaseList, RawAppConfigTlv, RawUciMessage,
+ SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, UpdateTime,
};
use uwb_uci_packets::{
AppConfigTlvType, CapTlv, Controlee, Controlee_V2_0_16_Byte_Version,
@@ -385,6 +385,82 @@ fn native_set_app_configurations(
uci_manager.session_set_app_config(session_id as u32, tlvs)
}
+fn parse_hybrid_config_phase_list_vec(
+ number_of_phases: usize,
+ byte_array: &[u8],
+) -> Result<Vec<PhaseList>> {
+ let mut parsed_phase_lists_len = 0;
+ let received_phase_list_len = byte_array.len();
+ let mut phase_lists = Vec::with_capacity(number_of_phases);
+ // The PhaseList consists of session handle as u32 in 4 bytes, Start Slot Index as u16
+ // in 2 byte and End Slot Index as u16 in 2 bytes
+ const PHASE_LIST_SIZE: usize = 8;
+ for chunk in byte_array.chunks_exact(PHASE_LIST_SIZE) {
+ let phase_list = PhaseList::parse(chunk).map_err(|_| Error::BadParameters)?;
+ parsed_phase_lists_len += PHASE_LIST_SIZE;
+ phase_lists.push(phase_list);
+ }
+
+ if parsed_phase_lists_len != received_phase_list_len {
+ return Err(Error::BadParameters);
+ }
+ Ok(phase_lists)
+}
+
+/// Set hybrid session configurations. Return null JObject if failed.
+#[no_mangle]
+pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetHybridSessionConfigurations(
+ env: JNIEnv,
+ obj: JObject,
+ session_id: jint,
+ number_of_phases: jint,
+ update_time: jbyteArray,
+ phase_list: jbyteArray,
+ chip_id: JString,
+) -> jbyte {
+ debug!("{}: enter", function_name!());
+ byte_result_helper(
+ native_set_hybrid_session_configurations(
+ env,
+ obj,
+ session_id,
+ number_of_phases,
+ update_time,
+ phase_list,
+ chip_id,
+ ),
+ function_name!(),
+ )
+}
+
+fn native_set_hybrid_session_configurations(
+ env: JNIEnv,
+ obj: JObject,
+ session_id: jint,
+ number_of_phases: jint,
+ update_time: jbyteArray,
+ phase_list: jbyteArray,
+ chip_id: JString,
+) -> Result<()> {
+ let uci_manager = Dispatcher::get_uci_manager(env, obj, chip_id)?;
+ let phase_list_bytes =
+ env.convert_byte_array(phase_list).map_err(|_| Error::ForeignFunctionInterface)?;
+ let phase_list_vec =
+ parse_hybrid_config_phase_list_vec(number_of_phases as usize, &phase_list_bytes)?;
+
+ let update_time_bytes =
+ env.convert_byte_array(update_time).map_err(|_| Error::ForeignFunctionInterface)?;
+ let update_time_array: [u8; 8] =
+ TryFrom::try_from(&update_time_bytes[..]).map_err(|_| Error::BadParameters)?;
+
+ uci_manager.session_set_hybrid_config(
+ session_id as u32,
+ number_of_phases as u8,
+ UpdateTime::new(&update_time_array).unwrap(),
+ phase_list_vec,
+ )
+}
+
fn create_get_config_response(tlvs: Vec<AppConfigTlv>, env: JNIEnv) -> Result<jbyteArray> {
let tlv_data_class =
env.find_class(TLV_DATA_CLASS).map_err(|_| Error::ForeignFunctionInterface)?;