1 /* 2 * Copyright (C) 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.uwb.params; 18 19 import static com.android.server.uwb.config.CapabilityParam.ALIRO_SUPPORTED_MAC_MODES; 20 import static com.android.server.uwb.config.CapabilityParam.CCC_CHANNEL_5; 21 import static com.android.server.uwb.config.CapabilityParam.CCC_CHANNEL_9; 22 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_12; 23 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_24; 24 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_3; 25 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_4; 26 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_6; 27 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_8; 28 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_9; 29 import static com.android.server.uwb.config.CapabilityParam.CCC_EXTENSION_HOPPING_MODE_ADAPTIVE_BITMASK; 30 import static com.android.server.uwb.config.CapabilityParam.CCC_EXTENSION_HOPPING_MODE_CONTINUOUS_BITMASK; 31 import static com.android.server.uwb.config.CapabilityParam.CCC_EXTENSION_HOPPING_MODE_NONE_BITMASK; 32 import static com.android.server.uwb.config.CapabilityParam.CCC_EXTENSION_HOPPING_SEQUENCE_AES_BITMASK; 33 import static com.android.server.uwb.config.CapabilityParam.CCC_EXTENSION_HOPPING_SEQUENCE_DEFAULT_BITMASK; 34 import static com.android.server.uwb.config.CapabilityParam.CCC_HOPPING_CONFIG_MODE_ADAPTIVE; 35 import static com.android.server.uwb.config.CapabilityParam.CCC_HOPPING_CONFIG_MODE_CONTINUOUS; 36 import static com.android.server.uwb.config.CapabilityParam.CCC_HOPPING_CONFIG_MODE_NONE; 37 import static com.android.server.uwb.config.CapabilityParam.CCC_HOPPING_SEQUENCE_AES; 38 import static com.android.server.uwb.config.CapabilityParam.CCC_HOPPING_SEQUENCE_DEFAULT; 39 import static com.android.server.uwb.config.CapabilityParam.CCC_PRIORITIZED_CHANNEL_LIST; 40 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_CHANNELS; 41 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_CHAPS_PER_SLOT; 42 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES; 43 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER; 44 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_MIN_UWB_INITIATION_TIME_MS; 45 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_PULSE_SHAPE_COMBOS; 46 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_RAN_MULTIPLIER; 47 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_SYNC_CODES; 48 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_UWB_CONFIGS; 49 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_UWBS_MAX_PPM; 50 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_VERSIONS; 51 52 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_12; 53 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_24; 54 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_3; 55 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_4; 56 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_6; 57 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_8; 58 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_9; 59 import static com.google.uwb.support.aliro.AliroParams.HOPPING_CONFIG_MODE_ADAPTIVE; 60 import static com.google.uwb.support.aliro.AliroParams.HOPPING_CONFIG_MODE_CONTINUOUS; 61 import static com.google.uwb.support.aliro.AliroParams.HOPPING_CONFIG_MODE_NONE; 62 import static com.google.uwb.support.aliro.AliroParams.HOPPING_SEQUENCE_AES; 63 import static com.google.uwb.support.aliro.AliroParams.HOPPING_SEQUENCE_DEFAULT; 64 import static com.google.uwb.support.aliro.AliroParams.UWB_CHANNEL_5; 65 import static com.google.uwb.support.aliro.AliroParams.UWB_CHANNEL_9; 66 67 import android.util.Log; 68 69 import com.android.server.uwb.UwbInjector; 70 import com.android.server.uwb.config.ConfigParam; 71 72 import com.google.uwb.support.aliro.AliroProtocolVersion; 73 import com.google.uwb.support.aliro.AliroPulseShapeCombo; 74 import com.google.uwb.support.aliro.AliroRangingStartedParams; 75 import com.google.uwb.support.aliro.AliroRangingStoppedParams; 76 import com.google.uwb.support.aliro.AliroSpecificationParams; 77 import com.google.uwb.support.base.Params; 78 import com.google.uwb.support.base.ProtocolVersion; 79 80 import java.nio.ByteBuffer; 81 import java.nio.ByteOrder; 82 83 /** 84 * Aliro decoder - this started out as a copy of the CCC decoder. 85 */ 86 public class AliroDecoder extends TlvDecoder { 87 private static final String TAG = "AliroDecoder"; 88 private final UwbInjector mUwbInjector; 89 AliroDecoder(UwbInjector uwbInjector)90 public AliroDecoder(UwbInjector uwbInjector) { 91 mUwbInjector = uwbInjector; 92 } 93 94 @Override getParams(TlvDecoderBuffer tlvs, Class<T> paramsType, ProtocolVersion protocolVersion)95 public <T extends Params> T getParams(TlvDecoderBuffer tlvs, Class<T> paramsType, 96 ProtocolVersion protocolVersion) 97 throws IllegalArgumentException { 98 if (AliroRangingStartedParams.class.equals(paramsType)) { 99 return (T) getAliroRangingStartedParamsFromTlvBuffer(tlvs); 100 } 101 if (AliroSpecificationParams.class.equals(paramsType)) { 102 return (T) getAliroSpecificationParamsFromTlvBuffer(tlvs); 103 } 104 if (AliroRangingStoppedParams.class.equals(paramsType)) { 105 return (T) getAliroRangingStoppedParamsFromTlvBuffer(tlvs); 106 } 107 return null; 108 } 109 isBitSet(int flags, int mask)110 private static boolean isBitSet(int flags, int mask) { 111 return (flags & mask) != 0; 112 } 113 getAliroRangingStartedParamsFromTlvBuffer( TlvDecoderBuffer tlvs)114 private AliroRangingStartedParams getAliroRangingStartedParamsFromTlvBuffer( 115 TlvDecoderBuffer tlvs) { 116 byte[] hopModeKey = tlvs.getByteArray(ConfigParam.HOP_MODE_KEY); 117 int hopModeKeyInt = ByteBuffer.wrap(hopModeKey).order(ByteOrder.LITTLE_ENDIAN).getInt(); 118 long uwbTime0; 119 // Backwards compatibility with vendors who were using Google defined 120 // UWB_TIME0 TLV param. 121 try { 122 uwbTime0 = tlvs.getLong(ConfigParam.UWB_TIME0); 123 } catch (IllegalArgumentException e) { 124 uwbTime0 = tlvs.getLong(ConfigParam.UWB_INITIATION_TIME); 125 } 126 127 return new AliroRangingStartedParams.Builder() 128 // STS_Index0 0 - 0x3FFFFFFFF 129 .setStartingStsIndex(tlvs.getInt(ConfigParam.STS_INDEX)) 130 .setHopModeKey(hopModeKeyInt) 131 // UWB_Time0 0 - 0xFFFFFFFFFFFFFFFF UWB_INITIATION_TIME 132 .setUwbTime0(uwbTime0) 133 // RANGING_INTERVAL = RAN_Multiplier * 96 134 .setRanMultiplier(tlvs.getInt(ConfigParam.RANGING_INTERVAL) / 96) 135 .setSyncCodeIndex(tlvs.getByte(ConfigParam.PREAMBLE_CODE_INDEX)) 136 .build(); 137 } 138 getAliroSpecificationParamsFromTlvBuffer( TlvDecoderBuffer tlvs)139 private AliroSpecificationParams getAliroSpecificationParamsFromTlvBuffer( 140 TlvDecoderBuffer tlvs) { 141 AliroSpecificationParams.Builder builder = new AliroSpecificationParams.Builder(); 142 byte[] versions = tlvs.getByteArray(CCC_SUPPORTED_VERSIONS); 143 if (versions.length % 2 != 0) { 144 throw new IllegalArgumentException("Invalid supported protocol versions len " 145 + versions.length); 146 } 147 for (int i = 0; i < versions.length; i += 2) { 148 builder.addProtocolVersion(AliroProtocolVersion.fromBytes(versions, i)); 149 } 150 byte[] configs = tlvs.getByteArray(CCC_SUPPORTED_UWB_CONFIGS); 151 for (int i = 0; i < configs.length; i++) { 152 builder.addUwbConfig(configs[i]); 153 } 154 byte[] pulse_shape_combos = tlvs.getByteArray(CCC_SUPPORTED_PULSE_SHAPE_COMBOS); 155 for (int i = 0; i < pulse_shape_combos.length; i++) { 156 builder.addPulseShapeCombo(AliroPulseShapeCombo.fromBytes(pulse_shape_combos, i)); 157 } 158 builder.setRanMultiplier(tlvs.getInt(CCC_SUPPORTED_RAN_MULTIPLIER)); 159 byte chapsPerslot = tlvs.getByte(CCC_SUPPORTED_CHAPS_PER_SLOT); 160 if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_3)) { 161 builder.addChapsPerSlot(CHAPS_PER_SLOT_3); 162 } 163 if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_4)) { 164 builder.addChapsPerSlot(CHAPS_PER_SLOT_4); 165 } 166 if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_6)) { 167 builder.addChapsPerSlot(CHAPS_PER_SLOT_6); 168 } 169 if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_8)) { 170 builder.addChapsPerSlot(CHAPS_PER_SLOT_8); 171 } 172 if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_9)) { 173 builder.addChapsPerSlot(CHAPS_PER_SLOT_9); 174 } 175 if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_12)) { 176 builder.addChapsPerSlot(CHAPS_PER_SLOT_12); 177 } 178 if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_24)) { 179 builder.addChapsPerSlot(CHAPS_PER_SLOT_24); 180 } 181 // TODO(b/321757248): Consider replacing with an Aliro flag. 182 if (mUwbInjector.getDeviceConfigFacade().isCccSupportedSyncCodesLittleEndian()) { 183 byte[] syncCodes = tlvs.getByteArray(CCC_SUPPORTED_SYNC_CODES); 184 for (int byteIndex = 0; byteIndex < syncCodes.length; byteIndex++) { 185 byte syncCodeByte = syncCodes[byteIndex]; 186 for (int bitIndex = 0; bitIndex < 8; bitIndex++) { 187 if ((syncCodeByte & (1 << bitIndex)) != 0) { 188 int syncCodeValue = (byteIndex * 8) + bitIndex + 1; 189 builder.addSyncCode(syncCodeValue); 190 } 191 } 192 } 193 } else { 194 int syncCodes = ByteBuffer.wrap(tlvs.getByteArray(CCC_SUPPORTED_SYNC_CODES)).getInt(); 195 for (int i = 0; i < 32; i++) { 196 if (isBitSet(syncCodes, 1 << i)) { 197 builder.addSyncCode(i + 1); 198 } 199 } 200 } 201 202 try { 203 byte[] prioritizedChannels = tlvs.getByteArray(CCC_PRIORITIZED_CHANNEL_LIST); 204 byte channels = tlvs.getByte(CCC_SUPPORTED_CHANNELS); 205 for (byte prioritizedChannel : prioritizedChannels) { 206 if (isBitSet(channels, CCC_CHANNEL_5) && prioritizedChannel == UWB_CHANNEL_5) { 207 builder.addChannel(prioritizedChannel); 208 } 209 if (isBitSet(channels, CCC_CHANNEL_9) && prioritizedChannel == UWB_CHANNEL_9) { 210 builder.addChannel(prioritizedChannel); 211 } 212 } 213 } catch (IllegalArgumentException e) { 214 Log.w(TAG, "CCC_PRIORITIZED_CHANNEL_LIST not found"); 215 byte channels = tlvs.getByte(CCC_SUPPORTED_CHANNELS); 216 if (isBitSet(channels, CCC_CHANNEL_5)) { 217 builder.addChannel(UWB_CHANNEL_5); 218 } 219 if (isBitSet(channels, CCC_CHANNEL_9)) { 220 builder.addChannel(UWB_CHANNEL_9); 221 } 222 } 223 224 boolean isFiraExtensionSupported = 225 mUwbInjector.getDeviceConfigFacade().isFiraSupportedExtensionForCCC(); 226 byte hoppingConfigModesAndSequences = 227 tlvs.getByte(CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES); 228 if (isBitSet(hoppingConfigModesAndSequences, 229 isFiraExtensionSupported 230 ? CCC_EXTENSION_HOPPING_MODE_NONE_BITMASK : 231 CCC_HOPPING_CONFIG_MODE_NONE)) { 232 builder.addHoppingConfigMode(HOPPING_CONFIG_MODE_NONE); 233 } 234 if (isBitSet(hoppingConfigModesAndSequences, 235 isFiraExtensionSupported 236 ? CCC_EXTENSION_HOPPING_MODE_CONTINUOUS_BITMASK : 237 CCC_HOPPING_CONFIG_MODE_CONTINUOUS)) { 238 builder.addHoppingConfigMode(HOPPING_CONFIG_MODE_CONTINUOUS); 239 } 240 if (isBitSet(hoppingConfigModesAndSequences, 241 isFiraExtensionSupported 242 ? CCC_EXTENSION_HOPPING_MODE_ADAPTIVE_BITMASK : 243 CCC_HOPPING_CONFIG_MODE_ADAPTIVE)) { 244 builder.addHoppingConfigMode(HOPPING_CONFIG_MODE_ADAPTIVE); 245 } 246 if (isBitSet(hoppingConfigModesAndSequences, 247 isFiraExtensionSupported 248 ? CCC_EXTENSION_HOPPING_SEQUENCE_AES_BITMASK : 249 CCC_HOPPING_SEQUENCE_AES)) { 250 builder.addHoppingSequence(HOPPING_SEQUENCE_AES); 251 } 252 if (isBitSet(hoppingConfigModesAndSequences, 253 isFiraExtensionSupported 254 ? CCC_EXTENSION_HOPPING_SEQUENCE_DEFAULT_BITMASK : 255 CCC_HOPPING_SEQUENCE_DEFAULT)) { 256 builder.addHoppingSequence(HOPPING_SEQUENCE_DEFAULT); 257 } 258 259 try { 260 int maxRangingSessionNumber = tlvs.getInt(CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER); 261 builder.setMaxRangingSessionNumber(maxRangingSessionNumber); 262 } catch (IllegalArgumentException e) { 263 Log.w(TAG, "SUPPORTED_MAX_RANGING_SESSION_NUMBER not found"); 264 } 265 266 try { 267 int minUwbInitiationTimeMs = tlvs.getInt(CCC_SUPPORTED_MIN_UWB_INITIATION_TIME_MS); 268 builder.setMinUwbInitiationTimeMs(minUwbInitiationTimeMs); 269 } catch (IllegalArgumentException e) { 270 Log.w(TAG, "SUPPORTED_MIN_UWB_INITIATION_TIME_MS not found"); 271 } 272 273 // Attempt to parse the UWBS_MAX_PPM as a short, since the CCC spec R3 defines the 274 // field Device_max_PPM field (in the TimeSync message) as a 2-octet field. 275 try { 276 short uwbsMaxPPM = tlvs.getShort(CCC_SUPPORTED_UWBS_MAX_PPM); 277 builder.setUwbsMaxPPM(uwbsMaxPPM); 278 } catch (IllegalArgumentException e) { 279 Log.w(TAG, "CCC_SUPPORTED_UWBS_MAX_PPM not found"); 280 } 281 try { 282 byte[] modes = tlvs.getByteArray(ALIRO_SUPPORTED_MAC_MODES); 283 for (int i = 0; i < modes.length; i++) { 284 builder.addMacMode(modes[i]); 285 } 286 } catch (IllegalArgumentException e) { 287 Log.w(TAG, "ALIRO_SUPPORTED_MAC_MODES not found"); 288 } 289 290 return builder.build(); 291 } 292 getAliroRangingStoppedParamsFromTlvBuffer( TlvDecoderBuffer tlvs)293 private AliroRangingStoppedParams getAliroRangingStoppedParamsFromTlvBuffer( 294 TlvDecoderBuffer tlvs) { 295 int lastStsIndexUsed = tlvs.getInt(ConfigParam.LAST_STS_INDEX_USED); 296 return new AliroRangingStoppedParams.Builder() 297 .setLastStsIndexUsed(lastStsIndexUsed) 298 .build(); 299 } 300 } 301