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.android.internal.util.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 @ChapsPerSlot private final List<Integer> mChapsPerSlot; 51 @SyncCodeIndex private final List<Integer> mSyncCodes; 52 @Channel private final List<Integer> mChannels; 53 @HoppingConfigMode private final List<Integer> mHoppingConfigModes; 54 @HoppingSequence private final List<Integer> mHoppingSequences; 55 56 private static final String KEY_PROTOCOL_VERSIONS = "protocol_versions"; 57 private static final String KEY_UWB_CONFIGS = "uwb_configs"; 58 private static final String KEY_PULSE_SHAPE_COMBOS = "pulse_shape_combos"; 59 private static final String KEY_RAN_MULTIPLIER = "ran_multiplier"; 60 private static final String KEY_CHAPS_PER_SLOTS = "chaps_per_slots"; 61 private static final String KEY_SYNC_CODES = "sync_codes"; 62 private static final String KEY_CHANNELS = "channels"; 63 private static final String KEY_HOPPING_CONFIGS = "hopping_config_modes"; 64 private static final String KEY_HOPPING_SEQUENCES = "hopping_sequences"; 65 CccSpecificationParams( List<CccProtocolVersion> protocolVersions, @UwbConfig List<Integer> uwbConfigs, List<CccPulseShapeCombo> pulseShapeCombos, int ranMultiplier, @ChapsPerSlot List<Integer> chapsPerSlot, @SyncCodeIndex List<Integer> syncCodes, @Channel List<Integer> channels, @HoppingConfigMode List<Integer> hoppingConfigModes, @HoppingSequence List<Integer> hoppingSequences)66 private CccSpecificationParams( 67 List<CccProtocolVersion> protocolVersions, 68 @UwbConfig List<Integer> uwbConfigs, 69 List<CccPulseShapeCombo> pulseShapeCombos, 70 int ranMultiplier, 71 @ChapsPerSlot List<Integer> chapsPerSlot, 72 @SyncCodeIndex List<Integer> syncCodes, 73 @Channel List<Integer> channels, 74 @HoppingConfigMode List<Integer> hoppingConfigModes, 75 @HoppingSequence List<Integer> hoppingSequences) { 76 mProtocolVersions = protocolVersions; 77 mUwbConfigs = uwbConfigs; 78 mPulseShapeCombos = pulseShapeCombos; 79 mRanMultiplier = ranMultiplier; 80 mChapsPerSlot = chapsPerSlot; 81 mSyncCodes = syncCodes; 82 mChannels = channels; 83 mHoppingConfigModes = hoppingConfigModes; 84 mHoppingSequences = hoppingSequences; 85 } 86 87 @Override getBundleVersion()88 protected int getBundleVersion() { 89 return BUNDLE_VERSION_CURRENT; 90 } 91 92 @Override toBundle()93 public PersistableBundle toBundle() { 94 PersistableBundle bundle = super.toBundle(); 95 String[] protocols = new String[mProtocolVersions.size()]; 96 for (int i = 0; i < protocols.length; i++) { 97 protocols[i] = mProtocolVersions.get(i).toString(); 98 } 99 String[] pulseShapeCombos = new String[mPulseShapeCombos.size()]; 100 for (int i = 0; i < pulseShapeCombos.length; i++) { 101 pulseShapeCombos[i] = mPulseShapeCombos.get(i).toString(); 102 } 103 bundle.putStringArray(KEY_PROTOCOL_VERSIONS, protocols); 104 bundle.putIntArray(KEY_UWB_CONFIGS, toIntArray(mUwbConfigs)); 105 bundle.putStringArray(KEY_PULSE_SHAPE_COMBOS, pulseShapeCombos); 106 bundle.putInt(KEY_RAN_MULTIPLIER, mRanMultiplier); 107 bundle.putIntArray(KEY_CHAPS_PER_SLOTS, toIntArray(mChapsPerSlot)); 108 bundle.putIntArray(KEY_SYNC_CODES, toIntArray(mSyncCodes)); 109 bundle.putIntArray(KEY_CHANNELS, toIntArray(mChannels)); 110 bundle.putIntArray(KEY_HOPPING_CONFIGS, toIntArray(mHoppingConfigModes)); 111 bundle.putIntArray(KEY_HOPPING_SEQUENCES, toIntArray(mHoppingSequences)); 112 return bundle; 113 } 114 fromBundle(PersistableBundle bundle)115 public static CccSpecificationParams fromBundle(PersistableBundle bundle) { 116 if (!isCorrectProtocol(bundle)) { 117 throw new IllegalArgumentException("Invalid protocol"); 118 } 119 120 switch (getBundleVersion(bundle)) { 121 case BUNDLE_VERSION_1: 122 return parseVersion1(bundle); 123 124 default: 125 throw new IllegalArgumentException("Invalid bundle version"); 126 } 127 } 128 parseVersion1(PersistableBundle bundle)129 private static CccSpecificationParams parseVersion1(PersistableBundle bundle) { 130 CccSpecificationParams.Builder builder = new CccSpecificationParams.Builder(); 131 String[] protocolStrings = checkNotNull(bundle.getStringArray(KEY_PROTOCOL_VERSIONS)); 132 for (String protocol : protocolStrings) { 133 builder.addProtocolVersion(CccProtocolVersion.fromString(protocol)); 134 } 135 136 for (int config : checkNotNull(bundle.getIntArray(KEY_UWB_CONFIGS))) { 137 builder.addUwbConfig(config); 138 } 139 140 String[] pulseShapeComboStrings = 141 checkNotNull(bundle.getStringArray(KEY_PULSE_SHAPE_COMBOS)); 142 for (String pulseShapeCombo : pulseShapeComboStrings) { 143 builder.addPulseShapeCombo(CccPulseShapeCombo.fromString(pulseShapeCombo)); 144 } 145 146 builder.setRanMultiplier(bundle.getInt(KEY_RAN_MULTIPLIER)); 147 148 for (int chapsPerSlot : checkNotNull(bundle.getIntArray(KEY_CHAPS_PER_SLOTS))) { 149 builder.addChapsPerSlot(chapsPerSlot); 150 } 151 152 for (int syncCode : checkNotNull(bundle.getIntArray(KEY_SYNC_CODES))) { 153 builder.addSyncCode(syncCode); 154 } 155 156 for (int channel : checkNotNull(bundle.getIntArray(KEY_CHANNELS))) { 157 builder.addChannel(channel); 158 } 159 160 for (int hoppingConfig : checkNotNull(bundle.getIntArray(KEY_HOPPING_CONFIGS))) { 161 builder.addHoppingConfigMode(hoppingConfig); 162 } 163 164 for (int hoppingSequence : checkNotNull(bundle.getIntArray(KEY_HOPPING_SEQUENCES))) { 165 builder.addHoppingSequence(hoppingSequence); 166 } 167 168 return builder.build(); 169 } 170 toIntArray(List<Integer> data)171 private int[] toIntArray(List<Integer> data) { 172 int[] res = new int[data.size()]; 173 for (int i = 0; i < data.size(); i++) { 174 res[i] = data.get(i); 175 } 176 return res; 177 } 178 getProtocolVersions()179 public List<CccProtocolVersion> getProtocolVersions() { 180 return mProtocolVersions; 181 } 182 183 @UwbConfig getUwbConfigs()184 public List<Integer> getUwbConfigs() { 185 return mUwbConfigs; 186 } 187 getPulseShapeCombos()188 public List<CccPulseShapeCombo> getPulseShapeCombos() { 189 return mPulseShapeCombos; 190 } 191 192 @IntRange(from = 0, to = 255) getRanMultiplier()193 public int getRanMultiplier() { 194 return mRanMultiplier; 195 } 196 197 @ChapsPerSlot getChapsPerSlot()198 public List<Integer> getChapsPerSlot() { 199 return mChapsPerSlot; 200 } 201 202 @SyncCodeIndex getSyncCodes()203 public List<Integer> getSyncCodes() { 204 return mSyncCodes; 205 } 206 207 @Channel getChannels()208 public List<Integer> getChannels() { 209 return mChannels; 210 } 211 212 @HoppingSequence getHoppingSequences()213 public List<Integer> getHoppingSequences() { 214 return mHoppingSequences; 215 } 216 217 @HoppingConfigMode getHoppingConfigModes()218 public List<Integer> getHoppingConfigModes() { 219 return mHoppingConfigModes; 220 } 221 222 @Override equals(@ullable Object other)223 public boolean equals(@Nullable Object other) { 224 if (other instanceof CccSpecificationParams) { 225 CccSpecificationParams otherSpecificationParams = (CccSpecificationParams) other; 226 return otherSpecificationParams.mProtocolVersions.equals(mProtocolVersions) 227 && otherSpecificationParams.mPulseShapeCombos.equals(mPulseShapeCombos) 228 && otherSpecificationParams.mUwbConfigs.equals(mUwbConfigs) 229 && otherSpecificationParams.mRanMultiplier == mRanMultiplier 230 && otherSpecificationParams.mChapsPerSlot.equals(mChapsPerSlot) 231 && otherSpecificationParams.mSyncCodes.equals(mSyncCodes) 232 && otherSpecificationParams.mChannels.equals(mChannels) 233 && otherSpecificationParams.mHoppingConfigModes.equals(mHoppingConfigModes) 234 && otherSpecificationParams.mHoppingSequences.equals(mHoppingSequences); 235 } 236 return false; 237 } 238 239 @Override hashCode()240 public int hashCode() { 241 return Arrays.hashCode( 242 new int[] { 243 mProtocolVersions.hashCode(), 244 mPulseShapeCombos.hashCode(), 245 mUwbConfigs.hashCode(), 246 mRanMultiplier, 247 mChapsPerSlot.hashCode(), 248 mSyncCodes.hashCode(), 249 mChannels.hashCode(), 250 mHoppingConfigModes.hashCode(), 251 mHoppingSequences.hashCode() 252 }); 253 } 254 255 /** Builder */ 256 public static class Builder { 257 private List<CccProtocolVersion> mProtocolVersions = new ArrayList<>(); 258 @UwbConfig private List<Integer> mUwbConfigs = new ArrayList<>(); 259 private List<CccPulseShapeCombo> mPulseShapeCombos = new ArrayList<>(); 260 private RequiredParam<Integer> mRanMultiplier = new RequiredParam<>(); 261 @ChapsPerSlot private List<Integer> mChapsPerSlot = new ArrayList<>(); 262 @SyncCodeIndex private List<Integer> mSyncCodes = new ArrayList<>(); 263 @Channel private List<Integer> mChannels = new ArrayList<>(); 264 @HoppingSequence private List<Integer> mHoppingSequences = new ArrayList<>(); 265 @HoppingConfigMode private List<Integer> mHoppingConfigModes = new ArrayList<>(); 266 addProtocolVersion(@onNull CccProtocolVersion version)267 public Builder addProtocolVersion(@NonNull CccProtocolVersion version) { 268 mProtocolVersions.add(version); 269 return this; 270 } 271 addUwbConfig(@wbConfig int uwbConfig)272 public Builder addUwbConfig(@UwbConfig int uwbConfig) { 273 mUwbConfigs.add(uwbConfig); 274 return this; 275 } 276 addPulseShapeCombo(CccPulseShapeCombo pulseShapeCombo)277 public Builder addPulseShapeCombo(CccPulseShapeCombo pulseShapeCombo) { 278 mPulseShapeCombos.add(pulseShapeCombo); 279 return this; 280 } 281 setRanMultiplier(int ranMultiplier)282 public Builder setRanMultiplier(int ranMultiplier) { 283 if (ranMultiplier < 0 || ranMultiplier > 255) { 284 throw new IllegalArgumentException("Invalid RAN Multiplier"); 285 } 286 mRanMultiplier.set(ranMultiplier); 287 return this; 288 } 289 addChapsPerSlot(@hapsPerSlot int chapsPerSlot)290 public Builder addChapsPerSlot(@ChapsPerSlot int chapsPerSlot) { 291 mChapsPerSlot.add(chapsPerSlot); 292 return this; 293 } 294 addSyncCode(@yncCodeIndex int syncCode)295 public Builder addSyncCode(@SyncCodeIndex int syncCode) { 296 mSyncCodes.add(syncCode); 297 return this; 298 } 299 addChannel(@hannel int channel)300 public Builder addChannel(@Channel int channel) { 301 mChannels.add(channel); 302 return this; 303 } 304 addHoppingConfigMode(@oppingConfigMode int hoppingConfigMode)305 public Builder addHoppingConfigMode(@HoppingConfigMode int hoppingConfigMode) { 306 mHoppingConfigModes.add(hoppingConfigMode); 307 return this; 308 } 309 addHoppingSequence(@oppingSequence int hoppingSequence)310 public Builder addHoppingSequence(@HoppingSequence int hoppingSequence) { 311 mHoppingSequences.add(hoppingSequence); 312 return this; 313 } 314 build()315 public CccSpecificationParams build() { 316 if (mProtocolVersions.size() == 0) { 317 throw new IllegalStateException("No protocol versions set"); 318 } 319 320 if (mUwbConfigs.size() == 0) { 321 throw new IllegalStateException("No UWB Configs set"); 322 } 323 324 if (mPulseShapeCombos.size() == 0) { 325 throw new IllegalStateException("No Pulse Shape Combos set"); 326 } 327 328 if (mChapsPerSlot.size() == 0) { 329 throw new IllegalStateException("No Slot Durations set"); 330 } 331 332 if (mSyncCodes.size() == 0) { 333 throw new IllegalStateException("No Sync Codes set"); 334 } 335 336 if (mChannels.size() == 0) { 337 throw new IllegalStateException("No channels set"); 338 } 339 340 if (mHoppingConfigModes.size() == 0) { 341 throw new IllegalStateException("No hopping config modes set"); 342 } 343 344 if (mHoppingSequences.size() == 0) { 345 throw new IllegalStateException("No hopping sequences set"); 346 } 347 348 return new CccSpecificationParams( 349 mProtocolVersions, 350 mUwbConfigs, 351 mPulseShapeCombos, 352 mRanMultiplier.get(), 353 mChapsPerSlot, 354 mSyncCodes, 355 mChannels, 356 mHoppingConfigModes, 357 mHoppingSequences); 358 } 359 } 360 } 361