blob: 670594807be4aa8ffb42463bdf5282f68086cfd5 [file] [log] [blame]
/*
* 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 com.android.server.uwb;
import static com.android.server.uwb.data.UwbUciConstants.RANGING_MEASUREMENT_TYPE_TWO_WAY;
import static com.android.server.uwb.util.UwbUtil.convertFloatToQFormat;
import static com.android.server.uwb.util.UwbUtil.degreeToRadian;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.validateMockitoUsage;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.AttributionSource;
import android.platform.test.annotations.Presubmit;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
import android.uwb.AngleMeasurement;
import android.uwb.AngleOfArrivalMeasurement;
import android.uwb.DistanceMeasurement;
import android.uwb.IUwbRangingCallbacks;
import android.uwb.RangingMeasurement;
import android.uwb.RangingReport;
import android.uwb.SessionHandle;
import android.uwb.UwbAddress;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.uwb.data.UwbRangingData;
import com.android.server.uwb.data.UwbTwoWayMeasurement;
import com.android.server.uwb.data.UwbUciConstants;
import com.android.server.uwb.params.TlvUtil;
import com.google.uwb.support.fira.FiraOpenSessionParams;
import com.google.uwb.support.fira.FiraParams;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Unit tests for {@link com.android.server.uwb.UwbSettingsStore}.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class UwbSessionNotificationManagerTest {
private static final long TEST_SEQ_COUNTER = 5;
private static final long TEST_SESSION_ID = 7;
private static final int TEST_RCR_INDICATION = 7;
private static final long TEST_CURR_RANGING_INTERVAL = 100;
private static final int TEST_RANGING_MEASURES_TYPE = RANGING_MEASUREMENT_TYPE_TWO_WAY;
private static final int TEST_MAC_ADDRESS_MODE = 1;
private static final byte[] TEST_MAC_ADDRESS = {0x1, 0x3};
private static final int TEST_STATUS = FiraParams.STATUS_CODE_OK;
private static final int TEST_LOS = 0;
private static final int TEST_DISTANCE = 101;
private static final float TEST_AOA_AZIMUTH = 67;
private static final int TEST_AOA_AZIMUTH_FOM = 50;
private static final float TEST_AOA_ELEVATION = 37;
private static final int TEST_AOA_ELEVATION_FOM = 90;
private static final float TEST_AOA_DEST_AZIMUTH = 67;
private static final int TEST_AOA_DEST_AZIMUTH_FOM = 50;
private static final float TEST_AOA_DEST_ELEVATION = 37;
private static final int TEST_AOA_DEST_ELEVATION_FOM = 90;
private static final int TEST_SLOT_IDX = 10;
private static final long TEST_ELAPSED_NANOS = 100L;
private static final int UID = 343453;
private static final String PACKAGE_NAME = "com.uwb.test";
private static final AttributionSource ATTRIBUTION_SOURCE =
new AttributionSource.Builder(UID).setPackageName(PACKAGE_NAME).build();
@Mock private UwbInjector mUwbInjector;
@Mock private UwbSessionManager.UwbSession mUwbSession;
@Mock private SessionHandle mSessionHandle;
@Mock private IUwbRangingCallbacks mIUwbRangingCallbacks;
@Mock private FiraOpenSessionParams mFiraParams;
private UwbSessionNotificationManager mUwbSessionNotificationManager;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mUwbSession.getSessionHandle()).thenReturn(mSessionHandle);
when(mUwbSession.getIUwbRangingCallbacks()).thenReturn(mIUwbRangingCallbacks);
when(mUwbSession.getProtocolName()).thenReturn(FiraParams.PROTOCOL_NAME);
when(mUwbSession.getParams()).thenReturn(mFiraParams);
when(mUwbSession.getAttributionSource()).thenReturn(ATTRIBUTION_SOURCE);
when(mFiraParams.getAoaResultRequest()).thenReturn(
FiraParams.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS);
when(mFiraParams.hasResultReportPhase()).thenReturn(false);
when(mUwbInjector.checkUwbRangingPermissionForDataDelivery(any(), any())).thenReturn(true);
when(mUwbInjector.getElapsedSinceBootNanos()).thenReturn(TEST_ELAPSED_NANOS);
mUwbSessionNotificationManager = new UwbSessionNotificationManager(mUwbInjector);
}
/**
* Called after each testGG
*/
@After
public void cleanup() {
validateMockitoUsage();
}
@Test
public void testOnRangingResultWithoutUwbRangingPermission() throws Exception {
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
generateRangingDataAndRangingReport(true, true, false, false);
when(mUwbInjector.checkUwbRangingPermissionForDataDelivery(eq(ATTRIBUTION_SOURCE), any()))
.thenReturn(false);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
verify(mIUwbRangingCallbacks, never()).onRangingResult(any(), any());
}
@Test
public void testOnRangingResultWithAoa() throws Exception {
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
generateRangingDataAndRangingReport(true, true, false, false);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
verify(mIUwbRangingCallbacks).onRangingResult(
mSessionHandle, testRangingDataAndRangingReport.second);
}
@Test
public void testOnRangingResultWithNoAoa() throws Exception {
when(mFiraParams.getAoaResultRequest()).thenReturn(
FiraParams.AOA_RESULT_REQUEST_MODE_NO_AOA_REPORT);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
generateRangingDataAndRangingReport(false, false, false, false);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
verify(mIUwbRangingCallbacks).onRangingResult(
mSessionHandle, testRangingDataAndRangingReport.second);
}
@Test
public void testOnRangingResultWithNoAoaElevation() throws Exception {
when(mFiraParams.getAoaResultRequest()).thenReturn(
FiraParams.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS_AZIMUTH_ONLY);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
generateRangingDataAndRangingReport(true, false, false, false);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
verify(mIUwbRangingCallbacks).onRangingResult(
mSessionHandle, testRangingDataAndRangingReport.second);
}
@Test
public void testOnRangingResultWithNoAoaAzimuth() throws Exception {
when(mFiraParams.getAoaResultRequest()).thenReturn(
FiraParams.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS_ELEVATION_ONLY);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
generateRangingDataAndRangingReport(false, true, false, false);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
verify(mIUwbRangingCallbacks).onRangingResult(
mSessionHandle, testRangingDataAndRangingReport.second);
}
@Test
public void testOnRangingResultWithAoaAndDestAoa() throws Exception {
when(mFiraParams.getAoaResultRequest()).thenReturn(
FiraParams.AOA_RESULT_REQUEST_MODE_REQ_AOA_RESULTS);
when(mFiraParams.hasResultReportPhase()).thenReturn(true);
when(mFiraParams.hasAngleOfArrivalAzimuthReport()).thenReturn(true);
when(mFiraParams.hasAngleOfArrivalElevationReport()).thenReturn(true);
Pair<UwbRangingData, RangingReport> testRangingDataAndRangingReport =
generateRangingDataAndRangingReport(true, true, true, true);
mUwbSessionNotificationManager.onRangingResult(
mUwbSession, testRangingDataAndRangingReport.first);
verify(mIUwbRangingCallbacks).onRangingResult(
mSessionHandle, testRangingDataAndRangingReport.second);
}
@Test
public void testOnRangingOpened() throws Exception {
mUwbSessionNotificationManager.onRangingOpened(mUwbSession);
verify(mIUwbRangingCallbacks).onRangingOpened(mSessionHandle);
}
@Test
public void testOnRangingOpenFailed() throws Exception {
int status = UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED;
mUwbSessionNotificationManager.onRangingOpenFailed(mUwbSession, status);
verify(mIUwbRangingCallbacks).onRangingOpenFailed(eq(mSessionHandle),
eq(UwbSessionNotificationHelper.convertStatusToReasonCode(
FiraParams.PROTOCOL_NAME, status)),
argThat(p -> (p.getInt("status_code")) == status));
}
@Test
public void testOnRangingStarted() throws Exception {
mUwbSessionNotificationManager.onRangingStarted(mUwbSession, mUwbSession.getParams());
verify(mIUwbRangingCallbacks).onRangingStarted(mSessionHandle,
mUwbSession.getParams().toBundle());
}
@Test
public void testOnRangingStartFailed() throws Exception {
int status = UwbUciConstants.STATUS_CODE_INVALID_PARAM;
mUwbSessionNotificationManager.onRangingStartFailed(mUwbSession, status);
verify(mIUwbRangingCallbacks).onRangingStartFailed(eq(mSessionHandle),
eq(UwbSessionNotificationHelper.convertStatusToReasonCode(
FiraParams.PROTOCOL_NAME, status)),
argThat(p -> (p.getInt("status_code")) == status));
}
@Test
public void testOnRangingStopped() throws Exception {
int reason = UwbUciConstants.REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS;
mUwbSessionNotificationManager.onRangingStopped(mUwbSession, reason);
verify(mIUwbRangingCallbacks).onRangingStopped(eq(mSessionHandle),
eq(UwbSessionNotificationHelper.convertStatusToReasonCode(
FiraParams.PROTOCOL_NAME, reason)),
argThat(p-> p.getInt("reason_code") == reason));
}
@Test
public void testORangingStopFailed() throws Exception {
int status = UwbUciConstants.STATUS_CODE_INVALID_RANGE;
mUwbSessionNotificationManager.onRangingStopFailed(mUwbSession, status);
verify(mIUwbRangingCallbacks).onRangingStopFailed(eq(mSessionHandle),
eq(UwbSessionNotificationHelper.convertStatusToReasonCode(
FiraParams.PROTOCOL_NAME, status)),
argThat(p -> (p.getInt("status_code")) == status));
}
@Test
public void testOnRangingReconfigured() throws Exception {
mUwbSessionNotificationManager.onRangingReconfigured(mUwbSession);
verify(mIUwbRangingCallbacks).onRangingReconfigured(eq(mSessionHandle), any());
}
@Test
public void testOnRangingReconfigureFailed() throws Exception {
int status = UwbUciConstants.STATUS_CODE_INVALID_MESSAGE_SIZE;
mUwbSessionNotificationManager.onRangingReconfigureFailed(mUwbSession, status);
verify(mIUwbRangingCallbacks).onRangingReconfigureFailed(eq(mSessionHandle),
eq(UwbSessionNotificationHelper.convertStatusToReasonCode(
FiraParams.PROTOCOL_NAME, status)),
argThat(p -> (p.getInt("status_code")) == status));
}
@Test
public void testOnRangingClosed() throws Exception {
int reason = UwbUciConstants.REASON_ERROR_SLOT_LENGTH_NOT_SUPPORTED;
mUwbSessionNotificationManager.onRangingClosed(mUwbSession, reason);
verify(mIUwbRangingCallbacks).onRangingClosed(eq(mSessionHandle),
eq(UwbSessionNotificationHelper.convertStatusToReasonCode(
FiraParams.PROTOCOL_NAME, reason)),
argThat(p-> p.getInt("reason_code") == reason));
}
// Helper method to generate a UwbRangingData instance and corresponding RangingMeasurement
private Pair<UwbRangingData, RangingReport> generateRangingDataAndRangingReport(
boolean isAoaAzimuthEnabled, boolean isAoaElevationEnabled,
boolean isDestAoaAzimuthEnabled, boolean isDestAoaElevationEnabled) {
final int noOfRangingMeasures = 1;
final UwbTwoWayMeasurement[] uwbTwoWayMeasurements =
new UwbTwoWayMeasurement[noOfRangingMeasures];
uwbTwoWayMeasurements[0] = new UwbTwoWayMeasurement(TEST_MAC_ADDRESS, TEST_STATUS, TEST_LOS,
TEST_DISTANCE, convertFloatToQFormat(TEST_AOA_AZIMUTH, 9, 7),
TEST_AOA_AZIMUTH_FOM, convertFloatToQFormat(TEST_AOA_ELEVATION, 9, 7),
TEST_AOA_ELEVATION_FOM, convertFloatToQFormat(TEST_AOA_DEST_AZIMUTH, 9, 7),
TEST_AOA_DEST_AZIMUTH_FOM, convertFloatToQFormat(TEST_AOA_DEST_ELEVATION, 9, 7),
TEST_AOA_DEST_ELEVATION_FOM, TEST_SLOT_IDX);
UwbRangingData uwbRangingData = new UwbRangingData(TEST_SEQ_COUNTER, TEST_SESSION_ID,
TEST_RCR_INDICATION, TEST_CURR_RANGING_INTERVAL, TEST_RANGING_MEASURES_TYPE,
TEST_MAC_ADDRESS_MODE, noOfRangingMeasures, uwbTwoWayMeasurements);
AngleOfArrivalMeasurement aoaMeasurement = null;
AngleOfArrivalMeasurement aoaDestMeasurement = null;
if (isAoaAzimuthEnabled || isAoaElevationEnabled) {
AngleMeasurement aoaAzimuth = null;
AngleMeasurement aoaElevation = null;
if (isAoaAzimuthEnabled) {
aoaAzimuth =
new AngleMeasurement(
degreeToRadian(TEST_AOA_AZIMUTH), 0,
TEST_AOA_AZIMUTH_FOM / (double) 100);
}
if (isAoaElevationEnabled) {
aoaElevation =
new AngleMeasurement(
degreeToRadian(TEST_AOA_ELEVATION), 0,
TEST_AOA_ELEVATION_FOM / (double) 100);
}
aoaMeasurement = new AngleOfArrivalMeasurement.Builder(aoaAzimuth)
.setAltitude(aoaElevation)
.build();
}
if (isDestAoaAzimuthEnabled || isDestAoaElevationEnabled) {
AngleMeasurement aoaDestAzimuth = null;
AngleMeasurement aoaDestElevation = null;
if (isDestAoaAzimuthEnabled) {
aoaDestAzimuth =
new AngleMeasurement(
degreeToRadian(TEST_AOA_DEST_AZIMUTH), 0,
TEST_AOA_DEST_AZIMUTH_FOM / (double) 100);
}
if (isDestAoaElevationEnabled) {
aoaDestElevation =
new AngleMeasurement(
degreeToRadian(TEST_AOA_DEST_ELEVATION), 0,
TEST_AOA_DEST_ELEVATION_FOM / (double) 100);
}
aoaDestMeasurement = new AngleOfArrivalMeasurement.Builder(aoaDestAzimuth)
.setAltitude(aoaDestElevation)
.build();
}
RangingMeasurement rangingMeasurement = new RangingMeasurement.Builder()
.setRemoteDeviceAddress(UwbAddress.fromBytes(
TlvUtil.getReverseBytes(TEST_MAC_ADDRESS)))
.setStatus(TEST_STATUS)
.setElapsedRealtimeNanos(TEST_ELAPSED_NANOS)
.setDistanceMeasurement(
new DistanceMeasurement.Builder()
.setMeters(TEST_DISTANCE / (double) 100)
.setErrorMeters(0)
.setConfidenceLevel(0)
.build())
.setAngleOfArrivalMeasurement(aoaMeasurement)
.setDestinationAngleOfArrivalMeasurement(aoaDestMeasurement)
.setLineOfSight(TEST_LOS)
.build();
RangingReport rangingReport = new RangingReport.Builder()
.addMeasurement(rangingMeasurement)
.build();
return Pair.create(uwbRangingData, rangingReport);
}
}