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.google.uwb.support.fira; 18 19 import static java.util.Objects.requireNonNull; 20 21 import android.os.PersistableBundle; 22 import android.uwb.UwbManager; 23 24 import com.google.uwb.support.base.FlagEnum; 25 26 import java.util.ArrayList; 27 import java.util.Collection; 28 import java.util.EnumSet; 29 import java.util.List; 30 31 /** 32 * Defines parameters for FIRA capability. 33 * 34 * <p>This is returned as a bundle from the service API {@link UwbManager#getSpecificationInfo}. 35 */ 36 public class FiraSpecificationParams extends FiraParams { 37 private static final int BUNDLE_VERSION_1 = 1; 38 private static final int BUNDLE_VERSION_2 = 2; 39 private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_2; 40 41 private final FiraProtocolVersion mMinPhyVersionSupported; 42 private final FiraProtocolVersion mMaxPhyVersionSupported; 43 private final FiraProtocolVersion mMinMacVersionSupported; 44 private final FiraProtocolVersion mMaxMacVersionSupported; 45 46 private final List<Integer> mSupportedChannels; 47 48 private final EnumSet<AoaCapabilityFlag> mAoaCapabilities; 49 50 private final EnumSet<DeviceRoleCapabilityFlag> mDeviceRoleCapabilities; 51 52 private final boolean mHasBlockStridingSupport; 53 54 private final boolean mHasHoppingPreferenceSupport; 55 56 private final boolean mHasExtendedMacAddressSupport; 57 58 private final boolean mHasNonDeferredModeSupport; 59 60 private final boolean mHasInitiationTimeSupport; 61 62 private final boolean mHasRssiReportingSupport; 63 64 private final boolean mHasDiagnosticsSupport; 65 66 private final int mMinRangingInterval; 67 68 private final int mMinSlotDuration; 69 70 private final int mMaxRangingSessionNumber; 71 72 private final EnumSet<MultiNodeCapabilityFlag> mMultiNodeCapabilities; 73 74 private final EnumSet<RangingTimeStructCapabilitiesFlag> mRangingTimeStructCapabilities; 75 76 private final EnumSet<SchedulingModeCapabilitiesFlag> mSchedulingModeCapabilities; 77 78 private final EnumSet<CcConstraintLengthCapabilitiesFlag> mCcConstraintLengthCapabilities; 79 80 private final EnumSet<PrfCapabilityFlag> mPrfCapabilities; 81 82 private final EnumSet<RangingRoundCapabilityFlag> mRangingRoundCapabilities; 83 84 private final EnumSet<RframeCapabilityFlag> mRframeCapabilities; 85 86 private final EnumSet<StsCapabilityFlag> mStsCapabilities; 87 88 private final EnumSet<PsduDataRateCapabilityFlag> mPsduDataRateCapabilities; 89 90 private final EnumSet<BprfParameterSetCapabilityFlag> mBprfParameterSetCapabilities; 91 92 private final EnumSet<HprfParameterSetCapabilityFlag> mHprfParameterSetCapabilities; 93 94 private final Integer mMaxMessageSize; 95 96 private final Integer mMaxDataPacketPayloadSize; 97 98 private final EnumSet<RangeDataNtfConfigCapabilityFlag> mRangeDataNtfConfigCapabilities; 99 100 private final int mDeviceType; 101 102 private final boolean mSuspendRangingSupport; 103 104 private final int mSessionKeyLength; 105 106 private final int mDtTagMaxActiveRr; 107 108 private static final String KEY_MIN_PHY_VERSION = "min_phy_version"; 109 private static final String KEY_MAX_PHY_VERSION = "max_phy_version"; 110 private static final String KEY_MIN_MAC_VERSION = "min_mac_version"; 111 private static final String KEY_MAX_MAC_VERSION = "max_mac_version"; 112 113 private static final String KEY_SUPPORTED_CHANNELS = "channels"; 114 private static final String KEY_AOA_CAPABILITIES = "aoa_capabilities"; 115 private static final String KEY_DEVICE_ROLE_CAPABILITIES = "device_role_capabilities"; 116 private static final String KEY_BLOCK_STRIDING_SUPPORT = "block_striding"; 117 private static final String KEY_HOPPING_PREFERENCE_SUPPORT = "hopping_preference"; 118 private static final String KEY_EXTENDED_MAC_ADDRESS_SUPPORT = "extended_mac_address"; 119 private static final String KEY_NON_DEFERRED_MODE_SUPPORT = "non_deferred_mode"; 120 private static final String KEY_INITIATION_TIME_SUPPORT = "initiation_time"; 121 private static final String KEY_RSSI_REPORTING_SUPPORT = "rssi_reporting"; 122 private static final String KEY_DIAGNOSTICS_SUPPORT = "diagnostics"; 123 private static final String KEY_MIN_RANGING_INTERVAL = "min_ranging_interval"; 124 private static final String KEY_MIN_SLOT_DURATION = "min_slot_duration"; 125 private static final String KEY_MAX_RANGING_SESSION_NUMBER = "max_ranging_session_number"; 126 private static final String KEY_MULTI_NODE_CAPABILITIES = "multi_node_capabilities"; 127 private static final String KEY_RANGING_TIME_STRUCT_CAPABILITIES = 128 "ranging_time_struct_capabilities"; 129 private static final String KEY_SCHEDULING_MODE_CAPABILITIES = "scheduling_mode_capabilities"; 130 private static final String KEY_CC_CONSTRAINT_LENGTH_CAPABILITIES = 131 "cc_constraint_length_capabilities"; 132 private static final String KEY_PRF_CAPABILITIES = "prf_capabilities"; 133 private static final String KEY_RANGING_ROUND_CAPABILITIES = 134 "ranging_round_capabilities"; 135 private static final String KEY_RFRAME_CAPABILITIES = "rframe_capabilities"; 136 private static final String KEY_STS_CAPABILITIES = "sts_capabilities"; 137 private static final String KEY_PSDU_DATA_RATE_CAPABILITIES = "psdu_data_rate_capabilities"; 138 private static final String KEY_BPRF_PARAMETER_SET_CAPABILITIES = 139 "bprf_parameter_set_capabilities"; 140 private static final String KEY_HPRF_PARAMETER_SET_CAPABILITIES = 141 "hprf_parameter_set_capabilities"; 142 private static final String KEY_MAX_MESSAGE_SIZE = "max_message_size"; 143 private static final String KEY_MAX_DATA_PACKET_PAYLOAD_SIZE = "max_data_packet_payload_size"; 144 private static final String KEY_RANGE_DATA_NTF_CONFIG_CAPABILITIES = 145 "range_data_ntf_config_capabilities"; 146 private static final String KEY_DEVICE_TYPE = 147 "device_type"; 148 private static final String KEY_SUSPEND_RANGING_SUPPORT = 149 "suspend_ranging_support"; 150 private static final String KEY_SESSION_KEY_LENGTH = 151 "session_key_length"; 152 public static final String DT_TAG_MAX_ACTIVE_RR = "dt_tag_max_active_rr"; 153 154 public static final int DEFAULT_MAX_RANGING_SESSIONS_NUMBER = 5; 155 FiraSpecificationParams( FiraProtocolVersion minPhyVersionSupported, FiraProtocolVersion maxPhyVersionSupported, FiraProtocolVersion minMacVersionSupported, FiraProtocolVersion maxMacVersionSupported, List<Integer> supportedChannels, EnumSet<AoaCapabilityFlag> aoaCapabilities, EnumSet<DeviceRoleCapabilityFlag> deviceRoleCapabilities, boolean hasBlockStridingSupport, boolean hasHoppingPreferenceSupport, boolean hasExtendedMacAddressSupport, boolean hasNonDeferredModeSupport, boolean hasInitiationTimeSupport, boolean hasRssiReportingSupport, boolean hasDiagnosticsSupport, int minRangingInterval, int minSlotDuration, int maxRangingSessionNumber, EnumSet<MultiNodeCapabilityFlag> multiNodeCapabilities, EnumSet<RangingTimeStructCapabilitiesFlag> rangingTimeStructCapabilities, EnumSet<SchedulingModeCapabilitiesFlag> schedulingModeCapabilities, EnumSet<CcConstraintLengthCapabilitiesFlag> ccConstraintLengthCapabilities, EnumSet<PrfCapabilityFlag> prfCapabilities, EnumSet<RangingRoundCapabilityFlag> rangingRoundCapabilities, EnumSet<RframeCapabilityFlag> rframeCapabilities, EnumSet<StsCapabilityFlag> stsCapabilities, EnumSet<PsduDataRateCapabilityFlag> psduDataRateCapabilities, EnumSet<BprfParameterSetCapabilityFlag> bprfParameterSetCapabilities, EnumSet<HprfParameterSetCapabilityFlag> hprfParameterSetCapabilities, Integer maxMessageSize, Integer maxDataPacketPayloadSize, EnumSet<RangeDataNtfConfigCapabilityFlag> rangeDataNtfConfigCapabilities, int deviceType, boolean suspendRangingSupport, int sessionKeyLength, int dtTagMaxActiveRr)156 private FiraSpecificationParams( 157 FiraProtocolVersion minPhyVersionSupported, 158 FiraProtocolVersion maxPhyVersionSupported, 159 FiraProtocolVersion minMacVersionSupported, 160 FiraProtocolVersion maxMacVersionSupported, 161 List<Integer> supportedChannels, 162 EnumSet<AoaCapabilityFlag> aoaCapabilities, 163 EnumSet<DeviceRoleCapabilityFlag> deviceRoleCapabilities, 164 boolean hasBlockStridingSupport, 165 boolean hasHoppingPreferenceSupport, 166 boolean hasExtendedMacAddressSupport, 167 boolean hasNonDeferredModeSupport, 168 boolean hasInitiationTimeSupport, 169 boolean hasRssiReportingSupport, 170 boolean hasDiagnosticsSupport, 171 int minRangingInterval, 172 int minSlotDuration, 173 int maxRangingSessionNumber, 174 EnumSet<MultiNodeCapabilityFlag> multiNodeCapabilities, 175 EnumSet<RangingTimeStructCapabilitiesFlag> rangingTimeStructCapabilities, 176 EnumSet<SchedulingModeCapabilitiesFlag> schedulingModeCapabilities, 177 EnumSet<CcConstraintLengthCapabilitiesFlag> ccConstraintLengthCapabilities, 178 EnumSet<PrfCapabilityFlag> prfCapabilities, 179 EnumSet<RangingRoundCapabilityFlag> rangingRoundCapabilities, 180 EnumSet<RframeCapabilityFlag> rframeCapabilities, 181 EnumSet<StsCapabilityFlag> stsCapabilities, 182 EnumSet<PsduDataRateCapabilityFlag> psduDataRateCapabilities, 183 EnumSet<BprfParameterSetCapabilityFlag> bprfParameterSetCapabilities, 184 EnumSet<HprfParameterSetCapabilityFlag> hprfParameterSetCapabilities, 185 Integer maxMessageSize, 186 Integer maxDataPacketPayloadSize, 187 EnumSet<RangeDataNtfConfigCapabilityFlag> rangeDataNtfConfigCapabilities, 188 int deviceType, boolean suspendRangingSupport, int sessionKeyLength, 189 int dtTagMaxActiveRr) { 190 mMinPhyVersionSupported = minPhyVersionSupported; 191 mMaxPhyVersionSupported = maxPhyVersionSupported; 192 mMinMacVersionSupported = minMacVersionSupported; 193 mMaxMacVersionSupported = maxMacVersionSupported; 194 mSupportedChannels = supportedChannels; 195 mAoaCapabilities = aoaCapabilities; 196 mDeviceRoleCapabilities = deviceRoleCapabilities; 197 mHasBlockStridingSupport = hasBlockStridingSupport; 198 mHasHoppingPreferenceSupport = hasHoppingPreferenceSupport; 199 mHasExtendedMacAddressSupport = hasExtendedMacAddressSupport; 200 mHasNonDeferredModeSupport = hasNonDeferredModeSupport; 201 mHasInitiationTimeSupport = hasInitiationTimeSupport; 202 mHasRssiReportingSupport = hasRssiReportingSupport; 203 mHasDiagnosticsSupport = hasDiagnosticsSupport; 204 mMinRangingInterval = minRangingInterval; 205 mMinSlotDuration = minSlotDuration; 206 mMaxRangingSessionNumber = maxRangingSessionNumber; 207 mMultiNodeCapabilities = multiNodeCapabilities; 208 mRangingTimeStructCapabilities = rangingTimeStructCapabilities; 209 mSchedulingModeCapabilities = schedulingModeCapabilities; 210 mCcConstraintLengthCapabilities = ccConstraintLengthCapabilities; 211 mPrfCapabilities = prfCapabilities; 212 mRangingRoundCapabilities = rangingRoundCapabilities; 213 mRframeCapabilities = rframeCapabilities; 214 mStsCapabilities = stsCapabilities; 215 mPsduDataRateCapabilities = psduDataRateCapabilities; 216 mBprfParameterSetCapabilities = bprfParameterSetCapabilities; 217 mHprfParameterSetCapabilities = hprfParameterSetCapabilities; 218 mMaxMessageSize = maxMessageSize; 219 mMaxDataPacketPayloadSize = maxDataPacketPayloadSize; 220 mRangeDataNtfConfigCapabilities = rangeDataNtfConfigCapabilities; 221 mDeviceType = deviceType; 222 mSuspendRangingSupport = suspendRangingSupport; 223 mSessionKeyLength = sessionKeyLength; 224 mDtTagMaxActiveRr = dtTagMaxActiveRr; 225 } 226 227 @Override getBundleVersion()228 protected int getBundleVersion() { 229 return BUNDLE_VERSION_CURRENT; 230 } 231 getMinPhyVersionSupported()232 public FiraProtocolVersion getMinPhyVersionSupported() { 233 return mMinPhyVersionSupported; 234 } 235 getMaxPhyVersionSupported()236 public FiraProtocolVersion getMaxPhyVersionSupported() { 237 return mMaxPhyVersionSupported; 238 } 239 getMinMacVersionSupported()240 public FiraProtocolVersion getMinMacVersionSupported() { 241 return mMinMacVersionSupported; 242 } 243 getMaxMacVersionSupported()244 public FiraProtocolVersion getMaxMacVersionSupported() { 245 return mMaxMacVersionSupported; 246 } 247 getSupportedChannels()248 public List<Integer> getSupportedChannels() { 249 return mSupportedChannels; 250 } 251 getAoaCapabilities()252 public EnumSet<AoaCapabilityFlag> getAoaCapabilities() { 253 return mAoaCapabilities; 254 } 255 getDeviceRoleCapabilities()256 public EnumSet<DeviceRoleCapabilityFlag> getDeviceRoleCapabilities() { 257 return mDeviceRoleCapabilities; 258 } 259 hasBlockStridingSupport()260 public boolean hasBlockStridingSupport() { 261 return mHasBlockStridingSupport; 262 } 263 hasHoppingPreferenceSupport()264 public boolean hasHoppingPreferenceSupport() { 265 return mHasHoppingPreferenceSupport; 266 } 267 hasExtendedMacAddressSupport()268 public boolean hasExtendedMacAddressSupport() { 269 return mHasExtendedMacAddressSupport; 270 } 271 hasNonDeferredModeSupport()272 public boolean hasNonDeferredModeSupport() { 273 return mHasNonDeferredModeSupport; 274 } 275 hasInitiationTimeSupport()276 public boolean hasInitiationTimeSupport() { 277 return mHasInitiationTimeSupport; 278 } 279 280 /** get a boolean whether rssi reporting is supported. */ hasRssiReportingSupport()281 public boolean hasRssiReportingSupport() { 282 return mHasRssiReportingSupport; 283 } 284 285 /** get a boolean whether diagnostics is supported. */ hasDiagnosticsSupport()286 public boolean hasDiagnosticsSupport() { 287 return mHasDiagnosticsSupport; 288 } 289 getMinRangingInterval()290 public int getMinRangingInterval() { 291 return mMinRangingInterval; 292 } 293 getMinSlotDuration()294 public int getMinSlotDuration() { 295 return mMinSlotDuration; 296 } 297 getMaxRangingSessionNumber()298 public int getMaxRangingSessionNumber() { 299 return mMaxRangingSessionNumber; 300 } 301 getMultiNodeCapabilities()302 public EnumSet<MultiNodeCapabilityFlag> getMultiNodeCapabilities() { 303 return mMultiNodeCapabilities; 304 } 305 getRangingTimeStructCapabilities()306 public EnumSet<RangingTimeStructCapabilitiesFlag> getRangingTimeStructCapabilities() { 307 return mRangingTimeStructCapabilities; 308 } 309 getSchedulingModeCapabilities()310 public EnumSet<SchedulingModeCapabilitiesFlag> getSchedulingModeCapabilities() { 311 return mSchedulingModeCapabilities; 312 } 313 getCcConstraintLengthCapabilities()314 public EnumSet<CcConstraintLengthCapabilitiesFlag> getCcConstraintLengthCapabilities() { 315 return mCcConstraintLengthCapabilities; 316 } 317 getPrfCapabilities()318 public EnumSet<PrfCapabilityFlag> getPrfCapabilities() { 319 return mPrfCapabilities; 320 } 321 getRangingRoundCapabilities()322 public EnumSet<RangingRoundCapabilityFlag> getRangingRoundCapabilities() { 323 return mRangingRoundCapabilities; 324 } 325 getRframeCapabilities()326 public EnumSet<RframeCapabilityFlag> getRframeCapabilities() { 327 return mRframeCapabilities; 328 } 329 getStsCapabilities()330 public EnumSet<StsCapabilityFlag> getStsCapabilities() { 331 return mStsCapabilities; 332 } 333 getPsduDataRateCapabilities()334 public EnumSet<PsduDataRateCapabilityFlag> getPsduDataRateCapabilities() { 335 return mPsduDataRateCapabilities; 336 } 337 getBprfParameterSetCapabilities()338 public EnumSet<BprfParameterSetCapabilityFlag> getBprfParameterSetCapabilities() { 339 return mBprfParameterSetCapabilities; 340 } 341 getHprfParameterSetCapabilities()342 public EnumSet<HprfParameterSetCapabilityFlag> getHprfParameterSetCapabilities() { 343 return mHprfParameterSetCapabilities; 344 } 345 getRangeDataNtfConfigCapabilities()346 public EnumSet<RangeDataNtfConfigCapabilityFlag> getRangeDataNtfConfigCapabilities() { 347 return mRangeDataNtfConfigCapabilities; 348 } 349 getMaxMessageSize()350 public Integer getMaxMessageSize() { 351 return mMaxMessageSize; 352 } 353 getMaxDataPacketPayloadSize()354 public Integer getMaxDataPacketPayloadSize() { 355 return mMaxDataPacketPayloadSize; 356 } 357 getDeviceType()358 public int getDeviceType() { 359 return mDeviceType; 360 } 361 hasSuspendRangingSupport()362 public boolean hasSuspendRangingSupport() { 363 return mSuspendRangingSupport; 364 } 365 getSessionKeyLength()366 public int getSessionKeyLength() { 367 return mSessionKeyLength; 368 } 369 getDtTagMaxActiveRr()370 public int getDtTagMaxActiveRr() { 371 return mDtTagMaxActiveRr; 372 } 373 toIntArray(List<Integer> data)374 private static int[] toIntArray(List<Integer> data) { 375 int[] res = new int[data.size()]; 376 for (int i = 0; i < data.size(); i++) { 377 res[i] = data.get(i); 378 } 379 return res; 380 } 381 382 @Override toBundle()383 public PersistableBundle toBundle() { 384 PersistableBundle bundle = super.toBundle(); 385 bundle.putString(KEY_MIN_PHY_VERSION, mMinPhyVersionSupported.toString()); 386 bundle.putString(KEY_MAX_PHY_VERSION, mMaxPhyVersionSupported.toString()); 387 bundle.putString(KEY_MIN_MAC_VERSION, mMinMacVersionSupported.toString()); 388 bundle.putString(KEY_MAX_MAC_VERSION, mMaxMacVersionSupported.toString()); 389 bundle.putIntArray(KEY_SUPPORTED_CHANNELS, toIntArray(mSupportedChannels)); 390 bundle.putInt(KEY_AOA_CAPABILITIES, FlagEnum.toInt(mAoaCapabilities)); 391 bundle.putInt(KEY_DEVICE_ROLE_CAPABILITIES, FlagEnum.toInt(mDeviceRoleCapabilities)); 392 bundle.putBoolean(KEY_BLOCK_STRIDING_SUPPORT, mHasBlockStridingSupport); 393 bundle.putBoolean(KEY_HOPPING_PREFERENCE_SUPPORT, mHasHoppingPreferenceSupport); 394 bundle.putBoolean(KEY_EXTENDED_MAC_ADDRESS_SUPPORT, mHasExtendedMacAddressSupport); 395 bundle.putBoolean(KEY_NON_DEFERRED_MODE_SUPPORT, mHasNonDeferredModeSupport); 396 bundle.putBoolean(KEY_INITIATION_TIME_SUPPORT, mHasInitiationTimeSupport); 397 bundle.putBoolean(KEY_RSSI_REPORTING_SUPPORT, mHasRssiReportingSupport); 398 bundle.putBoolean(KEY_DIAGNOSTICS_SUPPORT, mHasDiagnosticsSupport); 399 bundle.putInt(KEY_MIN_RANGING_INTERVAL, mMinRangingInterval); 400 bundle.putInt(KEY_MIN_SLOT_DURATION, mMinSlotDuration); 401 bundle.putInt(KEY_MAX_RANGING_SESSION_NUMBER, mMaxRangingSessionNumber); 402 bundle.putInt(KEY_MULTI_NODE_CAPABILITIES, FlagEnum.toInt(mMultiNodeCapabilities)); 403 bundle.putInt(KEY_RANGING_TIME_STRUCT_CAPABILITIES, 404 FlagEnum.toInt(mRangingTimeStructCapabilities)); 405 bundle.putInt(KEY_SCHEDULING_MODE_CAPABILITIES, 406 FlagEnum.toInt(mSchedulingModeCapabilities)); 407 bundle.putInt(KEY_CC_CONSTRAINT_LENGTH_CAPABILITIES, 408 FlagEnum.toInt(mCcConstraintLengthCapabilities)); 409 bundle.putInt(KEY_PRF_CAPABILITIES, FlagEnum.toInt(mPrfCapabilities)); 410 bundle.putInt(KEY_RANGING_ROUND_CAPABILITIES, FlagEnum.toInt(mRangingRoundCapabilities)); 411 bundle.putInt(KEY_RFRAME_CAPABILITIES, FlagEnum.toInt(mRframeCapabilities)); 412 bundle.putInt(KEY_STS_CAPABILITIES, FlagEnum.toInt(mStsCapabilities)); 413 bundle.putInt(KEY_PSDU_DATA_RATE_CAPABILITIES, FlagEnum.toInt(mPsduDataRateCapabilities)); 414 bundle.putInt(KEY_BPRF_PARAMETER_SET_CAPABILITIES, 415 FlagEnum.toInt(mBprfParameterSetCapabilities)); 416 bundle.putLong(KEY_HPRF_PARAMETER_SET_CAPABILITIES, 417 FlagEnum.toLong(mHprfParameterSetCapabilities)); 418 bundle.putInt(KEY_MAX_MESSAGE_SIZE, mMaxMessageSize); 419 bundle.putInt(KEY_MAX_DATA_PACKET_PAYLOAD_SIZE, mMaxDataPacketPayloadSize); 420 bundle.putInt(KEY_RANGE_DATA_NTF_CONFIG_CAPABILITIES, 421 FlagEnum.toInt(mRangeDataNtfConfigCapabilities)); 422 bundle.putInt(KEY_DEVICE_TYPE, mDeviceType); 423 bundle.putBoolean(KEY_SUSPEND_RANGING_SUPPORT, mSuspendRangingSupport); 424 bundle.putInt(KEY_SESSION_KEY_LENGTH, mSessionKeyLength); 425 bundle.putInt(DT_TAG_MAX_ACTIVE_RR, mDtTagMaxActiveRr); 426 return bundle; 427 } 428 fromBundle(PersistableBundle bundle)429 public static FiraSpecificationParams fromBundle(PersistableBundle bundle) { 430 if (!isCorrectProtocol(bundle)) { 431 throw new IllegalArgumentException("Invalid protocol"); 432 } 433 434 switch (getBundleVersion(bundle)) { 435 case BUNDLE_VERSION_1: 436 return parseVersion1(bundle).build(); 437 case BUNDLE_VERSION_2: 438 return parseVersion2(bundle).build(); 439 default: 440 throw new IllegalArgumentException("Invalid bundle version"); 441 } 442 } 443 toIntList(int[] data)444 private static List<Integer> toIntList(int[] data) { 445 List<Integer> res = new ArrayList<>(); 446 for (int datum : data) { 447 res.add(datum); 448 } 449 return res; 450 } 451 parseVersion2(PersistableBundle bundle)452 private static FiraSpecificationParams.Builder parseVersion2(PersistableBundle bundle) { 453 FiraSpecificationParams.Builder builder = parseVersion1(bundle); 454 builder.setDeviceType(bundle.getInt(KEY_DEVICE_TYPE)); 455 builder.setSuspendRangingSupport(bundle.getBoolean(KEY_SUSPEND_RANGING_SUPPORT)); 456 builder.setSessionKeyLength(bundle.getInt(KEY_SESSION_KEY_LENGTH)); 457 builder.setDtTagMaxActiveRr(bundle.getInt(DT_TAG_MAX_ACTIVE_RR, 0)); 458 return builder; 459 } 460 parseVersion1(PersistableBundle bundle)461 private static FiraSpecificationParams.Builder parseVersion1(PersistableBundle bundle) { 462 FiraSpecificationParams.Builder builder = new FiraSpecificationParams.Builder(); 463 List<Integer> supportedChannels = 464 toIntList(requireNonNull(bundle.getIntArray(KEY_SUPPORTED_CHANNELS))); 465 builder.setMinPhyVersionSupported( 466 FiraProtocolVersion.fromString(bundle.getString(KEY_MIN_PHY_VERSION))) 467 .setMaxPhyVersionSupported( 468 FiraProtocolVersion.fromString(bundle.getString(KEY_MAX_PHY_VERSION))) 469 .setMinMacVersionSupported( 470 FiraProtocolVersion.fromString(bundle.getString(KEY_MIN_MAC_VERSION))) 471 .setMaxMacVersionSupported( 472 FiraProtocolVersion.fromString(bundle.getString(KEY_MAX_MAC_VERSION))) 473 .setSupportedChannels(supportedChannels) 474 .setAoaCapabilities( 475 FlagEnum.toEnumSet( 476 bundle.getInt(KEY_AOA_CAPABILITIES), AoaCapabilityFlag.values())) 477 .setDeviceRoleCapabilities( 478 FlagEnum.toEnumSet( 479 bundle.getInt(KEY_DEVICE_ROLE_CAPABILITIES), 480 DeviceRoleCapabilityFlag.values())) 481 .hasBlockStridingSupport(bundle.getBoolean(KEY_BLOCK_STRIDING_SUPPORT)) 482 .hasHoppingPreferenceSupport(bundle.getBoolean(KEY_HOPPING_PREFERENCE_SUPPORT)) 483 .hasExtendedMacAddressSupport(bundle.getBoolean(KEY_EXTENDED_MAC_ADDRESS_SUPPORT)) 484 .hasNonDeferredModeSupport(bundle.getBoolean(KEY_NON_DEFERRED_MODE_SUPPORT)) 485 .hasInitiationTimeSupport(bundle.getBoolean(KEY_INITIATION_TIME_SUPPORT)) 486 .setMinRangingIntervalSupported(bundle.getInt(KEY_MIN_RANGING_INTERVAL, -1)) 487 .setMinSlotDurationSupportedUs(bundle.getInt(KEY_MIN_SLOT_DURATION, -1)) 488 .setMultiNodeCapabilities( 489 FlagEnum.toEnumSet( 490 bundle.getInt(KEY_MULTI_NODE_CAPABILITIES), 491 MultiNodeCapabilityFlag.values())) 492 .setRangingTimeStructCapabilities( 493 FlagEnum.toEnumSet( 494 bundle.getInt(KEY_RANGING_TIME_STRUCT_CAPABILITIES), 495 RangingTimeStructCapabilitiesFlag.values())) 496 .setSchedulingModeCapabilities( 497 FlagEnum.toEnumSet( 498 bundle.getInt(KEY_SCHEDULING_MODE_CAPABILITIES), 499 SchedulingModeCapabilitiesFlag.values())) 500 .setCcConstraintLengthCapabilities( 501 FlagEnum.toEnumSet( 502 bundle.getInt(KEY_CC_CONSTRAINT_LENGTH_CAPABILITIES), 503 CcConstraintLengthCapabilitiesFlag.values())) 504 .setPrfCapabilities( 505 FlagEnum.toEnumSet( 506 bundle.getInt(KEY_PRF_CAPABILITIES), PrfCapabilityFlag.values())) 507 .setRangingRoundCapabilities( 508 FlagEnum.toEnumSet( 509 bundle.getInt(KEY_RANGING_ROUND_CAPABILITIES), 510 RangingRoundCapabilityFlag.values())) 511 .setRframeCapabilities( 512 FlagEnum.toEnumSet( 513 bundle.getInt(KEY_RFRAME_CAPABILITIES), 514 RframeCapabilityFlag.values())) 515 .setStsCapabilities( 516 FlagEnum.toEnumSet( 517 bundle.getInt(KEY_STS_CAPABILITIES), StsCapabilityFlag.values())) 518 .setPsduDataRateCapabilities( 519 FlagEnum.toEnumSet( 520 bundle.getInt(KEY_PSDU_DATA_RATE_CAPABILITIES), 521 PsduDataRateCapabilityFlag.values())) 522 .setBprfParameterSetCapabilities( 523 FlagEnum.toEnumSet( 524 bundle.getInt(KEY_BPRF_PARAMETER_SET_CAPABILITIES), 525 BprfParameterSetCapabilityFlag.values())) 526 .setMaxMessageSize(bundle.getInt(KEY_MAX_MESSAGE_SIZE)) 527 .setMaxDataPacketPayloadSize(bundle.getInt(KEY_MAX_DATA_PACKET_PAYLOAD_SIZE)) 528 .setHprfParameterSetCapabilities( 529 FlagEnum.longToEnumSet( 530 bundle.getLong(KEY_HPRF_PARAMETER_SET_CAPABILITIES), 531 HprfParameterSetCapabilityFlag.values())); 532 // Newer params need to be backward compatible with existing devices. 533 if (bundle.containsKey(KEY_RANGE_DATA_NTF_CONFIG_CAPABILITIES)) { 534 builder.setRangeDataNtfConfigCapabilities( 535 FlagEnum.toEnumSet( 536 bundle.getInt(KEY_RANGE_DATA_NTF_CONFIG_CAPABILITIES), 537 RangeDataNtfConfigCapabilityFlag.values())); 538 } 539 if (bundle.containsKey(KEY_RSSI_REPORTING_SUPPORT)) { 540 builder.hasRssiReportingSupport(bundle.getBoolean(KEY_RSSI_REPORTING_SUPPORT)); 541 } 542 if (bundle.containsKey(KEY_DIAGNOSTICS_SUPPORT)) { 543 builder.hasDiagnosticsSupport(bundle.getBoolean(KEY_DIAGNOSTICS_SUPPORT)); 544 } 545 if (bundle.containsKey(KEY_MAX_RANGING_SESSION_NUMBER)) { 546 builder.setMaxRangingSessionNumberSupported( 547 bundle.getInt(KEY_MAX_RANGING_SESSION_NUMBER)); 548 } 549 return builder; 550 } 551 552 /** Builder */ 553 public static class Builder { 554 // Set all default protocol version to FiRa 1.1 555 private FiraProtocolVersion mMinPhyVersionSupported = new FiraProtocolVersion(1, 1); 556 private FiraProtocolVersion mMaxPhyVersionSupported = new FiraProtocolVersion(1, 1); 557 private FiraProtocolVersion mMinMacVersionSupported = new FiraProtocolVersion(1, 1); 558 private FiraProtocolVersion mMaxMacVersionSupported = new FiraProtocolVersion(1, 1); 559 560 private List<Integer> mSupportedChannels = new ArrayList<>(); 561 562 private final EnumSet<AoaCapabilityFlag> mAoaCapabilities = 563 EnumSet.noneOf(AoaCapabilityFlag.class); 564 565 // Controller-intiator, Cotrolee-responder are mandatory. 566 private final EnumSet<DeviceRoleCapabilityFlag> mDeviceRoleCapabilities = 567 EnumSet.of( 568 DeviceRoleCapabilityFlag.HAS_CONTROLLER_INITIATOR_SUPPORT, 569 DeviceRoleCapabilityFlag.HAS_CONTROLEE_RESPONDER_SUPPORT); 570 571 // Enable/Disable ntf config is mandatory. 572 private final EnumSet<RangeDataNtfConfigCapabilityFlag> mRangeDataNtfConfigCapabilities = 573 EnumSet.noneOf(RangeDataNtfConfigCapabilityFlag.class); 574 575 private boolean mHasBlockStridingSupport = false; 576 577 private boolean mHasHoppingPreferenceSupport = false; 578 579 private boolean mHasExtendedMacAddressSupport = false; 580 581 private boolean mHasNonDeferredModeSupport = false; 582 583 private boolean mHasInitiationTimeSupport = false; 584 585 private boolean mHasRssiReportingSupport = false; 586 587 private boolean mHasDiagnosticsSupport = false; 588 589 private int mMinRangingInterval = -1; 590 591 private int mMinSlotDuration = -1; 592 593 private int mMaxRangingSessionNumber = DEFAULT_MAX_RANGING_SESSIONS_NUMBER; 594 595 // Unicast support is mandatory 596 private final EnumSet<MultiNodeCapabilityFlag> mMultiNodeCapabilities = 597 EnumSet.of(MultiNodeCapabilityFlag.HAS_UNICAST_SUPPORT); 598 599 private final EnumSet<RangingTimeStructCapabilitiesFlag> mRangingTimeStructCapabilities = 600 EnumSet.of( 601 RangingTimeStructCapabilitiesFlag.HAS_INTERVAL_BASED_SCHEDULING_SUPPORT, 602 RangingTimeStructCapabilitiesFlag.HAS_BLOCK_BASED_SCHEDULING_SUPPORT); 603 604 private final EnumSet<SchedulingModeCapabilitiesFlag> mSchedulingModeCapabilities = 605 EnumSet.of( 606 SchedulingModeCapabilitiesFlag.HAS_CONTENTION_BASED_RANGING_SUPPORT, 607 SchedulingModeCapabilitiesFlag.HAS_TIME_SCHEDULED_RANGING_SUPPORT); 608 609 private final EnumSet<CcConstraintLengthCapabilitiesFlag> mCcConstraintLengthCapabilities = 610 EnumSet.of( 611 CcConstraintLengthCapabilitiesFlag.HAS_CONSTRAINT_LENGTH_3_SUPPORT, 612 CcConstraintLengthCapabilitiesFlag.HAS_CONSTRAINT_LENGTH_7_SUPPORT); 613 614 // BPRF mode is mandatory 615 private final EnumSet<PrfCapabilityFlag> mPrfCapabilities = 616 EnumSet.of(PrfCapabilityFlag.HAS_BPRF_SUPPORT); 617 618 // DS-TWR is mandatory 619 private final EnumSet<RangingRoundCapabilityFlag> mRangingRoundCapabilities = 620 EnumSet.of(RangingRoundCapabilityFlag.HAS_DS_TWR_SUPPORT); 621 622 // SP3 RFrame is mandatory 623 private final EnumSet<RframeCapabilityFlag> mRframeCapabilities = 624 EnumSet.of(RframeCapabilityFlag.HAS_SP3_RFRAME_SUPPORT); 625 626 // STATIC STS is mandatory 627 private final EnumSet<StsCapabilityFlag> mStsCapabilities = 628 EnumSet.of(StsCapabilityFlag.HAS_STATIC_STS_SUPPORT); 629 630 // 6.81Mb/s PSDU data rate is mandatory 631 private final EnumSet<PsduDataRateCapabilityFlag> mPsduDataRateCapabilities = 632 EnumSet.of(PsduDataRateCapabilityFlag.HAS_6M81_SUPPORT); 633 634 private final EnumSet<BprfParameterSetCapabilityFlag> mBprfParameterSetCapabilities = 635 EnumSet.noneOf(BprfParameterSetCapabilityFlag.class); 636 637 private final EnumSet<HprfParameterSetCapabilityFlag> mHprfParameterSetCapabilities = 638 EnumSet.noneOf(HprfParameterSetCapabilityFlag.class); 639 640 private Integer mMaxMessageSize = 0; 641 642 private Integer mMaxDataPacketPayloadSize = 0; 643 644 // Default to Controlee 645 private int mDeviceType = RANGING_DEVICE_TYPE_CONTROLEE; 646 647 // Default to no suspend ranging support 648 private boolean mSuspendRangingSupport = false; 649 650 // Default to 256 bits key length not supported 651 private int mSessionKeyLength = 0; 652 653 //Default to 0 i.e., DT tag role not supported. 654 private int mDtTagMaxActiveRr = 0; 655 setMinPhyVersionSupported( FiraProtocolVersion version)656 public FiraSpecificationParams.Builder setMinPhyVersionSupported( 657 FiraProtocolVersion version) { 658 mMinPhyVersionSupported = version; 659 return this; 660 } 661 setMaxPhyVersionSupported( FiraProtocolVersion version)662 public FiraSpecificationParams.Builder setMaxPhyVersionSupported( 663 FiraProtocolVersion version) { 664 mMaxPhyVersionSupported = version; 665 return this; 666 } 667 setMinMacVersionSupported( FiraProtocolVersion version)668 public FiraSpecificationParams.Builder setMinMacVersionSupported( 669 FiraProtocolVersion version) { 670 mMinMacVersionSupported = version; 671 return this; 672 } 673 setMaxMacVersionSupported( FiraProtocolVersion version)674 public FiraSpecificationParams.Builder setMaxMacVersionSupported( 675 FiraProtocolVersion version) { 676 mMaxMacVersionSupported = version; 677 return this; 678 } 679 setSupportedChannels( List<Integer> supportedChannels)680 public FiraSpecificationParams.Builder setSupportedChannels( 681 List<Integer> supportedChannels) { 682 mSupportedChannels = List.copyOf(supportedChannels); 683 return this; 684 } 685 setAoaCapabilities( Collection<AoaCapabilityFlag> aoaCapabilities)686 public FiraSpecificationParams.Builder setAoaCapabilities( 687 Collection<AoaCapabilityFlag> aoaCapabilities) { 688 mAoaCapabilities.addAll(aoaCapabilities); 689 return this; 690 } 691 setDeviceRoleCapabilities( Collection<DeviceRoleCapabilityFlag> deviceRoleCapabilities)692 public FiraSpecificationParams.Builder setDeviceRoleCapabilities( 693 Collection<DeviceRoleCapabilityFlag> deviceRoleCapabilities) { 694 mDeviceRoleCapabilities.addAll(deviceRoleCapabilities); 695 return this; 696 } 697 hasBlockStridingSupport(boolean value)698 public FiraSpecificationParams.Builder hasBlockStridingSupport(boolean value) { 699 mHasBlockStridingSupport = value; 700 return this; 701 } 702 hasHoppingPreferenceSupport(boolean value)703 public FiraSpecificationParams.Builder hasHoppingPreferenceSupport(boolean value) { 704 mHasHoppingPreferenceSupport = value; 705 return this; 706 } 707 hasExtendedMacAddressSupport(boolean value)708 public FiraSpecificationParams.Builder hasExtendedMacAddressSupport(boolean value) { 709 mHasExtendedMacAddressSupport = value; 710 return this; 711 } 712 hasNonDeferredModeSupport(boolean value)713 public FiraSpecificationParams.Builder hasNonDeferredModeSupport(boolean value) { 714 mHasNonDeferredModeSupport = value; 715 return this; 716 } 717 hasInitiationTimeSupport(boolean value)718 public FiraSpecificationParams.Builder hasInitiationTimeSupport(boolean value) { 719 mHasInitiationTimeSupport = value; 720 return this; 721 } 722 723 /** Set whether rssi reporting is supported. */ hasRssiReportingSupport(boolean value)724 public FiraSpecificationParams.Builder hasRssiReportingSupport(boolean value) { 725 mHasRssiReportingSupport = value; 726 return this; 727 } 728 729 /** Set whether diagnostics is supported. */ hasDiagnosticsSupport(boolean value)730 public FiraSpecificationParams.Builder hasDiagnosticsSupport(boolean value) { 731 mHasDiagnosticsSupport = value; 732 return this; 733 } 734 735 /** 736 * Set minimum supported ranging interval 737 * @param value : minimum ranging interval supported 738 * @return FiraSpecificationParams builder 739 */ setMinRangingIntervalSupported(int value)740 public FiraSpecificationParams.Builder setMinRangingIntervalSupported(int value) { 741 mMinRangingInterval = value; 742 return this; 743 } 744 745 /** 746 * Set minimum supported slot duration in microsecond 747 * @param value : minimum slot duration supported in microsecond 748 * @return FiraSpecificationParams builder 749 */ setMinSlotDurationSupportedUs(int value)750 public FiraSpecificationParams.Builder setMinSlotDurationSupportedUs(int value) { 751 mMinSlotDuration = value; 752 return this; 753 } 754 755 /** 756 * Set maximum supported ranging session number 757 * @param value : maximum ranging session number supported 758 * @return FiraSpecificationParams builder 759 */ setMaxRangingSessionNumberSupported(int value)760 public FiraSpecificationParams.Builder setMaxRangingSessionNumberSupported(int value) { 761 mMaxRangingSessionNumber = value; 762 return this; 763 } 764 setMultiNodeCapabilities( Collection<MultiNodeCapabilityFlag> multiNodeCapabilities)765 public FiraSpecificationParams.Builder setMultiNodeCapabilities( 766 Collection<MultiNodeCapabilityFlag> multiNodeCapabilities) { 767 mMultiNodeCapabilities.addAll(multiNodeCapabilities); 768 return this; 769 } 770 setRangingTimeStructCapabilities( Collection<RangingTimeStructCapabilitiesFlag> rangingTimeStructCapabilities)771 public FiraSpecificationParams.Builder setRangingTimeStructCapabilities( 772 Collection<RangingTimeStructCapabilitiesFlag> rangingTimeStructCapabilities) { 773 mRangingTimeStructCapabilities.addAll(rangingTimeStructCapabilities); 774 return this; 775 } 776 setSchedulingModeCapabilities( Collection<SchedulingModeCapabilitiesFlag> schedulingModeCapabilities)777 public FiraSpecificationParams.Builder setSchedulingModeCapabilities( 778 Collection<SchedulingModeCapabilitiesFlag> schedulingModeCapabilities) { 779 mSchedulingModeCapabilities.addAll(schedulingModeCapabilities); 780 return this; 781 } 782 setCcConstraintLengthCapabilities( Collection<CcConstraintLengthCapabilitiesFlag> ccConstraintLengthCapabilities)783 public FiraSpecificationParams.Builder setCcConstraintLengthCapabilities( 784 Collection<CcConstraintLengthCapabilitiesFlag> ccConstraintLengthCapabilities) { 785 mCcConstraintLengthCapabilities.addAll(ccConstraintLengthCapabilities); 786 return this; 787 } 788 setPrfCapabilities( Collection<PrfCapabilityFlag> prfCapabilities)789 public FiraSpecificationParams.Builder setPrfCapabilities( 790 Collection<PrfCapabilityFlag> prfCapabilities) { 791 mPrfCapabilities.addAll(prfCapabilities); 792 return this; 793 } 794 setRangingRoundCapabilities( Collection<RangingRoundCapabilityFlag> rangingRoundCapabilities)795 public FiraSpecificationParams.Builder setRangingRoundCapabilities( 796 Collection<RangingRoundCapabilityFlag> rangingRoundCapabilities) { 797 mRangingRoundCapabilities.addAll(rangingRoundCapabilities); 798 return this; 799 } 800 setRframeCapabilities( Collection<RframeCapabilityFlag> rframeCapabilities)801 public FiraSpecificationParams.Builder setRframeCapabilities( 802 Collection<RframeCapabilityFlag> rframeCapabilities) { 803 mRframeCapabilities.addAll(rframeCapabilities); 804 return this; 805 } 806 setStsCapabilities( Collection<StsCapabilityFlag> stsCapabilities)807 public FiraSpecificationParams.Builder setStsCapabilities( 808 Collection<StsCapabilityFlag> stsCapabilities) { 809 mStsCapabilities.addAll(stsCapabilities); 810 return this; 811 } 812 setPsduDataRateCapabilities( Collection<PsduDataRateCapabilityFlag> psduDataRateCapabilities)813 public FiraSpecificationParams.Builder setPsduDataRateCapabilities( 814 Collection<PsduDataRateCapabilityFlag> psduDataRateCapabilities) { 815 mPsduDataRateCapabilities.addAll(psduDataRateCapabilities); 816 return this; 817 } 818 setBprfParameterSetCapabilities( Collection<BprfParameterSetCapabilityFlag> bprfParameterSetCapabilities)819 public FiraSpecificationParams.Builder setBprfParameterSetCapabilities( 820 Collection<BprfParameterSetCapabilityFlag> bprfParameterSetCapabilities) { 821 mBprfParameterSetCapabilities.addAll(bprfParameterSetCapabilities); 822 return this; 823 } 824 setHprfParameterSetCapabilities( Collection<HprfParameterSetCapabilityFlag> hprfParameterSetCapabilities)825 public FiraSpecificationParams.Builder setHprfParameterSetCapabilities( 826 Collection<HprfParameterSetCapabilityFlag> hprfParameterSetCapabilities) { 827 mHprfParameterSetCapabilities.addAll(hprfParameterSetCapabilities); 828 return this; 829 } 830 setRangeDataNtfConfigCapabilities( Collection<RangeDataNtfConfigCapabilityFlag> rangeDataNtfConfigCapabilities)831 public FiraSpecificationParams.Builder setRangeDataNtfConfigCapabilities( 832 Collection<RangeDataNtfConfigCapabilityFlag> rangeDataNtfConfigCapabilities) { 833 mRangeDataNtfConfigCapabilities.addAll(rangeDataNtfConfigCapabilities); 834 return this; 835 } 836 setMaxMessageSize(Integer value)837 public FiraSpecificationParams.Builder setMaxMessageSize(Integer value) { 838 mMaxMessageSize = value; 839 return this; 840 } 841 setMaxDataPacketPayloadSize(Integer value)842 public FiraSpecificationParams.Builder setMaxDataPacketPayloadSize(Integer value) { 843 mMaxDataPacketPayloadSize = value; 844 return this; 845 } 846 setDeviceType(Integer value)847 public FiraSpecificationParams.Builder setDeviceType(Integer value) { 848 mDeviceType = value; 849 return this; 850 851 } 852 setSuspendRangingSupport(Boolean value)853 public FiraSpecificationParams.Builder setSuspendRangingSupport(Boolean value) { 854 mSuspendRangingSupport = value; 855 return this; 856 } 857 setSessionKeyLength(Integer value)858 public FiraSpecificationParams.Builder setSessionKeyLength(Integer value) { 859 mSessionKeyLength = value; 860 return this; 861 } 862 setDtTagMaxActiveRr(int value)863 public FiraSpecificationParams.Builder setDtTagMaxActiveRr(int value) { 864 mDtTagMaxActiveRr = value; 865 return this; 866 } 867 build()868 public FiraSpecificationParams build() { 869 return new FiraSpecificationParams( 870 mMinPhyVersionSupported, 871 mMaxPhyVersionSupported, 872 mMinMacVersionSupported, 873 mMaxMacVersionSupported, 874 mSupportedChannels, 875 mAoaCapabilities, 876 mDeviceRoleCapabilities, 877 mHasBlockStridingSupport, 878 mHasHoppingPreferenceSupport, 879 mHasExtendedMacAddressSupport, 880 mHasNonDeferredModeSupport, 881 mHasInitiationTimeSupport, 882 mHasRssiReportingSupport, 883 mHasDiagnosticsSupport, 884 mMinRangingInterval, 885 mMinSlotDuration, 886 mMaxRangingSessionNumber, 887 mMultiNodeCapabilities, 888 mRangingTimeStructCapabilities, 889 mSchedulingModeCapabilities, 890 mCcConstraintLengthCapabilities, 891 mPrfCapabilities, 892 mRangingRoundCapabilities, 893 mRframeCapabilities, 894 mStsCapabilities, 895 mPsduDataRateCapabilities, 896 mBprfParameterSetCapabilities, 897 mHprfParameterSetCapabilities, 898 mMaxMessageSize, 899 mMaxDataPacketPayloadSize, 900 mRangeDataNtfConfigCapabilities, 901 mDeviceType, 902 mSuspendRangingSupport, 903 mSessionKeyLength, 904 mDtTagMaxActiveRr); 905 } 906 } 907 } 908