1 /* 2 * Copyright 2024 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.ranging.session; 18 19 import static android.ranging.RangingPreference.DEVICE_ROLE_INITIATOR; 20 21 import android.ranging.RangingConfig; 22 import android.ranging.RangingDevice; 23 import android.ranging.RangingManager; 24 import android.ranging.RangingPreference; 25 import android.ranging.SessionConfig; 26 import android.ranging.raw.RawRangingDevice; 27 import android.ranging.uwb.UwbAddress; 28 import android.ranging.uwb.UwbRangingParams; 29 30 import androidx.annotation.NonNull; 31 32 import com.android.server.ranging.RangingTechnology; 33 import com.android.server.ranging.blerssi.BleRssiConfig; 34 import com.android.server.ranging.cs.CsConfig; 35 import com.android.server.ranging.rtt.RttConfig; 36 import com.android.server.ranging.uwb.UwbConfig; 37 38 import com.google.common.collect.BiMap; 39 import com.google.common.collect.HashBiMap; 40 import com.google.common.collect.ImmutableBiMap; 41 import com.google.common.collect.ImmutableSet; 42 import com.google.common.collect.Sets; 43 44 import java.util.Arrays; 45 import java.util.HashMap; 46 import java.util.HashSet; 47 import java.util.Map; 48 import java.util.Objects; 49 import java.util.Set; 50 51 public class RangingSessionConfig { 52 private final @RangingPreference.DeviceRole int mDeviceRole; 53 private final @RangingConfig.RangingSessionType int mSessionType; 54 private final SessionConfig mSessionConfig; 55 56 /** A complete configuration for a session within a specific ranging technology's stack */ 57 public interface TechnologyConfig { 58 @NonNull getTechnology()59 RangingTechnology getTechnology(); 60 getDeviceRole()61 @RangingPreference.DeviceRole int getDeviceRole(); 62 } 63 64 /** A config for a technology that only supports 1 peer per session. */ 65 public interface UnicastTechnologyConfig extends TechnologyConfig { 66 @NonNull getPeerDevice()67 RangingDevice getPeerDevice(); 68 } 69 70 /** A config for a technology that supports multiple peers per session. */ 71 public interface MulticastTechnologyConfig extends TechnologyConfig { 72 /** 73 * @return the set of peers within this technology-specific session. 74 */ 75 @NonNull getPeerDevices()76 ImmutableSet<RangingDevice> getPeerDevices(); 77 } 78 RangingSessionConfig(Builder builder)79 private RangingSessionConfig(Builder builder) { 80 mDeviceRole = builder.mDeviceRole; 81 mSessionType = builder.mSessionType; 82 mSessionConfig = builder.mSessionConfig; 83 } 84 getTechnologyConfigs( @onNull Set<RawRangingDevice> peerParams )85 public @NonNull ImmutableSet<TechnologyConfig> getTechnologyConfigs( 86 @NonNull Set<RawRangingDevice> peerParams 87 ) { 88 return ImmutableSet.copyOf(Sets.union( 89 getConfigsForUnicastTechnologies(peerParams), 90 getConfigsForMulticastTechnologies(peerParams))); 91 } 92 getConfigsForMulticastTechnologies( @onNull Set<RawRangingDevice> peerParams )93 private @NonNull Set<MulticastTechnologyConfig> getConfigsForMulticastTechnologies( 94 @NonNull Set<RawRangingDevice> peerParams 95 ) { 96 Set<MulticastTechnologyConfig> configs = new HashSet<>(); 97 98 Map<PeerIgnoringParamsHasher<UwbRangingParams>, BiMap<RangingDevice, UwbAddress>> 99 uwbPeersByParams = PeerIgnoringParamsHasher.groupUwbPeersByParams(peerParams); 100 101 // Create a config for each unique params. When multiple peers share the same params, this 102 // config will specify a multicast session containing all of them. 103 for (PeerIgnoringParamsHasher<UwbRangingParams> key : uwbPeersByParams.keySet()) { 104 configs.add(new UwbConfig.Builder(key.mParams) 105 .setDeviceRole(mDeviceRole) 106 .setPeerAddresses(ImmutableBiMap.copyOf(uwbPeersByParams.get(key))) 107 .setSessionConfig(mSessionConfig) 108 .build()); 109 } 110 111 return configs; 112 } 113 getConfigsForUnicastTechnologies( @onNull Set<RawRangingDevice> peerParams )114 private @NonNull Set<UnicastTechnologyConfig> getConfigsForUnicastTechnologies( 115 @NonNull Set<RawRangingDevice> peerParams 116 ) { 117 Set<UnicastTechnologyConfig> configs = new HashSet<>(); 118 119 for (RawRangingDevice peer : peerParams) { 120 if (peer.getRttRangingParams() != null) { 121 configs.add(new RttConfig( 122 mDeviceRole, 123 peer.getRttRangingParams(), 124 mSessionConfig, 125 peer.getRangingDevice())); 126 } 127 if (peer.getBleRssiRangingParams() != null) { 128 configs.add(new BleRssiConfig( 129 mDeviceRole, 130 peer.getBleRssiRangingParams(), 131 mSessionConfig, 132 peer.getRangingDevice())); 133 } 134 // Only CS initiator needs to be configured. 135 if (peer.getCsRangingParams() != null && mDeviceRole == DEVICE_ROLE_INITIATOR) { 136 configs.add(new CsConfig( 137 peer.getCsRangingParams(), 138 mSessionConfig, 139 peer.getRangingDevice())); 140 } 141 } 142 143 return configs; 144 } 145 getDeviceRole()146 public @RangingPreference.DeviceRole int getDeviceRole() { 147 return mDeviceRole; 148 } 149 getSessionType()150 public @RangingConfig.RangingSessionType int getSessionType() { 151 return mSessionType; 152 } 153 getSessionConfig()154 public SessionConfig getSessionConfig() { 155 return mSessionConfig; 156 } 157 158 public static class Builder { 159 private @RangingPreference.DeviceRole int mDeviceRole; 160 private @RangingConfig.RangingSessionType int mSessionType; 161 private SessionConfig mSessionConfig; 162 build()163 public RangingSessionConfig build() { 164 return new RangingSessionConfig(this); 165 } 166 setDeviceRole(@angingPreference.DeviceRole int role)167 public Builder setDeviceRole(@RangingPreference.DeviceRole int role) { 168 mDeviceRole = role; 169 return this; 170 } 171 setSessionType(@angingConfig.RangingSessionType int type)172 public Builder setSessionType(@RangingConfig.RangingSessionType int type) { 173 mSessionType = type; 174 return this; 175 } 176 setSessionConfig(@onNull SessionConfig config)177 public Builder setSessionConfig(@NonNull SessionConfig config) { 178 mSessionConfig = config; 179 return this; 180 } 181 } 182 183 private static class PeerIgnoringParamsHasher<P> { 184 private final P mParams; 185 186 /** 187 * Group together UWB peer devices that share the same params so that they can be put into a 188 * a multicast session. 189 */ 190 public static Map<PeerIgnoringParamsHasher<UwbRangingParams>, BiMap<RangingDevice, 191 UwbAddress>> groupUwbPeersByParams(@onNull Set<RawRangingDevice> peerParams)192 groupUwbPeersByParams(@NonNull Set<RawRangingDevice> peerParams) { 193 Map<PeerIgnoringParamsHasher<UwbRangingParams>, BiMap<RangingDevice, UwbAddress>> 194 peersByParams = new HashMap<>(); 195 for (RawRangingDevice peer : peerParams) { 196 if (peer.getUwbRangingParams() == null) continue; 197 198 PeerIgnoringParamsHasher<UwbRangingParams> key = 199 new PeerIgnoringParamsHasher<>(peer.getUwbRangingParams()); 200 201 if (peersByParams.containsKey(key)) { 202 peersByParams.get(key).put( 203 peer.getRangingDevice(), 204 key.mParams.getPeerAddress()); 205 } else { 206 peersByParams.put(key, HashBiMap.create(Map.of( 207 peer.getRangingDevice(), 208 key.mParams.getPeerAddress()))); 209 } 210 } 211 return peersByParams; 212 } 213 PeerIgnoringParamsHasher(P params)214 PeerIgnoringParamsHasher(P params) { 215 mParams = params; 216 } 217 218 @Override hashCode()219 public int hashCode() { 220 if (mParams instanceof UwbRangingParams params) { 221 return Objects.hash( 222 RangingManager.UWB, 223 params.getSessionId(), 224 params.getSubSessionId(), 225 params.getConfigId(), 226 params.getDeviceAddress(), 227 Arrays.hashCode(params.getSessionKeyInfo()), 228 Arrays.hashCode(params.getSubSessionKeyInfo()), 229 params.getComplexChannel(), 230 params.getRangingUpdateRate(), 231 params.getSlotDuration()); 232 } else { 233 throw new IllegalArgumentException("Provided params object is not supported"); 234 } 235 } 236 237 @Override equals(Object o)238 public boolean equals(Object o) { 239 if (this == o) return true; 240 if (!(o instanceof PeerIgnoringParamsHasher<?> hasher)) return false; 241 242 if (mParams instanceof UwbRangingParams me 243 && hasher.mParams instanceof UwbRangingParams other 244 ) { 245 return me.getSessionId() == other.getSessionId() 246 && me.getSubSessionId() == other.getSubSessionId() 247 && me.getConfigId() == other.getConfigId() 248 && me.getDeviceAddress().equals(other.getDeviceAddress()) 249 && Arrays.equals(me.getSessionKeyInfo(), other.getSessionKeyInfo()) 250 && Arrays.equals(me.getSubSessionKeyInfo(), other.getSubSessionKeyInfo()) 251 && me.getComplexChannel().equals(other.getComplexChannel()) 252 && me.getRangingUpdateRate() == other.getRangingUpdateRate() 253 && me.getSlotDuration() == other.getSlotDuration(); 254 } 255 256 return false; 257 } 258 } 259 260 @Override toString()261 public String toString() { 262 return "RangingSessionConfig{" 263 + "mDeviceRole=" 264 + mDeviceRole 265 + ", mSessionConfig=" 266 + mSessionConfig 267 + '}'; 268 } 269 } 270