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