1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.ranging.oob; 18 19 import com.android.server.ranging.RangingTechnology; 20 import com.android.server.ranging.blerssi.BleRssiOobConfig; 21 import com.android.server.ranging.cs.CsOobConfig; 22 import com.android.server.ranging.rtt.RttOobConfig; 23 import com.android.server.ranging.uwb.UwbOobConfig; 24 25 import com.google.auto.value.AutoValue; 26 import com.google.common.base.Preconditions; 27 import com.google.common.collect.ImmutableList; 28 29 import java.nio.ByteBuffer; 30 import java.util.Arrays; 31 32 import javax.annotation.Nullable; 33 34 /** The Set Configuration Message Additional Data for Finder OOB. */ 35 @AutoValue 36 public abstract class SetConfigurationMessage { 37 38 public interface TechnologyOobConfig { } 39 40 // Size in bytes of properties when serialized. 41 private static final int MIN_SIZE_BYTES = 4; 42 private static final int RANGING_TECHNOLOGIES_SET_SIZE = 2; 43 private static final int START_RANGING_LIST_SIZE = 2; 44 45 /** 46 * Parses the given byte array and returns {@link SetConfigurationMessage} object. Throws {@link 47 * IllegalArgumentException} on invalid input. 48 */ parseBytes(byte[] payload)49 public static SetConfigurationMessage parseBytes(byte[] payload) { 50 OobHeader header = OobHeader.parseBytes(payload); 51 52 if (header.getMessageType() != MessageType.SET_CONFIGURATION) { 53 throw new IllegalArgumentException( 54 String.format( 55 "Invalid message type: %s, expected %s", 56 header.getMessageType(), MessageType.SET_CONFIGURATION)); 57 } 58 59 if (payload.length < header.getSize() + MIN_SIZE_BYTES) { 60 throw new IllegalArgumentException( 61 String.format( 62 "CapabilityResponseMessage payload size is %d bytes", payload.length)); 63 } 64 65 int parseCursor = header.getSize(); 66 67 // Parse Ranging Technologies Set 68 var rangingTechnologiesSet = 69 RangingTechnology.fromBitmap(Arrays.copyOfRange( 70 payload, parseCursor, parseCursor + RANGING_TECHNOLOGIES_SET_SIZE)); 71 parseCursor += RANGING_TECHNOLOGIES_SET_SIZE; 72 73 // Parse Start Ranging List 74 var startRangingList = 75 RangingTechnology.fromBitmap(Arrays.copyOfRange( 76 payload, parseCursor, parseCursor + START_RANGING_LIST_SIZE)); 77 parseCursor += START_RANGING_LIST_SIZE; 78 79 // Parse Configs for ranging technologies that are set 80 UwbOobConfig uwbConfig = null; 81 CsOobConfig csConfig = null; 82 RttOobConfig rttConfig = null; 83 BleRssiOobConfig bleRssiConfig = null; 84 int countTechsParsed = 0; 85 while (parseCursor < payload.length && countTechsParsed++ < rangingTechnologiesSet.size()) { 86 byte[] remainingBytes = Arrays.copyOfRange(payload, parseCursor, payload.length); 87 TechnologyHeader techHeader = TechnologyHeader.parseBytes(remainingBytes); 88 switch (techHeader.getRangingTechnology()) { 89 case UWB: 90 if (uwbConfig != null) { 91 throw new IllegalArgumentException( 92 "Failed to parse SetConfigurationMessage, UwbConfig already set. " 93 + "Bytes: " + Arrays.toString(payload)); 94 } 95 uwbConfig = UwbOobConfig.parseBytes(remainingBytes); 96 parseCursor += uwbConfig.getSize(); 97 break; 98 case CS: 99 if (csConfig != null) { 100 throw new IllegalArgumentException( 101 "Failed to parse SetConfigurationMessage, CsConfig already set. " 102 + "Bytes: " + Arrays.toString(payload)); 103 } 104 csConfig = CsOobConfig.parseBytes(remainingBytes); 105 parseCursor += csConfig.getSize(); 106 break; 107 case RTT: 108 if (rttConfig != null) { 109 throw new IllegalArgumentException( 110 "Failed to parse SetConfigurationMessage, RttConfig already set. " 111 + "Bytes: " + Arrays.toString(payload)); 112 } 113 rttConfig = RttOobConfig.parseBytes(remainingBytes); 114 parseCursor += rttConfig.getSize(); 115 break; 116 case RSSI: 117 if (bleRssiConfig != null) { 118 throw new IllegalArgumentException( 119 "Failed to parse SetConfigurationMessage, BleRssiConfig already " 120 + "set. Bytes: " + Arrays.toString(payload)); 121 } 122 bleRssiConfig = BleRssiOobConfig.parseBytes(remainingBytes); 123 parseCursor += bleRssiConfig.getSize(); 124 break; 125 default: 126 parseCursor += techHeader.getSize(); 127 } 128 } 129 130 return builder() 131 .setHeader(header) 132 .setRangingTechnologiesSet(rangingTechnologiesSet) 133 .setStartRangingList(startRangingList) 134 .setUwbConfig(uwbConfig) 135 .setCsConfig(csConfig) 136 .setRttConfig(rttConfig) 137 .setBleRssiConfig(bleRssiConfig) 138 .build(); 139 } 140 141 /** Serializes this {@link SetConfigurationMessage} object to bytes. */ toBytes()142 public final byte[] toBytes() { 143 int size = MIN_SIZE_BYTES + getHeader().getSize(); 144 UwbOobConfig uwbConfig = getUwbConfig(); 145 CsOobConfig csConfig = getCsConfig(); 146 RttOobConfig rttConfig = getRttConfig(); 147 BleRssiOobConfig bleRssiConfig = getBleRssiConfig(); 148 if (uwbConfig != null) { 149 size += uwbConfig.getSize(); 150 } 151 if (csConfig != null) { 152 size += csConfig.getSize(); 153 } 154 if (rttConfig != null) { 155 size += rttConfig.getSize(); 156 } 157 if (bleRssiConfig != null) { 158 size += bleRssiConfig.getSize(); 159 } 160 ByteBuffer byteBuffer = ByteBuffer.allocate(size); 161 byteBuffer 162 .put(getHeader().toBytes()) 163 .put(RangingTechnology.toBitmap(getRangingTechnologiesSet())) 164 .put(RangingTechnology.toBitmap(getStartRangingList())); 165 if (uwbConfig != null) { 166 byteBuffer.put(uwbConfig.toBytes()); 167 } 168 if (csConfig != null) { 169 byteBuffer.put(csConfig.toBytes()); 170 } 171 if (rttConfig != null) { 172 byteBuffer.put(rttConfig.toBytes()); 173 } 174 if (bleRssiConfig != null) { 175 byteBuffer.put(bleRssiConfig.toBytes()); 176 } 177 return byteBuffer.array(); 178 } 179 180 /** Returns the OOB header. */ getHeader()181 public abstract OobHeader getHeader(); 182 183 /** Returns a list of ranging technologies that are set as part of this message. */ getRangingTechnologiesSet()184 public abstract ImmutableList<RangingTechnology> getRangingTechnologiesSet(); 185 186 /** 187 * Returns a list of ranging technologies that should start ranging as soon as this message is 188 * received. 189 */ getStartRangingList()190 public abstract ImmutableList<RangingTechnology> getStartRangingList(); 191 192 /** Returns @Nullable UwbConfig data that should be used to configure UWB ranging session. */ 193 @Nullable getUwbConfig()194 public abstract UwbOobConfig getUwbConfig(); 195 196 /** Returns @Nullable CsConfig data that should be used to configure CS ranging session. */ 197 @Nullable getCsConfig()198 public abstract CsOobConfig getCsConfig(); 199 200 @Nullable getRttConfig()201 public abstract RttOobConfig getRttConfig(); 202 203 @Nullable getBleRssiConfig()204 public abstract BleRssiOobConfig getBleRssiConfig(); 205 206 /** Returns a builder for {@link SetConfigurationMessage}. */ builder()207 public static Builder builder() { 208 return new AutoValue_SetConfigurationMessage.Builder() 209 .setRangingTechnologiesSet(ImmutableList.of()) 210 .setStartRangingList(ImmutableList.of()); 211 } 212 213 /** Builder for {@link SetConfigurationMessage}. */ 214 @AutoValue.Builder 215 public abstract static class Builder { 216 setHeader(OobHeader header)217 public abstract Builder setHeader(OobHeader header); 218 setRangingTechnologiesSet( ImmutableList<RangingTechnology> rangingTechnologiesSet)219 public abstract Builder setRangingTechnologiesSet( 220 ImmutableList<RangingTechnology> rangingTechnologiesSet); 221 setStartRangingList( ImmutableList<RangingTechnology> startRangingList )222 public abstract Builder setStartRangingList( 223 ImmutableList<RangingTechnology> startRangingList 224 ); 225 setUwbConfig(@ullable UwbOobConfig uwbConfig)226 public abstract Builder setUwbConfig(@Nullable UwbOobConfig uwbConfig); 227 setCsConfig(@ullable CsOobConfig csConfig)228 public abstract Builder setCsConfig(@Nullable CsOobConfig csConfig); 229 setRttConfig(@ullable RttOobConfig rttConfig)230 public abstract Builder setRttConfig(@Nullable RttOobConfig rttConfig); 231 setBleRssiConfig(@ullable BleRssiOobConfig bleRssiConfig)232 public abstract Builder setBleRssiConfig(@Nullable BleRssiOobConfig bleRssiConfig); 233 setTechnologyConfig(@ullable TechnologyOobConfig config)234 public Builder setTechnologyConfig(@Nullable TechnologyOobConfig config) { 235 return switch (config) { 236 case UwbOobConfig c -> setUwbConfig(c); 237 case CsOobConfig c -> setCsConfig(c); 238 case RttOobConfig c -> setRttConfig(c); 239 case BleRssiOobConfig c -> setBleRssiConfig(c); 240 default -> this; 241 }; 242 } 243 autoBuild()244 abstract SetConfigurationMessage autoBuild(); 245 build()246 public SetConfigurationMessage build() { 247 SetConfigurationMessage setConfigurationMessage = autoBuild(); 248 Preconditions.checkArgument( 249 setConfigurationMessage 250 .getRangingTechnologiesSet() 251 .containsAll(setConfigurationMessage.getStartRangingList()), 252 "startRangingList contains items that are not in rangingTechnologiesSet list."); 253 Preconditions.checkArgument( 254 setConfigurationMessage 255 .getRangingTechnologiesSet() 256 .contains(RangingTechnology.UWB) 257 == (setConfigurationMessage.getUwbConfig() != null), 258 "UwbConfig or rangingTechnologiesSet for UWB not set properly."); 259 Preconditions.checkArgument( 260 setConfigurationMessage 261 .getRangingTechnologiesSet() 262 .contains(RangingTechnology.CS) 263 == (setConfigurationMessage.getCsConfig() != null), 264 "csConfig or rangingTechnologiesSet for CS not set properly."); 265 Preconditions.checkArgument( 266 setConfigurationMessage 267 .getRangingTechnologiesSet() 268 .contains(RangingTechnology.RTT) 269 == (setConfigurationMessage.getRttConfig() != null), 270 "rttConfig or rangingTechnologiesSet for Rtt not set properly."); 271 Preconditions.checkArgument( 272 setConfigurationMessage 273 .getRangingTechnologiesSet() 274 .contains(RangingTechnology.RSSI) 275 == (setConfigurationMessage.getBleRssiConfig() != null), 276 "BleRssiConfig or rangingTechnologiesSet for BLE RSSI not set properly."); 277 return setConfigurationMessage; 278 } 279 } 280 } 281