1 /*
2  * Copyright (C) 2022 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.secure.csml;
18 
19 import static com.android.server.uwb.config.CapabilityParam.AOA_AZIMUTH_180;
20 import static com.android.server.uwb.config.CapabilityParam.AOA_AZIMUTH_90;
21 import static com.android.server.uwb.config.CapabilityParam.AOA_ELEVATION;
22 import static com.android.server.uwb.config.CapabilityParam.AOA_FOM;
23 import static com.android.server.uwb.config.CapabilityParam.CC_CONSTRAINT_LENGTH_K3;
24 import static com.android.server.uwb.config.CapabilityParam.CC_CONSTRAINT_LENGTH_K7;
25 import static com.android.server.uwb.config.CapabilityParam.CHANNEL_10;
26 import static com.android.server.uwb.config.CapabilityParam.CHANNEL_12;
27 import static com.android.server.uwb.config.CapabilityParam.CHANNEL_13;
28 import static com.android.server.uwb.config.CapabilityParam.CHANNEL_14;
29 import static com.android.server.uwb.config.CapabilityParam.CHANNEL_5;
30 import static com.android.server.uwb.config.CapabilityParam.CHANNEL_6;
31 import static com.android.server.uwb.config.CapabilityParam.CHANNEL_8;
32 import static com.android.server.uwb.config.CapabilityParam.CHANNEL_9;
33 import static com.android.server.uwb.config.CapabilityParam.DS_TWR_DEFERRED;
34 import static com.android.server.uwb.config.CapabilityParam.DS_TWR_NON_DEFERRED;
35 import static com.android.server.uwb.config.CapabilityParam.DYNAMIC_STS;
36 import static com.android.server.uwb.config.CapabilityParam.DYNAMIC_STS_RESPONDER_SPECIFIC_SUBSESSION_KEY;
37 import static com.android.server.uwb.config.CapabilityParam.INITIATOR;
38 import static com.android.server.uwb.config.CapabilityParam.MANY_TO_MANY;
39 import static com.android.server.uwb.config.CapabilityParam.ONE_TO_MANY;
40 import static com.android.server.uwb.config.CapabilityParam.OWR_UL_TDOA;
41 import static com.android.server.uwb.config.CapabilityParam.RESPONDER;
42 import static com.android.server.uwb.config.CapabilityParam.SP0;
43 import static com.android.server.uwb.config.CapabilityParam.SP1;
44 import static com.android.server.uwb.config.CapabilityParam.SP3;
45 import static com.android.server.uwb.config.CapabilityParam.SS_TWR_DEFERRED;
46 import static com.android.server.uwb.config.CapabilityParam.SS_TWR_NON_DEFERRED;
47 import static com.android.server.uwb.config.CapabilityParam.STATIC_STS;
48 import static com.android.server.uwb.config.CapabilityParam.UNICAST;
49 
50 import static com.google.uwb.support.fira.FiraParams.CONSTRAINT_LENGTH_3;
51 import static com.google.uwb.support.fira.FiraParams.CONSTRAINT_LENGTH_7;
52 import static com.google.uwb.support.fira.FiraParams.CONTENTION_BASED_RANGING;
53 import static com.google.uwb.support.fira.FiraParams.MAC_ADDRESS_MODE_2_BYTES;
54 import static com.google.uwb.support.fira.FiraParams.MAC_ADDRESS_MODE_8_BYTES;
55 import static com.google.uwb.support.fira.FiraParams.RANGING_ROUND_USAGE_DL_TDOA;
56 import static com.google.uwb.support.fira.FiraParams.RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE;
57 import static com.google.uwb.support.fira.FiraParams.RANGING_ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE;
58 import static com.google.uwb.support.fira.FiraParams.RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE;
59 import static com.google.uwb.support.fira.FiraParams.RANGING_ROUND_USAGE_SS_TWR_NON_DEFERRED_MODE;
60 import static com.google.uwb.support.fira.FiraParams.RFRAME_CONFIG_SP0;
61 import static com.google.uwb.support.fira.FiraParams.RFRAME_CONFIG_SP1;
62 import static com.google.uwb.support.fira.FiraParams.RFRAME_CONFIG_SP3;
63 import static com.google.uwb.support.fira.FiraParams.STS_CONFIG_DYNAMIC;
64 import static com.google.uwb.support.fira.FiraParams.STS_CONFIG_DYNAMIC_FOR_CONTROLEE_INDIVIDUAL_KEY;
65 import static com.google.uwb.support.fira.FiraParams.TIME_SCHEDULED_RANGING;
66 
67 import androidx.annotation.NonNull;
68 import androidx.annotation.Nullable;
69 
70 import com.android.server.uwb.config.CapabilityParam;
71 import com.android.server.uwb.params.TlvBuffer;
72 import com.android.server.uwb.params.TlvDecoderBuffer;
73 
74 import com.google.uwb.support.base.FlagEnum;
75 import com.google.uwb.support.fira.FiraParams;
76 import com.google.uwb.support.fira.FiraParams.StsCapabilityFlag;
77 import com.google.uwb.support.fira.FiraProtocolVersion;
78 import com.google.uwb.support.fira.FiraSpecificationParams;
79 
80 import java.util.ArrayList;
81 import java.util.EnumSet;
82 import java.util.List;
83 import java.util.Optional;
84 import java.util.stream.Collectors;
85 
86 /**
87  * UWB_CAPABILITY defined in 8.5.3.2
88  */
89 public class UwbCapability {
90     public static final int FIRA_PHY_VERSION_RANGE = 0x80;
91     public static final int FIRA_MAC_VERSION_RANGE = 0x81;
92     public static final int DEVICE_ROLES = 0x82;
93     public static final int RANGING_METHOD = 0x83;
94     public static final int STS_CONFIG = 0x84;
95     public static final int MULTI_NODE_MODE = 0x85;
96     public static final int RANGING_TIME_STRUCT = 0x86;
97     public static final int SCHEDULED_MODE = 0x87;
98     public static final int HOPPING_MODE = 0x88;
99     public static final int BLOCK_STRIDING = 0x89;
100     public static final int UWB_INITIATION_TIME = 0x8A;
101     public static final int CHANNELS = 0x8B;
102     public static final int RFRAME_CONFIG = 0x8C;
103     public static final int CC_CONSTRAINT_LENGTH = 0x8D;
104     public static final int BPRF_PARAMETER_SETS = 0x8E;
105     public static final int HPRF_PARAMETER_SETS = 0x8F;
106     public static final int AOA_SUPPORT = 0x90;
107     public static final int EXTENDED_MAC_ADDRESS = 0x91;
108     public static final int UWB_CAPABILITY_MAX_COUNT = 18;
109 
110     public static final int DEFAULT_CHANNEL = 9;
111 
112     public final FiraProtocolVersion mMinPhyVersionSupported;
113     public final FiraProtocolVersion mMaxPhyVersionSupported;
114     public final FiraProtocolVersion mMinMacVersionSupported;
115     public final FiraProtocolVersion mMaxMacVersionSupported;
116     public final Optional<EnumSet<FiraParams.DeviceRoleCapabilityFlag>> mDeviceRoles;
117     public final Optional<Byte> mRangingMethod;
118     public final Optional<EnumSet<FiraParams.StsCapabilityFlag>> mStsConfig;
119     public final Optional<EnumSet<FiraParams.MultiNodeCapabilityFlag>> mMultiNodeMode;
120     public final Optional<Byte> mRangingTimeStruct;
121     public final Optional<Byte> mScheduledMode;
122     public final Optional<Boolean> mHoppingMode;
123     public final Optional<Boolean> mBlockStriding;
124     public final Optional<Boolean> mUwbInitiationTime;
125     public final Optional<List<Integer>> mChannels;
126     public final Optional<EnumSet<FiraParams.RframeCapabilityFlag>> mRframeConfig;
127     public final Optional<Byte> mCcConstraintLength;
128     public final Optional<EnumSet<FiraParams.BprfParameterSetCapabilityFlag>> mBprfParameterSet;
129     public final Optional<EnumSet<FiraParams.HprfParameterSetCapabilityFlag>> mHprfParameterSet;
130     public final Optional<EnumSet<FiraParams.AoaCapabilityFlag>> mAoaSupport;
131     public final Optional<Byte> mExtendedMacSupport;
132 
UwbCapability(FiraProtocolVersion minPhyVersionSupported, FiraProtocolVersion maxPhyVersionSupported, FiraProtocolVersion minMacVersionSupported, FiraProtocolVersion maxMacVersionSupported, Optional<EnumSet<FiraParams.DeviceRoleCapabilityFlag>> deviceRoles, Optional<Byte> rangingMethod, Optional<EnumSet<FiraParams.StsCapabilityFlag>> stsConfig, Optional<EnumSet<FiraParams.MultiNodeCapabilityFlag>> multiNodeMode, Optional<Byte> rangingTimeStruct, Optional<Byte> scheduledMode, Optional<Boolean> hoppingMode, Optional<Boolean> blockStriding, Optional<Boolean> uwbInitiationTime, Optional<List<Integer>> channels, Optional<EnumSet<FiraParams.RframeCapabilityFlag>> rframeConfig, Optional<Byte> ccConstraintLength, Optional<EnumSet<FiraParams.BprfParameterSetCapabilityFlag>> bprfParameterSet, Optional<EnumSet<FiraParams.HprfParameterSetCapabilityFlag>> hprfParameterSet, Optional<EnumSet<FiraParams.AoaCapabilityFlag>> aoaSupport, Optional<Byte> extendedMacSupport)133     private UwbCapability(FiraProtocolVersion minPhyVersionSupported,
134             FiraProtocolVersion maxPhyVersionSupported,
135             FiraProtocolVersion minMacVersionSupported,
136             FiraProtocolVersion maxMacVersionSupported,
137             Optional<EnumSet<FiraParams.DeviceRoleCapabilityFlag>> deviceRoles,
138             Optional<Byte> rangingMethod,
139             Optional<EnumSet<FiraParams.StsCapabilityFlag>> stsConfig,
140             Optional<EnumSet<FiraParams.MultiNodeCapabilityFlag>> multiNodeMode,
141             Optional<Byte> rangingTimeStruct,
142             Optional<Byte> scheduledMode,
143             Optional<Boolean> hoppingMode,
144             Optional<Boolean> blockStriding,
145             Optional<Boolean> uwbInitiationTime,
146             Optional<List<Integer>> channels,
147             Optional<EnumSet<FiraParams.RframeCapabilityFlag>> rframeConfig,
148             Optional<Byte> ccConstraintLength,
149             Optional<EnumSet<FiraParams.BprfParameterSetCapabilityFlag>> bprfParameterSet,
150             Optional<EnumSet<FiraParams.HprfParameterSetCapabilityFlag>> hprfParameterSet,
151             Optional<EnumSet<FiraParams.AoaCapabilityFlag>> aoaSupport,
152             Optional<Byte> extendedMacSupport) {
153         mMinPhyVersionSupported = minPhyVersionSupported;
154         mMaxPhyVersionSupported = maxPhyVersionSupported;
155         mMinMacVersionSupported = minMacVersionSupported;
156         mMaxMacVersionSupported = maxMacVersionSupported;
157         mDeviceRoles = deviceRoles;
158         mRangingMethod = rangingMethod;
159         mStsConfig = stsConfig;
160         mMultiNodeMode = multiNodeMode;
161         mRangingTimeStruct = rangingTimeStruct;
162         mScheduledMode = scheduledMode;
163         mHoppingMode = hoppingMode;
164         mBlockStriding = blockStriding;
165         mUwbInitiationTime = uwbInitiationTime;
166         mChannels = channels;
167         mRframeConfig = rframeConfig;
168         mCcConstraintLength = ccConstraintLength;
169         mBprfParameterSet = bprfParameterSet;
170         mHprfParameterSet = hprfParameterSet;
171         mAoaSupport = aoaSupport;
172         mExtendedMacSupport = extendedMacSupport;
173     }
174 
175     /**
176      * Converts the UwbCapabilities to the bytes which are combined per the TLV of CSML 8.5.3.2.
177      */
178     @NonNull
toBytes()179     public byte[] toBytes() {
180         TlvBuffer.Builder uwbCapabilityBuilder = new TlvBuffer.Builder()
181                 .putByteArray(FIRA_PHY_VERSION_RANGE, new byte[]{
182                         (byte) mMinPhyVersionSupported.getMajor(),
183                         (byte) mMinPhyVersionSupported.getMinor(),
184                         (byte) mMaxPhyVersionSupported.getMajor(),
185                         (byte) mMaxPhyVersionSupported.getMinor(),
186                 })
187                 .putByteArray(FIRA_MAC_VERSION_RANGE, new byte[]{
188                         (byte) mMinMacVersionSupported.getMajor(),
189                         (byte) mMinMacVersionSupported.getMinor(),
190                         (byte) mMaxMacVersionSupported.getMajor(),
191                         (byte) mMaxMacVersionSupported.getMinor(),
192                 });
193         if (mDeviceRoles.isPresent()) {
194             byte deviceRoles = 0;
195             if (mDeviceRoles.get().contains(
196                     FiraParams.DeviceRoleCapabilityFlag.HAS_CONTROLEE_RESPONDER_SUPPORT)
197                     && mDeviceRoles.get().contains(
198                     FiraParams.DeviceRoleCapabilityFlag.HAS_CONTROLLER_RESPONDER_SUPPORT)) {
199                 deviceRoles = (byte) (deviceRoles | RESPONDER);
200             }
201             if (mDeviceRoles.get().contains(
202                     FiraParams.DeviceRoleCapabilityFlag.HAS_CONTROLEE_INITIATOR_SUPPORT)
203                     && mDeviceRoles.get().contains(
204                     FiraParams.DeviceRoleCapabilityFlag.HAS_CONTROLLER_INITIATOR_SUPPORT)) {
205                 deviceRoles = (byte) (deviceRoles | INITIATOR);
206             }
207             uwbCapabilityBuilder.putByte(DEVICE_ROLES, deviceRoles);
208         }
209         if (mRangingMethod.isPresent()) {
210             uwbCapabilityBuilder.putByte(RANGING_METHOD, mRangingMethod.get());
211         }
212         if (mStsConfig.isPresent()) {
213             byte stsConfig = 0;
214             if (mStsConfig.get().contains(FiraParams.StsCapabilityFlag.HAS_STATIC_STS_SUPPORT)) {
215                 stsConfig = (byte) (stsConfig | STATIC_STS);
216             }
217             if (mStsConfig.get().contains(FiraParams.StsCapabilityFlag.HAS_DYNAMIC_STS_SUPPORT)) {
218                 stsConfig = (byte) (stsConfig | DYNAMIC_STS);
219             }
220             if (mStsConfig.get().contains(
221                     FiraParams.StsCapabilityFlag
222                             .HAS_DYNAMIC_STS_INDIVIDUAL_CONTROLEE_KEY_SUPPORT)) {
223                 stsConfig = (byte) (stsConfig | DYNAMIC_STS_RESPONDER_SPECIFIC_SUBSESSION_KEY);
224             }
225             uwbCapabilityBuilder.putByte(STS_CONFIG, stsConfig);
226         }
227         if (mMultiNodeMode.isPresent()) {
228             byte multiMode = 0;
229             if (mMultiNodeMode.get().contains(
230                     FiraParams.MultiNodeCapabilityFlag.HAS_UNICAST_SUPPORT)) {
231                 multiMode = (byte) (multiMode | UNICAST);
232             }
233             if (mMultiNodeMode.get().contains(
234                     FiraParams.MultiNodeCapabilityFlag.HAS_ONE_TO_MANY_SUPPORT)) {
235                 multiMode = (byte) (multiMode | ONE_TO_MANY);
236             }
237             if (mMultiNodeMode.get().contains(
238                     FiraParams.MultiNodeCapabilityFlag.HAS_MANY_TO_MANY_SUPPORT)) {
239                 multiMode = (byte) (multiMode | MANY_TO_MANY);
240             }
241             uwbCapabilityBuilder.putByte(MULTI_NODE_MODE, multiMode);
242         }
243         mRangingTimeStruct.ifPresent(
244                 aByte -> uwbCapabilityBuilder.putByte(RANGING_TIME_STRUCT, aByte));
245 
246         mScheduledMode.ifPresent(
247                 aByte -> uwbCapabilityBuilder.putByte(SCHEDULED_MODE, aByte));
248 
249         mHoppingMode.ifPresent(aBoolean -> uwbCapabilityBuilder.putByte(HOPPING_MODE,
250                 (byte) (aBoolean ? 1 : 0)));
251 
252         mBlockStriding.ifPresent(aBoolean -> uwbCapabilityBuilder.putByte(BLOCK_STRIDING,
253                 (byte) (aBoolean ? 1 : 0)));
254 
255         mUwbInitiationTime.ifPresent(aBoolean -> uwbCapabilityBuilder.putByte(UWB_INITIATION_TIME,
256                 (byte) (aBoolean ? 1 : 0)));
257         if (mChannels.isPresent()) {
258             byte channels = 0;
259             if (mChannels.get().contains(5)) {
260                 channels = (byte) (channels | CHANNEL_5);
261             }
262             if (mChannels.get().contains(6)) {
263                 channels = (byte) (channels | CHANNEL_6);
264             }
265             if (mChannels.get().contains(8)) {
266                 channels = (byte) (channels | CHANNEL_8);
267             }
268             if (mChannels.get().contains(9)) {
269                 channels = (byte) (channels | CHANNEL_9);
270             }
271             if (mChannels.get().contains(10)) {
272                 channels = (byte) (channels | CHANNEL_10);
273             }
274             if (mChannels.get().contains(12)) {
275                 channels = (byte) (channels | CHANNEL_12);
276             }
277             if (mChannels.get().contains(13)) {
278                 channels = (byte) (channels | CHANNEL_13);
279             }
280             if (mChannels.get().contains(14)) {
281                 channels = (byte) (channels | CHANNEL_14);
282             }
283             uwbCapabilityBuilder.putByte(CHANNELS, channels);
284         }
285         if (mRframeConfig.isPresent()) {
286             byte rFrameConfig = 0;
287             if (mRframeConfig.get().contains(
288                     FiraParams.RframeCapabilityFlag.HAS_SP0_RFRAME_SUPPORT)) {
289                 rFrameConfig = (byte) (rFrameConfig | SP0);
290             }
291             if (mRframeConfig.get().contains(
292                     FiraParams.RframeCapabilityFlag.HAS_SP1_RFRAME_SUPPORT)) {
293                 rFrameConfig = (byte) (rFrameConfig | SP1);
294             }
295             if (mRframeConfig.get().contains(
296                     FiraParams.RframeCapabilityFlag.HAS_SP3_RFRAME_SUPPORT)) {
297                 rFrameConfig = (byte) (rFrameConfig | SP3);
298             }
299             uwbCapabilityBuilder.putByte(RFRAME_CONFIG, rFrameConfig);
300         }
301         if (mCcConstraintLength.isPresent()) {
302             uwbCapabilityBuilder.putByte(CC_CONSTRAINT_LENGTH, mCcConstraintLength.get());
303         }
304         if (mBprfParameterSet.isPresent()) {
305             byte bprfParameterSet = (byte) FlagEnum.toInt(mBprfParameterSet.get());
306             uwbCapabilityBuilder.putByte(BPRF_PARAMETER_SETS, bprfParameterSet);
307         }
308         if (mHprfParameterSet.isPresent()) {
309             byte hprfParameterSet = (byte) FlagEnum.toInt(mHprfParameterSet.get());
310             uwbCapabilityBuilder.putByte(HPRF_PARAMETER_SETS, hprfParameterSet);
311         }
312         if (mAoaSupport.isPresent()) {
313             byte aoaSupport = 0;
314             if (mAoaSupport.get().contains(FiraParams.AoaCapabilityFlag.HAS_AZIMUTH_SUPPORT)) {
315                 aoaSupport = (byte) (aoaSupport | AOA_AZIMUTH_90);
316             }
317             if (mAoaSupport.get().contains(FiraParams.AoaCapabilityFlag.HAS_FULL_AZIMUTH_SUPPORT)) {
318                 aoaSupport = (byte) (aoaSupport | AOA_AZIMUTH_180);
319             }
320             if (mAoaSupport.get().contains(FiraParams.AoaCapabilityFlag.HAS_ELEVATION_SUPPORT)) {
321                 aoaSupport = (byte) (aoaSupport | AOA_ELEVATION);
322             }
323             if (mAoaSupport.get().contains(FiraParams.AoaCapabilityFlag.HAS_FOM_SUPPORT)) {
324                 aoaSupport = (byte) (aoaSupport | AOA_FOM);
325             }
326             uwbCapabilityBuilder.putByte(AOA_SUPPORT, aoaSupport);
327         }
328         mExtendedMacSupport.ifPresent(
329                 aByte -> uwbCapabilityBuilder.putByte(EXTENDED_MAC_ADDRESS, aByte.byteValue()));
330 
331         return uwbCapabilityBuilder.build().getByteArray();
332     }
333 
isBitSet(int flags, int mask)334     private static boolean isBitSet(int flags, int mask) {
335         return (flags & mask) != 0;
336     }
337 
isPresent(TlvDecoderBuffer tlvDecoderBuffer, int tagType)338     private static boolean isPresent(TlvDecoderBuffer tlvDecoderBuffer, int tagType) {
339         try {
340             tlvDecoderBuffer.getByte(tagType);
341         } catch (IllegalArgumentException e) {
342             try {
343                 tlvDecoderBuffer.getByteArray(tagType);
344             } catch (IllegalArgumentException e1) {
345                 return false;
346             }
347         }
348         return true;
349     }
350 
getRangingMethod(@onNull FiraSpecificationParams firaSpecificationParams)351     private static byte getRangingMethod(@NonNull FiraSpecificationParams firaSpecificationParams) {
352         EnumSet<FiraParams.RangingRoundCapabilityFlag>  rangingRoundCapabilityFlags =
353                 firaSpecificationParams.getRangingRoundCapabilities();
354         int rangingMethod = 0;
355         if (rangingRoundCapabilityFlags.contains(
356                 FiraParams.RangingRoundCapabilityFlag.HAS_DS_TWR_SUPPORT)) {
357             rangingMethod |= DS_TWR_DEFERRED;
358             if (firaSpecificationParams.hasNonDeferredModeSupport()) {
359                 rangingMethod |= DS_TWR_NON_DEFERRED;
360             }
361         }
362         if (rangingRoundCapabilityFlags
363                 .contains(FiraParams.RangingRoundCapabilityFlag.HAS_SS_TWR_SUPPORT)) {
364             rangingMethod |= SS_TWR_DEFERRED;
365             if (firaSpecificationParams.hasNonDeferredModeSupport()) {
366                 rangingMethod |= SS_TWR_NON_DEFERRED;
367             }
368         }
369         return (byte) rangingMethod;
370     }
371 
372     /** Converts the FiRaSpecificationParam to UwbCapability. */
373     @NonNull
fromFiRaSpecificationParam( @onNull FiraSpecificationParams firaSpecificationParams)374     public static UwbCapability fromFiRaSpecificationParam(
375             @NonNull FiraSpecificationParams firaSpecificationParams) {
376         return new UwbCapability.Builder()
377                 .setMinPhyVersionSupported(firaSpecificationParams.getMinPhyVersionSupported())
378                 .setMaxPhyVersionSupported(firaSpecificationParams.getMaxPhyVersionSupported())
379                 .setMinMacVersionSupported(firaSpecificationParams.getMinMacVersionSupported())
380                 .setMaxMacVersionSupported(firaSpecificationParams.getMaxMacVersionSupported())
381                 .setDeviceRoles(firaSpecificationParams.getDeviceRoleCapabilities())
382                 .setRangingMethod(getRangingMethod(firaSpecificationParams))
383                 .setStsConfig(firaSpecificationParams.getStsCapabilities())
384                 .setMultiNodeMode(firaSpecificationParams.getMultiNodeCapabilities())
385                 .setBlockStriding(firaSpecificationParams.hasBlockStridingSupport())
386                 .setUwbInitiationTime(firaSpecificationParams.hasInitiationTimeSupport())
387                 .setChannels(firaSpecificationParams.getSupportedChannels())
388                 .setRFrameConfig(firaSpecificationParams.getRframeCapabilities())
389                 .setCcConstraintLength(getCcConstraintLength(
390                         firaSpecificationParams.getPsduDataRateCapabilities()))
391                 .setBprfParameterSet(firaSpecificationParams.getBprfParameterSetCapabilities())
392                 .setHprfParameterSet(firaSpecificationParams.getHprfParameterSetCapabilities())
393                 .setAoaSupport(firaSpecificationParams.getAoaCapabilities())
394                 .build();
395     }
396 
getCcConstraintLength( EnumSet<FiraParams.PsduDataRateCapabilityFlag> psduDataRateCapabilityFlags)397     private static byte getCcConstraintLength(
398             EnumSet<FiraParams.PsduDataRateCapabilityFlag> psduDataRateCapabilityFlags) {
399         byte ccConstraintLength = (byte) 0;
400         if (psduDataRateCapabilityFlags.isEmpty()
401                 || psduDataRateCapabilityFlags.contains(
402                         FiraParams.PsduDataRateCapabilityFlag.HAS_6M81_SUPPORT)
403                 || psduDataRateCapabilityFlags.contains(
404                         FiraParams.PsduDataRateCapabilityFlag.HAS_27M2_SUPPORT)) {
405             ccConstraintLength |= (byte) CC_CONSTRAINT_LENGTH_K3;
406         }
407         if (psduDataRateCapabilityFlags.contains(
408                         FiraParams.PsduDataRateCapabilityFlag.HAS_7M80_SUPPORT)
409                 || psduDataRateCapabilityFlags.contains(
410                         FiraParams.PsduDataRateCapabilityFlag.HAS_31M2_SUPPORT)) {
411             ccConstraintLength |= (byte) CC_CONSTRAINT_LENGTH_K7;
412         }
413 
414         return ccConstraintLength;
415     }
416 
417     /** Checks if the capabilities are compatible. */
isCompatibleTo(@onNull UwbCapability remoteCap)418     boolean isCompatibleTo(@NonNull UwbCapability remoteCap) {
419         // mac version
420         if (mMinMacVersionSupported.getMajor() > remoteCap.mMaxMacVersionSupported.getMajor()
421                 || mMaxMacVersionSupported.getMajor()
422                         < remoteCap.mMinMacVersionSupported.getMajor()) {
423             return false;
424         } else if (mMinMacVersionSupported.getMinor() > remoteCap.mMaxMacVersionSupported.getMinor()
425                 || mMaxMacVersionSupported.getMinor()
426                         < remoteCap.mMinMacVersionSupported.getMinor()) {
427             return false;
428         }
429 
430         // phy version
431         if (mMinPhyVersionSupported.getMajor() > remoteCap.mMaxPhyVersionSupported.getMajor()
432                 || mMaxPhyVersionSupported.getMajor()
433                         < remoteCap.mMinPhyVersionSupported.getMajor()) {
434             return false;
435         } else if (mMinPhyVersionSupported.getMinor() > remoteCap.mMaxPhyVersionSupported.getMinor()
436                 || mMaxPhyVersionSupported.getMinor()
437                         < remoteCap.mMinPhyVersionSupported.getMinor()) {
438             return false;
439         }
440         return true;
441     }
442 
443     /** Gets the minimum phy version supported by both devices. */
444     @NonNull
getPreferredPhyVersion(FiraProtocolVersion remoteMinPhyVersion)445     FiraProtocolVersion getPreferredPhyVersion(FiraProtocolVersion remoteMinPhyVersion) {
446 
447         if (mMinPhyVersionSupported.getMajor() < remoteMinPhyVersion.getMajor()) {
448             return remoteMinPhyVersion;
449         } else if (mMinPhyVersionSupported.getMajor() > remoteMinPhyVersion.getMajor()) {
450             return mMinPhyVersionSupported;
451         } else if (mMinPhyVersionSupported.getMinor() < remoteMinPhyVersion.getMinor()) {
452             return remoteMinPhyVersion;
453         }
454         return mMinPhyVersionSupported;
455     }
456 
457     /** Gets the minimum mac version supported by both devices. */
458     @NonNull
getPreferredMacVersion(FiraProtocolVersion remoteMinMacVersion)459     FiraProtocolVersion getPreferredMacVersion(FiraProtocolVersion remoteMinMacVersion) {
460 
461         if (mMinMacVersionSupported.getMajor() < remoteMinMacVersion.getMajor()) {
462             return remoteMinMacVersion;
463         } else if (mMinMacVersionSupported.getMajor() > remoteMinMacVersion.getMajor()) {
464             return mMinMacVersionSupported;
465         } else if (mMinMacVersionSupported.getMinor() < remoteMinMacVersion.getMinor()) {
466             return remoteMinMacVersion;
467         }
468         return mMinMacVersionSupported;
469     }
470 
471     @FiraParams.MacAddressMode
getPreferredMacAddressMode(Optional<Byte> remoteExtendedMacSupport)472     int getPreferredMacAddressMode(Optional<Byte> remoteExtendedMacSupport) {
473         if (mExtendedMacSupport.isPresent() && mExtendedMacSupport.get() != 0
474                 && remoteExtendedMacSupport.isPresent() && remoteExtendedMacSupport.get() != 0) {
475             return MAC_ADDRESS_MODE_8_BYTES;
476         }
477         return MAC_ADDRESS_MODE_2_BYTES;
478     }
479 
480     @FiraParams.SchedulingMode
getPreferredScheduleMode(Optional<Byte> remoteScheduleMode)481     int getPreferredScheduleMode(Optional<Byte> remoteScheduleMode) {
482         if (mScheduledMode.isPresent() && remoteScheduleMode.isPresent()
483                 && (mScheduledMode.get() & remoteScheduleMode.get()
484                         & (byte) CapabilityParam.CONTENTION_BASED_RANGING) != 0) {
485             return CONTENTION_BASED_RANGING;
486         }
487         return TIME_SCHEDULED_RANGING;
488     }
489 
490     @FiraParams.RframeConfig
getPreferredRframeConfig( Optional<EnumSet<FiraParams.RframeCapabilityFlag>> remoteRframeConfig)491     int getPreferredRframeConfig(
492             Optional<EnumSet<FiraParams.RframeCapabilityFlag>> remoteRframeConfig) {
493         if (mRframeConfig.isEmpty() || remoteRframeConfig.isEmpty()) {
494             return RFRAME_CONFIG_SP3;
495         }
496         if (mRframeConfig.get().contains(FiraParams.RframeCapabilityFlag.HAS_SP3_RFRAME_SUPPORT)
497                 && remoteRframeConfig.get().contains(
498                         FiraParams.RframeCapabilityFlag.HAS_SP3_RFRAME_SUPPORT)) {
499             return RFRAME_CONFIG_SP3;
500         }
501         if (mRframeConfig.get().contains(FiraParams.RframeCapabilityFlag.HAS_SP1_RFRAME_SUPPORT)
502                 && remoteRframeConfig.get().contains(
503                         FiraParams.RframeCapabilityFlag.HAS_SP1_RFRAME_SUPPORT)) {
504             return RFRAME_CONFIG_SP1;
505         }
506         if (mRframeConfig.get().contains(FiraParams.RframeCapabilityFlag.HAS_SP0_RFRAME_SUPPORT)
507                 && remoteRframeConfig.get().contains(
508                         FiraParams.RframeCapabilityFlag.HAS_SP0_RFRAME_SUPPORT)) {
509             return RFRAME_CONFIG_SP0;
510         }
511         return RFRAME_CONFIG_SP3;
512     }
513 
514     @FiraParams.StsConfig
getPreferredStsConfig( Optional<EnumSet<StsCapabilityFlag>> remoteStsCapFlags, boolean isMultiCast)515     int getPreferredStsConfig(
516             Optional<EnumSet<StsCapabilityFlag>> remoteStsCapFlags,
517             boolean isMultiCast) {
518         if (!isMultiCast) {
519             return STS_CONFIG_DYNAMIC;
520         }
521         if (mStsConfig.isEmpty() && remoteStsCapFlags.isEmpty()) {
522             return STS_CONFIG_DYNAMIC_FOR_CONTROLEE_INDIVIDUAL_KEY;
523         }
524 
525         if ((remoteStsCapFlags.isEmpty() && mStsConfig.get().contains(
526                     StsCapabilityFlag.HAS_DYNAMIC_STS_INDIVIDUAL_CONTROLEE_KEY_SUPPORT))
527                     || (mStsConfig.isEmpty() && remoteStsCapFlags.get().contains(
528                             StsCapabilityFlag.HAS_DYNAMIC_STS_INDIVIDUAL_CONTROLEE_KEY_SUPPORT))
529                     || (mStsConfig.get().contains(
530                             StsCapabilityFlag.HAS_DYNAMIC_STS_INDIVIDUAL_CONTROLEE_KEY_SUPPORT)
531                         && remoteStsCapFlags.get().contains(
532                                 StsCapabilityFlag
533                                         .HAS_DYNAMIC_STS_INDIVIDUAL_CONTROLEE_KEY_SUPPORT))) {
534             return STS_CONFIG_DYNAMIC_FOR_CONTROLEE_INDIVIDUAL_KEY;
535         }
536 
537         return STS_CONFIG_DYNAMIC;
538     }
539 
540     @NonNull
getPreferredChannel(Optional<List<Integer>> remoteChannels)541     Optional<Integer> getPreferredChannel(Optional<List<Integer>> remoteChannels) {
542         if ((mChannels.isEmpty() && remoteChannels.isEmpty())
543                 || (mChannels.isEmpty() && remoteChannels.get().contains(DEFAULT_CHANNEL))
544                 || (remoteChannels.isEmpty() && mChannels.get().contains(DEFAULT_CHANNEL))) {
545             return Optional.of(DEFAULT_CHANNEL);
546         }
547         List<Integer> commonChannels = mChannels.get().stream()
548                 .distinct().filter(remoteChannels.get()::contains)
549                 .collect(Collectors.toList());
550 
551         return commonChannels.stream().findAny();
552     }
553 
getPreferredHoppingMode(Optional<Boolean> remoteHoppingMode)554     boolean getPreferredHoppingMode(Optional<Boolean> remoteHoppingMode) {
555         if (mHoppingMode.isEmpty() || remoteHoppingMode.isEmpty()) {
556             return false;
557         }
558         return mHoppingMode.get() && remoteHoppingMode.get();
559     }
560 
561     @FiraParams.CcConstraintLength
getPreferredConstrainLengthOfConvolutionalCode( Optional<Byte> remoteCcConstrainLength)562     int getPreferredConstrainLengthOfConvolutionalCode(
563             Optional<Byte> remoteCcConstrainLength) {
564         if (mCcConstraintLength.isEmpty() || remoteCcConstrainLength.isEmpty()) {
565             return CONSTRAINT_LENGTH_3;
566         }
567         if ((mCcConstraintLength.get() & remoteCcConstrainLength.get()
568                 & CC_CONSTRAINT_LENGTH_K7) != 0) {
569             return CONSTRAINT_LENGTH_7;
570         }
571         return CONSTRAINT_LENGTH_3;
572     }
573 
574     @FiraParams.RangingRoundUsage
getPreferredRangingMethod(Optional<Byte> remoteRangingMethod)575     int getPreferredRangingMethod(Optional<Byte> remoteRangingMethod) {
576         if (mRangingMethod.isEmpty() || remoteRangingMethod.isEmpty()) {
577             return RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE;
578         }
579 
580         byte rangingMethodMask = (byte) (mRangingMethod.get() & remoteRangingMethod.get());
581 
582         if ((rangingMethodMask & DS_TWR_DEFERRED) != 0) {
583             return RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE;
584         }
585         if ((rangingMethodMask & DS_TWR_NON_DEFERRED) != 0) {
586             return RANGING_ROUND_USAGE_DS_TWR_NON_DEFERRED_MODE;
587         }
588         if ((rangingMethodMask & SS_TWR_DEFERRED) != 0) {
589             return RANGING_ROUND_USAGE_SS_TWR_DEFERRED_MODE;
590         }
591         if ((rangingMethodMask & SS_TWR_NON_DEFERRED) != 0) {
592             return RANGING_ROUND_USAGE_SS_TWR_NON_DEFERRED_MODE;
593         }
594         if (((rangingMethodMask & OWR_UL_TDOA) != 0)) {
595             return RANGING_ROUND_USAGE_DL_TDOA;
596         }
597         return RANGING_ROUND_USAGE_DS_TWR_DEFERRED_MODE;
598     }
599 
getPreferredBlockStriding(Optional<Boolean> remoteBlockStriding)600     boolean getPreferredBlockStriding(Optional<Boolean> remoteBlockStriding) {
601         if (mBlockStriding.isEmpty() || remoteBlockStriding.isEmpty()) {
602             return false;
603         }
604         return mBlockStriding.get() && remoteBlockStriding.get();
605     }
606 
607     /**
608      * Converts the UwbCapabilities from the data stream, which is encoded per the CSML 8.5.3.2.
609      *
610      * @return null if the data cannot be decoded per spec.
611      */
612     @Nullable
fromBytes(@onNull byte[] data)613     public static UwbCapability fromBytes(@NonNull byte[] data) {
614         TlvDecoderBuffer uwbCapabilityTlv = new TlvDecoderBuffer(data, UWB_CAPABILITY_MAX_COUNT);
615         uwbCapabilityTlv.parse();
616         UwbCapability.Builder uwbCapabilityBuilder = new UwbCapability.Builder();
617 
618         if (isPresent(uwbCapabilityTlv, FIRA_PHY_VERSION_RANGE)) {
619             byte[] firaPhyVersionRange = uwbCapabilityTlv.getByteArray(FIRA_PHY_VERSION_RANGE);
620             if (firaPhyVersionRange.length == 4) {
621                 FiraProtocolVersion minVersion = new FiraProtocolVersion(firaPhyVersionRange[0],
622                         firaPhyVersionRange[1]);
623                 FiraProtocolVersion maxVersion = new FiraProtocolVersion(firaPhyVersionRange[2],
624                         firaPhyVersionRange[3]);
625                 uwbCapabilityBuilder.setMinPhyVersionSupported(minVersion);
626                 uwbCapabilityBuilder.setMaxPhyVersionSupported(maxVersion);
627             }
628         }
629         if (isPresent(uwbCapabilityTlv, FIRA_MAC_VERSION_RANGE)) {
630             byte[] firaMacVersionRange = uwbCapabilityTlv.getByteArray(FIRA_MAC_VERSION_RANGE);
631             if (firaMacVersionRange.length == 4) {
632                 FiraProtocolVersion minVersion = new FiraProtocolVersion(firaMacVersionRange[0],
633                         firaMacVersionRange[1]);
634                 FiraProtocolVersion maxVersion = new FiraProtocolVersion(firaMacVersionRange[2],
635                         firaMacVersionRange[3]);
636                 uwbCapabilityBuilder.setMinMacVersionSupported(minVersion);
637                 uwbCapabilityBuilder.setMaxMacVersionSupported(maxVersion);
638             }
639         }
640         if (isPresent(uwbCapabilityTlv, DEVICE_ROLES)) {
641             EnumSet<FiraParams.DeviceRoleCapabilityFlag> deviceRoles = EnumSet.noneOf(
642                     FiraParams.DeviceRoleCapabilityFlag.class);
643             byte deviceRolesRaw = uwbCapabilityTlv.getByte(DEVICE_ROLES);
644             if (isBitSet(deviceRolesRaw, INITIATOR)) {
645                 deviceRoles.add(
646                         FiraParams.DeviceRoleCapabilityFlag.HAS_CONTROLEE_INITIATOR_SUPPORT);
647                 deviceRoles.add(
648                         FiraParams.DeviceRoleCapabilityFlag.HAS_CONTROLLER_INITIATOR_SUPPORT);
649             }
650             if (isBitSet(deviceRolesRaw, RESPONDER)) {
651                 deviceRoles.add(
652                         FiraParams.DeviceRoleCapabilityFlag.HAS_CONTROLEE_RESPONDER_SUPPORT);
653                 deviceRoles.add(
654                         FiraParams.DeviceRoleCapabilityFlag.HAS_CONTROLLER_RESPONDER_SUPPORT);
655             }
656             uwbCapabilityBuilder.setDeviceRoles(deviceRoles);
657         }
658         if (isPresent(uwbCapabilityTlv, RANGING_METHOD)) {
659             uwbCapabilityBuilder.setRangingMethod(uwbCapabilityTlv.getByte(RANGING_METHOD));
660         }
661         if (isPresent(uwbCapabilityTlv, STS_CONFIG)) {
662             EnumSet<FiraParams.StsCapabilityFlag> stsConfig = EnumSet.noneOf(
663                     FiraParams.StsCapabilityFlag.class);
664             byte stsConfigRaw = uwbCapabilityTlv.getByte(STS_CONFIG);
665             if (isBitSet(stsConfigRaw, STATIC_STS)) {
666                 stsConfig.add(FiraParams.StsCapabilityFlag.HAS_STATIC_STS_SUPPORT);
667             }
668             if (isBitSet(stsConfigRaw, DYNAMIC_STS)) {
669                 stsConfig.add(FiraParams.StsCapabilityFlag.HAS_DYNAMIC_STS_SUPPORT);
670             }
671             if (isBitSet(stsConfigRaw, DYNAMIC_STS_RESPONDER_SPECIFIC_SUBSESSION_KEY)) {
672                 stsConfig.add(
673                         FiraParams.StsCapabilityFlag
674                                 .HAS_DYNAMIC_STS_INDIVIDUAL_CONTROLEE_KEY_SUPPORT);
675             }
676             uwbCapabilityBuilder.setStsConfig(stsConfig);
677         }
678         if (isPresent(uwbCapabilityTlv, MULTI_NODE_MODE)) {
679             EnumSet<FiraParams.MultiNodeCapabilityFlag> multiNodeMode = EnumSet.noneOf(
680                     FiraParams.MultiNodeCapabilityFlag.class);
681             byte multiNodeRaw = uwbCapabilityTlv.getByte(MULTI_NODE_MODE);
682             if (isBitSet(multiNodeRaw, UNICAST)) {
683                 multiNodeMode.add(FiraParams.MultiNodeCapabilityFlag.HAS_UNICAST_SUPPORT);
684             }
685             if (isBitSet(multiNodeRaw, ONE_TO_MANY)) {
686                 multiNodeMode.add(FiraParams.MultiNodeCapabilityFlag.HAS_ONE_TO_MANY_SUPPORT);
687             }
688             if (isBitSet(multiNodeRaw, MANY_TO_MANY)) {
689                 multiNodeMode.add(FiraParams.MultiNodeCapabilityFlag.HAS_MANY_TO_MANY_SUPPORT);
690             }
691             uwbCapabilityBuilder.setMultiMode(multiNodeMode);
692         }
693         if (isPresent(uwbCapabilityTlv, RANGING_TIME_STRUCT)) {
694             uwbCapabilityBuilder.setRangingTimeStruct(
695                     uwbCapabilityTlv.getByte(RANGING_TIME_STRUCT));
696         }
697         if (isPresent(uwbCapabilityTlv, SCHEDULED_MODE)) {
698             uwbCapabilityBuilder.setScheduledMode(uwbCapabilityTlv.getByte(SCHEDULED_MODE));
699         }
700         if (isPresent(uwbCapabilityTlv, HOPPING_MODE)) {
701             uwbCapabilityBuilder.setHoppingMode(uwbCapabilityTlv.getByte(HOPPING_MODE) == 1);
702         }
703         if (isPresent(uwbCapabilityTlv, BLOCK_STRIDING)) {
704             uwbCapabilityBuilder.setBlockStriding(uwbCapabilityTlv.getByte(BLOCK_STRIDING) == 1);
705         }
706         if (isPresent(uwbCapabilityTlv, UWB_INITIATION_TIME)) {
707             uwbCapabilityBuilder.setUwbInitiationTime(
708                     uwbCapabilityTlv.getByte(UWB_INITIATION_TIME) == 1);
709         }
710         if (isPresent(uwbCapabilityTlv, CHANNELS)) {
711             List<Integer> channels = new ArrayList<>();
712             byte channelsRaw = uwbCapabilityTlv.getByte(CHANNELS);
713             if (isBitSet(channelsRaw, CHANNEL_5)) {
714                 channels.add(5);
715             }
716             if (isBitSet(channelsRaw, CHANNEL_6)) {
717                 channels.add(6);
718             }
719             if (isBitSet(channelsRaw, CHANNEL_8)) {
720                 channels.add(8);
721             }
722             if (isBitSet(channelsRaw, CHANNEL_9)) {
723                 channels.add(9);
724             }
725             if (isBitSet(channelsRaw, CHANNEL_10)) {
726                 channels.add(10);
727             }
728             if (isBitSet(channelsRaw, CHANNEL_12)) {
729                 channels.add(12);
730             }
731             if (isBitSet(channelsRaw, CHANNEL_13)) {
732                 channels.add(13);
733             }
734             if (isBitSet(channelsRaw, CHANNEL_14)) {
735                 channels.add(14);
736             }
737             uwbCapabilityBuilder.setChannels(channels);
738         }
739         if (isPresent(uwbCapabilityTlv, RFRAME_CONFIG)) {
740             EnumSet<FiraParams.RframeCapabilityFlag> rFrameConfig = EnumSet.noneOf(
741                     FiraParams.RframeCapabilityFlag.class);
742             byte rFrameConfigRaw = uwbCapabilityTlv.getByte(RFRAME_CONFIG);
743             if (isBitSet(rFrameConfigRaw, SP0)) {
744                 rFrameConfig.add(FiraParams.RframeCapabilityFlag.HAS_SP0_RFRAME_SUPPORT);
745             }
746             if (isBitSet(rFrameConfigRaw, SP1)) {
747                 rFrameConfig.add(FiraParams.RframeCapabilityFlag.HAS_SP1_RFRAME_SUPPORT);
748             }
749             if (isBitSet(rFrameConfigRaw, SP3)) {
750                 rFrameConfig.add(FiraParams.RframeCapabilityFlag.HAS_SP3_RFRAME_SUPPORT);
751             }
752             uwbCapabilityBuilder.setRFrameConfig(rFrameConfig);
753         }
754         if (isPresent(uwbCapabilityTlv, CC_CONSTRAINT_LENGTH)) {
755             byte ccConstraintLength = uwbCapabilityTlv.getByte(CC_CONSTRAINT_LENGTH);
756             uwbCapabilityBuilder.setCcConstraintLength(ccConstraintLength);
757         }
758         if (isPresent(uwbCapabilityTlv, AOA_SUPPORT)) {
759             EnumSet<FiraParams.AoaCapabilityFlag> aoaSupport = EnumSet.noneOf(
760                     FiraParams.AoaCapabilityFlag.class);
761             byte aoaSupportRaw = uwbCapabilityTlv.getByte(AOA_SUPPORT);
762             if (isBitSet(aoaSupportRaw, AOA_AZIMUTH_90)) {
763                 aoaSupport.add(FiraParams.AoaCapabilityFlag.HAS_AZIMUTH_SUPPORT);
764             }
765             if (isBitSet(aoaSupportRaw, AOA_AZIMUTH_180)) {
766                 aoaSupport.add(FiraParams.AoaCapabilityFlag.HAS_FULL_AZIMUTH_SUPPORT);
767             }
768             if (isBitSet(aoaSupportRaw, AOA_ELEVATION)) {
769                 aoaSupport.add(FiraParams.AoaCapabilityFlag.HAS_ELEVATION_SUPPORT);
770             }
771             if (isBitSet(aoaSupportRaw, AOA_FOM)) {
772                 aoaSupport.add(FiraParams.AoaCapabilityFlag.HAS_FOM_SUPPORT);
773             }
774             uwbCapabilityBuilder.setAoaSupport(aoaSupport);
775         }
776         if (isPresent(uwbCapabilityTlv, BPRF_PARAMETER_SETS)) {
777             byte bprfSets = uwbCapabilityTlv.getByte(BPRF_PARAMETER_SETS);
778             int bprfSetsValue = Integer.valueOf(bprfSets);
779             EnumSet<FiraParams.BprfParameterSetCapabilityFlag> bprfFlag;
780             bprfFlag = FlagEnum.toEnumSet(bprfSetsValue,
781                     FiraParams.BprfParameterSetCapabilityFlag.values());
782             uwbCapabilityBuilder.setBprfParameterSet(bprfFlag);
783         }
784         if (isPresent(uwbCapabilityTlv, HPRF_PARAMETER_SETS)) {
785             byte hprfSets = uwbCapabilityTlv.getByte(HPRF_PARAMETER_SETS);
786             int hprfSetsValue = Integer.valueOf(hprfSets);
787             EnumSet<FiraParams.HprfParameterSetCapabilityFlag> hprfFlag;
788             hprfFlag = FlagEnum.toEnumSet(hprfSetsValue,
789                     FiraParams.HprfParameterSetCapabilityFlag.values());
790             uwbCapabilityBuilder.setHprfParameterSet(hprfFlag);
791         }
792         if (isPresent(uwbCapabilityTlv, EXTENDED_MAC_ADDRESS)) {
793             uwbCapabilityBuilder.setExtendedMacSupport(
794                     uwbCapabilityTlv.getByte(EXTENDED_MAC_ADDRESS));
795         }
796         return uwbCapabilityBuilder.build();
797     }
798 
799     /** Builder for UwbCapabilities */
800     public static class Builder {
801         // Set all default protocol version to FiRa 1.1
802         private FiraProtocolVersion mMinPhyVersionSupported = new FiraProtocolVersion(1, 1);
803         private FiraProtocolVersion mMaxPhyVersionSupported = new FiraProtocolVersion(1, 1);
804         private FiraProtocolVersion mMinMacVersionSupported = new FiraProtocolVersion(1, 1);
805         private FiraProtocolVersion mMaxMacVersionSupported = new FiraProtocolVersion(1, 1);
806         private Optional<EnumSet<FiraParams.DeviceRoleCapabilityFlag>> mDeviceRoles =
807                 Optional.empty();
808         private Optional<Byte> mRangingMethod = Optional.empty();
809         private Optional<EnumSet<FiraParams.StsCapabilityFlag>> mStsConfig = Optional.empty();
810         private Optional<EnumSet<FiraParams.MultiNodeCapabilityFlag>> mMultiNodeMode =
811                 Optional.empty();
812         private Optional<Byte> mRangingTimeStruct = Optional.empty();
813         private Optional<Byte> mScheduledMode = Optional.empty();
814         private Optional<Boolean> mHoppingMode = Optional.empty();
815         private Optional<Boolean> mBlockStriding = Optional.empty();
816         private Optional<Boolean> mUwbInitiationTime = Optional.empty();
817         private Optional<List<Integer>> mChannels = Optional.empty();
818         private Optional<EnumSet<FiraParams.RframeCapabilityFlag>> mRframeConfig = Optional.empty();
819         private Optional<Byte> mCcConstraintLength =
820                 Optional.empty();
821         private Optional<EnumSet<FiraParams.BprfParameterSetCapabilityFlag>> mBprfParameterSet =
822                 Optional.empty();
823         private Optional<EnumSet<FiraParams.HprfParameterSetCapabilityFlag>> mHprfParameterSet =
824                 Optional.empty();
825         private Optional<EnumSet<FiraParams.AoaCapabilityFlag>> mAoaSupport = Optional.empty();
826         private Optional<Byte> mExtendedMacSupport = Optional.empty();
827 
setMinPhyVersionSupported( FiraProtocolVersion minPhyVersionSupported)828         UwbCapability.Builder setMinPhyVersionSupported(
829                 FiraProtocolVersion minPhyVersionSupported) {
830             mMinPhyVersionSupported = minPhyVersionSupported;
831             return this;
832         }
833 
setMaxPhyVersionSupported( FiraProtocolVersion maxPhyVersionSupported)834         UwbCapability.Builder setMaxPhyVersionSupported(
835                 FiraProtocolVersion maxPhyVersionSupported) {
836             mMaxPhyVersionSupported = maxPhyVersionSupported;
837             return this;
838         }
839 
setMinMacVersionSupported( FiraProtocolVersion minMacVersionSupported)840         UwbCapability.Builder setMinMacVersionSupported(
841                 FiraProtocolVersion minMacVersionSupported) {
842             mMinMacVersionSupported = minMacVersionSupported;
843             return this;
844         }
845 
setMaxMacVersionSupported( FiraProtocolVersion maxMacVersionSupported)846         UwbCapability.Builder setMaxMacVersionSupported(
847                 FiraProtocolVersion maxMacVersionSupported) {
848             mMaxMacVersionSupported = maxMacVersionSupported;
849             return this;
850         }
851 
setDeviceRoles( EnumSet<FiraParams.DeviceRoleCapabilityFlag> deviceRoles)852         UwbCapability.Builder setDeviceRoles(
853                 EnumSet<FiraParams.DeviceRoleCapabilityFlag> deviceRoles) {
854             mDeviceRoles = Optional.of(deviceRoles);
855             return this;
856         }
857 
setRangingMethod( byte rangingMethod)858         UwbCapability.Builder setRangingMethod(
859                 byte rangingMethod) {
860             mRangingMethod = Optional.of(rangingMethod);
861             return this;
862         }
863 
setStsConfig( EnumSet<FiraParams.StsCapabilityFlag> stsConfig)864         UwbCapability.Builder setStsConfig(
865                 EnumSet<FiraParams.StsCapabilityFlag> stsConfig) {
866             mStsConfig = Optional.of(stsConfig);
867             return this;
868         }
869 
setMultiMode( EnumSet<FiraParams.MultiNodeCapabilityFlag> multiNodeMode)870         UwbCapability.Builder setMultiMode(
871                 EnumSet<FiraParams.MultiNodeCapabilityFlag> multiNodeMode) {
872             mMultiNodeMode = Optional.of(multiNodeMode);
873             return this;
874         }
875 
setRangingTimeStruct(Byte rangingTimeStruct)876         UwbCapability.Builder setRangingTimeStruct(Byte rangingTimeStruct) {
877             mRangingTimeStruct = Optional.of(rangingTimeStruct);
878             return this;
879         }
880 
setScheduledMode(Byte scheduledMode)881         UwbCapability.Builder setScheduledMode(Byte scheduledMode) {
882             mScheduledMode = Optional.of(scheduledMode);
883             return this;
884         }
885 
setHoppingMode(Boolean hoppingMode)886         UwbCapability.Builder setHoppingMode(Boolean hoppingMode) {
887             mHoppingMode = Optional.of(hoppingMode);
888             return this;
889         }
890 
setBlockStriding(Boolean blockStriding)891         UwbCapability.Builder setBlockStriding(Boolean blockStriding) {
892             mBlockStriding = Optional.of(blockStriding);
893             return this;
894         }
895 
setUwbInitiationTime(Boolean uwbInitiationTime)896         UwbCapability.Builder setUwbInitiationTime(Boolean uwbInitiationTime) {
897             mUwbInitiationTime = Optional.of(uwbInitiationTime);
898             return this;
899         }
900 
setChannels(List<Integer> channels)901         UwbCapability.Builder setChannels(List<Integer> channels) {
902             mChannels = Optional.of(channels);
903             return this;
904         }
905 
setMultiNodeMode( EnumSet<FiraParams.MultiNodeCapabilityFlag> multiNodeMode)906         UwbCapability.Builder setMultiNodeMode(
907                 EnumSet<FiraParams.MultiNodeCapabilityFlag> multiNodeMode) {
908             mMultiNodeMode = Optional.of(multiNodeMode);
909             return this;
910         }
911 
setRFrameConfig( EnumSet<FiraParams.RframeCapabilityFlag> rFrameConfig)912         UwbCapability.Builder setRFrameConfig(
913                 EnumSet<FiraParams.RframeCapabilityFlag> rFrameConfig) {
914             mRframeConfig = Optional.of(rFrameConfig);
915             return this;
916         }
917 
setCcConstraintLength(byte ccConstraintLength)918         UwbCapability.Builder setCcConstraintLength(byte ccConstraintLength) {
919             mCcConstraintLength = Optional.of(ccConstraintLength);
920             return this;
921         }
922 
setBprfParameterSet( EnumSet<FiraParams.BprfParameterSetCapabilityFlag> bprfParameterSet)923         UwbCapability.Builder setBprfParameterSet(
924                 EnumSet<FiraParams.BprfParameterSetCapabilityFlag> bprfParameterSet) {
925             mBprfParameterSet = Optional.of(bprfParameterSet);
926             return this;
927         }
928 
setHprfParameterSet( EnumSet<FiraParams.HprfParameterSetCapabilityFlag> hprfParameterSet)929         UwbCapability.Builder setHprfParameterSet(
930                 EnumSet<FiraParams.HprfParameterSetCapabilityFlag> hprfParameterSet) {
931             mHprfParameterSet = Optional.of(hprfParameterSet);
932             return this;
933         }
934 
setAoaSupport( EnumSet<FiraParams.AoaCapabilityFlag> aoaSupport)935         UwbCapability.Builder setAoaSupport(
936                 EnumSet<FiraParams.AoaCapabilityFlag> aoaSupport) {
937             mAoaSupport = Optional.of(aoaSupport);
938             return this;
939         }
940 
setExtendedMacSupport(Byte extendedMacSupport)941         UwbCapability.Builder setExtendedMacSupport(Byte extendedMacSupport) {
942             mExtendedMacSupport = Optional.of(extendedMacSupport);
943             return this;
944         }
945 
build()946         UwbCapability build() {
947             return new UwbCapability(
948                     mMinPhyVersionSupported,
949                     mMaxPhyVersionSupported,
950                     mMinMacVersionSupported,
951                     mMaxMacVersionSupported,
952                     mDeviceRoles,
953                     mRangingMethod,
954                     mStsConfig,
955                     mMultiNodeMode,
956                     mRangingTimeStruct,
957                     mScheduledMode,
958                     mHoppingMode,
959                     mBlockStriding,
960                     mUwbInitiationTime,
961                     mChannels,
962                     mRframeConfig,
963                     mCcConstraintLength,
964                     mBprfParameterSet,
965                     mHprfParameterSet,
966                     mAoaSupport,
967                     mExtendedMacSupport
968             );
969         }
970     }
971 }
972