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.ccc; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import android.os.Build.VERSION_CODES; 22 import android.os.PersistableBundle; 23 import android.uwb.UwbManager; 24 25 import androidx.annotation.IntRange; 26 import androidx.annotation.NonNull; 27 import androidx.annotation.Nullable; 28 import androidx.annotation.RequiresApi; 29 30 import com.google.uwb.support.base.RequiredParam; 31 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.List; 35 36 /** 37 * Defines parameters for CCC capability reports 38 * 39 * <p>This is returned as a bundle from the service API {@link UwbManager#getSpecificationInfo}. 40 */ 41 @RequiresApi(VERSION_CODES.LOLLIPOP) 42 public class CccSpecificationParams extends CccParams { 43 private static final int BUNDLE_VERSION_1 = 1; 44 private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_1; 45 46 private final List<CccProtocolVersion> mProtocolVersions; 47 @UwbConfig private final List<Integer> mUwbConfigs; 48 private final List<CccPulseShapeCombo> mPulseShapeCombos; 49 private final int mRanMultiplier; 50 private final int mMaxRangingSessionNumber; 51 private final int mMinUwbInitiationTimeMs; 52 @ChapsPerSlot private final List<Integer> mChapsPerSlot; 53 @SyncCodeIndex private final List<Integer> mSyncCodes; 54 @Channel private final List<Integer> mChannels; 55 @HoppingConfigMode private final List<Integer> mHoppingConfigModes; 56 @HoppingSequence private final List<Integer> mHoppingSequences; 57 58 private static final String KEY_PROTOCOL_VERSIONS = "protocol_versions"; 59 private static final String KEY_UWB_CONFIGS = "uwb_configs"; 60 private static final String KEY_PULSE_SHAPE_COMBOS = "pulse_shape_combos"; 61 private static final String KEY_RAN_MULTIPLIER = "ran_multiplier"; 62 private static final String KEY_MAX_RANGING_SESSION_NUMBER = "max_ranging_session_number"; 63 private static final String KEY_MIN_UWB_INITIATION_TIME_MS = "min_uwb_initiation_time_ms"; 64 private static final String KEY_CHAPS_PER_SLOTS = "chaps_per_slots"; 65 private static final String KEY_SYNC_CODES = "sync_codes"; 66 private static final String KEY_CHANNELS = "channels"; 67 private static final String KEY_HOPPING_CONFIGS = "hopping_config_modes"; 68 private static final String KEY_HOPPING_SEQUENCES = "hopping_sequences"; 69 70 public static final int DEFAULT_MAX_RANGING_SESSIONS_NUMBER = 1; 71 CccSpecificationParams( List<CccProtocolVersion> protocolVersions, @UwbConfig List<Integer> uwbConfigs, List<CccPulseShapeCombo> pulseShapeCombos, int ranMultiplier, int maxRangingSessionNumber, int minUwbInitiationTimeMs, @ChapsPerSlot List<Integer> chapsPerSlot, @SyncCodeIndex List<Integer> syncCodes, @Channel List<Integer> channels, @HoppingConfigMode List<Integer> hoppingConfigModes, @HoppingSequence List<Integer> hoppingSequences)72 private CccSpecificationParams( 73 List<CccProtocolVersion> protocolVersions, 74 @UwbConfig List<Integer> uwbConfigs, 75 List<CccPulseShapeCombo> pulseShapeCombos, 76 int ranMultiplier, 77 int maxRangingSessionNumber, 78 int minUwbInitiationTimeMs, 79 @ChapsPerSlot List<Integer> chapsPerSlot, 80 @SyncCodeIndex List<Integer> syncCodes, 81 @Channel List<Integer> channels, 82 @HoppingConfigMode List<Integer> hoppingConfigModes, 83 @HoppingSequence List<Integer> hoppingSequences) { 84 mProtocolVersions = protocolVersions; 85 mUwbConfigs = uwbConfigs; 86 mPulseShapeCombos = pulseShapeCombos; 87 mRanMultiplier = ranMultiplier; 88 mMaxRangingSessionNumber = maxRangingSessionNumber; 89 mMinUwbInitiationTimeMs = minUwbInitiationTimeMs; 90 mChapsPerSlot = chapsPerSlot; 91 mSyncCodes = syncCodes; 92 mChannels = channels; 93 mHoppingConfigModes = hoppingConfigModes; 94 mHoppingSequences = hoppingSequences; 95 } 96 97 @Override getBundleVersion()98 protected int getBundleVersion() { 99 return BUNDLE_VERSION_CURRENT; 100 } 101 102 @Override toBundle()103 public PersistableBundle toBundle() { 104 PersistableBundle bundle = super.toBundle(); 105 String[] protocols = new String[mProtocolVersions.size()]; 106 for (int i = 0; i < protocols.length; i++) { 107 protocols[i] = mProtocolVersions.get(i).toString(); 108 } 109 String[] pulseShapeCombos = new String[mPulseShapeCombos.size()]; 110 for (int i = 0; i < pulseShapeCombos.length; i++) { 111 pulseShapeCombos[i] = mPulseShapeCombos.get(i).toString(); 112 } 113 bundle.putStringArray(KEY_PROTOCOL_VERSIONS, protocols); 114 bundle.putIntArray(KEY_UWB_CONFIGS, toIntArray(mUwbConfigs)); 115 bundle.putStringArray(KEY_PULSE_SHAPE_COMBOS, pulseShapeCombos); 116 bundle.putInt(KEY_RAN_MULTIPLIER, mRanMultiplier); 117 bundle.putInt(KEY_MAX_RANGING_SESSION_NUMBER, mMaxRangingSessionNumber); 118 bundle.putInt(KEY_MIN_UWB_INITIATION_TIME_MS, mMinUwbInitiationTimeMs); 119 bundle.putIntArray(KEY_CHAPS_PER_SLOTS, toIntArray(mChapsPerSlot)); 120 bundle.putIntArray(KEY_SYNC_CODES, toIntArray(mSyncCodes)); 121 bundle.putIntArray(KEY_CHANNELS, toIntArray(mChannels)); 122 bundle.putIntArray(KEY_HOPPING_CONFIGS, toIntArray(mHoppingConfigModes)); 123 bundle.putIntArray(KEY_HOPPING_SEQUENCES, toIntArray(mHoppingSequences)); 124 return bundle; 125 } 126 fromBundle(PersistableBundle bundle)127 public static CccSpecificationParams fromBundle(PersistableBundle bundle) { 128 if (!isCorrectProtocol(bundle)) { 129 throw new IllegalArgumentException("Invalid protocol"); 130 } 131 132 switch (getBundleVersion(bundle)) { 133 case BUNDLE_VERSION_1: 134 return parseVersion1(bundle); 135 136 default: 137 throw new IllegalArgumentException("Invalid bundle version"); 138 } 139 } 140 parseVersion1(PersistableBundle bundle)141 private static CccSpecificationParams parseVersion1(PersistableBundle bundle) { 142 CccSpecificationParams.Builder builder = new CccSpecificationParams.Builder(); 143 String[] protocolStrings = checkNotNull(bundle.getStringArray(KEY_PROTOCOL_VERSIONS)); 144 for (String protocol : protocolStrings) { 145 builder.addProtocolVersion(CccProtocolVersion.fromString(protocol)); 146 } 147 148 for (int config : checkNotNull(bundle.getIntArray(KEY_UWB_CONFIGS))) { 149 builder.addUwbConfig(config); 150 } 151 152 String[] pulseShapeComboStrings = 153 checkNotNull(bundle.getStringArray(KEY_PULSE_SHAPE_COMBOS)); 154 for (String pulseShapeCombo : pulseShapeComboStrings) { 155 builder.addPulseShapeCombo(CccPulseShapeCombo.fromString(pulseShapeCombo)); 156 } 157 158 builder.setRanMultiplier(bundle.getInt(KEY_RAN_MULTIPLIER)); 159 160 if (bundle.containsKey(KEY_MAX_RANGING_SESSION_NUMBER)) { 161 builder.setMaxRangingSessionNumber(bundle.getInt(KEY_MAX_RANGING_SESSION_NUMBER)); 162 } 163 164 if (bundle.containsKey(KEY_MIN_UWB_INITIATION_TIME_MS)) { 165 builder.setMinUwbInitiationTimeMs(bundle.getInt(KEY_MIN_UWB_INITIATION_TIME_MS)); 166 } 167 168 for (int chapsPerSlot : checkNotNull(bundle.getIntArray(KEY_CHAPS_PER_SLOTS))) { 169 builder.addChapsPerSlot(chapsPerSlot); 170 } 171 172 for (int syncCode : checkNotNull(bundle.getIntArray(KEY_SYNC_CODES))) { 173 builder.addSyncCode(syncCode); 174 } 175 176 for (int channel : checkNotNull(bundle.getIntArray(KEY_CHANNELS))) { 177 builder.addChannel(channel); 178 } 179 180 for (int hoppingConfig : checkNotNull(bundle.getIntArray(KEY_HOPPING_CONFIGS))) { 181 builder.addHoppingConfigMode(hoppingConfig); 182 } 183 184 for (int hoppingSequence : checkNotNull(bundle.getIntArray(KEY_HOPPING_SEQUENCES))) { 185 builder.addHoppingSequence(hoppingSequence); 186 } 187 188 return builder.build(); 189 } 190 toIntArray(List<Integer> data)191 private int[] toIntArray(List<Integer> data) { 192 int[] res = new int[data.size()]; 193 for (int i = 0; i < data.size(); i++) { 194 res[i] = data.get(i); 195 } 196 return res; 197 } 198 getProtocolVersions()199 public List<CccProtocolVersion> getProtocolVersions() { 200 return mProtocolVersions; 201 } 202 203 @UwbConfig getUwbConfigs()204 public List<Integer> getUwbConfigs() { 205 return mUwbConfigs; 206 } 207 getPulseShapeCombos()208 public List<CccPulseShapeCombo> getPulseShapeCombos() { 209 return mPulseShapeCombos; 210 } 211 212 @IntRange(from = 0, to = 255) getRanMultiplier()213 public int getRanMultiplier() { 214 return mRanMultiplier; 215 } 216 getMaxRangingSessionNumber()217 public int getMaxRangingSessionNumber() { 218 return mMaxRangingSessionNumber; 219 } 220 getMinUwbInitiationTimeMs()221 public int getMinUwbInitiationTimeMs() { 222 return mMinUwbInitiationTimeMs; 223 } 224 225 @ChapsPerSlot getChapsPerSlot()226 public List<Integer> getChapsPerSlot() { 227 return mChapsPerSlot; 228 } 229 230 @SyncCodeIndex getSyncCodes()231 public List<Integer> getSyncCodes() { 232 return mSyncCodes; 233 } 234 235 @Channel getChannels()236 public List<Integer> getChannels() { 237 return mChannels; 238 } 239 240 @HoppingSequence getHoppingSequences()241 public List<Integer> getHoppingSequences() { 242 return mHoppingSequences; 243 } 244 245 @HoppingConfigMode getHoppingConfigModes()246 public List<Integer> getHoppingConfigModes() { 247 return mHoppingConfigModes; 248 } 249 250 @Override equals(@ullable Object other)251 public boolean equals(@Nullable Object other) { 252 if (other instanceof CccSpecificationParams) { 253 CccSpecificationParams otherSpecificationParams = (CccSpecificationParams) other; 254 return otherSpecificationParams.mProtocolVersions.equals(mProtocolVersions) 255 && otherSpecificationParams.mPulseShapeCombos.equals(mPulseShapeCombos) 256 && otherSpecificationParams.mUwbConfigs.equals(mUwbConfigs) 257 && otherSpecificationParams.mRanMultiplier == mRanMultiplier 258 && otherSpecificationParams.mMaxRangingSessionNumber == mMaxRangingSessionNumber 259 && otherSpecificationParams.mMinUwbInitiationTimeMs == mMinUwbInitiationTimeMs 260 && otherSpecificationParams.mChapsPerSlot.equals(mChapsPerSlot) 261 && otherSpecificationParams.mSyncCodes.equals(mSyncCodes) 262 && otherSpecificationParams.mChannels.equals(mChannels) 263 && otherSpecificationParams.mHoppingConfigModes.equals(mHoppingConfigModes) 264 && otherSpecificationParams.mHoppingSequences.equals(mHoppingSequences); 265 } 266 return false; 267 } 268 269 @Override hashCode()270 public int hashCode() { 271 return Arrays.hashCode( 272 new int[] { 273 mProtocolVersions.hashCode(), 274 mPulseShapeCombos.hashCode(), 275 mUwbConfigs.hashCode(), 276 mRanMultiplier, 277 mMaxRangingSessionNumber, 278 mMinUwbInitiationTimeMs, 279 mChapsPerSlot.hashCode(), 280 mSyncCodes.hashCode(), 281 mChannels.hashCode(), 282 mHoppingConfigModes.hashCode(), 283 mHoppingSequences.hashCode() 284 }); 285 } 286 287 /** Builder */ 288 public static class Builder { 289 private List<CccProtocolVersion> mProtocolVersions = new ArrayList<>(); 290 @UwbConfig private List<Integer> mUwbConfigs = new ArrayList<>(); 291 private List<CccPulseShapeCombo> mPulseShapeCombos = new ArrayList<>(); 292 private RequiredParam<Integer> mRanMultiplier = new RequiredParam<>(); 293 private int mMinUwbInitiationTimeMs = -1; 294 private int mMaxRangingSessionNumber = DEFAULT_MAX_RANGING_SESSIONS_NUMBER; 295 @ChapsPerSlot private List<Integer> mChapsPerSlot = new ArrayList<>(); 296 @SyncCodeIndex private List<Integer> mSyncCodes = new ArrayList<>(); 297 @Channel private List<Integer> mChannels = new ArrayList<>(); 298 @HoppingSequence private List<Integer> mHoppingSequences = new ArrayList<>(); 299 @HoppingConfigMode private List<Integer> mHoppingConfigModes = new ArrayList<>(); 300 addProtocolVersion(@onNull CccProtocolVersion version)301 public Builder addProtocolVersion(@NonNull CccProtocolVersion version) { 302 mProtocolVersions.add(version); 303 return this; 304 } 305 addUwbConfig(@wbConfig int uwbConfig)306 public Builder addUwbConfig(@UwbConfig int uwbConfig) { 307 mUwbConfigs.add(uwbConfig); 308 return this; 309 } 310 addPulseShapeCombo(CccPulseShapeCombo pulseShapeCombo)311 public Builder addPulseShapeCombo(CccPulseShapeCombo pulseShapeCombo) { 312 mPulseShapeCombos.add(pulseShapeCombo); 313 return this; 314 } 315 setRanMultiplier(int ranMultiplier)316 public Builder setRanMultiplier(int ranMultiplier) { 317 if (ranMultiplier < 0 || ranMultiplier > 255) { 318 throw new IllegalArgumentException("Invalid RAN Multiplier"); 319 } 320 mRanMultiplier.set(ranMultiplier); 321 return this; 322 } 323 324 /** 325 * Set maximum supported ranging session number 326 * @param maxRangingSessionNumber : maximum ranging session number supported 327 * @return CccSpecificationParams builder 328 */ setMaxRangingSessionNumber(int maxRangingSessionNumber)329 public Builder setMaxRangingSessionNumber(int maxRangingSessionNumber) { 330 mMaxRangingSessionNumber = maxRangingSessionNumber; 331 return this; 332 } 333 334 /** 335 * Set minimum initiation time delay in ms 336 * @param minUwbInitiationTimeMs : minimum initiation time delay supported 337 * @return CccSpecificationParams builder 338 */ setMinUwbInitiationTimeMs(int minUwbInitiationTimeMs)339 public Builder setMinUwbInitiationTimeMs(int minUwbInitiationTimeMs) { 340 mMinUwbInitiationTimeMs = minUwbInitiationTimeMs; 341 return this; 342 } 343 addChapsPerSlot(@hapsPerSlot int chapsPerSlot)344 public Builder addChapsPerSlot(@ChapsPerSlot int chapsPerSlot) { 345 mChapsPerSlot.add(chapsPerSlot); 346 return this; 347 } 348 addSyncCode(@yncCodeIndex int syncCode)349 public Builder addSyncCode(@SyncCodeIndex int syncCode) { 350 mSyncCodes.add(syncCode); 351 return this; 352 } 353 addChannel(@hannel int channel)354 public Builder addChannel(@Channel int channel) { 355 mChannels.add(channel); 356 return this; 357 } 358 addHoppingConfigMode(@oppingConfigMode int hoppingConfigMode)359 public Builder addHoppingConfigMode(@HoppingConfigMode int hoppingConfigMode) { 360 mHoppingConfigModes.add(hoppingConfigMode); 361 return this; 362 } 363 addHoppingSequence(@oppingSequence int hoppingSequence)364 public Builder addHoppingSequence(@HoppingSequence int hoppingSequence) { 365 mHoppingSequences.add(hoppingSequence); 366 return this; 367 } 368 build()369 public CccSpecificationParams build() { 370 if (mProtocolVersions.size() == 0) { 371 throw new IllegalStateException("No protocol versions set"); 372 } 373 374 if (mUwbConfigs.size() == 0) { 375 throw new IllegalStateException("No UWB Configs set"); 376 } 377 378 if (mPulseShapeCombos.size() == 0) { 379 throw new IllegalStateException("No Pulse Shape Combos set"); 380 } 381 382 if (mChapsPerSlot.size() == 0) { 383 throw new IllegalStateException("No Slot Durations set"); 384 } 385 386 if (mSyncCodes.size() == 0) { 387 throw new IllegalStateException("No Sync Codes set"); 388 } 389 390 if (mHoppingConfigModes.size() == 0) { 391 throw new IllegalStateException("No hopping config modes set"); 392 } 393 394 if (mHoppingSequences.size() == 0) { 395 throw new IllegalStateException("No hopping sequences set"); 396 } 397 398 return new CccSpecificationParams( 399 mProtocolVersions, 400 mUwbConfigs, 401 mPulseShapeCombos, 402 mRanMultiplier.get(), 403 mMaxRangingSessionNumber, 404 mMinUwbInitiationTimeMs, 405 mChapsPerSlot, 406 mSyncCodes, 407 mChannels, 408 mHoppingConfigModes, 409 mHoppingSequences); 410 } 411 } 412 } 413