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