summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--androidx_backend/Android.bp3
-rw-r--r--androidx_backend/AndroidManifest.xml1
-rw-r--r--androidx_backend/src/androidx/core/uwb/backend/impl/UwbClient.java147
-rw-r--r--androidx_backend/src/androidx/core/uwb/backend/impl/UwbControleeClient.java82
-rw-r--r--androidx_backend/src/androidx/core/uwb/backend/impl/UwbControllerClient.java100
-rw-r--r--androidx_backend/src/androidx/core/uwb/backend/impl/UwbService.java22
-rw-r--r--androidx_backend/tests/src/androidx/core/uwb/backend/impl/UwbControleeClientTest.java77
-rw-r--r--androidx_backend/tests/src/androidx/core/uwb/backend/impl/UwbControllerClientTest.java114
-rw-r--r--apex/Android.bp1
-rw-r--r--framework/Android.bp26
-rw-r--r--framework/api/system-current.txt13
-rw-r--r--framework/java/android/uwb/IUwbAdapter.aidl7
-rw-r--r--framework/java/android/uwb/IUwbRangingCallbacks.aidl3
-rw-r--r--framework/java/android/uwb/RangingManager.java15
-rw-r--r--framework/java/android/uwb/RangingMeasurement.java18
-rw-r--r--framework/java/android/uwb/RangingReport.java18
-rw-r--r--framework/java/android/uwb/RangingSession.java49
-rw-r--r--framework/java/android/uwb/UwbManager.java3
-rw-r--r--framework/java/android/uwb/util/PersistableBundleUtils.java631
-rw-r--r--indev_uwb_adaptation/jni/src/api.rs4
-rw-r--r--service/Android.bp7
-rw-r--r--service/java/com/android/server/uwb/DeviceConfigFacade.java2
-rw-r--r--service/java/com/android/server/uwb/UwbConfigurationManager.java5
-rw-r--r--service/java/com/android/server/uwb/UwbMetrics.java45
-rw-r--r--service/java/com/android/server/uwb/UwbServiceCore.java8
-rw-r--r--service/java/com/android/server/uwb/UwbServiceImpl.java79
-rw-r--r--service/java/com/android/server/uwb/UwbSessionManager.java217
-rw-r--r--service/java/com/android/server/uwb/UwbSessionNotificationManager.java76
-rw-r--r--service/java/com/android/server/uwb/UwbShellCommand.java3
-rw-r--r--service/java/com/android/server/uwb/UwbTestUtils.java114
-rw-r--r--service/java/com/android/server/uwb/advertisement/UwbAdvertiseManager.java56
-rw-r--r--service/java/com/android/server/uwb/config/CapabilityParam.java2
-rw-r--r--service/java/com/android/server/uwb/data/DtTagUpdateRangingRoundsStatus.java53
-rw-r--r--service/java/com/android/server/uwb/data/UwbDlTDoAMeasurement.java189
-rw-r--r--service/java/com/android/server/uwb/data/UwbRangingData.java32
-rw-r--r--service/java/com/android/server/uwb/data/UwbTwoWayMeasurement.java28
-rw-r--r--service/java/com/android/server/uwb/data/UwbUciConstants.java27
-rw-r--r--service/java/com/android/server/uwb/jni/NativeUwbManager.java125
-rw-r--r--service/java/com/android/server/uwb/params/CccEncoder.java7
-rw-r--r--service/java/com/android/server/uwb/params/FiraDecoder.java8
-rw-r--r--service/java/com/android/server/uwb/params/TlvDecoderBuffer.java2
-rw-r--r--service/java/com/android/server/uwb/util/DataTypeConversionUtil.java32
-rw-r--r--service/support_lib/Android.bp38
-rw-r--r--service/support_lib/src/com/google/uwb/support/ccc/CccOpenRangingParams.java4
-rw-r--r--service/support_lib/src/com/google/uwb/support/ccc/CccSpecificationParams.java2
-rw-r--r--service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoAMeasurement.java325
-rw-r--r--service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoARangingRoundsUpdate.java146
-rw-r--r--service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoARangingRoundsUpdateStatus.java147
-rw-r--r--service/support_lib/src/com/google/uwb/support/fira/FiraControleeParams.java3
-rw-r--r--service/support_lib/src/com/google/uwb/support/fira/FiraOpenSessionParams.java4
-rw-r--r--service/support_lib/src/com/google/uwb/support/fira/FiraParams.java21
-rw-r--r--service/support_lib/src/com/google/uwb/support/fira/FiraRangingReconfigureParams.java2
-rw-r--r--service/support_lib/src/com/google/uwb/support/fira/FiraSpecificationParams.java24
-rw-r--r--service/support_lib/src/com/google/uwb/support/generic/GenericSpecificationParams.java3
-rw-r--r--service/support_lib/src/com/google/uwb/support/profile/UuidBundleWrapper.java2
-rw-r--r--service/support_lib/test/Android.bp1
-rw-r--r--service/support_lib/test/DlTDoATests.java127
-rw-r--r--service/tests/src/com/android/server/uwb/DeviceConfigFacadeTest.java2
-rw-r--r--service/tests/src/com/android/server/uwb/UwbMetricsTest.java3
-rw-r--r--service/tests/src/com/android/server/uwb/UwbServiceImplTest.java30
-rw-r--r--service/tests/src/com/android/server/uwb/UwbSessionManagerTest.java540
-rw-r--r--service/tests/src/com/android/server/uwb/UwbSessionNotificationManagerTest.java73
-rw-r--r--service/tests/src/com/android/server/uwb/advertisement/UwbAdvertiseManagerTest.java59
-rw-r--r--service/tests/src/com/android/server/uwb/data/UwbRangingDataTest.java57
-rw-r--r--service/tests/src/com/android/server/uwb/params/CccEncoderTest.java4
-rw-r--r--service/tests/src/com/android/server/uwb/params/FiraDecoderTest.java5
-rw-r--r--service/tests/src/com/android/server/uwb/util/DataTypeConversionUtilTest.java50
-rw-r--r--[-rwxr-xr-x]service/uci/jni/Android.bp9
-rw-r--r--service/uci/jni/rust/lib.rs1563
-rw-r--r--service/uci/jni/rust/mock_context.rs192
-rw-r--r--service/uci/jni/rust/mock_dispatcher.rs101
-rw-r--r--service/uci/jni/src/dispatcher.rs (renamed from service/uci/jni_new/src/dispatcher.rs)81
-rw-r--r--service/uci/jni/src/error.rs (renamed from service/uci/jni_new/src/error.rs)0
-rw-r--r--service/uci/jni/src/helper.rs (renamed from service/uci/jni_new/src/helper.rs)0
-rw-r--r--service/uci/jni/src/jclass_name.rs (renamed from service/uci/jni_new/src/jclass_name.rs)2
-rw-r--r--service/uci/jni/src/lib.rs (renamed from service/uci/jni_new/src/lib.rs)0
-rw-r--r--service/uci/jni/src/notification_manager_android.rs (renamed from service/uci/jni_new/src/notification_manager_android.rs)2
-rw-r--r--service/uci/jni/src/uci_jni_android_new.rs (renamed from service/uci/jni_new/src/uci_jni_android_new.rs)159
-rw-r--r--service/uci/jni/src/unique_jvm.rs (renamed from service/uci/jni_new/src/unique_jvm.rs)0
-rw-r--r--service/uci/jni_new/Android.bp64
-rwxr-xr-xservice/uci/jni_unused/Android.bp3
-rwxr-xr-xservice/uci/jni_unused/UwbEventManager.cpp (renamed from service/uci/jni/UwbEventManager.cpp)0
-rwxr-xr-xservice/uci/jni_unused/UwbEventManager.h (renamed from service/uci/jni/UwbEventManager.h)0
-rwxr-xr-xservice/uci/jni_unused/UwbJniInternal.h (renamed from service/uci/jni/UwbJniInternal.h)0
-rwxr-xr-xservice/uci/jni_unused/UwbJniTypes.h (renamed from service/uci/jni/UwbJniTypes.h)0
-rwxr-xr-xservice/uci/jni_unused/UwbNativeManager.cpp (renamed from service/uci/jni/UwbNativeManager.cpp)0
-rwxr-xr-xservice/uci/jni_unused/rfTest/UwbRfTestManager.cpp (renamed from service/uci/jni/rfTest/UwbRfTestManager.cpp)0
-rwxr-xr-xservice/uci/jni_unused/rfTest/UwbRfTestManager.h (renamed from service/uci/jni/rfTest/UwbRfTestManager.h)0
-rwxr-xr-xservice/uci/jni_unused/rfTest/UwbRfTestNativeManager.cpp (renamed from service/uci/jni/rfTest/UwbRfTestNativeManager.cpp)0
-rwxr-xr-xservice/uci/jni_unused/utils/CondVar.cpp (renamed from service/uci/jni/utils/CondVar.cpp)0
-rwxr-xr-xservice/uci/jni_unused/utils/CondVar.h (renamed from service/uci/jni/utils/CondVar.h)0
-rwxr-xr-xservice/uci/jni_unused/utils/IntervalTimer.cpp (renamed from service/uci/jni/utils/IntervalTimer.cpp)0
-rwxr-xr-xservice/uci/jni_unused/utils/IntervalTimer.h (renamed from service/uci/jni/utils/IntervalTimer.h)0
-rwxr-xr-xservice/uci/jni_unused/utils/JniLog.h (renamed from service/uci/jni/utils/JniLog.h)0
-rwxr-xr-xservice/uci/jni_unused/utils/Mutex.cpp (renamed from service/uci/jni/utils/Mutex.cpp)0
-rwxr-xr-xservice/uci/jni_unused/utils/Mutex.h (renamed from service/uci/jni/utils/Mutex.h)0
-rwxr-xr-xservice/uci/jni_unused/utils/ScopedJniEnv.h (renamed from service/uci/jni/utils/ScopedJniEnv.h)0
-rwxr-xr-xservice/uci/jni_unused/utils/SyncEvent.cpp (renamed from service/uci/jni/utils/SyncEvent.cpp)0
-rwxr-xr-xservice/uci/jni_unused/utils/SyncEvent.h (renamed from service/uci/jni/utils/SyncEvent.h)0
-rwxr-xr-xservice/uci/jni_unused/utils/UwbJniUtil.cpp (renamed from service/uci/jni/utils/UwbJniUtil.cpp)0
-rwxr-xr-xservice/uci/jni_unused/utils/UwbJniUtil.h (renamed from service/uci/jni/utils/UwbJniUtil.h)0
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/Android.bp79
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/FiraRangingTests/AndroidTest.xml51
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/OWNERS7
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/UwbManagerTests/AndroidTest.xml51
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/lib/uwb_base_test.py80
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/lib/uwb_ranging_decorator.py209
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/lib/uwb_ranging_params.py218
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/ranging_test.py1117
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/snippet/Android.bp35
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/snippet/AndroidManifest.xml24
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/snippet/UwbManagerSnippet.java79
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/test_utils/uwb_test_utils.py155
-rw-r--r--tests/cts/hostsidetests/multidevices/uwb/uwb_manager_test.py152
-rw-r--r--tests/cts/tests/Android.bp43
-rw-r--r--tests/cts/tests/AndroidManifest.xml28
-rw-r--r--tests/cts/tests/AndroidTest.xml33
-rw-r--r--tests/cts/tests/OWNERS2
-rw-r--r--tests/cts/tests/src/android/uwb/cts/AngleMeasurementTest.java95
-rw-r--r--tests/cts/tests/src/android/uwb/cts/AngleOfArrivalMeasurementTest.java79
-rw-r--r--tests/cts/tests/src/android/uwb/cts/DistanceMeasurementTest.java88
-rw-r--r--tests/cts/tests/src/android/uwb/cts/RangingMeasurementTest.java134
-rw-r--r--tests/cts/tests/src/android/uwb/cts/RangingReportTest.java90
-rw-r--r--tests/cts/tests/src/android/uwb/cts/UwbAddressTest.java80
-rw-r--r--tests/cts/tests/src/android/uwb/cts/UwbFrameworkInitializerTest.java68
-rw-r--r--tests/cts/tests/src/android/uwb/cts/UwbManagerTest.java1175
-rw-r--r--tests/cts/tests/src/android/uwb/cts/UwbTestUtils.java124
127 files changed, 8255 insertions, 2340 deletions
diff --git a/androidx_backend/Android.bp b/androidx_backend/Android.bp
index 4c56514f..c0c77c7d 100644
--- a/androidx_backend/Android.bp
+++ b/androidx_backend/Android.bp
@@ -25,7 +25,8 @@ aidl_interface {
],
backend: {
java: {
- platform_apis: true,
+ enabled: true,
+ min_sdk_version: "31",
},
},
visibility: ["//visibility:public"],
diff --git a/androidx_backend/AndroidManifest.xml b/androidx_backend/AndroidManifest.xml
index 4e3180b3..0ef9b1fb 100644
--- a/androidx_backend/AndroidManifest.xml
+++ b/androidx_backend/AndroidManifest.xml
@@ -7,7 +7,6 @@
<uses-permission android:name="android.permission.UWB_RANGING"/>
<application
- android:name="androidx.core.uwb.backend.service"
android:persistent="true"
android:directBootAware="true"
android:defaultToDeviceProtectedStorage="true">
diff --git a/androidx_backend/src/androidx/core/uwb/backend/impl/UwbClient.java b/androidx_backend/src/androidx/core/uwb/backend/impl/UwbClient.java
new file mode 100644
index 00000000..3ec83a9a
--- /dev/null
+++ b/androidx_backend/src/androidx/core/uwb/backend/impl/UwbClient.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 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 androidx.core.uwb.backend.impl;
+
+import android.os.RemoteException;
+
+import androidx.core.uwb.backend.IRangingSessionCallback;
+import androidx.core.uwb.backend.IUwbClient;
+import androidx.core.uwb.backend.RangingCapabilities;
+import androidx.core.uwb.backend.RangingMeasurement;
+import androidx.core.uwb.backend.RangingParameters;
+import androidx.core.uwb.backend.UwbAddress;
+import androidx.core.uwb.backend.impl.internal.RangingDevice;
+import androidx.core.uwb.backend.impl.internal.RangingPosition;
+import androidx.core.uwb.backend.impl.internal.RangingSessionCallback;
+import androidx.core.uwb.backend.impl.internal.UwbDevice;
+import androidx.core.uwb.backend.impl.internal.UwbServiceImpl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Implements operations of IUwbClient. */
+public abstract class UwbClient extends IUwbClient.Stub {
+ protected final UwbServiceImpl mUwbService;
+ protected final RangingDevice mDevice;
+
+ protected UwbClient(RangingDevice device, UwbServiceImpl uwbService) {
+ mDevice = device;
+ mUwbService = uwbService;
+ }
+
+ @Override
+ public boolean isAvailable() throws RemoteException {
+ return mUwbService.isAvailable();
+ }
+
+ @Override
+ public RangingCapabilities getRangingCapabilities() throws RemoteException {
+ androidx.core.uwb.backend.impl.internal.RangingCapabilities cap =
+ mUwbService.getRangingCapabilities();
+ RangingCapabilities rangingCapabilities = new RangingCapabilities();
+ rangingCapabilities.supportsAzimuthalAngle = cap.supportsAzimuthalAngle();
+ rangingCapabilities.supportsDistance = cap.supportsDistance();
+ rangingCapabilities.supportsElevationAngle = cap.supportsElevationAngle();
+ return rangingCapabilities;
+ }
+
+ @Override
+ public UwbAddress getLocalAddress() throws RemoteException {
+ androidx.core.uwb.backend.impl.internal.UwbAddress address = mDevice.getLocalAddress();
+ UwbAddress uwbAddress = new UwbAddress();
+ uwbAddress.address = address.toBytes();
+ return uwbAddress;
+ }
+
+ protected void setRangingParameters(RangingParameters parameters) throws RemoteException {
+ androidx.core.uwb.backend.impl.internal.UwbComplexChannel channel =
+ new androidx.core.uwb.backend.impl.internal.UwbComplexChannel(
+ parameters.complexChannel.channel, parameters.complexChannel.preambleIndex);
+ List<androidx.core.uwb.backend.impl.internal.UwbAddress> addresses = new ArrayList<>();
+ for (androidx.core.uwb.backend.UwbDevice device : parameters.peerDevices) {
+ addresses.add(androidx.core.uwb.backend.impl.internal.UwbAddress
+ .fromBytes(device.address.address));
+ }
+ mDevice.setRangingParameters(
+ new androidx.core.uwb.backend.impl.internal.RangingParameters(
+ parameters.uwbConfigId, parameters.sessionId, parameters.sessionKeyInfo,
+ channel, addresses, parameters.rangingUpdateRate));
+ }
+
+ protected androidx.core.uwb.backend.impl.internal.RangingSessionCallback convertCallback(
+ IRangingSessionCallback callback) {
+ return new RangingSessionCallback() {
+ @Override
+ public void onRangingInitialized(UwbDevice device) {
+ androidx.core.uwb.backend.UwbDevice backendDevice =
+ new androidx.core.uwb.backend.UwbDevice();
+ backendDevice.address = new UwbAddress();
+ backendDevice.address.address = device.getAddress().toBytes();
+ try {
+ callback.onRangingInitialized(backendDevice);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onRangingResult(UwbDevice device, RangingPosition position) {
+ androidx.core.uwb.backend.UwbDevice backendDevice =
+ new androidx.core.uwb.backend.UwbDevice();
+ backendDevice.address = new UwbAddress();
+ backendDevice.address.address = device.getAddress().toBytes();
+ androidx.core.uwb.backend.RangingPosition rangingPosition =
+ new androidx.core.uwb.backend.RangingPosition();
+ RangingMeasurement distance = new RangingMeasurement();
+ distance.confidence = position.getDistance().getConfidence();
+ distance.value = position.getDistance().getValue();
+ rangingPosition.distance = distance;
+ if (position.getAzimuth() != null) {
+ RangingMeasurement azimuth = new RangingMeasurement();
+ azimuth.confidence = position.getAzimuth().getConfidence();
+ azimuth.value = position.getAzimuth().getValue();
+ rangingPosition.azimuth = azimuth;
+ }
+ if (position.getElevation() != null) {
+ RangingMeasurement elevation = new RangingMeasurement();
+ elevation.confidence = position.getElevation().getConfidence();
+ elevation.value = position.getElevation().getValue();
+ rangingPosition.elevation = elevation;
+ }
+ rangingPosition.elapsedRealtimeNanos = position.getElapsedRealtimeNanos();
+ try {
+ callback.onRangingResult(backendDevice, rangingPosition);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onRangingSuspended(UwbDevice device, int reason) {
+ androidx.core.uwb.backend.UwbDevice backendDevice =
+ new androidx.core.uwb.backend.UwbDevice();
+ backendDevice.address = new UwbAddress();
+ backendDevice.address.address = device.getAddress().toBytes();
+ try {
+ callback.onRangingSuspended(backendDevice, reason);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ };
+ }
+}
diff --git a/androidx_backend/src/androidx/core/uwb/backend/impl/UwbControleeClient.java b/androidx_backend/src/androidx/core/uwb/backend/impl/UwbControleeClient.java
new file mode 100644
index 00000000..e76e1faf
--- /dev/null
+++ b/androidx_backend/src/androidx/core/uwb/backend/impl/UwbControleeClient.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 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 androidx.core.uwb.backend.impl;
+
+import static androidx.core.uwb.backend.impl.internal.Utils.STATUS_OK;
+import static androidx.core.uwb.backend.impl.internal.Utils.TAG;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+import androidx.core.uwb.backend.IRangingSessionCallback;
+import androidx.core.uwb.backend.RangingParameters;
+import androidx.core.uwb.backend.UwbAddress;
+import androidx.core.uwb.backend.UwbComplexChannel;
+import androidx.core.uwb.backend.impl.internal.RangingControlee;
+import androidx.core.uwb.backend.impl.internal.UwbServiceImpl;
+
+import java.util.concurrent.Executors;
+
+/** This class implement the operations of a uwb controlee. */
+public class UwbControleeClient extends UwbClient {
+
+ public UwbControleeClient(RangingControlee rangingControlee, UwbServiceImpl uwbService) {
+ super(rangingControlee, uwbService);
+ }
+
+ @Override
+ public UwbComplexChannel getComplexChannel() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void startRanging(RangingParameters parameters, IRangingSessionCallback callback)
+ throws RemoteException {
+ setRangingParameters(parameters);
+ int status = mDevice.startRanging(
+ convertCallback(callback), Executors.newSingleThreadExecutor());
+ if (status != STATUS_OK) {
+ Log.w(TAG, String.format("Ranging start failed with status %d", status));
+ }
+ }
+
+ @Override
+ public void stopRanging(IRangingSessionCallback callback) throws RemoteException {
+ int status = mDevice.stopRanging();
+ if (status != STATUS_OK) {
+ Log.w(TAG, String.format("Ranging stop failed with status %d", status));
+ }
+ }
+
+ @Override
+ public void addControlee(UwbAddress address) throws RemoteException {
+ }
+
+ @Override
+ public void removeControlee(UwbAddress address) throws RemoteException {
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
+ }
+}
diff --git a/androidx_backend/src/androidx/core/uwb/backend/impl/UwbControllerClient.java b/androidx_backend/src/androidx/core/uwb/backend/impl/UwbControllerClient.java
new file mode 100644
index 00000000..d2c08ba0
--- /dev/null
+++ b/androidx_backend/src/androidx/core/uwb/backend/impl/UwbControllerClient.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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 androidx.core.uwb.backend.impl;
+
+import static androidx.core.uwb.backend.impl.internal.Utils.STATUS_OK;
+import static androidx.core.uwb.backend.impl.internal.Utils.TAG;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+import androidx.core.uwb.backend.IRangingSessionCallback;
+import androidx.core.uwb.backend.RangingParameters;
+import androidx.core.uwb.backend.UwbAddress;
+import androidx.core.uwb.backend.UwbComplexChannel;
+import androidx.core.uwb.backend.impl.internal.RangingController;
+import androidx.core.uwb.backend.impl.internal.UwbServiceImpl;
+
+import java.util.concurrent.Executors;
+
+/** This class implement the operations of a uwb controller. */
+public class UwbControllerClient extends UwbClient {
+
+ public UwbControllerClient(RangingController rangingController, UwbServiceImpl uwbService) {
+ super(rangingController, uwbService);
+ }
+
+ @Override
+ public UwbComplexChannel getComplexChannel() throws RemoteException {
+ RangingController controller = (RangingController) mDevice;
+ androidx.core.uwb.backend.impl.internal.UwbComplexChannel channel =
+ controller.getComplexChannel();
+ UwbComplexChannel uwbComplexChannel = new UwbComplexChannel();
+ uwbComplexChannel.channel = channel.getChannel();
+ uwbComplexChannel.preambleIndex = channel.getPreambleIndex();
+ return uwbComplexChannel;
+ }
+
+ @Override
+ public void startRanging(RangingParameters parameters, IRangingSessionCallback callback)
+ throws RemoteException {
+ setRangingParameters(parameters);
+ int status = ((RangingController) mDevice)
+ .startRanging(convertCallback(callback), Executors.newSingleThreadExecutor());
+ if (status != STATUS_OK) {
+ Log.w(TAG, String.format("Ranging start failed with status %d", status));
+ }
+ }
+
+ @Override
+ public void stopRanging(IRangingSessionCallback callback) throws RemoteException {
+ int status = ((RangingController) mDevice).stopRanging();
+ if (status != STATUS_OK) {
+ Log.w(TAG, String.format("Ranging stop failed with status %d", status));
+ }
+ }
+
+ @Override
+ public void addControlee(UwbAddress address) throws RemoteException {
+ androidx.core.uwb.backend.impl.internal.UwbAddress uwbAddress =
+ androidx.core.uwb.backend.impl.internal.UwbAddress.fromBytes(address.address);
+ int status = ((RangingController) mDevice).addControlee(uwbAddress);
+ if (status != STATUS_OK) {
+ Log.w(TAG, String.format("Adding controlee failed with status %d", status));
+ }
+ }
+
+ @Override
+ public void removeControlee(UwbAddress address) throws RemoteException {
+ androidx.core.uwb.backend.impl.internal.UwbAddress uwbAddress =
+ androidx.core.uwb.backend.impl.internal.UwbAddress.fromBytes(address.address);
+ int status = ((RangingController) mDevice).removeControlee(uwbAddress);
+ if (status != STATUS_OK) {
+ Log.w(TAG, String.format("Removing controlee failed with status %d", status));
+ }
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
+ }
+}
diff --git a/androidx_backend/src/androidx/core/uwb/backend/impl/UwbService.java b/androidx_backend/src/androidx/core/uwb/backend/impl/UwbService.java
index 8b68a302..19f0db64 100644
--- a/androidx_backend/src/androidx/core/uwb/backend/impl/UwbService.java
+++ b/androidx_backend/src/androidx/core/uwb/backend/impl/UwbService.java
@@ -18,6 +18,7 @@ package androidx.core.uwb.backend.impl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
+import android.util.Log;
import androidx.core.uwb.backend.IUwb;
import androidx.core.uwb.backend.IUwbClient;
@@ -26,15 +27,11 @@ import androidx.core.uwb.backend.impl.internal.UwbServiceImpl;
/** Uwb service entry point of the backend. */
public class UwbService extends Service {
- private final UwbServiceImpl mUwbServiceImpl;
-
- public UwbService() {
- mUwbServiceImpl = new UwbServiceImpl(this);
- }
-
+ private UwbServiceImpl mUwbServiceImpl;
@Override
public void onCreate() {
super.onCreate();
+ mUwbServiceImpl = new UwbServiceImpl(this);
}
@Override
@@ -53,15 +50,18 @@ public class UwbService extends Service {
new IUwb.Stub() {
@Override
public IUwbClient getControleeClient() {
- // TODO (b/234033640): Implement this. How do we reuse gmscore code here?
- // Need to create a context from calling package. Then create a ranging device.
- return null;
+ Log.i("UwbService", "Getting controleeClient");
+ return new UwbControleeClient(mUwbServiceImpl
+ .getControlee(UwbService.this.getApplicationContext()),
+ mUwbServiceImpl);
}
@Override
public IUwbClient getControllerClient() {
- // TODO (b/234033640): Implement this. How do we reuse gmscore code here?
- return null;
+ Log.i("UwbService", "Getting controllerClient");
+ return new UwbControllerClient(mUwbServiceImpl
+ .getController(UwbService.this.getApplicationContext()),
+ mUwbServiceImpl);
}
@Override
diff --git a/androidx_backend/tests/src/androidx/core/uwb/backend/impl/UwbControleeClientTest.java b/androidx_backend/tests/src/androidx/core/uwb/backend/impl/UwbControleeClientTest.java
new file mode 100644
index 00000000..dc0b8a05
--- /dev/null
+++ b/androidx_backend/tests/src/androidx/core/uwb/backend/impl/UwbControleeClientTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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 androidx.core.uwb.backend.impl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.core.uwb.backend.IRangingSessionCallback;
+import androidx.core.uwb.backend.RangingParameters;
+import androidx.core.uwb.backend.UwbComplexChannel;
+import androidx.core.uwb.backend.impl.internal.RangingControlee;
+import androidx.core.uwb.backend.impl.internal.RangingSessionCallback;
+import androidx.core.uwb.backend.impl.internal.UwbServiceImpl;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class UwbControleeClientTest {
+
+ @Mock private RangingControlee mRangingControlee;
+ @Mock private UwbServiceImpl mUwbService;
+ @Mock private IRangingSessionCallback mRangingSessionCallback;
+ private UwbControleeClient mUwbControleeClient;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mUwbControleeClient = new UwbControleeClient(mRangingControlee, mUwbService);
+ }
+
+ @Test
+ public void testStartRanging() throws RemoteException {
+ RangingParameters params = new RangingParameters();
+ params.complexChannel = new UwbComplexChannel();
+ params.complexChannel.channel = 9;
+ params.complexChannel.preambleIndex = 9;
+ params.peerDevices = new ArrayList<>();
+ mUwbControleeClient.startRanging(params, mRangingSessionCallback);
+ verify(mRangingControlee).setRangingParameters(any());
+ verify(mRangingControlee).startRanging(
+ any(RangingSessionCallback.class), any(ExecutorService.class));
+ }
+
+ @Test
+ public void testStopRanging() throws RemoteException {
+ mUwbControleeClient.stopRanging(mRangingSessionCallback);
+ verify(mRangingControlee).stopRanging();
+ }
+}
diff --git a/androidx_backend/tests/src/androidx/core/uwb/backend/impl/UwbControllerClientTest.java b/androidx_backend/tests/src/androidx/core/uwb/backend/impl/UwbControllerClientTest.java
new file mode 100644
index 00000000..0c5cfe68
--- /dev/null
+++ b/androidx_backend/tests/src/androidx/core/uwb/backend/impl/UwbControllerClientTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 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 androidx.core.uwb.backend.impl;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.core.uwb.backend.IRangingSessionCallback;
+import androidx.core.uwb.backend.RangingParameters;
+import androidx.core.uwb.backend.UwbAddress;
+import androidx.core.uwb.backend.impl.internal.RangingController;
+import androidx.core.uwb.backend.impl.internal.RangingSessionCallback;
+import androidx.core.uwb.backend.impl.internal.UwbComplexChannel;
+import androidx.core.uwb.backend.impl.internal.UwbServiceImpl;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class UwbControllerClientTest {
+
+ @Mock private RangingController mRangingController;
+ @Mock private UwbServiceImpl mUwbService;
+ @Mock private IRangingSessionCallback mRangingSessionCallback;
+ @Captor
+ ArgumentCaptor<androidx.core.uwb.backend.impl.internal.UwbAddress> mAddressCaptor;
+ private UwbControllerClient mUwbControllerClient;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mUwbControllerClient = new UwbControllerClient(mRangingController, mUwbService);
+ }
+
+ @Test
+ public void testGetComplexChannel() throws RemoteException {
+ UwbComplexChannel channel = new UwbComplexChannel(9, 9);
+ when(mRangingController.getComplexChannel()).thenReturn(channel);
+ androidx.core.uwb.backend.UwbComplexChannel complexChannel =
+ mUwbControllerClient.getComplexChannel();
+ assertEquals(complexChannel.channel, channel.getChannel());
+ assertEquals(complexChannel.preambleIndex, channel.getPreambleIndex());
+ }
+
+ @Test
+ public void testStartRanging() throws RemoteException {
+ RangingParameters params = new RangingParameters();
+ params.complexChannel = new androidx.core.uwb.backend.UwbComplexChannel();
+ params.complexChannel.channel = 9;
+ params.complexChannel.preambleIndex = 9;
+ params.peerDevices = new ArrayList<>();
+ mUwbControllerClient.startRanging(params, mRangingSessionCallback);
+ verify(mRangingController).setRangingParameters(any(
+ androidx.core.uwb.backend.impl.internal.RangingParameters.class));
+ verify(mRangingController).startRanging(
+ any(RangingSessionCallback.class), any(ExecutorService.class));
+ }
+
+ @Test
+ public void testStopRanging() throws RemoteException {
+ mUwbControllerClient.stopRanging(mRangingSessionCallback);
+ verify(mRangingController).stopRanging();
+ }
+
+ @Test
+ public void testAddControlee() throws RemoteException {
+ UwbAddress address = new UwbAddress();
+ address.address = new byte[] {0x1, 0x2};
+ mUwbControllerClient.addControlee(address);
+ verify(mRangingController).addControlee(mAddressCaptor.capture());
+ assertArrayEquals(address.address, mAddressCaptor.getValue().toBytes());
+ }
+
+ @Test
+ public void testRemoveControlee() throws RemoteException {
+ UwbAddress address = new UwbAddress();
+ address.address = new byte[] {0x1, 0x2};
+ mUwbControllerClient.removeControlee(address);
+ verify(mRangingController).removeControlee(mAddressCaptor.capture());
+ assertArrayEquals(address.address, mAddressCaptor.getValue().toBytes());
+ }
+}
diff --git a/apex/Android.bp b/apex/Android.bp
index 9e86c9b9..b89804a9 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -108,6 +108,7 @@ bootclasspath_fragment {
// result in a build failure due to inconsistent flags.
package_prefixes: [
"com.android.x",
+ "android.uwb.util",
],
},
}
diff --git a/framework/Android.bp b/framework/Android.bp
index 58bf8f69..7f75ec68 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -74,8 +74,6 @@ java_library {
defaults: ["framework-uwb-defaults"],
sdk_version: "module_Tiramisu",
libs: ["framework-annotations-lib",],
- // java_api_finder must accompany `srcs` (`srcs` defined in `framework-uwb-defaults`)
- plugins: ["java_api_finder"],
installable: false,
}
@@ -95,8 +93,6 @@ java_sdk_library {
hostdex: true, // for hiddenapi check
impl_library_visibility: [
- //TODO(b/244347268): Remove this after code move.
- "//cts/tests/uwb:__subpackages__",
"//external/sl4a/Common:__subpackages__",
"//packages/modules/Uwb:__subpackages__",
],
@@ -106,6 +102,7 @@ java_sdk_library {
],
permitted_packages: [
"android.uwb",
+ "android.uwb.util",
// Created by jarjar rules.
"com.android.x.uwb",
],
@@ -127,27 +124,6 @@ java_defaults {
],
}
-// TODO(b/186585880): Fix all @hide dependencies.
-// defaults for CTS tests that need to build against framework-uwb's @hide APIs
-java_defaults {
- name: "framework-uwb-cts-defaults",
- sdk_version: "core_current",
- libs: [
- // order matters: classes in framework-uwb are resolved before framework, meaning
- // @hide APIs in framework-uwb are resolved before @SystemApi stubs in framework
- "framework-uwb.impl",
- "framework",
-
- // if sdk_version="" this gets automatically included, but here we need to add manually.
- "framework-res",
- ],
- defaults_visibility: [
- //TODO(b/244347268): Remove this after code move.
- "//cts/tests/uwb:__subpackages__",
- "//packages/modules/Uwb/tests/cts:__subpackages__",
- ],
-}
-
filegroup {
name: "uwb-jarjar-rules",
srcs: ["jarjar-rules.txt"],
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 5ce39a16..bc51295c 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -50,7 +50,6 @@ package android.uwb {
method public long getElapsedRealtimeNanos();
method public int getLineOfSight();
method public int getMeasurementFocus();
- method @NonNull public android.os.PersistableBundle getRangingMeasurementMetadata();
method @NonNull public android.uwb.UwbAddress getRemoteDeviceAddress();
method @IntRange(from=android.uwb.RangingMeasurement.RSSI_UNKNOWN, to=android.uwb.RangingMeasurement.RSSI_MAX) public int getRssiDbm();
method public int getStatus();
@@ -80,7 +79,6 @@ package android.uwb {
method @NonNull public android.uwb.RangingMeasurement.Builder setElapsedRealtimeNanos(long);
method @NonNull public android.uwb.RangingMeasurement.Builder setLineOfSight(int);
method @NonNull public android.uwb.RangingMeasurement.Builder setMeasurementFocus(int);
- method @NonNull public android.uwb.RangingMeasurement.Builder setRangingMeasurementMetadata(@NonNull android.os.PersistableBundle);
method @NonNull public android.uwb.RangingMeasurement.Builder setRemoteDeviceAddress(@NonNull android.uwb.UwbAddress);
method @NonNull public android.uwb.RangingMeasurement.Builder setRssiDbm(@IntRange(from=android.uwb.RangingMeasurement.RSSI_UNKNOWN, to=android.uwb.RangingMeasurement.RSSI_MAX) int);
method @NonNull public android.uwb.RangingMeasurement.Builder setStatus(int);
@@ -89,7 +87,6 @@ package android.uwb {
public final class RangingReport implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.uwb.RangingMeasurement> getMeasurements();
- method @NonNull public android.os.PersistableBundle getRangingReportMetadata();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.uwb.RangingReport> CREATOR;
}
@@ -98,7 +95,6 @@ package android.uwb {
ctor public RangingReport.Builder();
method @NonNull public android.uwb.RangingReport.Builder addMeasurement(@NonNull android.uwb.RangingMeasurement);
method @NonNull public android.uwb.RangingReport.Builder addMeasurements(@NonNull java.util.List<android.uwb.RangingMeasurement>);
- method @NonNull public android.uwb.RangingReport.Builder addRangingReportMetadata(@NonNull android.os.PersistableBundle);
method @NonNull public android.uwb.RangingReport build();
}
@@ -184,14 +180,12 @@ package android.uwb {
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.UWB_PRIVILEGED, android.Manifest.permission.UWB_RANGING}) public android.os.CancellationSignal openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback, @NonNull String);
method public void provisionProfileAdfByScript(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdfProvisionStateCallback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerUwbOemExtensionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.UwbOemExtensionCallback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerUwbVendorUciCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.UwbVendorUciCallback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int removeProfileAdf(@NonNull android.os.PersistableBundle);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int removeServiceProfile(@NonNull android.os.PersistableBundle);
method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int sendVendorUciMessage(@IntRange(from=9, to=15) int, int, @NonNull byte[]);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void setUwbEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterUwbOemExtensionCallback(@NonNull android.uwb.UwbManager.UwbOemExtensionCallback);
method public void unregisterUwbVendorUciCallback(@NonNull android.uwb.UwbManager.UwbVendorUciCallback);
field public static final int REMOVE_PROFILE_ADF_ERROR_INTERNAL = 2; // 0x2
field public static final int REMOVE_PROFILE_ADF_ERROR_UNKNOWN_SERVICE = 1; // 0x1
@@ -227,13 +221,6 @@ package android.uwb {
field public static final int REASON_UNKNOWN = 3; // 0x3
}
- public static interface UwbManager.UwbOemExtensionCallback {
- method public void onDeviceStatusNotificationReceived(@NonNull android.os.PersistableBundle);
- method @NonNull public android.uwb.RangingReport onRangingReportReceived(@NonNull android.uwb.RangingReport);
- method @NonNull public int onSessionConfigurationComplete(@NonNull android.os.PersistableBundle);
- method public void onSessionStatusNotificationReceived(@NonNull android.os.PersistableBundle);
- }
-
public static interface UwbManager.UwbVendorUciCallback {
method public void onVendorUciNotification(@IntRange(from=9, to=15) int, int, @NonNull byte[]);
method public void onVendorUciResponse(@IntRange(from=9, to=15) int, int, @NonNull byte[]);
diff --git a/framework/java/android/uwb/IUwbAdapter.aidl b/framework/java/android/uwb/IUwbAdapter.aidl
index 4902ef7e..ad23183a 100644
--- a/framework/java/android/uwb/IUwbAdapter.aidl
+++ b/framework/java/android/uwb/IUwbAdapter.aidl
@@ -347,6 +347,8 @@ interface IUwbAdapter {
int sendVendorUciMessage(int gid, int oid, in byte[] payload);
+ void onRangingRoundsUpdateDtTag(in SessionHandle sessionHandle, in PersistableBundle parameters);
+
/**
* The maximum allowed time to open a ranging session.
*/
@@ -362,4 +364,9 @@ interface IUwbAdapter {
* closed.
*/
const int RANGING_SESSION_CLOSE_THRESHOLD_MS = 3000; // Value TBD
+
+ /**
+ * The maximum allowed time to configure ranging rounds update for DT Tag
+ */
+ const int RANGING_ROUNDS_UPDATE_DT_TAG_THRESHOLD_MS = 3000; // Value TBD
}
diff --git a/framework/java/android/uwb/IUwbRangingCallbacks.aidl b/framework/java/android/uwb/IUwbRangingCallbacks.aidl
index a961718b..44bd4d9e 100644
--- a/framework/java/android/uwb/IUwbRangingCallbacks.aidl
+++ b/framework/java/android/uwb/IUwbRangingCallbacks.aidl
@@ -258,4 +258,7 @@ oneway interface IUwbRangingCallbacks {
void onServiceDiscovered(in SessionHandle sessionHandle, in PersistableBundle parameters);
void onServiceConnected(in SessionHandle sessionHandle, in PersistableBundle parameters);
+
+ void onRangingRoundsUpdateDtTagStatus(in SessionHandle sessionHandle,
+ in PersistableBundle parameters);
}
diff --git a/framework/java/android/uwb/RangingManager.java b/framework/java/android/uwb/RangingManager.java
index 491808ae..adbbbae6 100644
--- a/framework/java/android/uwb/RangingManager.java
+++ b/framework/java/android/uwb/RangingManager.java
@@ -459,6 +459,21 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
}
}
+ @Override
+ public void onRangingRoundsUpdateDtTagStatus(SessionHandle sessionHandle,
+ @NonNull PersistableBundle parameters) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(mTag, "onRangingRoundsUpdateDtTagStatus - received unexpected "
+ + "SessionHandle: " + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingRoundsUpdateDtTagStatus(parameters);
+ }
+ }
+
// TODO(b/211025367): Remove this conversion and use direct API values.
@RangingSession.Callback.Reason
private static int convertToReason(@RangingChangeReason int reason) {
diff --git a/framework/java/android/uwb/RangingMeasurement.java b/framework/java/android/uwb/RangingMeasurement.java
index d47e1020..cbd7e358 100644
--- a/framework/java/android/uwb/RangingMeasurement.java
+++ b/framework/java/android/uwb/RangingMeasurement.java
@@ -26,6 +26,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.SystemClock;
+import android.uwb.util.PersistableBundleUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -252,7 +253,7 @@ public final class RangingMeasurement implements Parcelable {
/**
* Gets ranging measurement metadata passed by vendor
- *
+ * @hide
* @return vendor data for ranging measurement
*/
@NonNull
@@ -282,8 +283,9 @@ public final class RangingMeasurement implements Parcelable {
other.getDestinationAngleOfArrivalMeasurement())
&& mLineOfSight == other.getLineOfSight()
&& mMeasurementFocus == other.getMeasurementFocus()
- && mRssiDbm == other.getRssiDbm();
- // TODO: Equality for RangingMeasurementMetadata
+ && mRssiDbm == other.getRssiDbm()
+ && PersistableBundleUtils.isEqual(mRangingMeasurementMetadata,
+ other.mRangingMeasurementMetadata);
}
return false;
}
@@ -295,7 +297,8 @@ public final class RangingMeasurement implements Parcelable {
public int hashCode() {
return Objects.hash(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos,
mDistanceMeasurement, mAngleOfArrivalMeasurement,
- mDestinationAngleOfArrivalMeasurement, mLineOfSight, mMeasurementFocus, mRssiDbm);
+ mDestinationAngleOfArrivalMeasurement, mLineOfSight, mMeasurementFocus, mRssiDbm,
+ PersistableBundleUtils.getHashCode(mRangingMeasurementMetadata));
}
@Override
@@ -335,8 +338,9 @@ public final class RangingMeasurement implements Parcelable {
builder.setLineOfSight(in.readInt());
builder.setMeasurementFocus(in.readInt());
builder.setRssiDbm(in.readInt());
- builder.setRangingMeasurementMetadata(
- in.readPersistableBundle(getClass().getClassLoader()));
+ PersistableBundle metadata =
+ in.readPersistableBundle(getClass().getClassLoader());
+ if (metadata != null) builder.setRangingMeasurementMetadata(metadata);
return builder.build();
}
@@ -489,7 +493,7 @@ public final class RangingMeasurement implements Parcelable {
/**
* Set Ranging measurement metadata
- *
+ * @hide
* @param rangingMeasurementMetadata vendor data per ranging measurement
*
* @throws IllegalStateException if rangingMeasurementMetadata is null
diff --git a/framework/java/android/uwb/RangingReport.java b/framework/java/android/uwb/RangingReport.java
index 4bce4b42..b3079786 100644
--- a/framework/java/android/uwb/RangingReport.java
+++ b/framework/java/android/uwb/RangingReport.java
@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import android.uwb.util.PersistableBundleUtils;
import java.util.ArrayList;
import java.util.List;
@@ -60,7 +61,7 @@ public final class RangingReport implements Parcelable {
/**
* Gets ranging report metadata passed by vendor
- *
+ * @hide
* @return vendor data for ranging report
*/
@NonNull
@@ -79,8 +80,9 @@ public final class RangingReport implements Parcelable {
if (obj instanceof RangingReport) {
RangingReport other = (RangingReport) obj;
- return mRangingMeasurements.equals(other.getMeasurements());
- // TODO(b/256734264): Equality for RangingReportMetadata
+ return mRangingMeasurements.equals(other.getMeasurements())
+ && PersistableBundleUtils.isEqual(mRangingReportMetadata,
+ other.getRangingReportMetadata());
}
return false;
}
@@ -90,7 +92,8 @@ public final class RangingReport implements Parcelable {
*/
@Override
public int hashCode() {
- return Objects.hash(mRangingMeasurements);
+ return Objects.hash(mRangingMeasurements, PersistableBundleUtils
+ .getHashCode(mRangingReportMetadata));
}
@Override
@@ -110,8 +113,9 @@ public final class RangingReport implements Parcelable {
public RangingReport createFromParcel(Parcel in) {
Builder builder = new Builder();
builder.addMeasurements(in.createTypedArrayList(RangingMeasurement.CREATOR));
- builder.addRangingReportMetadata(in.readPersistableBundle(
- getClass().getClassLoader()));
+ PersistableBundle metadata =
+ in.readPersistableBundle(getClass().getClassLoader());
+ if (metadata != null) builder.addRangingReportMetadata(metadata);
return builder.build();
}
@@ -161,7 +165,7 @@ public final class RangingReport implements Parcelable {
/**
* Add ranging report metadata
- *
+ * @hide
* @param rangingReportMetadata vendor data per ranging report
*
* @throws IllegalStateException if rangingReportMetadata is null
diff --git a/framework/java/android/uwb/RangingSession.java b/framework/java/android/uwb/RangingSession.java
index d5844f55..1c05bd93 100644
--- a/framework/java/android/uwb/RangingSession.java
+++ b/framework/java/android/uwb/RangingSession.java
@@ -417,6 +417,16 @@ public final class RangingSession implements AutoCloseable {
* @param parameters protocol specific params for connected service.
*/
default void onServiceConnected(@NonNull PersistableBundle parameters) {}
+
+ /**
+ * @hide
+ * Invoked when a response/status is received for active ranging rounds update
+ *
+ * @param parameters bundle of ranging rounds update status
+ * {@link com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdateStatus}
+ */
+ // TODO: Add @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) after ag/19901449
+ default void onRangingRoundsUpdateDtTagStatus(@NonNull PersistableBundle parameters) {}
}
/**
@@ -729,6 +739,32 @@ public final class RangingSession implements AutoCloseable {
/**
* @hide
+ * Update active ranging rounds for DT Tag
+ *
+ * <p> On successfully sending the command,
+ * {@link RangingSession.Callback#onRangingRoundsUpdateDtTag(PersistableBundle)}
+ * is invoked
+ * @param params Parameters to configure active ranging rounds
+ * {@link com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdate}
+ */
+ // TODO: Add @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) after ag/19901449
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
+ public void onRangingRoundsUpdateDtTag(@NonNull PersistableBundle params) {
+ if (mState != State.ACTIVE) {
+ throw new IllegalStateException();
+ }
+
+ Log.v(mTag, "onRangingRoundsUpdateDtTag - sessionHandle: " + mSessionHandle);
+ try {
+ mAdapter.onRangingRoundsUpdateDtTag(mSessionHandle, params);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * @hide
*/
public void onRangingOpened() {
if (mState == State.CLOSED) {
@@ -1056,6 +1092,19 @@ public final class RangingSession implements AutoCloseable {
/**
* @hide
*/
+ public void onRangingRoundsUpdateDtTagStatus(@NonNull PersistableBundle params) {
+ if (!isOpen()) {
+ Log.w(mTag, "onDlTDoARangingRoundsUpdateStatus invoked for non-open session");
+ return;
+ }
+
+ Log.v(mTag, "onDlTDoARangingRoundsUpdateStatus - sessionHandle: " + mSessionHandle);
+ executeCallback(() -> mCallback.onRangingRoundsUpdateDtTagStatus(params));
+ }
+
+ /**
+ * @hide
+ */
private void executeCallback(@NonNull Runnable runnable) {
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/framework/java/android/uwb/UwbManager.java b/framework/java/android/uwb/UwbManager.java
index d2871294..c3f4e5b6 100644
--- a/framework/java/android/uwb/UwbManager.java
+++ b/framework/java/android/uwb/UwbManager.java
@@ -340,6 +340,7 @@ public final class UwbManager {
/**
* Interface for Oem extensions on ongoing session
+ * @hide
*/
// TODO: Add @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) after ag/19901449
public interface UwbOemExtensionCallback {
@@ -456,6 +457,7 @@ public final class UwbManager {
}
/**
+ * @hide
* Register an {@link UwbOemExtensionCallback} to listen for UWB oem extension callbacks
* <p>The provided callback will be invoked by the given {@link Executor}.
*
@@ -470,6 +472,7 @@ public final class UwbManager {
}
/**
+ * @hide
* Unregister the specified {@link UwbOemExtensionCallback}
*
* <p>The same {@link UwbOemExtensionCallback} object used when calling
diff --git a/framework/java/android/uwb/util/PersistableBundleUtils.java b/framework/java/android/uwb/util/PersistableBundleUtils.java
new file mode 100644
index 00000000..d81e7198
--- /dev/null
+++ b/framework/java/android/uwb/util/PersistableBundleUtils.java
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2022 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.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.TreeSet;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/** @hide */
+public class PersistableBundleUtils {
+// private static final String LIST_KEY_FORMAT = "LIST_ITEM_%d";
+// private static final String COLLECTION_SIZE_KEY = "COLLECTION_LENGTH";
+// private static final String MAP_KEY_FORMAT = "MAP_KEY_%d";
+// private static final String MAP_VALUE_FORMAT = "MAP_VALUE_%d";
+//
+// private static final String PARCEL_UUID_KEY = "PARCEL_UUID";
+// private static final String BYTE_ARRAY_KEY = "BYTE_ARRAY_KEY";
+// private static final String INTEGER_KEY = "INTEGER_KEY";
+// private static final String STRING_KEY = "STRING_KEY";
+//
+// private final static char[] HEX_DIGITS =
+// {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+// private final static char[] HEX_LOWER_CASE_DIGITS =
+// {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+
+// /**
+// * Functional interface to convert an object of the specified type to a PersistableBundle.
+// *
+// * @param <T> the type of the source object
+// */
+// public interface Serializer<T> {
+// /**
+// * Converts this object to a PersistableBundle.
+// *
+// * @return the PersistableBundle representation of this object
+// */
+// PersistableBundle toPersistableBundle(T obj);
+// }
+//
+// /**
+// * Functional interface used to create an object of the specified type from a PersistableBundle.
+// *
+// * @param <T> the type of the resultant object
+// */
+// public interface Deserializer<T> {
+// /**
+// * Creates an instance of specified type from a PersistableBundle representation.
+// *
+// * @param in the PersistableBundle representation
+// * @return an instance of the specified type
+// */
+// T fromPersistableBundle(PersistableBundle in);
+// }
+//
+// /** Serializer to convert an integer to a PersistableBundle. */
+// public static final Serializer<Integer> INTEGER_SERIALIZER =
+// (i) -> {
+// final PersistableBundle result = new PersistableBundle();
+// result.putInt(INTEGER_KEY, i);
+// return result;
+// };
+//
+// /** Deserializer to convert a PersistableBundle to an integer. */
+// public static final Deserializer<Integer> INTEGER_DESERIALIZER =
+// (bundle) -> {
+// Objects.requireNonNull(bundle, "PersistableBundle is null");
+// return bundle.getInt(INTEGER_KEY);
+// };
+//
+// /** Serializer to convert s String to a PersistableBundle. */
+// public static final Serializer<String> STRING_SERIALIZER =
+// (i) -> {
+// final PersistableBundle result = new PersistableBundle();
+// result.putString(STRING_KEY, i);
+// return result;
+// };
+//
+// /** Deserializer to convert a PersistableBundle to a String. */
+// public static final Deserializer<String> STRING_DESERIALIZER =
+// (bundle) -> {
+// Objects.requireNonNull(bundle, "PersistableBundle is null");
+// return bundle.getString(STRING_KEY);
+// };
+//
+// /**
+// * Converts a ParcelUuid to a PersistableBundle.
+// *
+// * <p>To avoid key collisions, NO additional key/value pairs should be added to the returned
+// * PersistableBundle object.
+// *
+// * @param uuid a ParcelUuid instance to persist
+// * @return the PersistableBundle instance
+// */
+// public static PersistableBundle fromParcelUuid(ParcelUuid uuid) {
+// final PersistableBundle result = new PersistableBundle();
+//
+// result.putString(PARCEL_UUID_KEY, uuid.toString());
+//
+// return result;
+// }
+//
+// /**
+// * Converts from a PersistableBundle to a ParcelUuid.
+// *
+// * @param bundle the PersistableBundle containing the ParcelUuid
+// * @return the ParcelUuid instance
+// */
+// public static ParcelUuid toParcelUuid(PersistableBundle bundle) {
+// return ParcelUuid.fromString(bundle.getString(PARCEL_UUID_KEY));
+// }
+//
+// /**
+// * Converts from a list of Persistable objects to a single PersistableBundle.
+// *
+// * <p>To avoid key collisions, NO additional key/value pairs should be added to the returned
+// * PersistableBundle object.
+// *
+// * @param <T> the type of the objects to convert to the PersistableBundle
+// * @param in the list of objects to be serialized into a PersistableBundle
+// * @param serializer an implementation of the {@link Serializer} functional interface that
+// * converts an object of type T to a PersistableBundle
+// */
+// @NonNull
+// public static <T> PersistableBundle fromList(
+// @NonNull List<T> in, @NonNull Serializer<T> serializer) {
+// final PersistableBundle result = new PersistableBundle();
+//
+// result.putInt(COLLECTION_SIZE_KEY, in.size());
+// for (int i = 0; i < in.size(); i++) {
+// final String key = String.format(LIST_KEY_FORMAT, i);
+// result.putPersistableBundle(key, serializer.toPersistableBundle(in.get(i)));
+// }
+// return result;
+// }
+//
+// /**
+// * Converts from a PersistableBundle to a list of objects.
+// *
+// * @param <T> the type of the objects to convert from a PersistableBundle
+// * @param in the PersistableBundle containing the persisted list
+// * @param deserializer an implementation of the {@link Deserializer} functional interface that
+// * builds the relevant type of objects.
+// */
+// @NonNull
+// public static <T> List<T> toList(
+// @NonNull PersistableBundle in, @NonNull Deserializer<T> deserializer) {
+// final int listLength = in.getInt(COLLECTION_SIZE_KEY);
+// final ArrayList<T> result = new ArrayList<>(listLength);
+//
+// for (int i = 0; i < listLength; i++) {
+// final String key = String.format(LIST_KEY_FORMAT, i);
+// final PersistableBundle item = in.getPersistableBundle(key);
+//
+// result.add(deserializer.fromPersistableBundle(item));
+// }
+// return result;
+// }
+//
+// // TODO: b/170513329 Delete #fromByteArray and #toByteArray once BaseBundle#putByteArray and
+// // BaseBundle#getByteArray are exposed.
+//
+// /**
+// * Converts a byte array to a PersistableBundle.
+// *
+// * <p>To avoid key collisions, NO additional key/value pairs should be added to the returned
+// * PersistableBundle object.
+// *
+// * @param array a byte array instance to persist
+// * @return the PersistableBundle instance
+// */
+// public static PersistableBundle fromByteArray(byte[] array) {
+// final PersistableBundle result = new PersistableBundle();
+//
+// result.putString(BYTE_ARRAY_KEY, toHexString(array));
+//
+// return result;
+// }
+//
+// // Copied from com.android.internal.util.HexDump
+// @UnsupportedAppUsage
+// public static String toHexString(byte[] array, int offset, int length) {
+// return toHexString(array, offset, length, true);
+// }
+//
+// public static String toHexString(byte[] array, int offset, int length, boolean upperCase) {
+// char[] digits = upperCase ? HEX_DIGITS : HEX_LOWER_CASE_DIGITS;
+// char[] buf = new char[length * 2];
+//
+// int bufIndex = 0;
+// for (int i = offset; i < offset + length; i++) {
+// byte b = array[i];
+// buf[bufIndex++] = digits[(b >>> 4) & 0x0F];
+// buf[bufIndex++] = digits[b & 0x0F];
+// }
+//
+// return new String(buf);
+// }
+//
+// @UnsupportedAppUsage
+// public static String toHexString(byte[] array) {
+// return toHexString(array, 0, array.length, true);
+// }
+//
+// @UnsupportedAppUsage
+// public static String toHexString(int i) {
+// return toHexString(toByteArray(i));
+// }
+//
+// public static byte[] toByteArray(int i) {
+// byte[] array = new byte[4];
+//
+// array[3] = (byte) (i & 0xFF);
+// array[2] = (byte) ((i >> 8) & 0xFF);
+// array[1] = (byte) ((i >> 16) & 0xFF);
+// array[0] = (byte) ((i >> 24) & 0xFF);
+//
+// return array;
+// }
+//
+// @UnsupportedAppUsage
+// public static byte[] hexStringToByteArray(String hexString) {
+// int length = hexString.length();
+// byte[] buffer = new byte[length / 2];
+//
+// for (int i = 0; i < length; i += 2) {
+// buffer[i / 2] = (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(
+// hexString.charAt(i + 1)));
+// }
+//
+// return buffer;
+// }
+//
+// private static int toByte(char c) {
+// if (c >= '0' && c <= '9') return (c - '0');
+// if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
+// if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
+//
+// throw new RuntimeException("Invalid hex char '" + c + "'");
+// }
+//
+// /**
+// * Converts from a PersistableBundle to a byte array.
+// *
+// * @param bundle the PersistableBundle containing the byte array
+// * @return the byte array instance
+// */
+// public static byte[] toByteArray(PersistableBundle bundle) {
+// Objects.requireNonNull(bundle, "PersistableBundle is null");
+//
+// String hex = bundle.getString(BYTE_ARRAY_KEY);
+// if (hex == null || hex.length() % 2 != 0) {
+// throw new IllegalArgumentException("PersistableBundle contains invalid byte array");
+// }
+//
+// return hexStringToByteArray(hex);
+// }
+//
+// /**
+// * Converts from a Map of Persistable objects to a single PersistableBundle.
+// *
+// * <p>To avoid key collisions, NO additional key/value pairs should be added to the returned
+// * PersistableBundle object.
+// *
+// * @param <K> the type of the map-key to convert to the PersistableBundle
+// * @param <V> the type of the map-value to convert to the PersistableBundle
+// * @param in the Map of objects implementing the {@link Persistable} interface
+// * @param keySerializer an implementation of the {@link Serializer} functional interface that
+// * converts a map-key of type T to a PersistableBundle
+// * @param valueSerializer an implementation of the {@link Serializer} functional interface that
+// * converts a map-value of type E to a PersistableBundle
+// */
+// @NonNull
+// public static <K, V> PersistableBundle fromMap(
+// @NonNull Map<K, V> in,
+// @NonNull Serializer<K> keySerializer,
+// @NonNull Serializer<V> valueSerializer) {
+// final PersistableBundle result = new PersistableBundle();
+//
+// result.putInt(COLLECTION_SIZE_KEY, in.size());
+// int i = 0;
+// for (Entry<K, V> entry : in.entrySet()) {
+// final String keyKey = String.format(MAP_KEY_FORMAT, i);
+// final String valueKey = String.format(MAP_VALUE_FORMAT, i);
+// result.putPersistableBundle(keyKey, keySerializer.toPersistableBundle(entry.getKey()));
+// result.putPersistableBundle(
+// valueKey, valueSerializer.toPersistableBundle(entry.getValue()));
+//
+// i++;
+// }
+//
+// return result;
+// }
+//
+// /**
+// * Converts from a PersistableBundle to a Map of objects.
+// *
+// * <p>In an attempt to preserve ordering, the returned map will be a LinkedHashMap. However, the
+// * guarantees on the ordering can only ever be as strong as the map that was serialized in
+// * {@link fromMap()}. If the initial map that was serialized had no ordering guarantees, the
+// * deserialized map similarly may be of a non-deterministic order.
+// *
+// * @param <K> the type of the map-key to convert from a PersistableBundle
+// * @param <V> the type of the map-value to convert from a PersistableBundle
+// * @param in the PersistableBundle containing the persisted Map
+// * @param keyDeserializer an implementation of the {@link Deserializer} functional interface
+// * that builds the relevant type of map-key.
+// * @param valueDeserializer an implementation of the {@link Deserializer} functional interface
+// * that builds the relevant type of map-value.
+// * @return An instance of the parsed map as a LinkedHashMap (in an attempt to preserve
+// * ordering).
+// */
+// @NonNull
+// public static <K, V> LinkedHashMap<K, V> toMap(
+// @NonNull PersistableBundle in,
+// @NonNull Deserializer<K> keyDeserializer,
+// @NonNull Deserializer<V> valueDeserializer) {
+// final int mapSize = in.getInt(COLLECTION_SIZE_KEY);
+// final LinkedHashMap<K, V> result = new LinkedHashMap<>(mapSize);
+//
+// for (int i = 0; i < mapSize; i++) {
+// final String keyKey = String.format(MAP_KEY_FORMAT, i);
+// final String valueKey = String.format(MAP_VALUE_FORMAT, i);
+// final PersistableBundle keyBundle = in.getPersistableBundle(keyKey);
+// final PersistableBundle valueBundle = in.getPersistableBundle(valueKey);
+//
+// final K key = keyDeserializer.fromPersistableBundle(keyBundle);
+// final V value = valueDeserializer.fromPersistableBundle(valueBundle);
+// result.put(key, value);
+// }
+// return result;
+// }
+//
+// /**
+// * Converts a PersistableBundle into a disk-stable byte array format
+// *
+// * @param bundle the PersistableBundle to be converted to a disk-stable format
+// * @return the byte array representation of the PersistableBundle
+// */
+// @Nullable
+// public static byte[] toDiskStableBytes(@NonNull PersistableBundle bundle) throws IOException {
+// final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+// bundle.writeToStream(outputStream);
+// return outputStream.toByteArray();
+// }
+//
+// /**
+// * Converts from a disk-stable byte array format to a PersistableBundle
+// *
+// * @param bytes the disk-stable byte array
+// * @return the PersistableBundle parsed from this byte array.
+// */
+// public static PersistableBundle fromDiskStableBytes(@NonNull byte[] bytes) throws IOException {
+// final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
+// return PersistableBundle.readFromStream(inputStream);
+// }
+//
+// /**
+// * Ensures safe reading and writing of {@link PersistableBundle}s to and from disk.
+// *
+// * <p>This class will enforce exclusion between reads and writes using the standard semantics of
+// * a ReadWriteLock. Specifically, concurrent readers ARE allowed, but reads/writes from/to the
+// * file are mutually exclusive. In other words, for an unbounded number n, the acceptable states
+// * are n readers, OR 1 writer (but not both).
+// */
+// public static class LockingReadWriteHelper {
+// private final ReadWriteLock mDiskLock = new ReentrantReadWriteLock();
+// private final String mPath;
+//
+// public LockingReadWriteHelper(@NonNull String path) {
+// mPath = Objects.requireNonNull(path, "fileName was null");
+// }
+//
+// /**
+// * Reads the {@link PersistableBundle} from the disk.
+// *
+// * @return the PersistableBundle, if the file existed, or null otherwise
+// */
+// @Nullable
+// public PersistableBundle readFromDisk() throws IOException {
+// try {
+// mDiskLock.readLock().lock();
+// final File file = new File(mPath);
+// if (!file.exists()) {
+// return null;
+// }
+//
+// try (FileInputStream fis = new FileInputStream(file)) {
+// return PersistableBundle.readFromStream(fis);
+// }
+// } finally {
+// mDiskLock.readLock().unlock();
+// }
+// }
+//
+// /**
+// * Writes a {@link PersistableBundle} to disk.
+// *
+// * @param bundle the {@link PersistableBundle} to write to disk
+// */
+// public void writeToDisk(@NonNull PersistableBundle bundle) throws IOException {
+// Objects.requireNonNull(bundle, "bundle was null");
+//
+// try {
+// mDiskLock.writeLock().lock();
+// final File file = new File(mPath);
+// if (!file.exists()) {
+// file.getParentFile().mkdirs();
+// }
+//
+// try (FileOutputStream fos = new FileOutputStream(file)) {
+// bundle.writeToStream(fos);
+// }
+// } finally {
+// mDiskLock.writeLock().unlock();
+// }
+// }
+// }
+//
+// /**
+// * Returns a copy of the persistable bundle with only the specified keys
+// *
+// * <p>This allows for holding minimized copies for memory-saving purposes.
+// */
+// @NonNull
+// public static PersistableBundle minimizeBundle(
+// @NonNull PersistableBundle bundle, String... keys) {
+// final PersistableBundle minimized = new PersistableBundle();
+//
+// if (bundle == null) {
+// return minimized;
+// }
+//
+// for (String key : keys) {
+// if (bundle.containsKey(key)) {
+// final Object value = bundle.get(key);
+// if (value == null) {
+// continue;
+// }
+//
+// if (value instanceof Boolean) {
+// minimized.putBoolean(key, (Boolean) value);
+// } else if (value instanceof boolean[]) {
+// minimized.putBooleanArray(key, (boolean[]) value);
+// } else if (value instanceof Double) {
+// minimized.putDouble(key, (Double) value);
+// } else if (value instanceof double[]) {
+// minimized.putDoubleArray(key, (double[]) value);
+// } else if (value instanceof Integer) {
+// minimized.putInt(key, (Integer) value);
+// } else if (value instanceof int[]) {
+// minimized.putIntArray(key, (int[]) value);
+// } else if (value instanceof Long) {
+// minimized.putLong(key, (Long) value);
+// } else if (value instanceof long[]) {
+// minimized.putLongArray(key, (long[]) value);
+// } else if (value instanceof String) {
+// minimized.putString(key, (String) value);
+// } else if (value instanceof String[]) {
+// minimized.putStringArray(key, (String[]) value);
+// } else if (value instanceof PersistableBundle) {
+// minimized.putPersistableBundle(key, (PersistableBundle) value);
+// } else {
+// continue;
+// }
+// }
+// }
+//
+// return minimized;
+// }
+
+ /** Builds a stable hashcode */
+ public static int getHashCode(@Nullable PersistableBundle bundle) {
+ if (bundle == null) {
+ return -1;
+ }
+
+ int iterativeHashcode = 0;
+ TreeSet<String> treeSet = new TreeSet<>(bundle.keySet());
+ for (String key : treeSet) {
+ Object val = bundle.get(key);
+ if (val instanceof PersistableBundle) {
+ iterativeHashcode =
+ Objects.hash(iterativeHashcode, key, getHashCode((PersistableBundle) val));
+ } else {
+ iterativeHashcode = Objects.hash(iterativeHashcode, key, val);
+ }
+ }
+
+ return iterativeHashcode;
+ }
+
+ /** Checks for persistable bundle equality */
+ public static boolean isEqual(
+ @Nullable PersistableBundle left, @Nullable PersistableBundle right) {
+ // Check for pointer equality & null equality
+ if (Objects.equals(left, right)) {
+ return true;
+ }
+
+ // If only one of the two is null, but not the other, not equal by definition.
+ if (Objects.isNull(left) != Objects.isNull(right)) {
+ return false;
+ }
+
+ if (!left.keySet().equals(right.keySet())) {
+ return false;
+ }
+
+ for (String key : left.keySet()) {
+ Object leftVal = left.get(key);
+ Object rightVal = right.get(key);
+
+ // Check for equality
+ if (Objects.equals(leftVal, rightVal)) {
+ continue;
+ } else if (Objects.isNull(leftVal) != Objects.isNull(rightVal)) {
+ // If only one of the two is null, but not the other, not equal by definition.
+ return false;
+ } else if (!Objects.equals(leftVal.getClass(), rightVal.getClass())) {
+ // If classes are different, not equal by definition.
+ return false;
+ }
+ if (leftVal instanceof PersistableBundle) {
+ if (!isEqual((PersistableBundle) leftVal, (PersistableBundle) rightVal)) {
+ return false;
+ }
+ } else if (leftVal.getClass().isArray()) {
+ if (leftVal instanceof boolean[]) {
+ if (!Arrays.equals((boolean[]) leftVal, (boolean[]) rightVal)) {
+ return false;
+ }
+ } else if (leftVal instanceof double[]) {
+ if (!Arrays.equals((double[]) leftVal, (double[]) rightVal)) {
+ return false;
+ }
+ } else if (leftVal instanceof int[]) {
+ if (!Arrays.equals((int[]) leftVal, (int[]) rightVal)) {
+ return false;
+ }
+ } else if (leftVal instanceof long[]) {
+ if (!Arrays.equals((long[]) leftVal, (long[]) rightVal)) {
+ return false;
+ }
+ } else if (!Arrays.equals((Object[]) leftVal, (Object[]) rightVal)) {
+ return false;
+ }
+ } else {
+ if (!Objects.equals(leftVal, rightVal)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+// /**
+// * Wrapper class around PersistableBundles to allow equality comparisons
+// *
+// * <p>This class exposes the minimal getters to retrieve values.
+// */
+// public static class PersistableBundleWrapper {
+// @NonNull
+// private final PersistableBundle mBundle;
+//
+// public PersistableBundleWrapper(@NonNull PersistableBundle bundle) {
+// mBundle = Objects.requireNonNull(bundle, "Bundle was null");
+// }
+//
+// /**
+// * Retrieves the integer associated with the provided key.
+// *
+// * @param key the string key to query
+// * @param defaultValue the value to return if key does not exist
+// * @return the int value, or the default
+// */
+// public int getInt(String key, int defaultValue) {
+// return mBundle.getInt(key, defaultValue);
+// }
+//
+// @Override
+// public int hashCode() {
+// return getHashCode(mBundle);
+// }
+//
+// @Override
+// public boolean equals(Object obj) {
+// if (!(obj instanceof PersistableBundleWrapper)) {
+// return false;
+// }
+//
+// final PersistableBundleWrapper other = (PersistableBundleWrapper) obj;
+//
+// return isEqual(mBundle, other.mBundle);
+// }
+// }
+}
diff --git a/indev_uwb_adaptation/jni/src/api.rs b/indev_uwb_adaptation/jni/src/api.rs
index b902dea2..937aff9d 100644
--- a/indev_uwb_adaptation/jni/src/api.rs
+++ b/indev_uwb_adaptation/jni/src/api.rs
@@ -412,8 +412,8 @@ fn send_raw_vendor_cmd(
return Err(Error::Parse(format!("Failed to convert payload {:?}", err)));
}
};
- let vendor_message = uwb_service.send_vendor_cmd(gid, oid, payload);
- // TODO(cante): figure out if we send RawVendorMessage back in a callback
+ let vendor_message = uwb_service.raw_uci_cmd(gid, oid, payload);
+ // TODO(cante): figure out if we send RawUciMessage back in a callback
todo!();
}
diff --git a/service/Android.bp b/service/Android.bp
index 4df8983a..2e49b29b 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -45,10 +45,8 @@ java_library {
installable: false,
defaults: ["service-uwb-common-defaults"],
srcs: [ ":service-uwb-srcs" ],
- // java_api_finder must accompany `srcs`
- plugins: ["java_api_finder"],
required: ["libuwb_uci_jni_rust"],
- sdk_version: "system_server_Tiramisu",
+ sdk_version: "system_server_current",
lint: {
strict_updatability_linting: true,
@@ -71,6 +69,7 @@ java_library {
"com.uwb.support.multichip",
"com.uwb.support.profile",
"com.uwb.support.oemextension",
+ "com.uwb.support.dltdoa",
"guava",
"modules-utils-shell-command-handler",
"modules-utils-handlerexecutor",
@@ -109,7 +108,7 @@ java_library {
"framework-connectivity.stubs.module_lib",
],
- sdk_version: "system_server_Tiramisu",
+ sdk_version: "system_server_current",
jarjar_rules: ":uwb-jarjar-rules",
optimize: {
diff --git a/service/java/com/android/server/uwb/DeviceConfigFacade.java b/service/java/com/android/server/uwb/DeviceConfigFacade.java
index db2662a3..500c2d8b 100644
--- a/service/java/com/android/server/uwb/DeviceConfigFacade.java
+++ b/service/java/com/android/server/uwb/DeviceConfigFacade.java
@@ -50,7 +50,7 @@ public class DeviceConfigFacade {
mRangingResultLogIntervalMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_UWB,
"ranging_result_log_interval_ms", DEFAULT_RANGING_RESULT_LOG_INTERVAL_MS);
mDeviceErrorBugreportEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_UWB,
- "device_error_bugreport_enabled", true);
+ "device_error_bugreport_enabled", false);
mBugReportMinIntervalMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_UWB,
"bug_report_min_interval_ms", DEFAULT_BUG_REPORT_MIN_INTERVAL_MS);
}
diff --git a/service/java/com/android/server/uwb/UwbConfigurationManager.java b/service/java/com/android/server/uwb/UwbConfigurationManager.java
index 788c2289..c62f22aa 100644
--- a/service/java/com/android/server/uwb/UwbConfigurationManager.java
+++ b/service/java/com/android/server/uwb/UwbConfigurationManager.java
@@ -83,7 +83,7 @@ public class UwbConfigurationManager {
UwbTlvData getAppConfig = mNativeUwbManager.getAppConfigurations(sessionId,
appConfigIds.length, appConfigIds.length, appConfigIds, chipId);
Log.i(TAG, "getAppConfigurations respData: "
- + getAppConfig != null ? getAppConfig.toString() : "null");
+ + (getAppConfig != null ? getAppConfig.toString() : "null"));
return decodeTLV(protocolName, getAppConfig, paramType);
}
@@ -95,7 +95,8 @@ public class UwbConfigurationManager {
Log.d(TAG, "getCapsInfo for protocol: " + protocolName);
UwbTlvData capsInfo = mNativeUwbManager.getCapsInfo(chipId);
- Log.i(TAG, "getCapsInfo respData: " + capsInfo != null ? capsInfo.toString() : "null");
+ Log.i(TAG, "getCapsInfo respData: "
+ + (capsInfo != null ? capsInfo.toString() : "null"));
return decodeTLV(protocolName, capsInfo, paramType);
}
diff --git a/service/java/com/android/server/uwb/UwbMetrics.java b/service/java/com/android/server/uwb/UwbMetrics.java
index 0e369f1e..f2302ab7 100644
--- a/service/java/com/android/server/uwb/UwbMetrics.java
+++ b/service/java/com/android/server/uwb/UwbMetrics.java
@@ -66,6 +66,7 @@ public class UwbMetrics {
private long mStartTimeSinceBootMs;
private int mInitLatencyMs;
private int mInitStatus;
+ private int mRangingStatus;
private int mActiveDuration;
private int mRangingCount;
private int mValidRangingCount;
@@ -133,6 +134,42 @@ public class UwbMetrics {
}
}
+ private void convertRangingStatus(int status) {
+ mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RANGING_GENERAL_FAILURE;
+ switch (status) {
+ case UwbUciConstants.STATUS_CODE_OK:
+ case UwbUciConstants.STATUS_CODE_OK_NEGATIVE_DISTANCE_REPORT:
+ mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RANGING_SUCCESS;
+ break;
+ case UwbUciConstants.STATUS_CODE_RANGING_TX_FAILED:
+ mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__TX_FAILED;
+ break;
+ case UwbUciConstants.STATUS_CODE_RANGING_RX_PHY_DEC_FAILED:
+ mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_PHY_DEC_FAILED;
+ break;
+ case UwbUciConstants.STATUS_CODE_RANGING_RX_PHY_TOA_FAILED:
+ mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_PHY_TOA_FAILED;
+ break;
+ case UwbUciConstants.STATUS_CODE_RANGING_RX_PHY_STS_FAILED:
+ mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_PHY_STS_FAILED;
+ break;
+ case UwbUciConstants.STATUS_CODE_RANGING_RX_MAC_DEC_FAILED:
+ mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_MAC_DEC_FAILED;
+ break;
+ case UwbUciConstants.STATUS_CODE_RANGING_RX_MAC_IE_DEC_FAILED:
+ mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_MAC_IE_DEC_FAILED;
+ break;
+ case UwbUciConstants.STATUS_CODE_RANGING_RX_MAC_IE_MISSING:
+ mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RX_MAC_IE_MISSING;
+ break;
+ case UwbUciConstants.STATUS_CODE_INVALID_PARAM:
+ case UwbUciConstants.STATUS_CODE_INVALID_RANGE:
+ case UwbUciConstants.STATUS_CODE_INVALID_MESSAGE_SIZE:
+ mRangingStatus = UwbStatsLog.UWB_START_RANGING__STATUS__RANGING_BAD_PARAMS;
+ break;
+ }
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -249,7 +286,12 @@ public class UwbMetrics {
session.mHasValidRangingSinceStart = false;
return;
}
+ session.convertRangingStatus(status);
session.mStartTimeSinceBootMs = mUwbInjector.getElapsedSinceBootMillis();
+ UwbStatsLog.write(UwbStatsLog.UWB_RANGING_START, uwbSession.getProfileType(),
+ session.mStsType, session.mIsInitiator,
+ session.mIsController, session.mIsDiscoveredByFramework, session.mIsOutOfBand,
+ session.mRangingStatus);
}
}
@@ -374,8 +416,7 @@ public class UwbMetrics {
session.mRangingCount++;
}
- int rangingStatus = measurement.getRangingStatus();
- if (rangingStatus != UwbUciConstants.STATUS_CODE_OK) {
+ if (!measurement.isStatusCodeOk()) {
return;
}
diff --git a/service/java/com/android/server/uwb/UwbServiceCore.java b/service/java/com/android/server/uwb/UwbServiceCore.java
index fa3ca1a5..f4b5f2b2 100644
--- a/service/java/com/android/server/uwb/UwbServiceCore.java
+++ b/service/java/com/android/server/uwb/UwbServiceCore.java
@@ -606,6 +606,14 @@ public class UwbServiceCore implements INativeUwbManager.DeviceNotification,
return status;
}
+ public void rangingRoundsUpdateDtTag(SessionHandle sessionHandle,
+ PersistableBundle params) throws RemoteException {
+ if (!isUwbEnabled()) {
+ throw new IllegalStateException("Uwb is not enabled");
+ }
+ mSessionManager.rangingRoundsUpdateDtTag(sessionHandle, params);
+ }
+
private class EnableDisableTask extends Handler {
EnableDisableTask(Looper looper) {
diff --git a/service/java/com/android/server/uwb/UwbServiceImpl.java b/service/java/com/android/server/uwb/UwbServiceImpl.java
index a97eaea4..63dbb030 100644
--- a/service/java/com/android/server/uwb/UwbServiceImpl.java
+++ b/service/java/com/android/server/uwb/UwbServiceImpl.java
@@ -19,6 +19,7 @@ package com.android.server.uwb;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
+import android.app.admin.SecurityLog;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -29,6 +30,7 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import android.uwb.IUwbAdapter;
@@ -66,6 +68,8 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
private final UwbSettingsStore mUwbSettingsStore;
private final UwbServiceCore mUwbServiceCore;
+ private boolean mUwbUserRestricted;
+
UwbServiceImpl(@NonNull Context context, @NonNull UwbInjector uwbInjector) {
mContext = context;
@@ -73,6 +77,8 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
mUwbSettingsStore = uwbInjector.getUwbSettingsStore();
mUwbServiceCore = uwbInjector.getUwbServiceCore();
registerAirplaneModeReceiver();
+ mUwbUserRestricted = isUwbUserRestricted();
+ registerUserRestrictionsReceiver();
}
/**
@@ -129,6 +135,31 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
"UwbService");
}
+ private void onUserRestrictionsChanged() {
+ if (mUwbUserRestricted == isUwbUserRestricted()) {
+ return;
+ }
+
+ Log.i(TAG, "Disallow UWB user restriction changed from " + mUwbUserRestricted + " to "
+ + !mUwbUserRestricted + ".");
+ mUwbUserRestricted = !mUwbUserRestricted;
+ logSecurityUwbUserRestrictionChanged(mUwbUserRestricted);
+
+ try {
+ mUwbServiceCore.setEnabled(isUwbEnabled());
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to set UWB Adapter state.", e);
+ }
+ }
+
+ private void logSecurityUwbUserRestrictionChanged(boolean restricted) {
+ if (restricted) {
+ SecurityLog.writeEvent(SecurityLog.TAG_USER_RESTRICTION_ADDED);
+ } else {
+ SecurityLog.writeEvent(SecurityLog.TAG_USER_RESTRICTION_ADDED);
+ }
+ }
+
@Override
public void registerAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks)
throws RemoteException {
@@ -283,6 +314,17 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
mUwbServiceCore.sendData(sessionHandle, remoteDeviceAddress, params, data);
}
+ // TODO: Add @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) after ag/19901449
+ @Override
+ public void onRangingRoundsUpdateDtTag(SessionHandle sessionHandle,
+ PersistableBundle parameters) throws RemoteException {
+ if (!SdkLevel.isAtLeastU()) {
+ throw new UnsupportedOperationException();
+ }
+ enforceUwbPrivilegedPermission();
+ mUwbServiceCore.rangingRoundsUpdateDtTag(sessionHandle, parameters);
+ }
+
@Override
public synchronized int getAdapterState() throws RemoteException {
return mUwbServiceCore.getAdapterState();
@@ -292,7 +334,8 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
public synchronized void setEnabled(boolean enabled) throws RemoteException {
enforceUwbPrivilegedPermission();
persistUwbToggleState(enabled);
- // Shell command from rooted shell, we allow UWB toggle on even if APM mode is on.
+ // Shell command from rooted shell, we allow UWB toggle on even if APM mode and
+ // user restriction are on.
if (Binder.getCallingUid() == Process.ROOT_UID) {
mUwbServiceCore.setEnabled(isUwbToggleEnabled());
return;
@@ -396,7 +439,7 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
@NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
@NonNull String[] args) {
- UwbShellCommand shellCommand = mUwbInjector.makeUwbShellCommand(this);
+ UwbShellCommand shellCommand = mUwbInjector.makeUwbShellCommand(this);
return shellCommand.exec(this, in.getFileDescriptor(), out.getFileDescriptor(),
err.getFileDescriptor(), args);
}
@@ -415,9 +458,25 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
}
- /** Returns true if UWB is enabled - based on UWB and APM toggle */
+ /** Returns true if UWB has user restriction set. */
+ private boolean isUwbUserRestricted() {
+ if (!SdkLevel.isAtLeastU()) {
+ return false; // older platforms did not have a uwb user restriction.
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mUwbInjector.getUserManager().getUserRestrictions().getBoolean(
+ // Not available on tm-mainline-prod
+ // UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO);
+ "no_ultra_wideband_radio");
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /** Returns true if UWB is enabled - based on UWB, APM toggle and user restriction */
private boolean isUwbEnabled() {
- return isUwbToggleEnabled() && !isAirplaneModeOn();
+ return isUwbToggleEnabled() && !isAirplaneModeOn() && !isUwbUserRestricted();
}
private void registerAirplaneModeReceiver() {
@@ -429,6 +488,18 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
}, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
}
+ private void registerUserRestrictionsReceiver() {
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRestrictionsChanged();
+ }
+ },
+ new IntentFilter(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)
+ );
+ }
+
private void handleAirplaneModeEvent() {
try {
mUwbServiceCore.setEnabled(isUwbEnabled());
diff --git a/service/java/com/android/server/uwb/UwbSessionManager.java b/service/java/com/android/server/uwb/UwbSessionManager.java
index dad92670..3f9e8184 100644
--- a/service/java/com/android/server/uwb/UwbSessionManager.java
+++ b/service/java/com/android/server/uwb/UwbSessionManager.java
@@ -17,8 +17,15 @@ package com.android.server.uwb;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+import static com.android.server.uwb.data.UwbUciConstants.MAC_ADDRESSING_MODE_EXTENDED;
+import static com.android.server.uwb.data.UwbUciConstants.MAC_ADDRESSING_MODE_SHORT;
+import static com.android.server.uwb.data.UwbUciConstants.RANGING_DEVICE_ROLE_OBSERVER;
import static com.android.server.uwb.data.UwbUciConstants.REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS;
+import static com.android.server.uwb.data.UwbUciConstants.ROUND_USAGE_OWR_AOA_MEASUREMENT;
import static com.android.server.uwb.data.UwbUciConstants.UWB_DEVICE_EXT_MAC_ADDRESS_LEN;
+import static com.android.server.uwb.data.UwbUciConstants.UWB_DEVICE_SHORT_MAC_ADDRESS_LEN;
+import static com.android.server.uwb.data.UwbUciConstants.UWB_SESSION_STATE_ACTIVE;
+import static com.android.server.uwb.util.DataTypeConversionUtil.macAddressByteArrayToLong;
import static com.google.uwb.support.fira.FiraParams.MULTICAST_LIST_UPDATE_ACTION_ADD;
import static com.google.uwb.support.fira.FiraParams.MULTICAST_LIST_UPDATE_ACTION_DELETE;
@@ -49,6 +56,7 @@ import android.uwb.UwbAddress;
import androidx.annotation.VisibleForTesting;
import com.android.server.uwb.advertisement.UwbAdvertiseManager;
+import com.android.server.uwb.data.DtTagUpdateRangingRoundsStatus;
import com.android.server.uwb.data.UwbMulticastListUpdateStatus;
import com.android.server.uwb.data.UwbOwrAoaMeasurement;
import com.android.server.uwb.data.UwbRangingData;
@@ -56,7 +64,6 @@ import com.android.server.uwb.data.UwbTwoWayMeasurement;
import com.android.server.uwb.data.UwbUciConstants;
import com.android.server.uwb.jni.INativeUwbManager;
import com.android.server.uwb.jni.NativeUwbManager;
-import com.android.server.uwb.params.TlvUtil;
import com.android.server.uwb.proto.UwbStatsLog;
import com.android.server.uwb.util.ArrayUtils;
import com.android.server.uwb.util.UwbUtil;
@@ -66,6 +73,8 @@ import com.google.uwb.support.ccc.CccOpenRangingParams;
import com.google.uwb.support.ccc.CccParams;
import com.google.uwb.support.ccc.CccRangingStartedParams;
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.FiraOpenSessionParams;
import com.google.uwb.support.fira.FiraParams;
import com.google.uwb.support.fira.FiraRangingReconfigureParams;
@@ -74,6 +83,7 @@ import com.google.uwb.support.oemextension.SessionStatus;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
@@ -82,7 +92,10 @@ import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
@@ -98,11 +111,13 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
@VisibleForTesting
public static final int SESSION_RECONFIG_RANGING = 4;
@VisibleForTesting
- public static final int SESSION_CLOSE = 5;
+ public static final int SESSION_DEINIT = 5;
@VisibleForTesting
public static final int SESSION_ON_DEINIT = 6;
@VisibleForTesting
public static final int SESSION_SEND_DATA = 7;
+ @VisibleForTesting
+ public static final int SESSION_UPDATE_ACTIVE_RR_DT_TAG = 8;
// TODO: don't expose the internal field for testing.
@VisibleForTesting
@@ -197,7 +212,7 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
if (rangingData.getRangingMeasuresType()
== UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY) {
for (UwbTwoWayMeasurement measure : rangingData.getRangingTwoWayMeasures()) {
- if (measure.getRangingStatus() == UwbUciConstants.STATUS_CODE_OK) {
+ if (measure.isStatusCodeOk()) {
return false;
}
}
@@ -237,16 +252,18 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
public void onDataReceived(
long sessionId, int status, long sequenceNum,
byte[] address, int sourceEndPoint, int destEndPoint, byte[] data) {
- Log.d(TAG, "onDataReceived - Data: " + UwbUtil.toHexString(data));
+ Log.d(TAG, "onDataReceived - address: " + UwbUtil.toHexString(address)
+ + ", Data: " + UwbUtil.toHexString(data));
- // Size of address is always expected to be 8(EXTENDED_ADDRESS_BYTE_LENGTH). It can contain
- // the MacAddress in short format however (2 LSB with MacAddress, 6 MSB zeroed out).
+ // Size of address in the UCI Packet for DATA_MESSAGE_RCV is always expected to be 8
+ // (EXTENDED_ADDRESS_BYTE_LENGTH). It can contain the MacAddress in short format however
+ // (2 LSB with MacAddress, 6 MSB zeroed out).
if (address.length != UWB_DEVICE_EXT_MAC_ADDRESS_LEN) {
- Log.e(TAG, "onDataReceived(): Received data for sessionId=" + sessionId
+ Log.e(TAG, "onDataReceived(): Received data for sessionId = " + sessionId
+ ", with unexpected MacAddress length = " + address.length);
return;
}
- Long longAddress = ByteBuffer.wrap(address).getLong();
+ Long longAddress = macAddressByteArrayToLong(address);
ReceivedDataInfo info = new ReceivedDataInfo();
info.sessionId = sessionId;
@@ -455,7 +472,7 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
Log.i(TAG, "deinitSession() - sessionId: " + sessionId
+ ", sessionHandle: " + sessionHandle);
UwbSession uwbSession = getUwbSession(sessionId);
- mEventTask.execute(SESSION_CLOSE, uwbSession);
+ mEventTask.execute(SESSION_DEINIT, uwbSession);
return;
}
@@ -556,10 +573,23 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
return;
}
+ if (!isValidUwbSessionForOwrAoaRanging(uwbSession)) {
+ return;
+ }
+
+ // Record the OWR Aoa Measurement from the RANGE_DATA_NTF.
UwbOwrAoaMeasurement uwbOwrAoaMeasurement = rangingData.getRangingOwrAoaMeasure();
mAdvertiseManager.updateAdvertiseTarget(uwbOwrAoaMeasurement);
- byte[] macAddress = uwbOwrAoaMeasurement.getMacAddress();
+ byte[] macAddress = getValidMacAddressFromOwrAoaMeasurement(
+ rangingData, uwbOwrAoaMeasurement);
+ if (macAddress == null) {
+ Log.i(TAG, "OwR Aoa UwbSession: Invalid MacAddress for remote device");
+ return;
+ }
+ uwbSession.setRemoteMacAddress(macAddress);
+
+ // Get any application payload data received in this OWR AOA ranging session and notify it.
ReceivedDataInfo receivedDataInfo = getReceivedDataInfo(macAddress);
if (receivedDataInfo == null) {
return;
@@ -571,23 +601,30 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
}
if (mAdvertiseManager.isPointedTarget(macAddress)) {
- UwbAddress uwbAddress = UwbAddress.fromBytes(TlvUtil.getReverseBytes(macAddress));
+ UwbAddress uwbAddress = UwbAddress.fromBytes(macAddress);
mSessionNotificationManager.onDataReceived(
uwbSession, uwbAddress, new PersistableBundle(), receivedDataInfo.payload);
+ mAdvertiseManager.removeAdvertiseTarget(macAddress);
}
}
+ @Nullable
+ private byte[] getValidMacAddressFromOwrAoaMeasurement(UwbRangingData rangingData,
+ UwbOwrAoaMeasurement uwbOwrAoaMeasurement) {
+ byte[] macAddress = uwbOwrAoaMeasurement.getMacAddress();
+ if (rangingData.getMacAddressMode() == MAC_ADDRESSING_MODE_SHORT) {
+ return (macAddress.length == UWB_DEVICE_SHORT_MAC_ADDRESS_LEN) ? macAddress : null;
+ } else if (rangingData.getMacAddressMode() == MAC_ADDRESSING_MODE_EXTENDED) {
+ return (macAddress.length == UWB_DEVICE_EXT_MAC_ADDRESS_LEN) ? macAddress : null;
+ }
+ return null;
+ }
+
/** Get any received data for the given device MacAddress */
@VisibleForTesting
public ReceivedDataInfo getReceivedDataInfo(byte[] macAddress) {
- /* Extend the size of addr because addr size from onDataReceived() is always 8 */
- byte[] extendedAddr = new byte[] {0, 0, 0, 0, 0, 0, 0, 0};
- for (int i = 0; i < macAddress.length; i++) {
- extendedAddr[i] = macAddress[i];
- }
-
- Long longAddress = ByteBuffer.wrap(extendedAddr).getLong();
- return mReceivedDataMap.get(longAddress);
+ // Convert the macAddress to a long as the address could be in short or extended format.
+ return mReceivedDataMap.get(macAddressByteArrayToLong(macAddress));
}
public boolean isExistedSession(SessionHandle sessionHandle) {
@@ -711,14 +748,94 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
public byte[] data;
}
+ private static final class RangingRoundsUpdateDtTagInfo {
+ public SessionHandle sessionHandle;
+ public PersistableBundle params;
+ }
+
+ /** DT Tag ranging round update */
+ public void rangingRoundsUpdateDtTag(SessionHandle sessionHandle,
+ PersistableBundle bundle) {
+ RangingRoundsUpdateDtTagInfo info = new RangingRoundsUpdateDtTagInfo();
+ info.sessionHandle = sessionHandle;
+ info.params = bundle;
+
+ mEventTask.execute(SESSION_UPDATE_ACTIVE_RR_DT_TAG, info);
+ }
+
+ /** Handle ranging rounds update for DT Tag */
+ public void handleRangingRoundsUpdateDtTag(RangingRoundsUpdateDtTagInfo info) {
+ SessionHandle sessionHandle = info.sessionHandle;
+ Integer sessionId = getSessionId(sessionHandle);
+ if (sessionId == null) {
+ Log.i(TAG, "UwbSessionId not found");
+ return;
+ }
+ UwbSession uwbSession = getUwbSession(sessionId);
+ if (uwbSession == null) {
+ Log.i(TAG, "UwbSession not found");
+ return;
+ }
+ DlTDoARangingRoundsUpdate dlTDoARangingRoundsUpdate = DlTDoARangingRoundsUpdate
+ .fromBundle(info.params);
+
+ if (dlTDoARangingRoundsUpdate.getSessionId() != getSessionId(sessionHandle)) {
+ throw new IllegalArgumentException("Wrong session ID");
+ }
+
+ FutureTask<DtTagUpdateRangingRoundsStatus> rangingRoundsUpdateTask = new FutureTask<>(
+ () -> {
+ synchronized (uwbSession.getWaitObj()) {
+ return mNativeUwbManager.sessionUpdateActiveRoundsDtTag(
+ (int) dlTDoARangingRoundsUpdate.getSessionId(),
+ dlTDoARangingRoundsUpdate.getNoOfActiveRangingRounds(),
+ dlTDoARangingRoundsUpdate.getRangingRoundIndexes(),
+ uwbSession.getChipId());
+ }
+ }
+ );
+
+ DtTagUpdateRangingRoundsStatus status = new DtTagUpdateRangingRoundsStatus(
+ UwbUciConstants.STATUS_CODE_ERROR_ROUND_INDEX_NOT_ACTIVATED,
+ 0,
+ new byte[]{});
+
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ executor.submit(rangingRoundsUpdateTask);
+ try {
+ status = rangingRoundsUpdateTask.get(IUwbAdapter
+ .RANGING_ROUNDS_UPDATE_DT_TAG_THRESHOLD_MS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ Log.i(TAG, "Failed to update ranging rounds for Dt tag - status : TIMEOUT");
+ executor.shutdownNow();
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace();
+ }
+ PersistableBundle params = new DlTDoARangingRoundsUpdateStatus.Builder()
+ .setStatus(status.getStatus())
+ .setNoOfActiveRangingRounds(status.getNoOfActiveRangingRounds())
+ .setRangingRoundIndexes(status.getRangingRoundIndexes())
+ .build()
+ .toBundle();
+ mSessionNotificationManager.onRangingRoundsUpdateStatus(uwbSession, params);
+ }
+
void removeSession(UwbSession uwbSession) {
if (uwbSession != null) {
uwbSession.getBinder().unlinkToDeath(uwbSession, 0);
removeFromNonPrivilegedUidToFiraSessionTableIfNecessary(uwbSession);
+ removeAdvertiserData(uwbSession);
mSessionTable.remove(uwbSession.getSessionId());
}
}
+ private void removeAdvertiserData(UwbSession uwbSession) {
+ byte[] remoteMacAddress = uwbSession.getRemoteMacAddress();
+ if (remoteMacAddress != null) {
+ mAdvertiseManager.removeAdvertiseTarget(remoteMacAddress);
+ }
+ }
+
void addToNonPrivilegedUidToFiraSessionTableIfNecessary(@NonNull UwbSession uwbSession) {
if (getSessionType(uwbSession.getProtocolName()) == UwbUciConstants.SESSION_TYPE_RANGING) {
AttributionSource nonPrivilegedAppAttrSource =
@@ -806,9 +923,9 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
break;
}
- case SESSION_CLOSE: {
+ case SESSION_DEINIT: {
UwbSession uwbSession = (UwbSession) msg.obj;
- handleClose(uwbSession);
+ handleDeInit(uwbSession);
break;
}
@@ -825,6 +942,13 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
break;
}
+ case SESSION_UPDATE_ACTIVE_RR_DT_TAG: {
+ Log.d(TAG, "SESSION_UPDATE_ACTIVE_RR_DT_TAG");
+ RangingRoundsUpdateDtTagInfo info = (RangingRoundsUpdateDtTagInfo) msg.obj;
+ handleRangingRoundsUpdateDtTag(info);
+ break;
+ }
+
default: {
Log.d(TAG, "EventTask : Undefined Task");
break;
@@ -1031,6 +1155,7 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
}
// Reset all UWB session timers when the session is stopped.
uwbSession.stopTimers();
+ removeAdvertiserData(uwbSession);
}
private void handleReconfigure(UwbSession uwbSession, @Nullable Params param,
@@ -1181,9 +1306,9 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
}
}
- private void handleClose(UwbSession uwbSession) {
+ private void handleDeInit(UwbSession uwbSession) {
// TODO(b/211445008): Consolidate to a single uwb thread.
- FutureTask<Integer> closeTask = new FutureTask<>(
+ FutureTask<Integer> deInitTask = new FutureTask<>(
(Callable<Integer>) () -> {
int status = UwbUciConstants.STATUS_CODE_FAILED;
synchronized (uwbSession.getWaitObj()) {
@@ -1202,7 +1327,7 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
int status = UwbUciConstants.STATUS_CODE_FAILED;
try {
- status = mUwbInjector.runTaskOnSingleThreadExecutor(closeTask,
+ status = mUwbInjector.runTaskOnSingleThreadExecutor(deInitTask,
IUwbAdapter.RANGING_SESSION_CLOSE_THRESHOLD_MS);
} catch (TimeoutException e) {
Log.i(TAG, "Failed to Stop Ranging - status : TIMEOUT");
@@ -1212,7 +1337,7 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
}
mUwbMetrics.logRangingCloseEvent(uwbSession, status);
- // Reset all UWB session timers when the session is closed.
+ // Reset all UWB session timers when the session is de-initialized (ie, closed).
uwbSession.stopTimers();
removeSession(uwbSession);
Log.i(TAG, "deinit finish : status :" + status);
@@ -1251,6 +1376,14 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
FutureTask<Integer> sendDataTask = new FutureTask<>((Callable<Integer>) () -> {
int sendDataStatus = UwbUciConstants.STATUS_CODE_FAILED;
synchronized (uwbSession.getWaitObj()) {
+ if (!isValidUwbSessionForApplicationDataTransfer(uwbSession)) {
+ sendDataStatus = UwbUciConstants.STATUS_CODE_FAILED;
+ Log.i(TAG, "UwbSession not in active state");
+ mSessionNotificationManager.onDataSendFailed(
+ uwbSession, sendDataInfo.remoteDeviceAddress, sendDataStatus,
+ sendDataInfo.params);
+ return sendDataStatus;
+ }
if (!isValidSendDataInfo(sendDataInfo)) {
sendDataStatus = UwbUciConstants.STATUS_CODE_INVALID_PARAM;
mSessionNotificationManager.onDataSendFailed(
@@ -1296,6 +1429,31 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
}
}
+ private boolean isValidUwbSessionForOwrAoaRanging(UwbSession uwbSession) {
+ Params params = uwbSession.getParams();
+ if (params instanceof FiraOpenSessionParams) {
+ FiraOpenSessionParams firaParams = (FiraOpenSessionParams) params;
+ if (firaParams.getRangingRoundUsage() != ROUND_USAGE_OWR_AOA_MEASUREMENT) {
+ Log.i(TAG, "OwR Aoa UwbSession: Invalid ranging round usage value = "
+ + firaParams.getRangingRoundUsage());
+ return false;
+ }
+ if (firaParams.getDeviceRole() != RANGING_DEVICE_ROLE_OBSERVER) {
+ Log.i(TAG, "OwR Aoa UwbSession: Invalid device role value = "
+ + firaParams.getDeviceRole());
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isValidUwbSessionForApplicationDataTransfer(UwbSession uwbSession) {
+ // The session state must be SESSION_STATE_ACTIVE, as that's required to transmit or receive
+ // application data.
+ return uwbSession != null && uwbSession.getSessionState() == UWB_SESSION_STATE_ACTIVE;
+ }
+
private boolean isValidSendDataInfo(SendDataInfo sendDataInfo) {
if (sendDataInfo.data == null) {
return false;
@@ -1325,6 +1483,7 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
private final AttributionSource mAttributionSource;
private final SessionHandle mSessionHandle;
private final int mSessionId;
+ private byte[] mRemoteMacAddress;
private final IUwbRangingCallbacks mIUwbRangingCallbacks;
private final String mProtocolName;
private final IBinder mIBinder;
@@ -1512,6 +1671,14 @@ public class UwbSessionManager implements INativeUwbManager.SessionNotification
this.mSessionState = state;
}
+ public byte[] getRemoteMacAddress() {
+ return mRemoteMacAddress;
+ }
+
+ public void setRemoteMacAddress(byte[] remoteMacAddress) {
+ this.mRemoteMacAddress = Arrays.copyOf(remoteMacAddress, remoteMacAddress.length);
+ }
+
public void setMulticastListUpdateStatus(
UwbMulticastListUpdateStatus multicastListUpdateStatus) {
mMulticastListUpdateStatus = multicastListUpdateStatus;
diff --git a/service/java/com/android/server/uwb/UwbSessionNotificationManager.java b/service/java/com/android/server/uwb/UwbSessionNotificationManager.java
index d7bf201e..0c095dd8 100644
--- a/service/java/com/android/server/uwb/UwbSessionNotificationManager.java
+++ b/service/java/com/android/server/uwb/UwbSessionNotificationManager.java
@@ -30,6 +30,7 @@ import android.uwb.SessionHandle;
import android.uwb.UwbAddress;
import com.android.server.uwb.UwbSessionManager.UwbSession;
+import com.android.server.uwb.data.UwbDlTDoAMeasurement;
import com.android.server.uwb.data.UwbOwrAoaMeasurement;
import com.android.server.uwb.data.UwbRangingData;
import com.android.server.uwb.data.UwbTwoWayMeasurement;
@@ -40,6 +41,7 @@ import com.android.server.uwb.util.UwbUtil;
import com.google.uwb.support.base.Params;
import com.google.uwb.support.ccc.CccParams;
import com.google.uwb.support.ccc.CccRangingReconfiguredParams;
+import com.google.uwb.support.dltdoa.DlTDoAMeasurement;
import com.google.uwb.support.fira.FiraOpenSessionParams;
import com.google.uwb.support.fira.FiraParams;
import com.google.uwb.support.oemextension.RangingReportMetadata;
@@ -375,12 +377,29 @@ public class UwbSessionNotificationManager {
}
}
+ /** Notify the response for Ranging rounds update status for Dt Tag. */
+ public void onRangingRoundsUpdateStatus(
+ UwbSession uwbSession, PersistableBundle parameters) {
+ SessionHandle sessionHandle = uwbSession.getSessionHandle();
+ IUwbRangingCallbacks uwbRangingCallbacks = uwbSession.getIUwbRangingCallbacks();
+ try {
+ uwbRangingCallbacks.onRangingRoundsUpdateDtTagStatus(sessionHandle,
+ parameters);
+ Log.i(TAG, "IUwbRangingCallbacks - onRangingRoundsUpdateDtTagStatus");
+ } catch (Exception e) {
+ Log.e(TAG, "IUwbRangingCallbacks - onRangingRoundsUpdateDtTagStatus : Failed");
+ e.printStackTrace();
+ }
+ }
+
private static RangingReport getRangingReport(
@NonNull UwbRangingData rangingData, String protocolName,
Params sessionParams, long elapsedRealtimeNanos) {
if (rangingData.getRangingMeasuresType() != UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY
&& rangingData.getRangingMeasuresType()
- != UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA) {
+ != UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA
+ && rangingData.getRangingMeasuresType()
+ != UwbUciConstants.RANGING_MEASUREMENT_TYPE_DL_TDOA) {
return null;
}
boolean isAoaAzimuthEnabled = true;
@@ -439,7 +458,7 @@ public class UwbSessionNotificationManager {
List<RangingMeasurement> rangingMeasurements = new ArrayList<>();
UwbTwoWayMeasurement[] uwbTwoWayMeasurement = rangingData.getRangingTwoWayMeasures();
for (int i = 0; i < rangingData.getNoOfRangingMeasures(); ++i) {
- int rangingStatus = uwbTwoWayMeasurement[i].getRangingStatus();
+ int rangingStatus = uwbTwoWayMeasurement[i].convertStatusCode();
RangingMeasurement.Builder rangingMeasurementBuilder = buildRangingMeasurement(
uwbTwoWayMeasurement[i].getMacAddress(), rangingStatus,
@@ -449,7 +468,7 @@ public class UwbSessionNotificationManager {
rangingMeasurementBuilder.setRssiDbm(rssi);
}
- if (rangingStatus == FiraParams.STATUS_CODE_OK) {
+ if (uwbTwoWayMeasurement[i].isStatusCodeOk()) {
// Distance measurement is mandatory
rangingMeasurementBuilder.setDistanceMeasurement(
buildDistanceMeasurement(uwbTwoWayMeasurement[i].getDistance()));
@@ -514,6 +533,57 @@ public class UwbSessionNotificationManager {
}
rangingReportBuilder.addMeasurement(rangingMeasurementBuilder.build());
+ } else if (rangingData.getRangingMeasuresType()
+ == UwbUciConstants.RANGING_MEASUREMENT_TYPE_DL_TDOA) {
+ List<RangingMeasurement> rangingMeasurements = new ArrayList<>();
+ UwbDlTDoAMeasurement[] uwbDlTDoAMeasurements = rangingData.getUwbDlTDoAMeasurements();
+ for (int i = 0; i < rangingData.getNoOfRangingMeasures(); ++i) {
+ int rangingStatus = uwbDlTDoAMeasurements[i].getStatus();
+
+ RangingMeasurement.Builder rangingMeasurementBuilder = buildRangingMeasurement(
+ uwbDlTDoAMeasurements[i].getMacAddress(), rangingStatus,
+ elapsedRealtimeNanos, uwbDlTDoAMeasurements[i].getNLoS());
+ int rssi = uwbDlTDoAMeasurements[i].getRssi();
+ if (rssi < 0) {
+ rangingMeasurementBuilder.setRssiDbm(rssi);
+ }
+ if (rangingStatus == FiraParams.STATUS_CODE_OK) {
+ AngleOfArrivalMeasurement angleOfArrivalMeasurement =
+ computeAngleOfArrivalMeasurement(
+ isAoaAzimuthEnabled, isAoaElevationEnabled,
+ uwbDlTDoAMeasurements[i].getAoaAzimuth(),
+ uwbDlTDoAMeasurements[i].getAoaAzimuthFom(),
+ uwbDlTDoAMeasurements[i].getAoaElevation(),
+ uwbDlTDoAMeasurements[i].getAoaElevationFom());
+ if (angleOfArrivalMeasurement != null) {
+ rangingMeasurementBuilder.setAngleOfArrivalMeasurement(
+ angleOfArrivalMeasurement);
+ }
+ }
+ DlTDoAMeasurement dlTDoAMeasurement = new DlTDoAMeasurement.Builder()
+ .setMessageType(uwbDlTDoAMeasurements[i].getMessageType())
+ .setMessageControl(uwbDlTDoAMeasurements[i].getMessageControl())
+ .setBlockIndex(uwbDlTDoAMeasurements[i].getBlockIndex())
+ .setNLoS(uwbDlTDoAMeasurements[i].getNLoS())
+ .setTxTimestamp(uwbDlTDoAMeasurements[i].getTxTimestamp())
+ .setRxTimestamp(uwbDlTDoAMeasurements[i].getRxTimestamp())
+ .setAnchorCfo(uwbDlTDoAMeasurements[i].getAnchorCfo())
+ .setCfo(uwbDlTDoAMeasurements[i].getCfo())
+ .setInitiatorReplyTime(uwbDlTDoAMeasurements[i].getInitiatorReplyTime())
+ .setResponderReplyTime(uwbDlTDoAMeasurements[i].getResponderReplyTime())
+ .setInitiatorResponderTof(uwbDlTDoAMeasurements[i]
+ .getInitiatorResponderTof())
+ .setAnchorLocation(uwbDlTDoAMeasurements[i].getAnchorLocation())
+ .setActiveRangingRounds(uwbDlTDoAMeasurements[i].getActiveRangingRounds())
+ .build();
+
+ rangingMeasurementBuilder.setRangingMeasurementMetadata(
+ dlTDoAMeasurement.toBundle());
+
+ rangingMeasurements.add(rangingMeasurementBuilder.build());
+ }
+
+ rangingReportBuilder.addMeasurements(rangingMeasurements);
}
return rangingReportBuilder.build();
}
diff --git a/service/java/com/android/server/uwb/UwbShellCommand.java b/service/java/com/android/server/uwb/UwbShellCommand.java
index 599c0e66..af5b0c3d 100644
--- a/service/java/com/android/server/uwb/UwbShellCommand.java
+++ b/service/java/com/android/server/uwb/UwbShellCommand.java
@@ -329,6 +329,9 @@ public class UwbShellCommand extends BasicShellCommandHandler {
public void onServiceDiscovered(SessionHandle sessionHandle, PersistableBundle params) {}
public void onServiceConnected(SessionHandle sessionHandle, PersistableBundle params) {}
+
+ public void onRangingRoundsUpdateDtTagStatus(SessionHandle sessionHandle,
+ PersistableBundle params) {}
}
diff --git a/service/java/com/android/server/uwb/UwbTestUtils.java b/service/java/com/android/server/uwb/UwbTestUtils.java
index 53c02de9..c5358841 100644
--- a/service/java/com/android/server/uwb/UwbTestUtils.java
+++ b/service/java/com/android/server/uwb/UwbTestUtils.java
@@ -16,6 +16,8 @@
package com.android.server.uwb;
+import static com.android.server.uwb.data.UwbUciConstants.MAC_ADDRESSING_MODE_SHORT;
+import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_DL_TDOA;
import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA;
import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY;
import static com.android.server.uwb.util.UwbUtil.convertFloatToQFormat;
@@ -30,31 +32,34 @@ import android.uwb.RangingMeasurement;
import android.uwb.RangingReport;
import android.uwb.UwbAddress;
+import com.android.server.uwb.data.UwbDlTDoAMeasurement;
import com.android.server.uwb.data.UwbOwrAoaMeasurement;
import com.android.server.uwb.data.UwbRangingData;
import com.android.server.uwb.data.UwbTwoWayMeasurement;
import com.android.server.uwb.params.TlvUtil;
+import com.google.uwb.support.dltdoa.DlTDoAMeasurement;
import com.google.uwb.support.fira.FiraParams;
import com.google.uwb.support.oemextension.RangingReportMetadata;
public class UwbTestUtils {
public static final int TEST_SESSION_ID = 7;
public static final int TEST_SESSION_ID_2 = 8;
- public static final byte[] PEER_SHORT_MAC_ADDRESS = {0x1, 0x3};
+ public static final byte[] PEER_SHORT_MAC_ADDRESS = {0x35, 0x37};
+ public static final byte[] PEER_EXTENDED_SHORT_MAC_ADDRESS =
+ {0x35, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
public static final byte[] PEER_EXTENDED_MAC_ADDRESS =
- new byte[] {0x12, 0x14, 0x16, 0x18, 0x31, 0x33, 0x35, 0x37};
- public static final byte[] PEER_EXTENDED_MAC_ADDRESS_REVERSE_BYTES =
- new byte[] {0x37, 0x35, 0x33, 0x31, 0x18, 0x16, 0x14, 0x12};
+ {0x12, 0x14, 0x16, 0x18, 0x31, 0x33, 0x35, 0x37};
public static final byte[] PEER_EXTENDED_MAC_ADDRESS_2 =
- new byte[] {0x2, 0x4, 0x6, 0x8, 0x1, 0x3, 0x5, 0x7};
- public static final byte[] PEER_BAD_MAC_ADDRESS = new byte[] {0x12, 0x14, 0x16, 0x18};
- public static final byte[] PEER_EXTENDED_SHORT_MAC_ADDRESS =
- new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x37};
- public static final UwbAddress PEER_UWB_ADDRESS = UwbAddress.fromBytes(
- PEER_EXTENDED_MAC_ADDRESS_REVERSE_BYTES);
+ {0x2, 0x4, 0x6, 0x8, 0x1, 0x3, 0x5, 0x7};
+ public static final byte[] PEER_BAD_MAC_ADDRESS = {0x12, 0x14, 0x16, 0x18};
+ public static final UwbAddress PEER_EXTENDED_UWB_ADDRESS = UwbAddress.fromBytes(
+ PEER_EXTENDED_MAC_ADDRESS);
+ public static final UwbAddress PEER_SHORT_UWB_ADDRESS = UwbAddress.fromBytes(
+ PEER_SHORT_MAC_ADDRESS);
public static final PersistableBundle PERSISTABLE_BUNDLE = new PersistableBundle();
public static final byte[] DATA_PAYLOAD = new byte[] {0x13, 0x15, 0x18};
+ public static final int RANGING_MEASUREMENT_TYPE_UNDEFINED = 0; // RFU in spec
private static final byte[] TEST_RAW_NTF_DATA = {0x10, 0x01, 0x05};
private static final long TEST_SEQ_COUNTER = 5;
@@ -76,20 +81,35 @@ public class UwbTestUtils {
private static final int TEST_FRAME_SEQUENCE_NUMBER = 1;
private static final int TEST_BLOCK_IDX = 100;
private static final int TEST_SLOT_IDX = 10;
+ private static final int TEST_MESSAGE_TYPE = 1;
+ private static final int TEST_MESSAGE_CONTROL = 1331;
+ private static final int TEST_BLOCK_INDEX = 5;
+ private static final int TEST_ROUND_INDEX = 1;
+ private static final long TEST_TIMESTAMP = 500_000L;
+ private static final int TEST_ANCHOR_CFO = 100;
+ private static final int TEST_CFO = 200;
+ private static final long TEST_INTIATOR_REPLY_TIME = 500_000L;
+ private static final long TEST_RESPONDER_REPLY_TIME = 300_000L;
+ private static final int TEST_INITIATOR_RESPONDER_TOF = 500;
+ private static final byte[] TEST_ANCHOR_LOCATION = {0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09, 0x10};
+ private static final byte[] TEST_ACTIVE_RANGING_ROUNDS = {0x02, 0x08};
private static final int TEST_RSSI = 150;
private UwbTestUtils() {}
/** Build UwbRangingData for all Ranging Measurement Type(s). */
public static UwbRangingData generateRangingData(
- int rangingMeasurementType, int rangingStatus) {
+ int rangingMeasurementType, int macAddressingMode, int rangingStatus) {
switch (rangingMeasurementType) {
case RANGING_MEASUREMENT_TYPE_TWO_WAY:
return generateTwoWayMeasurementRangingData(rangingStatus);
case RANGING_MEASUREMENT_TYPE_OWR_AOA:
- return generateOwrAoaMeasurementRangingData(rangingStatus);
+ return generateOwrAoaMeasurementRangingData(macAddressingMode, rangingStatus);
+ case RANGING_MEASUREMENT_TYPE_DL_TDOA:
+ return generateDlTDoAMeasurementRangingData(macAddressingMode, rangingStatus);
default:
- return null;
+ return generateDefaultRangingData();
}
}
@@ -109,30 +129,67 @@ public class UwbTestUtils {
TEST_RAW_NTF_DATA);
}
- private static UwbRangingData generateOwrAoaMeasurementRangingData(int rangingStatus) {
+ private static UwbRangingData generateOwrAoaMeasurementRangingData(
+ int macAddressingMode, int rangingStatus) {
final int noOfRangingMeasures = 1;
+ byte[] macAddress = (macAddressingMode == MAC_ADDRESSING_MODE_SHORT)
+ ? PEER_SHORT_MAC_ADDRESS : PEER_EXTENDED_MAC_ADDRESS;
final UwbOwrAoaMeasurement uwbOwrAoaMeasurement = new UwbOwrAoaMeasurement(
- PEER_EXTENDED_MAC_ADDRESS, rangingStatus, TEST_LOS,
+ macAddress, rangingStatus, TEST_LOS,
TEST_FRAME_SEQUENCE_NUMBER, TEST_BLOCK_IDX,
convertFloatToQFormat(TEST_AOA_AZIMUTH, 9, 7), TEST_AOA_AZIMUTH_FOM,
convertFloatToQFormat(TEST_AOA_ELEVATION, 9, 7), TEST_AOA_ELEVATION_FOM);
return new UwbRangingData(TEST_SEQ_COUNTER, TEST_SESSION_ID,
TEST_RCR_INDICATION, TEST_CURR_RANGING_INTERVAL, RANGING_MEASUREMENT_TYPE_OWR_AOA,
- TEST_MAC_ADDRESS_MODE, noOfRangingMeasures, uwbOwrAoaMeasurement,
+ macAddressingMode, noOfRangingMeasures, uwbOwrAoaMeasurement,
+ TEST_RAW_NTF_DATA);
+ }
+
+ private static UwbRangingData generateDlTDoAMeasurementRangingData(
+ int macAddressingMode, int rangingStatus) {
+ final int noOfRangingMeasures = 1;
+ byte[] macAddress = (macAddressingMode == MAC_ADDRESSING_MODE_SHORT)
+ ? PEER_SHORT_MAC_ADDRESS : PEER_EXTENDED_MAC_ADDRESS;
+ final UwbDlTDoAMeasurement[] uwbDlTDoAMeasurements =
+ new UwbDlTDoAMeasurement[noOfRangingMeasures];
+ uwbDlTDoAMeasurements[0] = new UwbDlTDoAMeasurement(macAddress, rangingStatus,
+ TEST_MESSAGE_TYPE, TEST_MESSAGE_CONTROL, TEST_BLOCK_INDEX, TEST_ROUND_INDEX,
+ TEST_LOS, convertFloatToQFormat(TEST_AOA_AZIMUTH, 9, 7),
+ TEST_AOA_AZIMUTH_FOM, convertFloatToQFormat(TEST_AOA_ELEVATION, 9, 7),
+ TEST_AOA_ELEVATION_FOM, TEST_RSSI, TEST_TIMESTAMP, TEST_TIMESTAMP, TEST_ANCHOR_CFO,
+ TEST_CFO, TEST_INTIATOR_REPLY_TIME, TEST_RESPONDER_REPLY_TIME,
+ TEST_INITIATOR_RESPONDER_TOF, TEST_ANCHOR_LOCATION, TEST_ACTIVE_RANGING_ROUNDS);
+
+ return new UwbRangingData(TEST_SEQ_COUNTER, TEST_SESSION_ID,
+ TEST_RCR_INDICATION, TEST_CURR_RANGING_INTERVAL, RANGING_MEASUREMENT_TYPE_DL_TDOA,
+ macAddressingMode, noOfRangingMeasures, uwbDlTDoAMeasurements,
+ TEST_RAW_NTF_DATA);
+ }
+
+ // Create a UwbRangingData with no measurements, for negative test cases (example: incorrect
+ // ranging measurement type).
+ private static UwbRangingData generateDefaultRangingData() {
+ final int noOfRangingMeasures = 0;
+ final UwbTwoWayMeasurement[] uwbEmptyTwoWayMeasurements =
+ new UwbTwoWayMeasurement[noOfRangingMeasures];
+ return new UwbRangingData(TEST_SEQ_COUNTER, TEST_SESSION_ID,
+ TEST_RCR_INDICATION, TEST_CURR_RANGING_INTERVAL, RANGING_MEASUREMENT_TYPE_UNDEFINED,
+ TEST_MAC_ADDRESS_MODE, noOfRangingMeasures, uwbEmptyTwoWayMeasurements,
TEST_RAW_NTF_DATA);
}
// Helper method to generate a UwbRangingData instance and corresponding RangingMeasurement
public static Pair<UwbRangingData, RangingReport> generateRangingDataAndRangingReport(
- byte[] macAddress, int rangingMeasurementType,
+ byte[] macAddress, int macAddressingMode, int rangingMeasurementType,
boolean isAoaAzimuthEnabled, boolean isAoaElevationEnabled,
boolean isDestAoaAzimuthEnabled, boolean isDestAoaElevationEnabled,
long elapsedRealtimeNanos) {
- UwbRangingData uwbRangingData = generateRangingData(rangingMeasurementType, TEST_STATUS);
+ UwbRangingData uwbRangingData = generateRangingData(rangingMeasurementType,
+ macAddressingMode, TEST_STATUS);
- // TODO(b/246678053): Add rawNtfData[] for both TwoWay and OWR AoA Measurements..
PersistableBundle rangingReportMetadata = new RangingReportMetadata.Builder()
.setSessionId(0)
+ .setRawNtfData(new byte[] {0x10, 0x01, 0x05})
.build()
.toBundle();
@@ -212,6 +269,25 @@ public class UwbTestUtils {
.setRangingMeasurementMetadata(rangingMeasurementMetadata);
}
+ if (rangingMeasurementType == RANGING_MEASUREMENT_TYPE_DL_TDOA) {
+ DlTDoAMeasurement dlTDoAMeasurement = new DlTDoAMeasurement.Builder()
+ .setMessageType(TEST_MESSAGE_TYPE)
+ .setMessageControl(TEST_MESSAGE_CONTROL)
+ .setBlockIndex(TEST_BLOCK_INDEX)
+ .setNLoS(TEST_LOS)
+ .setTxTimestamp(TEST_TIMESTAMP)
+ .setRxTimestamp(TEST_TIMESTAMP)
+ .setAnchorCfo(TEST_ANCHOR_CFO)
+ .setCfo(TEST_CFO)
+ .setInitiatorReplyTime(TEST_INTIATOR_REPLY_TIME)
+ .setResponderReplyTime(TEST_RESPONDER_REPLY_TIME)
+ .setInitiatorResponderTof(TEST_INITIATOR_RESPONDER_TOF)
+ .setAnchorLocation(TEST_ANCHOR_LOCATION)
+ .setActiveRangingRounds(TEST_ACTIVE_RANGING_ROUNDS)
+ .build();
+ rangingMeasurementBuilder.setRangingMeasurementMetadata(dlTDoAMeasurement.toBundle());
+ }
+
return new RangingReport.Builder()
.addMeasurement(rangingMeasurementBuilder.build())
.addRangingReportMetadata(rangingReportMetadata)
diff --git a/service/java/com/android/server/uwb/advertisement/UwbAdvertiseManager.java b/service/java/com/android/server/uwb/advertisement/UwbAdvertiseManager.java
index df8588e5..0355f604 100644
--- a/service/java/com/android/server/uwb/advertisement/UwbAdvertiseManager.java
+++ b/service/java/com/android/server/uwb/advertisement/UwbAdvertiseManager.java
@@ -15,7 +15,7 @@
*/
package com.android.server.uwb.advertisement;
-import static com.android.server.uwb.util.DataTypeConversionUtil.byteArrayToI16;
+import static com.android.server.uwb.util.DataTypeConversionUtil.macAddressByteArrayToLong;
import androidx.annotation.Nullable;
@@ -23,7 +23,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.uwb.UwbInjector;
import com.android.server.uwb.data.UwbOwrAoaMeasurement;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
@@ -31,7 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class UwbAdvertiseManager {
private static final String TAG = "UwbAdvertiseManager";
- private final ConcurrentHashMap<Integer, UwbAdvertiseTarget> mAdvertiseTargetMap =
+ private final ConcurrentHashMap<Long, UwbAdvertiseTarget> mAdvertiseTargetMap =
new ConcurrentHashMap<>();
// TODO(b/246678053): Use overlays to allow OEMs to modify these values.
@@ -52,8 +51,9 @@ public class UwbAdvertiseManager {
* Check if the current device is pointing at the remote device, from which we have received
* One-way Ranging AoA Measurement(s).
*/
- public boolean isPointedTarget(byte[] macAddress) {
- UwbAdvertiseTarget uwbAdvertiseTarget = getAdvertiseTarget(byteArrayToI16(macAddress));
+ public boolean isPointedTarget(byte[] macAddressBytes) {
+ UwbAdvertiseTarget uwbAdvertiseTarget = getAdvertiseTarget(
+ macAddressByteArrayToLong(macAddressBytes));
if (uwbAdvertiseTarget == null) {
return false;
}
@@ -75,9 +75,21 @@ public class UwbAdvertiseManager {
* Store a One-way Ranging AoA Measurement from the remote device in a UWB ranging session.
*/
public void updateAdvertiseTarget(UwbOwrAoaMeasurement uwbOwrAoaMeasurement) {
+ // First check if there exists a stale UwbAdvertiseTarget for the device, and remove it.
+ checkAndRemoveStaleAdvertiseTarget(uwbOwrAoaMeasurement.mMacAddress);
+
+ // Now store the new measurement for the device.
updateAdvertiseTargetInfo(uwbOwrAoaMeasurement);
}
+ /**
+ * Remove all the stored AdvertiseTarget data for the given device.
+ */
+ public void removeAdvertiseTarget(byte[] macAddressBytes) {
+ long macAddress = macAddressByteArrayToLong(macAddressBytes);
+ mAdvertiseTargetMap.remove(macAddress);
+ }
+
private boolean isWithinCriterionVariance(UwbAdvertiseTarget uwbAdvertiseTarget) {
if (!uwbAdvertiseTarget.isVarianceCalculated()) {
return false;
@@ -93,6 +105,18 @@ public class UwbAdvertiseManager {
return true;
}
+ private void checkAndRemoveStaleAdvertiseTarget(byte[] macAddressBytes) {
+ long macAddress = macAddressByteArrayToLong(macAddressBytes);
+ UwbAdvertiseTarget uwbAdvertiseTarget = getAdvertiseTarget(macAddress);
+ if (uwbAdvertiseTarget == null) {
+ return;
+ }
+
+ if (!isWithinTimeThreshold(uwbAdvertiseTarget)) {
+ removeAdvertiseTarget(macAddressBytes);
+ }
+ }
+
private boolean isWithinTimeThreshold(UwbAdvertiseTarget uwbAdvertiseTarget) {
long currentTime = mUwbInjector.getElapsedSinceBootMillis();
if (currentTime - uwbAdvertiseTarget.getLastUpdatedTime() > TIME_THRESHOLD) {
@@ -104,8 +128,7 @@ public class UwbAdvertiseManager {
private UwbAdvertiseTarget updateAdvertiseTargetInfo(
UwbOwrAoaMeasurement uwbOwrAoaMeasurement) {
long currentTime = mUwbInjector.getElapsedSinceBootMillis();
- ByteBuffer byteBuffer = ByteBuffer.wrap(uwbOwrAoaMeasurement.getMacAddress());
- int macAddress = (int) byteBuffer.getShort();
+ long macAddress = macAddressByteArrayToLong(uwbOwrAoaMeasurement.getMacAddress());
UwbAdvertiseTarget advertiseTarget = getOrAddAdvertiseTarget(macAddress);
advertiseTarget.calculateAoaVariance(uwbOwrAoaMeasurement);
@@ -116,11 +139,11 @@ public class UwbAdvertiseManager {
@VisibleForTesting
@Nullable
- public UwbAdvertiseTarget getAdvertiseTarget(int macAddress) {
+ public UwbAdvertiseTarget getAdvertiseTarget(long macAddress) {
return isAdvertiseTargetExist(macAddress) ? mAdvertiseTargetMap.get(macAddress) : null;
}
- private UwbAdvertiseTarget getOrAddAdvertiseTarget(int macAddress) {
+ private UwbAdvertiseTarget getOrAddAdvertiseTarget(long macAddress) {
UwbAdvertiseTarget uwbAdvertiseTarget;
if (isAdvertiseTargetExist(macAddress)) {
uwbAdvertiseTarget = mAdvertiseTargetMap.get(macAddress);
@@ -130,18 +153,23 @@ public class UwbAdvertiseManager {
return uwbAdvertiseTarget;
}
- private boolean isAdvertiseTargetExist(int macAddress) {
+ private boolean isAdvertiseTargetExist(long macAddress) {
return mAdvertiseTargetMap.containsKey(macAddress);
}
- private UwbAdvertiseTarget addAdvertiseTarget(int macAddress) {
+ private UwbAdvertiseTarget addAdvertiseTarget(long macAddress) {
UwbAdvertiseTarget advertiseTarget = new UwbAdvertiseTarget(macAddress);
mAdvertiseTargetMap.put(macAddress, advertiseTarget);
return advertiseTarget;
}
- private static class UwbAdvertiseTarget {
- private final int mMacAddress;
+ /**
+ * Stored Owr Aoa Measurements for the remote devices. The data should be cleared when the
+ * UWB session is closed.
+ */
+ @VisibleForTesting
+ public static class UwbAdvertiseTarget {
+ private final long mMacAddress;
private final ArrayList<Double> mRecentAoaAzimuth = new ArrayList<>();
private final ArrayList<Double> mRecentAoaElevation = new ArrayList<>();
private double mVarianceOfAzimuth;
@@ -149,7 +177,7 @@ public class UwbAdvertiseManager {
private long mLastMeasuredTime;
private boolean mIsVarianceCalculated;
- private UwbAdvertiseTarget(int macAddress) {
+ private UwbAdvertiseTarget(long macAddress) {
mMacAddress = macAddress;
mIsVarianceCalculated = false;
}
diff --git a/service/java/com/android/server/uwb/config/CapabilityParam.java b/service/java/com/android/server/uwb/config/CapabilityParam.java
index 525d6fe7..c1092c7d 100644
--- a/service/java/com/android/server/uwb/config/CapabilityParam.java
+++ b/service/java/com/android/server/uwb/config/CapabilityParam.java
@@ -51,6 +51,8 @@ public class CapabilityParam {
UwbVendorCapabilityTlvTypes.SUPPORTED_RSSI_REPORTING;
public static final int SUPPORTED_DIAGNOSTICS =
UwbVendorCapabilityTlvTypes.SUPPORTED_DIAGNOSTICS;
+ public static final int SUPPORTED_MIN_SLOT_DURATION =
+ UwbVendorCapabilityTlvTypes.SUPPORTED_MIN_SLOT_DURATION;
// CCC specific
public static final int CCC_SUPPORTED_VERSIONS =
diff --git a/service/java/com/android/server/uwb/data/DtTagUpdateRangingRoundsStatus.java b/service/java/com/android/server/uwb/data/DtTagUpdateRangingRoundsStatus.java
new file mode 100644
index 00000000..9d4fdcbf
--- /dev/null
+++ b/service/java/com/android/server/uwb/data/DtTagUpdateRangingRoundsStatus.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 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.android.server.uwb.data;
+
+import java.util.Arrays;
+
+public class DtTagUpdateRangingRoundsStatus {
+ private final int mStatus;
+ private final int mNoOfActiveRangingRounds;
+ private final byte[] mRangingRoundIndexes;
+
+ public DtTagUpdateRangingRoundsStatus(int status, int noOfActiveRangingRounds,
+ byte[] rangingRoundIndexes) {
+ mStatus = status;
+ mNoOfActiveRangingRounds = noOfActiveRangingRounds;
+ mRangingRoundIndexes = rangingRoundIndexes;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public int getNoOfActiveRangingRounds() {
+ return mNoOfActiveRangingRounds;
+ }
+
+ public byte[] getRangingRoundIndexes() {
+ return mRangingRoundIndexes;
+ }
+
+ @Override
+ public String toString() {
+ return "DtTagActiveRoundsStatus { "
+ + "Status = " + mStatus
+ + ", NoOfActiveRangingRounds =" + mNoOfActiveRangingRounds
+ + ", RangingRoundIndexes = " + Arrays.toString(mRangingRoundIndexes)
+ + '}';
+ }
+}
diff --git a/service/java/com/android/server/uwb/data/UwbDlTDoAMeasurement.java b/service/java/com/android/server/uwb/data/UwbDlTDoAMeasurement.java
new file mode 100644
index 00000000..5163bfd0
--- /dev/null
+++ b/service/java/com/android/server/uwb/data/UwbDlTDoAMeasurement.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 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.android.server.uwb.data;
+
+import com.android.server.uwb.util.UwbUtil;
+
+import java.util.Arrays;
+
+public class UwbDlTDoAMeasurement {
+ public byte[] mMacAddress;
+ public int mStatus;
+ public int mMessageType;
+ public int mMessageControl;
+ public int mBlockIndex;
+ public int mRoundIndex;
+ public int mNLoS;
+ public float mAoaAzimuth;
+ public int mAoaAzimuthFom;
+ public float mAoaElevation;
+ public int mAoaElevationFom;
+ public int mRssi;
+ public long mTxTimestamp;
+ public long mRxTimestamp;
+ public int mAnchorCfo;
+ public int mCfo;
+ public long mInitiatorReplyTime;
+ public long mResponderReplyTime;
+ public int mInitiatorResponderTof;
+ public byte[] mAnchorLocation;
+ public byte[] mActiveRangingRounds;
+
+ public UwbDlTDoAMeasurement(byte[] macAddress, int status, int messageType, int messageControl,
+ int blockIndex, int roundIndex, int nLoS, int aoaAzimuth, int aoaAzimuthFom,
+ int aoaElevation, int aoaElevationFom, int rssi, long txTimestamp, long rxTimestamp,
+ int anchorCfo, int cfo, long initiatorReplyTime, long responderReplyTime,
+ int initiatorResponderTof, byte[] anchorLocation, byte[] activeRangingRounds) {
+ mMacAddress = macAddress;
+ mStatus = status;
+ mMessageType = messageType;
+ mMessageControl = messageControl;
+ mBlockIndex = blockIndex;
+ mRoundIndex = roundIndex;
+ mNLoS = nLoS;
+ mAoaAzimuth = toFloatFromQFormat(aoaAzimuth);
+ mAoaAzimuthFom = aoaAzimuthFom;
+ mAoaElevation = toFloatFromQFormat(aoaElevation);
+ mAoaElevationFom = aoaElevationFom;
+ mRssi = rssi;
+ mTxTimestamp = txTimestamp;
+ mRxTimestamp = rxTimestamp;
+ mAnchorCfo = anchorCfo;
+ mCfo = cfo;
+ mInitiatorReplyTime = initiatorReplyTime;
+ mResponderReplyTime = responderReplyTime;
+ mInitiatorResponderTof = initiatorResponderTof;
+ mAnchorLocation = anchorLocation;
+ mActiveRangingRounds = activeRangingRounds;
+ }
+
+ public byte[] getMacAddress() {
+ return mMacAddress;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public int getMessageType() {
+ return mMessageType;
+ }
+
+ public int getMessageControl() {
+ return mMessageControl;
+ }
+
+ public int getBlockIndex() {
+ return mBlockIndex;
+ }
+
+ public int getRoundIndex() {
+ return mRoundIndex;
+ }
+
+ public int getNLoS() {
+ return mNLoS;
+ }
+
+ public float getAoaAzimuth() {
+ return mAoaAzimuth;
+ }
+
+ public int getAoaAzimuthFom() {
+ return mAoaAzimuthFom;
+ }
+
+ public float getAoaElevation() {
+ return mAoaElevation;
+ }
+
+ public int getAoaElevationFom() {
+ return mAoaElevationFom;
+ }
+
+ public int getRssi() {
+ return mRssi;
+ }
+
+ public long getTxTimestamp() {
+ return mTxTimestamp;
+ }
+
+ public long getRxTimestamp() {
+ return mRxTimestamp;
+ }
+
+ public int getAnchorCfo() {
+ return mAnchorCfo;
+ }
+
+ public int getCfo() {
+ return mCfo;
+ }
+
+ public long getInitiatorReplyTime() {
+ return mInitiatorReplyTime;
+ }
+
+ public long getResponderReplyTime() {
+ return mResponderReplyTime;
+ }
+
+ public int getInitiatorResponderTof() {
+ return mInitiatorResponderTof;
+ }
+
+ public byte[] getAnchorLocation() {
+ return mAnchorLocation;
+ }
+
+ public byte[] getActiveRangingRounds() {
+ return mActiveRangingRounds;
+ }
+
+ private float toFloatFromQFormat(int value) {
+ return UwbUtil.convertQFormatToFloat(UwbUtil.twos_compliment(value, 16),
+ 9, 7);
+ }
+
+ @Override
+ public String toString() {
+ return "UwbDLTDoAMeasurement{" +
+ "MacAddress=" + Arrays.toString(mMacAddress) +
+ ", Status=" + mStatus +
+ ", MessageType=" + mMessageType +
+ ", MessageControl=" + mMessageControl +
+ ", BlockIndex=" + mBlockIndex +
+ ", RoundIndex=" + mRoundIndex +
+ ", NLos=" + mNLoS +
+ ", AoaAzimuth=" + mAoaAzimuth +
+ ", AoaAzimuthFom=" + mAoaAzimuthFom +
+ ", AoaElevation=" + mAoaElevation +
+ ", AoaElevationFom=" + mAoaElevationFom +
+ ", Rssi=" + mRssi +
+ ", TxTimestamp=" + mTxTimestamp +
+ ", RxTimestamp=" + mRxTimestamp +
+ ", AnchorCfo=" + mAnchorCfo +
+ ", Cfo=" + mCfo +
+ ", InitiatorReplyTime=" + mInitiatorReplyTime +
+ ", ResponderReplyTime=" + mResponderReplyTime +
+ ", InitiatorResponderTof=" + mInitiatorResponderTof +
+ ", AnchorLocation=" + Arrays.toString(mAnchorLocation) +
+ ", ActiveRangingRounds=" + Arrays.toString(mActiveRangingRounds) +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/uwb/data/UwbRangingData.java b/service/java/com/android/server/uwb/data/UwbRangingData.java
index 780a8cba..78b6dafb 100644
--- a/service/java/com/android/server/uwb/data/UwbRangingData.java
+++ b/service/java/com/android/server/uwb/data/UwbRangingData.java
@@ -28,6 +28,7 @@ public class UwbRangingData {
public UwbTwoWayMeasurement[] mRangingTwoWayMeasures;
public byte[] mRawNtfData;
public UwbOwrAoaMeasurement mRangingOwrAoaMeasure;
+ public UwbDlTDoAMeasurement[] mUwbDlTDoAMeasurements;
public UwbRangingData(long seqCounter, long sessionId, int rcrIndication,
long currRangingInterval, int rangingMeasuresType, int macAddressMode,
@@ -59,6 +60,21 @@ public class UwbRangingData {
this.mRawNtfData = rawNtfData;
}
+ public UwbRangingData(long seqCounter, long sessionId, int rcrIndication,
+ long currRangingInterval, int rangingMeasuresType, int macAddressMode,
+ int noOfRangingMeasures, UwbDlTDoAMeasurement[] uwbDlTDoAMeasurements,
+ byte[] rawNtfData) {
+ this.mSeqCounter = seqCounter;
+ this.mSessionId = sessionId;
+ this.mRcrIndication = rcrIndication;
+ this.mCurrRangingInterval = currRangingInterval;
+ this.mRangingMeasuresType = rangingMeasuresType;
+ this.mMacAddressMode = macAddressMode;
+ this.mNoOfRangingMeasures = noOfRangingMeasures;
+ this.mUwbDlTDoAMeasurements = uwbDlTDoAMeasurements;
+ this.mRawNtfData = rawNtfData;
+ }
+
public long getSequenceCounter() {
return mSeqCounter;
}
@@ -99,6 +115,10 @@ public class UwbRangingData {
return mRangingOwrAoaMeasure;
}
+ public UwbDlTDoAMeasurement[] getUwbDlTDoAMeasurements() {
+ return mUwbDlTDoAMeasurements;
+ }
+
public String toString() {
if (mRangingMeasuresType == UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY) {
return "UwbRangingData { "
@@ -124,6 +144,18 @@ public class UwbRangingData {
+ ", RangingOwrAoaMeasure = " + mRangingOwrAoaMeasure.toString()
+ ", RawNotificationData = " + Arrays.toString(mRawNtfData)
+ '}';
+ } else if (mRangingMeasuresType == UwbUciConstants.RANGING_MEASUREMENT_TYPE_DL_TDOA) {
+ return "UwbRangingData { "
+ + " SeqCounter = " + mSeqCounter
+ + ", SessionId = " + mSessionId
+ + ", RcrIndication = " + mRcrIndication
+ + ", CurrRangingInterval = " + mCurrRangingInterval
+ + ", RangingMeasuresType = " + mRangingMeasuresType
+ + ", MacAddressMode = " + mMacAddressMode
+ + ", NoOfRangingMeasures = " + mNoOfRangingMeasures
+ + ", RangingDlTDoAMeasure = " + Arrays.toString(mUwbDlTDoAMeasurements)
+ + ", RawNotificationData = " + Arrays.toString(mRawNtfData)
+ + '}';
} else {
// TODO(jh0.jang) : ONE WAY RANGING(TDOA)?
return null;
diff --git a/service/java/com/android/server/uwb/data/UwbTwoWayMeasurement.java b/service/java/com/android/server/uwb/data/UwbTwoWayMeasurement.java
index fbe70817..f763ab80 100644
--- a/service/java/com/android/server/uwb/data/UwbTwoWayMeasurement.java
+++ b/service/java/com/android/server/uwb/data/UwbTwoWayMeasurement.java
@@ -15,7 +15,7 @@
*/
package com.android.server.uwb.data;
-import static android.uwb.RangingMeasurement.RSSI_MIN;
+import android.uwb.RangingMeasurement;
import com.android.server.uwb.util.UwbUtil;
@@ -43,7 +43,9 @@ public class UwbTwoWayMeasurement {
this.mMacAddress = macAddress;
this.mStatus = status;
this.mNLoS = nLoS;
- this.mDistance = distance;
+ // Set distance to negative value if the status code indicates negative distance
+ this.mDistance = status == UwbUciConstants.STATUS_CODE_OK_NEGATIVE_DISTANCE_REPORT
+ ? -distance : distance;
this.mAoaAzimuth = toFloatFromQFormat(aoaAzimuth);
this.mAoaAzimuthFom = aoaAzimuthFom;
this.mAoaElevation = toFloatFromQFormat(aoaElevation);
@@ -59,7 +61,7 @@ public class UwbTwoWayMeasurement {
* Just need to divide this number by two and take the negative value.
* If the reported RSSI is lower than RSSI_MIN, set it to RSSI_MIN to avoid exceptions.
*/
- this.mRssi = Math.max(-rssiHalfDbmAbs / 2, RSSI_MIN);
+ this.mRssi = Math.max(-rssiHalfDbmAbs / 2, RangingMeasurement.RSSI_MIN);
}
public byte[] getMacAddress() {
@@ -117,6 +119,26 @@ public class UwbTwoWayMeasurement {
return mRssi;
}
+ public boolean isStatusCodeOk() {
+ return mStatus == UwbUciConstants.STATUS_CODE_OK
+ || mStatus == UwbUciConstants.STATUS_CODE_OK_NEGATIVE_DISTANCE_REPORT;
+ }
+
+ /**
+ * Convert the UCI status code to success, out of range, or unknown error
+ */
+ public int convertStatusCode() {
+ switch (mStatus) {
+ case UwbUciConstants.STATUS_CODE_OK:
+ case UwbUciConstants.STATUS_CODE_OK_NEGATIVE_DISTANCE_REPORT:
+ return RangingMeasurement.RANGING_STATUS_SUCCESS;
+ case UwbUciConstants.STATUS_CODE_INVALID_RANGE:
+ return RangingMeasurement.RANGING_STATUS_FAILURE_OUT_OF_RANGE;
+ default:
+ return RangingMeasurement.RANGING_STATUS_FAILURE_UNKNOWN_ERROR;
+ }
+ }
+
private float toFloatFromQFormat(int value) {
return UwbUtil.convertQFormatToFloat(UwbUtil.twos_compliment(value, 16),
9, 7);
diff --git a/service/java/com/android/server/uwb/data/UwbUciConstants.java b/service/java/com/android/server/uwb/data/UwbUciConstants.java
index 1e49d4b9..975a4924 100644
--- a/service/java/com/android/server/uwb/data/UwbUciConstants.java
+++ b/service/java/com/android/server/uwb/data/UwbUciConstants.java
@@ -87,6 +87,8 @@ public class UwbUciConstants {
FiraParams.RANGING_ROUND_USAGE_SS_TWR_NON_DEFERRED_MODE;
public static final int ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE =
FiraParams.RANGING_ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE;
+ public static final int ROUND_USAGE_OWR_AOA_MEASUREMENT =
+ FiraParams.RANGING_ROUND_USAGE_OWR_AOA_MEASUREMENT;
public static final int MULTI_NODE_MODE_UNICAST = FiraParams.MULTI_NODE_MODE_UNICAST;
public static final int MULTI_NODE_MODE_ONE_TO_MANY = FiraParams.MULTI_NODE_MODE_ONE_TO_MANY;
@@ -113,17 +115,28 @@ public class UwbUciConstants {
public static final int RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY =
FiraParams.RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY_LEVEL_TRIG;
+ /**
+ * Table 54: APP Configuration Parameter IDs
+ */
public static final int RANGING_DEVICE_ROLE_RESPONDER =
FiraParams.RANGING_DEVICE_ROLE_RESPONDER;
public static final int RANGING_DEVICE_ROLE_INITIATOR =
FiraParams.RANGING_DEVICE_ROLE_INITIATOR;
+ public static final int RANGING_DEVICE_ROLE_ADVERTISER =
+ FiraParams.RANGING_DEVICE_ROLE_ADVERTISER;
+ public static final int RANGING_DEVICE_ROLE_OBSERVER =
+ FiraParams.RANGING_DEVICE_ROLE_OBSERVER;
/**
- * Table 22: Ranging Data Notification
+ * Table 37: Ranging Data Notification
*/
public static final byte RANGING_MEASUREMENT_TYPE_TWO_WAY = 0X01;
+ public static final byte RANGING_MEASUREMENT_TYPE_DL_TDOA = 0x02;
public static final byte RANGING_MEASUREMENT_TYPE_OWR_AOA = 0x03;
+ public static final byte MAC_ADDRESSING_MODE_SHORT = 0x00;
+ public static final byte MAC_ADDRESSING_MODE_EXTENDED = 0x01;
+
/**
* Table 32: Status Codes
*/
@@ -159,6 +172,8 @@ public class UwbUciConstants {
FiraParams.STATUS_CODE_ERROR_ADDRESS_NOT_FOUND;
public static final int STATUS_CODE_ERROR_ADDRESS_ALREADY_PRESENT =
FiraParams.STATUS_CODE_ERROR_ADDRESS_ALREADY_PRESENT;
+ public static final int STATUS_CODE_OK_NEGATIVE_DISTANCE_REPORT =
+ FiraParams.STATUS_CODE_OK_NEGATIVE_DISTANCE_REPORT;
/* UWB Ranging Session Specific Status Codes */
public static final int STATUS_CODE_RANGING_TX_FAILED =
FiraParams.STATUS_CODE_RANGING_TX_FAILED;
@@ -176,11 +191,21 @@ public class UwbUciConstants {
FiraParams.STATUS_CODE_RANGING_RX_MAC_IE_DEC_FAILED;
public static final int STATUS_CODE_RANGING_RX_MAC_IE_MISSING =
FiraParams.STATUS_CODE_RANGING_RX_MAC_IE_MISSING;
+ public static final int STATUS_CODE_ERROR_ROUND_INDEX_NOT_ACTIVATED =
+ FiraParams.STATUS_CODE_ERROR_ROUND_INDEX_NOT_ACTIVATED;
+ public static final int STATUS_CODE_ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED =
+ FiraParams.STATUS_CODE_ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED;
+ public static final int STATUS_CODE_ERROR_ROUND_INDEX_NOT_SET_AS_INITIATOR =
+ FiraParams.STATUS_CODE_ERROR_ROUND_INDEX_NOT_SET_AS_INITIATOR;
+ public static final int
+ STATUS_CODE_ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST =
+ FiraParams.STATUS_CODE_ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST;
public static final int STATUS_CODE_CCC_SE_BUSY = STATUS_ERROR_CCC_SE_BUSY;
public static final int STATUS_CODE_CCC_LIFECYCLE = STATUS_ERROR_CCC_LIFECYCLE;
/* UWB Device Extended Mac address length */
+ public static final int UWB_DEVICE_SHORT_MAC_ADDRESS_LEN = 2;
public static final int UWB_DEVICE_EXT_MAC_ADDRESS_LEN = 8;
/* UWB Data Session Specific Status Codes */
diff --git a/service/java/com/android/server/uwb/jni/NativeUwbManager.java b/service/java/com/android/server/uwb/jni/NativeUwbManager.java
index d9866a42..edf3b60a 100644
--- a/service/java/com/android/server/uwb/jni/NativeUwbManager.java
+++ b/service/java/com/android/server/uwb/jni/NativeUwbManager.java
@@ -21,6 +21,7 @@ import android.util.Log;
import com.android.internal.annotations.Keep;
import com.android.server.uwb.UciLogModeStore;
import com.android.server.uwb.UwbInjector;
+import com.android.server.uwb.data.DtTagUpdateRangingRoundsStatus;
import com.android.server.uwb.data.UwbConfigStatusData;
import com.android.server.uwb.data.UwbMulticastListUpdateStatus;
import com.android.server.uwb.data.UwbRangingData;
@@ -30,15 +31,13 @@ import com.android.server.uwb.data.UwbVendorUciResponse;
import com.android.server.uwb.info.UwbPowerStats;
import com.android.server.uwb.multchip.UwbMultichipData;
+import java.util.Arrays;
+
@Keep
public class NativeUwbManager {
private static final String TAG = NativeUwbManager.class.getSimpleName();
- public final Object mSessionFnLock = new Object();
- public final Object mSessionCountFnLock = new Object();
- public final Object mGlobalStateFnLock = new Object();
- public final Object mGetSessionStatusFnLock = new Object();
- public final Object mSetAppConfigFnLock = new Object();
+ public final Object mNativeLock = new Object();
private final UwbInjector mUwbInjector;
private final UciLogModeStore mUciLogModeStore;
private final UwbMultichipData mUwbMultichipData;
@@ -57,7 +56,9 @@ public class NativeUwbManager {
protected void loadLibrary() {
System.loadLibrary("uwb_uci_jni_rust");
- nativeInit();
+ synchronized (mNativeLock) {
+ nativeInit();
+ }
}
public void setDeviceListener(INativeUwbManager.DeviceNotification deviceListener) {
@@ -109,7 +110,8 @@ public class NativeUwbManager {
* Vendor callback invoked via the JNI
*/
public void onVendorUciNotificationReceived(int gid, int oid, byte[] payload) {
- Log.d(TAG, "onVendorUciNotificationReceived: " + gid + ", " + oid + ", " + payload);
+ Log.d(TAG, "onVendorUciNotificationReceived: " + gid + ", " + oid + ", "
+ + Arrays.toString(payload));
mVendorListener.onVendorUciNotificationReceived(gid, oid, payload);
}
@@ -118,14 +120,16 @@ public class NativeUwbManager {
*
* @return : If this returns true, UWB is on
*/
- public synchronized boolean doInitialize() {
- mDispatcherPointer = nativeDispatcherNew(mUwbMultichipData.getChipIds().toArray());
- for (String chipId : mUwbMultichipData.getChipIds()) {
- if (!nativeDoInitialize(chipId)) {
- return false;
+ public boolean doInitialize() {
+ synchronized (mNativeLock) {
+ mDispatcherPointer = nativeDispatcherNew(mUwbMultichipData.getChipIds().toArray());
+ for (String chipId : mUwbMultichipData.getChipIds()) {
+ if (!nativeDoInitialize(chipId)) {
+ return false;
+ }
}
+ nativeSetLogMode(mUciLogModeStore.getMode());
}
- nativeSetLogMode(mUciLogModeStore.getMode());
return true;
}
@@ -134,17 +138,24 @@ public class NativeUwbManager {
*
* @return : If this returns true, UWB is off
*/
- public synchronized boolean doDeinitialize() {
- for (String chipId : mUwbMultichipData.getChipIds()) {
- nativeDoDeinitialize(chipId);
- }
+ public boolean doDeinitialize() {
+ synchronized (mNativeLock) {
+ for (String chipId : mUwbMultichipData.getChipIds()) {
+ nativeDoDeinitialize(chipId);
+ }
- nativeDispatcherDestroy();
- mDispatcherPointer = 0L;
+ nativeDispatcherDestroy();
+ mDispatcherPointer = 0L;
+ }
return true;
}
- public synchronized long getTimestampResolutionNanos() {
+ /**
+ * Gets the timestamp resolution in nanosecond
+ *
+ * @return : timestamp resolution in nanosecond
+ */
+ public long getTimestampResolutionNanos() {
return 0L;
/* TODO: Not Implemented in native stack
return nativeGetTimestampResolutionNanos(); */
@@ -156,14 +167,18 @@ public class NativeUwbManager {
* @return : Retrieves maximum number of UWB sessions concurrently
*/
public int getMaxSessionNumber() {
- return nativeGetMaxSessionNumber();
+ synchronized (mNativeLock) {
+ return nativeGetMaxSessionNumber();
+ }
}
/**
* Retrieves power related stats
*/
public UwbPowerStats getPowerStats(String chipId) {
- return nativeGetPowerStats(chipId);
+ synchronized (mNativeLock) {
+ return nativeGetPowerStats(chipId);
+ }
}
/**
@@ -177,7 +192,7 @@ public class NativeUwbManager {
* @return : {@link UwbUciConstants} Status code
*/
public byte initSession(int sessionId, byte sessionType, String chipId) {
- synchronized (mSessionFnLock) {
+ synchronized (mNativeLock) {
return nativeSessionInit(sessionId, sessionType, chipId);
}
}
@@ -190,7 +205,7 @@ public class NativeUwbManager {
* @return : {@link UwbUciConstants} Status code
*/
public byte deInitSession(int sessionId, String chipId) {
- synchronized (mSessionFnLock) {
+ synchronized (mNativeLock) {
return nativeSessionDeInit(sessionId, chipId);
}
}
@@ -203,7 +218,9 @@ public class NativeUwbManager {
* @return : {@link UwbUciConstants} Status code
*/
public byte resetDevice(byte resetConfig, String chipId) {
- return nativeResetDevice(resetConfig, chipId);
+ synchronized (mNativeLock) {
+ return nativeResetDevice(resetConfig, chipId);
+ }
}
/**
@@ -213,7 +230,7 @@ public class NativeUwbManager {
* @return : Number of UWB sessions present in the UWBS.
*/
public byte getSessionCount(String chipId) {
- synchronized (mSessionCountFnLock) {
+ synchronized (mNativeLock) {
return nativeGetSessionCount(chipId);
}
}
@@ -226,7 +243,7 @@ public class NativeUwbManager {
* @return : {@link UwbUciConstants} Session State
*/
public byte getSessionState(int sessionId, String chipId) {
- synchronized (mGetSessionStatusFnLock) {
+ synchronized (mNativeLock) {
return nativeGetSessionState(sessionId, chipId);
}
}
@@ -239,7 +256,7 @@ public class NativeUwbManager {
* @return : {@link UwbUciConstants} Status code
*/
public byte startRanging(int sessionId, String chipId) {
- synchronized (mSessionFnLock) {
+ synchronized (mNativeLock) {
return nativeRangingStart(sessionId, chipId);
}
}
@@ -252,7 +269,7 @@ public class NativeUwbManager {
* @return : {@link UwbUciConstants} Status code
*/
public byte stopRanging(int sessionId, String chipId) {
- synchronized (mSessionFnLock) {
+ synchronized (mNativeLock) {
return nativeRangingStop(sessionId, chipId);
}
}
@@ -268,7 +285,7 @@ public class NativeUwbManager {
*/
public UwbConfigStatusData setAppConfigurations(int sessionId, int noOfParams,
int appConfigParamLen, byte[] appConfigParams, String chipId) {
- synchronized (mSetAppConfigFnLock) {
+ synchronized (mNativeLock) {
return nativeSetAppConfigurations(sessionId, noOfParams, appConfigParamLen,
appConfigParams, chipId);
}
@@ -285,7 +302,7 @@ public class NativeUwbManager {
*/
public UwbTlvData getAppConfigurations(int sessionId, int noOfParams, int appConfigParamLen,
byte[] appConfigIds, String chipId) {
- synchronized (mSetAppConfigFnLock) {
+ synchronized (mNativeLock) {
return nativeGetAppConfigurations(sessionId, noOfParams, appConfigParamLen,
appConfigIds, chipId);
}
@@ -298,7 +315,7 @@ public class NativeUwbManager {
* @return : {@link UwbTlvData} : All tlvs that are to be decoded
*/
public UwbTlvData getCapsInfo(String chipId) {
- synchronized (mGlobalStateFnLock) {
+ synchronized (mNativeLock) {
return nativeGetCapsInfo(chipId);
}
}
@@ -318,7 +335,7 @@ public class NativeUwbManager {
*/
public byte controllerMulticastListUpdateV1(int sessionId, int action, int noOfControlee,
short[] addresses, int[] subSessionIds, String chipId) {
- synchronized (mSessionFnLock) {
+ synchronized (mNativeLock) {
return nativeControllerMulticastListUpdateV1(sessionId, (byte) action,
(byte) noOfControlee, addresses, subSessionIds, chipId);
}
@@ -349,7 +366,7 @@ public class NativeUwbManager {
public byte controllerMulticastListUpdateV2(int sessionId, int action, int noOfControlee,
short[] addresses, int[] subSessionIds, int messageControl,
int[] subSessionKeyList, String chipId) {
- synchronized (mSessionFnLock) {
+ synchronized (mNativeLock) {
return nativeControllerMulticastListUpdateV2(sessionId, (byte) action,
(byte) noOfControlee, addresses, subSessionIds, messageControl,
subSessionKeyList, chipId);
@@ -364,7 +381,7 @@ public class NativeUwbManager {
public byte setCountryCode(byte[] countryCode) {
Log.i(TAG, "setCountryCode: " + new String(countryCode));
- synchronized (mGlobalStateFnLock) {
+ synchronized (mNativeLock) {
for (String chipId : mUwbMultichipData.getChipIds()) {
byte status = nativeSetCountryCode(countryCode, chipId);
if (status != UwbUciConstants.STATUS_CODE_OK) {
@@ -383,7 +400,9 @@ public class NativeUwbManager {
*/
public boolean setLogMode(String logModeStr) {
if (mUciLogModeStore.storeMode(logModeStr)) {
- return nativeSetLogMode(mUciLogModeStore.getMode());
+ synchronized (mNativeLock) {
+ return nativeSetLogMode(mUciLogModeStore.getMode());
+ }
} else {
return false;
}
@@ -391,7 +410,7 @@ public class NativeUwbManager {
@NonNull
public UwbVendorUciResponse sendRawVendorCmd(int gid, int oid, byte[] payload, String chipId) {
- synchronized (mGlobalStateFnLock) {
+ synchronized (mNativeLock) {
return nativeSendRawVendorCmd(gid, oid, payload, chipId);
}
}
@@ -412,11 +431,32 @@ public class NativeUwbManager {
*/
public byte sendData(
int sessionId, byte[] address, byte destEndPoint, int sequenceNum, byte[] appData) {
- return nativeSendData(sessionId, address, destEndPoint, sequenceNum, appData);
+ synchronized (mNativeLock) {
+ return nativeSendData(sessionId, address, destEndPoint, sequenceNum, appData);
+ }
+ }
+
+
+ /**
+ * Update active Ranging Rounds for DT Tag
+ *
+ * @param sessionId Session ID to which ranging round to be updated
+ * @param noOfActiveRangingRounds new active ranging round
+ * @param rangingRoundIndexes Indexes of ranging rounds
+ * @return refer to SESSION_SET_APP_CONFIG_RSP
+ * in the Table 16: Control messages to set Application configurations
+ */
+ public DtTagUpdateRangingRoundsStatus sessionUpdateActiveRoundsDtTag(int sessionId,
+ int noOfActiveRangingRounds, byte[] rangingRoundIndexes, String chipId) {
+ synchronized (mNativeLock) {
+ return nativeSessionUpdateActiveRoundsDtTag(sessionId, noOfActiveRangingRounds,
+ rangingRoundIndexes, chipId);
+ }
}
- private native byte nativeSendData(int sessionId, byte[] address,
- byte destEndPoint, int sequenceNum, byte[] appData);
+ // TODO(b/259487023): no native implementation
+ private native byte nativeSendData(int sessionId, byte[] address, byte destEndPoint,
+ int sequenceNum, byte[] appData);
private native long nativeDispatcherNew(Object[] chipIds);
@@ -434,6 +474,7 @@ public class NativeUwbManager {
private native int nativeGetMaxSessionNumber();
+ // TODO(b/259487023): no native implementation
private native byte nativeResetDevice(byte resetConfig, String chipId);
private native byte nativeSessionInit(int sessionId, byte sessionType, String chipId);
@@ -459,6 +500,7 @@ public class NativeUwbManager {
private native byte nativeControllerMulticastListUpdateV1(int sessionId, byte action,
byte noOfControlee, short[] address, int[] subSessionId, String chipId);
+ // TODO(b/259487023): no native implementation
private native byte nativeControllerMulticastListUpdateV2(int sessionId, byte action,
byte noOfControlee, short[] address, int[] subSessionId, int messageControl,
int[] subSessionKeyList, String chipId);
@@ -469,4 +511,7 @@ public class NativeUwbManager {
private native UwbVendorUciResponse nativeSendRawVendorCmd(int gid, int oid, byte[] payload,
String chipId);
+
+ private native DtTagUpdateRangingRoundsStatus nativeSessionUpdateActiveRoundsDtTag(
+ int sessionId, int noOfActiveRangingRounds, byte[] rangingRoundIndexes, String chipId);
}
diff --git a/service/java/com/android/server/uwb/params/CccEncoder.java b/service/java/com/android/server/uwb/params/CccEncoder.java
index 93a2f25e..1debeaa8 100644
--- a/service/java/com/android/server/uwb/params/CccEncoder.java
+++ b/service/java/com/android/server/uwb/params/CccEncoder.java
@@ -40,6 +40,7 @@ public class CccEncoder extends TlvEncoder {
int hoppingSequence = params.getHoppingSequence();
int hoppingMode = CccParams.HOPPING_CONFIG_MODE_NONE;
+ byte[] protocolVer = params.getProtocolVersion().toBytes();
switch (hoppingConfig) {
@@ -61,7 +62,7 @@ public class CccEncoder extends TlvEncoder {
TlvBuffer tlvBuffer = new TlvBuffer.Builder()
.putByte(ConfigParam.DEVICE_TYPE,
- (byte) UwbUciConstants.DEVICE_TYPE_CONTROLEE) // DEVICE_TYPE
+ (byte) UwbUciConstants.DEVICE_TYPE_CONTROLLER) // DEVICE_TYPE
.putByte(ConfigParam.STS_CONFIG,
(byte) UwbUciConstants.STS_MODE_DYNAMIC) // STS_CONFIG
.putByte(ConfigParam.CHANNEL_NUMBER, (byte) params.getChannel()) // CHANNEL_ID
@@ -81,7 +82,7 @@ public class CccEncoder extends TlvEncoder {
.putByte(ConfigParam.HOPPING_MODE, (byte) hoppingMode) // HOPPING_MODE
.putByteArray(ConfigParam.RANGING_PROTOCOL_VER,
ConfigParam.RANGING_PROTOCOL_VER_BYTE_COUNT,
- params.getProtocolVersion().toBytes()) // RANGING_PROTOCOL_VER
+ new byte[] { protocolVer[1], protocolVer[0] }) // RANGING_PROTOCOL_VER
.putShort(ConfigParam.UWB_CONFIG_ID, (short) params.getUwbConfig()) // UWB_CONFIG_ID
.putByte(ConfigParam.PULSESHAPE_COMBO,
params.getPulseShapeCombo().toBytes()[0]) // PULSESHAPE_COMBO
@@ -97,4 +98,4 @@ public class CccEncoder extends TlvEncoder {
return tlvBuffer;
}
-}
+} \ No newline at end of file
diff --git a/service/java/com/android/server/uwb/params/FiraDecoder.java b/service/java/com/android/server/uwb/params/FiraDecoder.java
index 466848a5..cce79e39 100644
--- a/service/java/com/android/server/uwb/params/FiraDecoder.java
+++ b/service/java/com/android/server/uwb/params/FiraDecoder.java
@@ -73,6 +73,7 @@ import static com.android.server.uwb.config.CapabilityParam.SUPPORTED_FIRA_MAC_V
import static com.android.server.uwb.config.CapabilityParam.SUPPORTED_FIRA_PHY_VERSION_RANGE;
import static com.android.server.uwb.config.CapabilityParam.SUPPORTED_HPRF_PARAMETER_SETS;
import static com.android.server.uwb.config.CapabilityParam.SUPPORTED_MIN_RANGING_INTERVAL_MS;
+import static com.android.server.uwb.config.CapabilityParam.SUPPORTED_MIN_SLOT_DURATION;
import static com.android.server.uwb.config.CapabilityParam.SUPPORTED_MULTI_NODE_MODES;
import static com.android.server.uwb.config.CapabilityParam.SUPPORTED_RANGE_DATA_NTF_CONFIG;
import static com.android.server.uwb.config.CapabilityParam.SUPPORTED_RANGING_METHOD;
@@ -134,6 +135,13 @@ public class FiraDecoder extends TlvDecoder {
}
try {
+ int minSlotDuration = tlvs.getInt(SUPPORTED_MIN_SLOT_DURATION);
+ builder.setMinRangingIntervalSupported(minSlotDuration);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "SUPPORTED_MIN_SLOT_DURATION not found.");
+ }
+
+ try {
byte rssiReporting = tlvs.getByte(SUPPORTED_RSSI_REPORTING);
if (isBitSet(rssiReporting, RSSI_REPORTING)) {
builder.hasRssiReportingSupport(true);
diff --git a/service/java/com/android/server/uwb/params/TlvDecoderBuffer.java b/service/java/com/android/server/uwb/params/TlvDecoderBuffer.java
index 9fc41e80..49d34752 100644
--- a/service/java/com/android/server/uwb/params/TlvDecoderBuffer.java
+++ b/service/java/com/android/server/uwb/params/TlvDecoderBuffer.java
@@ -125,7 +125,7 @@ public class TlvDecoderBuffer {
private Tlv getTlv(int tagType) {
byte[] tagTypeByte = ConfigParam.getTagBytes(tagType);
if (tagTypeByte.length > 1) {
- throw new IllegalArgumentException("Invalid tagType: " + tagTypeByte);
+ throw new IllegalArgumentException("Invalid tagType: " + Arrays.toString(tagTypeByte));
}
Tlv tlv = mTlvs.get(tagTypeByte[0]);
if (tlv == null) {
diff --git a/service/java/com/android/server/uwb/util/DataTypeConversionUtil.java b/service/java/com/android/server/uwb/util/DataTypeConversionUtil.java
index 2e5ff972..0f191412 100644
--- a/service/java/com/android/server/uwb/util/DataTypeConversionUtil.java
+++ b/service/java/com/android/server/uwb/util/DataTypeConversionUtil.java
@@ -15,6 +15,9 @@
*/
package com.android.server.uwb.util;
+import static com.android.server.uwb.data.UwbUciConstants.UWB_DEVICE_EXT_MAC_ADDRESS_LEN;
+import static com.android.server.uwb.data.UwbUciConstants.UWB_DEVICE_SHORT_MAC_ADDRESS_LEN;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -137,5 +140,34 @@ public class DataTypeConversionUtil {
return ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(n).array();
}
+ /**
+ * Convert the byte array (in Little Endian format) to a long. The input array could be: of
+ * shorter size (eg: 2 bytes, to represent a shortMacAddress). It could also have length of 8
+ * bytes, but have the MSB 6 bytes zeroed out (the 2 LSB bytes contain the MacAddress).
+ */
+ public static long macAddressByteArrayToLong(byte[] bytes) {
+ if (bytes.length == 2) {
+ return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
+ } else if (bytes.length == 4) {
+ return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
+ } else if (bytes.length == 8) {
+ if (isExtendedMSBZeroedOut(bytes)) {
+ return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
+ } else {
+ return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong();
+ }
+ } else {
+ throw new NumberFormatException("Expected length one of (2, 4, 8) but was "
+ + bytes.length);
+ }
+ }
+
+ // Check if the MSB bytes are zeroed out.
+ private static boolean isExtendedMSBZeroedOut(byte[] bytes) {
+ for (int i = UWB_DEVICE_SHORT_MAC_ADDRESS_LEN; i < UWB_DEVICE_EXT_MAC_ADDRESS_LEN; i++) {
+ if (bytes[i] != 0) return false;
+ }
+ return true;
+ }
private DataTypeConversionUtil() {}
}
diff --git a/service/support_lib/Android.bp b/service/support_lib/Android.bp
index 96083d0d..45eb875a 100644
--- a/service/support_lib/Android.bp
+++ b/service/support_lib/Android.bp
@@ -19,19 +19,15 @@ package {
java_defaults {
name: "support-lib-uwb-common-defaults",
- defaults: ["uwb-module-sdk-version-defaults"],
- sdk_version: "module_Tiramisu",
- libs: [
- "framework-annotations-lib",
- "framework-uwb-pre-jarjar",
+ sdk_version: "system_31",
+ static_libs: [
+ "androidx.annotation_annotation",
+ "guava"
],
apex_available: [
"com.android.uwb",
],
visibility : [
- //TODO(b/244347268): Remove these 2 after code move.
- "//cts/tests/uwb",
- "//cts/hostsidetests/multidevices/uwb/snippet",
"//external/sl4a/Common",
"//packages/modules/Uwb:__subpackages__",
]
@@ -43,10 +39,6 @@ java_library {
srcs: [
"src/com/google/uwb/support/base/**/*.java",
],
- static_libs: [
- "androidx.annotation_annotation",
- "modules-utils-preconditions",
- ],
}
java_library {
@@ -56,9 +48,7 @@ java_library {
"src/com/google/uwb/support/ccc/**/*.java",
],
static_libs: [
- "androidx.annotation_annotation",
"com.uwb.support.base",
- "modules-utils-preconditions",
],
}
@@ -69,9 +59,7 @@ java_library {
"src/com/google/uwb/support/fira/**/*.java",
],
static_libs: [
- "androidx.annotation_annotation",
"com.uwb.support.base",
- "modules-utils-preconditions",
],
}
@@ -82,11 +70,9 @@ java_library {
"src/com/google/uwb/support/generic/**/*.java",
],
static_libs: [
- "androidx.annotation_annotation",
"com.uwb.support.base",
"com.uwb.support.ccc",
"com.uwb.support.fira",
- "modules-utils-preconditions",
],
}
@@ -96,8 +82,6 @@ java_library {
srcs: [
"src/com/google/uwb/support/multichip/**/*.java",
],
- static_libs: [
- ],
}
java_library {
@@ -107,10 +91,8 @@ java_library {
"src/com/google/uwb/support/profile/**/*.java",
],
static_libs: [
- "androidx.annotation_annotation",
"com.uwb.support.base",
"com.uwb.support.fira",
- "modules-utils-preconditions",
],
}
@@ -124,3 +106,15 @@ java_library {
"com.uwb.support.base",
],
}
+
+java_library {
+ name: "com.uwb.support.dltdoa",
+ defaults: ["support-lib-uwb-common-defaults"],
+ srcs: [
+ "src/com/google/uwb/support/dltdoa/**/*.java",
+ ],
+ static_libs: [
+ "com.uwb.support.base",
+ "com.uwb.support.fira",
+ ],
+}
diff --git a/service/support_lib/src/com/google/uwb/support/ccc/CccOpenRangingParams.java b/service/support_lib/src/com/google/uwb/support/ccc/CccOpenRangingParams.java
index bd6fbcd7..f0323b16 100644
--- a/service/support_lib/src/com/google/uwb/support/ccc/CccOpenRangingParams.java
+++ b/service/support_lib/src/com/google/uwb/support/ccc/CccOpenRangingParams.java
@@ -16,14 +16,14 @@
package com.google.uwb.support.ccc;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkNotNull;
-import android.annotation.NonNull;
import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
import android.uwb.UwbManager;
import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.google.uwb.support.base.RequiredParam;
diff --git a/service/support_lib/src/com/google/uwb/support/ccc/CccSpecificationParams.java b/service/support_lib/src/com/google/uwb/support/ccc/CccSpecificationParams.java
index 105aeab0..beeb7876 100644
--- a/service/support_lib/src/com/google/uwb/support/ccc/CccSpecificationParams.java
+++ b/service/support_lib/src/com/google/uwb/support/ccc/CccSpecificationParams.java
@@ -16,7 +16,7 @@
package com.google.uwb.support.ccc;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkNotNull;
import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
diff --git a/service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoAMeasurement.java b/service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoAMeasurement.java
new file mode 100644
index 00000000..7c2c61c1
--- /dev/null
+++ b/service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoAMeasurement.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2022 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.dltdoa;
+
+import android.os.PersistableBundle;
+import android.uwb.RangingMeasurement;
+
+import androidx.annotation.Nullable;
+
+/**
+ * DlTDoA measurement values
+ *
+ * <p> This is passed as a bundle with RangingMeasurement for mRangingReportMetadata
+ * {@link RangingMeasurement#getRangingMeasurementMetadata()} This will be passed for sessions
+ * with DL-TDoA measurements only. For other sessions, the metadata will contain something else
+ */
+public class DlTDoAMeasurement {
+ private final int mMessageType;
+ private final int mMessageControl;
+ private final int mBlockIndex;
+ private final int mRoundIndex;
+ private final int mNLoS;
+ private final long mTxTimestamp;
+ private final long mRxTimestamp;
+ private final int mAnchorCfo;
+ private final int mCfo;
+ private final long mInitiatorReplyTime;
+ private final long mResponderReplyTime;
+ private final int mInitiatorResponderTof;
+ private final byte[] mAnchorLocation;
+ private final byte[] mActiveRangingRounds;
+
+ public static final String KEY_BUNDLE_VERSION = "bundle_version";
+ public static final String MESSAGE_TYPE = "message_type";
+ public static final String MESSAGE_CONTROL = "message_control";
+ public static final String BLOCK_INDEX = "block_index";
+ public static final String ROUND_INDEX = "round_index";
+ public static final String NLOS = "nlos";
+ public static final String RSSI = "rssi";
+ public static final String TX_TIMESTAMP = "tx_timestamp";
+ public static final String RX_TIMESTAMP = "rx_timestamp";
+ public static final String ANCHOR_CFO = "anchor_cfo";
+ public static final String CFO = "cfo";
+ public static final String INITIATOR_REPLY_TIME = "initiator_reply_time";
+ public static final String RESPONDER_REPLY_TIME = "responder_reply_time";
+ public static final String INITIATOR_RESPONDER_TOF = "initiator_responder_time";
+ public static final String ANCHOR_LOCATION = "anchor_location";
+ public static final String ACTIVE_RANGING_ROUNDS = "active_ranging_rounds";
+
+ private static final int BUNDLE_VERSION_1 = 1;
+ private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_1;
+
+ public DlTDoAMeasurement(int messageType, int messageControl, int blockIndex, int roundIndex,
+ int NLoS, long txTimestamp, long rxTimestamp, int anchorCfo, int cfo,
+ long initiatorReplyTime, long responderReplyTime, int initiatorResponderTof,
+ byte[] anchorLocation, byte[] activeRangingRounds) {
+ mMessageType = messageType;
+ mMessageControl = messageControl;
+ mBlockIndex = blockIndex;
+ mRoundIndex = roundIndex;
+ mNLoS = NLoS;
+ mTxTimestamp = txTimestamp;
+ mRxTimestamp = rxTimestamp;
+ mAnchorCfo = anchorCfo;
+ mCfo = cfo;
+ mInitiatorReplyTime = initiatorReplyTime;
+ mResponderReplyTime = responderReplyTime;
+ mInitiatorResponderTof = initiatorResponderTof;
+ mAnchorLocation = anchorLocation;
+ mActiveRangingRounds = activeRangingRounds;
+ }
+
+ protected int getBundleVersion() {
+ return BUNDLE_VERSION_CURRENT;
+ }
+
+ public int getMessageType() {
+ return mMessageType;
+ }
+
+ public int getMessageControl() {
+ return mMessageControl;
+ }
+
+ public int getBlockIndex() {
+ return mBlockIndex;
+ }
+
+ public int getRoundIndex() {
+ return mRoundIndex;
+ }
+
+ public int getNLoS() {
+ return mNLoS;
+ }
+
+ public long getTxTimestamp() {
+ return mTxTimestamp;
+ }
+
+ public long getRxTimestamp() {
+ return mRxTimestamp;
+ }
+
+ public int getAnchorCfo() {
+ return mAnchorCfo;
+ }
+
+ public int getCfo() {
+ return mCfo;
+ }
+
+ public long getInitiatorReplyTime() {
+ return mInitiatorReplyTime;
+ }
+
+ public long getResponderReplyTime() {
+ return mResponderReplyTime;
+ }
+
+ public int getInitiatorResponderTof() {
+ return mInitiatorResponderTof;
+ }
+
+ public byte[] getAnchorLocation() {
+ return mAnchorLocation;
+ }
+
+ public byte[] getActiveRangingRounds() {
+ return mActiveRangingRounds;
+ }
+
+ @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 = new PersistableBundle();
+ bundle.putInt(KEY_BUNDLE_VERSION, BUNDLE_VERSION_CURRENT);
+ bundle.putInt(MESSAGE_TYPE, mMessageType);
+ bundle.putInt(MESSAGE_CONTROL, mMessageControl);
+ bundle.putInt(BLOCK_INDEX, mBlockIndex);
+ bundle.putInt(ROUND_INDEX, mRoundIndex);
+ bundle.putInt(NLOS, mNLoS);
+ bundle.putLong(TX_TIMESTAMP, mTxTimestamp);
+ bundle.putLong(RX_TIMESTAMP, mRxTimestamp);
+ bundle.putInt(ANCHOR_CFO, mAnchorCfo);
+ bundle.putInt(CFO, mCfo);
+ bundle.putLong(INITIATOR_REPLY_TIME, mInitiatorReplyTime);
+ bundle.putLong(RESPONDER_REPLY_TIME, mResponderReplyTime);
+ bundle.putInt(INITIATOR_RESPONDER_TOF, mInitiatorResponderTof);
+ bundle.putIntArray(ANCHOR_LOCATION, byteArrayToIntArray(mAnchorLocation));
+ bundle.putIntArray(ACTIVE_RANGING_ROUNDS, byteArrayToIntArray(mActiveRangingRounds));
+ return bundle;
+ }
+
+ public static DlTDoAMeasurement 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 DlTDoAMeasurement parseVersion1(PersistableBundle bundle) {
+ return new DlTDoAMeasurement.Builder()
+ .setMessageType(bundle.getInt(MESSAGE_TYPE))
+ .setMessageControl(bundle.getInt(MESSAGE_CONTROL))
+ .setBlockIndex(bundle.getInt(BLOCK_INDEX))
+ .setRoundIndex(bundle.getInt(ROUND_INDEX))
+ .setNLoS(bundle.getInt(NLOS))
+ .setTxTimestamp(bundle.getLong(TX_TIMESTAMP))
+ .setRxTimestamp(bundle.getLong(RX_TIMESTAMP))
+ .setAnchorCfo(bundle.getInt(ANCHOR_CFO))
+ .setCfo(bundle.getInt(CFO))
+ .setInitiatorReplyTime(bundle.getLong(INITIATOR_REPLY_TIME))
+ .setResponderReplyTime(bundle.getLong(RESPONDER_REPLY_TIME))
+ .setInitiatorResponderTof(bundle.getInt(INITIATOR_RESPONDER_TOF))
+ .setAnchorLocation(intArrayToByteArray(bundle.getIntArray(ANCHOR_LOCATION)))
+ .setActiveRangingRounds(
+ intArrayToByteArray(bundle.getIntArray(ACTIVE_RANGING_ROUNDS)))
+ .build();
+ }
+
+ /** Builder */
+ public static class Builder {
+ private int mMessageType;
+ private int mMessageControl;
+ private int mBlockIndex;
+ private int mRoundIndex;
+ private int mNLoS;
+ private long mTxTimestamp;
+ private long mRxTimestamp;
+ private int mAnchorCfo;
+ private int mCfo;
+ private long mInitiatorReplyTime;
+ private long mResponderReplyTime;
+ private int mInitiatorResponderTof;
+ private byte[] mAnchorLocation;
+ private byte[] mActiveRangingRounds;
+
+ public DlTDoAMeasurement.Builder setMessageType(int messageType) {
+ mMessageType = messageType;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setMessageControl(int messageControl) {
+ mMessageControl = messageControl;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setBlockIndex(int blockIndex) {
+ mBlockIndex = blockIndex;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setRoundIndex(int roundIndex) {
+ mRoundIndex = roundIndex;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setNLoS(int nLoS) {
+ mNLoS = nLoS;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setTxTimestamp(long txTimestamp) {
+ mTxTimestamp = txTimestamp;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setRxTimestamp(long rxTimestamp) {
+ mRxTimestamp = rxTimestamp;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setAnchorCfo(int anchorCfo) {
+ mAnchorCfo = anchorCfo;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setCfo(int cfo) {
+ mCfo = cfo;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setInitiatorReplyTime(long initiatorReplyTime) {
+ mInitiatorReplyTime = initiatorReplyTime;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setResponderReplyTime(long responderReplyTime) {
+ mResponderReplyTime = responderReplyTime;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setInitiatorResponderTof(int initiatorResponderTof) {
+ mInitiatorResponderTof = initiatorResponderTof;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setAnchorLocation(byte[] anchorLocation) {
+ mAnchorLocation = anchorLocation;
+ return this;
+ }
+
+ public DlTDoAMeasurement.Builder setActiveRangingRounds(byte[] activeRangingRounds) {
+ mActiveRangingRounds = activeRangingRounds;
+ return this;
+ }
+
+ public DlTDoAMeasurement build() {
+ return new DlTDoAMeasurement(
+ mMessageType,
+ mMessageControl,
+ mBlockIndex,
+ mRoundIndex,
+ mNLoS,
+ mTxTimestamp,
+ mRxTimestamp,
+ mAnchorCfo,
+ mCfo,
+ mInitiatorReplyTime,
+ mResponderReplyTime,
+ mInitiatorResponderTof,
+ mAnchorLocation,
+ mActiveRangingRounds);
+ }
+ }
+}
diff --git a/service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoARangingRoundsUpdate.java b/service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoARangingRoundsUpdate.java
new file mode 100644
index 00000000..25bda81c
--- /dev/null
+++ b/service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoARangingRoundsUpdate.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 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.dltdoa;
+
+import android.os.PersistableBundle;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Used to send SESSION_UPDATE_ACTIVE_ROUNDS_DT_TAG_CMD
+ *
+ * <p> This is passed as a bundle to update ranging rounds for DT Tag
+ */
+public class DlTDoARangingRoundsUpdate {
+ private final long mSessionId;
+ private final int mNoOfActiveRangingRounds;
+ private final byte[] mRangingRoundIndexes;
+
+ private static final int BUNDLE_VERSION_1 = 1;
+ private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_1;
+
+ public static final String KEY_BUNDLE_VERSION = "bundle_version";
+ public static final String SESSION_ID = "session_id";
+ public static final String NO_OF_ACTIVE_RANGING_ROUNDS = "no_active_ranging_rounds";
+ public static final String RANGING_ROUND_INDEXES = "ranging_round_indexes";
+
+
+ private DlTDoARangingRoundsUpdate(long sessionId, int noOfActiveRangingRounds,
+ byte[] rangingRoundIndexes) {
+ mSessionId = sessionId;
+ mNoOfActiveRangingRounds = noOfActiveRangingRounds;
+ mRangingRoundIndexes = rangingRoundIndexes;
+ }
+
+ public int getBundleVersion() {
+ return BUNDLE_VERSION_CURRENT;
+ }
+
+ public long getSessionId() {
+ return mSessionId;
+ }
+
+ public int getNoOfActiveRangingRounds() {
+ return mNoOfActiveRangingRounds;
+ }
+
+ public byte[] getRangingRoundIndexes() {
+ return mRangingRoundIndexes;
+ }
+
+ @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 = new PersistableBundle();
+ bundle.putInt(KEY_BUNDLE_VERSION, BUNDLE_VERSION_CURRENT);
+ bundle.putLong(SESSION_ID, mSessionId);
+ bundle.putInt(NO_OF_ACTIVE_RANGING_ROUNDS, mNoOfActiveRangingRounds);
+ bundle.putIntArray(RANGING_ROUND_INDEXES, byteArrayToIntArray(mRangingRoundIndexes));
+ return bundle;
+ }
+
+ public static DlTDoARangingRoundsUpdate 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 DlTDoARangingRoundsUpdate parseVersion1(PersistableBundle bundle) {
+ return new DlTDoARangingRoundsUpdate.Builder()
+ .setSessionId(bundle.getLong(SESSION_ID))
+ .setNoOfActiveRangingRounds(bundle.getInt(NO_OF_ACTIVE_RANGING_ROUNDS))
+ .setRangingRoundIndexes(
+ intArrayToByteArray(bundle.getIntArray(RANGING_ROUND_INDEXES)))
+ .build();
+ }
+
+ /** Builder */
+ public static class Builder {
+ private long mSessionId = 0;
+ private int mNoOfActiveRangingRounds = 0;
+ private byte[] mRangingRoundIndexes = new byte[]{};
+
+ public DlTDoARangingRoundsUpdate.Builder setSessionId(long sessionId) {
+ mSessionId = sessionId;
+ return this;
+ }
+
+ public DlTDoARangingRoundsUpdate.Builder setNoOfActiveRangingRounds(
+ int activeRangingRounds) {
+ mNoOfActiveRangingRounds = activeRangingRounds;
+ return this;
+ }
+
+ public DlTDoARangingRoundsUpdate.Builder setRangingRoundIndexes(
+ byte[] rangingRoundIndexes) {
+ mRangingRoundIndexes = rangingRoundIndexes;
+ return this;
+ }
+
+ public DlTDoARangingRoundsUpdate build() {
+ return new DlTDoARangingRoundsUpdate(
+ mSessionId,
+ mNoOfActiveRangingRounds,
+ mRangingRoundIndexes);
+ }
+ }
+}
diff --git a/service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoARangingRoundsUpdateStatus.java b/service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoARangingRoundsUpdateStatus.java
new file mode 100644
index 00000000..753a4eb8
--- /dev/null
+++ b/service/support_lib/src/com/google/uwb/support/dltdoa/DlTDoARangingRoundsUpdateStatus.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 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.dltdoa;
+
+import android.os.PersistableBundle;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Used to send SESSION_UPDATE_ACTIVE_ROUNDS_DT_TAG_RSP
+ *
+ * <p> This is passed as a bundle for response received for command
+ * SESSION_UPDATE_ACTIVE_ROUNDS_DT_TAG_CMD
+ */
+public class DlTDoARangingRoundsUpdateStatus {
+ private final int mStatus;
+ private final int mNoOfActiveRangingRounds;
+ private final byte[] mRangingRoundIndexes;
+
+ private static final int BUNDLE_VERSION_1 = 1;
+ private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_1;
+
+ public static final String KEY_BUNDLE_VERSION = "bundle_version";
+ public static final String STATUS = "status";
+ public static final String NO_OF_ACTIVE_RANGING_ROUNDS = "no_active_ranging_rounds";
+ public static final String RANGING_ROUND_INDEXES = "ranging_round_indexes";
+
+
+ private DlTDoARangingRoundsUpdateStatus(int status, int noOfActiveRangingRounds,
+ byte[] rangingRoundIndexes) {
+ mStatus = status;
+ mNoOfActiveRangingRounds = noOfActiveRangingRounds;
+ mRangingRoundIndexes = rangingRoundIndexes;
+ }
+
+ public int getBundleVersion() {
+ return BUNDLE_VERSION_CURRENT;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public int getNoOfActiveRangingRounds() {
+ return mNoOfActiveRangingRounds;
+ }
+
+ public byte[] getRangingRoundIndexes() {
+ return mRangingRoundIndexes;
+ }
+
+ @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 = new PersistableBundle();
+ bundle.putInt(KEY_BUNDLE_VERSION, BUNDLE_VERSION_CURRENT);
+ bundle.putInt(STATUS, mStatus);
+ bundle.putInt(NO_OF_ACTIVE_RANGING_ROUNDS, mNoOfActiveRangingRounds);
+ bundle.putIntArray(RANGING_ROUND_INDEXES, byteArrayToIntArray(mRangingRoundIndexes));
+ return bundle;
+ }
+
+ public static DlTDoARangingRoundsUpdateStatus 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 DlTDoARangingRoundsUpdateStatus parseVersion1(PersistableBundle bundle) {
+ return new DlTDoARangingRoundsUpdateStatus.Builder()
+ .setStatus(bundle.getInt(STATUS))
+ .setNoOfActiveRangingRounds(bundle.getInt(NO_OF_ACTIVE_RANGING_ROUNDS))
+ .setRangingRoundIndexes(
+ intArrayToByteArray(bundle.getIntArray(RANGING_ROUND_INDEXES)))
+ .build();
+ }
+
+ /** Builder */
+ public static class Builder {
+ private int mStatus = 0;
+ private int mNoOfActiveRangingRounds = 0;
+ private byte[] mRangingRoundIndexes = new byte[]{};
+
+ public DlTDoARangingRoundsUpdateStatus.Builder setStatus(int status) {
+ mStatus = status;
+ return this;
+ }
+
+ public DlTDoARangingRoundsUpdateStatus.Builder setNoOfActiveRangingRounds(
+ int activeRangingRounds) {
+ mNoOfActiveRangingRounds = activeRangingRounds;
+ return this;
+ }
+
+ public DlTDoARangingRoundsUpdateStatus.Builder setRangingRoundIndexes(
+ byte[] rangingRoundIndexes) {
+ mRangingRoundIndexes = rangingRoundIndexes;
+ return this;
+ }
+
+ public DlTDoARangingRoundsUpdateStatus build() {
+ return new DlTDoARangingRoundsUpdateStatus(
+ mStatus,
+ mNoOfActiveRangingRounds,
+ mRangingRoundIndexes);
+ }
+ }
+}
diff --git a/service/support_lib/src/com/google/uwb/support/fira/FiraControleeParams.java b/service/support_lib/src/com/google/uwb/support/fira/FiraControleeParams.java
index 89df6923..2930efd1 100644
--- a/service/support_lib/src/com/google/uwb/support/fira/FiraControleeParams.java
+++ b/service/support_lib/src/com/google/uwb/support/fira/FiraControleeParams.java
@@ -16,14 +16,13 @@
package com.google.uwb.support.fira;
-import static com.android.internal.util.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
import android.os.PersistableBundle;
import android.uwb.RangingSession;
import android.uwb.UwbAddress;
-import android.uwb.UwbManager;
import androidx.annotation.Nullable;
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 5b9cee07..d91b08c3 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
@@ -16,8 +16,8 @@
package com.google.uwb.support.fira;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Objects.requireNonNull;
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 c10688ea..c4772bdd 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
@@ -150,6 +150,8 @@ public abstract class FiraParams extends Params {
RANGING_DEVICE_ROLE_INITIATOR,
RANGING_DEVICE_ROLE_ADVERTISER,
RANGING_DEVICE_ROLE_OBSERVER,
+ RANGING_DEVICE_DT_ANCHOR,
+ RANGING_DEVICE_DT_TAG,
})
public @interface RangingDeviceRole {}
@@ -161,6 +163,10 @@ public abstract class FiraParams extends Params {
public static final int RANGING_DEVICE_ROLE_OBSERVER = 6;
+ public static final int RANGING_DEVICE_DT_ANCHOR = 7;
+
+ public static final int RANGING_DEVICE_DT_TAG = 8;
+
/** Ranging Round Usage */
@IntDef(
value = {
@@ -169,6 +175,7 @@ public abstract class FiraParams extends Params {
RANGING_ROUND_USAGE_SS_TWR_NON_DEFERRED_MODE,
RANGING_ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE,
RANGING_ROUND_USAGE_OWR_AOA_MEASUREMENT,
+ RANGING_ROUND_USAGE_DL_TDOA,
})
public @interface RangingRoundUsage {}
@@ -184,6 +191,9 @@ public abstract class FiraParams extends Params {
/** Double-sided two-way ranging, non-deferred */
public static final int RANGING_ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE = 4;
+ /** Downlink Time Difference of Arrival */
+ public static final int RANGING_ROUND_USAGE_DL_TDOA = 5;
+
/** OWR for AoA measurement */
public static final int RANGING_ROUND_USAGE_OWR_AOA_MEASUREMENT = 6;
@@ -466,6 +476,7 @@ public abstract class FiraParams extends Params {
STATUS_CODE_ERROR_MULTICAST_LIST_FULL,
STATUS_CODE_ERROR_ADDRESS_NOT_FOUND,
STATUS_CODE_ERROR_ADDRESS_ALREADY_PRESENT,
+ STATUS_CODE_OK_NEGATIVE_DISTANCE_REPORT,
STATUS_CODE_RANGING_TX_FAILED,
STATUS_CODE_RANGING_RX_TIMEOUT,
STATUS_CODE_RANGING_RX_PHY_DEC_FAILED,
@@ -474,6 +485,10 @@ public abstract class FiraParams extends Params {
STATUS_CODE_RANGING_RX_MAC_DEC_FAILED,
STATUS_CODE_RANGING_RX_MAC_IE_DEC_FAILED,
STATUS_CODE_RANGING_RX_MAC_IE_MISSING,
+ STATUS_CODE_ERROR_ROUND_INDEX_NOT_ACTIVATED,
+ STATUS_CODE_ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED,
+ STATUS_CODE_ERROR_ROUND_INDEX_NOT_SET_AS_INITIATOR,
+ STATUS_CODE_ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST,
})
public @interface StatusCode {}
@@ -497,6 +512,7 @@ public abstract class FiraParams extends Params {
public static final int STATUS_CODE_ERROR_MULTICAST_LIST_FULL = 0x17;
public static final int STATUS_CODE_ERROR_ADDRESS_NOT_FOUND = 0x18;
public static final int STATUS_CODE_ERROR_ADDRESS_ALREADY_PRESENT = 0x19;
+ public static final int STATUS_CODE_OK_NEGATIVE_DISTANCE_REPORT = 0x1B;
public static final int STATUS_CODE_RANGING_TX_FAILED = 0x20;
public static final int STATUS_CODE_RANGING_RX_TIMEOUT = 0x21;
public static final int STATUS_CODE_RANGING_RX_PHY_DEC_FAILED = 0x22;
@@ -505,6 +521,11 @@ public abstract class FiraParams extends Params {
public static final int STATUS_CODE_RANGING_RX_MAC_DEC_FAILED = 0x25;
public static final int STATUS_CODE_RANGING_RX_MAC_IE_DEC_FAILED = 0x26;
public static final int STATUS_CODE_RANGING_RX_MAC_IE_MISSING = 0x27;
+ public static final int STATUS_CODE_ERROR_ROUND_INDEX_NOT_ACTIVATED = 0X28;
+ public static final int STATUS_CODE_ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED = 0X29;
+ public static final int STATUS_CODE_ERROR_ROUND_INDEX_NOT_SET_AS_INITIATOR = 0X2A;
+ public static final int
+ STATUS_CODE_ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST = 0X2B;
/** State change reason codes defined in UCI table-15 */
@IntDef(
diff --git a/service/support_lib/src/com/google/uwb/support/fira/FiraRangingReconfigureParams.java b/service/support_lib/src/com/google/uwb/support/fira/FiraRangingReconfigureParams.java
index 3c741138..cb74f5f9 100644
--- a/service/support_lib/src/com/google/uwb/support/fira/FiraRangingReconfigureParams.java
+++ b/service/support_lib/src/com/google/uwb/support/fira/FiraRangingReconfigureParams.java
@@ -16,7 +16,7 @@
package com.google.uwb.support.fira;
-import static com.android.internal.util.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
diff --git a/service/support_lib/src/com/google/uwb/support/fira/FiraSpecificationParams.java b/service/support_lib/src/com/google/uwb/support/fira/FiraSpecificationParams.java
index bb373ae0..9beacad3 100644
--- a/service/support_lib/src/com/google/uwb/support/fira/FiraSpecificationParams.java
+++ b/service/support_lib/src/com/google/uwb/support/fira/FiraSpecificationParams.java
@@ -60,6 +60,8 @@ public class FiraSpecificationParams extends FiraParams {
private final int mMinRangingInterval;
+ private final int mMinSlotDuration;
+
private final EnumSet<MultiNodeCapabilityFlag> mMultiNodeCapabilities;
private final EnumSet<PrfCapabilityFlag> mPrfCapabilities;
@@ -92,6 +94,7 @@ public class FiraSpecificationParams extends FiraParams {
private static final String KEY_RSSI_REPORTING_SUPPORT = "rssi_reporting";
private static final String KEY_DIAGNOSTICS_SUPPORT = "diagnostics";
private static final String KEY_MIN_RANGING_INTERVAL = "min_ranging_interval";
+ private static final String KEY_MIN_SLOT_DURATION = "min_slot_duration";
private static final String KEY_MULTI_NODE_CAPABILITIES = "multi_node_capabilities";
private static final String KEY_PRF_CAPABILITIES = "prf_capabilities";
private static final String KEY_RANGING_ROUND_CAPABILITIES = "ranging_round_capabilities";
@@ -119,6 +122,7 @@ public class FiraSpecificationParams extends FiraParams {
boolean hasRssiReportingSupport,
boolean hasDiagnosticsSupport,
int minRangingInterval,
+ int minSlotDuration,
EnumSet<MultiNodeCapabilityFlag> multiNodeCapabilities,
EnumSet<PrfCapabilityFlag> prfCapabilities,
EnumSet<RangingRoundCapabilityFlag> rangingRoundCapabilities,
@@ -141,6 +145,7 @@ public class FiraSpecificationParams extends FiraParams {
mHasRssiReportingSupport = hasRssiReportingSupport;
mHasDiagnosticsSupport = hasDiagnosticsSupport;
mMinRangingInterval = minRangingInterval;
+ mMinSlotDuration = minSlotDuration;
mMultiNodeCapabilities = multiNodeCapabilities;
mPrfCapabilities = prfCapabilities;
mRangingRoundCapabilities = rangingRoundCapabilities;
@@ -211,6 +216,10 @@ public class FiraSpecificationParams extends FiraParams {
return mMinRangingInterval;
}
+ public int getMinSlotDuration() {
+ return mMinSlotDuration;
+ }
+
public EnumSet<MultiNodeCapabilityFlag> getMultiNodeCapabilities() {
return mMultiNodeCapabilities;
}
@@ -271,6 +280,7 @@ public class FiraSpecificationParams extends FiraParams {
bundle.putBoolean(KEY_RSSI_REPORTING_SUPPORT, mHasRssiReportingSupport);
bundle.putBoolean(KEY_DIAGNOSTICS_SUPPORT, mHasDiagnosticsSupport);
bundle.putInt(KEY_MIN_RANGING_INTERVAL, mMinRangingInterval);
+ bundle.putInt(KEY_MIN_SLOT_DURATION, mMinSlotDuration);
bundle.putInt(KEY_MULTI_NODE_CAPABILITIES, FlagEnum.toInt(mMultiNodeCapabilities));
bundle.putInt(KEY_PRF_CAPABILITIES, FlagEnum.toInt(mPrfCapabilities));
bundle.putInt(KEY_RANGING_ROUND_CAPABILITIES, FlagEnum.toInt(mRangingRoundCapabilities));
@@ -332,6 +342,7 @@ public class FiraSpecificationParams extends FiraParams {
.hasNonDeferredModeSupport(bundle.getBoolean(KEY_NON_DEFERRED_MODE_SUPPORT))
.hasInitiationTimeSupport(bundle.getBoolean(KEY_INITIATION_TIME_SUPPORT))
.setMinRangingIntervalSupported(bundle.getInt(KEY_MIN_RANGING_INTERVAL, -1))
+ .setMinSlotDurationSupported(bundle.getInt(KEY_MIN_SLOT_DURATION, -1))
.setMultiNodeCapabilities(
FlagEnum.toEnumSet(
bundle.getInt(KEY_MULTI_NODE_CAPABILITIES),
@@ -413,6 +424,8 @@ public class FiraSpecificationParams extends FiraParams {
private int mMinRangingInterval = -1;
+ private int mMinSlotDuration = -1;
+
// Unicast support is mandatory
private final EnumSet<MultiNodeCapabilityFlag> mMultiNodeCapabilities =
EnumSet.of(MultiNodeCapabilityFlag.HAS_UNICAST_SUPPORT);
@@ -522,6 +535,16 @@ public class FiraSpecificationParams extends FiraParams {
return this;
}
+ /**
+ * Set minimum supported slot duration
+ * @param value : minimum slot duration supported
+ * @return FiraSpecificationParams builder
+ */
+ public FiraSpecificationParams.Builder setMinSlotDurationSupported(int value) {
+ mMinSlotDuration = value;
+ return this;
+ }
+
public FiraSpecificationParams.Builder setMultiNodeCapabilities(
Collection<MultiNodeCapabilityFlag> multiNodeCapabilities) {
mMultiNodeCapabilities.addAll(multiNodeCapabilities);
@@ -595,6 +618,7 @@ public class FiraSpecificationParams extends FiraParams {
mHasRssiReportingSupport,
mHasDiagnosticsSupport,
mMinRangingInterval,
+ mMinSlotDuration,
mMultiNodeCapabilities,
mPrfCapabilities,
mRangingRoundCapabilities,
diff --git a/service/support_lib/src/com/google/uwb/support/generic/GenericSpecificationParams.java b/service/support_lib/src/com/google/uwb/support/generic/GenericSpecificationParams.java
index 55b10292..0abd3aa9 100644
--- a/service/support_lib/src/com/google/uwb/support/generic/GenericSpecificationParams.java
+++ b/service/support_lib/src/com/google/uwb/support/generic/GenericSpecificationParams.java
@@ -16,10 +16,11 @@
package com.google.uwb.support.generic;
-import android.annotation.NonNull;
import android.os.PersistableBundle;
import android.uwb.UwbManager;
+import androidx.annotation.NonNull;
+
import com.google.uwb.support.base.RequiredParam;
import com.google.uwb.support.ccc.CccParams;
import com.google.uwb.support.ccc.CccSpecificationParams;
diff --git a/service/support_lib/src/com/google/uwb/support/profile/UuidBundleWrapper.java b/service/support_lib/src/com/google/uwb/support/profile/UuidBundleWrapper.java
index 53757bbc..5bfed828 100644
--- a/service/support_lib/src/com/google/uwb/support/profile/UuidBundleWrapper.java
+++ b/service/support_lib/src/com/google/uwb/support/profile/UuidBundleWrapper.java
@@ -47,7 +47,7 @@ public class UuidBundleWrapper {
public PersistableBundle toBundle() {
PersistableBundle bundle = new PersistableBundle();
bundle.putInt(KEY_BUNDLE_VERSION, getBundleVersion());
- if (!mServiceInstanceID.isEmpty()) {
+ if (mServiceInstanceID.isPresent()) {
bundle.putString(SERVICE_INSTANCE_ID, mServiceInstanceID.get().toString());
}
return bundle;
diff --git a/service/support_lib/test/Android.bp b/service/support_lib/test/Android.bp
index 26639f74..14f91a1f 100644
--- a/service/support_lib/test/Android.bp
+++ b/service/support_lib/test/Android.bp
@@ -13,6 +13,7 @@ android_test {
"com.uwb.support.generic",
"com.uwb.support.multichip",
"com.uwb.support.oemextension",
+ "com.uwb.support.dltdoa",
],
platform_apis: true,
certificate: "platform",
diff --git a/service/support_lib/test/DlTDoATests.java b/service/support_lib/test/DlTDoATests.java
new file mode 100644
index 00000000..e51997f9
--- /dev/null
+++ b/service/support_lib/test/DlTDoATests.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.google.uwb.support.dltdoa.DlTDoAMeasurement;
+import com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdate;
+import com.google.uwb.support.dltdoa.DlTDoARangingRoundsUpdateStatus;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DlTDoATests {
+
+ @Test
+ public void dlTDoAMeasurementTest() {
+ int messageType = 0x02;
+ int messageControl = 0x513;
+ int blockIndex = 4;
+ int roundIndex = 6;
+ int nLoS = 40;
+ long txTimestamp = 40_000L;
+ long rxTimestamp = 50_000L;
+ int anchorCfo = 433;
+ int cfo = 0x56;
+ long initiatorReplyTime = 100;
+ long responderReplyTime = 200;
+ int initiatorResponderTof = 400;
+ byte[] anchorLocation = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+ byte[] activeRangingRounds = new byte[]{0x01, 0x02};
+
+ DlTDoAMeasurement dlTDoAMeasurement = new DlTDoAMeasurement.Builder()
+ .setMessageType(messageType)
+ .setMessageControl(messageControl)
+ .setBlockIndex(blockIndex)
+ .setRoundIndex(roundIndex)
+ .setNLoS(nLoS)
+ .setTxTimestamp(txTimestamp)
+ .setRxTimestamp(rxTimestamp)
+ .setAnchorCfo(anchorCfo)
+ .setCfo(cfo)
+ .setInitiatorReplyTime(initiatorReplyTime)
+ .setResponderReplyTime(responderReplyTime)
+ .setInitiatorResponderTof(initiatorResponderTof)
+ .setAnchorLocation(anchorLocation)
+ .setActiveRangingRounds(activeRangingRounds)
+ .build();
+
+ DlTDoAMeasurement fromBundle = DlTDoAMeasurement.fromBundle(dlTDoAMeasurement.toBundle());
+
+ assertEquals(fromBundle.getMessageType(), messageType);
+ assertEquals(fromBundle.getMessageControl(), messageControl);
+ assertEquals(fromBundle.getBlockIndex(), blockIndex);
+ assertEquals(fromBundle.getRoundIndex(), roundIndex);
+ assertEquals(fromBundle.getNLoS(), nLoS);
+ assertEquals(fromBundle.getTxTimestamp(), txTimestamp);
+ assertEquals(fromBundle.getRxTimestamp(), rxTimestamp);
+ assertEquals(fromBundle.getAnchorCfo(), anchorCfo);
+ assertEquals(fromBundle.getCfo(), cfo);
+ assertEquals(fromBundle.getInitiatorReplyTime(), initiatorReplyTime);
+ assertEquals(fromBundle.getResponderReplyTime(), responderReplyTime);
+ assertEquals(fromBundle.getInitiatorResponderTof(), initiatorResponderTof);
+ assertArrayEquals(fromBundle.getAnchorLocation(), anchorLocation);
+ assertArrayEquals(fromBundle.getActiveRangingRounds(), activeRangingRounds);
+ }
+
+ @Test
+ public void dlTDoARangingRoundsUpdateTest() {
+ int sessionId = 1234;
+ int noOfActiveRangingRounds = 3;
+ byte[] rangingRoundIndexes = new byte[]{0x01, 0x02, 0x03};
+
+ DlTDoARangingRoundsUpdate dlTDoARangingRoundsUpdate = new DlTDoARangingRoundsUpdate
+ .Builder()
+ .setSessionId(sessionId)
+ .setNoOfActiveRangingRounds(noOfActiveRangingRounds)
+ .setRangingRoundIndexes(rangingRoundIndexes)
+ .build();
+
+ DlTDoARangingRoundsUpdate fromBundle = DlTDoARangingRoundsUpdate.fromBundle(
+ dlTDoARangingRoundsUpdate.toBundle());
+
+ assertEquals(fromBundle.getSessionId(), sessionId);
+ assertEquals(fromBundle.getNoOfActiveRangingRounds(), noOfActiveRangingRounds);
+ assertArrayEquals(fromBundle.getRangingRoundIndexes(), rangingRoundIndexes);
+ }
+
+ @Test
+ public void dlTDoARangingRoundsUpdateStatusTest() {
+ int status = 1;
+ int noOfActiveRangingRounds = 2;
+ byte[] rangingRoundIndexes = new byte[]{0x02, 0x03};
+
+ DlTDoARangingRoundsUpdateStatus dlTDoARangingRoundsUpdateStatus =
+ new DlTDoARangingRoundsUpdateStatus.Builder()
+ .setStatus(status)
+ .setNoOfActiveRangingRounds(noOfActiveRangingRounds)
+ .setRangingRoundIndexes(rangingRoundIndexes)
+ .build();
+
+ DlTDoARangingRoundsUpdateStatus fromBundle = DlTDoARangingRoundsUpdateStatus.fromBundle(
+ dlTDoARangingRoundsUpdateStatus.toBundle());
+
+ assertEquals(fromBundle.getStatus(), status);
+ assertEquals(fromBundle.getNoOfActiveRangingRounds(), noOfActiveRangingRounds);
+ assertArrayEquals(fromBundle.getRangingRoundIndexes(), rangingRoundIndexes);
+ }
+}
diff --git a/service/tests/src/com/android/server/uwb/DeviceConfigFacadeTest.java b/service/tests/src/com/android/server/uwb/DeviceConfigFacadeTest.java
index 2ef232ef..59025a51 100644
--- a/service/tests/src/com/android/server/uwb/DeviceConfigFacadeTest.java
+++ b/service/tests/src/com/android/server/uwb/DeviceConfigFacadeTest.java
@@ -113,7 +113,7 @@ public class DeviceConfigFacadeTest {
public void testDefaultValue() throws Exception {
assertEquals(DeviceConfigFacade.DEFAULT_RANGING_RESULT_LOG_INTERVAL_MS,
mDeviceConfigFacade.getRangingResultLogIntervalMs());
- assertEquals(true, mDeviceConfigFacade.isDeviceErrorBugreportEnabled());
+ assertEquals(false, mDeviceConfigFacade.isDeviceErrorBugreportEnabled());
assertEquals(DeviceConfigFacade.DEFAULT_BUG_REPORT_MIN_INTERVAL_MS,
mDeviceConfigFacade.getBugReportMinIntervalMs());
}
diff --git a/service/tests/src/com/android/server/uwb/UwbMetricsTest.java b/service/tests/src/com/android/server/uwb/UwbMetricsTest.java
index 16b127a0..ce9340c2 100644
--- a/service/tests/src/com/android/server/uwb/UwbMetricsTest.java
+++ b/service/tests/src/com/android/server/uwb/UwbMetricsTest.java
@@ -65,6 +65,7 @@ public class UwbMetricsTest {
private static final int NLOS_DEFAULT = 1;
private static final int VALID_RANGING_COUNT = 5;
private static final int RSSI_DEFAULT_DBM = -75;
+ private static final boolean IS_STATUS_CODE_OK_DEFAULT = true;
@Mock
private UwbInjector mUwbInjector;
@Mock
@@ -93,6 +94,7 @@ public class UwbMetricsTest {
when(mRangingData.getRangingMeasuresType()).thenReturn(
(int) UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY);
when(mTwoWayMeasurement.getRangingStatus()).thenReturn(FiraParams.STATUS_CODE_OK);
+ when(mTwoWayMeasurement.isStatusCodeOk()).thenReturn(IS_STATUS_CODE_OK_DEFAULT);
when(mRangingData.getRangingTwoWayMeasures()).thenReturn(mTwoWayMeasurements);
when(mUwbSession.getSessionId()).thenReturn(1);
@@ -165,6 +167,7 @@ public class UwbMetricsTest {
mRangingData);
}
when(mTwoWayMeasurement.getRangingStatus()).thenReturn(UwbUciConstants.STATUS_CODE_FAILED);
+ when(mTwoWayMeasurement.isStatusCodeOk()).thenReturn(!IS_STATUS_CODE_OK_DEFAULT);
mUwbMetrics.logRangingResult(UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__FIRA,
mRangingData);
diff --git a/service/tests/src/com/android/server/uwb/UwbServiceImplTest.java b/service/tests/src/com/android/server/uwb/UwbServiceImplTest.java
index 79958242..c8863068 100644
--- a/service/tests/src/com/android/server/uwb/UwbServiceImplTest.java
+++ b/service/tests/src/com/android/server/uwb/UwbServiceImplTest.java
@@ -29,6 +29,7 @@ import static com.google.uwb.support.fira.FiraParams.RANGE_DATA_NTF_CONFIG_ENABL
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -47,6 +48,7 @@ import android.content.Intent;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.Process;
+import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
@@ -59,6 +61,7 @@ import android.uwb.UwbAddress;
import androidx.test.runner.AndroidJUnit4;
+import com.android.modules.utils.build.SdkLevel;
import com.android.server.uwb.data.UwbUciConstants;
import com.android.server.uwb.jni.NativeUwbManager;
import com.android.server.uwb.multchip.UwbMultichipData;
@@ -72,6 +75,7 @@ import com.google.uwb.support.profile.UuidBundleWrapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
@@ -103,11 +107,13 @@ public class UwbServiceImplTest {
@Mock private NativeUwbManager mNativeUwbManager;
@Mock private UwbMultichipData mUwbMultichipData;
@Mock private ProfileManager mProfileManager;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private UserManager mUserManager;
@Captor private ArgumentCaptor<IUwbRangingCallbacks> mRangingCbCaptor;
@Captor private ArgumentCaptor<IUwbRangingCallbacks> mRangingCbCaptor2;
@Captor private ArgumentCaptor<IBinder.DeathRecipient> mClientDeathCaptor;
@Captor private ArgumentCaptor<IBinder.DeathRecipient> mUwbServiceCoreDeathCaptor;
@Captor private ArgumentCaptor<BroadcastReceiver> mApmModeBroadcastReceiver;
+ @Captor private ArgumentCaptor<BroadcastReceiver> mUserRestrictionReceiver;
private UwbServiceImpl mUwbServiceImpl;
@@ -122,12 +128,17 @@ public class UwbServiceImplTest {
when(mUwbInjector.getMultichipData()).thenReturn(mUwbMultichipData);
when(mUwbInjector.getSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(0);
when(mUwbInjector.getNativeUwbManager()).thenReturn(mNativeUwbManager);
+ when(mUwbInjector.getUserManager()).thenReturn(mUserManager);
+ when(mUserManager.getUserRestrictions().getBoolean(anyString())).thenReturn(false);
mUwbServiceImpl = new UwbServiceImpl(mContext, mUwbInjector);
verify(mContext).registerReceiver(
mApmModeBroadcastReceiver.capture(),
argThat(i -> i.getAction(0).equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)));
+ verify(mContext).registerReceiver(
+ mUserRestrictionReceiver.capture(),
+ argThat(i -> i.getAction(0).equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)));
}
@Test
@@ -356,6 +367,25 @@ public class UwbServiceImplTest {
}
@Test
+ public void testUserRestrictionChanged() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
+
+ mUwbServiceImpl.setEnabled(true);
+
+ // User restriction changes to disallow UWB
+ when(mUserManager.getUserRestrictions().getBoolean(anyString())).thenReturn(true);
+ mUserRestrictionReceiver.getValue().onReceive(
+ mContext, new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
+ verify(mUwbServiceCore).setEnabled(false);
+
+ // User restriction changes to allow UWB
+ when(mUserManager.getUserRestrictions().getBoolean(anyString())).thenReturn(true);
+ mUserRestrictionReceiver.getValue().onReceive(
+ mContext, new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
+ verify(mUwbServiceCore, times(1)).setEnabled(true);
+ }
+
+ @Test
public void testToggleFromRootedShellWhenApmModeOn() throws Exception {
BinderUtil.setUid(Process.ROOT_UID);
when(mUwbInjector.getSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(1);
diff --git a/service/tests/src/com/android/server/uwb/UwbSessionManagerTest.java b/service/tests/src/com/android/server/uwb/UwbSessionManagerTest.java
index ec77fede..5795acd4 100644
--- a/service/tests/src/com/android/server/uwb/UwbSessionManagerTest.java
+++ b/service/tests/src/com/android/server/uwb/UwbSessionManagerTest.java
@@ -25,14 +25,24 @@ import static com.android.server.uwb.UwbTestUtils.PEER_BAD_MAC_ADDRESS;
import static com.android.server.uwb.UwbTestUtils.PEER_EXTENDED_MAC_ADDRESS;
import static com.android.server.uwb.UwbTestUtils.PEER_EXTENDED_MAC_ADDRESS_2;
import static com.android.server.uwb.UwbTestUtils.PEER_EXTENDED_SHORT_MAC_ADDRESS;
-import static com.android.server.uwb.UwbTestUtils.PEER_UWB_ADDRESS;
+import static com.android.server.uwb.UwbTestUtils.PEER_EXTENDED_UWB_ADDRESS;
+import static com.android.server.uwb.UwbTestUtils.PEER_SHORT_MAC_ADDRESS;
+import static com.android.server.uwb.UwbTestUtils.PEER_SHORT_UWB_ADDRESS;
import static com.android.server.uwb.UwbTestUtils.PERSISTABLE_BUNDLE;
+import static com.android.server.uwb.UwbTestUtils.RANGING_MEASUREMENT_TYPE_UNDEFINED;
import static com.android.server.uwb.UwbTestUtils.TEST_SESSION_ID;
import static com.android.server.uwb.UwbTestUtils.TEST_SESSION_ID_2;
+import static com.android.server.uwb.data.UwbUciConstants.MAC_ADDRESSING_MODE_EXTENDED;
+import static com.android.server.uwb.data.UwbUciConstants.MAC_ADDRESSING_MODE_SHORT;
+import static com.android.server.uwb.data.UwbUciConstants.RANGING_DEVICE_ROLE_ADVERTISER;
+import static com.android.server.uwb.data.UwbUciConstants.RANGING_DEVICE_ROLE_OBSERVER;
import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA;
import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY;
+import static com.android.server.uwb.data.UwbUciConstants.ROUND_USAGE_DS_TWR_DEFERRED_MODE;
+import static com.android.server.uwb.data.UwbUciConstants.ROUND_USAGE_OWR_AOA_MEASUREMENT;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.uwb.support.fira.FiraParams.PROTOCOL_NAME;
import static com.google.uwb.support.fira.FiraParams.RangeDataNtfConfigCapabilityFlag.HAS_RANGE_DATA_NTF_CONFIG_DISABLE;
import static com.google.uwb.support.fira.FiraParams.RangeDataNtfConfigCapabilityFlag.HAS_RANGE_DATA_NTF_CONFIG_ENABLE;
@@ -107,6 +117,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -235,7 +246,8 @@ public class UwbSessionManagerTest {
@Test
public void onRangeDataNotificationReceivedWithValidUwbSession_twoWay() {
UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_TWO_WAY, UwbUciConstants.STATUS_CODE_OK);
+ RANGING_MEASUREMENT_TYPE_TWO_WAY, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
UwbSession mockUwbSession = mock(UwbSession.class);
when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
doReturn(mockUwbSession)
@@ -251,7 +263,8 @@ public class UwbSessionManagerTest {
@Test
public void onRangeDataNotificationReceivedWithInvalidSession_twoWay() {
UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_TWO_WAY, UwbUciConstants.STATUS_CODE_OK);
+ RANGING_MEASUREMENT_TYPE_TWO_WAY, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
doReturn(null)
.when(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
@@ -262,19 +275,27 @@ public class UwbSessionManagerTest {
verify(mUwbMetrics, never()).logRangingResult(anyInt(), eq(uwbRangingData));
}
+ // Test scenario for receiving Application payload data followed by a RANGE_DATA_NTF with an
+ // OWR Aoa Measurement (such that the ExtendedMacAddress format is used for the remote device).
@Test
- public void onRangeDataNotificationReceived_owrAoa_success() {
- UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_OWR_AOA, UwbUciConstants.STATUS_CODE_OK);
+ public void onRangeDataNotificationReceived_owrAoa_success_extendedMacAddress() {
UwbSession mockUwbSession = mock(UwbSession.class);
when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
doReturn(mockUwbSession)
.when(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
+ // First call onDataReceived() to get the application payload data.
mUwbSessionManager.onDataReceived(TEST_SESSION_ID, UwbUciConstants.STATUS_CODE_OK,
DATA_SEQUENCE_NUM, PEER_EXTENDED_MAC_ADDRESS, SOURCE_END_POINT, DEST_END_POINT,
DATA_PAYLOAD);
+ // Next call onRangeDataNotificationReceived() to process the RANGE_DATA_NTF.
+ UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
+ RANGING_MEASUREMENT_TYPE_OWR_AOA, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
+ Params firaParams = setupFiraParams(
+ RANGING_DEVICE_ROLE_OBSERVER, Optional.of(ROUND_USAGE_OWR_AOA_MEASUREMENT));
+ when(mockUwbSession.getParams()).thenReturn(firaParams);
when(mUwbAdvertiseManager.isPointedTarget(PEER_EXTENDED_MAC_ADDRESS)).thenReturn(true);
mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
@@ -282,36 +303,162 @@ public class UwbSessionManagerTest {
.onRangingResult(eq(mockUwbSession), eq(uwbRangingData));
verify(mUwbAdvertiseManager).updateAdvertiseTarget(uwbRangingData.mRangingOwrAoaMeasure);
verify(mUwbSessionNotificationManager)
- .onDataReceived(eq(mockUwbSession), eq(PEER_UWB_ADDRESS),
+ .onDataReceived(eq(mockUwbSession), eq(PEER_EXTENDED_UWB_ADDRESS),
+ isA(PersistableBundle.class), eq(DATA_PAYLOAD));
+ verify(mUwbAdvertiseManager).removeAdvertiseTarget(PEER_EXTENDED_MAC_ADDRESS);
+ verify(mUwbMetrics).logRangingResult(anyInt(), eq(uwbRangingData));
+ }
+
+ // Test scenario for receiving Application payload data followed by a RANGE_DATA_NTF with an
+ // OWR Aoa Measurement (such that the ShortMacAddress format is used for the remote device).
+ @Test
+ public void onRangeDataNotificationReceived_owrAoa_success_shortMacAddress() {
+ UwbSession mockUwbSession = mock(UwbSession.class);
+ when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
+ doReturn(mockUwbSession)
+ .when(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
+
+ // First call onDataReceived() to get the application payload data. This should always have
+ // the MacAddress (in 8 Bytes), even for a Short MacAddress (MSB are zeroed out).
+ mUwbSessionManager.onDataReceived(TEST_SESSION_ID, UwbUciConstants.STATUS_CODE_OK,
+ DATA_SEQUENCE_NUM, PEER_EXTENDED_SHORT_MAC_ADDRESS,
+ SOURCE_END_POINT, DEST_END_POINT, DATA_PAYLOAD);
+
+ // Next call onRangeDataNotificationReceived() to process the RANGE_DATA_NTF.
+ UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
+ RANGING_MEASUREMENT_TYPE_OWR_AOA, MAC_ADDRESSING_MODE_SHORT,
+ UwbUciConstants.STATUS_CODE_OK);
+ Params firaParams = setupFiraParams(
+ RANGING_DEVICE_ROLE_OBSERVER, Optional.of(ROUND_USAGE_OWR_AOA_MEASUREMENT));
+ when(mockUwbSession.getParams()).thenReturn(firaParams);
+ when(mUwbAdvertiseManager.isPointedTarget(PEER_SHORT_MAC_ADDRESS)).thenReturn(true);
+ mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
+
+ verify(mUwbSessionNotificationManager)
+ .onRangingResult(eq(mockUwbSession), eq(uwbRangingData));
+ verify(mUwbAdvertiseManager).updateAdvertiseTarget(uwbRangingData.mRangingOwrAoaMeasure);
+ verify(mUwbSessionNotificationManager)
+ .onDataReceived(eq(mockUwbSession), eq(PEER_SHORT_UWB_ADDRESS),
isA(PersistableBundle.class), eq(DATA_PAYLOAD));
+ verify(mUwbAdvertiseManager).removeAdvertiseTarget(PEER_SHORT_MAC_ADDRESS);
verify(mUwbMetrics).logRangingResult(anyInt(), eq(uwbRangingData));
}
@Test
public void onRangeDataNotificationReceived_owrAoa_missingUwbSession() {
UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_OWR_AOA, UwbUciConstants.STATUS_CODE_OK);
+ RANGING_MEASUREMENT_TYPE_OWR_AOA, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
+
+ // Setup the test scenario such that the UwbSession (from the RANGE_DATA_NTF) doesn't exist.
doReturn(null)
.when(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
+ // First call onDataReceived() to get the application payload data.
mUwbSessionManager.onDataReceived(TEST_SESSION_ID, UwbUciConstants.STATUS_CODE_OK,
DATA_SEQUENCE_NUM, PEER_EXTENDED_MAC_ADDRESS, SOURCE_END_POINT, DEST_END_POINT,
DATA_PAYLOAD);
+
+ // Next call onRangeDataNotificationReceived() to process the RANGE_DATA_NTF.
mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
verifyZeroInteractions(mUwbAdvertiseManager, mUwbSessionNotificationManager, mUwbMetrics);
}
@Test
+ public void onRangeDataNotificationReceived_incorrectRangingMeasureType() {
+ UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
+ RANGING_MEASUREMENT_TYPE_UNDEFINED, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
+ UwbSession mockUwbSession = mock(UwbSession.class);
+ when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
+ doReturn(mockUwbSession)
+ .when(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
+
+ // First call onDataReceived() to get the application payload data.
+ mUwbSessionManager.onDataReceived(TEST_SESSION_ID, UwbUciConstants.STATUS_CODE_OK,
+ DATA_SEQUENCE_NUM, PEER_EXTENDED_MAC_ADDRESS, SOURCE_END_POINT, DEST_END_POINT,
+ DATA_PAYLOAD);
+
+ // Next call onRangeDataNotificationReceived() to process the RANGE_DATA_NTF.
+ mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
+
+ verify(mUwbSessionNotificationManager)
+ .onRangingResult(eq(mockUwbSession), eq(uwbRangingData));
+ verify(mUwbMetrics).logRangingResult(anyInt(), eq(uwbRangingData));
+ verifyZeroInteractions(mUwbAdvertiseManager);
+ }
+
+ @Test
+ public void onRangeDataNotificationReceived_owrAoa_incorrectRangingRoundUsage() {
+ UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
+ RANGING_MEASUREMENT_TYPE_OWR_AOA, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
+ UwbSession mockUwbSession = mock(UwbSession.class);
+ when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
+ doReturn(mockUwbSession)
+ .when(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
+
+ // First call onDataReceived() to get the application payload data.
+ mUwbSessionManager.onDataReceived(TEST_SESSION_ID, UwbUciConstants.STATUS_CODE_OK,
+ DATA_SEQUENCE_NUM, PEER_EXTENDED_MAC_ADDRESS, SOURCE_END_POINT, DEST_END_POINT,
+ DATA_PAYLOAD);
+
+ // Next call onRangeDataNotificationReceived() to process the RANGE_DATA_NTF (with an
+ // incorrect RangingRoundUsage value).
+ Params firaParams = setupFiraParams(
+ RANGING_DEVICE_ROLE_OBSERVER, Optional.of(ROUND_USAGE_DS_TWR_DEFERRED_MODE));
+ when(mockUwbSession.getParams()).thenReturn(firaParams);
+ mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
+
+ verify(mUwbSessionNotificationManager)
+ .onRangingResult(eq(mockUwbSession), eq(uwbRangingData));
+ verify(mUwbMetrics).logRangingResult(anyInt(), eq(uwbRangingData));
+ verifyZeroInteractions(mUwbAdvertiseManager);
+ }
+
+ @Test
+ public void onRangeDataNotificationReceived_owrAoa_incorrectDeviceRole() {
+ UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
+ RANGING_MEASUREMENT_TYPE_OWR_AOA, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
+ UwbSession mockUwbSession = mock(UwbSession.class);
+ when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
+ doReturn(mockUwbSession)
+ .when(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
+
+ // First call onDataReceived() to get the application payload data.
+ mUwbSessionManager.onDataReceived(TEST_SESSION_ID, UwbUciConstants.STATUS_CODE_OK,
+ DATA_SEQUENCE_NUM, PEER_EXTENDED_MAC_ADDRESS, SOURCE_END_POINT, DEST_END_POINT,
+ DATA_PAYLOAD);
+
+ // Next call onRangeDataNotificationReceived() to process the RANGE_DATA_NTF.
+ Params firaParams = setupFiraParams(
+ RANGING_DEVICE_ROLE_ADVERTISER, Optional.of(ROUND_USAGE_OWR_AOA_MEASUREMENT));
+ when(mockUwbSession.getParams()).thenReturn(firaParams);
+ mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
+
+ verify(mUwbSessionNotificationManager)
+ .onRangingResult(eq(mockUwbSession), eq(uwbRangingData));
+ verify(mUwbMetrics).logRangingResult(anyInt(), eq(uwbRangingData));
+ verifyZeroInteractions(mUwbAdvertiseManager);
+ }
+
+ @Test
public void onRangeDataNotificationReceived_owrAoa_receivedDataNotCalled() {
UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_OWR_AOA, UwbUciConstants.STATUS_CODE_OK);
+ RANGING_MEASUREMENT_TYPE_OWR_AOA, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
UwbSession mockUwbSession = mock(UwbSession.class);
when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
doReturn(mockUwbSession)
.when(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
- // Skip call to mUwbSessionManager.onDataReceived()
+ // Skip call to mUwbSessionManager.onDataReceived(). This means there is no application
+ // payload data, and so mUwbSessionNotificationManager.onDataReceived() shouldn't be called.
+ Params firaParams = setupFiraParams(
+ RANGING_DEVICE_ROLE_OBSERVER, Optional.of(ROUND_USAGE_OWR_AOA_MEASUREMENT));
+ when(mockUwbSession.getParams()).thenReturn(firaParams);
mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
verify(mUwbSessionNotificationManager)
@@ -326,7 +473,8 @@ public class UwbSessionManagerTest {
@Test
public void onRangeDataNotificationReceived_owrAoa_receivedDataDifferentMacAddress() {
UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_OWR_AOA, UwbUciConstants.STATUS_CODE_OK);
+ RANGING_MEASUREMENT_TYPE_OWR_AOA, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
UwbSession mockUwbSession = mock(UwbSession.class);
when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
doReturn(mockUwbSession)
@@ -337,6 +485,11 @@ public class UwbSessionManagerTest {
mUwbSessionManager.onDataReceived(TEST_SESSION_ID, UwbUciConstants.STATUS_CODE_OK,
DATA_SEQUENCE_NUM, PEER_EXTENDED_MAC_ADDRESS_2, SOURCE_END_POINT, DEST_END_POINT,
DATA_PAYLOAD);
+
+ // Next call onRangeDataNotificationReceived() to process the RANGE_DATA_NTF.
+ Params firaParams = setupFiraParams(
+ RANGING_DEVICE_ROLE_OBSERVER, Optional.of(ROUND_USAGE_OWR_AOA_MEASUREMENT));
+ when(mockUwbSession.getParams()).thenReturn(firaParams);
mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
verify(mUwbSessionNotificationManager)
@@ -351,7 +504,8 @@ public class UwbSessionManagerTest {
@Test
public void onRangeDataNotificationReceived_owrAoa_receivedDataDifferentUwbSession() {
UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_OWR_AOA, UwbUciConstants.STATUS_CODE_OK);
+ RANGING_MEASUREMENT_TYPE_OWR_AOA, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
UwbSession mockUwbSession = mock(UwbSession.class);
when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
doReturn(mockUwbSession)
@@ -363,11 +517,15 @@ public class UwbSessionManagerTest {
DATA_SEQUENCE_NUM, PEER_EXTENDED_MAC_ADDRESS, SOURCE_END_POINT, DEST_END_POINT,
DATA_PAYLOAD);
+ // Next call onRangeDataNotificationReceived() to process the RANGE_DATA_NTF.
UwbSession mockUwbSession2 = mock(UwbSession.class);
when(mockUwbSession2.getWaitObj()).thenReturn(mock(WaitObj.class));
doReturn(mockUwbSession2)
.when(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID_2));
+ Params firaParams = setupFiraParams(
+ RANGING_DEVICE_ROLE_OBSERVER, Optional.of(ROUND_USAGE_OWR_AOA_MEASUREMENT));
+ when(mockUwbSession.getParams()).thenReturn(firaParams);
mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
verify(mUwbSessionNotificationManager)
@@ -382,7 +540,8 @@ public class UwbSessionManagerTest {
@Test
public void onRangeDataNotificationReceived_owrAoa_notPointedTarget() {
UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_OWR_AOA, UwbUciConstants.STATUS_CODE_OK);
+ RANGING_MEASUREMENT_TYPE_OWR_AOA, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
UwbSession mockUwbSession = mock(UwbSession.class);
when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
doReturn(mockUwbSession)
@@ -399,6 +558,7 @@ public class UwbSessionManagerTest {
verify(mUwbSessionNotificationManager)
.onRangingResult(eq(mockUwbSession), eq(uwbRangingData));
verify(mUwbMetrics).logRangingResult(anyInt(), eq(uwbRangingData));
+ verify(mUwbAdvertiseManager, never()).removeAdvertiseTarget(isA(byte[].class));
verifyZeroInteractions(mUwbSessionNotificationManager);
}
@@ -573,27 +733,6 @@ public class UwbSessionManagerTest {
}
@Test
- public void deInitSession_notExistedSession() {
- doReturn(false).when(mUwbSessionManager).isExistedSession(any());
-
- mUwbSessionManager.deInitSession(mock(SessionHandle.class));
-
- verify(mUwbSessionManager, never()).getSessionId(any());
- assertThat(mTestLooper.nextMessage()).isNull();
- }
-
- @Test
- public void deInitSession_success() {
- doReturn(true).when(mUwbSessionManager).isExistedSession(any());
- doReturn(TEST_SESSION_ID).when(mUwbSessionManager).getSessionId(any());
-
- mUwbSessionManager.deInitSession(mock(SessionHandle.class));
-
- verify(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
- assertThat(mTestLooper.nextMessage().what).isEqualTo(5); // SESSION_CLOSE
- }
-
- @Test
public void startRanging_notExistedSession() {
doReturn(false).when(mUwbSessionManager).isExistedSession(any());
doReturn(TEST_SESSION_ID).when(mUwbSessionManager).getSessionId(any());
@@ -678,6 +817,31 @@ public class UwbSessionManagerTest {
}
@Test
+ public void stopRanging_currentSessionStateActive_owrAoa() {
+ UwbSession mockUwbSession = mock(UwbSession.class);
+
+ doReturn(true).when(mUwbSessionManager).isExistedSession(any());
+ doReturn(TEST_SESSION_ID).when(mUwbSessionManager).getSessionId(any());
+ doReturn(mockUwbSession).when(mUwbSessionManager).getUwbSession(anyInt());
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_ACTIVE)
+ .when(mUwbSessionManager).getCurrentSessionState(anyInt());
+ when(mNativeUwbManager.stopRanging(eq(TEST_SESSION_ID), anyString()))
+ .thenReturn((byte) UwbUciConstants.STATUS_CODE_OK);
+
+ doReturn(PROTOCOL_NAME).when(mockUwbSession).getProtocolName();
+ doReturn(0).when(mockUwbSession).getCurrentFiraRangingIntervalMs();
+
+ // Setup the UwbSession to have the peer device's MacAddress stored (which happens when
+ // a valid RANGE_DATA_NTF with an OWR AoA Measurement is received).
+ doReturn(PEER_EXTENDED_MAC_ADDRESS).when(mockUwbSession).getRemoteMacAddress();
+
+ mUwbSessionManager.stopRanging(mock(SessionHandle.class));
+ mTestLooper.dispatchNext();
+
+ verify(mUwbAdvertiseManager).removeAdvertiseTarget(PEER_EXTENDED_MAC_ADDRESS);
+ }
+
+ @Test
public void stopRanging_currentSessionStateIdle() {
doReturn(true).when(mUwbSessionManager).isExistedSession(any());
doReturn(TEST_SESSION_ID).when(mUwbSessionManager).getSessionId(any());
@@ -805,29 +969,6 @@ public class UwbSessionManagerTest {
}
@Test
- public void deinitAllSession() {
- UwbSession mockUwbSession1 = mock(UwbSession.class);
- mUwbSessionManager.mSessionTable.put(TEST_SESSION_ID, mockUwbSession1);
- when(mockUwbSession1.getBinder()).thenReturn(mock(IBinder.class));
- when(mockUwbSession1.getSessionId()).thenReturn(TEST_SESSION_ID);
- when(mockUwbSession1.getProtocolName()).thenReturn(FiraParams.PROTOCOL_NAME);
- UwbSession mockUwbSession2 = mock(UwbSession.class);
- mUwbSessionManager.mSessionTable.put(TEST_SESSION_ID + 100, mockUwbSession2);
- when(mockUwbSession2.getBinder()).thenReturn(mock(IBinder.class));
- when(mockUwbSession2.getSessionId()).thenReturn(TEST_SESSION_ID + 100);
- when(mockUwbSession2.getProtocolName()).thenReturn(FiraParams.PROTOCOL_NAME);
-
- mUwbSessionManager.deinitAllSession();
-
- verify(mUwbSessionNotificationManager, times(2))
- .onRangingClosedWithApiReasonCode(any(), eq(RangingChangeReason.SYSTEM_POLICY));
- verify(mUwbSessionManager, times(2)).removeSession(any());
- // TODO: enable it when the resetDevice is enabled.
- // verify(mNativeUwbManager).resetDevice(eq(UwbUciConstants.UWBS_RESET));
- assertThat(mUwbSessionManager.getSessionCount()).isEqualTo(0);
- }
-
- @Test
public void setCurrentSessionState() {
UwbSession mockUwbSession = mock(UwbSession.class);
mUwbSessionManager.mSessionTable.put(TEST_SESSION_ID, mockUwbSession);
@@ -883,21 +1024,8 @@ public class UwbSessionManagerTest {
doReturn(false).when(mUwbSessionManager).isExistedSession(anyInt());
IUwbRangingCallbacks mockRangingCallbacks = mock(IUwbRangingCallbacks.class);
SessionHandle mockSessionHandle = mock(SessionHandle.class);
- Params params = new FiraOpenSessionParams.Builder()
- .setDeviceAddress(UwbAddress.fromBytes(new byte[] {(byte) 0x01, (byte) 0x02 }))
- .setVendorId(new byte[] { (byte) 0x00, (byte) 0x01 })
- .setStaticStsIV(new byte[] { (byte) 0x01, (byte) 0x02, (byte) 0x03,
- (byte) 0x04, (byte) 0x05, (byte) 0x06 })
- .setDestAddressList(Arrays.asList(
- UWB_DEST_ADDRESS))
- .setProtocolVersion(new FiraProtocolVersion(1, 0))
- .setSessionId(10)
- .setDeviceType(FiraParams.RANGING_DEVICE_TYPE_CONTROLLER)
- .setDeviceRole(FiraParams.RANGING_DEVICE_ROLE_INITIATOR)
- .setMultiNodeMode(FiraParams.MULTI_NODE_MODE_UNICAST)
- .setRangingIntervalMs(TEST_RANGING_INTERVAL_MS)
- .build();
IBinder mockBinder = mock(IBinder.class);
+ Params params = setupFiraParams();
UwbSession uwbSession = spy(
mUwbSessionManager.new UwbSession(attributionSource, mockSessionHandle,
TEST_SESSION_ID, FiraParams.PROTOCOL_NAME, params, mockRangingCallbacks,
@@ -910,6 +1038,32 @@ public class UwbSessionManagerTest {
return uwbSession;
}
+ private Params setupFiraParams() {
+ return setupFiraParams(FiraParams.RANGING_DEVICE_ROLE_INITIATOR, Optional.empty());
+ }
+
+ private Params setupFiraParams(int deviceRole, Optional<Integer> rangingRoundUsageOptional) {
+ FiraOpenSessionParams.Builder paramsBuilder = new FiraOpenSessionParams.Builder()
+ .setDeviceAddress(UwbAddress.fromBytes(new byte[] {(byte) 0x01, (byte) 0x02 }))
+ .setVendorId(new byte[] { (byte) 0x00, (byte) 0x01 })
+ .setStaticStsIV(new byte[] { (byte) 0x01, (byte) 0x02, (byte) 0x03,
+ (byte) 0x04, (byte) 0x05, (byte) 0x06 })
+ .setDestAddressList(Arrays.asList(
+ UWB_DEST_ADDRESS))
+ .setProtocolVersion(new FiraProtocolVersion(1, 0))
+ .setSessionId(10)
+ .setDeviceType(FiraParams.RANGING_DEVICE_TYPE_CONTROLLER)
+ .setDeviceRole(deviceRole)
+ .setMultiNodeMode(FiraParams.MULTI_NODE_MODE_UNICAST)
+ .setRangingIntervalMs(TEST_RANGING_INTERVAL_MS);
+
+ if (rangingRoundUsageOptional.isPresent()) {
+ paramsBuilder.setRangingRoundUsage(rangingRoundUsageOptional.get());
+ }
+
+ return paramsBuilder.build();
+ }
+
private UwbSession setUpCccUwbSessionForExecution() throws RemoteException {
// setup message
doReturn(0).when(mUwbSessionManager).getSessionCount();
@@ -1393,7 +1547,8 @@ public class UwbSessionManagerTest {
// Now send a range data notification.
UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_TWO_WAY, UwbUciConstants.STATUS_CODE_OK);
+ RANGING_MEASUREMENT_TYPE_TWO_WAY, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
verify(mUwbSessionNotificationManager).onRangingResult(uwbSession, uwbRangingData);
}
@@ -1427,7 +1582,8 @@ public class UwbSessionManagerTest {
// Now send a range data notification with an error.
UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- rangingMeasurementType, UwbUciConstants.STATUS_CODE_RANGING_RX_TIMEOUT);
+ rangingMeasurementType, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_RANGING_RX_TIMEOUT);
mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
verify(mUwbSessionNotificationManager).onRangingResult(uwbSession, uwbRangingData);
ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor =
@@ -1438,7 +1594,8 @@ public class UwbSessionManagerTest {
// Send one more error and ensure that the timer is not cancelled.
uwbRangingData = UwbTestUtils.generateRangingData(
- rangingMeasurementType, UwbUciConstants.STATUS_CODE_RANGING_RX_TIMEOUT);
+ rangingMeasurementType, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_RANGING_RX_TIMEOUT);
mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
verify(mUwbSessionNotificationManager).onRangingResult(uwbSession, uwbRangingData);
@@ -1480,7 +1637,8 @@ public class UwbSessionManagerTest {
// Now send a range data notification with an error.
UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_TWO_WAY, UwbUciConstants.STATUS_CODE_RANGING_RX_TIMEOUT);
+ RANGING_MEASUREMENT_TYPE_TWO_WAY, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_RANGING_RX_TIMEOUT);
mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
verify(mUwbSessionNotificationManager).onRangingResult(uwbSession, uwbRangingData);
ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor =
@@ -1491,7 +1649,8 @@ public class UwbSessionManagerTest {
// Send success and ensure that the timer is cancelled.
uwbRangingData = UwbTestUtils.generateRangingData(
- RANGING_MEASUREMENT_TYPE_TWO_WAY, UwbUciConstants.STATUS_CODE_OK);
+ RANGING_MEASUREMENT_TYPE_TWO_WAY, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
verify(mUwbSessionNotificationManager).onRangingResult(uwbSession, uwbRangingData);
@@ -1594,33 +1753,47 @@ public class UwbSessionManagerTest {
public void sendData_success_validUwbSession() throws Exception {
UwbSession uwbSession = prepareExistingUwbSession();
- when(mNativeUwbManager.sendData(eq(TEST_SESSION_ID), eq(PEER_UWB_ADDRESS.toBytes()),
+ // Setup the UwbSession to start ranging (and move it to active state).
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_IDLE).when(uwbSession).getSessionState();
+ when(mNativeUwbManager.startRanging(eq(TEST_SESSION_ID), anyString()))
+ .thenReturn((byte) UwbUciConstants.STATUS_CODE_OK);
+
+ mUwbSessionManager.startRanging(
+ uwbSession.getSessionHandle(), uwbSession.getParams());
+ mTestLooper.dispatchAll();
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_ACTIVE).when(uwbSession).getSessionState();
+
+ // Send data on the UWB session.
+ when(mNativeUwbManager.sendData(eq(TEST_SESSION_ID),
+ eq(PEER_EXTENDED_UWB_ADDRESS.toBytes()),
eq(UwbUciConstants.UWB_DESTINATION_END_POINT_HOST), eq(DATA_SEQUENCE_NUM),
eq(DATA_PAYLOAD))).thenReturn((byte) UwbUciConstants.STATUS_CODE_OK);
- mUwbSessionManager.sendData(uwbSession.getSessionHandle(), PEER_UWB_ADDRESS,
+ mUwbSessionManager.sendData(uwbSession.getSessionHandle(), PEER_EXTENDED_UWB_ADDRESS,
PERSISTABLE_BUNDLE, DATA_PAYLOAD);
mTestLooper.dispatchNext();
- verify(mNativeUwbManager).sendData(eq(TEST_SESSION_ID), eq(PEER_UWB_ADDRESS.toBytes()),
+ verify(mNativeUwbManager).sendData(eq(TEST_SESSION_ID),
+ eq(PEER_EXTENDED_UWB_ADDRESS.toBytes()),
eq(UwbUciConstants.UWB_DESTINATION_END_POINT_HOST), eq(DATA_SEQUENCE_NUM),
eq(DATA_PAYLOAD));
verify(mUwbSessionNotificationManager).onDataSent(
- eq(uwbSession), eq(PEER_UWB_ADDRESS), eq(PERSISTABLE_BUNDLE));
+ eq(uwbSession), eq(PEER_EXTENDED_UWB_ADDRESS), eq(PERSISTABLE_BUNDLE));
}
@Test
public void sendData_missingSessionHandle() throws Exception {
mUwbSessionManager.sendData(
- null /* sessionHandle */, PEER_UWB_ADDRESS, PERSISTABLE_BUNDLE, DATA_PAYLOAD);
+ null /* sessionHandle */, PEER_EXTENDED_UWB_ADDRESS, PERSISTABLE_BUNDLE,
+ DATA_PAYLOAD);
mTestLooper.dispatchNext();
verify(mNativeUwbManager, never()).sendData(
- eq(TEST_SESSION_ID), eq(PEER_UWB_ADDRESS.toBytes()),
+ eq(TEST_SESSION_ID), eq(PEER_EXTENDED_UWB_ADDRESS.toBytes()),
eq(UwbUciConstants.UWB_DESTINATION_END_POINT_HOST), eq(DATA_SEQUENCE_NUM),
eq(DATA_PAYLOAD));
verify(mUwbSessionNotificationManager).onDataSendFailed(
- eq(null), eq(PEER_UWB_ADDRESS),
+ eq(null), eq(PEER_EXTENDED_UWB_ADDRESS),
eq(UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST), eq(PERSISTABLE_BUNDLE));
}
@@ -1631,32 +1804,63 @@ public class UwbSessionManagerTest {
SessionHandle sessionHandle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
mUwbSessionManager.sendData(
- sessionHandle, PEER_UWB_ADDRESS, PERSISTABLE_BUNDLE, DATA_PAYLOAD);
+ sessionHandle, PEER_EXTENDED_UWB_ADDRESS, PERSISTABLE_BUNDLE, DATA_PAYLOAD);
mTestLooper.dispatchNext();
verify(mNativeUwbManager, never()).sendData(
- eq(TEST_SESSION_ID), eq(PEER_UWB_ADDRESS.toBytes()),
+ eq(TEST_SESSION_ID), eq(PEER_EXTENDED_UWB_ADDRESS.toBytes()),
eq(UwbUciConstants.UWB_DESTINATION_END_POINT_HOST), eq(DATA_SEQUENCE_NUM),
eq(DATA_PAYLOAD));
verify(mUwbSessionNotificationManager).onDataSendFailed(
- eq(null), eq(PEER_UWB_ADDRESS),
+ eq(null), eq(PEER_EXTENDED_UWB_ADDRESS),
eq(UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST), eq(PERSISTABLE_BUNDLE));
}
@Test
+ public void sendData_invalidUwbSessionState() throws Exception {
+ // Setup a uwbSession and don't start ranging, so it remains in IDLE state.
+ UwbSession uwbSession = prepareExistingUwbSession();
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_IDLE).when(uwbSession).getSessionState();
+
+ // Attempt to send data on the UWB session.
+ mUwbSessionManager.sendData(
+ uwbSession.getSessionHandle(), PEER_EXTENDED_UWB_ADDRESS, PERSISTABLE_BUNDLE, null);
+ mTestLooper.dispatchNext();
+
+ verify(mNativeUwbManager, never()).sendData(
+ eq(TEST_SESSION_ID), eq(PEER_EXTENDED_UWB_ADDRESS.toBytes()),
+ eq(UwbUciConstants.UWB_DESTINATION_END_POINT_HOST), eq(DATA_SEQUENCE_NUM),
+ eq(DATA_PAYLOAD));
+ verify(mUwbSessionNotificationManager).onDataSendFailed(
+ eq(uwbSession), eq(PEER_EXTENDED_UWB_ADDRESS),
+ eq(UwbUciConstants.STATUS_CODE_FAILED), eq(PERSISTABLE_BUNDLE));
+ }
+
+ @Test
public void sendData_missingDataPayload() throws Exception {
UwbSession uwbSession = prepareExistingUwbSession();
+ // Setup the UwbSession to start ranging (and move it to active state).
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_IDLE).when(uwbSession).getSessionState();
+ when(mNativeUwbManager.startRanging(eq(TEST_SESSION_ID), anyString()))
+ .thenReturn((byte) UwbUciConstants.STATUS_CODE_OK);
+
+ mUwbSessionManager.startRanging(
+ uwbSession.getSessionHandle(), uwbSession.getParams());
+ mTestLooper.dispatchAll();
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_ACTIVE).when(uwbSession).getSessionState();
+
+ // Attempt to send data on the UWB session.
mUwbSessionManager.sendData(
- uwbSession.getSessionHandle(), PEER_UWB_ADDRESS, PERSISTABLE_BUNDLE, null);
+ uwbSession.getSessionHandle(), PEER_EXTENDED_UWB_ADDRESS, PERSISTABLE_BUNDLE, null);
mTestLooper.dispatchNext();
verify(mNativeUwbManager, never()).sendData(
- eq(TEST_SESSION_ID), eq(PEER_UWB_ADDRESS.toBytes()),
+ eq(TEST_SESSION_ID), eq(PEER_EXTENDED_UWB_ADDRESS.toBytes()),
eq(UwbUciConstants.UWB_DESTINATION_END_POINT_HOST), eq(DATA_SEQUENCE_NUM),
eq(DATA_PAYLOAD));
verify(mUwbSessionNotificationManager).onDataSendFailed(
- eq(uwbSession), eq(PEER_UWB_ADDRESS),
+ eq(uwbSession), eq(PEER_EXTENDED_UWB_ADDRESS),
eq(UwbUciConstants.STATUS_CODE_INVALID_PARAM), eq(PERSISTABLE_BUNDLE));
}
@@ -1664,12 +1868,23 @@ public class UwbSessionManagerTest {
public void sendData_missingRemoteDevice() throws Exception {
UwbSession uwbSession = prepareExistingUwbSession();
+ // Setup the UwbSession to start ranging (and move it to active state).
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_IDLE).when(uwbSession).getSessionState();
+ when(mNativeUwbManager.startRanging(eq(TEST_SESSION_ID), anyString()))
+ .thenReturn((byte) UwbUciConstants.STATUS_CODE_OK);
+
+ mUwbSessionManager.startRanging(
+ uwbSession.getSessionHandle(), uwbSession.getParams());
+ mTestLooper.dispatchAll();
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_ACTIVE).when(uwbSession).getSessionState();
+
+ // Attempt to send data on the UWB session.
mUwbSessionManager.sendData(
uwbSession.getSessionHandle(), null, PERSISTABLE_BUNDLE, DATA_PAYLOAD);
mTestLooper.dispatchNext();
verify(mNativeUwbManager, never()).sendData(
- eq(TEST_SESSION_ID), eq(PEER_UWB_ADDRESS.toBytes()),
+ eq(TEST_SESSION_ID), eq(PEER_EXTENDED_UWB_ADDRESS.toBytes()),
eq(UwbUciConstants.UWB_DESTINATION_END_POINT_HOST), eq(DATA_SEQUENCE_NUM),
eq(DATA_PAYLOAD));
verify(mUwbSessionNotificationManager).onDataSendFailed(
@@ -1681,19 +1896,32 @@ public class UwbSessionManagerTest {
public void sendData_dataSendFailure() throws Exception {
UwbSession uwbSession = prepareExistingUwbSession();
- when(mNativeUwbManager.sendData(eq(TEST_SESSION_ID), eq(PEER_UWB_ADDRESS.toBytes()),
+ // Setup the UwbSession to start ranging (and move it to active state).
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_IDLE).when(uwbSession).getSessionState();
+ when(mNativeUwbManager.startRanging(eq(TEST_SESSION_ID), anyString()))
+ .thenReturn((byte) UwbUciConstants.STATUS_CODE_OK);
+
+ mUwbSessionManager.startRanging(
+ uwbSession.getSessionHandle(), uwbSession.getParams());
+ mTestLooper.dispatchAll();
+ doReturn(UwbUciConstants.UWB_SESSION_STATE_ACTIVE).when(uwbSession).getSessionState();
+
+ // Attempt to send data on the UWB session.
+ when(mNativeUwbManager.sendData(eq(TEST_SESSION_ID),
+ eq(PEER_EXTENDED_UWB_ADDRESS.toBytes()),
eq(UwbUciConstants.UWB_DESTINATION_END_POINT_HOST), eq(DATA_SEQUENCE_NUM),
eq(DATA_PAYLOAD))).thenReturn((byte) UwbUciConstants.STATUS_CODE_FAILED);
- mUwbSessionManager.sendData(uwbSession.getSessionHandle(), PEER_UWB_ADDRESS,
+ mUwbSessionManager.sendData(uwbSession.getSessionHandle(), PEER_EXTENDED_UWB_ADDRESS,
PERSISTABLE_BUNDLE, DATA_PAYLOAD);
mTestLooper.dispatchNext();
- verify(mNativeUwbManager).sendData(eq(TEST_SESSION_ID), eq(PEER_UWB_ADDRESS.toBytes()),
+ verify(mNativeUwbManager).sendData(eq(TEST_SESSION_ID),
+ eq(PEER_EXTENDED_UWB_ADDRESS.toBytes()),
eq(UwbUciConstants.UWB_DESTINATION_END_POINT_HOST), eq(DATA_SEQUENCE_NUM),
eq(DATA_PAYLOAD));
verify(mUwbSessionNotificationManager).onDataSendFailed(
- eq(uwbSession), eq(PEER_UWB_ADDRESS),
+ eq(uwbSession), eq(PEER_EXTENDED_UWB_ADDRESS),
eq(UwbUciConstants.STATUS_CODE_FAILED), eq(PERSISTABLE_BUNDLE));
}
@@ -2148,16 +2376,63 @@ public class UwbSessionManagerTest {
}
@Test
- public void deInitSession() throws Exception {
+ public void deInitSession_notExistedSession() {
+ doReturn(false).when(mUwbSessionManager).isExistedSession(any());
+
+ mUwbSessionManager.deInitSession(mock(SessionHandle.class));
+
+ verify(mUwbSessionManager, never()).getSessionId(any());
+ assertThat(mTestLooper.nextMessage()).isNull();
+ }
+
+ @Test
+ public void deInitSession_success() {
+ doReturn(true).when(mUwbSessionManager).isExistedSession(any());
+ doReturn(TEST_SESSION_ID).when(mUwbSessionManager).getSessionId(any());
+
+ mUwbSessionManager.deInitSession(mock(SessionHandle.class));
+
+ verify(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
+ assertThat(mTestLooper.nextMessage().what).isEqualTo(5); // SESSION_DEINIT
+
+ verifyZeroInteractions(mUwbAdvertiseManager);
+ }
+
+ @Test
+ public void deInitSession_success_afterOwrAoaMeasurement() {
+ UwbSession mockUwbSession = mock(UwbSession.class);
+ when(mockUwbSession.getWaitObj()).thenReturn(mock(WaitObj.class));
+ doReturn(mockUwbSession).when(mUwbSessionManager).getUwbSession(eq(TEST_SESSION_ID));
+
+ // Setup the UwbSession to have the peer device's MacAddress stored (which happens when
+ // a valid RANGE_DATA_NTF with an OWR AoA Measurement is received).
+ doReturn(PEER_EXTENDED_MAC_ADDRESS).when(mockUwbSession).getRemoteMacAddress();
+
+ // Call deInitSession().
+ IBinder mockBinder = mock(IBinder.class);
+ doReturn(mockBinder).when(mockUwbSession).getBinder();
+ doReturn(FiraParams.PROTOCOL_NAME).when(mockUwbSession).getProtocolName();
+ doReturn(null).when(mockUwbSession).getAnyNonPrivilegedAppInAttributionSource();
+ doReturn(true).when(mUwbSessionManager).isExistedSession(any());
+ doReturn(TEST_SESSION_ID).when(mUwbSessionManager).getSessionId(any());
+ mUwbSessionManager.deInitSession(mock(SessionHandle.class));
+
+ mTestLooper.dispatchNext();
+
+ verify(mUwbAdvertiseManager).removeAdvertiseTarget(PEER_EXTENDED_MAC_ADDRESS);
+ }
+
+ @Test
+ public void execDeInitSession() throws Exception {
UwbSession uwbSession = prepareExistingUwbSession();
mUwbSessionManager.deInitSession(uwbSession.getSessionHandle());
- assertThat(mTestLooper.nextMessage().what).isEqualTo(5); // SESSION_CLOSE
+ assertThat(mTestLooper.nextMessage().what).isEqualTo(5); // SESSION_DEINIT
}
@Test
- public void execCloseSession_success() throws Exception {
+ public void execDeInitSession_success() throws Exception {
UwbSession uwbSession = prepareExistingUwbSession();
when(mNativeUwbManager.deInitSession(eq(TEST_SESSION_ID), anyString()))
.thenReturn((byte) UwbUciConstants.STATUS_CODE_OK);
@@ -2170,10 +2445,11 @@ public class UwbSessionManagerTest {
verify(mUwbMetrics).logRangingCloseEvent(
eq(uwbSession), eq(UwbUciConstants.STATUS_CODE_OK));
assertThat(mUwbSessionManager.getSessionCount()).isEqualTo(0);
+ verifyZeroInteractions(mUwbAdvertiseManager);
}
@Test
- public void execCloseSession_failed() throws Exception {
+ public void execDeInitSession_failed() throws Exception {
UwbSession uwbSession = prepareExistingUwbSession();
when(mNativeUwbManager.deInitSession(eq(TEST_SESSION_ID), anyString()))
.thenReturn((byte) UwbUciConstants.STATUS_CODE_FAILED);
@@ -2183,9 +2459,34 @@ public class UwbSessionManagerTest {
verify(mUwbSessionNotificationManager).onRangingClosed(
eq(uwbSession), eq(UwbUciConstants.STATUS_CODE_FAILED));
+ verify(mUwbAdvertiseManager, never()).removeAdvertiseTarget(isA(byte[].class));
verify(mUwbMetrics).logRangingCloseEvent(
eq(uwbSession), eq(UwbUciConstants.STATUS_CODE_FAILED));
assertThat(mUwbSessionManager.getSessionCount()).isEqualTo(0);
+ verifyZeroInteractions(mUwbAdvertiseManager);
+ }
+
+ @Test
+ public void deinitAllSession() {
+ UwbSession mockUwbSession1 = mock(UwbSession.class);
+ mUwbSessionManager.mSessionTable.put(TEST_SESSION_ID, mockUwbSession1);
+ when(mockUwbSession1.getBinder()).thenReturn(mock(IBinder.class));
+ when(mockUwbSession1.getSessionId()).thenReturn(TEST_SESSION_ID);
+ when(mockUwbSession1.getProtocolName()).thenReturn(FiraParams.PROTOCOL_NAME);
+ UwbSession mockUwbSession2 = mock(UwbSession.class);
+ mUwbSessionManager.mSessionTable.put(TEST_SESSION_ID + 100, mockUwbSession2);
+ when(mockUwbSession2.getBinder()).thenReturn(mock(IBinder.class));
+ when(mockUwbSession2.getSessionId()).thenReturn(TEST_SESSION_ID + 100);
+ when(mockUwbSession2.getProtocolName()).thenReturn(FiraParams.PROTOCOL_NAME);
+
+ mUwbSessionManager.deinitAllSession();
+
+ verify(mUwbSessionNotificationManager, times(2))
+ .onRangingClosedWithApiReasonCode(any(), eq(RangingChangeReason.SYSTEM_POLICY));
+ verify(mUwbSessionManager, times(2)).removeSession(any());
+ // TODO: enable it when the resetDevice is enabled.
+ // verify(mNativeUwbManager).resetDevice(eq(UwbUciConstants.UWBS_RESET));
+ assertThat(mUwbSessionManager.getSessionCount()).isEqualTo(0);
}
@Test
@@ -2207,6 +2508,49 @@ public class UwbSessionManagerTest {
}
@Test
+ public void onSessionStatusNotification_session_deinit_owrAoa() throws Exception {
+ UwbSession uwbSession = prepareExistingUwbSession();
+ UwbRangingData uwbRangingData = UwbTestUtils.generateRangingData(
+ RANGING_MEASUREMENT_TYPE_OWR_AOA, MAC_ADDRESSING_MODE_EXTENDED,
+ UwbUciConstants.STATUS_CODE_OK);
+
+ // First call onDataReceived() to get the application payload data.
+ mUwbSessionManager.onDataReceived(TEST_SESSION_ID, UwbUciConstants.STATUS_CODE_OK,
+ DATA_SEQUENCE_NUM, PEER_EXTENDED_MAC_ADDRESS, SOURCE_END_POINT, DEST_END_POINT,
+ DATA_PAYLOAD);
+
+ // Next call onRangeDataNotificationReceived() to process the RANGE_DATA_NTF. Setup
+ // isPointedTarget() to return "false", as in that scenario the stored AdvertiseTarget
+ // is not removed.
+ Params firaParams = setupFiraParams(
+ RANGING_DEVICE_ROLE_OBSERVER, Optional.of(ROUND_USAGE_OWR_AOA_MEASUREMENT));
+ when(uwbSession.getParams()).thenReturn(firaParams);
+ when(mUwbAdvertiseManager.isPointedTarget(PEER_EXTENDED_MAC_ADDRESS)).thenReturn(false);
+ mUwbSessionManager.onRangeDataNotificationReceived(uwbRangingData);
+
+ verify(mUwbAdvertiseManager).updateAdvertiseTarget(uwbRangingData.mRangingOwrAoaMeasure);
+ verify(mUwbAdvertiseManager).isPointedTarget(PEER_EXTENDED_MAC_ADDRESS);
+
+ // Now call onSessionStatusNotificationReceived() on the same UwbSession, and verify that
+ // removeAdvertiseTarget() is called to remove any stored OwR AoA Measurement(s).
+ when(mNativeUwbManager.deInitSession(eq(TEST_SESSION_ID), anyString()))
+ .thenReturn((byte) UwbUciConstants.STATUS_CODE_OK);
+
+ mUwbSessionManager.onSessionStatusNotificationReceived(
+ uwbSession.getSessionId(), UwbUciConstants.UWB_SESSION_STATE_DEINIT,
+ UwbUciConstants.REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS);
+ mTestLooper.dispatchNext();
+
+ verify(mUwbSessionNotificationManager).onRangingClosedWithApiReasonCode(
+ eq(uwbSession), eq(RangingChangeReason.SYSTEM_POLICY));
+ verify(mUwbMetrics).logRangingCloseEvent(
+ eq(uwbSession), eq(UwbUciConstants.STATUS_CODE_OK));
+ assertThat(mUwbSessionManager.getSessionCount()).isEqualTo(0);
+
+ verify(mUwbAdvertiseManager).removeAdvertiseTarget(isA(byte[].class));
+ }
+
+ @Test
public void testHandleClientDeath() throws Exception {
UwbSession uwbSession = prepareExistingUwbSession();
when(mNativeUwbManager.deInitSession(eq(TEST_SESSION_ID), anyString()))
diff --git a/service/tests/src/com/android/server/uwb/UwbSessionNotificationManagerTest.java b/service/tests/src/com/android/server/uwb/UwbSessionNotificationManagerTest.java
index 7cfce4db..5b3810da 100644
--- a/service/tests/src/com/android/server/uwb/UwbSessionNotificationManagerTest.java
+++ b/service/tests/src/com/android/server/uwb/UwbSessionNotificationManagerTest.java
@@ -17,10 +17,11 @@
package com.android.server.uwb;
import static com.android.server.uwb.UwbTestUtils.DATA_PAYLOAD;
-import static com.android.server.uwb.UwbTestUtils.PEER_EXTENDED_MAC_ADDRESS;
+import static com.android.server.uwb.UwbTestUtils.PEER_EXTENDED_UWB_ADDRESS;
import static com.android.server.uwb.UwbTestUtils.PEER_SHORT_MAC_ADDRESS;
-import static com.android.server.uwb.UwbTestUtils.PEER_UWB_ADDRESS;
import static com.android.server.uwb.UwbTestUtils.PERSISTABLE_BUNDLE;
+import static com.android.server.uwb.data.UwbUciConstants.MAC_ADDRESSING_MODE_SHORT;
+import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_DL_TDOA;
import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA;
import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY;
import static com.android.server.uwb.data.UwbUciConstants.STATUS_CODE_FAILED;
@@ -110,7 +111,8 @@ public class UwbSessionNotificationManagerTest {
public void testOnRangingResultWithoutUwbRangingPermission() throws Exception {
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
true, true, false, false, TEST_ELAPSED_NANOS);
when(mUwbInjector.checkUwbRangingPermissionForDataDelivery(eq(ATTRIBUTION_SOURCE), any()))
.thenReturn(false);
@@ -124,7 +126,8 @@ public class UwbSessionNotificationManagerTest {
public void testOnRangingResult_forTwoWay_WithAoa() throws Exception {
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
true, true, false, false, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -138,7 +141,8 @@ public class UwbSessionNotificationManagerTest {
FiraParams.AOA_RESULT_REQUEST_MODE_NO_AOA_REPORT);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
false, false, false, false, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -152,7 +156,8 @@ public class UwbSessionNotificationManagerTest {
FiraParams.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS_AZIMUTH_ONLY);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
true, false, false, false, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -166,7 +171,8 @@ public class UwbSessionNotificationManagerTest {
FiraParams.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS_ELEVATION_ONLY);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
false, true, false, false, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -183,7 +189,8 @@ public class UwbSessionNotificationManagerTest {
when(mFiraParams.hasAngleOfArrivalElevationReport()).thenReturn(true);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
true, true, true, true, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -200,7 +207,8 @@ public class UwbSessionNotificationManagerTest {
when(mFiraParams.hasAngleOfArrivalElevationReport()).thenReturn(true);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
true, true, false, true, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -217,7 +225,8 @@ public class UwbSessionNotificationManagerTest {
when(mFiraParams.hasAngleOfArrivalElevationReport()).thenReturn(false);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
true, true, true, false, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -234,7 +243,8 @@ public class UwbSessionNotificationManagerTest {
when(mFiraParams.hasAngleOfArrivalElevationReport()).thenReturn(true);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
false, false, true, true, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -251,7 +261,8 @@ public class UwbSessionNotificationManagerTest {
when(mFiraParams.hasAngleOfArrivalElevationReport()).thenReturn(true);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
false, true, true, true, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -268,7 +279,8 @@ public class UwbSessionNotificationManagerTest {
when(mFiraParams.hasAngleOfArrivalElevationReport()).thenReturn(true);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_SHORT_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_TWO_WAY,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_TWO_WAY,
true, false, true, true, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -280,7 +292,21 @@ public class UwbSessionNotificationManagerTest {
public void testOnRangingResult_forOwrAoa() throws Exception {
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
UwbTestUtils.generateRangingDataAndRangingReport(
- PEER_EXTENDED_MAC_ADDRESS, RANGING_MEASUREMENT_TYPE_OWR_AOA,
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_OWR_AOA,
+ true, true, false, false, TEST_ELAPSED_NANOS);
+ mUwbSessionNotificationManager.onRangingResult(
+ mUwbSession, testRangingDataAndRangingReport.first);
+ verify(mIUwbRangingCallbacks).onRangingResult(
+ mSessionHandle, testRangingDataAndRangingReport.second);
+ }
+
+ @Test
+ public void testOnRangingResult_forDlTDoA() throws Exception {
+ Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
+ UwbTestUtils.generateRangingDataAndRangingReport(
+ PEER_SHORT_MAC_ADDRESS, MAC_ADDRESSING_MODE_SHORT,
+ RANGING_MEASUREMENT_TYPE_DL_TDOA,
true, true, false, false, TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
@@ -436,37 +462,40 @@ public class UwbSessionNotificationManagerTest {
@Test
public void testOnDataReceived() throws Exception {
- mUwbSessionNotificationManager.onDataReceived(mUwbSession, PEER_UWB_ADDRESS,
+ mUwbSessionNotificationManager.onDataReceived(mUwbSession, PEER_EXTENDED_UWB_ADDRESS,
PERSISTABLE_BUNDLE, DATA_PAYLOAD);
- verify(mIUwbRangingCallbacks).onDataReceived(eq(mSessionHandle), eq(PEER_UWB_ADDRESS),
+ verify(mIUwbRangingCallbacks).onDataReceived(eq(mSessionHandle), eq(
+ PEER_EXTENDED_UWB_ADDRESS),
eq(PERSISTABLE_BUNDLE), eq(DATA_PAYLOAD));
}
@Test
public void testOnDataReceiveFailed() throws Exception {
- mUwbSessionNotificationManager.onDataReceiveFailed(mUwbSession, PEER_UWB_ADDRESS,
+ mUwbSessionNotificationManager.onDataReceiveFailed(mUwbSession, PEER_EXTENDED_UWB_ADDRESS,
STATUS_CODE_FAILED, PERSISTABLE_BUNDLE);
- verify(mIUwbRangingCallbacks).onDataReceiveFailed(eq(mSessionHandle), eq(PEER_UWB_ADDRESS),
+ verify(mIUwbRangingCallbacks).onDataReceiveFailed(eq(mSessionHandle), eq(
+ PEER_EXTENDED_UWB_ADDRESS),
eq(STATUS_CODE_FAILED), eq(PERSISTABLE_BUNDLE));
}
@Test
public void testOnDataSent() throws Exception {
- mUwbSessionNotificationManager.onDataSent(mUwbSession, PEER_UWB_ADDRESS,
+ mUwbSessionNotificationManager.onDataSent(mUwbSession, PEER_EXTENDED_UWB_ADDRESS,
PERSISTABLE_BUNDLE);
- verify(mIUwbRangingCallbacks).onDataSent(eq(mSessionHandle), eq(PEER_UWB_ADDRESS),
+ verify(mIUwbRangingCallbacks).onDataSent(eq(mSessionHandle), eq(PEER_EXTENDED_UWB_ADDRESS),
eq(PERSISTABLE_BUNDLE));
}
@Test
public void testOnDataSendFailed() throws Exception {
- mUwbSessionNotificationManager.onDataSendFailed(mUwbSession, PEER_UWB_ADDRESS,
+ mUwbSessionNotificationManager.onDataSendFailed(mUwbSession, PEER_EXTENDED_UWB_ADDRESS,
STATUS_CODE_FAILED, PERSISTABLE_BUNDLE);
- verify(mIUwbRangingCallbacks).onDataSendFailed(eq(mSessionHandle), eq(PEER_UWB_ADDRESS),
+ verify(mIUwbRangingCallbacks).onDataSendFailed(eq(mSessionHandle), eq(
+ PEER_EXTENDED_UWB_ADDRESS),
eq(STATUS_CODE_FAILED), eq(PERSISTABLE_BUNDLE));
}
}
diff --git a/service/tests/src/com/android/server/uwb/advertisement/UwbAdvertiseManagerTest.java b/service/tests/src/com/android/server/uwb/advertisement/UwbAdvertiseManagerTest.java
index e7bbad60..a4bd7b03 100644
--- a/service/tests/src/com/android/server/uwb/advertisement/UwbAdvertiseManagerTest.java
+++ b/service/tests/src/com/android/server/uwb/advertisement/UwbAdvertiseManagerTest.java
@@ -19,6 +19,7 @@ package com.android.server.uwb.advertisement;
import static com.android.server.uwb.advertisement.UwbAdvertiseManager.CRITERIA_ANGLE;
import static com.android.server.uwb.advertisement.UwbAdvertiseManager.SIZE_OF_ARRAY_TO_CHECK;
import static com.android.server.uwb.advertisement.UwbAdvertiseManager.TRUSTED_VALUE_OF_VARIANCE;
+import static com.android.server.uwb.util.DataTypeConversionUtil.macAddressByteArrayToLong;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -53,9 +54,11 @@ import java.nio.ByteBuffer;
@Presubmit
public class UwbAdvertiseManagerTest {
private static final byte[] TEST_MAC_ADDRESS_A = {0x11, 0x13};
- private static final int TEST_MAC_ADDRESS_A_INT =
- ByteBuffer.wrap(TEST_MAC_ADDRESS_A).getShort();
+ private static final long TEST_MAC_ADDRESS_A_LONG =
+ macAddressByteArrayToLong(TEST_MAC_ADDRESS_A);
private static final byte[] TEST_MAC_ADDRESS_B = {0x15, 0x17};
+ private static final int TEST_MAC_ADDRESS_B_INT =
+ ByteBuffer.wrap(TEST_MAC_ADDRESS_B).getShort();
private static final byte[] TEST_MAC_ADDRESS_C = {0x12, 0x14};
private static final int TEST_STATUS = FiraParams.STATUS_CODE_OK;
private static final int TEST_LOS = 3;
@@ -153,7 +156,7 @@ public class UwbAdvertiseManagerTest {
@Test
public void testIsTarget_beforeUpdate() throws Exception {
assertFalse(mUwbAdvertiseManager.isPointedTarget(TEST_MAC_ADDRESS_A));
- assertNull(mUwbAdvertiseManager.getAdvertiseTarget(TEST_MAC_ADDRESS_A_INT));
+ assertNull(mUwbAdvertiseManager.getAdvertiseTarget(TEST_MAC_ADDRESS_A_LONG));
}
// Call isPointedTarget() with a different MacAddress (device B), after a call to
@@ -162,7 +165,7 @@ public class UwbAdvertiseManagerTest {
public void testIsTarget_differentMacAddress() throws Exception {
mUwbAdvertiseManager.updateAdvertiseTarget(UWB_OWR_AOA_MEASUREMENT_DEVICE_A);
assertFalse(mUwbAdvertiseManager.isPointedTarget(TEST_MAC_ADDRESS_B));
- assertNotNull(mUwbAdvertiseManager.getAdvertiseTarget(TEST_MAC_ADDRESS_A_INT));
+ assertNotNull(mUwbAdvertiseManager.getAdvertiseTarget(TEST_MAC_ADDRESS_A_LONG));
}
// Confirm the device is not considered to be a target when there aren't sufficient OWR AoA
@@ -279,6 +282,54 @@ public class UwbAdvertiseManagerTest {
assertFalse(mUwbAdvertiseManager.isPointedTarget(TEST_MAC_ADDRESS_A));
}
+ @Test
+ public void testUpdateAdvertiseTarget_outsideTimeThreshold() throws Exception {
+ // Setup OwR AoA Measurements such that the device is a pointed target.
+ UwbOwrAoaMeasurement uwbOwrAoaMeasurement = setupOwrAoaMeasurements(TEST_MAC_ADDRESS_A,
+ NUM_REQUIRED_OWR_AOA_MEASUREMENTS,
+ TEST_AOA_AZIMUTH_Q97_FORMAT, TEST_DELTA_AOA_INSIDE_VARIANCE,
+ TEST_AOA_ELEVATION_Q97_FORMAT, TEST_DELTA_AOA_INSIDE_VARIANCE);
+ assertTrue(mUwbAdvertiseManager.isPointedTarget(TEST_MAC_ADDRESS_A));
+ UwbAdvertiseManager.UwbAdvertiseTarget uwbAdvertiseTarget =
+ mUwbAdvertiseManager.getAdvertiseTarget(TEST_MAC_ADDRESS_A_LONG);
+ assertTrue(uwbAdvertiseTarget.isVarianceCalculated());
+
+ // Fake the current time such that the stored OwR AoA Measurements now seem to be stale, and
+ // record one more OwR AoA Measurement.
+ uwbOwrAoaMeasurement.mFrameSequenceNumber++;
+ when(mUwbInjector.getElapsedSinceBootMillis()).thenReturn(
+ OWR_AOA_MEASUREMENT_TIME_OUTSIDE_THRESHOLD_MILLIS);
+ mUwbAdvertiseManager.updateAdvertiseTarget(uwbOwrAoaMeasurement);
+
+ // Check that the variance is not calculated (as a proxy for the number of stored OwR AoA
+ // measurements for the target, which should now be just 1).
+ uwbAdvertiseTarget = mUwbAdvertiseManager.getAdvertiseTarget(TEST_MAC_ADDRESS_A_LONG);
+ assertFalse(uwbAdvertiseTarget.isVarianceCalculated());
+ assertFalse(mUwbAdvertiseManager.isPointedTarget(TEST_MAC_ADDRESS_A));
+ }
+
+ @Test
+ public void testRemoveAdvertiseTarget() throws Exception {
+ // Call updateAdvertiseTarget() with a OwR AoA Measurement and verify that a
+ // UwbAdvertiseTarget gets created for it (but not for another random device).
+ UwbOwrAoaMeasurement uwbOwrAoaMeasurement = setupOwrAoaMeasurements(TEST_MAC_ADDRESS_A,
+ NUM_REQUIRED_OWR_AOA_MEASUREMENTS,
+ TEST_AOA_AZIMUTH_Q97_FORMAT, TEST_DELTA_AOA_INSIDE_VARIANCE,
+ TEST_AOA_ELEVATION_Q97_FORMAT, TEST_DELTA_AOA_INSIDE_VARIANCE);
+ mUwbAdvertiseManager.updateAdvertiseTarget(uwbOwrAoaMeasurement);
+
+ assertNotNull(mUwbAdvertiseManager.getAdvertiseTarget(TEST_MAC_ADDRESS_A_LONG));
+ assertNull(mUwbAdvertiseManager.getAdvertiseTarget(TEST_MAC_ADDRESS_B_INT));
+
+ // Call removeAdvertiseTarget() for the device and verify that it has been removed.
+ mUwbAdvertiseManager.removeAdvertiseTarget(TEST_MAC_ADDRESS_A);
+ assertNull(mUwbAdvertiseManager.getAdvertiseTarget(TEST_MAC_ADDRESS_A_LONG));
+
+ // Call removeAdvertiseTarget() for a device that doesn't exist and verify no exceptions.
+ mUwbAdvertiseManager.removeAdvertiseTarget(TEST_MAC_ADDRESS_B);
+ assertNull(mUwbAdvertiseManager.getAdvertiseTarget(TEST_MAC_ADDRESS_B_INT));
+ }
+
private UwbOwrAoaMeasurement setupOwrAoaMeasurements(byte[] macAddress, int numMeasurements,
int aoaAzimuth, int aoaAzimuthVariance,
int aoaElevation, int aoaElevationVariance) {
diff --git a/service/tests/src/com/android/server/uwb/data/UwbRangingDataTest.java b/service/tests/src/com/android/server/uwb/data/UwbRangingDataTest.java
index addcf25c..11d71550 100644
--- a/service/tests/src/com/android/server/uwb/data/UwbRangingDataTest.java
+++ b/service/tests/src/com/android/server/uwb/data/UwbRangingDataTest.java
@@ -16,6 +16,7 @@
package com.android.server.uwb.data;
+import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_DL_TDOA;
import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_OWR_AOA;
import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY;
import static com.android.server.uwb.util.UwbUtil.convertFloatToQFormat;
@@ -50,8 +51,11 @@ public class UwbRangingDataTest {
private static final int TEST_MAC_ADDRESS_MODE = 1;
private static final byte[] TEST_MAC_ADDRESS = {0x1, 0x3};
private static final int TEST_STATUS = FiraParams.STATUS_CODE_OK;
+ private static final int TEST_MESSAGE_TYPE = 1;
+ private static final int TEST_MESSAGE_CONTROL = 1331;
private static final int TEST_LOS = 0;
private static final int TEST_BLOCK_INDEX = 5;
+ private static final int TEST_ROUND_INDEX = 1;
private static final int TEST_FRAME_SEQ_NUMBER = 1;
private static final int TEST_DISTANCE = 101;
private static final int TEST_AOA_AZIMUTH = 67;
@@ -72,6 +76,15 @@ public class UwbRangingDataTest {
private static final int TEST_AOA_DEST_ELEVATION_FOM = 90;
private static final int TEST_SLOT_IDX = 10;
private static final int TEST_RSSI = -1;
+ private static final long TEST_TIMESTAMP = 500_000L;
+ private static final int TEST_ANCHOR_CFO = 100;
+ private static final int TEST_CFO = 200;
+ private static final long TEST_INTIATOR_REPLY_TIME = 500_000L;
+ private static final long TEST_RESPONDER_REPLY_TIME = 300_000L;
+ private static final int TEST_INITIATOR_RESPONDER_TOF = 500;
+ private static final byte[] TEST_ANCHOR_LOCATION = {0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09, 0x10};
+ private static final byte[] TEST_ACTIVE_RANGING_ROUNDS = {0x02, 0x08};
private static final byte[] TEST_RAW_NTF_DATA = {0x10, 0x01};
private UwbRangingData mUwbRangingData;
@@ -153,4 +166,48 @@ public class UwbRangingDataTest {
assertThat(mUwbRangingData.toString()).isEqualTo(testString);
}
+
+
+ @Test
+ public void testInitializeUwbRangingData_withUwbDlTDoAMeasurement() throws Exception {
+ final int noOfRangingMeasures = 1;
+ final UwbDlTDoAMeasurement[] uwbDlTDoAMeasurements =
+ new UwbDlTDoAMeasurement[noOfRangingMeasures];
+ uwbDlTDoAMeasurements[0] = new UwbDlTDoAMeasurement(TEST_MAC_ADDRESS, TEST_STATUS,
+ TEST_MESSAGE_TYPE, TEST_MESSAGE_CONTROL, TEST_BLOCK_INDEX, TEST_ROUND_INDEX,
+ TEST_LOS, TEST_AOA_AZIMUTH_Q97_FORMAT, TEST_AOA_AZIMUTH_FOM,
+ TEST_AOA_ELEVATION_Q97_FORMAT, TEST_AOA_ELEVATION_FOM, TEST_RSSI, TEST_TIMESTAMP,
+ TEST_TIMESTAMP, TEST_ANCHOR_CFO, TEST_CFO, TEST_INTIATOR_REPLY_TIME,
+ TEST_RESPONDER_REPLY_TIME, TEST_INITIATOR_RESPONDER_TOF, TEST_ANCHOR_LOCATION,
+ TEST_ACTIVE_RANGING_ROUNDS);
+
+ final int rangingMeasuresType = RANGING_MEASUREMENT_TYPE_DL_TDOA;
+ mUwbRangingData = new UwbRangingData(TEST_SEQ_COUNTER, TEST_SESSION_ID,
+ TEST_RCR_INDICATION, TEST_CURR_RANGING_INTERVAL, rangingMeasuresType,
+ TEST_MAC_ADDRESS_MODE, noOfRangingMeasures, uwbDlTDoAMeasurements,
+ TEST_RAW_NTF_DATA);
+
+ assertThat(mUwbRangingData.getSequenceCounter()).isEqualTo(TEST_SEQ_COUNTER);
+ assertThat(mUwbRangingData.getSessionId()).isEqualTo(TEST_SESSION_ID);
+ assertThat(mUwbRangingData.getRcrIndication()).isEqualTo(TEST_RCR_INDICATION);
+ assertThat(mUwbRangingData.getCurrRangingInterval()).isEqualTo(TEST_CURR_RANGING_INTERVAL);
+ assertThat(mUwbRangingData.getRangingMeasuresType()).isEqualTo(rangingMeasuresType);
+ assertThat(mUwbRangingData.getMacAddressMode()).isEqualTo(TEST_MAC_ADDRESS_MODE);
+ assertThat(mUwbRangingData.getNoOfRangingMeasures()).isEqualTo(1);
+ assertThat(mUwbRangingData.getRawNtfData()).isEqualTo(TEST_RAW_NTF_DATA);
+
+ final String testString = "UwbRangingData { "
+ + " SeqCounter = " + TEST_SEQ_COUNTER
+ + ", SessionId = " + TEST_SESSION_ID
+ + ", RcrIndication = " + TEST_RCR_INDICATION
+ + ", CurrRangingInterval = " + TEST_CURR_RANGING_INTERVAL
+ + ", RangingMeasuresType = " + rangingMeasuresType
+ + ", MacAddressMode = " + TEST_MAC_ADDRESS_MODE
+ + ", NoOfRangingMeasures = " + noOfRangingMeasures
+ + ", RangingDlTDoAMeasure = " + Arrays.toString(uwbDlTDoAMeasurements)
+ + ", RawNotificationData = " + Arrays.toString(TEST_RAW_NTF_DATA)
+ + '}';
+
+ assertThat(mUwbRangingData.toString()).isEqualTo(testString);
+ }
}
diff --git a/service/tests/src/com/android/server/uwb/params/CccEncoderTest.java b/service/tests/src/com/android/server/uwb/params/CccEncoderTest.java
index fcd2aa90..766920ed 100644
--- a/service/tests/src/com/android/server/uwb/params/CccEncoderTest.java
+++ b/service/tests/src/com/android/server/uwb/params/CccEncoderTest.java
@@ -65,8 +65,8 @@ public class CccEncoderTest {
.setHoppingSequence(HOPPING_SEQUENCE_DEFAULT);
private static final byte[] TEST_CCC_OPEN_RANGING_TLV_DATA =
- UwbUtil.getByteArray("0001000201010401090501010904800100000E010011010103010"
- + "11B01062301012C0100A3020100A4020000A50100A602D0020802B004140101");
+ UwbUtil.getByteArray("0001010201010401090501010904800100000E010011010103010"
+ + "11B01062301012C0100A3020001A4020000A50100A602D0020802B004140101");
private final CccEncoder mCccEncoder = new CccEncoder();
diff --git a/service/tests/src/com/android/server/uwb/params/FiraDecoderTest.java b/service/tests/src/com/android/server/uwb/params/FiraDecoderTest.java
index d63172f5..67c1bf89 100644
--- a/service/tests/src/com/android/server/uwb/params/FiraDecoderTest.java
+++ b/service/tests/src/com/android/server/uwb/params/FiraDecoderTest.java
@@ -92,10 +92,11 @@ public class FiraDecoderTest {
+ "E40401010101"
+ "E50403000000"
+ "E601FF"
- + "E70101";
+ + "E70101"
+ + "E80401010101";
private static final byte[] TEST_FIRA_SPECIFICATION_TLV_DATA =
UwbUtil.getByteArray(TEST_FIRA_SPECIFICATION_TLV_STRING);
- public static final int TEST_FIRA_SPECIFICATION_TLV_NUM_PARAMS = 23;
+ public static final int TEST_FIRA_SPECIFICATION_TLV_NUM_PARAMS = 24;
private final FiraDecoder mFiraDecoder = new FiraDecoder();
public static void verifyFiraSpecification(FiraSpecificationParams firaSpecificationParams) {
diff --git a/service/tests/src/com/android/server/uwb/util/DataTypeConversionUtilTest.java b/service/tests/src/com/android/server/uwb/util/DataTypeConversionUtilTest.java
index e14cc968..fba1cc73 100644
--- a/service/tests/src/com/android/server/uwb/util/DataTypeConversionUtilTest.java
+++ b/service/tests/src/com/android/server/uwb/util/DataTypeConversionUtilTest.java
@@ -168,4 +168,54 @@ public class DataTypeConversionUtilTest {
new byte[0]));
}
+
+ @Test
+ public void macAddressByteArrayToLong_success_shortMacAddress_LittleEndian() {
+ assertThat(
+ DataTypeConversionUtil.macAddressByteArrayToLong(new byte[] {0x35, 0x37}))
+ .isEqualTo(0x3735);
+ }
+
+ @Test
+ public void macAddressByteArrayToLong_success_int_LittleEndian() {
+ assertThat(
+ DataTypeConversionUtil.macAddressByteArrayToLong(
+ new byte[] {0x35, 0x37, 0x38, 0x48}))
+ .isEqualTo(0x48383735);
+ }
+
+ @Test
+ public void macAddressByteArrayToLong_success_extendedMacAddress_LittleEndian() {
+ assertThat(
+ DataTypeConversionUtil.macAddressByteArrayToLong(
+ new byte[] {0x35, 0x37, 0x38, 0x48, 0x22, 0x24, 0x26, 0x28}))
+ .isEqualTo(0x2826242248383735L);
+ }
+
+ @Test
+ public void macAddressByteArrayToLong_success_shortInExtendedMacAddressFormat_LittleEndian() {
+ assertThat(
+ DataTypeConversionUtil.macAddressByteArrayToLong(
+ new byte[] {0x35, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}))
+ .isEqualTo(0x3735);
+ }
+
+ @Test
+ public void macAddressByteArrayToLong_badLength() {
+ // Unsupported lengths.
+ assertThrows(
+ NumberFormatException.class,
+ () -> DataTypeConversionUtil.macAddressByteArrayToLong(new byte[]{0x01}));
+ assertThrows(
+ NumberFormatException.class,
+ () -> DataTypeConversionUtil.macAddressByteArrayToLong(
+ new byte[]{0x01, 0x02, 0x03}));
+
+ // Too long
+ assertThrows(
+ NumberFormatException.class,
+ () -> DataTypeConversionUtil.macAddressByteArrayToLong(
+ new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}));
+
+ }
}
diff --git a/service/uci/jni/Android.bp b/service/uci/jni/Android.bp
index 7d3c65ba..c5402e27 100755..100644
--- a/service/uci/jni/Android.bp
+++ b/service/uci/jni/Android.bp
@@ -8,15 +8,18 @@ rust_defaults {
lints: "android",
clippy_lints: "android",
min_sdk_version: "Tiramisu",
- srcs: ["rust/lib.rs"],
+ srcs: ["src/lib.rs"],
rustlibs: [
- "libjni",
"libbinder_rs",
+ "libjni",
"liblog_rust",
"liblogger",
"libnum_traits",
+ "libthiserror",
+ "libtokio",
+ "libuci_hal_android",
+ "libuwb_core",
"libuwb_uci_packets",
- "libuwb_uci_rust",
],
prefer_rlib: true,
apex_available: [
diff --git a/service/uci/jni/rust/lib.rs b/service/uci/jni/rust/lib.rs
deleted file mode 100644
index 0287e35b..00000000
--- a/service/uci/jni/rust/lib.rs
+++ /dev/null
@@ -1,1563 +0,0 @@
-//! jni for uwb native stack
-use jni::objects::{JObject, JString, JValue};
-use jni::sys::{
- jarray, jboolean, jbyte, jbyteArray, jint, jintArray, jlong, jobject, jobjectArray, jshort,
- jshortArray, jsize,
-};
-use jni::JNIEnv;
-use log::{error, info};
-use num_traits::ToPrimitive;
-use uwb_uci_packets::{
- GetCapsInfoRspPacket, Packet, SessionGetAppConfigRspPacket, SessionSetAppConfigRspPacket,
- StatusCode, UciResponseChild, UciResponsePacket, UciVendor_9_ResponseChild,
- UciVendor_A_ResponseChild, UciVendor_B_ResponseChild, UciVendor_E_ResponseChild,
- UciVendor_F_ResponseChild,
-};
-use uwb_uci_rust::error::UwbErr;
-use uwb_uci_rust::event_manager::EventManagerImpl as EventManager;
-use uwb_uci_rust::uci::{uci_hrcv::UciResponse, Dispatcher, DispatcherImpl, JNICommand};
-
-trait Context<'a> {
- fn convert_byte_array(&self, array: jbyteArray) -> Result<Vec<u8>, jni::errors::Error>;
- fn get_array_length(&self, array: jarray) -> Result<jsize, jni::errors::Error>;
- fn get_short_array_region(
- &self,
- array: jshortArray,
- start: jsize,
- buf: &mut [jshort],
- ) -> Result<(), jni::errors::Error>;
- fn get_int_array_region(
- &self,
- array: jintArray,
- start: jsize,
- buf: &mut [jint],
- ) -> Result<(), jni::errors::Error>;
- fn is_same_object(&self, obj1: JObject, obj2: JObject) -> Result<bool, jni::errors::Error>;
- fn get_dispatcher(&self) -> Result<&'a mut dyn Dispatcher, UwbErr>;
-}
-
-struct JniContext<'a> {
- env: JNIEnv<'a>,
- obj: JObject<'a>,
-}
-
-impl<'a> JniContext<'a> {
- fn new(env: JNIEnv<'a>, obj: JObject<'a>) -> Self {
- Self { env, obj }
- }
-}
-
-struct ControleeData {
- addresses: jshortArray,
- sub_session_ids: jintArray,
- message_control: jint,
- sub_session_keys: jbyteArray,
-}
-
-impl<'a> Context<'a> for JniContext<'a> {
- fn convert_byte_array(&self, array: jbyteArray) -> Result<Vec<u8>, jni::errors::Error> {
- self.env.convert_byte_array(array)
- }
- fn get_array_length(&self, array: jarray) -> Result<jsize, jni::errors::Error> {
- self.env.get_array_length(array)
- }
- fn get_short_array_region(
- &self,
- array: jshortArray,
- start: jsize,
- buf: &mut [jshort],
- ) -> Result<(), jni::errors::Error> {
- self.env.get_short_array_region(array, start, buf)
- }
- fn get_int_array_region(
- &self,
- array: jintArray,
- start: jsize,
- buf: &mut [jint],
- ) -> Result<(), jni::errors::Error> {
- self.env.get_int_array_region(array, start, buf)
- }
- fn is_same_object(&self, obj1: JObject, obj2: JObject) -> Result<bool, jni::errors::Error> {
- self.env.is_same_object(obj1, obj2)
- }
- fn get_dispatcher(&self) -> Result<&'a mut dyn Dispatcher, UwbErr> {
- let dispatcher_ptr_value = self.env.get_field(self.obj, "mDispatcherPointer", "J")?;
- let dispatcher_ptr = dispatcher_ptr_value.j()?;
- if dispatcher_ptr == 0i64 {
- error!("The dispatcher is not initialized.");
- return Err(UwbErr::NoneDispatcher);
- }
- // Safety: dispatcher pointer must not be a null pointer and it must point to a valid
- // dispatcher object. This can be ensured because the dispatcher is created in an earlier
- // stage and won't be deleted before calling doDeinitialize.
- unsafe { Ok(&mut *(dispatcher_ptr as *mut DispatcherImpl)) }
- }
-}
-
-/// Initialize UWB
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeInit(
- _env: JNIEnv,
- _obj: JObject,
-) -> jboolean {
- logger::init(
- logger::Config::default()
- .with_tag_on_device("uwb")
- .with_min_level(log::Level::Trace)
- .with_filter("trace,jni=info"),
- );
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeInit: enter");
- true as jboolean
-}
-
-/// Get max session number
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetMaxSessionNumber(
- _env: JNIEnv,
- _obj: JObject,
-) -> jint {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetMaxSessionNumber: enter");
- 5
-}
-
-/// Turn on UWB. initialize the GKI module and HAL module for UWB device.
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoInitialize(
- env: JNIEnv,
- obj: JObject,
- chip_id: JString,
-) -> jboolean {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoInitialize: enter");
- boolean_result_helper(
- do_initialize(&JniContext::new(env, obj), env.get_string(chip_id).unwrap().into()),
- "DoInitialize",
- )
-}
-
-/// Turn off UWB. Deinitilize the GKI and HAL module, power of the UWB device.
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoDeinitialize(
- env: JNIEnv,
- obj: JObject,
- chip_id: JString,
-) -> jboolean {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoDeinitialize: enter");
- boolean_result_helper(
- do_deinitialize(&JniContext::new(env, obj), env.get_string(chip_id).unwrap().into()),
- "DoDeinitialize",
- )
-}
-
-/// get nanos
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetTimestampResolutionNanos(
- _env: JNIEnv,
- _obj: JObject,
-) -> jlong {
- info!(
- "Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetTimestampResolutionNanos: enter"
- );
- 0
-}
-
-/// reset the device
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDeviceReset(
- env: JNIEnv,
- obj: JObject,
- reset_config: jbyte,
- chip_id: JString,
-) -> jbyte {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeDeviceReset: enter");
- byte_result_helper(
- reset_device(
- &JniContext::new(env, obj),
- reset_config as u8,
- env.get_string(chip_id).unwrap().into(),
- ),
- "ResetDevice",
- )
-}
-
-/// init the session
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionInit(
- env: JNIEnv,
- obj: JObject,
- session_id: jint,
- session_type: jbyte,
- chip_id: JString,
-) -> jbyte {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionInit: enter");
- byte_result_helper(
- session_init(
- &JniContext::new(env, obj),
- session_id as u32,
- session_type as u8,
- env.get_string(chip_id).unwrap().into(),
- ),
- "SessionInit",
- )
-}
-
-/// deinit the session
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionDeInit(
- env: JNIEnv,
- obj: JObject,
- session_id: jint,
- chip_id: JString,
-) -> jbyte {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionDeInit: enter");
- byte_result_helper(
- session_deinit(
- &JniContext::new(env, obj),
- session_id as u32,
- env.get_string(chip_id).unwrap().into(),
- ),
- "SessionDeInit",
- )
-}
-
-/// get session count
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetSessionCount(
- env: JNIEnv,
- obj: JObject,
- chip_id: JString,
-) -> jbyte {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetSessionCount: enter");
- match get_session_count(&JniContext::new(env, obj), env.get_string(chip_id).unwrap().into()) {
- Ok(count) => count,
- Err(e) => {
- error!("GetSessionCount failed with {:?}", e);
- -1
- }
- }
-}
-
-/// start the ranging
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeRangingStart(
- env: JNIEnv,
- obj: JObject,
- session_id: jint,
- chip_id: JString,
-) -> jbyte {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeRangingStart: enter");
- byte_result_helper(
- ranging_start(
- &JniContext::new(env, obj),
- session_id as u32,
- env.get_string(chip_id).unwrap().into(),
- ),
- "RangingStart",
- )
-}
-
-/// stop the ranging
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeRangingStop(
- env: JNIEnv,
- obj: JObject,
- session_id: jint,
- chip_id: JString,
-) -> jbyte {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeRangingStop: enter");
- byte_result_helper(
- ranging_stop(
- &JniContext::new(env, obj),
- session_id as u32,
- env.get_string(chip_id).unwrap().into(),
- ),
- "RangingStop",
- )
-}
-
-/// get the session state
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetSessionState(
- env: JNIEnv,
- obj: JObject,
- session_id: jint,
- chip_id: JString,
-) -> jbyte {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetSessionState: enter");
- match get_session_state(
- &JniContext::new(env, obj),
- session_id as u32,
- env.get_string(chip_id).unwrap().into(),
- ) {
- Ok(state) => state,
- Err(e) => {
- error!("GetSessionState failed with {:?}", e);
- -1
- }
- }
-}
-
-/// set app configurations
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetAppConfigurations(
- env: JNIEnv,
- obj: JObject,
- session_id: jint,
- no_of_params: jint,
- app_config_param_len: jint,
- app_config_params: jbyteArray,
- chip_id: JString,
-) -> jbyteArray {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetAppConfigurations: enter");
- match set_app_configurations(
- &JniContext::new(env, obj),
- session_id as u32,
- no_of_params as u32,
- app_config_param_len as u32,
- app_config_params,
- env.get_string(chip_id).unwrap().into(),
- ) {
- Ok(data) => {
- let uwb_config_status_class =
- env.find_class("com/android/server/uwb/data/UwbConfigStatusData").unwrap();
- let mut buf: Vec<u8> = Vec::new();
- for iter in data.get_cfg_status() {
- buf.push(iter.cfg_id as u8);
- buf.push(iter.status as u8);
- }
- let cfg_jbytearray = env.byte_array_from_slice(&buf).unwrap();
- let uwb_config_status_object = env.new_object(
- uwb_config_status_class,
- "(II[B)V",
- &[
- JValue::Int(data.get_status().to_i32().unwrap()),
- JValue::Int(data.get_cfg_status().len().to_i32().unwrap()),
- JValue::Object(JObject::from(cfg_jbytearray)),
- ],
- );
- *uwb_config_status_object.unwrap()
- }
- Err(e) => {
- error!("SetAppConfig failed with: {:?}", e);
- *JObject::null()
- }
- }
-}
-
-/// get app configurations
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetAppConfigurations(
- env: JNIEnv,
- obj: JObject,
- session_id: jint,
- no_of_params: jint,
- app_config_param_len: jint,
- app_config_params: jbyteArray,
- chip_id: JString,
-) -> jbyteArray {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetAppConfigurations: enter");
- match get_app_configurations(
- &JniContext::new(env, obj),
- session_id as u32,
- no_of_params as u32,
- app_config_param_len as u32,
- app_config_params,
- env.get_string(chip_id).unwrap().into(),
- ) {
- Ok(data) => {
- let uwb_tlv_info_class =
- env.find_class("com/android/server/uwb/data/UwbTlvData").unwrap();
- let mut buf: Vec<u8> = Vec::new();
- for tlv in data.get_tlvs() {
- buf.push(tlv.cfg_id as u8);
- buf.push(tlv.v.len() as u8);
- buf.extend(&tlv.v);
- }
- let tlv_jbytearray = env.byte_array_from_slice(&buf).unwrap();
- let uwb_tlv_info_object = env.new_object(
- uwb_tlv_info_class,
- "(II[B)V",
- &[
- JValue::Int(data.get_status().to_i32().unwrap()),
- JValue::Int(data.get_tlvs().len().to_i32().unwrap()),
- JValue::Object(JObject::from(tlv_jbytearray)),
- ],
- );
- *uwb_tlv_info_object.unwrap()
- }
- Err(e) => {
- error!("GetAppConfig failed with: {:?}", e);
- *JObject::null()
- }
- }
-}
-
-/// get capability info
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetCapsInfo(
- env: JNIEnv,
- obj: JObject,
- chip_id: JString,
-) -> jbyteArray {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetCapsInfo: enter");
- match get_caps_info(&JniContext::new(env, obj), env.get_string(chip_id).unwrap().into()) {
- Ok(data) => {
- let uwb_tlv_info_class =
- env.find_class("com/android/server/uwb/data/UwbTlvData").unwrap();
- let mut buf: Vec<u8> = Vec::new();
- for tlv in data.get_tlvs() {
- buf.push(tlv.t as u8);
- buf.push(tlv.v.len() as u8);
- buf.extend(&tlv.v);
- }
- let tlv_jbytearray = env.byte_array_from_slice(&buf).unwrap();
- let uwb_tlv_info_object = env.new_object(
- uwb_tlv_info_class,
- "(II[B)V",
- &[
- JValue::Int(data.get_status().to_i32().unwrap()),
- JValue::Int(data.get_tlvs().len().to_i32().unwrap()),
- JValue::Object(JObject::from(tlv_jbytearray)),
- ],
- );
- *uwb_tlv_info_object.unwrap()
- }
- Err(e) => {
- error!("GetCapsInfo failed with: {:?}", e);
- *JObject::null()
- }
- }
-}
-
-/// update multicast list by SESSION_UPDATE_CONTROLLER_MULTICAST_LIST_CMD
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeControllerMulticastListUpdateV1(
- env: JNIEnv,
- obj: JObject,
- session_id: jint,
- action: jbyte,
- no_of_controlee: jbyte,
- addresses: jshortArray,
- sub_session_ids: jintArray,
- chip_id: JString,
-) -> jbyte {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeControllerMulticastListUpdateV1: enter");
- let controlee_data = ControleeData {
- addresses,
- sub_session_ids,
- message_control: -1,
- sub_session_keys: *JObject::null(),
- };
- byte_result_helper(
- multicast_list_update(
- &JniContext::new(env, obj),
- session_id as u32,
- action as u8,
- no_of_controlee as u8,
- controlee_data,
- env.get_string(chip_id).unwrap().into(),
- ),
- "ControllerMulticastListUpdate",
- )
-}
-
-/// update multicast list by SESSION_UPDATE_CONTROLLER_MULTICAST_LIST_V2_CMD
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeControllerMulticastListUpdateV2(
- env: JNIEnv,
- obj: JObject,
- session_id: jint,
- action: jbyte,
- no_of_controlee: jbyte,
- addresses: jshortArray,
- sub_session_ids: jintArray,
- message_control: jint,
- sub_session_keys: jbyteArray,
- chip_id: JString,
-) -> jbyte {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeControllerMulticastListUpdateV2: enter");
- let controlee_data =
- ControleeData { addresses, sub_session_ids, message_control, sub_session_keys };
- byte_result_helper(
- multicast_list_update(
- &JniContext::new(env, obj),
- session_id as u32,
- action as u8,
- no_of_controlee as u8,
- controlee_data,
- env.get_string(chip_id).unwrap().into(),
- ),
- "ControllerMulticastListUpdate",
- )
-}
-
-/// set country code
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetCountryCode(
- env: JNIEnv,
- obj: JObject,
- country_code: jbyteArray,
- chip_id: JString,
-) -> jbyte {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetCountryCode: enter");
- byte_result_helper(
- set_country_code(
- &JniContext::new(env, obj),
- country_code,
- env.get_string(chip_id).unwrap().into(),
- ),
- "SetCountryCode",
- )
-}
-
-/// Set log mode for new stack.
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetLogMode(
- _env: JNIEnv,
- _obj: JObject,
- _log_mode_jstring: JString, // Ignored as existing stack sets log mode differently.
-) -> jboolean {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeSetLogMode: enter");
- false as jboolean
-}
-
-/// set country code
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSendRawVendorCmd(
- env: JNIEnv,
- obj: JObject,
- gid: jint,
- oid: jint,
- payload: jbyteArray,
- chip_id: JString,
-) -> jobject {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeRawVendor: enter");
- let uwb_vendor_uci_response_class =
- env.find_class("com/android/server/uwb/data/UwbVendorUciResponse").unwrap();
- match send_raw_vendor_cmd(
- &JniContext::new(env, obj),
- gid.try_into().expect("invalid gid"),
- oid.try_into().expect("invalid oid"),
- payload,
- env.get_string(chip_id).unwrap().into(),
- ) {
- Ok((gid, oid, payload)) => *env
- .new_object(
- uwb_vendor_uci_response_class,
- "(BII[B)V",
- &[
- JValue::Byte(StatusCode::UciStatusOk.to_i8().unwrap()),
- JValue::Int(gid.to_i32().unwrap()),
- JValue::Int(oid.to_i32().unwrap()),
- JValue::Object(JObject::from(
- env.byte_array_from_slice(payload.as_ref()).unwrap(),
- )),
- ],
- )
- .unwrap(),
- Err(e) => {
- error!("send raw uci cmd failed with: {:?}", e);
- *env.new_object(
- uwb_vendor_uci_response_class,
- "(BII[B)V",
- &[
- JValue::Byte(StatusCode::UciStatusFailed.to_i8().unwrap()),
- JValue::Int(-1),
- JValue::Int(-1),
- JValue::Object(JObject::null()),
- ],
- )
- .unwrap()
- }
- }
-}
-
-/// retrieve the UWB power stats
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetPowerStats(
- env: JNIEnv,
- obj: JObject,
- chip_id: JString,
-) -> jobject {
- info!("Java_com_android_server_uwb_jni_NativeUwbManager_nativeGetPowerStats: enter");
- let uwb_power_stats_class =
- env.find_class("com/android/server/uwb/info/UwbPowerStats").unwrap();
- match get_power_stats(&JniContext::new(env, obj), env.get_string(chip_id).unwrap().into()) {
- Ok(para) => {
- let power_stats = env.new_object(uwb_power_stats_class, "(IIII)V", &para).unwrap();
- *power_stats
- }
- Err(e) => {
- error!("Get power stats failed with: {:?}", e);
- *JObject::null()
- }
- }
-}
-
-fn boolean_result_helper(result: Result<(), UwbErr>, function_name: &str) -> jboolean {
- match result {
- Ok(()) => true as jboolean,
- Err(err) => {
- error!("{} failed with: {:?}", function_name, err);
- false as jboolean
- }
- }
-}
-
-fn byte_result_helper(result: Result<(), UwbErr>, function_name: &str) -> jbyte {
- match result {
- Ok(()) => StatusCode::UciStatusOk.to_i8().unwrap(),
- Err(err) => {
- error!("{} failed with: {:?}", function_name, err);
- match err {
- UwbErr::StatusCode(status_code) => status_code
- .to_i8()
- .unwrap_or_else(|| StatusCode::UciStatusFailed.to_i8().unwrap()),
- _ => StatusCode::UciStatusFailed.to_i8().unwrap(),
- }
- }
- }
-}
-
-fn do_initialize<'a, T: Context<'a>>(context: &T, chip_id: String) -> Result<(), UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- match dispatcher.block_on_jni_command(JNICommand::Enable, chip_id.clone())? {
- UciResponse::EnableRsp(enable) => {
- if !enable {
- error!("Enable UWB failed.");
- return Err(UwbErr::failed());
- }
- }
- _ => {
- error!("Received wrong response!");
- return Err(UwbErr::failed());
- }
- }
- match uwa_get_device_info(dispatcher, chip_id) {
- Ok(res) => {
- if let UciResponse::GetDeviceInfoRsp(device_info) = res {
- dispatcher.set_device_info(Some(device_info));
- }
- }
- Err(e) => {
- error!("GetDeviceInfo failed with: {:?}", e);
- return Err(UwbErr::failed());
- }
- }
- Ok(())
-}
-
-fn do_deinitialize<'a, T: Context<'a>>(context: &T, chip_id: String) -> Result<(), UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- dispatcher.send_jni_command(JNICommand::Disable(true), chip_id)?;
- dispatcher.wait_for_exit()?;
- Ok(())
-}
-
-// unused, but leaving this behind if we want to use it later.
-#[allow(dead_code)]
-fn get_specification_info<'a, T: Context<'a>>(
- context: &T,
- _chip_id: String,
-) -> Result<[JValue<'a>; 16], UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- match dispatcher.get_device_info() {
- Some(data) => {
- Ok([
- JValue::Int((data.get_uci_version() & 0xFF).into()),
- JValue::Int(((data.get_uci_version() >> 8) & 0xF).into()),
- JValue::Int(((data.get_uci_version() >> 12) & 0xF).into()),
- JValue::Int((data.get_mac_version() & 0xFF).into()),
- JValue::Int(((data.get_mac_version() >> 8) & 0xF).into()),
- JValue::Int(((data.get_mac_version() >> 12) & 0xF).into()),
- JValue::Int((data.get_phy_version() & 0xFF).into()),
- JValue::Int(((data.get_phy_version() >> 8) & 0xF).into()),
- JValue::Int(((data.get_phy_version() >> 12) & 0xF).into()),
- JValue::Int((data.get_uci_test_version() & 0xFF).into()),
- JValue::Int(((data.get_uci_test_version() >> 8) & 0xF).into()),
- JValue::Int(((data.get_uci_test_version() >> 12) & 0xF).into()),
- JValue::Int(1), // fira_major_version
- JValue::Int(0), // fira_minor_version
- JValue::Int(1), // ccc_major_version
- JValue::Int(0), // ccc_minor_version
- ])
- }
- None => {
- error!("Fail to get specification info.");
- Err(UwbErr::failed())
- }
- }
-}
-
-fn session_init<'a, T: Context<'a>>(
- context: &T,
- session_id: u32,
- session_type: u8,
- chip_id: String,
-) -> Result<(), UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- let res = match dispatcher
- .block_on_jni_command(JNICommand::UciSessionInit(session_id, session_type), chip_id)?
- {
- UciResponse::SessionInitRsp(data) => data,
- _ => return Err(UwbErr::failed()),
- };
- status_code_to_res(res.get_status())
-}
-
-fn session_deinit<'a, T: Context<'a>>(
- context: &T,
- session_id: u32,
- chip_id: String,
-) -> Result<(), UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- let res =
- match dispatcher.block_on_jni_command(JNICommand::UciSessionDeinit(session_id), chip_id)? {
- UciResponse::SessionDeinitRsp(data) => data,
- _ => return Err(UwbErr::failed()),
- };
- status_code_to_res(res.get_status())
-}
-
-fn get_session_count<'a, T: Context<'a>>(context: &T, chip_id: String) -> Result<jbyte, UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- match dispatcher.block_on_jni_command(JNICommand::UciSessionGetCount, chip_id)? {
- UciResponse::SessionGetCountRsp(rsp) => match status_code_to_res(rsp.get_status()) {
- Ok(()) => Ok(rsp.get_session_count() as jbyte),
- Err(err) => Err(err),
- },
- _ => Err(UwbErr::failed()),
- }
-}
-
-fn ranging_start<'a, T: Context<'a>>(
- context: &T,
- session_id: u32,
- chip_id: String,
-) -> Result<(), UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- let res =
- match dispatcher.block_on_jni_command(JNICommand::UciStartRange(session_id), chip_id)? {
- UciResponse::RangeStartRsp(data) => data,
- _ => return Err(UwbErr::failed()),
- };
- status_code_to_res(res.get_status())
-}
-
-fn ranging_stop<'a, T: Context<'a>>(
- context: &T,
- session_id: u32,
- chip_id: String,
-) -> Result<(), UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- let res =
- match dispatcher.block_on_jni_command(JNICommand::UciStopRange(session_id), chip_id)? {
- UciResponse::RangeStopRsp(data) => data,
- _ => return Err(UwbErr::failed()),
- };
- status_code_to_res(res.get_status())
-}
-
-fn get_session_state<'a, T: Context<'a>>(
- context: &T,
- session_id: u32,
- chip_id: String,
-) -> Result<jbyte, UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- match dispatcher.block_on_jni_command(JNICommand::UciGetSessionState(session_id), chip_id)? {
- UciResponse::SessionGetStateRsp(data) => Ok(data.get_session_state() as jbyte),
- _ => Err(UwbErr::failed()),
- }
-}
-
-fn set_app_configurations<'a, T: Context<'a>>(
- context: &T,
- session_id: u32,
- no_of_params: u32,
- app_config_param_len: u32,
- app_config_params: jintArray,
- chip_id: String,
-) -> Result<SessionSetAppConfigRspPacket, UwbErr> {
- let app_configs = context.convert_byte_array(app_config_params)?;
- let dispatcher = context.get_dispatcher()?;
- match dispatcher.block_on_jni_command(
- JNICommand::UciSetAppConfig { session_id, no_of_params, app_config_param_len, app_configs },
- chip_id,
- )? {
- UciResponse::SessionSetAppConfigRsp(data) => Ok(data),
- _ => Err(UwbErr::failed()),
- }
-}
-
-fn get_app_configurations<'a, T: Context<'a>>(
- context: &T,
- session_id: u32,
- no_of_params: u32,
- app_config_param_len: u32,
- app_config_params: jintArray,
- chip_id: String,
-) -> Result<SessionGetAppConfigRspPacket, UwbErr> {
- let app_configs = context.convert_byte_array(app_config_params)?;
- let dispatcher = context.get_dispatcher()?;
- match dispatcher.block_on_jni_command(
- JNICommand::UciGetAppConfig { session_id, no_of_params, app_config_param_len, app_configs },
- chip_id,
- )? {
- UciResponse::SessionGetAppConfigRsp(data) => Ok(data),
- _ => Err(UwbErr::failed()),
- }
-}
-
-fn get_caps_info<'a, T: Context<'a>>(
- context: &T,
- chip_id: String,
-) -> Result<GetCapsInfoRspPacket, UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- match dispatcher.block_on_jni_command(JNICommand::UciGetCapsInfo, chip_id)? {
- UciResponse::GetCapsInfoRsp(data) => Ok(data),
- _ => Err(UwbErr::failed()),
- }
-}
-
-fn multicast_list_update<'a, T: Context<'a>>(
- context: &T,
- session_id: u32,
- action: u8,
- no_of_controlee: u8,
- controlee_data: ControleeData,
- chip_id: String,
-) -> Result<(), UwbErr> {
- let mut address_list = vec![0i16; no_of_controlee as usize];
- context.get_short_array_region(controlee_data.addresses, 0, &mut address_list)?;
- let mut sub_session_id_list = vec![0i32; no_of_controlee as usize];
- context.get_int_array_region(controlee_data.sub_session_ids, 0, &mut sub_session_id_list)?;
-
- let sub_session_key_list = match context
- .is_same_object(controlee_data.sub_session_keys.into(), JObject::null())?
- {
- true => vec![0i32; no_of_controlee as usize],
- false => {
- let mut keys =
- vec![
- 0i32;
- context.get_array_length(controlee_data.sub_session_keys)?.try_into().unwrap()
- ];
- context.get_int_array_region(controlee_data.sub_session_keys, 0, &mut keys)?;
- keys
- }
- };
-
- let dispatcher = context.get_dispatcher()?;
- let res = match dispatcher.block_on_jni_command(
- JNICommand::UciSessionUpdateMulticastList {
- session_id,
- action,
- no_of_controlee,
- address_list: address_list.to_vec(),
- sub_session_id_list: sub_session_id_list.to_vec(),
- message_control: controlee_data.message_control,
- sub_session_key_list: split_sub_session_keys(
- sub_session_key_list,
- controlee_data.message_control,
- )?,
- },
- chip_id,
- )? {
- UciResponse::SessionUpdateControllerMulticastListRsp(data) => data,
- _ => return Err(UwbErr::failed()),
- };
- status_code_to_res(res.get_status())
-}
-
-fn split_sub_session_keys(
- sub_session_key_list: Vec<i32>,
- message_control: i32,
-) -> Result<Vec<Vec<u8>>, UwbErr> {
- if (message_control >> 3) & 1 == 0 {
- Ok(Vec::new())
- } else {
- match message_control & 1 {
- 0 => sub_session_key_builder(sub_session_key_list, 4),
- 1 => sub_session_key_builder(sub_session_key_list, 8),
- _ => Err(UwbErr::InvalidArgs),
- }
- }
-}
-
-fn sub_session_key_builder(
- sub_session_key_list: Vec<i32>,
- size: usize,
-) -> Result<Vec<Vec<u8>>, UwbErr> {
- let mut res = Vec::new();
- for chunk in sub_session_key_list.chunks(size) {
- let mut key_in_byte = Vec::new();
- for key in chunk.iter() {
- key_in_byte.extend_from_slice(&key.to_be_bytes());
- }
- res.push(key_in_byte);
- }
- Ok(res)
-}
-
-fn set_country_code<'a, T: Context<'a>>(
- context: &T,
- country_code: jbyteArray,
- chip_id: String,
-) -> Result<(), UwbErr> {
- let code = context.convert_byte_array(country_code)?;
- if code.len() != 2 {
- return Err(UwbErr::failed());
- }
- let dispatcher = context.get_dispatcher()?;
- let res =
- match dispatcher.block_on_jni_command(JNICommand::UciSetCountryCode { code }, chip_id)? {
- UciResponse::AndroidSetCountryCodeRsp(data) => data,
- _ => return Err(UwbErr::failed()),
- };
- status_code_to_res(res.get_status())
-}
-
-fn get_vendor_uci_payload(data: UciResponsePacket) -> Result<Vec<u8>, UwbErr> {
- match data.specialize() {
- UciResponseChild::UciVendor_9_Response(evt) => match evt.specialize() {
- UciVendor_9_ResponseChild::Payload(payload) => Ok(payload.to_vec()),
- UciVendor_9_ResponseChild::None => Ok(Vec::new()),
- },
- UciResponseChild::UciVendor_A_Response(evt) => match evt.specialize() {
- UciVendor_A_ResponseChild::Payload(payload) => Ok(payload.to_vec()),
- UciVendor_A_ResponseChild::None => Ok(Vec::new()),
- },
- UciResponseChild::UciVendor_B_Response(evt) => match evt.specialize() {
- UciVendor_B_ResponseChild::Payload(payload) => Ok(payload.to_vec()),
- UciVendor_B_ResponseChild::None => Ok(Vec::new()),
- },
- UciResponseChild::UciVendor_E_Response(evt) => match evt.specialize() {
- UciVendor_E_ResponseChild::Payload(payload) => Ok(payload.to_vec()),
- UciVendor_E_ResponseChild::None => Ok(Vec::new()),
- },
- UciResponseChild::UciVendor_F_Response(evt) => match evt.specialize() {
- UciVendor_F_ResponseChild::Payload(payload) => Ok(payload.to_vec()),
- UciVendor_F_ResponseChild::None => Ok(Vec::new()),
- },
- _ => {
- error!("Invalid vendor response with gid {:?}", data.get_group_id());
- Err(UwbErr::Specialize(data.to_vec()))
- }
- }
-}
-
-fn send_raw_vendor_cmd<'a, T: Context<'a>>(
- context: &T,
- gid: u32,
- oid: u32,
- payload: jbyteArray,
- chip_id: String,
-) -> Result<(i32, i32, Vec<u8>), UwbErr> {
- let payload = context.convert_byte_array(payload)?;
- let dispatcher = context.get_dispatcher()?;
- match dispatcher
- .block_on_jni_command(JNICommand::UciRawVendorCmd { gid, oid, payload }, chip_id)?
- {
- UciResponse::RawVendorRsp(response) => Ok((
- response.get_group_id().to_i32().unwrap(),
- response.get_opcode().to_i32().unwrap(),
- get_vendor_uci_payload(response)?,
- )),
- _ => Err(UwbErr::failed()),
- }
-}
-
-fn status_code_to_res(status_code: StatusCode) -> Result<(), UwbErr> {
- match status_code {
- StatusCode::UciStatusOk => Ok(()),
- _ => Err(UwbErr::StatusCode(status_code)),
- }
-}
-
-/// create a dispatcher instance
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDispatcherNew(
- env: JNIEnv,
- obj: JObject,
- chip_ids: jobjectArray,
-) -> jlong {
- let eventmanager = match EventManager::new(env, obj) {
- Ok(evtmgr) => evtmgr,
- Err(err) => {
- error!("Fail to create event manager{:?}", err);
- return *JObject::null() as jlong;
- }
- };
-
- let mut chip_ids_vec = Vec::new();
- for n in 0..env.get_array_length(chip_ids).unwrap() {
- let chip_id = env
- .get_string(env.get_object_array_element(chip_ids, n).unwrap().into())
- .unwrap()
- .into();
- chip_ids_vec.push(chip_id);
- }
- match DispatcherImpl::new(eventmanager, chip_ids_vec) {
- Ok(dispatcher) => Box::into_raw(Box::new(dispatcher)) as jlong,
- Err(err) => {
- error!("Fail to create dispatcher {:?}", err);
- *JObject::null() as jlong
- }
- }
-}
-
-/// destroy the dispatcher instance
-#[no_mangle]
-pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDispatcherDestroy(
- env: JNIEnv,
- obj: JObject,
-) {
- let dispatcher_ptr_value = match env.get_field(obj, "mDispatcherPointer", "J") {
- Ok(value) => value,
- Err(err) => {
- error!("Failed to get the pointer with: {:?}", err);
- return;
- }
- };
- let dispatcher_ptr = match dispatcher_ptr_value.j() {
- Ok(value) => value,
- Err(err) => {
- error!("Failed to get the pointer with: {:?}", err);
- return;
- }
- };
- // Safety: dispatcher pointer must not be a null pointer and must point to a valid dispatcher object.
- // This can be ensured because the dispatcher is created in an earlier stage and
- // won't be deleted before calling this destroy function.
- // This function will early return if the instance is already destroyed.
- let _boxed_dispatcher = unsafe { Box::from_raw(dispatcher_ptr as *mut DispatcherImpl) };
- info!("The dispatcher successfully destroyed.");
-}
-
-fn get_power_stats<'a, T: Context<'a>>(
- context: &T,
- chip_id: String,
-) -> Result<[JValue<'a>; 4], UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- match dispatcher.block_on_jni_command(JNICommand::UciGetPowerStats, chip_id)? {
- UciResponse::AndroidGetPowerStatsRsp(data) => Ok([
- JValue::Int(data.get_stats().idle_time_ms as i32),
- JValue::Int(data.get_stats().tx_time_ms as i32),
- JValue::Int(data.get_stats().rx_time_ms as i32),
- JValue::Int(data.get_stats().total_wake_count as i32),
- ]),
- _ => Err(UwbErr::failed()),
- }
-}
-
-fn uwa_get_device_info(
- dispatcher: &dyn Dispatcher,
- chip_id: String,
-) -> Result<UciResponse, UwbErr> {
- let res = dispatcher.block_on_jni_command(JNICommand::UciGetDeviceInfo, chip_id)?;
- Ok(res)
-}
-
-fn reset_device<'a, T: Context<'a>>(
- context: &T,
- reset_config: u8,
- chip_id: String,
-) -> Result<(), UwbErr> {
- let dispatcher = context.get_dispatcher()?;
- let res = match dispatcher
- .block_on_jni_command(JNICommand::UciDeviceReset { reset_config }, chip_id)?
- {
- UciResponse::DeviceResetRsp(data) => data,
- _ => return Err(UwbErr::failed()),
- };
- status_code_to_res(res.get_status())
-}
-
-#[cfg(test)]
-mod mock_context;
-#[cfg(test)]
-mod mock_dispatcher;
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- use crate::mock_context::MockContext;
- use crate::mock_dispatcher::MockDispatcher;
-
- #[test]
- fn test_boolean_result_helper() {
- assert_eq!(true as jboolean, boolean_result_helper(Ok(()), "Foo"));
- assert_eq!(false as jboolean, boolean_result_helper(Err(UwbErr::Undefined), "Foo"));
- }
-
- #[test]
- fn test_byte_result_helper() {
- assert_eq!(StatusCode::UciStatusOk.to_i8().unwrap(), byte_result_helper(Ok(()), "Foo"));
- assert_eq!(
- StatusCode::UciStatusFailed.to_i8().unwrap(),
- byte_result_helper(Err(UwbErr::Undefined), "Foo")
- );
- assert_eq!(
- StatusCode::UciStatusRejected.to_i8().unwrap(),
- byte_result_helper(Err(UwbErr::StatusCode(StatusCode::UciStatusRejected)), "Foo")
- );
- }
-
- #[test]
- fn test_do_initialize() {
- let chip_id = String::from("chip_id");
- let packet = uwb_uci_packets::GetDeviceInfoRspBuilder {
- status: StatusCode::UciStatusOk,
- uci_version: 0,
- mac_version: 0,
- phy_version: 0,
- uci_test_version: 0,
- vendor_spec_info: vec![],
- }
- .build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher
- .expect_block_on_jni_command(JNICommand::Enable, Ok(UciResponse::EnableRsp(true)));
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciGetDeviceInfo,
- Ok(UciResponse::GetDeviceInfoRsp(packet.clone())),
- );
- let mut context = MockContext::new(dispatcher);
-
- let result = do_initialize(&context, chip_id);
- let device_info = context.get_mock_dispatcher().get_device_info().clone();
- assert!(result.is_ok());
- assert_eq!(device_info.unwrap().to_vec(), packet.to_vec());
- }
-
- #[test]
- fn test_do_deinitialize() {
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_send_jni_command(JNICommand::Disable(true), Ok(()));
- dispatcher.expect_wait_for_exit(Ok(()));
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let result = do_deinitialize(&context, chip_id);
- assert!(result.is_ok());
- }
-
- #[test]
- fn test_get_specification_info() {
- let packet = uwb_uci_packets::GetDeviceInfoRspBuilder {
- status: StatusCode::UciStatusOk,
- uci_version: 0x1234,
- mac_version: 0x5678,
- phy_version: 0x9ABC,
- uci_test_version: 0x1357,
- vendor_spec_info: vec![],
- }
- .build();
- let expected_array = [
- 0x34, 0x2, 0x1, // uci_version
- 0x78, 0x6, 0x5, // mac_version.
- 0xBC, 0xA, 0x9, // phy_version.
- 0x57, 0x3, 0x1, // uci_test_version.
- 1, // fira_major_version
- 0, // fira_minor_version
- 1, // ccc_major_version
- 0, // ccc_minor_version
- ];
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.set_device_info(Some(packet));
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let results = get_specification_info(&context, chip_id).unwrap();
- for (idx, result) in results.iter().enumerate() {
- assert_eq!(TryInto::<jint>::try_into(*result).unwrap(), expected_array[idx]);
- }
- }
-
- #[test]
- fn test_session_init() {
- let session_id = 1234;
- let session_type = 5;
- let packet =
- uwb_uci_packets::SessionInitRspBuilder { status: StatusCode::UciStatusOk }.build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciSessionInit(session_id, session_type),
- Ok(UciResponse::SessionInitRsp(packet)),
- );
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let result = session_init(&context, session_id, session_type, chip_id);
- assert!(result.is_ok());
- }
-
- #[test]
- fn test_session_deinit() {
- let session_id = 1234;
- let packet =
- uwb_uci_packets::SessionDeinitRspBuilder { status: StatusCode::UciStatusOk }.build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciSessionDeinit(session_id),
- Ok(UciResponse::SessionDeinitRsp(packet)),
- );
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let result = session_deinit(&context, session_id, chip_id);
- assert!(result.is_ok());
- }
-
- #[test]
- fn test_get_session_count() {
- let session_count = 7;
- let packet = uwb_uci_packets::SessionGetCountRspBuilder {
- status: StatusCode::UciStatusOk,
- session_count,
- }
- .build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciSessionGetCount,
- Ok(UciResponse::SessionGetCountRsp(packet)),
- );
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let result = get_session_count(&context, chip_id).unwrap();
- assert_eq!(result, session_count as jbyte);
- }
-
- #[test]
- fn test_ranging_start() {
- let session_id = 1234;
- let packet =
- uwb_uci_packets::RangeStartRspBuilder { status: StatusCode::UciStatusOk }.build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciStartRange(session_id),
- Ok(UciResponse::RangeStartRsp(packet)),
- );
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let result = ranging_start(&context, session_id, chip_id);
- assert!(result.is_ok());
- }
-
- #[test]
- fn test_ranging_stop() {
- let session_id = 1234;
- let packet =
- uwb_uci_packets::RangeStopRspBuilder { status: StatusCode::UciStatusOk }.build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciStopRange(session_id),
- Ok(UciResponse::RangeStopRsp(packet)),
- );
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let result = ranging_stop(&context, session_id, chip_id);
- assert!(result.is_ok());
- }
-
- #[test]
- fn test_get_session_state() {
- let session_id = 1234;
- let session_state = uwb_uci_packets::SessionState::SessionStateActive;
- let packet = uwb_uci_packets::SessionGetStateRspBuilder {
- status: StatusCode::UciStatusOk,
- session_state,
- }
- .build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciGetSessionState(session_id),
- Ok(UciResponse::SessionGetStateRsp(packet)),
- );
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let result = get_session_state(&context, session_id, chip_id).unwrap();
- assert_eq!(result, session_state as jbyte);
- }
-
- #[test]
- fn test_set_app_configurations() {
- let session_id = 1234;
- let no_of_params = 3;
- let app_config_param_len = 5;
- let app_configs = vec![1, 2, 3, 4, 5];
- let fake_app_config_params = std::ptr::null_mut();
- let packet = uwb_uci_packets::SessionSetAppConfigRspBuilder {
- status: StatusCode::UciStatusOk,
- cfg_status: vec![],
- }
- .build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciSetAppConfig {
- session_id,
- no_of_params,
- app_config_param_len,
- app_configs: app_configs.clone(),
- },
- Ok(UciResponse::SessionSetAppConfigRsp(packet.clone())),
- );
- let mut context = MockContext::new(dispatcher);
- context.expect_convert_byte_array(fake_app_config_params, Ok(app_configs));
- let chip_id = String::from("chip_id");
-
- let result = set_app_configurations(
- &context,
- session_id,
- no_of_params,
- app_config_param_len,
- fake_app_config_params,
- chip_id,
- )
- .unwrap();
- assert_eq!(result.to_vec(), packet.to_vec());
- }
-
- #[test]
- fn test_get_app_configurations() {
- let session_id = 1234;
- let no_of_params = 3;
- let app_config_param_len = 5;
- let app_configs = vec![1, 2, 3, 4, 5];
- let fake_app_config_params = std::ptr::null_mut();
- let packet = uwb_uci_packets::SessionGetAppConfigRspBuilder {
- status: StatusCode::UciStatusOk,
- tlvs: vec![],
- }
- .build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciGetAppConfig {
- session_id,
- no_of_params,
- app_config_param_len,
- app_configs: app_configs.clone(),
- },
- Ok(UciResponse::SessionGetAppConfigRsp(packet.clone())),
- );
- let mut context = MockContext::new(dispatcher);
- context.expect_convert_byte_array(fake_app_config_params, Ok(app_configs));
- let chip_id = String::from("chip_id");
-
- let result = get_app_configurations(
- &context,
- session_id,
- no_of_params,
- app_config_param_len,
- fake_app_config_params,
- chip_id,
- )
- .unwrap();
- assert_eq!(result.to_vec(), packet.to_vec());
- }
-
- #[test]
- fn test_get_caps_info() {
- let packet = uwb_uci_packets::GetCapsInfoRspBuilder {
- status: StatusCode::UciStatusOk,
- tlvs: vec![],
- }
- .build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciGetCapsInfo,
- Ok(UciResponse::GetCapsInfoRsp(packet.clone())),
- );
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let result = get_caps_info(&context, chip_id).unwrap();
- assert_eq!(result.to_vec(), packet.to_vec());
- }
-
- #[test]
- fn test_multicast_list_update() {
- let session_id = 1234;
- let action = 3;
- let no_of_controlee = 5;
- let fake_addresses = std::ptr::null_mut();
- let address_list = Box::new([1, 3, 5, 7, 9]);
- let fake_sub_session_ids = std::ptr::null_mut();
- let sub_session_id_list = Box::new([2, 4, 6, 8, 10]);
- let message_control = 8;
- let fake_sub_session_key_list = std::ptr::null_mut();
- let sub_session_key_list = Box::new([1, 2, 3, 4]);
- let split_sub_session_keys =
- split_sub_session_keys(sub_session_key_list.to_vec(), message_control).unwrap();
- let packet = uwb_uci_packets::SessionUpdateControllerMulticastListRspBuilder {
- status: StatusCode::UciStatusOk,
- }
- .build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciSessionUpdateMulticastList {
- session_id,
- action,
- no_of_controlee,
- address_list: address_list.to_vec(),
- sub_session_id_list: sub_session_id_list.to_vec(),
- message_control,
- sub_session_key_list: split_sub_session_keys,
- },
- Ok(UciResponse::SessionUpdateControllerMulticastListRsp(packet)),
- );
- let mut context = MockContext::new(dispatcher);
- context.expect_get_short_array_region(fake_addresses, 0, Ok(address_list));
- context.expect_get_int_array_region(fake_sub_session_ids, 0, Ok(sub_session_id_list));
- context.expect_get_array_length(
- fake_sub_session_key_list,
- Ok(sub_session_key_list.len() as jsize),
- );
- context.expect_get_int_array_region(fake_sub_session_key_list, 0, Ok(sub_session_key_list));
- let chip_id = String::from("chip_id");
-
- let result = multicast_list_update(
- &context,
- session_id,
- action,
- no_of_controlee,
- ControleeData {
- addresses: fake_addresses,
- sub_session_ids: fake_sub_session_ids,
- message_control,
- sub_session_keys: fake_sub_session_key_list,
- },
- chip_id,
- );
- assert!(result.is_ok());
- }
-
- #[test]
- fn test_set_country_code() {
- let fake_country_code = std::ptr::null_mut();
- let country_code = "US".as_bytes().to_vec();
- let packet =
- uwb_uci_packets::AndroidSetCountryCodeRspBuilder { status: StatusCode::UciStatusOk }
- .build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciSetCountryCode { code: country_code.clone() },
- Ok(UciResponse::AndroidSetCountryCodeRsp(packet)),
- );
- let mut context = MockContext::new(dispatcher);
- context.expect_convert_byte_array(fake_country_code, Ok(country_code));
- let chip_id = String::from("chip_id");
-
- let result = set_country_code(&context, fake_country_code, chip_id);
- assert!(result.is_ok());
- }
-
- #[test]
- fn test_send_raw_vendor_cmd() {
- let gid = 2;
- let oid = 4;
- let opcode = 6;
- let fake_payload = std::ptr::null_mut();
- let payload = vec![1, 2, 4, 8];
- let response = vec![3, 6, 9];
- let packet = uwb_uci_packets::UciVendor_9_ResponseBuilder {
- opcode,
- payload: Some(response.clone().into()),
- }
- .build()
- .into();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciRawVendorCmd { gid, oid, payload: payload.clone() },
- Ok(UciResponse::RawVendorRsp(packet)),
- );
- let mut context = MockContext::new(dispatcher);
- context.expect_convert_byte_array(fake_payload, Ok(payload));
- let chip_id = String::from("chip_id");
-
- let result = send_raw_vendor_cmd(&context, gid, oid, fake_payload, chip_id).unwrap();
- assert_eq!(result.0, uwb_uci_packets::GroupId::VendorReserved9 as i32);
- assert_eq!(result.1, opcode as i32);
- assert_eq!(result.2, response);
- }
-
- #[test]
- fn test_get_power_stats() {
- let idle_time_ms = 5;
- let tx_time_ms = 4;
- let rx_time_ms = 3;
- let total_wake_count = 2;
- let packet = uwb_uci_packets::AndroidGetPowerStatsRspBuilder {
- stats: uwb_uci_packets::PowerStats {
- status: StatusCode::UciStatusOk,
- idle_time_ms,
- tx_time_ms,
- rx_time_ms,
- total_wake_count,
- },
- }
- .build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciGetPowerStats,
- Ok(UciResponse::AndroidGetPowerStatsRsp(packet)),
- );
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let result = get_power_stats(&context, chip_id).unwrap();
- assert_eq!(TryInto::<jint>::try_into(result[0]).unwrap(), idle_time_ms as jint);
- assert_eq!(TryInto::<jint>::try_into(result[1]).unwrap(), tx_time_ms as jint);
- assert_eq!(TryInto::<jint>::try_into(result[2]).unwrap(), rx_time_ms as jint);
- assert_eq!(TryInto::<jint>::try_into(result[3]).unwrap(), total_wake_count as jint);
- }
-
- #[test]
- fn test_reset_device() {
- let reset_config = uwb_uci_packets::ResetConfig::UwbsReset as u8;
- let packet =
- uwb_uci_packets::DeviceResetRspBuilder { status: StatusCode::UciStatusOk }.build();
-
- let mut dispatcher = MockDispatcher::new();
- dispatcher.expect_block_on_jni_command(
- JNICommand::UciDeviceReset { reset_config },
- Ok(UciResponse::DeviceResetRsp(packet)),
- );
- let context = MockContext::new(dispatcher);
- let chip_id = String::from("chip_id");
-
- let result = reset_device(&context, reset_config, chip_id);
- assert!(result.is_ok());
- }
-
- #[test]
- fn test_split_sub_session_keys() {
- let sub_session_key_list = vec![1, 2, 3, 4, 5, 6, 7, 8];
- let message_control = 8;
- let expected_res = vec![
- vec![0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x4],
- vec![0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x8],
- ];
- let byte_array = split_sub_session_keys(sub_session_key_list, message_control).unwrap();
- assert_eq!(byte_array, expected_res);
- }
-}
diff --git a/service/uci/jni/rust/mock_context.rs b/service/uci/jni/rust/mock_context.rs
deleted file mode 100644
index 403070d7..00000000
--- a/service/uci/jni/rust/mock_context.rs
+++ /dev/null
@@ -1,192 +0,0 @@
-use std::cell::{Cell, RefCell};
-use std::collections::VecDeque;
-
-use jni::objects::JObject;
-use jni::sys::{jarray, jbyteArray, jint, jintArray, jshort, jshortArray, jsize};
-use uwb_uci_rust::error::UwbErr;
-use uwb_uci_rust::uci::Dispatcher;
-
-use crate::mock_dispatcher::MockDispatcher;
-use crate::Context;
-
-#[cfg(test)]
-pub struct MockContext {
- dispatcher: Cell<MockDispatcher>,
- expected_calls: RefCell<VecDeque<ExpectedCall>>,
-}
-
-#[cfg(test)]
-impl MockContext {
- pub fn new(dispatcher: MockDispatcher) -> Self {
- Self { dispatcher: Cell::new(dispatcher), expected_calls: Default::default() }
- }
-
- pub fn get_mock_dispatcher(&mut self) -> &mut MockDispatcher {
- self.dispatcher.get_mut()
- }
-
- pub fn expect_convert_byte_array(
- &mut self,
- expected_array: jbyteArray,
- out: Result<Vec<u8>, jni::errors::Error>,
- ) {
- self.expected_calls
- .borrow_mut()
- .push_back(ExpectedCall::ConvertByteArray { expected_array, out });
- }
-
- pub fn expect_get_array_length(
- &mut self,
- expected_array: jarray,
- out: Result<jsize, jni::errors::Error>,
- ) {
- self.expected_calls
- .borrow_mut()
- .push_back(ExpectedCall::GetArrayLength { expected_array, out });
- }
-
- pub fn expect_get_short_array_region(
- &mut self,
- expected_array: jshortArray,
- expected_start: jsize,
- out: Result<Box<[jshort]>, jni::errors::Error>,
- ) {
- self.expected_calls.borrow_mut().push_back(ExpectedCall::GetShortArrayRegion {
- expected_array,
- expected_start,
- out,
- });
- }
-
- pub fn expect_get_int_array_region(
- &mut self,
- expected_array: jintArray,
- expected_start: jsize,
- out: Result<Box<[jint]>, jni::errors::Error>,
- ) {
- self.expected_calls.borrow_mut().push_back(ExpectedCall::GetIntArrayRegion {
- expected_array,
- expected_start,
- out,
- });
- }
-}
-
-#[cfg(test)]
-impl<'a> Context<'a> for MockContext {
- fn convert_byte_array(&self, array: jbyteArray) -> Result<Vec<u8>, jni::errors::Error> {
- let mut expected_calls = self.expected_calls.borrow_mut();
- match expected_calls.pop_front() {
- Some(ExpectedCall::ConvertByteArray { expected_array, out })
- if array == expected_array =>
- {
- out
- }
- Some(call) => {
- expected_calls.push_front(call);
- Err(jni::errors::Error::JniCall(jni::errors::JniError::Unknown))
- }
- None => Err(jni::errors::Error::JniCall(jni::errors::JniError::Unknown)),
- }
- }
-
- fn get_array_length(&self, array: jarray) -> Result<jsize, jni::errors::Error> {
- let mut expected_calls = self.expected_calls.borrow_mut();
- match expected_calls.pop_front() {
- Some(ExpectedCall::GetArrayLength { expected_array, out })
- if array == expected_array =>
- {
- out
- }
- Some(call) => {
- expected_calls.push_front(call);
- Err(jni::errors::Error::JniCall(jni::errors::JniError::Unknown))
- }
- None => Err(jni::errors::Error::JniCall(jni::errors::JniError::Unknown)),
- }
- }
-
- fn get_short_array_region(
- &self,
- array: jshortArray,
- start: jsize,
- buf: &mut [jshort],
- ) -> Result<(), jni::errors::Error> {
- let mut expected_calls = self.expected_calls.borrow_mut();
- match expected_calls.pop_front() {
- Some(ExpectedCall::GetShortArrayRegion { expected_array, expected_start, out })
- if array == expected_array && start == expected_start =>
- {
- match out {
- Ok(expected_buf) => {
- buf.clone_from_slice(&expected_buf);
- Ok(())
- }
- Err(err) => Err(err),
- }
- }
- Some(call) => {
- expected_calls.push_front(call);
- Err(jni::errors::Error::JniCall(jni::errors::JniError::Unknown))
- }
- None => Err(jni::errors::Error::JniCall(jni::errors::JniError::Unknown)),
- }
- }
-
- fn get_int_array_region(
- &self,
- array: jintArray,
- start: jsize,
- buf: &mut [jint],
- ) -> Result<(), jni::errors::Error> {
- let mut expected_calls = self.expected_calls.borrow_mut();
- match expected_calls.pop_front() {
- Some(ExpectedCall::GetIntArrayRegion { expected_array, expected_start, out })
- if array == expected_array && start == expected_start =>
- {
- match out {
- Ok(expected_buf) => {
- buf.clone_from_slice(&expected_buf);
- Ok(())
- }
- Err(err) => Err(err),
- }
- }
- Some(call) => {
- expected_calls.push_front(call);
- Err(jni::errors::Error::JniCall(jni::errors::JniError::Unknown))
- }
- None => Err(jni::errors::Error::JniCall(jni::errors::JniError::Unknown)),
- }
- }
-
- fn is_same_object(&self, _obj1: JObject, _obj2: JObject) -> Result<bool, jni::errors::Error> {
- Ok(false)
- }
-
- fn get_dispatcher(&self) -> Result<&'a mut dyn Dispatcher, UwbErr> {
- unsafe { Ok(&mut *(self.dispatcher.as_ptr())) }
- }
-}
-
-#[cfg(test)]
-enum ExpectedCall {
- ConvertByteArray {
- expected_array: jbyteArray,
- out: Result<Vec<u8>, jni::errors::Error>,
- },
- GetArrayLength {
- expected_array: jarray,
- out: Result<jsize, jni::errors::Error>,
- },
- GetShortArrayRegion {
- expected_array: jshortArray,
- expected_start: jsize,
- out: Result<Box<[jshort]>, jni::errors::Error>,
- },
- GetIntArrayRegion {
- expected_array: jintArray,
- expected_start: jsize,
- out: Result<Box<[jint]>, jni::errors::Error>,
- },
-}
diff --git a/service/uci/jni/rust/mock_dispatcher.rs b/service/uci/jni/rust/mock_dispatcher.rs
deleted file mode 100644
index 3827be91..00000000
--- a/service/uci/jni/rust/mock_dispatcher.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-use std::cell::RefCell;
-use std::collections::VecDeque;
-
-use uwb_uci_packets::GetDeviceInfoRspPacket;
-use uwb_uci_rust::error::UwbErr;
-use uwb_uci_rust::uci::{uci_hrcv::UciResponse, Dispatcher, JNICommand, Result};
-
-#[cfg(test)]
-#[derive(Default)]
-pub struct MockDispatcher {
- expected_calls: RefCell<VecDeque<ExpectedCall>>,
- device_info: Option<GetDeviceInfoRspPacket>,
-}
-
-#[cfg(test)]
-impl MockDispatcher {
- pub fn new() -> Self {
- Default::default()
- }
-
- pub fn expect_send_jni_command(&mut self, expected_cmd: JNICommand, out: Result<()>) {
- self.expected_calls
- .borrow_mut()
- .push_back(ExpectedCall::SendJniCommand { expected_cmd, out })
- }
-
- pub fn expect_block_on_jni_command(
- &mut self,
- expected_cmd: JNICommand,
- out: Result<UciResponse>,
- ) {
- self.expected_calls
- .borrow_mut()
- .push_back(ExpectedCall::BlockOnJniCommand { expected_cmd, out })
- }
-
- pub fn expect_wait_for_exit(&mut self, out: Result<()>) {
- self.expected_calls.borrow_mut().push_back(ExpectedCall::WaitForExit { out })
- }
-}
-
-#[cfg(test)]
-impl Drop for MockDispatcher {
- fn drop(&mut self) {
- assert!(self.expected_calls.borrow().is_empty());
- }
-}
-
-#[cfg(test)]
-impl Dispatcher for MockDispatcher {
- fn send_jni_command(&self, cmd: JNICommand, _chip_id: String) -> Result<()> {
- let mut expected_calls = self.expected_calls.borrow_mut();
- match expected_calls.pop_front() {
- Some(ExpectedCall::SendJniCommand { expected_cmd, out }) if cmd == expected_cmd => out,
- Some(call) => {
- expected_calls.push_front(call);
- Err(UwbErr::Undefined)
- }
- None => Err(UwbErr::Undefined),
- }
- }
- fn block_on_jni_command(&self, cmd: JNICommand, _chip_id: String) -> Result<UciResponse> {
- let mut expected_calls = self.expected_calls.borrow_mut();
- match expected_calls.pop_front() {
- Some(ExpectedCall::BlockOnJniCommand { expected_cmd, out }) if cmd == expected_cmd => {
- out
- }
- Some(call) => {
- expected_calls.push_front(call);
- Err(UwbErr::Undefined)
- }
- None => Err(UwbErr::Undefined),
- }
- }
- fn wait_for_exit(&mut self) -> Result<()> {
- let mut expected_calls = self.expected_calls.borrow_mut();
- match expected_calls.pop_front() {
- Some(ExpectedCall::WaitForExit { out }) => out,
- Some(call) => {
- expected_calls.push_front(call);
- Err(UwbErr::Undefined)
- }
- None => Err(UwbErr::Undefined),
- }
- }
-
- fn set_device_info(&mut self, device_info: Option<GetDeviceInfoRspPacket>) {
- self.device_info = device_info;
- }
-
- fn get_device_info(&self) -> &Option<GetDeviceInfoRspPacket> {
- &self.device_info
- }
-}
-
-#[cfg(test)]
-enum ExpectedCall {
- SendJniCommand { expected_cmd: JNICommand, out: Result<()> },
- BlockOnJniCommand { expected_cmd: JNICommand, out: Result<UciResponse> },
- WaitForExit { out: Result<()> },
-}
diff --git a/service/uci/jni_new/src/dispatcher.rs b/service/uci/jni/src/dispatcher.rs
index 2bbea59a..bde146e0 100644
--- a/service/uci/jni_new/src/dispatcher.rs
+++ b/service/uci/jni/src/dispatcher.rs
@@ -12,15 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Implementation of Dispatcher.
+//! Implementation of Dispatcher and related methods.
+use crate::error::{Error, Result};
use crate::notification_manager_android::NotificationManagerAndroidBuilder;
use std::collections::HashMap;
+use std::ops::Deref;
use std::sync::Arc;
-use jni::objects::GlobalRef;
-use jni::JavaVM;
+use jni::objects::{GlobalRef, JObject, JString};
+use jni::{JNIEnv, JavaVM, MonitorGuard};
use tokio::runtime::{Builder as RuntimeBuilder, Runtime};
use uci_hal_android::uci_hal_android::UciHalAndroid;
use uwb_core::error::{Error as UwbCoreError, Result as UwbCoreResult};
@@ -76,8 +78,8 @@ impl Dispatcher {
}
/// Sets log mode for all chips.
- pub fn set_logger_mode(&mut self, logger_mode: UciLoggerMode) -> UwbCoreResult<()> {
- for (_, manager) in self.manager_map.iter_mut() {
+ pub fn set_logger_mode(&self, logger_mode: UciLoggerMode) -> UwbCoreResult<()> {
+ for (_, manager) in self.manager_map.iter() {
manager.set_logger_mode(logger_mode.clone())?;
}
Ok(())
@@ -101,4 +103,73 @@ impl Dispatcher {
pub unsafe fn destroy_ptr(dispatcher_ptr: *mut Dispatcher) {
let _ = Box::from_raw(dispatcher_ptr);
}
+
+ /// Gets reference to Dispatcher.
+ ///
+ /// # Safety
+ /// Must be called from a Java object holding a valid or null mDispatcherPointer.
+ pub unsafe fn get_dispatcher<'a>(
+ env: JNIEnv<'a>,
+ obj: JObject<'a>,
+ ) -> Result<GuardedDispatcher<'a>> {
+ let guard = env.lock_obj(obj)?;
+ let dispatcher_ptr_value = env.get_field(obj, "mDispatcherPointer", "J")?.j()?;
+ if dispatcher_ptr_value == 0 {
+ return Err(Error::UwbCoreError(UwbCoreError::BadParameters));
+ }
+ let dispatcher_ptr = dispatcher_ptr_value as *const Dispatcher;
+ Ok(GuardedDispatcher { _guard: guard, dispatcher: &*dispatcher_ptr })
+ }
+
+ /// Gets reference to UciManagerSync with chip_id.
+ ///
+ /// # Safety
+ /// Must be called from a Java object holding a valid or null mDispatcherPointer.
+ pub unsafe fn get_uci_manager<'a>(
+ env: JNIEnv<'a>,
+ obj: JObject<'a>,
+ chip_id: JString,
+ ) -> Result<GuardedUciManager<'a>> {
+ // Safety: get_dispatcher and get_uci_manager has the same assumption.
+ let guarded_dispatcher = Self::get_dispatcher(env, obj)?;
+ let chip_id_str = String::from(env.get_string(chip_id)?);
+ guarded_dispatcher.into_guarded_uci_manager(&chip_id_str)
+ }
+}
+
+/// Lifetimed reference to UciManagerSync that locks Java object while reference is alive.
+pub(crate) struct GuardedUciManager<'a> {
+ _guard: MonitorGuard<'a>,
+ uci_manager: &'a UciManagerSync,
+}
+
+impl<'a> Deref for GuardedUciManager<'a> {
+ type Target = UciManagerSync;
+ fn deref(&self) -> &Self::Target {
+ self.uci_manager
+ }
+}
+
+/// Lifetimed reference to Dispatcher that locks Java object while reference is alive.
+pub(crate) struct GuardedDispatcher<'a> {
+ _guard: MonitorGuard<'a>,
+ dispatcher: &'a Dispatcher,
+}
+
+impl<'a> GuardedDispatcher<'a> {
+ pub fn into_guarded_uci_manager(self, chip_id: &str) -> Result<GuardedUciManager<'a>> {
+ let uci_manager = self
+ .dispatcher
+ .manager_map
+ .get(chip_id)
+ .ok_or(Error::UwbCoreError(UwbCoreError::BadParameters))?;
+ Ok(GuardedUciManager { _guard: self._guard, uci_manager })
+ }
+}
+
+impl<'a> Deref for GuardedDispatcher<'a> {
+ type Target = Dispatcher;
+ fn deref(&self) -> &Self::Target {
+ self.dispatcher
+ }
}
diff --git a/service/uci/jni_new/src/error.rs b/service/uci/jni/src/error.rs
index cca2b56f..cca2b56f 100644
--- a/service/uci/jni_new/src/error.rs
+++ b/service/uci/jni/src/error.rs
diff --git a/service/uci/jni_new/src/helper.rs b/service/uci/jni/src/helper.rs
index 986eff92..986eff92 100644
--- a/service/uci/jni_new/src/helper.rs
+++ b/service/uci/jni/src/helper.rs
diff --git a/service/uci/jni_new/src/jclass_name.rs b/service/uci/jni/src/jclass_name.rs
index c3bbd5fd..ef75d53d 100644
--- a/service/uci/jni_new/src/jclass_name.rs
+++ b/service/uci/jni/src/jclass_name.rs
@@ -22,3 +22,5 @@ pub(crate) const UWB_RANGING_DATA_CLASS: &str = "com/android/server/uwb/data/Uwb
pub(crate) const UWB_TWO_WAY_MEASUREMENT_CLASS: &str =
"com/android/server/uwb/data/UwbTwoWayMeasurement";
pub(crate) const VENDOR_RESPONSE_CLASS: &str = "com/android/server/uwb/data/UwbVendorUciResponse";
+pub(crate) const DT_RANGING_ROUNDS_STATUS_CLASS: &str =
+ "com/android/server/uwb/data/DtTagUpdateRangingRoundsStatus";
diff --git a/service/uci/jni_new/src/lib.rs b/service/uci/jni/src/lib.rs
index 3a9cab2f..3a9cab2f 100644
--- a/service/uci/jni_new/src/lib.rs
+++ b/service/uci/jni/src/lib.rs
diff --git a/service/uci/jni_new/src/notification_manager_android.rs b/service/uci/jni/src/notification_manager_android.rs
index 8af4a51f..945419e8 100644
--- a/service/uci/jni_new/src/notification_manager_android.rs
+++ b/service/uci/jni/src/notification_manager_android.rs
@@ -494,7 +494,7 @@ impl NotificationManager for NotificationManagerAndroid {
fn on_vendor_notification(
&mut self,
- vendor_notification: uwb_core::params::RawVendorMessage,
+ vendor_notification: uwb_core::params::RawUciMessage,
) -> UwbCoreResult<()> {
debug!("UCI JNI: vendor notification callback.");
let payload_jbytearray = self
diff --git a/service/uci/jni_new/src/uci_jni_android_new.rs b/service/uci/jni/src/uci_jni_android_new.rs
index 251a25fb..4d5c5ac8 100644
--- a/service/uci/jni_new/src/uci_jni_android_new.rs
+++ b/service/uci/jni/src/uci_jni_android_new.rs
@@ -18,8 +18,8 @@ use crate::dispatcher::Dispatcher;
use crate::error::{Error, Result};
use crate::helper::{boolean_result_helper, byte_result_helper, option_result_helper};
use crate::jclass_name::{
- CONFIG_STATUS_DATA_CLASS, POWER_STATS_CLASS, TLV_DATA_CLASS, UWB_RANGING_DATA_CLASS,
- VENDOR_RESPONSE_CLASS,
+ CONFIG_STATUS_DATA_CLASS, DT_RANGING_ROUNDS_STATUS_CLASS, POWER_STATS_CLASS, TLV_DATA_CLASS,
+ UWB_RANGING_DATA_CLASS, VENDOR_RESPONSE_CLASS,
};
use crate::unique_jvm;
@@ -37,9 +37,9 @@ use log::{debug, error};
use num_traits::cast::FromPrimitive;
use uwb_core::error::Error as UwbCoreError;
use uwb_core::params::{
- AppConfigTlv, CountryCode, RawAppConfigTlv, RawVendorMessage, SetAppConfigResponse,
+ AppConfigTlv, CountryCode, RawAppConfigTlv, RawUciMessage,
+ SessionUpdateActiveRoundsDtTagResponse, SetAppConfigResponse,
};
-use uwb_core::uci::uci_manager_sync::UciManagerSync;
use uwb_uci_packets::{
AppConfigTlvType, CapTlv, Controlee, PowerStats, ResetConfig, SessionState, SessionType,
StatusCode, UpdateMulticastListAction,
@@ -98,39 +98,6 @@ pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGe
5
}
-/// get mutable reference to Dispatcher.
-///
-/// # Safety
-/// Must be called from a Java object holding a valid or null mDispatcherPointer, and remains valid
-/// until env and obj goes out of scope.
-unsafe fn get_dispatcher<'a>(env: JNIEnv<'a>, obj: JObject<'a>) -> Result<&'a mut Dispatcher> {
- let dispatcher_ptr_value = env.get_field(obj, "mDispatcherPointer", "J")?.j()?;
- if dispatcher_ptr_value == 0 {
- return Err(Error::UwbCoreError(UwbCoreError::BadParameters));
- }
- let dispatcher_ptr = dispatcher_ptr_value as *mut Dispatcher;
- Ok(&mut *dispatcher_ptr)
-}
-
-/// get mutable reference to UciManagerSync with chip_id.
-///
-/// # Safety
-/// Must be called from a Java object holding a valid or null mDispatcherPointer, and remains valid
-/// until env and obj goes out of scope.
-unsafe fn get_uci_manager<'a>(
- env: JNIEnv<'a>,
- obj: JObject<'a>,
- chip_id: JString,
-) -> Result<&'a mut UciManagerSync> {
- // Safety: get_dispatcher and get_uci_manager has the same assumption.
- let dispatcher_ref = get_dispatcher(env, obj)?;
- let chip_id_str = String::from(env.get_string(chip_id)?);
- match dispatcher_ref.manager_map.get_mut(&chip_id_str) {
- Some(m) => Ok(m),
- None => Err(Error::UwbCoreError(UwbCoreError::BadParameters)),
- }
-}
-
/// Turn on Single UWB chip.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDoInitialize(
@@ -145,7 +112,7 @@ pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDo
fn native_do_initialize(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.open_hal().map_err(|e| e.into())
}
@@ -163,7 +130,7 @@ pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDo
fn native_do_deinitialize(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.close_hal(true).map_err(|e| e.into())
}
@@ -193,7 +160,7 @@ pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDe
fn native_device_reset(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.device_reset(ResetConfig::UwbsReset).map_err(|e| e.into())
}
@@ -224,7 +191,7 @@ fn native_session_init(
SessionType::from_u8(session_type as u8).ok_or(UwbCoreError::BadParameters)?;
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.session_init(session_id as u32, session_type).map_err(|e| e.into())
}
@@ -248,7 +215,7 @@ fn native_session_deinit(
) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.session_deinit(session_id as u32).map_err(|e| e.into())
}
@@ -270,7 +237,7 @@ pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGe
fn native_get_session_count(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<u8> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.session_get_count().map_err(|e| e.into())
}
@@ -294,7 +261,7 @@ fn native_ranging_start(
) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.range_start(session_id as u32).map_err(|e| e.into())
}
@@ -318,7 +285,7 @@ fn native_ranging_stop(
) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.range_stop(session_id as u32).map_err(|e| e.into())
}
@@ -349,7 +316,7 @@ fn native_get_session_state(
) -> Result<SessionState> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.session_get_state(session_id as u32).map_err(|e| e.into())
}
@@ -436,7 +403,7 @@ fn native_set_app_configurations(
) -> Result<SetAppConfigResponse> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
let config_byte_array = env.convert_byte_array(app_config_params)?;
let tlvs = parse_app_config_tlv_vec(no_of_params, &config_byte_array)?;
uci_manager.session_set_app_config(session_id as u32, tlvs).map_err(|e| e.into())
@@ -500,7 +467,7 @@ fn native_get_app_configurations(
) -> Result<Vec<AppConfigTlv>> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
let app_config_bytearray = env.convert_byte_array(app_config_params)?;
uci_manager
.session_get_app_config(
@@ -557,7 +524,7 @@ pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGe
fn native_get_caps_info(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<Vec<CapTlv>> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.core_get_caps_info().map_err(|e| e.into())
}
@@ -603,7 +570,7 @@ fn native_controller_multicast_list_update(
) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it goes
// out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
let mut address_list = vec![
0i16;
env.get_array_length(addresses)?.try_into().map_err(|_| {
@@ -656,7 +623,7 @@ fn native_set_country_code(
) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it goes
// out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
let country_code = env.convert_byte_array(country_code)?;
debug!("Country code: {:?}", country_code);
if country_code.len() != 2 {
@@ -684,14 +651,14 @@ pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSe
fn native_set_log_mode(env: JNIEnv, obj: JObject, log_mode_jstring: JString) -> Result<()> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it goes
// out of scope.
- let dispatcher = unsafe { get_dispatcher(env, obj) }?;
+ let dispatcher = unsafe { Dispatcher::get_dispatcher(env, obj) }?;
let logger_mode_str = String::from(env.get_string(log_mode_jstring)?);
debug!("UCI log: log started in {} mode", &logger_mode_str);
let logger_mode = logger_mode_str.try_into()?;
dispatcher.set_logger_mode(logger_mode).map_err(|e| e.into())
}
-fn create_vendor_response(msg: RawVendorMessage, env: JNIEnv) -> Result<jobject> {
+fn create_vendor_response(msg: RawUciMessage, env: JNIEnv) -> Result<jobject> {
let vendor_response_class = env.find_class(VENDOR_RESPONSE_CLASS)?;
match env.new_object(
vendor_response_class,
@@ -725,6 +692,26 @@ fn create_invalid_vendor_response(env: JNIEnv) -> Result<jobject> {
}
}
+fn create_ranging_round_status(
+ response: SessionUpdateActiveRoundsDtTagResponse,
+ env: JNIEnv,
+) -> Result<jobject> {
+ let dt_ranging_rounds_update_status_class = env.find_class(DT_RANGING_ROUNDS_STATUS_CLASS)?;
+ let indexes = response.ranging_round_indexes;
+ match env.new_object(
+ dt_ranging_rounds_update_status_class,
+ "(II[B)V",
+ &[
+ JValue::Int(response.status as i32),
+ JValue::Int(indexes.len() as i32),
+ JValue::Object(JObject::from(env.byte_array_from_slice(indexes.as_ref())?)),
+ ],
+ ) {
+ Ok(o) => Ok(*o),
+ Err(e) => Err(e.into()),
+ }
+}
+
/// Send Raw vendor command on a single UWB device. Returns an invalid response if failed.
#[no_mangle]
pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSendRawVendorCmd(
@@ -759,12 +746,12 @@ fn native_send_raw_vendor_cmd(
oid: jint,
payload_jarray: jbyteArray,
chip_id: JString,
-) -> Result<RawVendorMessage> {
+) -> Result<RawUciMessage> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
let payload = env.convert_byte_array(payload_jarray)?;
- uci_manager.raw_vendor_cmd(gid as u32, oid as u32, payload).map_err(|e| e.into())
+ uci_manager.raw_uci_cmd(gid as u32, oid as u32, payload).map_err(|e| e.into())
}
fn create_power_stats(power_stats: PowerStats, env: JNIEnv) -> Result<jobject> {
@@ -806,10 +793,55 @@ pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeGe
fn native_get_power_stats(env: JNIEnv, obj: JObject, chip_id: JString) -> Result<PowerStats> {
// Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
// goes out of scope.
- let uci_manager = unsafe { get_uci_manager(env, obj, chip_id) }?;
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
uci_manager.android_get_power_stats().map_err(|e| e.into())
}
+/// Update active ranging rounds for DT-TAG
+#[no_mangle]
+pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeSessionUpdateActiveRoundsDtTag(
+ env: JNIEnv,
+ obj: JObject,
+ session_id: jint,
+ _ranging_rounds: jint,
+ ranging_round_indexes: jbyteArray,
+ chip_id: JString,
+) -> jobject {
+ debug!("{}: enter", function_name!());
+ match option_result_helper(
+ native_set_ranging_rounds_dt_tag(
+ env,
+ obj,
+ session_id as u32,
+ ranging_round_indexes,
+ chip_id,
+ ),
+ function_name!(),
+ ) {
+ Some(rr) => create_ranging_round_status(rr, env)
+ .map_err(|e| {
+ error!("{} failed with {:?}", function_name!(), &e);
+ e
+ })
+ .unwrap_or(*JObject::null()),
+ None => *JObject::null(),
+ }
+}
+
+fn native_set_ranging_rounds_dt_tag(
+ env: JNIEnv,
+ obj: JObject,
+ session_id: u32,
+ ranging_round_indexes: jbyteArray,
+ chip_id: JString,
+) -> Result<SessionUpdateActiveRoundsDtTagResponse> {
+ // Safety: Java side owns Dispatcher by pointer, and borrows to this function until it
+ // goes out of scope.
+ let uci_manager = unsafe { Dispatcher::get_uci_manager(env, obj, chip_id) }?;
+ let indexes = env.convert_byte_array(ranging_round_indexes)?;
+ uci_manager.session_update_active_rounds_dt_tag(session_id, indexes).map_err(|e| e.into())
+}
+
/// Get the class loader object. Has to be called from a JNIEnv where the local java classes are
/// loaded. Results in a global reference to the class loader object that can be used to look for
/// classes in other native thread.
@@ -876,9 +908,14 @@ pub extern "system" fn Java_com_android_server_uwb_jni_NativeUwbManager_nativeDi
fn native_dispatcher_destroy(env: JNIEnv, obj: JObject) -> Result<()> {
let dispatcher_ptr_long = env.get_field(obj, "mDispatcherPointer", "J")?.j()?;
- // Safety: Java side owns Dispatcher through the pointer, and asks it to be destroyed
- unsafe {
- Dispatcher::destroy_ptr(dispatcher_ptr_long as *mut Dispatcher);
+ if dispatcher_ptr_long == 0 {
+ error!("UCI JNI: pointer to dispatcher to be destroyed is null.");
+ Err(Error::UwbCoreError(UwbCoreError::BadParameters))
+ } else {
+ // Safety: Java side owns Dispatcher through the pointer, and asks it to be destroyed
+ unsafe {
+ Dispatcher::destroy_ptr(dispatcher_ptr_long as *mut Dispatcher);
+ }
+ Ok(())
}
- Ok(())
}
diff --git a/service/uci/jni_new/src/unique_jvm.rs b/service/uci/jni/src/unique_jvm.rs
index c378fbb1..c378fbb1 100644
--- a/service/uci/jni_new/src/unique_jvm.rs
+++ b/service/uci/jni/src/unique_jvm.rs
diff --git a/service/uci/jni_new/Android.bp b/service/uci/jni_new/Android.bp
deleted file mode 100644
index 0483f679..00000000
--- a/service/uci/jni_new/Android.bp
+++ /dev/null
@@ -1,64 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_defaults {
- name: "libuwb_uci_jni_rust_new_defaults",
- crate_name: "uwb_uci_jni_rust_new",
- lints: "android",
- clippy_lints: "android",
- min_sdk_version: "Tiramisu",
- srcs: ["src/lib.rs"],
- rustlibs: [
- "libbinder_rs",
- "libjni",
- "liblog_rust",
- "liblogger",
- "libnum_traits",
- "libthiserror",
- "libtokio",
- "libuci_hal_android",
- "libuwb_core",
- "libuwb_uci_packets",
- ],
- prefer_rlib: true,
- apex_available: [
- "com.android.uwb",
- ],
- host_supported: true,
-}
-
-rust_ffi_shared {
- name: "libuwb_uci_jni_rust_new",
- defaults: ["libuwb_uci_jni_rust_new_defaults"],
-}
-
-rust_test {
- name: "libuwb_uci_jni_rust_new_tests",
- defaults: ["libuwb_uci_jni_rust_new_defaults"],
- target: {
- android: {
- test_suites: [
- "general-tests",
- ],
- },
- host: {
- test_suites: [
- "general-tests",
- ],
- },
- },
- // Support multilib variants (using different suffix per sub-architecture), which is needed on
- // build targets with secondary architectures, as the MTS test suite packaging logic flattens
- // all test artifacts into a single `testcases` directory.
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "",
- },
- },
- auto_gen_config: true,
-} \ No newline at end of file
diff --git a/service/uci/jni_unused/Android.bp b/service/uci/jni_unused/Android.bp
new file mode 100755
index 00000000..7dbf6b64
--- /dev/null
+++ b/service/uci/jni_unused/Android.bp
@@ -0,0 +1,3 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
diff --git a/service/uci/jni/UwbEventManager.cpp b/service/uci/jni_unused/UwbEventManager.cpp
index 1445d674..1445d674 100755
--- a/service/uci/jni/UwbEventManager.cpp
+++ b/service/uci/jni_unused/UwbEventManager.cpp
diff --git a/service/uci/jni/UwbEventManager.h b/service/uci/jni_unused/UwbEventManager.h
index f95e92e1..f95e92e1 100755
--- a/service/uci/jni/UwbEventManager.h
+++ b/service/uci/jni_unused/UwbEventManager.h
diff --git a/service/uci/jni/UwbJniInternal.h b/service/uci/jni_unused/UwbJniInternal.h
index d031e279..d031e279 100755
--- a/service/uci/jni/UwbJniInternal.h
+++ b/service/uci/jni_unused/UwbJniInternal.h
diff --git a/service/uci/jni/UwbJniTypes.h b/service/uci/jni_unused/UwbJniTypes.h
index d3316a29..d3316a29 100755
--- a/service/uci/jni/UwbJniTypes.h
+++ b/service/uci/jni_unused/UwbJniTypes.h
diff --git a/service/uci/jni/UwbNativeManager.cpp b/service/uci/jni_unused/UwbNativeManager.cpp
index 527b39c5..527b39c5 100755
--- a/service/uci/jni/UwbNativeManager.cpp
+++ b/service/uci/jni_unused/UwbNativeManager.cpp
diff --git a/service/uci/jni/rfTest/UwbRfTestManager.cpp b/service/uci/jni_unused/rfTest/UwbRfTestManager.cpp
index d526f254..d526f254 100755
--- a/service/uci/jni/rfTest/UwbRfTestManager.cpp
+++ b/service/uci/jni_unused/rfTest/UwbRfTestManager.cpp
diff --git a/service/uci/jni/rfTest/UwbRfTestManager.h b/service/uci/jni_unused/rfTest/UwbRfTestManager.h
index 1d9ed430..1d9ed430 100755
--- a/service/uci/jni/rfTest/UwbRfTestManager.h
+++ b/service/uci/jni_unused/rfTest/UwbRfTestManager.h
diff --git a/service/uci/jni/rfTest/UwbRfTestNativeManager.cpp b/service/uci/jni_unused/rfTest/UwbRfTestNativeManager.cpp
index 5d5c8529..5d5c8529 100755
--- a/service/uci/jni/rfTest/UwbRfTestNativeManager.cpp
+++ b/service/uci/jni_unused/rfTest/UwbRfTestNativeManager.cpp
diff --git a/service/uci/jni/utils/CondVar.cpp b/service/uci/jni_unused/utils/CondVar.cpp
index 45e6ef59..45e6ef59 100755
--- a/service/uci/jni/utils/CondVar.cpp
+++ b/service/uci/jni_unused/utils/CondVar.cpp
diff --git a/service/uci/jni/utils/CondVar.h b/service/uci/jni_unused/utils/CondVar.h
index 52a74907..52a74907 100755
--- a/service/uci/jni/utils/CondVar.h
+++ b/service/uci/jni_unused/utils/CondVar.h
diff --git a/service/uci/jni/utils/IntervalTimer.cpp b/service/uci/jni_unused/utils/IntervalTimer.cpp
index 3bb4fd48..3bb4fd48 100755
--- a/service/uci/jni/utils/IntervalTimer.cpp
+++ b/service/uci/jni_unused/utils/IntervalTimer.cpp
diff --git a/service/uci/jni/utils/IntervalTimer.h b/service/uci/jni_unused/utils/IntervalTimer.h
index c7dcd7e5..c7dcd7e5 100755
--- a/service/uci/jni/utils/IntervalTimer.h
+++ b/service/uci/jni_unused/utils/IntervalTimer.h
diff --git a/service/uci/jni/utils/JniLog.h b/service/uci/jni_unused/utils/JniLog.h
index ac2a1072..ac2a1072 100755
--- a/service/uci/jni/utils/JniLog.h
+++ b/service/uci/jni_unused/utils/JniLog.h
diff --git a/service/uci/jni/utils/Mutex.cpp b/service/uci/jni_unused/utils/Mutex.cpp
index e2609f0f..e2609f0f 100755
--- a/service/uci/jni/utils/Mutex.cpp
+++ b/service/uci/jni_unused/utils/Mutex.cpp
diff --git a/service/uci/jni/utils/Mutex.h b/service/uci/jni_unused/utils/Mutex.h
index 86fa563f..86fa563f 100755
--- a/service/uci/jni/utils/Mutex.h
+++ b/service/uci/jni_unused/utils/Mutex.h
diff --git a/service/uci/jni/utils/ScopedJniEnv.h b/service/uci/jni_unused/utils/ScopedJniEnv.h
index 77ddc7ab..77ddc7ab 100755
--- a/service/uci/jni/utils/ScopedJniEnv.h
+++ b/service/uci/jni_unused/utils/ScopedJniEnv.h
diff --git a/service/uci/jni/utils/SyncEvent.cpp b/service/uci/jni_unused/utils/SyncEvent.cpp
index e9907e87..e9907e87 100755
--- a/service/uci/jni/utils/SyncEvent.cpp
+++ b/service/uci/jni_unused/utils/SyncEvent.cpp
diff --git a/service/uci/jni/utils/SyncEvent.h b/service/uci/jni_unused/utils/SyncEvent.h
index 45ae85b7..45ae85b7 100755
--- a/service/uci/jni/utils/SyncEvent.h
+++ b/service/uci/jni_unused/utils/SyncEvent.h
diff --git a/service/uci/jni/utils/UwbJniUtil.cpp b/service/uci/jni_unused/utils/UwbJniUtil.cpp
index 94ee987c..94ee987c 100755
--- a/service/uci/jni/utils/UwbJniUtil.cpp
+++ b/service/uci/jni_unused/utils/UwbJniUtil.cpp
diff --git a/service/uci/jni/utils/UwbJniUtil.h b/service/uci/jni_unused/utils/UwbJniUtil.h
index 52be6627..52be6627 100755
--- a/service/uci/jni/utils/UwbJniUtil.h
+++ b/service/uci/jni_unused/utils/UwbJniUtil.h
diff --git a/tests/cts/hostsidetests/multidevices/uwb/Android.bp b/tests/cts/hostsidetests/multidevices/uwb/Android.bp
new file mode 100644
index 00000000..6ea14c31
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/Android.bp
@@ -0,0 +1,79 @@
+// Copyright (C) 2022 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_defaults {
+ name: "CtsUwbMultiDevicePythonDefaults",
+ libs: [
+ "mobly",
+ ],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+}
+
+python_test_host {
+ name: "CtsUwbMultiDeviceTestCase_UwbManagerTests",
+ main: "uwb_manager_test.py",
+ srcs: [
+ "uwb_manager_test.py",
+ "lib/uwb_base_test.py",
+ "lib/uwb_ranging_decorator.py",
+ "lib/uwb_ranging_params.py",
+ "test_utils/uwb_test_utils.py",
+ ],
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ test_config: "UwbManagerTests/AndroidTest.xml",
+ test_options: {
+ unit_test: false,
+ },
+ data: [
+ // Package the snippet with the mobly test
+ ":uwb_snippet",
+ ],
+ defaults: ["CtsUwbMultiDevicePythonDefaults"],
+}
+
+python_test_host {
+ name: "CtsUwbMultiDeviceTestCase_FiraRangingTests",
+ main: "ranging_test.py",
+ srcs: [
+ "ranging_test.py",
+ "lib/uwb_base_test.py",
+ "lib/uwb_ranging_decorator.py",
+ "lib/uwb_ranging_params.py",
+ "test_utils/uwb_test_utils.py",
+ ],
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ test_config: "FiraRangingTests/AndroidTest.xml",
+ test_options: {
+ unit_test: false,
+ },
+ data: [
+ // Package the snippet with the mobly test
+ ":uwb_snippet",
+ ],
+ defaults: ["CtsUwbMultiDevicePythonDefaults"],
+}
diff --git a/tests/cts/hostsidetests/multidevices/uwb/FiraRangingTests/AndroidTest.xml b/tests/cts/hostsidetests/multidevices/uwb/FiraRangingTests/AndroidTest.xml
new file mode 100644
index 00000000..94ce8f19
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/FiraRangingTests/AndroidTest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Config for CTS Uwb multi-device test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="uwb" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+
+ <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController"
+ type="module_controller">
+ <option name="required-feature" value="android.hardware.uwb" />
+ </object>
+
+ <device name="device1">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="uwb_snippet.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
+ </target_preparer>
+ </device>
+ <device name="device2">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="uwb_snippet.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
+ </target_preparer>
+ </device>
+
+ <test class="com.android.tradefed.testtype.mobly.MoblyBinaryHostTest">
+ <!-- The mobly-par-file-name should match the module name -->
+ <option name="mobly-par-file-name" value="CtsUwbMultiDeviceTestCase_FiraRangingTests" />
+ <!-- Timeout limit in milliseconds for all test cases of the python binary -->
+ <option name="mobly-test-timeout" value="900000" />
+ </test>
+</configuration>
+
diff --git a/tests/cts/hostsidetests/multidevices/uwb/OWNERS b/tests/cts/hostsidetests/multidevices/uwb/OWNERS
new file mode 100644
index 00000000..ea05fdf0
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 33618
+include platform/packages/modules/Uwb:/OWNERS
+
+# Engprod - Not owner of the test but help maintaining the module as an example
+jdesprez@google.com
+frankfeng@google.com
+murj@google.com
diff --git a/tests/cts/hostsidetests/multidevices/uwb/UwbManagerTests/AndroidTest.xml b/tests/cts/hostsidetests/multidevices/uwb/UwbManagerTests/AndroidTest.xml
new file mode 100644
index 00000000..750f6de1
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/UwbManagerTests/AndroidTest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Config for CTS Uwb multi-device test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="uwb" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+
+ <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController"
+ type="module_controller">
+ <option name="required-feature" value="android.hardware.uwb" />
+ </object>
+
+ <device name="device1">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="uwb_snippet.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
+ </target_preparer>
+ </device>
+ <device name="device2">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="uwb_snippet.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
+ </target_preparer>
+ </device>
+
+ <test class="com.android.tradefed.testtype.mobly.MoblyBinaryHostTest">
+ <!-- The mobly-par-file-name should match the module name -->
+ <option name="mobly-par-file-name" value="CtsUwbMultiDeviceTestCase_UwbManagerTests" />
+ <!-- Timeout limit in milliseconds for all test cases of the python binary -->
+ <option name="mobly-test-timeout" value="60000" />
+ </test>
+</configuration>
+
diff --git a/tests/cts/hostsidetests/multidevices/uwb/lib/uwb_base_test.py b/tests/cts/hostsidetests/multidevices/uwb/lib/uwb_base_test.py
new file mode 100644
index 00000000..bd4dac2e
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/lib/uwb_base_test.py
@@ -0,0 +1,80 @@
+"""Uwb base test."""
+
+import logging
+import re
+
+from mobly import base_test
+from mobly import test_runner
+from mobly import records
+from mobly.controllers import android_device
+
+RELEASE_ID_REGEX = re.compile(r"\w+\.\d+\.\d+")
+
+
+class UwbBaseTest(base_test.BaseTestClass):
+ """Base class for Uwb tests."""
+
+ def setup_class(self):
+ """Sets up the Android devices for Uwb test."""
+ super().setup_class()
+ self.android_devices = self.register_controller(android_device,
+ min_number=2)
+ for ad in self.android_devices:
+ ad.load_snippet("uwb", "com.google.snippet.uwb")
+ ad.adb.shell(["cmd", "uwb", "force-country-code", "enabled", "US"])
+
+ def teardown_class(self):
+ super().teardown_class()
+ self._record_all()
+
+ def _get_effort_name(self) -> str:
+ """Gets the TestTracker effort name from the Android build ID.
+
+ Returns:
+ Testtracker effort name for the test results.
+ """
+ effort_name = "UNKNOWN"
+ for record in self._controller_manager.get_controller_info_records():
+ if record.controller_name == "AndroidDevice" and record.controller_info:
+ build_info = record.controller_info[0]["build_info"]
+ if re.match(RELEASE_ID_REGEX, build_info["build_id"]):
+ effort_name = build_info["build_id"]
+ else:
+ effort_name = build_info[android_device.BuildInfoConstants
+ .BUILD_VERSION_INCREMENTAL.build_info_key]
+ break
+ return effort_name
+
+ def _record(self, tr_record: records.TestResultRecord):
+ """Records TestTracker data for a single test.
+
+ Args:
+ tr_record: test case record.
+ """
+ self.record_data({
+ "Test Class": tr_record.test_class,
+ "Test Name": tr_record.test_name,
+ "sponge_properties": {
+ "test_tracker_effort_name": self._get_effort_name(),
+ "test_tracker_uuid": tr_record.uid
+ }
+ })
+
+ def _record_all(self):
+ """Records TestTracker data for all tests."""
+ tr_record_dict = {}
+ for tr_record in self.results.executed:
+ if not tr_record.uid:
+ logging.warning("Missing UID for test %s", tr_record.test_name)
+ continue
+ if tr_record.uid in tr_record_dict:
+ record = tr_record_dict[tr_record.uid]
+ if record in self.results.failed or record in self.results.error:
+ continue
+ tr_record_dict[tr_record.uid] = tr_record
+ for tr_record in tr_record_dict.values():
+ self._record(tr_record)
+
+
+if __name__ == "__main__":
+ test_runner.main()
diff --git a/tests/cts/hostsidetests/multidevices/uwb/lib/uwb_ranging_decorator.py b/tests/cts/hostsidetests/multidevices/uwb/lib/uwb_ranging_decorator.py
new file mode 100644
index 00000000..658877a9
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/lib/uwb_ranging_decorator.py
@@ -0,0 +1,209 @@
+"""Decorator for UWB ranging methods."""
+
+import time
+from typing import List
+from mobly.controllers import android_device
+from mobly.controllers.android_device_lib import jsonrpc_client_base
+
+from lib import uwb_ranging_params
+
+CALLBACK_WAIT_TIME_SEC = 3
+
+
+class UwbRangingDecorator():
+ """Decorator for Uwb ranging methods."""
+
+ def __init__(self, ad: android_device.AndroidDevice):
+ """Initialize the ranging device.
+
+ Args:
+ ad: android device object
+
+ Usage:
+ The ranging methods should be called in the following order
+ 1. open_ranging()
+ 2. start_ranging()
+ 3. find peer, distance measurement, aoa measurements.
+ 4. stop_ranging()
+ 5. close_ranging()
+ """
+ self.ad = ad
+ self._callback_keys = {}
+ self._event_handlers = {}
+ self.log = self.ad.log
+
+ def clear_ranging_session_callback_events(self, ranging_session_id: int = 0):
+ """Clear 'RangingSessionCallback' events from EventCache.
+
+ Args:
+ ranging_session_id: ranging session id.
+ """
+ handler = self._event_handlers[ranging_session_id]
+ handler.getAll("RangingSessionCallback")
+
+ def verify_callback_received(self,
+ ranging_event: str,
+ session: int = 0,
+ timeout: int = CALLBACK_WAIT_TIME_SEC):
+ """Verifies if the expected callback is received.
+
+ Args:
+ ranging_event: Expected ranging event.
+ session: ranging session.
+ timeout: callback timeout.
+
+ Raises:
+ TimeoutError: if the expected callback event is not received.
+ """
+ handler = self._event_handlers[session]
+ start_time = time.time()
+ while time.time() - start_time < timeout:
+ try:
+ event = handler.waitAndGet(
+ "RangingSessionCallback", timeout=timeout)
+ event_received = event.data["rangingSessionEvent"]
+ self.ad.log.debug("Received event - %s" % event_received)
+ if event_received == ranging_event:
+ self.ad.log.debug("Received the '%s' callback in %ss" %
+ (ranging_event, round(time.time() - start_time, 2)))
+ self.clear_ranging_session_callback_events(session)
+ return
+ except TimeoutError:
+ self.log.warn("Failed to receive 'RangingSessionCallback' event")
+ raise TimeoutError("Failed to receive '%s' event" % ranging_event)
+
+ def open_fira_ranging(self,
+ params: uwb_ranging_params.UwbRangingParams,
+ session: int = 0):
+ """Opens fira ranging session.
+
+ Args:
+ params: UWB ranging parameters.
+ session: ranging session.
+ """
+ callback_key = "fira_session_%s" % session
+ handler = self.ad.uwb.openFiraRangingSession(callback_key, params.to_dict())
+ self._event_handlers[session] = handler
+ self.verify_callback_received("Opened", session)
+ self._callback_keys[session] = callback_key
+
+ def start_fira_ranging(self, session: int = 0):
+ """Starts Fira ranging session.
+
+ Args:
+ session: ranging session.
+ """
+ self.ad.uwb.startFiraRangingSession(self._callback_keys[session])
+ self.verify_callback_received("Started", session)
+
+ def reconfigure_fira_ranging(
+ self,
+ params: uwb_ranging_params.UwbRangingReconfigureParams,
+ session: int = 0):
+ """Reconfigures Fira ranging parameters.
+
+ Args:
+ params: UWB reconfigured params.
+ session: ranging session.
+ """
+ self.ad.uwb.reconfigureFiraRangingSession(self._callback_keys[session],
+ params.to_dict())
+ self.verify_callback_received("Reconfigured", session)
+
+ def is_uwb_peer_found(self, addr: List[int], session: int = 0) -> bool:
+ """Verifies if the UWB peer is found.
+
+ Args:
+ addr: peer address.
+ session: ranging session.
+
+ Returns:
+ True if peer is found, False if not.
+ """
+ self.verify_callback_received("ReportReceived", session)
+ return self.ad.uwb.isUwbPeerFound(self._callback_keys[session], addr)
+
+ def get_distance_measurement(self,
+ addr: List[int],
+ session: int = 0) -> float:
+ """Returns distance measurement from peer.
+
+ Args:
+ addr: peer address.
+ session: ranging session.
+
+ Returns:
+ Distance measurement in float.
+
+ Raises:
+ ValueError: if the DistanceMeasurement object is null.
+ """
+ try:
+ return self.ad.uwb.getDistanceMeasurement(self._callback_keys[session],
+ addr)
+ except jsonrpc_client_base.ApiError as api_error:
+ raise ValueError("Failed to get distance measurement.") from api_error
+
+ def get_aoa_azimuth_measurement(self,
+ addr: List[int],
+ session: int = 0) -> float:
+ """Returns AoA azimuth measurement data from peer.
+
+ Args:
+ addr: list, peer address.
+ session: ranging session.
+
+ Returns:
+ AoA azimuth measurement in radians in float.
+
+ Raises:
+ ValueError: if the AngleMeasurement object is null.
+ """
+ try:
+ return self.ad.uwb.getAoAAzimuthMeasurement(
+ self._callback_keys[session], addr)
+ except jsonrpc_client_base.ApiError as api_error:
+ raise ValueError("Failed to get azimuth measurement.") from api_error
+
+ def get_aoa_altitude_measurement(self,
+ addr: List[int],
+ session: int = 0) -> float:
+ """Gets UWB AoA altitude measurement data.
+
+ Args:
+ addr: list, peer address.
+ session: ranging session.
+
+ Returns:
+ AoA altitude measurement in radians in float.
+
+ Raises:
+ ValueError: if the AngleMeasurement object is null.
+ """
+ try:
+ return self.ad.uwb.getAoAAltitudeMeasurement(
+ self._callback_keys[session], addr)
+ except jsonrpc_client_base.ApiError as api_error:
+ raise ValueError("Failed to get altitude measurement.") from api_error
+
+ def stop_ranging(self, session: int = 0):
+ """Stops UWB ranging session.
+
+ Args:
+ session: ranging session.
+ """
+ self.ad.uwb.stopRangingSession(self._callback_keys[session])
+ self.verify_callback_received("Stopped", session)
+
+ def close_ranging(self, session: int = 0):
+ """Closes ranging session.
+
+ Args:
+ session: ranging session.
+ """
+ if session not in self._callback_keys:
+ return
+ self.ad.uwb.closeRangingSession(self._callback_keys[session])
+ self.verify_callback_received("Closed", session)
+ self._callback_keys.pop(session, None)
+ self._event_handlers.pop(session, None)
diff --git a/tests/cts/hostsidetests/multidevices/uwb/lib/uwb_ranging_params.py b/tests/cts/hostsidetests/multidevices/uwb/lib/uwb_ranging_params.py
new file mode 100644
index 00000000..0d5a333a
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/lib/uwb_ranging_params.py
@@ -0,0 +1,218 @@
+"""Class for UWB ranging parameters."""
+
+import dataclasses
+from typing import Any, Dict, List, Optional
+
+
+class FiraParamEnums:
+ """Class for Fira parameter constants."""
+
+ # channels
+ UWB_CHANNEL_5 = 5
+ UWB_CHANNEL_9 = 9
+
+ # preamble codes
+ UWB_PREAMBLE_CODE_INDEX_9 = 9
+ UWB_PREAMBLE_CODE_INDEX_10 = 10
+ UWB_PREAMBLE_CODE_INDEX_11 = 11
+ UWB_PREAMBLE_CODE_INDEX_12 = 12
+
+ # ranging device types
+ DEVICE_TYPE_CONTROLEE = 0
+ DEVICE_TYPE_CONTROLLER = 1
+
+ # ranging device roles
+ DEVICE_ROLE_RESPONDER = 0
+ DEVICE_ROLE_INITIATOR = 1
+
+ # multi node modes
+ MULTI_NODE_MODE_UNICAST = 0
+ MULTI_NODE_MODE_ONE_TO_MANY = 1
+
+ # hopping modes
+ HOPPING_MODE_DISABLE = 0
+ HOPPING_MODE_FIRA_HOPPING_ENABLE = 1
+
+ # ranging round usage
+ RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE = 1
+ RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE = 2
+ RANGING_ROUND_USAGE_SS_TWR_NON_DEFERRED_MODE = 3
+ RANGING_ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE = 4
+
+ # mac address mode
+ MAC_ADDRESS_MODE_2_BYTES = 0
+ MAC_ADDRESS_MODE_8_BYTES = 2
+
+ # initiation time in ms
+ INITIATION_TIME_MS = 0
+
+ # slot duration rstu
+ SLOT_DURATION_RSTU = 2400
+
+ # ranging interval ms
+ RANGING_INTERVAL_MS = 200
+
+ # slots per ranging round
+ SLOTS_PER_RR = 30
+
+ # in band termination attempt count
+ IN_BAND_TERMINATION_ATTEMPT_COUNT = 1
+
+ # aoa report request
+ AOA_RESULT_REQUEST_MODE_NO_AOA_REPORT = 0
+ AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS = 1
+
+ # ranging round retries
+ MAX_RANGING_ROUND_RETRIES = 0
+
+ # block stride
+ BLOCK_STRIDE_LENGTH = 0
+
+ # list update actions
+ MULTICAST_LIST_UPDATE_ACTION_ADD = 0
+ MULTICAST_LIST_UPDATE_ACTION_DELETE = 1
+
+
+@dataclasses.dataclass
+class UwbRangingReconfigureParams():
+ """Class for UWB ranging reconfigure parameters.
+
+ Attributes:
+ action: Type of reconfigure action.
+ address_list: new address list.
+ block_stride_length: block stride length
+ """
+ action: Optional[int] = None
+ address_list: Optional[List[List[int]]] = None
+ block_stride_length: Optional[int] = None
+
+ def to_dict(self) -> Dict[str, Any]:
+ """Returns UWB ranging reconfigure parameters in dictionary for sl4a.
+
+ Returns:
+ UWB ranging reconfigure parameters in dictionary.
+ """
+ reconfigure_params = {}
+ if self.address_list is not None:
+ reconfigure_params["action"] = self.action
+ reconfigure_params["addressList"] = self.address_list
+ elif self.block_stride_length is not None:
+ reconfigure_params["blockStrideLength"] = self.block_stride_length
+ return reconfigure_params
+
+
+@dataclasses.dataclass
+class UwbRangingParams():
+ """Class for Uwb ranging parameters.
+
+ Attributes:
+ device_type: Type of ranging device - Controller or Controlee.
+ device_role: Role of ranging device - Initiator or Responder.
+ device_address: Address of the UWB device.
+ destination_addresses: List of UWB peer addresses.
+ channel: Channel for ranging. Possible values 5 or 9.
+ preamble: Preamble for ranging.
+ ranging_round_usage : Ranging Round Usage values.
+ hopping_mode : Hopping modes.
+ mac_address_mode : MAC address modes.
+ initiation_time_ms : Initiation Time in ms.
+ slot_duration_rstu : Slot duration RSTU.
+ ranging_interval_ms : Ranging interval in ms.
+ slots_per_ranging_round : Slots per Ranging Round.
+ in_band_termination_attempt_count : In Band Termination Attempt count.
+ aoa_result_request : AOA report request.
+ max_ranging_round_retries : Max Ranging round retries.
+ block_stride_length: Block Stride Length
+ session_id: Ranging session ID.
+ multi_node_mode: Ranging mode. Possible values 1 to 1 or 1 to many.
+ vendor_id: Ranging device vendor ID.
+ static_sts_iv: Static STS value.
+
+ Example:
+ An example of UWB ranging parameters passed to sl4a is below.
+
+ self.initiator_params = {
+ "sessionId": 10,
+ "deviceType": FiraParamEnums.RANGING_DEVICE_TYPE_CONTROLLER,
+ "deviceRole": FiraParamEnums.RANGING_DEVICE_ROLE_INITIATOR,
+ "multiNodeMode": FiraParamEnums.MULTI_NODE_MODE_ONE_TO_MANY,
+ "channel": FiraParamEnums.UWB_CHANNEL_9,
+ "deviceAddress": [1, 2],
+ "destinationAddresses": [[3, 4],],
+ "vendorId": [5, 6],
+ "staticStsIV": [5, 6, 7, 8, 9, 10],
+ }
+
+ The UwbRangingParams are passed to UwbManagerFacade#openRaningSession()
+ from the open_ranging() method as a JSONObject.
+ These are converted to FiraOpenSessionParams using
+ UwbManagerFacade#generateFiraOpenSessionParams().
+ If some of the values are skipped in the params, default values are used.
+ Please see com/google/uwb/support/fira/FiraParams.java for more details
+ on the default values.
+
+ If the passed params are invalid, then open_ranging() will fail.
+ """
+
+ device_type: int
+ device_role: int
+ device_address: List[int]
+ destination_addresses: List[List[int]]
+ session_id: int = 10
+ channel: int = FiraParamEnums.UWB_CHANNEL_9
+ preamble: int = FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_10
+ multi_node_mode: int = FiraParamEnums.MULTI_NODE_MODE_ONE_TO_MANY
+ ranging_round_usage: int = FiraParamEnums.RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE
+ mac_address_mode: int = FiraParamEnums.MAC_ADDRESS_MODE_2_BYTES
+ initiation_time_ms: int = FiraParamEnums.INITIATION_TIME_MS
+ slot_duration_rstu: int = FiraParamEnums.SLOT_DURATION_RSTU
+ ranging_interval_ms: int = FiraParamEnums.RANGING_INTERVAL_MS
+ slots_per_ranging_round: int = FiraParamEnums.SLOTS_PER_RR
+ in_band_termination_attempt_count: int = FiraParamEnums.IN_BAND_TERMINATION_ATTEMPT_COUNT
+ aoa_result_request: int = FiraParamEnums.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS
+ hopping_mode: int = FiraParamEnums.HOPPING_MODE_DISABLE
+ max_ranging_round_retries: int = FiraParamEnums.MAX_RANGING_ROUND_RETRIES
+ block_stride_length: int = FiraParamEnums.BLOCK_STRIDE_LENGTH
+ vendor_id: List[int] = dataclasses.field(default_factory=lambda: [5, 6])
+ static_sts_iv: List[int] = dataclasses.field(
+ default_factory=lambda: [5, 6, 7, 8, 9, 10])
+
+ def to_dict(self) -> Dict[str, Any]:
+ """Returns UWB ranging parameters in dictionary for sl4a.
+
+ Returns:
+ UWB ranging parameters in dictionary.
+ """
+ return {
+ "deviceType": self.device_type,
+ "deviceRole": self.device_role,
+ "deviceAddress": self.device_address,
+ "destinationAddresses": self.destination_addresses,
+ "channel": self.channel,
+ "preamble": self.preamble,
+ "rangingRoundUsage": self.ranging_round_usage,
+ "macAddressMode": self.mac_address_mode,
+ "initiationTimeMs": self.initiation_time_ms,
+ "slotDurationRstu": self.slot_duration_rstu,
+ "slotsPerRangingRound": self.slots_per_ranging_round,
+ "rangingIntervalMs": self.ranging_interval_ms,
+ "hoppingMode": self.hopping_mode,
+ "maxRangingRoundRetries": self.max_ranging_round_retries,
+ "inBandTerminationAttemptCount": self.in_band_termination_attempt_count,
+ "aoaResultRequest": self.aoa_result_request,
+ "blockStrideLength": self.block_stride_length,
+ "sessionId": self.session_id,
+ "multiNodeMode": self.multi_node_mode,
+ "vendorId": self.vendor_id,
+ "staticStsIV": self.static_sts_iv,
+ }
+
+ def update(self, **kwargs: Any):
+ """Updates the UWB parameters with the new values.
+
+ Args:
+ **kwargs: uwb attributes with new values.
+ """
+ for key, value in kwargs.items():
+ if hasattr(self, key):
+ setattr(self, key, value)
diff --git a/tests/cts/hostsidetests/multidevices/uwb/ranging_test.py b/tests/cts/hostsidetests/multidevices/uwb/ranging_test.py
new file mode 100644
index 00000000..da0596e3
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/ranging_test.py
@@ -0,0 +1,1117 @@
+"""Tests for Uwb Ranging APIs."""
+
+import logging
+import random
+import sys
+from typing import List
+
+from mobly import asserts
+from mobly import config_parser
+from mobly import test_runner
+from mobly import records
+from mobly import signals
+import timeout_decorator
+
+from lib import uwb_base_test
+from lib import uwb_ranging_decorator
+from lib import uwb_ranging_params
+from test_utils import uwb_test_utils
+
+RESPONDER_STOP_CALLBACK_TIMEOUT = 60
+
+_TEST_CASES = (
+ "test_ranging_device_tracker_profile_default",
+ "test_ranging_nearby_share_profile_default",
+ "test_ranging_device_tracker_profile_reconfigure_ranging_interval",
+ "test_ranging_nearby_share_profile_reconfigure_ranging_interval",
+ "test_ranging_device_tracker_profile_no_aoa_report",
+ "test_ranging_nearby_share_profile_hopping_mode_enabled",
+ "test_ranging_rr_ss_twr_deferred_device_tracker_profile",
+ "test_ranging_rr_ss_twr_deferred_nearby_share_profile",
+ "test_stop_initiator_ranging_device_tracker_profile",
+ "test_stop_initiator_ranging_nearby_share_profile",
+ "test_stop_responder_ranging_device_tracker_profile",
+ "test_stop_responder_ranging_nearby_share_profile",
+ "test_ranging_device_tracker_profile_with_airplane_mode_toggle",
+ "test_ranging_nearby_share_profile_with_airplane_mode_toggle",
+)
+
+
+class RangingTest(uwb_base_test.UwbBaseTest):
+ """Tests for UWB Ranging APIs.
+
+ Attributes:
+ android_devices: list of android device objects.
+ """
+
+ def __init__(self, configs: config_parser.TestRunConfig):
+ """Init method for the test class.
+
+ Args:
+ configs: A config_parser.TestRunConfig object.
+ """
+ super().__init__(configs)
+ self.tests = _TEST_CASES
+
+ def setup_class(self):
+ super().setup_class()
+ self.uwb_devices = [
+ uwb_ranging_decorator.UwbRangingDecorator(ad)
+ for ad in self.android_devices
+ ]
+ self.initiator, self.responder = self.uwb_devices
+ self.device_addresses = self.user_params.get("device_addresses",
+ [[1, 2], [3, 4]])
+ self.initiator_addr, self.responder_addr = self.device_addresses
+ self.new_responder_addr = [4, 5]
+ self.block_stride_length = random.randint(1, 10)
+
+ def setup_test(self):
+ super().setup_test()
+ for uwb_device in self.uwb_devices:
+ try:
+ uwb_device.close_ranging()
+ except timeout_decorator.TimeoutError:
+ uwb_device.log.warn("Failed to cleanup ranging sessions")
+ for uwb_device in self.uwb_devices:
+ uwb_test_utils.set_airplane_mode(uwb_device.ad, False)
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.responder.stop_ranging()
+ self.initiator.stop_ranging()
+ self.responder.close_ranging()
+ self.initiator.close_ranging()
+
+ def teardown_class(self):
+ super().teardown_class()
+ for count, ad in enumerate(self.android_devices):
+ test_name = "initiator" if not count else "responder"
+ ad.take_bug_report(
+ test_name=test_name, destination=self.current_test_info.output_path)
+
+ ### Helper Methods ###
+
+ def _verify_one_to_one_ranging(
+ self, initiator: uwb_ranging_decorator.UwbRangingDecorator,
+ responder: uwb_ranging_decorator.UwbRangingDecorator,
+ initiator_params: uwb_ranging_params.UwbRangingParams,
+ responder_params: uwb_ranging_params.UwbRangingParams,
+ peer_addr: List[int]):
+ """Verifies ranging between two uwb devices.
+
+ Args:
+ initiator: uwb device object.
+ responder: uwb device object.
+ initiator_params: ranging params for initiator.
+ responder_params: ranging params for responder.
+ peer_addr: address of uwb device.
+ """
+ initiator.open_fira_ranging(initiator_params)
+ responder.open_fira_ranging(responder_params)
+ initiator.start_fira_ranging()
+ responder.start_fira_ranging()
+ uwb_test_utils.verify_peer_found(initiator, peer_addr)
+
+ def _verify_one_to_one_ranging_reconfigured_params(
+ self, initiator: uwb_ranging_decorator.UwbRangingDecorator,
+ responder: uwb_ranging_decorator.UwbRangingDecorator,
+ initiator_params: uwb_ranging_params.UwbRangingParams,
+ responder_params: uwb_ranging_params.UwbRangingParams,
+ peer_addr: List[int]):
+ """Verifies ranging between two uwb devices with reconfigured params.
+
+ Args:
+ initiator: The uwb device object.
+ responder: The uwb device object.
+ initiator_params: The ranging params for initiator.
+ responder_params: The ranging params for responder.
+ peer_addr: The new address of uwb device.
+ """
+ # change responder addr and verify peer cannot be found
+ responder_params.update(device_address=peer_addr)
+ responder.open_fira_ranging(responder_params)
+ responder.start_fira_ranging()
+ try:
+ uwb_test_utils.verify_peer_found(initiator, peer_addr)
+ asserts.fail("Peer found without reconfiguring initiator.")
+ except signals.TestFailure:
+ logging.info("Peer %s not found as expected", peer_addr)
+
+ # reconfigure initiator with new peer addr and verify peer found
+ reconfigure_params = uwb_ranging_params.UwbRangingReconfigureParams(
+ action=uwb_ranging_params.FiraParamEnums
+ .MULTICAST_LIST_UPDATE_ACTION_ADD,
+ address_list=[peer_addr])
+ initiator.reconfigure_fira_ranging(reconfigure_params)
+ uwb_test_utils.verify_peer_found(initiator, peer_addr)
+
+ def _verify_stop_initiator_callback(
+ self, initiator: uwb_ranging_decorator.UwbRangingDecorator,
+ responder: uwb_ranging_decorator.UwbRangingDecorator,
+ initiator_params: uwb_ranging_params.UwbRangingParams,
+ responder_params: uwb_ranging_params.UwbRangingParams,
+ peer_addr: List[int]):
+ """Verifies stop callback on initiator.
+
+ Args:
+ initiator: uwb device object.
+ responder: uwb device object.
+ initiator_params: ranging params for initiator.
+ responder_params: ranging params for responder.
+ peer_addr: address of uwb device.
+ """
+
+ # Verify ranging
+ self._verify_one_to_one_ranging(initiator, responder, initiator_params,
+ responder_params, peer_addr)
+
+ # Verify Stopped callbacks
+ initiator.stop_ranging()
+ responder.verify_callback_received(
+ "Stopped", timeout=RESPONDER_STOP_CALLBACK_TIMEOUT)
+
+ # Restart and verify ranging
+ initiator.start_fira_ranging()
+ responder.start_fira_ranging()
+ uwb_test_utils.verify_peer_found(initiator, peer_addr)
+
+ def _verify_stop_responder_callback(
+ self, initiator: uwb_ranging_decorator.UwbRangingDecorator,
+ responder: uwb_ranging_decorator.UwbRangingDecorator,
+ initiator_params: uwb_ranging_params.UwbRangingParams,
+ responder_params: uwb_ranging_params.UwbRangingParams,
+ peer_addr: List[int]):
+ """Verifies stop callback on responder.
+
+ Args:
+ initiator: uwb device object.
+ responder: uwb device object.
+ initiator_params: ranging params for initiator.
+ responder_params: ranging params for responder.
+ peer_addr: address of uwb device.
+ """
+
+ # Verify ranging
+ self._verify_one_to_one_ranging(initiator, responder, initiator_params,
+ responder_params, peer_addr)
+
+ # Verify Stopped callbacks
+ responder.stop_ranging()
+
+ # Restart and verify ranging
+ responder.start_fira_ranging()
+ uwb_test_utils.verify_peer_found(initiator, peer_addr)
+
+ def _verify_one_to_one_ranging_airplane_mode_toggle(
+ self, initiator: uwb_ranging_decorator.UwbRangingDecorator,
+ responder: uwb_ranging_decorator.UwbRangingDecorator,
+ initiator_params: uwb_ranging_params.UwbRangingParams,
+ responder_params: uwb_ranging_params.UwbRangingParams,
+ peer_addr: List[int]):
+ """Verifies ranging with airplane mode toggle.
+
+ Args:
+ initiator: uwb device object.
+ responder: uwb device object.
+ initiator_params: ranging params for initiator.
+ responder_params: ranging params for responder.
+ peer_addr: address of uwb device.
+ """
+
+ # Verify ranging before APM toggle
+ self._verify_one_to_one_ranging(initiator, responder, initiator_params,
+ responder_params, peer_addr)
+
+ # Enable APM on initiator and verify callbacks
+ initiator.clear_ranging_session_callback_events()
+ responder.clear_ranging_session_callback_events()
+ uwb_test_utils.set_airplane_mode(initiator.ad, True)
+ initiator.verify_callback_received("Closed")
+ responder.verify_callback_received(
+ "Stopped", timeout=RESPONDER_STOP_CALLBACK_TIMEOUT)
+
+ # Disable APM, restart and verify ranging
+ uwb_test_utils.set_airplane_mode(initiator.ad, False)
+ uwb_test_utils.verify_uwb_state_callback(initiator.ad, "Inactive")
+ initiator.open_fira_ranging(initiator_params)
+ initiator.start_fira_ranging()
+ responder.start_fira_ranging()
+ uwb_test_utils.verify_peer_found(initiator, peer_addr)
+
+ # Enable APM on responder and verify callbacks
+ responder.clear_ranging_session_callback_events()
+ uwb_test_utils.set_airplane_mode(responder.ad, True)
+ responder.verify_callback_received("Closed")
+
+ # Disable APM, restart and verify ranging
+ uwb_test_utils.set_airplane_mode(responder.ad, False)
+ uwb_test_utils.verify_uwb_state_callback(responder.ad, "Inactive")
+ responder.open_fira_ranging(responder_params)
+ responder.start_fira_ranging()
+ uwb_test_utils.verify_peer_found(initiator, peer_addr)
+
+ def _verify_one_to_one_ranging_reconfigure_ranging_interval(
+ self, initiator: uwb_ranging_decorator.UwbRangingDecorator,
+ block_stride_length: int, peer_addr: List[int]):
+ """Verifies ranging with reconfigured ranging interval.
+
+ Args:
+ initiator: The uwb device object.
+ block_stride_length: The new block stride length to reconfigure.
+ peer_addr: address of the responder.
+ """
+ initiator.log.info("Reconfigure block stride length to: %s" %
+ block_stride_length)
+ reconfigure_params = uwb_ranging_params.UwbRangingReconfigureParams(
+ block_stride_length=block_stride_length)
+ initiator.reconfigure_fira_ranging(reconfigure_params)
+ uwb_test_utils.verify_peer_found(initiator, peer_addr)
+
+ ### Test Cases ###
+
+ @records.uid("534e80d7-cfba-47c1-9ae1-62b3630458ac")
+ def test_ranging_default_params(self):
+ """Verifies ranging with default Fira parameters."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+ self.responder.stop_ranging()
+ self.responder.close_ranging()
+ self._verify_one_to_one_ranging_reconfigured_params(
+ self.initiator, self.responder, initiator_params, responder_params,
+ self.new_responder_addr)
+
+ @records.uid("04ec82db-be17-4aaf-911c-56e702c1ea55")
+ def test_ranging_device_tracker_profile_default(self):
+ """Verifies ranging with device tracker profile default values."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("05fa3bc8-b4c8-4bd4-a802-80250aa27a0b")
+ def test_ranging_nearby_share_profile_default(self):
+ """Verifies ranging for device nearby share with default profile."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+ self.responder.stop_ranging()
+ self.responder.close_ranging()
+ self._verify_one_to_one_ranging_reconfigured_params(
+ self.initiator, self.responder, initiator_params, responder_params,
+ self.new_responder_addr)
+
+ @records.uid("fa9b18c2-a1e1-4bf8-bc76-796700a1c2cb")
+ def test_open_ranging_with_same_session_id_nearby_share(self):
+ """Verifies ranging for device nearby share with same session id."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+ self.responder.close_ranging()
+ self.initiator.close_ranging()
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("973a083a-f01a-44cc-a706-3b2953ca8e22")
+ def test_open_ranging_with_same_session_id_device_tracker(self):
+ """Verifies ranging with device tracker profile with same session id."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+ self.responder.close_ranging()
+ self.initiator.close_ranging()
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("dff3aaff-c3ed-47fb-b99f-d47f55c06770")
+ def test_ranging_default_params_reconfigure_ranging_interval(self):
+ """Verifies ranging with default Fira parameters."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+ self._verify_one_to_one_ranging_reconfigure_ranging_interval(
+ self.initiator, self.block_stride_length, self.responder_addr)
+
+ @records.uid("7800a229-af09-48e1-a6f9-bdd159afc460")
+ def test_ranging_device_tracker_profile_reconfigure_ranging_interval(self):
+ """Verifies ranging with device tracker profile default values."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+ self._verify_one_to_one_ranging_reconfigure_ranging_interval(
+ self.initiator, self.block_stride_length, self.responder_addr)
+
+ @records.uid("c1d98ba2-93bd-4458-abfe-0194307ac2d4")
+ def test_ranging_nearby_share_profile_reconfigure_ranging_interval(self):
+ """Verifies ranging for device nearby share with default profile."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+ self._verify_one_to_one_ranging_reconfigure_ranging_interval(
+ self.initiator, self.block_stride_length, self.responder_addr)
+
+ @records.uid("19ed274a-5cb2-4210-9592-0843313cb88f")
+ def test_ranging_device_tracker_profile_ch9_pr12(self):
+ """Verifies ranging with device tracker for channel 9 and preamble 12."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_12,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_12,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("810d13ef-678f-4ee5-affc-b0b50efaafc5")
+ def test_ranging_device_tracker_profile_ch5_pr11(self):
+ """Verifies ranging with device tracker for channel 5 and preamble 11."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ channel=uwb_ranging_params.FiraParamEnums.UWB_CHANNEL_5,
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_11,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ channel=uwb_ranging_params.FiraParamEnums.UWB_CHANNEL_5,
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_11,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("90d73266-07f2-46d8-bc06-a4a1206d693a")
+ def test_ranging_device_tracker_profile_ch9_pr11(self):
+ """Verifies device tracking profile with channel 9 and preamble 11."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_11,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_11,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("827c969e-15dc-43f9-ad65-c8dbcc91dd56")
+ def test_ranging_device_tracker_profile_ch5_pr10(self):
+ """Verifies device tracking profile with channel 5 and preamble 10."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ channel=uwb_ranging_params.FiraParamEnums.UWB_CHANNEL_5,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ channel=uwb_ranging_params.FiraParamEnums.UWB_CHANNEL_5,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("f7a4fad5-9340-461c-bfb3-e9e530a54a97")
+ def test_ranging_device_tracker_profile_ch9_pr9(self):
+ """Verifies ranging with device tracker for channel 9 and preamble 9."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ channel=uwb_ranging_params.FiraParamEnums.UWB_CHANNEL_9,
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_9,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ channel=uwb_ranging_params.FiraParamEnums.UWB_CHANNEL_9,
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_9,
+ multi_node_mode=uwb_ranging_params
+ .FiraParamEnums.MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("d75d7443-904c-4ebf-9cb2-5e1df484db7a")
+ def test_ranging_device_tracker_profile_ch5_pr9(self):
+ """Verifies ranging with device tracker for channel 5 and preamble 9."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ channel=uwb_ranging_params.FiraParamEnums.UWB_CHANNEL_5,
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_9,
+ multi_node_mode=uwb_ranging_params
+ .FiraParamEnums.MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ channel=uwb_ranging_params.FiraParamEnums.UWB_CHANNEL_5,
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_9,
+ multi_node_mode=uwb_ranging_params
+ .FiraParamEnums.MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("0413d123-8578-477f-b07e-be2e8e0a8652")
+ def test_ranging_device_tracker_profile_ch5_pr12(self):
+ """Verifies ranging with device tracker for channel 5 and preamble 12."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ channel=uwb_ranging_params.FiraParamEnums.UWB_CHANNEL_5,
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_12,
+ multi_node_mode=uwb_ranging_params
+ .FiraParamEnums.MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ channel=uwb_ranging_params.FiraParamEnums.UWB_CHANNEL_5,
+ preamble=uwb_ranging_params.FiraParamEnums.UWB_PREAMBLE_CODE_INDEX_12,
+ multi_node_mode=uwb_ranging_params
+ .FiraParamEnums.MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ # disable due to b/212455943
+ @records.uid("73c9e335-c1da-432f-937a-d4bf15f6b338")
+ def _test_ranging_device_nearby_share_profile_block_stride(self):
+ """Verifies nearby share profile with block stride."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ block_stride_length=0xFF,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ block_stride_length=0xFF,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("cf9d3a96-433e-40d6-a74e-5413c975da78")
+ def test_ranging_device_tracker_profile_no_aoa_report(self):
+ """Verifies ranging with device tracker profile with no aoa report."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ aoa_result_request=uwb_ranging_params.FiraParamEnums
+ .AOA_RESULT_REQUEST_MODE_NO_AOA_REPORT,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ aoa_result_request=uwb_ranging_params.FiraParamEnums
+ .AOA_RESULT_REQUEST_MODE_NO_AOA_REPORT,
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+ try:
+ self.initiator.get_aoa_azimuth_measurement(self.responder_addr)
+ asserts.fail("Received AoA measurement.")
+ except ValueError:
+ pass
+
+ @records.uid("68c6e10c-749f-448b-b650-a1edaa56a833")
+ def test_ranging_nearby_share_profile_hopping_mode_enabled(self):
+ """Verifies ranging with nearby share profile with hopping mode enabled."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ ranging_interval_ms=200,
+ hopping_mode=uwb_ranging_params.FiraParamEnums
+ .HOPPING_MODE_FIRA_HOPPING_ENABLE,
+ slots_per_ranging_round=20,
+ initiation_time_ms=100,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ ranging_interval_ms=200,
+ hopping_mode=uwb_ranging_params.FiraParamEnums
+ .HOPPING_MODE_FIRA_HOPPING_ENABLE,
+ slots_per_ranging_round=20,
+ initiation_time_ms=100,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("7c63291f-d610-4360-88c9-ae17c46cc0ba")
+ def test_ranging_rr_ss_twr_deferred_default_params(self):
+ """Verifies ranging with default Fira parameters and Ranging Round 1."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ ranging_round_usage=uwb_ranging_params.FiraParamEnums
+ .RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ ranging_round_usage=uwb_ranging_params.FiraParamEnums
+ .RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("cb404b61-626a-4bbe-87b2-a45bf1fa4174")
+ def test_ranging_rr_ss_twr_deferred_device_tracker_profile(self):
+ """Verifies ranging with device tracker profile and ranging round 1."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ ranging_round_usage=uwb_ranging_params.FiraParamEnums
+ .RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ ranging_round_usage=uwb_ranging_params.FiraParamEnums
+ .RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("1990b59c-dd57-4f24-8134-f1ccb2db3062")
+ def test_ranging_rr_ss_twr_deferred_nearby_share_profile(self):
+ """Verifies ranging for nearby share profile and ranging round 1."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ ranging_round_usage=uwb_ranging_params.FiraParamEnums
+ .RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ ranging_round_usage=uwb_ranging_params.FiraParamEnums
+ .RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE,
+ )
+ self._verify_one_to_one_ranging(self.initiator, self.responder,
+ initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("ae7a95b3-e8f1-4358-b3cc-ecb10cb6d20c")
+ def test_stop_initiator_ranging_device_tracker_profile(self):
+ """Verifies initiator stop ranging callbacks with device tracker profile."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_stop_initiator_callback(
+ self.initiator, self.responder, initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("cd339d74-910a-49e9-9201-b4868adf6db7")
+ def test_stop_initiator_ranging_nearby_share_profile(self):
+ """Verifies initiator stop ranging callbacks for nearby share profile."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_stop_initiator_callback(
+ self.initiator, self.responder, initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("c7757248-362f-4efa-95f4-344360e163ad")
+ def test_stop_responder_ranging_device_tracker_profile(self):
+ """Verifies responder stop ranging callbacks with device tracker profile."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_stop_responder_callback(
+ self.initiator, self.responder, initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("79f9dff3-6397-495f-9a35-bc1b21b7b500")
+ def test_stop_responder_ranging_nearby_share_profile(self):
+ """Verifies responder stop ranging callbacks for nearby share profile."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_stop_responder_callback(
+ self.initiator, self.responder, initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("8109b3b3-bd2d-4d0e-be70-844d98f4d6fb")
+ def test_ranging_device_tracker_profile_with_airplane_mode_toggle(self):
+ """Verifies ranging with device tracker profile and airplane mode toggle."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ multi_node_mode=uwb_ranging_params.FiraParamEnums
+ .MULTI_NODE_MODE_UNICAST,
+ initiation_time_ms=100,
+ ranging_interval_ms=240,
+ slots_per_ranging_round=6,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging_airplane_mode_toggle(
+ self.initiator, self.responder, initiator_params, responder_params,
+ self.responder_addr)
+
+ @records.uid("583d3c33-d41a-4d91-93ef-19dc5e064fb7")
+ def test_ranging_nearby_share_profile_with_airplane_mode_toggle(self):
+ """Verifies ranging for nearby share profile and APM toggle."""
+ initiator_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_INITIATOR,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLLER,
+ device_address=self.initiator_addr,
+ destination_addresses=[self.responder_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ responder_params = uwb_ranging_params.UwbRangingParams(
+ device_role=uwb_ranging_params.FiraParamEnums.DEVICE_ROLE_RESPONDER,
+ device_type=uwb_ranging_params.FiraParamEnums.DEVICE_TYPE_CONTROLEE,
+ device_address=self.responder_addr,
+ destination_addresses=[self.initiator_addr],
+ initiation_time_ms=100,
+ ranging_interval_ms=200,
+ slots_per_ranging_round=20,
+ in_band_termination_attempt_count=3,
+ )
+ self._verify_one_to_one_ranging_airplane_mode_toggle(
+ self.initiator, self.responder, initiator_params, responder_params,
+ self.responder_addr)
+
+if __name__ == "__main__":
+ index = sys.argv.index('--')
+ sys.argv = sys.argv[:1] + sys.argv[index + 1:]
+ test_runner.main()
diff --git a/tests/cts/hostsidetests/multidevices/uwb/snippet/Android.bp b/tests/cts/hostsidetests/multidevices/uwb/snippet/Android.bp
new file mode 100644
index 00000000..2ad7dd2e
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/snippet/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "uwb_snippet",
+ sdk_version: "system_current",
+ srcs: [
+ "UwbManagerSnippet.java",
+ ],
+ manifest: "AndroidManifest.xml",
+ static_libs: [
+ "androidx.test.runner",
+ "guava",
+ "mobly-snippet-lib",
+ "com.uwb.support.ccc",
+ "com.uwb.support.fira",
+ "com.uwb.support.generic",
+ "com.uwb.support.multichip",
+ ],
+}
diff --git a/tests/cts/hostsidetests/multidevices/uwb/snippet/AndroidManifest.xml b/tests/cts/hostsidetests/multidevices/uwb/snippet/AndroidManifest.xml
new file mode 100644
index 00000000..6b28be9b
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/snippet/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.snippet.uwb">
+
+ <uses-permission android:name="android.permissions.UWB_PRIVILEGED" />
+ <uses-permission android:name="android.permission.UWB_RANGING" />
+
+ <application>
+ <!-- Add any classes that implement the Snippet interface as meta-data, whose
+ value is a comma-separated string, each section being the package path
+ of a snippet class -->
+ <meta-data
+ android:name="mobly-snippets"
+ android:value="com.google.snippet.uwb.UwbManagerSnippet" />
+ </application>
+ <!-- Add an instrumentation tag so that the app can be launched through an
+ instrument command. The runner `com.google.android.mobly.snippet.SnippetRunner`
+ is derived from `AndroidJUnitRunner`, and is required to use the
+ Mobly Snippet Lib. -->
+ <instrumentation
+ android:name="com.google.android.mobly.snippet.SnippetRunner"
+ android:targetPackage="com.google.snippet.uwb" />
+</manifest>
diff --git a/tests/cts/hostsidetests/multidevices/uwb/snippet/UwbManagerSnippet.java b/tests/cts/hostsidetests/multidevices/uwb/snippet/UwbManagerSnippet.java
new file mode 100644
index 00000000..484d8cbd
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/snippet/UwbManagerSnippet.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 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.snippet.uwb;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.uwb.UwbManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.google.android.mobly.snippet.Snippet;
+import com.google.android.mobly.snippet.rpc.Rpc;
+import com.google.android.mobly.snippet.util.Log;
+
+import java.lang.reflect.Method;
+
+/** Snippet class exposing Android APIs for Uwb. */
+public class UwbManagerSnippet implements Snippet {
+ private static class UwbManagerSnippetException extends Exception {
+ private static final long serialVersionUID = 1;
+
+ UwbManagerSnippetException(String msg) {
+ super(msg);
+ }
+
+ UwbManagerSnippetException(String msg, Throwable err) {
+ super(msg, err);
+ }
+ }
+ private final UwbManager mUwbManager;
+ private final Context mContext;
+
+ public UwbManagerSnippet() throws Throwable {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mUwbManager = mContext.getSystemService(UwbManager.class);
+ adoptShellPermission();
+ }
+
+ /** Get the UWB state. */
+ @Rpc(description = "Get Uwb state")
+ public boolean isUwbEnabled() {
+ return mUwbManager.isUwbEnabled();
+ }
+
+ /** Get the UWB state. */
+ @Rpc(description = "Set Uwb state")
+ public void setUwbEnabled(boolean enabled) {
+ mUwbManager.setUwbEnabled(enabled);
+ }
+
+ @Override
+ public void shutdown() {}
+
+ private void adoptShellPermission() throws Throwable {
+ UiAutomation uia = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ uia.adoptShellPermissionIdentity();
+ try {
+ Class<?> cls = Class.forName("android.app.UiAutomation");
+ Method destroyMethod = cls.getDeclaredMethod("destroy");
+ destroyMethod.invoke(uia);
+ } catch (ReflectiveOperationException e) {
+ throw new UwbManagerSnippetException("Failed to cleaup Ui Automation", e);
+ }
+ }
+}
diff --git a/tests/cts/hostsidetests/multidevices/uwb/test_utils/uwb_test_utils.py b/tests/cts/hostsidetests/multidevices/uwb/test_utils/uwb_test_utils.py
new file mode 100644
index 00000000..341bd396
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/test_utils/uwb_test_utils.py
@@ -0,0 +1,155 @@
+"""Test utils for UWB."""
+
+import logging
+import random
+import time
+from typing import List
+from mobly import asserts
+from mobly.controllers import android_device
+
+from lib import uwb_ranging_decorator
+
+
+WAIT_TIME_SEC = 3
+
+
+def _get_uwb_state_callback_status(ad: android_device.AndroidDevice,
+ uwb_event: str) -> bool:
+ """Checks if expected callback is received.
+
+ Args:
+ ad: android device object.
+ uwb_event: uwb state callback.
+
+ Returns:
+ True if expected callback is received, False if not.
+ """
+ callback_key = "uwb_state_%s" % random.randint(1, 100)
+ handler = ad.uwb.registerUwbAdapterStateCallback(callback_key)
+ try:
+ event = handler.waitAndGet("UwbAdapterStateCallback", timeout=WAIT_TIME_SEC)
+ event_received = event.data["uwbAdapterStateEvent"]
+ logging.debug("Received event - %s", event_received)
+ status = event_received == uwb_event
+ except TimeoutError as t:
+ raise Exception("Event 'UwbAdapterStateCallback' not received.") from t
+ finally:
+ ad.uwb.unregisterUwbAdapterStateCallback(callback_key)
+ return status
+
+
+def verify_uwb_state_callback(ad: android_device.AndroidDevice,
+ uwb_event: str,
+ timeout: int = WAIT_TIME_SEC) -> bool:
+ """Verifies expected UWB callback is received.
+
+ Args:
+ ad: android device object.
+ uwb_event: expected callback event.
+ timeout: timeout for callback event.
+
+ Returns:
+ True if expected callback is received, False if not.
+ """
+ callback_status = False
+ start_time = time.time()
+ while time.time() - start_time < timeout:
+ time.sleep(0.1)
+ if _get_uwb_state_callback_status(ad, uwb_event):
+ logging.debug("Received the '%s' callback in %ss", uwb_event,
+ round(time.time() - start_time, 2))
+ callback_status = True
+ break
+ return callback_status
+
+
+def get_uwb_state(ad: android_device.AndroidDevice):
+ """Gets the current UWB state.
+
+ Args:
+ ad: android device object.
+
+ Returns:
+ UWB state, True if enabled, False if not.
+ """
+ if ad.build_info["build_id"].startswith("S"):
+ uwb_state = bool(ad.uwb.getAdapterState())
+ else:
+ uwb_state = ad.uwb.isUwbEnabled()
+ return uwb_state
+
+
+def set_uwb_state_and_verify(ad: android_device.AndroidDevice, state: bool):
+ """Sets UWB state to on or off and verifies it.
+
+ Args:
+ ad: android device object.
+ state: bool, True for UWB on, False for off.
+ """
+ failure_msg = "enabled" if state else "disabled"
+ ad.uwb.setUwbEnabled(state)
+ event_str = "Inactive" if state else "Disabled"
+ asserts.assert_true(verify_uwb_state_callback(ad, event_str),
+ "Uwb is not %s" % failure_msg)
+
+
+def verify_peer_found(ranging_dut: uwb_ranging_decorator.UwbRangingDecorator,
+ peer_addr: List[int]):
+ """Verifies if the UWB peer is found.
+
+ Args:
+ ranging_dut: uwb ranging device.
+ peer_addr: uwb peer device address.
+ """
+ start_time = time.time()
+ while not ranging_dut.is_uwb_peer_found(peer_addr):
+ if time.time() - start_time > WAIT_TIME_SEC:
+ asserts.fail("UWB peer with address %s not found" % peer_addr)
+ logging.info("Peer %s found in %s seconds", peer_addr,
+ round(time.time() - start_time, 2))
+
+
+def set_airplane_mode(ad: android_device.AndroidDevice, state: bool):
+ """Sets the airplane mode to the given state.
+
+ Args:
+ ad: android device object.
+ state: bool, True for Airplane mode on, False for off.
+ """
+ ad.adb.shell(
+ ["settings", "put", "global", "airplane_mode_on",
+ str(int(state))])
+ ad.adb.shell([
+ "am", "broadcast", "-a", "android.intent.action.AIRPLANE_MODE", "--ez",
+ "state",
+ str(state)
+ ])
+ start_time = time.time()
+ while get_airplane_mode(ad) != state:
+ time.sleep(0.5)
+ if time.time() - start_time > WAIT_TIME_SEC:
+ asserts.fail("Failed to set airplane mode to: %s" % state)
+
+
+def get_airplane_mode(ad: android_device.AndroidDevice) -> bool:
+ """Gets the airplane mode.
+
+ Args:
+ ad: android device object.
+
+ Returns:
+ True if airplane mode On, False for Off.
+ """
+ state = ad.adb.shell(["settings", "get", "global", "airplane_mode_on"])
+ return bool(int(state.decode().strip()))
+
+
+def set_screen_rotation(ad: android_device.AndroidDevice, val: int):
+ """Sets screen orientation to landscape or portrait mode.
+
+ Args:
+ ad: android device object.
+ val: False for potrait, True 1 for landscape mode.
+ """
+ ad.adb.shell(["settings", "put", "system", "accelerometer_rotation", "0"])
+ ad.adb.shell(["settings", "put", "system", "user_rotation", str(val)])
diff --git a/tests/cts/hostsidetests/multidevices/uwb/uwb_manager_test.py b/tests/cts/hostsidetests/multidevices/uwb/uwb_manager_test.py
new file mode 100644
index 00000000..a107d988
--- /dev/null
+++ b/tests/cts/hostsidetests/multidevices/uwb/uwb_manager_test.py
@@ -0,0 +1,152 @@
+"""Tests for UwbManager APIs."""
+
+import sys
+
+from mobly import asserts
+from mobly import config_parser
+from mobly import test_runner
+from mobly import records
+from mobly.controllers import android_device
+
+from lib import uwb_base_test
+from test_utils import uwb_test_utils
+
+
+_TEST_CASES = (
+ "test_default_uwb_state",
+ "test_disable_uwb_state",
+ "test_enable_uwb_state",
+ # "test_uwb_state_after_reboot_with_uwb_off",
+ # "test_uwb_state_after_reboot_with_uwb_on",
+ "test_uwb_state_with_airplane_mode_on",
+ "test_toggle_uwb_state_with_airplane_mode_on",
+ "test_uwb_state_with_airplane_mode_off",
+ "test_uwb_state_off_with_airplane_mode_toggle",
+)
+
+
+class UwbManagerTest(uwb_base_test.UwbBaseTest):
+ """Tests for UwbManager platform APIs.
+
+ Attributes:
+ android_devices: list of android device objects.
+ """
+
+ def __init__(self, configs: config_parser.TestRunConfig):
+ """Init method for the test class.
+
+ Args:
+ configs: A config_parser.TestRunConfig object.
+ """
+ super().__init__(configs)
+ self.tests = _TEST_CASES
+
+ def setup_class(self):
+ super().setup_class()
+ self.dut = self.android_devices[0]
+
+ def teardown_class(self):
+ super().teardown_class()
+ self.dut.take_bug_report(destination=self.current_test_info.output_path)
+
+ ### Helper methods ###
+
+ def _test_uwb_state_after_reboot(self, dut: android_device.AndroidDevice,
+ state: bool):
+ """Sets UWB state and verifies it is persistent after reboot.
+
+ Args:
+ dut: android device object.
+ state: bool, True for UWB mode on, False for off.
+ """
+ uwb_test_utils.set_uwb_state_and_verify(dut, state)
+ dut.reboot()
+ dut.adb.shell(["cmd", "uwb", "force-country-code", "enabled", "US"])
+ state_after_reboot = uwb_test_utils.get_uwb_state(dut)
+ asserts.assert_equal(
+ state, state_after_reboot,
+ "Uwb state before reboot: %s; after reboot: %s" %
+ (state, state_after_reboot))
+
+ ### Test Cases ###
+
+ @records.uid("2d2aff95-0273-478c-be85-097085db74cd")
+ def test_default_uwb_state(self):
+ """Verifies default UWB state is On after flashing the device."""
+ asserts.assert_true(uwb_test_utils.get_uwb_state(self.dut),
+ "UWB state: Off; Expected: On.")
+
+ @records.uid("a6a69945-39e8-404f-8d15-d87857cdf5af")
+ def test_disable_uwb_state(self):
+ """Disables and verifies UWB state."""
+ uwb_test_utils.set_uwb_state_and_verify(self.dut, False)
+
+ @records.uid("a9283694-aa0b-4032-9701-f45bb15cb0fa")
+ def test_enable_uwb_state(self):
+ """Enables and verifies UWB state."""
+ uwb_test_utils.set_uwb_state_and_verify(self.dut, True)
+
+ @records.uid("cda92ecc-c04f-46c6-b433-a5fc30fbd822")
+ def test_uwb_state_after_reboot_with_uwb_off(self):
+ """Sets UWB state to off and verifies it is persistent after reboot."""
+ self._test_uwb_state_after_reboot(self.dut, False)
+
+ @records.uid("6d8c4e95-57d6-4d97-9838-ab5c492b6f1c")
+ def test_uwb_state_after_reboot_with_uwb_on(self):
+ """Sets UWB state to on and verifies it is persistent after reboot."""
+ self._test_uwb_state_after_reboot(self.dut, True)
+
+ @records.uid("a9d2b85a-895f-48ad-87a0-aebd19b45264")
+ def test_uwb_state_with_airplane_mode_on(self):
+ """Verifies UWB is disabled with airplane mode on."""
+ uwb_test_utils.set_airplane_mode(self.dut, True)
+ asserts.assert_true(
+ uwb_test_utils.verify_uwb_state_callback(self.dut, "Disabled"),
+ "UWB is not disabled with airplane mode On.")
+
+ @records.uid("29886f5b-5f91-4ffa-99e8-6d90c21d70de")
+ def test_toggle_uwb_state_with_airplane_mode_on(self):
+ """Verifies UWB cannot be turned on with airplane mode On."""
+ uwb_test_utils.set_airplane_mode(self.dut, True)
+ asserts.assert_true(
+ uwb_test_utils.verify_uwb_state_callback(self.dut, "Disabled"),
+ "UWB is not disabled with airplane mode On.")
+ self.dut.uwb.setUwbEnabled(True)
+ asserts.assert_true(
+ uwb_test_utils.verify_uwb_state_callback(self.dut, "Disabled"),
+ "Enabling UWB with airplane mode On should not work.")
+
+ @records.uid("f1008ca6-88d6-4fad-8b88-7dea4f331810")
+ def test_uwb_state_with_airplane_mode_off(self):
+ """Verifies UWB is disabled with airplane mode off."""
+ uwb_test_utils.set_airplane_mode(self.dut, False)
+ asserts.assert_true(
+ uwb_test_utils.verify_uwb_state_callback(self.dut, "Inactive"),
+ "UWB is not enabled with airplane mode Off.")
+
+ @records.uid("dd3a7c5c-25b2-4c86-8ed1-2b6ff246b7b6")
+ def test_uwb_state_off_with_airplane_mode_toggle(self):
+ """Verifies UWB disabled state is persistent with airplane mode toggle."""
+
+ # disable UWB
+ uwb_test_utils.set_uwb_state_and_verify(self.dut, False)
+
+ # enable airplane mode and verify UWB is disabled.
+ uwb_test_utils.set_airplane_mode(self.dut, True)
+ asserts.assert_true(
+ uwb_test_utils.verify_uwb_state_callback(self.dut, "Disabled"),
+ "UWB is not disabled with airplane mode On.")
+
+ # disable airplane mode and verify UWB is disabled.
+ uwb_test_utils.set_airplane_mode(self.dut, False)
+ asserts.assert_false(
+ uwb_test_utils.verify_uwb_state_callback(self.dut, "Inactive"),
+ "UWB state: On, Expected state: Off")
+
+ # enable UWB
+ uwb_test_utils.set_uwb_state_and_verify(self.dut, True)
+
+if __name__ == "__main__":
+ index = sys.argv.index('--')
+ sys.argv = sys.argv[:1] + sys.argv[index + 1:]
+ test_runner.main()
diff --git a/tests/cts/tests/Android.bp b/tests/cts/tests/Android.bp
new file mode 100644
index 00000000..306606fb
--- /dev/null
+++ b/tests/cts/tests/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsUwbTestCases",
+ defaults: [
+ "cts_defaults",
+ ],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-uwb",
+ ],
+ libs: ["android.test.runner"],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ "mockito-target-minus-junit4",
+ "com.uwb.support.fira",
+ "com.uwb.support.multichip",
+ "com.uwb.support.oemextension",
+ "com.uwb.support.dltdoa",
+ ],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+}
diff --git a/tests/cts/tests/AndroidManifest.xml b/tests/cts/tests/AndroidManifest.xml
new file mode 100644
index 00000000..adecade4
--- /dev/null
+++ b/tests/cts/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.uwb.cts">
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for android.uwb"
+ android:targetPackage="android.uwb.cts" >
+ </instrumentation>
+</manifest>
+
diff --git a/tests/cts/tests/AndroidTest.xml b/tests/cts/tests/AndroidTest.xml
new file mode 100644
index 00000000..3104ec54
--- /dev/null
+++ b/tests/cts/tests/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<configuration description="Config for CTS UWB test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.uwb.apex" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsUwbTestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.uwb.cts" />
+ </test>
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.uwb" />
+ </object>
+</configuration>
diff --git a/tests/cts/tests/OWNERS b/tests/cts/tests/OWNERS
new file mode 100644
index 00000000..c4ad4164
--- /dev/null
+++ b/tests/cts/tests/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1042770
+include platform/packages/modules/Uwb:/OWNERS
diff --git a/tests/cts/tests/src/android/uwb/cts/AngleMeasurementTest.java b/tests/cts/tests/src/android/uwb/cts/AngleMeasurementTest.java
new file mode 100644
index 00000000..f96b7988
--- /dev/null
+++ b/tests/cts/tests/src/android/uwb/cts/AngleMeasurementTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2021 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.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.uwb.AngleMeasurement;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link AngleMeasurement}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AngleMeasurementTest {
+ @Test
+ public void testConstructs() {
+ double radians = 0.1234;
+ double errorRadians = 0.5678;
+ double confidence = 0.5;
+
+ AngleMeasurement measurement = new AngleMeasurement(radians, errorRadians, confidence);
+ assertEquals(measurement.getRadians(), radians, 0);
+ assertEquals(measurement.getErrorRadians(), errorRadians, 0);
+ assertEquals(measurement.getConfidenceLevel(), confidence, 0);
+ }
+
+ @Test
+ public void testInvalidRadians() {
+ double radians = Math.PI + 0.01;
+ double errorRadians = 0.5678;
+ double confidence = 0.5;
+
+ constructExpectFailure(radians, errorRadians, confidence);
+ constructExpectFailure(-radians, errorRadians, confidence);
+ }
+
+ @Test
+ public void testInvalidErrorRadians() {
+ double radians = 0.1234;
+ double confidence = 0.5;
+
+ constructExpectFailure(radians, -0.01, confidence);
+ constructExpectFailure(-radians, Math.PI + 0.01, confidence);
+ }
+
+ @Test
+ public void testInvalidConfidence() {
+ double radians = 0.1234;
+ double errorRadians = 0.5678;
+
+ constructExpectFailure(radians, errorRadians, -0.01);
+ constructExpectFailure(radians, errorRadians, 1.01);
+ }
+
+ private void constructExpectFailure(double radians, double errorRadians, double confidence) {
+ try {
+ new AngleMeasurement(radians, errorRadians, confidence);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ AngleMeasurement measurement = UwbTestUtils.getAngleMeasurement();
+ measurement.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AngleMeasurement fromParcel = AngleMeasurement.CREATOR.createFromParcel(parcel);
+ assertEquals(measurement, fromParcel);
+ }
+}
diff --git a/tests/cts/tests/src/android/uwb/cts/AngleOfArrivalMeasurementTest.java b/tests/cts/tests/src/android/uwb/cts/AngleOfArrivalMeasurementTest.java
new file mode 100644
index 00000000..085ce2e5
--- /dev/null
+++ b/tests/cts/tests/src/android/uwb/cts/AngleOfArrivalMeasurementTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 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.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.uwb.AngleMeasurement;
+import android.uwb.AngleOfArrivalMeasurement;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link AngleOfArrivalMeasurement}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AngleOfArrivalMeasurementTest {
+
+ @Test
+ public void testBuilder() {
+ AngleMeasurement azimuth = UwbTestUtils.getAngleMeasurement();
+ AngleMeasurement altitude = UwbTestUtils.getAngleMeasurement();
+
+ AngleOfArrivalMeasurement.Builder builder = new AngleOfArrivalMeasurement.Builder(azimuth);
+ builder.setAltitude(altitude);
+
+ AngleOfArrivalMeasurement measurement = tryBuild(builder, true);
+
+ assertEquals(azimuth, measurement.getAzimuth());
+ assertEquals(altitude, measurement.getAltitude());
+ }
+
+ private AngleOfArrivalMeasurement tryBuild(AngleOfArrivalMeasurement.Builder builder,
+ boolean expectSuccess) {
+ AngleOfArrivalMeasurement measurement = null;
+ try {
+ measurement = builder.build();
+ if (!expectSuccess) {
+ fail("Expected AngleOfArrivalMeasurement.Builder.build() to fail");
+ }
+ } catch (IllegalStateException e) {
+ if (expectSuccess) {
+ fail("Expected AngleOfArrivalMeasurement.Builder.build() to succeed");
+ }
+ }
+ return measurement;
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ AngleOfArrivalMeasurement measurement = UwbTestUtils.getAngleOfArrivalMeasurement();
+ measurement.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AngleOfArrivalMeasurement fromParcel =
+ AngleOfArrivalMeasurement.CREATOR.createFromParcel(parcel);
+ assertEquals(measurement, fromParcel);
+ }
+}
diff --git a/tests/cts/tests/src/android/uwb/cts/DistanceMeasurementTest.java b/tests/cts/tests/src/android/uwb/cts/DistanceMeasurementTest.java
new file mode 100644
index 00000000..fdebc78d
--- /dev/null
+++ b/tests/cts/tests/src/android/uwb/cts/DistanceMeasurementTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021 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.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.uwb.DistanceMeasurement;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link DistanceMeasurement}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DistanceMeasurementTest {
+ private static final double EPSILON = 0.00000000001;
+
+ @Test
+ public void testBuilder() {
+ double meters = 0.12;
+ double error = 0.54;
+ double confidence = 0.99;
+
+ DistanceMeasurement.Builder builder = new DistanceMeasurement.Builder();
+ tryBuild(builder, false);
+
+ builder.setMeters(meters);
+ tryBuild(builder, false);
+
+ builder.setErrorMeters(error);
+ tryBuild(builder, false);
+
+ builder.setConfidenceLevel(confidence);
+ DistanceMeasurement measurement = tryBuild(builder, true);
+
+ assertEquals(meters, measurement.getMeters(), 0);
+ assertEquals(error, measurement.getErrorMeters(), 0);
+ assertEquals(confidence, measurement.getConfidenceLevel(), 0);
+ }
+
+ private DistanceMeasurement tryBuild(DistanceMeasurement.Builder builder,
+ boolean expectSuccess) {
+ DistanceMeasurement measurement = null;
+ try {
+ measurement = builder.build();
+ if (!expectSuccess) {
+ fail("Expected DistanceMeasurement.Builder.build() to fail");
+ }
+ } catch (IllegalStateException e) {
+ if (expectSuccess) {
+ fail("Expected DistanceMeasurement.Builder.build() to succeed");
+ }
+ }
+ return measurement;
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ DistanceMeasurement measurement = UwbTestUtils.getDistanceMeasurement();
+ measurement.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ DistanceMeasurement fromParcel =
+ DistanceMeasurement.CREATOR.createFromParcel(parcel);
+ assertEquals(measurement, fromParcel);
+ }
+}
diff --git a/tests/cts/tests/src/android/uwb/cts/RangingMeasurementTest.java b/tests/cts/tests/src/android/uwb/cts/RangingMeasurementTest.java
new file mode 100644
index 00000000..5c0a23cc
--- /dev/null
+++ b/tests/cts/tests/src/android/uwb/cts/RangingMeasurementTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 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.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.uwb.AngleOfArrivalMeasurement;
+import android.uwb.DistanceMeasurement;
+import android.uwb.RangingMeasurement;
+import android.uwb.UwbAddress;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link RangingMeasurement}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RangingMeasurementTest {
+ private static final int TEST_RSSI_DBM = -80;
+ private static final int INVALID_RSSI_DBM = -129;
+
+ @Test
+ public void testBuilder() {
+ int status = RangingMeasurement.RANGING_STATUS_SUCCESS;
+ UwbAddress address = UwbTestUtils.getUwbAddress(false);
+ long time = SystemClock.elapsedRealtimeNanos();
+ AngleOfArrivalMeasurement angleMeasurement = UwbTestUtils.getAngleOfArrivalMeasurement();
+ AngleOfArrivalMeasurement destinationAngleMeasurement =
+ UwbTestUtils.getAngleOfArrivalMeasurement();
+ DistanceMeasurement distanceMeasurement = UwbTestUtils.getDistanceMeasurement();
+ int los = RangingMeasurement.NLOS;
+ int measurementFocus = RangingMeasurement.MEASUREMENT_FOCUS_RANGE;
+
+ RangingMeasurement.Builder builder = new RangingMeasurement.Builder();
+
+ builder.setStatus(status);
+ tryBuild(builder, false);
+
+ builder.setElapsedRealtimeNanos(time);
+ tryBuild(builder, false);
+
+ builder.setAngleOfArrivalMeasurement(angleMeasurement);
+ tryBuild(builder, false);
+
+ builder.setDestinationAngleOfArrivalMeasurement(destinationAngleMeasurement);
+ tryBuild(builder, false);
+
+ builder.setDistanceMeasurement(distanceMeasurement);
+ tryBuild(builder, false);
+
+ builder.setRssiDbm(TEST_RSSI_DBM);
+ tryBuild(builder, false);
+
+ builder.setRemoteDeviceAddress(address);
+ tryBuild(builder, true);
+
+ builder.setLineOfSight(los);
+ tryBuild(builder, true);
+
+ builder.setMeasurementFocus(measurementFocus);
+ RangingMeasurement measurement = tryBuild(builder, true);
+
+ assertEquals(status, measurement.getStatus());
+ assertEquals(address, measurement.getRemoteDeviceAddress());
+ assertEquals(time, measurement.getElapsedRealtimeNanos());
+ assertEquals(angleMeasurement, measurement.getAngleOfArrivalMeasurement());
+ assertEquals(destinationAngleMeasurement,
+ measurement.getDestinationAngleOfArrivalMeasurement());
+ assertEquals(distanceMeasurement, measurement.getDistanceMeasurement());
+ assertEquals(los, measurement.getLineOfSight());
+ assertEquals(measurementFocus, measurement.getMeasurementFocus());
+ assertEquals(TEST_RSSI_DBM, measurement.getRssiDbm());
+ }
+
+ @Test
+ public void testInvalidRssi() {
+ RangingMeasurement.Builder builder = new RangingMeasurement.Builder();
+ try {
+ builder.setRssiDbm(INVALID_RSSI_DBM);
+ fail("Expected RangingMeasurement.Builder.setRssiDbm() to fail");
+ } catch (Exception e) {
+ assertTrue(e.getMessage().contains("Invalid"));
+ }
+ }
+
+ private RangingMeasurement tryBuild(RangingMeasurement.Builder builder,
+ boolean expectSuccess) {
+ RangingMeasurement measurement = null;
+ try {
+ measurement = builder.build();
+ if (!expectSuccess) {
+ fail("Expected RangingMeasurement.Builder.build() to fail");
+ }
+ } catch (IllegalStateException e) {
+ if (expectSuccess) {
+ fail("Expected DistanceMeasurement.Builder.build() to succeed");
+ }
+ }
+ return measurement;
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ RangingMeasurement measurement = UwbTestUtils.getRangingMeasurement();
+ measurement.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ RangingMeasurement fromParcel = RangingMeasurement.CREATOR.createFromParcel(parcel);
+ assertEquals(measurement, fromParcel);
+ }
+}
diff --git a/tests/cts/tests/src/android/uwb/cts/RangingReportTest.java b/tests/cts/tests/src/android/uwb/cts/RangingReportTest.java
new file mode 100644
index 00000000..af4ebcb0
--- /dev/null
+++ b/tests/cts/tests/src/android/uwb/cts/RangingReportTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2021 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.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.uwb.RangingMeasurement;
+import android.uwb.RangingReport;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Test of {@link RangingReport}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RangingReportTest {
+
+ @Test
+ public void testBuilder() {
+ List<RangingMeasurement> measurements = UwbTestUtils.getRangingMeasurements(5);
+
+ RangingReport.Builder builder = new RangingReport.Builder();
+ builder.addMeasurements(measurements);
+ RangingReport report = tryBuild(builder, true);
+ verifyMeasurementsEqual(measurements, report.getMeasurements());
+ builder = new RangingReport.Builder();
+ for (RangingMeasurement measurement : measurements) {
+ builder.addMeasurement(measurement);
+ }
+ report = tryBuild(builder, true);
+ verifyMeasurementsEqual(measurements, report.getMeasurements());
+ }
+
+ private void verifyMeasurementsEqual(List<RangingMeasurement> expected,
+ List<RangingMeasurement> actual) {
+ assertEquals(expected.size(), actual.size());
+ for (int i = 0; i < expected.size(); i++) {
+ assertEquals(expected.get(i), actual.get(i));
+ }
+ }
+
+ private RangingReport tryBuild(RangingReport.Builder builder,
+ boolean expectSuccess) {
+ RangingReport report = null;
+ try {
+ report = builder.build();
+ if (!expectSuccess) {
+ fail("Expected RangingReport.Builder.build() to fail");
+ }
+ } catch (IllegalStateException e) {
+ if (expectSuccess) {
+ fail("Expected RangingReport.Builder.build() to succeed");
+ }
+ }
+ return report;
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ RangingReport report = UwbTestUtils.getRangingReports(5);
+ report.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ RangingReport fromParcel = RangingReport.CREATOR.createFromParcel(parcel);
+ assertEquals(report, fromParcel);
+ }
+}
diff --git a/tests/cts/tests/src/android/uwb/cts/UwbAddressTest.java b/tests/cts/tests/src/android/uwb/cts/UwbAddressTest.java
new file mode 100644
index 00000000..d2f42286
--- /dev/null
+++ b/tests/cts/tests/src/android/uwb/cts/UwbAddressTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 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.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.uwb.UwbAddress;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link UwbAddress}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class UwbAddressTest {
+
+ @Test
+ public void testFromBytes_Short() {
+ runFromBytes(UwbAddress.SHORT_ADDRESS_BYTE_LENGTH);
+ }
+
+ @Test
+ public void testFromBytes_Extended() {
+ runFromBytes(UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH);
+ }
+
+ private void runFromBytes(int len) {
+ byte[] addressBytes = getByteArray(len);
+ UwbAddress address = UwbAddress.fromBytes(addressBytes);
+ assertEquals(address.size(), len);
+ assertEquals(addressBytes, address.toBytes());
+ }
+
+ private byte[] getByteArray(int len) {
+ byte[] res = new byte[len];
+ for (int i = 0; i < len; i++) {
+ res[i] = (byte) i;
+ }
+ return res;
+ }
+
+ @Test
+ public void testParcel_Short() {
+ runParcel(true);
+ }
+
+ @Test
+ public void testParcel_Extended() {
+ runParcel(false);
+ }
+
+ private void runParcel(boolean useShortAddress) {
+ Parcel parcel = Parcel.obtain();
+ UwbAddress address = UwbTestUtils.getUwbAddress(useShortAddress);
+ address.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ UwbAddress fromParcel = UwbAddress.CREATOR.createFromParcel(parcel);
+ assertEquals(address, fromParcel);
+ }
+}
diff --git a/tests/cts/tests/src/android/uwb/cts/UwbFrameworkInitializerTest.java b/tests/cts/tests/src/android/uwb/cts/UwbFrameworkInitializerTest.java
new file mode 100644
index 00000000..ddd22c6c
--- /dev/null
+++ b/tests/cts/tests/src/android/uwb/cts/UwbFrameworkInitializerTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 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.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.platform.test.annotations.AppModeFull;
+import android.uwb.UwbFrameworkInitializer;
+import android.uwb.UwbManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+/**
+ * Test of {@link UwbManager}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Cannot get UwbManager in instant app mode")
+public class UwbFrameworkInitializerTest {
+ private final Context mContext = InstrumentationRegistry.getContext();
+ private UwbManager mUwbManager;
+
+ @Before
+ public void setup() throws Exception {
+ mUwbManager = mContext.getSystemService(UwbManager.class);
+ assumeTrue(UwbTestUtils.isUwbSupported(mContext));
+ assertThat(mUwbManager).isNotNull();
+ }
+
+ /**
+ * UwbFrameworkInitializer.registerServiceWrappers() should only be called by
+ * SystemServiceRegistry during boot up when Uwb is first initialized. Calling this API at
+ * any other time should throw an exception.
+ */
+ @Test
+ public void testRegisterServiceWrappers_failsWhenCalledOutsideOfSystemServiceRegistry() {
+ try {
+ UwbFrameworkInitializer.registerServiceWrappers();
+ fail("Expected exception when calling "
+ + "UwbFrameworkInitializer.registerServiceWrappers() outside of "
+ + "SystemServiceRegistry!");
+ } catch (IllegalStateException expected) { }
+ }
+}
diff --git a/tests/cts/tests/src/android/uwb/cts/UwbManagerTest.java b/tests/cts/tests/src/android/uwb/cts/UwbManagerTest.java
new file mode 100644
index 00000000..731d8944
--- /dev/null
+++ b/tests/cts/tests/src/android/uwb/cts/UwbManagerTest.java
@@ -0,0 +1,1175 @@
+/*
+ * Copyright 2021 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.cts;
+
+import static android.Manifest.permission.UWB_PRIVILEGED;
+import static android.Manifest.permission.UWB_RANGING;
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_DISABLED;
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE;
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.UiAutomation;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.content.ContextParams;
+import android.os.CancellationSignal;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.permission.PermissionManager;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+import android.uwb.RangingReport;
+import android.uwb.RangingSession;
+import android.uwb.UwbAddress;
+import android.uwb.UwbManager;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+import com.google.uwb.support.fira.FiraControleeParams;
+import com.google.uwb.support.fira.FiraOpenSessionParams;
+import com.google.uwb.support.fira.FiraParams;
+import com.google.uwb.support.fira.FiraProtocolVersion;
+import com.google.uwb.support.fira.FiraSpecificationParams;
+import com.google.uwb.support.multichip.ChipInfoParams;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test of {@link UwbManager}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Cannot get UwbManager in instant app mode")
+public class UwbManagerTest {
+ private static final String TAG = "UwbManagerTest";
+
+ private final Context mContext = InstrumentationRegistry.getContext();
+ private UwbManager mUwbManager;
+ private String mDefaultChipId;
+ public static final int UWB_SESSION_STATE_IDLE = 0x03;
+ public static final byte DEVICE_STATE_ACTIVE = 0x02;
+ public static final int REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS = 0x00;
+
+ @Before
+ public void setup() throws Exception {
+ mUwbManager = mContext.getSystemService(UwbManager.class);
+ assumeTrue(UwbTestUtils.isUwbSupported(mContext));
+ assertThat(mUwbManager).isNotNull();
+
+ // Ensure UWB is toggled on.
+ ShellIdentityUtils.invokeWithShellPermissions(() -> {
+ if (!mUwbManager.isUwbEnabled()) {
+ try {
+ setUwbEnabledAndWaitForCompletion(true);
+ } catch (Exception e) {
+ fail("Exception while processing UWB toggle " + e);
+ }
+ }
+ mDefaultChipId = mUwbManager.getDefaultChipId();
+ });
+ }
+
+ // Should be invoked with shell permissions.
+ private void setUwbEnabledAndWaitForCompletion(boolean enabled) throws Exception {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ int adapterState = enabled ? STATE_ENABLED_INACTIVE : STATE_DISABLED;
+ AdapterStateCallback adapterStateCallback =
+ new AdapterStateCallback(countDownLatch, adapterState);
+ try {
+ mUwbManager.registerAdapterStateCallback(
+ Executors.newSingleThreadExecutor(), adapterStateCallback);
+ mUwbManager.setUwbEnabled(enabled);
+ assertThat(countDownLatch.await(2, TimeUnit.SECONDS)).isTrue();
+ assertThat(mUwbManager.isUwbEnabled()).isEqualTo(enabled);
+ assertThat(adapterStateCallback.state).isEqualTo(adapterState);
+ } finally {
+ mUwbManager.unregisterAdapterStateCallback(adapterStateCallback);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testGetSpecificationInfo() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ PersistableBundle persistableBundle = mUwbManager.getSpecificationInfo();
+ assertThat(persistableBundle).isNotNull();
+ assertThat(persistableBundle.isEmpty()).isFalse();
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testGetSpecificationInfoWithChipId() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ PersistableBundle persistableBundle =
+ mUwbManager.getSpecificationInfo(mDefaultChipId);
+ assertThat(persistableBundle).isNotNull();
+ assertThat(persistableBundle.isEmpty()).isFalse();
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testGetChipInfos() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ List<PersistableBundle> chipInfos = mUwbManager.getChipInfos();
+ assertThat(chipInfos).hasSize(1);
+ ChipInfoParams chipInfoParams = ChipInfoParams.fromBundle(chipInfos.get(0));
+ assertThat(chipInfoParams.getChipId()).isEqualTo(mDefaultChipId);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testGetSpecificationInfoWithInvalidChipId() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ assertThrows(IllegalArgumentException.class,
+ () -> mUwbManager.getSpecificationInfo("invalidChipId"));
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testGetSpecificationInfoWithoutUwbPrivileged() {
+ try {
+ mUwbManager.getSpecificationInfo();
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testGetSpecificationInfoWithChipIdWithoutUwbPrivileged() {
+ try {
+ mUwbManager.getSpecificationInfo(mDefaultChipId);
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testElapsedRealtimeResolutionNanos() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ assertThat(mUwbManager.elapsedRealtimeResolutionNanos() >= 0L).isTrue();
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testElapsedRealtimeResolutionNanosWithChipId() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ assertThat(mUwbManager.elapsedRealtimeResolutionNanos(mDefaultChipId) >= 0L)
+ .isTrue();
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testElapsedRealtimeResolutionNanosWithInvalidChipId() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ assertThrows(IllegalArgumentException.class,
+ () -> mUwbManager.elapsedRealtimeResolutionNanos("invalidChipId"));
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testElapsedRealtimeResolutionNanosWithoutUwbPrivileged() {
+ try {
+ mUwbManager.elapsedRealtimeResolutionNanos();
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testElapsedRealtimeResolutionNanosWithChipIdWithoutUwbPrivileged() {
+ try {
+ mUwbManager.elapsedRealtimeResolutionNanos(mDefaultChipId);
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testAddServiceProfileWithoutUwbPrivileged() {
+ try {
+ mUwbManager.addServiceProfile(new PersistableBundle());
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testRemoveServiceProfileWithoutUwbPrivileged() {
+ try {
+ mUwbManager.removeServiceProfile(new PersistableBundle());
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testGetAllServiceProfilesWithoutUwbPrivileged() {
+ try {
+ mUwbManager.getAllServiceProfiles();
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testGetAdfProvisioningAuthoritiesWithoutUwbPrivileged() {
+ try {
+ mUwbManager.getAdfProvisioningAuthorities(new PersistableBundle());
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testGetAdfCertificateInfoWithoutUwbPrivileged() {
+ try {
+ mUwbManager.getAdfCertificateInfo(new PersistableBundle());
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testGetChipInfosWithoutUwbPrivileged() {
+ try {
+ mUwbManager.getChipInfos();
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testSendVendorUciWithoutUwbPrivileged() {
+ try {
+ mUwbManager.sendVendorUciMessage(10, 0, new byte[0]);
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ private class AdfProvisionStateCallback extends UwbManager.AdfProvisionStateCallback {
+ private final CountDownLatch mCountDownLatch;
+
+ public boolean onSuccessCalled;
+ public boolean onFailedCalled;
+
+ AdfProvisionStateCallback(@NonNull CountDownLatch countDownLatch) {
+ mCountDownLatch = countDownLatch;
+ }
+
+ @Override
+ public void onProfileAdfsProvisioned(@NonNull PersistableBundle params) {
+ onSuccessCalled = true;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void onProfileAdfsProvisionFailed(int reason, @NonNull PersistableBundle params) {
+ onFailedCalled = true;
+ mCountDownLatch.countDown();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testProvisionProfileAdfByScriptWithoutUwbPrivileged() {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ AdfProvisionStateCallback adfProvisionStateCallback =
+ new AdfProvisionStateCallback(countDownLatch);
+ try {
+ mUwbManager.provisionProfileAdfByScript(
+ new PersistableBundle(),
+ Executors.newSingleThreadExecutor(),
+ adfProvisionStateCallback);
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testRemoveProfileAdfWithoutUwbPrivileged() {
+ try {
+ mUwbManager.removeProfileAdf(new PersistableBundle());
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ private class UwbVendorUciCallback implements UwbManager.UwbVendorUciCallback {
+ private final CountDownLatch mRspCountDownLatch;
+ private final CountDownLatch mNtfCountDownLatch;
+
+ public int gid;
+ public int oid;
+ public byte[] payload;
+
+ UwbVendorUciCallback(
+ @NonNull CountDownLatch rspCountDownLatch,
+ @NonNull CountDownLatch ntfCountDownLatch) {
+ mRspCountDownLatch = rspCountDownLatch;
+ mNtfCountDownLatch = ntfCountDownLatch;
+ }
+
+ @Override
+ public void onVendorUciResponse(int gid, int oid, byte[] payload) {
+ this.gid = gid;
+ this.oid = oid;
+ this.payload = payload;
+ mRspCountDownLatch.countDown();
+ }
+
+ @Override
+ public void onVendorUciNotification(int gid, int oid, byte[] payload) {
+ this.gid = gid;
+ this.oid = oid;
+ this.payload = payload;
+ mNtfCountDownLatch.countDown();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testRegisterVendorUciCallbackWithoutUwbPrivileged() {
+ UwbManager.UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1));
+ try {
+ mUwbManager.registerUwbVendorUciCallback(
+ Executors.newSingleThreadExecutor(), cb);
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testUnregisterVendorUciCallbackWithoutUwbPrivileged() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ UwbManager.UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1));
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ mUwbManager.registerUwbVendorUciCallback(
+ Executors.newSingleThreadExecutor(), cb);
+ } catch (SecurityException e) {
+ /* pass */
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ try {
+ mUwbManager.unregisterUwbVendorUciCallback(cb);
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ }
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+ mUwbManager.unregisterUwbVendorUciCallback(cb);
+ /* pass */
+ } catch (SecurityException e) {
+ /* fail */
+ fail();
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testInvalidCallbackUnregisterVendorUciCallback() {
+ UwbManager.UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1));
+ try {
+ mUwbManager.registerUwbVendorUciCallback(
+ Executors.newSingleThreadExecutor(), cb);
+ } catch (SecurityException e) {
+ /* registration failed */
+ }
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ mUwbManager.unregisterUwbVendorUciCallback(cb);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ private class RangingSessionCallback implements RangingSession.Callback {
+ private CountDownLatch mCtrlCountDownLatch;
+ private CountDownLatch mResultCountDownLatch;
+
+ public boolean onOpenedCalled;
+ public boolean onOpenFailedCalled;
+ public boolean onStartedCalled;
+ public boolean onStartFailedCalled;
+ public boolean onReconfiguredCalled;
+ public boolean onReconfiguredFailedCalled;
+ public boolean onClosedCalled;
+ public RangingSession rangingSession;
+ public RangingReport rangingReport;
+
+ RangingSessionCallback(
+ @NonNull CountDownLatch ctrlCountDownLatch) {
+ this(ctrlCountDownLatch, null /* resultCountDownLaynch */);
+ }
+
+ RangingSessionCallback(
+ @NonNull CountDownLatch ctrlCountDownLatch,
+ @Nullable CountDownLatch resultCountDownLatch) {
+ mCtrlCountDownLatch = ctrlCountDownLatch;
+ mResultCountDownLatch = resultCountDownLatch;
+ }
+
+ public void replaceCtrlCountDownLatch(@NonNull CountDownLatch countDownLatch) {
+ mCtrlCountDownLatch = countDownLatch;
+ }
+
+ public void onOpened(@NonNull RangingSession session) {
+ onOpenedCalled = true;
+ rangingSession = session;
+ mCtrlCountDownLatch.countDown();
+ }
+
+ public void onOpenFailed(int reason, @NonNull PersistableBundle params) {
+ onOpenFailedCalled = true;
+ mCtrlCountDownLatch.countDown();
+ }
+
+ public void onStarted(@NonNull PersistableBundle sessionInfo) {
+ onStartedCalled = true;
+ mCtrlCountDownLatch.countDown();
+ }
+
+ public void onStartFailed(int reason, @NonNull PersistableBundle params) {
+ onStartFailedCalled = true;
+ mCtrlCountDownLatch.countDown();
+ }
+
+ public void onReconfigured(@NonNull PersistableBundle params) {
+ onReconfiguredCalled = true;
+ mCtrlCountDownLatch.countDown();
+ }
+
+ public void onReconfigureFailed(int reason, @NonNull PersistableBundle params) {
+ onReconfiguredFailedCalled = true;
+ mCtrlCountDownLatch.countDown();
+ }
+
+ public void onStopped(int reason, @NonNull PersistableBundle parameters) { }
+
+ public void onStopFailed(int reason, @NonNull PersistableBundle params) { }
+
+ public void onClosed(int reason, @NonNull PersistableBundle parameters) {
+ onClosedCalled = true;
+ mCtrlCountDownLatch.countDown();
+ }
+
+ public void onReportReceived(@NonNull RangingReport rangingReport) {
+ if (mResultCountDownLatch != null) {
+ this.rangingReport = rangingReport;
+ mResultCountDownLatch.countDown();
+ }
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testOpenRangingSessionWithInvalidChipId() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ RangingSessionCallback rangingSessionCallback = new RangingSessionCallback(countDownLatch);
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ // Try to start a ranging session with invalid params, should fail.
+ assertThrows(IllegalArgumentException.class, () -> mUwbManager.openRangingSession(
+ new PersistableBundle(),
+ Executors.newSingleThreadExecutor(),
+ rangingSessionCallback,
+ "invalidChipId"));
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testOpenRangingSessionWithChipIdWithBadParams() throws Exception {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ CancellationSignal cancellationSignal = null;
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ RangingSessionCallback rangingSessionCallback = new RangingSessionCallback(countDownLatch);
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ // Try to start a ranging session with invalid params, should fail.
+ cancellationSignal = mUwbManager.openRangingSession(
+ new PersistableBundle(),
+ Executors.newSingleThreadExecutor(),
+ rangingSessionCallback,
+ mDefaultChipId);
+ // Wait for the on start failed callback.
+ assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.onOpenedCalled).isFalse();
+ assertThat(rangingSessionCallback.onOpenFailedCalled).isTrue();
+ } finally {
+ if (cancellationSignal != null) {
+ cancellationSignal.cancel();
+ }
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testOpenRangingSessionWithInvalidChipIdWithBadParams() throws Exception {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ CancellationSignal cancellationSignal = null;
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ RangingSessionCallback rangingSessionCallback = new RangingSessionCallback(countDownLatch);
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ // Try to start a ranging session with invalid params, should fail.
+ cancellationSignal = mUwbManager.openRangingSession(
+ new PersistableBundle(),
+ Executors.newSingleThreadExecutor(),
+ rangingSessionCallback,
+ mDefaultChipId);
+ // Wait for the on start failed callback.
+ assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.onOpenedCalled).isFalse();
+ assertThat(rangingSessionCallback.onOpenFailedCalled).isTrue();
+ } finally {
+ if (cancellationSignal != null) {
+ cancellationSignal.cancel();
+ }
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ /**
+ * Simulates the app holding UWB_RANGING permission, but not UWB_PRIVILEGED.
+ */
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testOpenRangingSessionWithoutUwbPrivileged() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Only hold UWB_RANGING permission
+ uiAutomation.adoptShellPermissionIdentity(UWB_RANGING);
+ mUwbManager.openRangingSession(new PersistableBundle(),
+ Executors.newSingleThreadExecutor(),
+ new RangingSessionCallback(new CountDownLatch(1)));
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testOpenRangingSessionWithChipIdWithoutUwbPrivileged() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Only hold UWB_RANGING permission
+ uiAutomation.adoptShellPermissionIdentity(UWB_RANGING);
+ mUwbManager.openRangingSession(new PersistableBundle(),
+ Executors.newSingleThreadExecutor(),
+ new RangingSessionCallback(new CountDownLatch(1)),
+ mDefaultChipId);
+ // should fail if the call was successful without UWB_PRIVILEGED permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ /**
+ * Simulates the app holding UWB_PRIVILEGED permission, but not UWB_RANGING.
+ */
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testOpenRangingSessionWithoutUwbRanging() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity(UWB_PRIVILEGED);
+ mUwbManager.openRangingSession(new PersistableBundle(),
+ Executors.newSingleThreadExecutor(),
+ new RangingSessionCallback(new CountDownLatch(1)));
+ // should fail if the call was successful without UWB_RANGING permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testOpenRangingSessionWithChipIdWithoutUwbRanging() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity(UWB_PRIVILEGED);
+ mUwbManager.openRangingSession(new PersistableBundle(),
+ Executors.newSingleThreadExecutor(),
+ new RangingSessionCallback(new CountDownLatch(1)),
+ mDefaultChipId);
+ // should fail if the call was successful without UWB_RANGING permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ private AttributionSource getShellAttributionSourceWithRenouncedPermissions(
+ @Nullable Set<String> renouncedPermissions) {
+ try {
+ AttributionSource shellAttributionSource =
+ new AttributionSource.Builder(Process.SHELL_UID)
+ .setPackageName("com.android.shell")
+ .setRenouncedPermissions(renouncedPermissions)
+ .build();
+ PermissionManager permissionManager =
+ mContext.getSystemService(PermissionManager.class);
+ permissionManager.registerAttributionSource(shellAttributionSource);
+ return shellAttributionSource;
+ } catch (SecurityException e) {
+ fail("Failed to create shell attribution source" + e);
+ return null;
+ }
+ }
+
+ private Context createShellContextWithRenouncedPermissionsAndAttributionSource(
+ @Nullable Set<String> renouncedPermissions) {
+ return mContext.createContext(new ContextParams.Builder()
+ .setRenouncedPermissions(renouncedPermissions)
+ .setNextAttributionSource(
+ getShellAttributionSourceWithRenouncedPermissions(renouncedPermissions))
+ .build());
+ }
+
+ /**
+ * Simulates the calling app holding UWB_PRIVILEGED permission and UWB_RANGING permission, but
+ * the proxied app not holding UWB_RANGING permission.
+ */
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"})
+ public void testOpenRangingSessionWithoutUwbRangingInNextAttributeSource() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Only hold UWB_PRIVILEGED permission
+ uiAutomation.adoptShellPermissionIdentity();
+ Context shellContextWithUwbRangingRenounced =
+ createShellContextWithRenouncedPermissionsAndAttributionSource(
+ Set.of(UWB_RANGING));
+ UwbManager uwbManagerWithUwbRangingRenounced =
+ shellContextWithUwbRangingRenounced.getSystemService(UwbManager.class);
+ uwbManagerWithUwbRangingRenounced.openRangingSession(new PersistableBundle(),
+ Executors.newSingleThreadExecutor(),
+ new RangingSessionCallback(new CountDownLatch(1)));
+ // should fail if the call was successful without UWB_RANGING permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"})
+ public void testOpenRangingSessionWithChipIdWithoutUwbRangingInNextAttributeSource() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Only hold UWB_PRIVILEGED permission
+ uiAutomation.adoptShellPermissionIdentity();
+ Context shellContextWithUwbRangingRenounced =
+ createShellContextWithRenouncedPermissionsAndAttributionSource(
+ Set.of(UWB_RANGING));
+ UwbManager uwbManagerWithUwbRangingRenounced =
+ shellContextWithUwbRangingRenounced.getSystemService(UwbManager.class);
+ uwbManagerWithUwbRangingRenounced.openRangingSession(new PersistableBundle(),
+ Executors.newSingleThreadExecutor(),
+ new RangingSessionCallback(new CountDownLatch(1)),
+ mDefaultChipId);
+ // should fail if the call was successful without UWB_RANGING permission.
+ fail();
+ } catch (SecurityException e) {
+ /* pass */
+ Log.i(TAG, "Failed with expected security exception: " + e);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"})
+ public void testFiraRangingSession() throws Exception {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ CancellationSignal cancellationSignal = null;
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ CountDownLatch resultCountDownLatch = new CountDownLatch(1);
+ RangingSessionCallback rangingSessionCallback =
+ new RangingSessionCallback(countDownLatch, resultCountDownLatch);
+ FiraOpenSessionParams firaOpenSessionParams = new FiraOpenSessionParams.Builder()
+ .setProtocolVersion(new FiraProtocolVersion(1, 1))
+ .setSessionId(1)
+ .setStsConfig(FiraParams.STS_CONFIG_STATIC)
+ .setVendorId(new byte[]{0x5, 0x6})
+ .setStaticStsIV(new byte[]{0x5, 0x6, 0x9, 0xa, 0x4, 0x6})
+ .setDeviceType(FiraParams.RANGING_DEVICE_TYPE_CONTROLLER)
+ .setDeviceRole(FiraParams.RANGING_DEVICE_ROLE_INITIATOR)
+ .setMultiNodeMode(FiraParams.MULTI_NODE_MODE_UNICAST)
+ .setDeviceAddress(UwbAddress.fromBytes(new byte[] {0x5, 6}))
+ .setDestAddressList(List.of(UwbAddress.fromBytes(new byte[] {0x5, 6})))
+ .build();
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ // Try to start a ranging session with invalid params, should fail.
+ cancellationSignal = mUwbManager.openRangingSession(
+ firaOpenSessionParams.toBundle(),
+ Executors.newSingleThreadExecutor(),
+ rangingSessionCallback,
+ mDefaultChipId);
+ // Wait for the on opened callback.
+ assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.onOpenedCalled).isTrue();
+ assertThat(rangingSessionCallback.onOpenFailedCalled).isFalse();
+ assertThat(rangingSessionCallback.rangingSession).isNotNull();
+
+ countDownLatch = new CountDownLatch(1);
+ rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch);
+ rangingSessionCallback.rangingSession.start(new PersistableBundle());
+ // Wait for the on started callback.
+ assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.onStartedCalled).isTrue();
+ assertThat(rangingSessionCallback.onStartFailedCalled).isFalse();
+
+ // Wait for the on ranging report callback.
+ assertThat(resultCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.rangingReport).isNotNull();
+
+ // Check the UWB state.
+ assertThat(mUwbManager.getAdapterState()).isEqualTo(STATE_ENABLED_ACTIVE);
+
+ // Stop ongoing session.
+ rangingSessionCallback.rangingSession.stop();
+ } finally {
+ if (cancellationSignal != null) {
+ countDownLatch = new CountDownLatch(1);
+ rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch);
+
+ // Close session.
+ cancellationSignal.cancel();
+
+ // Wait for the on closed callback.
+ assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.onClosedCalled).isTrue();
+ }
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"})
+ public void testFiraRangingSessionWithProvisionedSTS() throws Exception {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ CancellationSignal cancellationSignal = null;
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ CountDownLatch resultCountDownLatch = new CountDownLatch(1);
+ RangingSessionCallback rangingSessionCallback =
+ new RangingSessionCallback(countDownLatch, resultCountDownLatch);
+ PersistableBundle bundle = mUwbManager.getSpecificationInfo();
+ if (bundle.keySet().contains(FiraParams.PROTOCOL_NAME)) {
+ bundle = requireNonNull(bundle.getPersistableBundle(FiraParams.PROTOCOL_NAME));
+ }
+ FiraSpecificationParams params =
+ FiraSpecificationParams.fromBundle(bundle);
+ EnumSet<FiraParams.StsCapabilityFlag> stsCapabilities = EnumSet.of(
+ FiraParams.StsCapabilityFlag.HAS_STATIC_STS_SUPPORT,
+ FiraParams.StsCapabilityFlag.HAS_PROVISIONED_STS_SUPPORT);
+ assumeTrue(params.getStsCapabilities() == stsCapabilities);
+
+ FiraOpenSessionParams firaOpenSessionParams = new FiraOpenSessionParams.Builder()
+ .setProtocolVersion(new FiraProtocolVersion(1, 1))
+ .setSessionId(1)
+ .setStsConfig(FiraParams.STS_CONFIG_PROVISIONED)
+ .setSessionKey(new byte[]{
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8
+ })
+ .setSubsessionKey(new byte[]{
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8
+ })
+ .setDeviceType(FiraParams.RANGING_DEVICE_TYPE_CONTROLLER)
+ .setDeviceRole(FiraParams.RANGING_DEVICE_ROLE_INITIATOR)
+ .setMultiNodeMode(FiraParams.MULTI_NODE_MODE_UNICAST)
+ .setDeviceAddress(UwbAddress.fromBytes(new byte[] {0x5, 6}))
+ .setDestAddressList(List.of(UwbAddress.fromBytes(new byte[] {0x5, 6})))
+ .build();
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ cancellationSignal = mUwbManager.openRangingSession(
+ firaOpenSessionParams.toBundle(),
+ Executors.newSingleThreadExecutor(),
+ rangingSessionCallback,
+ mDefaultChipId);
+ // Wait for the on opened callback.
+ assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.onOpenedCalled).isTrue();
+ assertThat(rangingSessionCallback.onOpenFailedCalled).isFalse();
+ assertThat(rangingSessionCallback.rangingSession).isNotNull();
+
+ countDownLatch = new CountDownLatch(1);
+ rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch);
+ rangingSessionCallback.rangingSession.start(new PersistableBundle());
+ // Wait for the on started callback.
+ assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.onStartedCalled).isTrue();
+ assertThat(rangingSessionCallback.onStartFailedCalled).isFalse();
+
+ countDownLatch = new CountDownLatch(1);
+ rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch);
+ UwbAddress uwbAddress = UwbAddress.fromBytes(new byte[] {0x5, 5});
+ rangingSessionCallback.rangingSession.addControlee(
+ new FiraControleeParams.Builder()
+ .setAddressList(new UwbAddress[] {uwbAddress})
+ .setSubSessionIdList(new int[] {1})
+ .build().toBundle()
+ );
+ // Wait for the on reconfigured callback.
+ assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.onReconfiguredCalled).isTrue();
+ assertThat(rangingSessionCallback.onReconfiguredFailedCalled).isFalse();
+
+ // Wait for the on ranging report callback.
+ assertThat(resultCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.rangingReport).isNotNull();
+
+ // Check the UWB state.
+ assertThat(mUwbManager.getAdapterState()).isEqualTo(STATE_ENABLED_ACTIVE);
+
+ // Stop ongoing session.
+ rangingSessionCallback.rangingSession.stop();
+ } finally {
+ if (cancellationSignal != null) {
+ countDownLatch = new CountDownLatch(1);
+ rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch);
+
+ // Close session.
+ cancellationSignal.cancel();
+
+ // Wait for the on closed callback.
+ assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(rangingSessionCallback.onClosedCalled).isTrue();
+ }
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ private class AdapterStateCallback implements UwbManager.AdapterStateCallback {
+ private final CountDownLatch mCountDownLatch;
+ private final Integer mWaitForState;
+ public int state;
+ public int reason;
+
+ AdapterStateCallback(@NonNull CountDownLatch countDownLatch,
+ @Nullable Integer waitForState) {
+ mCountDownLatch = countDownLatch;
+ mWaitForState = waitForState;
+ }
+
+ public void onStateChanged(int state, int reason) {
+ this.state = state;
+ this.reason = reason;
+ if (mWaitForState != null) {
+ if (mWaitForState == state) {
+ mCountDownLatch.countDown();
+ }
+ } else {
+ mCountDownLatch.countDown();
+ }
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-4"})
+ public void testUwbStateToggle() throws Exception {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ // Needs UWB_PRIVILEGED permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ assertThat(mUwbManager.isUwbEnabled()).isTrue();
+ assertThat(mUwbManager.getAdapterState()).isEqualTo(STATE_ENABLED_INACTIVE);
+ // Toggle the state
+ setUwbEnabledAndWaitForCompletion(false);
+ assertThat(mUwbManager.getAdapterState()).isEqualTo(STATE_DISABLED);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testSendVendorUciMessageVendorGid() throws Exception {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ CountDownLatch rspCountDownLatch = new CountDownLatch(1);
+ CountDownLatch ntfCountDownLatch = new CountDownLatch(1);
+ UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(rspCountDownLatch, ntfCountDownLatch);
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ mUwbManager.registerUwbVendorUciCallback(
+ Executors.newSingleThreadExecutor(), cb);
+
+ // Send random payload with a vendor gid.
+ byte[] payload = new byte[100];
+ new Random().nextBytes(payload);
+ int gid = 9;
+ int oid = 1;
+ mUwbManager.sendVendorUciMessage(gid, oid, payload);
+
+ // Wait for response.
+ assertThat(rspCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(cb.gid).isEqualTo(gid);
+ assertThat(cb.oid).isEqualTo(oid);
+ assertThat(cb.payload).isNotEmpty();
+ } catch (SecurityException e) {
+ /* pass */
+ } finally {
+ mUwbManager.unregisterUwbVendorUciCallback(cb);
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testSendVendorUciMessageFiraGid() throws Exception {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ CountDownLatch rspCountDownLatch = new CountDownLatch(1);
+ CountDownLatch ntfCountDownLatch = new CountDownLatch(1);
+ UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(rspCountDownLatch, ntfCountDownLatch);
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ mUwbManager.registerUwbVendorUciCallback(
+ Executors.newSingleThreadExecutor(), cb);
+
+ // Send random payload with a FIRA gid.
+ byte[] payload = new byte[100];
+ new Random().nextBytes(payload);
+ int gid = 1;
+ int oid = 3;
+ mUwbManager.sendVendorUciMessage(gid, oid, payload);
+
+ // Wait for response.
+ assertThat(rspCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(cb.gid).isEqualTo(gid);
+ assertThat(cb.oid).isEqualTo(oid);
+ assertThat(cb.payload).isNotEmpty();
+ } catch (SecurityException e) {
+ /* pass */
+ } finally {
+ mUwbManager.unregisterUwbVendorUciCallback(cb);
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"})
+ public void testSendVendorUciMessageWithFragmentedPackets() throws Exception {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ CountDownLatch rspCountDownLatch = new CountDownLatch(1);
+ CountDownLatch ntfCountDownLatch = new CountDownLatch(1);
+ UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(rspCountDownLatch, ntfCountDownLatch);
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ mUwbManager.registerUwbVendorUciCallback(
+ Executors.newSingleThreadExecutor(), cb);
+
+ // Send random payload > 255 bytes with a vendor gid.
+ byte[] payload = new byte[400];
+ new Random().nextBytes(payload);
+ int gid = 9;
+ int oid = 1;
+ mUwbManager.sendVendorUciMessage(gid, oid, payload);
+
+ // Wait for response.
+ assertThat(rspCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(cb.gid).isEqualTo(gid);
+ assertThat(cb.oid).isEqualTo(oid);
+ assertThat(cb.payload).isNotEmpty();
+ } catch (SecurityException e) {
+ /* pass */
+ } finally {
+ mUwbManager.unregisterUwbVendorUciCallback(cb);
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+}
diff --git a/tests/cts/tests/src/android/uwb/cts/UwbTestUtils.java b/tests/cts/tests/src/android/uwb/cts/UwbTestUtils.java
new file mode 100644
index 00000000..150823a7
--- /dev/null
+++ b/tests/cts/tests/src/android/uwb/cts/UwbTestUtils.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2021 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.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.PersistableBundle;
+import android.os.SystemClock;
+import android.uwb.AngleMeasurement;
+import android.uwb.AngleOfArrivalMeasurement;
+import android.uwb.DistanceMeasurement;
+import android.uwb.RangingMeasurement;
+import android.uwb.RangingReport;
+import android.uwb.UwbAddress;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class UwbTestUtils {
+ private UwbTestUtils() {}
+
+ public static boolean isUwbSupported(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_UWB);
+ }
+
+ public static AngleMeasurement getAngleMeasurement() {
+ return new AngleMeasurement(
+ getDoubleInRange(-Math.PI, Math.PI),
+ getDoubleInRange(0, Math.PI),
+ getDoubleInRange(0, 1));
+ }
+
+ public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
+ return new AngleOfArrivalMeasurement.Builder(getAngleMeasurement())
+ .setAltitude(getAngleMeasurement())
+ .build();
+ }
+
+ public static DistanceMeasurement getDistanceMeasurement() {
+ return new DistanceMeasurement.Builder()
+ .setMeters(getDoubleInRange(0, 100))
+ .setErrorMeters(getDoubleInRange(0, 10))
+ .setConfidenceLevel(getDoubleInRange(0, 1))
+ .build();
+ }
+
+ public static RangingMeasurement getRangingMeasurement() {
+ return getRangingMeasurement(getUwbAddress(false));
+ }
+
+ public static PersistableBundle getTestRangingMetadata() {
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt("TEST_KEY", 1);
+ return bundle;
+ }
+
+ public static RangingMeasurement getRangingMeasurement(UwbAddress address) {
+ return new RangingMeasurement.Builder()
+ .setDistanceMeasurement(getDistanceMeasurement())
+ .setAngleOfArrivalMeasurement(getAngleOfArrivalMeasurement())
+ .setDestinationAngleOfArrivalMeasurement(getAngleOfArrivalMeasurement())
+ .setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos())
+ .setRemoteDeviceAddress(address != null ? address : getUwbAddress(false))
+ .setStatus(RangingMeasurement.RANGING_STATUS_SUCCESS)
+ .setLineOfSight(RangingMeasurement.NLOS)
+ .setMeasurementFocus(RangingMeasurement.MEASUREMENT_FOCUS_RANGE)
+ .setRssiDbm(-85)
+ .build();
+ }
+
+ public static List<RangingMeasurement> getRangingMeasurements(int num) {
+ List<RangingMeasurement> result = new ArrayList<>();
+ for (int i = 0; i < num; i++) {
+ result.add(getRangingMeasurement());
+ }
+ return result;
+ }
+
+ public static RangingReport getRangingReports(int numMeasurements) {
+ RangingReport.Builder builder = new RangingReport.Builder();
+ for (int i = 0; i < numMeasurements; i++) {
+ builder.addMeasurement(getRangingMeasurement());
+ }
+ return builder.build();
+ }
+
+ private static double getDoubleInRange(double min, double max) {
+ return min + (max - min) * Math.random();
+ }
+
+ public static UwbAddress getUwbAddress(boolean isShortAddress) {
+ byte[] addressBytes = new byte[isShortAddress ? UwbAddress.SHORT_ADDRESS_BYTE_LENGTH :
+ UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH];
+ for (int i = 0; i < addressBytes.length; i++) {
+ addressBytes[i] = (byte) getDoubleInRange(1, 255);
+ }
+ return UwbAddress.fromBytes(addressBytes);
+ }
+
+ public static Executor getExecutor() {
+ return new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+ };
+ }
+}