/* * 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.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; import static com.android.server.uwb.util.UwbUtil.degreeToRadian; import static com.google.uwb.support.radar.RadarParams.BITS_PER_SAMPLES_48; import static com.google.uwb.support.radar.RadarParams.RADAR_DATA_TYPE_RADAR_SWEEP_SAMPLES; import android.os.PersistableBundle; import android.util.Pair; 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 com.android.modules.utils.build.SdkLevel; import com.android.server.uwb.data.UwbDlTDoAMeasurement; import com.android.server.uwb.data.UwbOwrAoaMeasurement; import com.android.server.uwb.data.UwbRadarData; import com.android.server.uwb.data.UwbRadarSweepData; 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; import com.google.uwb.support.radar.RadarData; import com.google.uwb.support.radar.RadarParams; import com.google.uwb.support.radar.RadarSweepData; public class UwbTestUtils { public static final int TEST_SESSION_ID = 7; public static final int TEST_SESSION_ID_2 = 8; public static final byte TEST_SESSION_TYPE = FiraParams.SESSION_TYPE_RANGING; public static final byte[] PEER_SHORT_MAC_ADDRESS = {0x35, 0x37}; public static final long PEER_SHORT_MAC_ADDRESS_LONG = 0x3735L; public static final byte[] PEER_EXTENDED_SHORT_MAC_ADDRESS = {0x35, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; public static final long PEER_EXTENDED_SHORT_MAC_ADDRESS_LONG = 0x3735L; public static final byte[] PEER_EXTENDED_MAC_ADDRESS = {0x12, 0x14, 0x16, 0x18, 0x31, 0x33, 0x35, 0x37}; public static final long PEER_EXTENDED_MAC_ADDRESS_LONG = 0x3735333118161412L; public static final byte[] PEER_EXTENDED_MAC_ADDRESS_2 = {0x2, 0x4, 0x6, 0x8, 0x1, 0x3, 0x5, 0x7}; public static final long PEER_EXTENDED_MAC_ADDRESS_2_LONG = 0x0705030108060402L; 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_EXTENDED_UWB_ADDRESS_2 = UwbAddress.fromBytes( PEER_EXTENDED_MAC_ADDRESS_2); public static final UwbAddress PEER_SHORT_UWB_ADDRESS = UwbAddress.fromBytes( PEER_SHORT_MAC_ADDRESS); public static final UwbAddress PEER_EXTENDED_SHORT_UWB_ADDRESS = UwbAddress.fromBytes( PEER_EXTENDED_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 public static final int MAX_DATA_SIZE = 100; private static final byte[] TEST_RAW_NTF_DATA = {0x10, 0x01, 0x05}; private static final long TEST_SEQ_COUNTER = 5; private static final long TEST_SEQ_COUNTER2 = 6; 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; public static final int TEST_STATUS = FiraParams.STATUS_CODE_OK; private static final int TEST_LOS = 0; private static final int TEST_DISTANCE = 101; private static final float TEST_AOA_AZIMUTH = 67; private static final int TEST_AOA_AZIMUTH_FOM = 50; private static final int TEST_BAD_AOA_AZIMUTH_FOM = 150; 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_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 long TEST_TIMESTAMP2 = 600_000L; private static final float TEST_ANCHOR_CFO = 12.50f; private static final float TEST_CFO = 15.50f; 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 static final int TEST_SAMPLES_PER_SWEEP = 64; private static final int TEST_BITS_PER_SAMPLE = BITS_PER_SAMPLES_48; private static final int TEST_SWEEP_OFFSET = -10; private static final byte[] TEST_VENDOR_SPECIFIC_DATA = {0x04, 0x07}; private static final byte[] TEST_SAMPLE_DATA = {0x06, 0x08, (byte) 0Xff}; private UwbTestUtils() {} /** Build UwbRangingData for all Ranging Measurement Type(s). */ public static UwbRangingData generateRangingData( int rangingMeasurementType, int macAddressingMode, int rangingStatus) { byte[] macAddress = (macAddressingMode == MAC_ADDRESSING_MODE_SHORT) ? PEER_SHORT_MAC_ADDRESS : PEER_EXTENDED_MAC_ADDRESS; return generateRangingData( rangingMeasurementType, macAddressingMode, macAddress, rangingStatus); } /** Build UwbRangingData for all Ranging Measurement Type(s). */ public static UwbRangingData generateRangingData( int rangingMeasurementType, int macAddressingMode, byte[] macAddress, int rangingStatus) { switch (rangingMeasurementType) { case RANGING_MEASUREMENT_TYPE_TWO_WAY: return generateTwoWayMeasurementRangingData(rangingStatus); case RANGING_MEASUREMENT_TYPE_OWR_AOA: return generateOwrAoaMeasurementRangingData( macAddressingMode, macAddress, rangingStatus); case RANGING_MEASUREMENT_TYPE_DL_TDOA: return generateDlTDoAMeasurementRangingData(macAddressingMode, rangingStatus); default: return generateDefaultRangingData(); } } private static UwbRangingData generateTwoWayMeasurementRangingData(int rangingStatus) { final int noOfRangingMeasures = 1; final UwbTwoWayMeasurement[] uwbTwoWayMeasurements = new UwbTwoWayMeasurement[noOfRangingMeasures]; uwbTwoWayMeasurements[0] = new UwbTwoWayMeasurement(PEER_SHORT_MAC_ADDRESS, rangingStatus, 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, TEST_RSSI); return new UwbRangingData(TEST_SEQ_COUNTER, TEST_SESSION_ID, TEST_RCR_INDICATION, TEST_CURR_RANGING_INTERVAL, RANGING_MEASUREMENT_TYPE_TWO_WAY, TEST_MAC_ADDRESS_MODE, noOfRangingMeasures, uwbTwoWayMeasurements, TEST_RAW_NTF_DATA); } private static UwbRangingData generateOwrAoaMeasurementRangingData( int macAddressingMode, byte[] macAddress, int rangingStatus) { final int noOfRangingMeasures = 1; final UwbOwrAoaMeasurement uwbOwrAoaMeasurement = new UwbOwrAoaMeasurement( 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, macAddressingMode, noOfRangingMeasures, uwbOwrAoaMeasurement, TEST_RAW_NTF_DATA); } /** Generate an OWR ranging data with a bad AoA Azimuth FOM */ public static UwbRangingData generateBadOwrAoaMeasurementRangingData( int macAddressingMode, byte[] macAddress) { final int noOfRangingMeasures = 1; final UwbOwrAoaMeasurement uwbOwrAoaMeasurement = new UwbOwrAoaMeasurement( macAddress, TEST_STATUS, TEST_LOS, TEST_FRAME_SEQUENCE_NUMBER, TEST_BLOCK_IDX, convertFloatToQFormat(TEST_AOA_AZIMUTH, 9, 7), TEST_BAD_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, 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, convertFloatToQFormat(TEST_ANCHOR_CFO, 6, 10), convertFloatToQFormat(TEST_CFO, 6, 10), 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 generateRangingDataAndRangingReport( byte[] macAddress, int macAddressingMode, int rangingMeasurementType, boolean isAoaAzimuthEnabled, boolean isAoaElevationEnabled, boolean isDestAoaAzimuthEnabled, boolean isDestAoaElevationEnabled, long elapsedRealtimeNanos) { UwbRangingData uwbRangingData = generateRangingData(rangingMeasurementType, macAddressingMode, TEST_STATUS); PersistableBundle rangingReportMetadata = new RangingReportMetadata.Builder() .setSessionId(0) .setRawNtfData(new byte[] {0x10, 0x01, 0x05}) .build() .toBundle(); AngleOfArrivalMeasurement aoaMeasurement = null; AngleOfArrivalMeasurement aoaDestMeasurement = null; if (isAoaAzimuthEnabled || isAoaElevationEnabled) { AngleMeasurement aoaAzimuth = null; AngleMeasurement aoaElevation = null; AngleOfArrivalMeasurement.Builder aoaBuilder = null; if (isAoaAzimuthEnabled) { aoaAzimuth = new AngleMeasurement( degreeToRadian(TEST_AOA_AZIMUTH), 0, TEST_AOA_AZIMUTH_FOM / (double) 100); aoaBuilder = new AngleOfArrivalMeasurement.Builder(aoaAzimuth); } if (isAoaElevationEnabled && aoaBuilder != null) { aoaElevation = new AngleMeasurement( degreeToRadian(TEST_AOA_ELEVATION), 0, TEST_AOA_ELEVATION_FOM / (double) 100); aoaBuilder.setAltitude(aoaElevation); } aoaMeasurement = (aoaBuilder != null) ? aoaBuilder.build() : null; } if (isDestAoaAzimuthEnabled || isDestAoaElevationEnabled) { AngleMeasurement aoaDestAzimuth = null; AngleMeasurement aoaDestElevation = null; AngleOfArrivalMeasurement.Builder aoaBuilder = null; if (isDestAoaAzimuthEnabled) { aoaDestAzimuth = new AngleMeasurement( degreeToRadian(TEST_AOA_DEST_AZIMUTH), 0, TEST_AOA_DEST_AZIMUTH_FOM / (double) 100); aoaBuilder = new AngleOfArrivalMeasurement.Builder(aoaDestAzimuth); } if (isDestAoaElevationEnabled && aoaBuilder != null) { aoaDestElevation = new AngleMeasurement( degreeToRadian(TEST_AOA_DEST_ELEVATION), 0, TEST_AOA_DEST_ELEVATION_FOM / (double) 100); aoaBuilder.setAltitude(aoaDestElevation); } aoaDestMeasurement = (aoaBuilder != null) ? aoaBuilder.build() : null; } RangingReport rangingReport = buildRangingReport(macAddress, rangingMeasurementType, aoaMeasurement, aoaDestMeasurement, elapsedRealtimeNanos, rangingReportMetadata); return Pair.create(uwbRangingData, rangingReport); } private static UwbAddress getComputedMacAddress(byte[] address) { if (!SdkLevel.isAtLeastU()) { return UwbAddress.fromBytes(TlvUtil.getReverseBytes(address)); } return UwbAddress.fromBytes(address); } private static RangingReport buildRangingReport(byte[] macAddress, int rangingMeasurementType, AngleOfArrivalMeasurement aoaMeasurement, AngleOfArrivalMeasurement aoaDestMeasurement, long elapsedRealtimeNanos, PersistableBundle rangingReportMetadata) { PersistableBundle rangingMeasurementMetadata = new PersistableBundle(); RangingMeasurement.Builder rangingMeasurementBuilder = new RangingMeasurement.Builder() .setRemoteDeviceAddress(getComputedMacAddress(macAddress)) .setStatus(TEST_STATUS) .setElapsedRealtimeNanos(elapsedRealtimeNanos) .setAngleOfArrivalMeasurement(aoaMeasurement) .setLineOfSight(TEST_LOS); if (rangingMeasurementType == RANGING_MEASUREMENT_TYPE_TWO_WAY) { rangingMeasurementBuilder .setDistanceMeasurement( new DistanceMeasurement.Builder() .setMeters(TEST_DISTANCE / (double) 100) .setErrorMeters(0) .setConfidenceLevel(1.0) .build()) .setDestinationAngleOfArrivalMeasurement(aoaDestMeasurement) .setRssiDbm(-TEST_RSSI / 2) .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) .setRoundIndex(TEST_ROUND_INDEX) .build(); rangingMeasurementBuilder.setRssiDbm(-TEST_RSSI / 2); rangingMeasurementBuilder.setRangingMeasurementMetadata(dlTDoAMeasurement.toBundle()); } return new RangingReport.Builder() .addMeasurement(rangingMeasurementBuilder.build()) .addRangingReportMetadata(rangingReportMetadata) .build(); } /** Build UwbRadarData for all Radar Data Type(s). */ public static UwbRadarData generateUwbRadarData(int radarDataType, int rangingStatus) { switch (radarDataType) { case RADAR_DATA_TYPE_RADAR_SWEEP_SAMPLES: return generateRadarSweepSamplesUwbRadarData(rangingStatus); default: return generateDefaultUwbRadarData(); } } /** Build UwbRadarData for Radar Sweep Samples Radar Data Type. */ public static UwbRadarData generateRadarSweepSamplesUwbRadarData(int rangingStatus) { final int noOfRadarSweeps = 2; final UwbRadarSweepData[] uwbRadarSweepData = new UwbRadarSweepData[noOfRadarSweeps]; uwbRadarSweepData[0] = new UwbRadarSweepData( TEST_SEQ_COUNTER, TEST_TIMESTAMP, TEST_VENDOR_SPECIFIC_DATA, TEST_SAMPLE_DATA); uwbRadarSweepData[1] = new UwbRadarSweepData( TEST_SEQ_COUNTER2, TEST_TIMESTAMP2, TEST_VENDOR_SPECIFIC_DATA, TEST_SAMPLE_DATA); return new UwbRadarData( TEST_SESSION_ID, rangingStatus, RADAR_DATA_TYPE_RADAR_SWEEP_SAMPLES, TEST_SAMPLES_PER_SWEEP, TEST_BITS_PER_SAMPLE, TEST_SWEEP_OFFSET, uwbRadarSweepData); } /* Create a UwbRadarData with no measurements */ private static UwbRadarData generateDefaultUwbRadarData() { final int noOfRadarSweeps = 0; final UwbRadarSweepData[] uwbEmptyRadarSweepData = new UwbRadarSweepData[noOfRadarSweeps]; return new UwbRadarData( TEST_SESSION_ID, TEST_STATUS, RADAR_DATA_TYPE_RADAR_SWEEP_SAMPLES, TEST_SAMPLES_PER_SWEEP, TEST_BITS_PER_SAMPLE, TEST_SWEEP_OFFSET, uwbEmptyRadarSweepData); } /** Build RadarData for all Radar Data Type(s). */ public static RadarData generateRadarData(UwbRadarData uwbRadarData) { RadarData.Builder radarDataBuilder = new RadarData.Builder() .setStatusCode(uwbRadarData.statusCode) .setRadarDataType(uwbRadarData.radarDataType) .setSamplesPerSweep(uwbRadarData.samplesPerSweep) .setBitsPerSample(uwbRadarData.bitsPerSample) .setSweepOffset(uwbRadarData.sweepOffset); if (uwbRadarData.radarDataType == RadarParams.RADAR_DATA_TYPE_RADAR_SWEEP_SAMPLES) { for (UwbRadarSweepData sweepData : uwbRadarData.radarSweepData) { radarDataBuilder.addSweepData( new RadarSweepData.Builder() .setSequenceNumber(sweepData.sequenceNumber) .setTimestamp(sweepData.timestamp) .setVendorSpecificData(sweepData.vendorSpecificData) .setSampleData(sweepData.sampleData) .build()); } } return radarDataBuilder.build(); } /** * Helper method to generate a {@link UwbRadarData} instance and a corresponding * {@link RadarData}. */ public static Pair generateUwbRadarDataAndRadarData( int radarDataType) { UwbRadarData uwbRadarData = generateUwbRadarData(radarDataType, TEST_STATUS); RadarData radarData = generateRadarData(uwbRadarData); return Pair.create(uwbRadarData, radarData); } }