1 /* 2 * Copyright (C) 2020 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.wifi.coex; 18 19 import static android.net.wifi.ScanResult.UNSPECIFIED; 20 import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ; 21 import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ; 22 23 import android.net.wifi.CoexUnsafeChannel; 24 import android.net.wifi.ScanResult; 25 import android.net.wifi.WifiAnnotations; 26 import android.os.Build; 27 import android.telephony.Annotation; 28 import android.telephony.PhysicalChannelConfig; 29 import android.util.Log; 30 import android.util.SparseIntArray; 31 32 import androidx.annotation.RequiresApi; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 36 import java.util.ArrayList; 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.NavigableSet; 42 import java.util.Objects; 43 import java.util.Set; 44 import java.util.TreeSet; 45 46 /** 47 * Class containing the unsafe channel algorithms and other utility methods for Wi-Fi/Cellular coex. 48 */ 49 @RequiresApi(Build.VERSION_CODES.S) 50 public class CoexUtils { 51 public static final String TAG = "CoexUtils"; 52 53 private static final int INVALID_CHANNEL = -1; 54 @VisibleForTesting 55 /* package */ static final int INVALID_BAND = -1; 56 @VisibleForTesting 57 /* package */ static final int INVALID_FREQ = -1; 58 59 public static final int GPS_L1_CENTER_FREQ_KHZ = 1575_420; 60 61 public static final int NUM_24_GHZ_CHANNELS = 14; 62 public static final NavigableSet<Integer> CHANNEL_SET_5_GHZ_20_MHZ = create5g20MhzChannels(); 63 public static final NavigableSet<Integer> CHANNEL_SET_5_GHZ_40_MHZ = create5g40MhzChannels(); 64 public static final NavigableSet<Integer> CHANNEL_SET_5_GHZ_80_MHZ = create5g80MhzChannels(); 65 public static final NavigableSet<Integer> CHANNEL_SET_5_GHZ_160_MHZ = create5g160MhzChannels(); 66 public static final NavigableSet<Integer> CHANNEL_SET_5_GHZ = new TreeSet<>(); 67 static { 68 CHANNEL_SET_5_GHZ.addAll(CHANNEL_SET_5_GHZ_20_MHZ); 69 CHANNEL_SET_5_GHZ.addAll(CHANNEL_SET_5_GHZ_40_MHZ); 70 CHANNEL_SET_5_GHZ.addAll(CHANNEL_SET_5_GHZ_80_MHZ); 71 CHANNEL_SET_5_GHZ.addAll(CHANNEL_SET_5_GHZ_160_MHZ); 72 } 73 private static final SparseIntArray DEPENDENT_MAP_5_GHZ = create5gDependentChannelMap(); 74 create5g20MhzChannels()75 private static NavigableSet<Integer> create5g20MhzChannels() { 76 NavigableSet<Integer> set = new TreeSet<>(); 77 for (int chan = 32; chan <= 68; chan += 4) { 78 set.add(chan); 79 } 80 for (int chan = 96; chan <= 144; chan += 4) { 81 set.add(chan); 82 } 83 for (int chan = 149; chan <= 173; chan += 4) { 84 set.add(chan); 85 } 86 return set; 87 } 88 create5g40MhzChannels()89 private static NavigableSet<Integer> create5g40MhzChannels() { 90 NavigableSet<Integer> set = new TreeSet<>(); 91 set.add(34); 92 for (int chan = 38; chan <= 62; chan += 8) { 93 set.add(chan); 94 } 95 for (int chan = 102; chan <= 142; chan += 8) { 96 set.add(chan); 97 } 98 for (int chan = 151; chan <= 159; chan += 8) { 99 set.add(chan); 100 } 101 return set; 102 } 103 create5g80MhzChannels()104 private static NavigableSet<Integer> create5g80MhzChannels() { 105 NavigableSet<Integer> set = new TreeSet<>(); 106 set.add(42); 107 set.add(58); 108 set.add(106); 109 set.add(122); 110 set.add(138); 111 set.add(155); 112 return set; 113 } 114 create5g160MhzChannels()115 private static NavigableSet<Integer> create5g160MhzChannels() { 116 NavigableSet<Integer> set = new TreeSet<>(); 117 set.add(50); 118 set.add(114); 119 return set; 120 } 121 122 /** 123 * Creates a SparseIntArray map of 5GHz channel to the dependent channel that contains it, 124 * if it exists. 125 */ create5gDependentChannelMap()126 private static SparseIntArray create5gDependentChannelMap() { 127 SparseIntArray map = new SparseIntArray(); 128 // Map 160Mhz channels with their dependency 80Mhz channels. 129 for (int chan : CHANNEL_SET_5_GHZ_160_MHZ) { 130 map.put(chan - 8, chan); 131 map.put(chan + 8, chan); 132 } 133 // Map 80Mhz channels with their dependency 40Mhz channels. 134 for (int chan : CHANNEL_SET_5_GHZ_80_MHZ) { 135 map.put(chan - 4, chan); 136 map.put(chan + 4, chan); 137 } 138 // Map 40Mhz channels with their dependency 20Mhz channels. 139 // Note channel 36 maps to both 34 and 38, but will only map to 38 in the dependent map. 140 for (int chan : CHANNEL_SET_5_GHZ_40_MHZ) { 141 map.put(chan - 2, chan); 142 map.put(chan + 2, chan); 143 } 144 return map; 145 } 146 147 // Channels to frequencies 148 149 /** Gets the upper or lower edge of a given channel */ getChannelEdgeKhz(int channel, @WifiAnnotations.WifiBandBasic int band, boolean lowerEdge)150 private static int getChannelEdgeKhz(int channel, @WifiAnnotations.WifiBandBasic int band, 151 boolean lowerEdge) { 152 int centerFreqMhz = ScanResult.convertChannelToFrequencyMhzIfSupported(channel, band); 153 if (centerFreqMhz == UNSPECIFIED) { 154 return INVALID_FREQ; 155 } 156 157 int bandwidthOffsetMhz = 0; 158 if (band == WIFI_BAND_24_GHZ) { 159 bandwidthOffsetMhz = 11; 160 } else if (band == WIFI_BAND_5_GHZ) { 161 if (CHANNEL_SET_5_GHZ_20_MHZ.contains(channel)) { 162 bandwidthOffsetMhz = 10; 163 } else if (CHANNEL_SET_5_GHZ_40_MHZ.contains(channel)) { 164 bandwidthOffsetMhz = 20; 165 } else if (CHANNEL_SET_5_GHZ_80_MHZ.contains(channel)) { 166 bandwidthOffsetMhz = 40; 167 } else { 168 bandwidthOffsetMhz = 80; 169 } 170 } 171 172 if (lowerEdge) { 173 bandwidthOffsetMhz = -bandwidthOffsetMhz; 174 } 175 return (centerFreqMhz + bandwidthOffsetMhz) * 1_000; 176 } 177 178 /** Gets the lower frequency of a given channel */ 179 @VisibleForTesting getLowerFreqKhz(int channel, @WifiAnnotations.WifiBandBasic int band)180 /* package */ static int getLowerFreqKhz(int channel, @WifiAnnotations.WifiBandBasic int band) { 181 return getChannelEdgeKhz(channel, band, true); 182 } 183 184 /** Gets the upper frequency of a given channel */ 185 @VisibleForTesting getUpperFreqKhz(int channel, @WifiAnnotations.WifiBandBasic int band)186 /* package */ static int getUpperFreqKhz(int channel, @WifiAnnotations.WifiBandBasic int band) { 187 return getChannelEdgeKhz(channel, band, false); 188 } 189 190 // Frequencies to channels 191 192 /** 193 * Gets the highest 2.4GHz Wi-Fi channel overlapping the upper edge of an interference 194 * frequency approaching from below the band. 195 * 196 * Returns INVALID_CHANNEL if there is no overlap. 197 */ get2gHighestOverlapChannel(int upperEdgeKhz)198 private static int get2gHighestOverlapChannel(int upperEdgeKhz) { 199 final int band = WIFI_BAND_24_GHZ; 200 if (upperEdgeKhz > getLowerFreqKhz(14, band)) { 201 return 14; 202 } 203 if (upperEdgeKhz > getLowerFreqKhz(13, band)) { 204 return 13; 205 } 206 final int chan1LowerFreqKhz = getLowerFreqKhz(1, band); 207 if (upperEdgeKhz > chan1LowerFreqKhz) { 208 return getOffsetChannel(1, upperEdgeKhz - chan1LowerFreqKhz, 1); 209 } 210 // Edge does not overlap the band. 211 return INVALID_CHANNEL; 212 } 213 214 /** 215 * Gets the lowest 2.4GHz Wi-Fi channel overlapping the lower edge of an interference 216 * frequency approaching from above the band. 217 * 218 * Returns INVALID_CHANNEL if there is no overlap. 219 */ get2gLowestOverlapChannel(int lowerEdgeKhz)220 private static int get2gLowestOverlapChannel(int lowerEdgeKhz) { 221 final int band = WIFI_BAND_24_GHZ; 222 if (lowerEdgeKhz < getUpperFreqKhz(1, band)) { 223 return 1; 224 } 225 final int chan13UpperFreqKhz = getUpperFreqKhz(13, band); 226 if (lowerEdgeKhz < chan13UpperFreqKhz) { 227 return getOffsetChannel(13, lowerEdgeKhz - chan13UpperFreqKhz, 1); 228 } 229 if (lowerEdgeKhz < getUpperFreqKhz(14, band)) { 230 return 14; 231 } 232 // Edge does not overlap the band. 233 return INVALID_CHANNEL; 234 } 235 236 /** 237 * Gets the highest 5GHz Wi-Fi channel overlapping the upper edge of an interference 238 * frequency approaching from below the band. 239 * 240 * Returns INVALID_CHANNEL if there is no overlap. 241 */ get5gHighestOverlap20MhzChannel(int upperEdgeKhz)242 private static int get5gHighestOverlap20MhzChannel(int upperEdgeKhz) { 243 final int band = WIFI_BAND_5_GHZ; 244 // 149 to 173 245 if (upperEdgeKhz > getLowerFreqKhz(173, band)) { 246 return 173; 247 } 248 final int chan149LowerFreqKhz = getLowerFreqKhz(149, band); 249 if (upperEdgeKhz > chan149LowerFreqKhz) { 250 return getOffsetChannel(149, upperEdgeKhz - chan149LowerFreqKhz, 4); 251 } 252 // 96 to 144 253 if (upperEdgeKhz > getLowerFreqKhz(144, band)) { 254 return 144; 255 } 256 final int chan96LowerFreqKhz = getLowerFreqKhz(96, band); 257 if (upperEdgeKhz > chan96LowerFreqKhz) { 258 return getOffsetChannel(96, upperEdgeKhz - chan96LowerFreqKhz, 4); 259 } 260 // 32 to 68 261 if (upperEdgeKhz > getLowerFreqKhz(68, band)) { 262 return 68; 263 } 264 final int chan32LowerFreqKhz = getLowerFreqKhz(32, band); 265 if (upperEdgeKhz > chan32LowerFreqKhz) { 266 return getOffsetChannel(32, upperEdgeKhz - chan32LowerFreqKhz, 4); 267 } 268 // Edge does not overlap the band. 269 return INVALID_CHANNEL; 270 } 271 272 /** 273 * Gets the lowest 5GHz Wi-Fi channel overlapping the lower edge of an interference 274 * frequency approaching from above the band. 275 * 276 * Returns INVALID_CHANNEL if there is no overlap. 277 */ get5gLowestOverlap20MhzChannel(int lowerEdgeKhz)278 private static int get5gLowestOverlap20MhzChannel(int lowerEdgeKhz) { 279 final int band = WIFI_BAND_5_GHZ; 280 // 32 to 68 281 if (lowerEdgeKhz < getUpperFreqKhz(32, band)) { 282 return 32; 283 } 284 final int chan68UpperFreqKhz = getUpperFreqKhz(68, band); 285 if (lowerEdgeKhz < chan68UpperFreqKhz) { 286 return getOffsetChannel(68, lowerEdgeKhz - chan68UpperFreqKhz, 4); 287 } 288 // 96 to 144 289 if (lowerEdgeKhz < getUpperFreqKhz(96, band)) { 290 return 96; 291 } 292 final int chan144UpperFreqKhz = getUpperFreqKhz(144, band); 293 if (lowerEdgeKhz < chan144UpperFreqKhz) { 294 return getOffsetChannel(144, lowerEdgeKhz - chan144UpperFreqKhz, 4); 295 } 296 // 149 to 173 297 if (lowerEdgeKhz < getUpperFreqKhz(149, band)) { 298 return 149; 299 } 300 final int chan173UpperFreqKhz = getUpperFreqKhz(173, band); 301 if (lowerEdgeKhz < chan173UpperFreqKhz) { 302 return getOffsetChannel(173, lowerEdgeKhz - chan173UpperFreqKhz, 4); 303 } 304 // Edge does not overlap the band. 305 return INVALID_CHANNEL; 306 } 307 308 /** 309 * Returns the furthest channel located a given frequency offset away from a start channel 310 * counting by a given channel step size. A positive frequency offset will give a channel 311 * above the start, and a negative frequency offset will give a channel below the start. 312 * 313 * @param startChannel Channel to start from 314 * @param offsetKhz Offset distance in Khz 315 * @param channelStepSize Step size to count channels by 316 * @return The channel that lies the given offset away from the start channel 317 */ 318 @VisibleForTesting getOffsetChannel( int startChannel, int offsetKhz, int channelStepSize)319 /* package */ static int getOffsetChannel( 320 int startChannel, int offsetKhz, int channelStepSize) { 321 // Each channel number is always separated by 5Mhz. 322 int channelSpacingKhz = 5_000; 323 int stepsToOffset = offsetKhz / (channelSpacingKhz * channelStepSize); 324 // Offset lands directly channel edge; use previous channel based on offset direction. 325 if (offsetKhz % (channelSpacingKhz * channelStepSize) == 0) { 326 if (offsetKhz > 0) { 327 stepsToOffset--; 328 } else if (offsetKhz < 0) { 329 stepsToOffset++; 330 } 331 } 332 return startChannel + (stepsToOffset * channelStepSize); 333 } 334 335 /** 336 * Returns the percent overlap (0 to 100) of an aggressor frequency range over a victim 337 * frequency range, 338 */ getOverlapPercent(int aggressorLowerKhz, int aggressorUpperKhz, int victimLowerKhz, int victimUpperKhz)339 private static int getOverlapPercent(int aggressorLowerKhz, int aggressorUpperKhz, 340 int victimLowerKhz, int victimUpperKhz) { 341 final int victimBandwidthKhz = victimUpperKhz - victimLowerKhz; 342 int overlapWidthKhz = Math.min(aggressorUpperKhz, victimUpperKhz) 343 - Math.max(aggressorLowerKhz, victimLowerKhz); 344 if (overlapWidthKhz < 0) { 345 overlapWidthKhz = 0; 346 } 347 if (victimBandwidthKhz == 0) { 348 return 0; 349 } 350 return overlapWidthKhz * 100 / victimBandwidthKhz; 351 } 352 353 /** 354 * Returns the CoexUnsafeChannels for the given cell channel and threshold. 355 */ getNeighboringCoexUnsafeChannels( int cellFreqKhz, int cellBandwidthKhz, int thresholdKhz, int powerCapDbm)356 public static List<CoexUnsafeChannel> getNeighboringCoexUnsafeChannels( 357 int cellFreqKhz, int cellBandwidthKhz, int thresholdKhz, int powerCapDbm) { 358 List<CoexUnsafeChannel> coexUnsafeChannels = new ArrayList<>(); 359 final int unsafeLowerKhz = cellFreqKhz - (cellBandwidthKhz / 2) - thresholdKhz; 360 final int unsafeUpperKhz = cellFreqKhz + (cellBandwidthKhz / 2) + thresholdKhz; 361 362 // 2.4Ghz 363 final int lowest2gChannel = get2gLowestOverlapChannel(unsafeLowerKhz); 364 final int highest2gChannel = get2gHighestOverlapChannel(unsafeUpperKhz); 365 // If the interference has a valid overlap over the 2.4GHz band, mark every channel 366 // in the inclusive range of the lowest and highest overlapped channels. 367 if (lowest2gChannel != INVALID_CHANNEL && highest2gChannel != INVALID_CHANNEL 368 && lowest2gChannel <= highest2gChannel) { 369 for (int channel = lowest2gChannel; channel <= highest2gChannel; channel++) { 370 coexUnsafeChannels.add( 371 new CoexUnsafeChannel(WIFI_BAND_24_GHZ, channel, powerCapDbm)); 372 } 373 } 374 375 // 5Ghz 376 final int highest5gChannel = get5gHighestOverlap20MhzChannel(unsafeUpperKhz); 377 final int lowest5gChannel = get5gLowestOverlap20MhzChannel(unsafeLowerKhz); 378 // If the interference has a valid overlap over the 5GHz band, mark every channel 379 // in the inclusive range of the lowest and highest overlapped channels. 380 if (lowest5gChannel != INVALID_CHANNEL && highest5gChannel != INVALID_CHANNEL 381 && lowest5gChannel <= highest5gChannel) { 382 final Set<Integer> overlapped5g20MhzChannels = CHANNEL_SET_5_GHZ_20_MHZ.subSet( 383 lowest5gChannel, true, 384 highest5gChannel, true); 385 final Set<Integer> seen = new HashSet<>(); 386 // Mark overlapped 20Mhz channels and their dependents as unsafe 387 for (int channel : overlapped5g20MhzChannels) { 388 while (channel != 0) { 389 if (!seen.add(channel)) { 390 // Dependent channel was already marked unsafe by another dependency channel 391 break; 392 } 393 coexUnsafeChannels.add( 394 new CoexUnsafeChannel(WIFI_BAND_5_GHZ, channel, powerCapDbm)); 395 // Go to each dependent 40, 80, 160Mhz channel and mark them as unsafe. 396 // If a dependent doesn't exist, channel will be set to 0 and the loop ends. 397 channel = DEPENDENT_MAP_5_GHZ.get(channel, 0); 398 } 399 } 400 // 36 should also map to 34, but only maps to 38 in the dependent channel map. 401 if (overlapped5g20MhzChannels.contains(36) && !seen.contains(34)) { 402 coexUnsafeChannels.add(new CoexUnsafeChannel(WIFI_BAND_5_GHZ, 34, powerCapDbm)); 403 } 404 } 405 406 return coexUnsafeChannels; 407 } 408 409 /** 410 * Returns the 2.4GHz UnsafeChannels affected by the harmonic interference from a given uplink 411 * cell channel. 412 */ get2gHarmonicCoexUnsafeChannels( int ulFreqKhz, int ulBandwidthKhz, int harmonicDegree, int overlapPercentThreshold, int powerCapDbm)413 public static List<CoexUnsafeChannel> get2gHarmonicCoexUnsafeChannels( 414 int ulFreqKhz, int ulBandwidthKhz, int harmonicDegree, int overlapPercentThreshold, 415 int powerCapDbm) { 416 List<CoexUnsafeChannel> coexUnsafeChannels = new ArrayList<>(); 417 final int unsafeLowerKhz = (ulFreqKhz - (ulBandwidthKhz / 2)) * harmonicDegree; 418 final int unsafeUpperKhz = (ulFreqKhz + (ulBandwidthKhz / 2)) * harmonicDegree; 419 420 int lowest2gChannel = get2gLowestOverlapChannel(unsafeLowerKhz); 421 int highest2gChannel = get2gHighestOverlapChannel(unsafeUpperKhz); 422 if (lowest2gChannel != INVALID_CHANNEL && highest2gChannel != INVALID_CHANNEL 423 && lowest2gChannel <= highest2gChannel) { 424 // Find lowest channel at max overlap, or invalid channel 15 if the threshold is not met 425 while (lowest2gChannel <= 14 && getOverlapPercent(unsafeLowerKhz, unsafeUpperKhz, 426 getLowerFreqKhz(lowest2gChannel, WIFI_BAND_24_GHZ), 427 getUpperFreqKhz(lowest2gChannel, WIFI_BAND_24_GHZ)) 428 < overlapPercentThreshold) { 429 lowest2gChannel++; 430 } 431 // Find highest channel at max overlap, or invalid channel 0 if the threshold is not met 432 while (highest2gChannel >= 1 && getOverlapPercent(unsafeLowerKhz, unsafeUpperKhz, 433 getLowerFreqKhz(highest2gChannel, WIFI_BAND_24_GHZ), 434 getUpperFreqKhz(highest2gChannel, WIFI_BAND_24_GHZ)) 435 < overlapPercentThreshold) { 436 highest2gChannel--; 437 } 438 // Mark every channel in between as unsafe 439 for (int channel = lowest2gChannel; channel <= highest2gChannel; channel++) { 440 coexUnsafeChannels.add( 441 new CoexUnsafeChannel(WIFI_BAND_24_GHZ, channel, powerCapDbm)); 442 } 443 } 444 return coexUnsafeChannels; 445 } 446 447 448 /** 449 * Returns the 5GHz CoexUnsafeChannels affected by the harmonic interference from a given uplink 450 * cell channel. 451 */ get5gHarmonicCoexUnsafeChannels( int ulFreqKhz, int ulBandwidthKhz, int harmonicDegree, int overlapPercentThreshold, int powerCapDbm)452 public static List<CoexUnsafeChannel> get5gHarmonicCoexUnsafeChannels( 453 int ulFreqKhz, int ulBandwidthKhz, int harmonicDegree, int overlapPercentThreshold, 454 int powerCapDbm) { 455 List<CoexUnsafeChannel> coexUnsafeChannels = new ArrayList<>(); 456 final int unsafeLowerKhz = (ulFreqKhz - (ulBandwidthKhz / 2)) * harmonicDegree; 457 final int unsafeUpperKhz = (ulFreqKhz + (ulBandwidthKhz / 2)) * harmonicDegree; 458 459 final int lowest5gChannel = get5gLowestOverlap20MhzChannel(unsafeLowerKhz); 460 final int highest5gChannel = get5gHighestOverlap20MhzChannel(unsafeUpperKhz); 461 if (lowest5gChannel != INVALID_CHANNEL && highest5gChannel != INVALID_CHANNEL 462 && lowest5gChannel <= highest5gChannel) { 463 Map<Integer, Integer> overlapPercents = new HashMap<>(); 464 // Find lowest 20MHz overlap channel 465 overlapPercents.put(lowest5gChannel, getOverlapPercent(unsafeLowerKhz, unsafeUpperKhz, 466 getLowerFreqKhz(lowest5gChannel, WIFI_BAND_5_GHZ), 467 getUpperFreqKhz(lowest5gChannel, WIFI_BAND_5_GHZ))); 468 // Find highest 2MHz overlap channel 469 overlapPercents.put(highest5gChannel, getOverlapPercent(unsafeLowerKhz, unsafeUpperKhz, 470 getLowerFreqKhz(highest5gChannel, WIFI_BAND_5_GHZ), 471 getUpperFreqKhz(highest5gChannel, WIFI_BAND_5_GHZ))); 472 // Every channel in between should be at 100 percent overlap 473 for (int channel : CHANNEL_SET_5_GHZ_20_MHZ.subSet( 474 lowest5gChannel, false, highest5gChannel, false)) { 475 overlapPercents.put(channel, 100); 476 } 477 // Iterate through each group of 20Mhz, 40Mhz, 80Mhz, 160Mhz channels, and add to 478 // unsafe channel set if the pre-calculated overlap percent meets the threshold. 479 while (!overlapPercents.isEmpty()) { 480 Map<Integer, Integer> dependentOverlaps = new HashMap<>(); 481 for (int channel : overlapPercents.keySet()) { 482 int overlapPercent = overlapPercents.get(channel); 483 // Add channel to unsafe channel set if overlap percent meets threshold. 484 if (overlapPercent >= overlapPercentThreshold) { 485 coexUnsafeChannels.add( 486 new CoexUnsafeChannel(WIFI_BAND_5_GHZ, channel, powerCapDbm)); 487 } 488 // Pre-calculate the dependent channel overlap for the next iteration by adding 489 // half of each dependency channel percent. 490 final int dependentChannel = DEPENDENT_MAP_5_GHZ.get(channel, 0); 491 if (dependentChannel != 0) { 492 dependentOverlaps.put(dependentChannel, overlapPercent / 2 493 + dependentOverlaps.getOrDefault(dependentChannel, 0)); 494 } 495 // 36 should also map to 34, but only maps to 38 in the dependent map. 496 if (channel == 36) { 497 dependentOverlaps.put(34, overlapPercent / 2 498 + dependentOverlaps.getOrDefault(34, 0)); 499 } 500 } 501 // Set the next dependent group to iterate over until there are no more dependents. 502 overlapPercents = dependentOverlaps; 503 } 504 } 505 return coexUnsafeChannels; 506 } 507 508 /** 509 * Returns CoexUnsafeChannels of a given band affected by the intermod interference from a given 510 * uplink and downlink cell channel. 511 */ getIntermodCoexUnsafeChannels( int ulFreqKhz, int ulBandwidthKhz, int dlFreqKhz, int dlBandwidthKhz, int n, int m, int overlapPercentThreshold, @WifiAnnotations.WifiBandBasic int band, int powerCapDbm)512 public static List<CoexUnsafeChannel> getIntermodCoexUnsafeChannels( 513 int ulFreqKhz, int ulBandwidthKhz, int dlFreqKhz, int dlBandwidthKhz, 514 int n, int m, int overlapPercentThreshold, @WifiAnnotations.WifiBandBasic int band, 515 int powerCapDbm) { 516 List<CoexUnsafeChannel> coexUnsafeChannels = new ArrayList<>(); 517 final int ulLowerKhz = (ulFreqKhz - (ulBandwidthKhz / 2)); 518 final int ulUpperKhz = (ulFreqKhz + (ulBandwidthKhz / 2)); 519 final int dlLowerKhz = (dlFreqKhz - (dlBandwidthKhz / 2)); 520 final int dlUpperKhz = (dlFreqKhz + (dlBandwidthKhz / 2)); 521 522 Set<Integer> channelSet = new HashSet<>(); 523 if (band == WIFI_BAND_24_GHZ) { 524 for (int channel = 1; channel <= 14; channel++) { 525 channelSet.add(channel); 526 } 527 } else if (band == WIFI_BAND_5_GHZ) { 528 channelSet.addAll(CHANNEL_SET_5_GHZ); 529 } 530 531 for (int channel : channelSet) { 532 final int wifiLowerKhz = getLowerFreqKhz(channel, band); 533 final int wifiUpperKhz = getUpperFreqKhz(channel, band); 534 final int intermodLowerKhz = Math.min(n * ulLowerKhz, n * ulUpperKhz) 535 + Math.min(m * wifiLowerKhz, m * wifiUpperKhz); 536 final int intermodUpperKhz = Math.max(n * ulLowerKhz, n * ulUpperKhz) 537 + Math.max(m * wifiLowerKhz, m * wifiUpperKhz); 538 if (getOverlapPercent(intermodLowerKhz, intermodUpperKhz, dlLowerKhz, dlUpperKhz) 539 >= overlapPercentThreshold) { 540 coexUnsafeChannels.add(new CoexUnsafeChannel(band, channel, powerCapDbm)); 541 } 542 } 543 544 return coexUnsafeChannels; 545 } 546 547 /** 548 * Returns CoexUnsafeChannels affecting GPS L1 due to the intermod interference from a given 549 * uplink and downlink cell channel and every possible Wi-Fi channel. 550 */ getCoexUnsafeChannelsForGpsL1( int cellUlFreqKhz, int cellUlBandwidthKhz, int thresholdKhz)551 public static List<CoexUnsafeChannel> getCoexUnsafeChannelsForGpsL1( 552 int cellUlFreqKhz, int cellUlBandwidthKhz, int thresholdKhz) { 553 List<CoexUnsafeChannel> coexUnsafeChannels = new ArrayList<>(); 554 for (int channel = 1; channel <= 14; channel++) { 555 int centerFreq2gMhz = 556 ScanResult.convertChannelToFrequencyMhzIfSupported(channel, WIFI_BAND_24_GHZ); 557 if (centerFreq2gMhz == UNSPECIFIED) { 558 continue; 559 } 560 if (isGpsL1ImpactedByCellAndWifi(cellUlFreqKhz, cellUlBandwidthKhz, 561 centerFreq2gMhz * 1000, 22_000, thresholdKhz)) { 562 coexUnsafeChannels.add(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, channel)); 563 } 564 } 565 for (int channel : CHANNEL_SET_5_GHZ_20_MHZ) { 566 int centerFreq5g20Mhz = 567 ScanResult.convertChannelToFrequencyMhzIfSupported(channel, WIFI_BAND_5_GHZ); 568 if (centerFreq5g20Mhz == UNSPECIFIED) { 569 continue; 570 } 571 if (isGpsL1ImpactedByCellAndWifi(cellUlFreqKhz, cellUlBandwidthKhz, 572 centerFreq5g20Mhz * 1000, 20_000, thresholdKhz)) { 573 coexUnsafeChannels.add(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, channel)); 574 } 575 } 576 for (int channel : CHANNEL_SET_5_GHZ_40_MHZ) { 577 int centerFreq5g40Mhz = 578 ScanResult.convertChannelToFrequencyMhzIfSupported(channel, WIFI_BAND_5_GHZ); 579 if (centerFreq5g40Mhz == UNSPECIFIED) { 580 continue; 581 } 582 if (isGpsL1ImpactedByCellAndWifi(cellUlFreqKhz, cellUlBandwidthKhz, 583 centerFreq5g40Mhz * 1000, 40_000, thresholdKhz)) { 584 coexUnsafeChannels.add(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, channel)); 585 } 586 } 587 for (int channel : CHANNEL_SET_5_GHZ_80_MHZ) { 588 int centerFreq5g80Mhz = 589 ScanResult.convertChannelToFrequencyMhzIfSupported(channel, WIFI_BAND_5_GHZ); 590 if (centerFreq5g80Mhz == UNSPECIFIED) { 591 continue; 592 } 593 if (isGpsL1ImpactedByCellAndWifi(cellUlFreqKhz, cellUlBandwidthKhz, 594 centerFreq5g80Mhz * 1000, 80_000, thresholdKhz)) { 595 coexUnsafeChannels.add(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, channel)); 596 } 597 } 598 for (int channel : CHANNEL_SET_5_GHZ_160_MHZ) { 599 int centerFreq5g160Mhz = 600 ScanResult.convertChannelToFrequencyMhzIfSupported(channel, WIFI_BAND_5_GHZ); 601 if (centerFreq5g160Mhz == UNSPECIFIED) { 602 continue; 603 } 604 if (isGpsL1ImpactedByCellAndWifi(cellUlFreqKhz, cellUlBandwidthKhz, 605 centerFreq5g160Mhz * 1000, 160_000, thresholdKhz)) { 606 coexUnsafeChannels.add(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, channel)); 607 } 608 } 609 return coexUnsafeChannels; 610 } 611 612 /** 613 * Returns whether or not GPS L1 is impacted by the given cell and wifi channels. 614 */ isGpsL1ImpactedByCellAndWifi( int cellCenterKhz, int cellBandwidthKhz, int wifiCenterKhz, int wifiBandwidthKhz, int thresholdKhz)615 public static boolean isGpsL1ImpactedByCellAndWifi( 616 int cellCenterKhz, int cellBandwidthKhz, 617 int wifiCenterKhz, int wifiBandwidthKhz, 618 int thresholdKhz) { 619 int cellLowerKhz = cellCenterKhz - cellBandwidthKhz / 2; 620 int cellUpperKhz = cellCenterKhz + cellBandwidthKhz / 2; 621 int wifiLowerKhz = wifiCenterKhz - wifiBandwidthKhz / 2; 622 int wifiUpperKhz = wifiCenterKhz + wifiBandwidthKhz / 2; 623 int intermodLowerKhz; 624 int intermodUpperKhz; 625 if (wifiCenterKhz > cellCenterKhz) { 626 intermodLowerKhz = wifiLowerKhz - cellUpperKhz; 627 intermodUpperKhz = wifiUpperKhz - cellLowerKhz; 628 } else { 629 intermodLowerKhz = cellLowerKhz - wifiUpperKhz; 630 intermodUpperKhz = cellUpperKhz - wifiLowerKhz; 631 } 632 int gpsLowerKhz = GPS_L1_CENTER_FREQ_KHZ - thresholdKhz; 633 int gpsUpperKhz = GPS_L1_CENTER_FREQ_KHZ + thresholdKhz; 634 return !(intermodLowerKhz > gpsUpperKhz || intermodUpperKhz < gpsLowerKhz); 635 } 636 637 /** 638 * Data structure class mirroring cell channel information from PhysicalChannelConfig used for 639 * coex calculations. 640 */ 641 public static class CoexCellChannel { 642 private final @Annotation.NetworkType int mRat; 643 private final int mBand; 644 private final int mDownlinkFreqKhz; 645 private final int mDownlinkBandwidthKhz; 646 private final int mUplinkFreqKhz; 647 private final int mUplinkBandwidthKhz; 648 private final int mSubId; 649 CoexCellChannel(@nnotation.NetworkType int rat, int band, int downlinkFreqKhz, int downlinkBandwidthKhz, int uplinkFreqKhz, int uplinkBandwidthKhz, int subId)650 public CoexCellChannel(@Annotation.NetworkType int rat, int band, 651 int downlinkFreqKhz, int downlinkBandwidthKhz, 652 int uplinkFreqKhz, int uplinkBandwidthKhz, int subId) { 653 if (band < 1 || band > 261) { 654 Log.wtf(TAG, "Band is " + band + " but should be a value from 1 to 261"); 655 } 656 if (downlinkFreqKhz < 0 && downlinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) { 657 Log.wtf(TAG, "Downlink frequency is " + downlinkFreqKhz + " but should be >= 0" 658 + " or PhysicalChannelConfig.FREQUENCY_UNKNOWN: " 659 + PhysicalChannelConfig.FREQUENCY_UNKNOWN); 660 } 661 if (downlinkBandwidthKhz <= 0 662 && downlinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) { 663 Log.wtf(TAG, "Downlink bandwidth is " + downlinkBandwidthKhz + " but should be > 0" 664 + " or PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN: " 665 + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN); 666 } 667 if (uplinkFreqKhz < 0 && uplinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) { 668 Log.wtf(TAG, "Uplink frequency is " + uplinkFreqKhz + " but should be >= 0" 669 + " or PhysicalChannelConfig.FREQUENCY_UNKNOWN: " 670 + PhysicalChannelConfig.FREQUENCY_UNKNOWN); 671 } 672 if (uplinkBandwidthKhz <= 0 673 && uplinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) { 674 Log.wtf(TAG, "Uplink bandwidth is " + uplinkBandwidthKhz + " but should be > 0" 675 + " or PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN: " 676 + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN); 677 } 678 mRat = rat; 679 mBand = band; 680 mDownlinkFreqKhz = downlinkFreqKhz; 681 mDownlinkBandwidthKhz = downlinkBandwidthKhz; 682 mUplinkFreqKhz = uplinkFreqKhz; 683 mUplinkBandwidthKhz = uplinkBandwidthKhz; 684 mSubId = subId; 685 } 686 687 /** 688 * Constructor to extract coex cell channel info from PhysicalChannelConfig 689 */ CoexCellChannel( @ndroid.annotation.NonNull PhysicalChannelConfig config, int subId)690 public CoexCellChannel( 691 @android.annotation.NonNull PhysicalChannelConfig config, int subId) { 692 this(config.getNetworkType(), 693 config.getBand(), 694 config.getDownlinkFrequencyKhz(), 695 config.getCellBandwidthDownlinkKhz(), 696 config.getUplinkFrequencyKhz(), 697 config.getCellBandwidthUplinkKhz(), 698 subId); 699 } 700 getRat()701 public @Annotation.NetworkType int getRat() { 702 return mRat; 703 } 704 getBand()705 public int getBand() { 706 return mBand; 707 } 708 getDownlinkFreqKhz()709 public int getDownlinkFreqKhz() { 710 return mDownlinkFreqKhz; 711 } 712 getDownlinkBandwidthKhz()713 public int getDownlinkBandwidthKhz() { 714 return mDownlinkBandwidthKhz; 715 } 716 getUplinkFreqKhz()717 public int getUplinkFreqKhz() { 718 return mUplinkFreqKhz; 719 } 720 getUplinkBandwidthKhz()721 public int getUplinkBandwidthKhz() { 722 return mUplinkBandwidthKhz; 723 } 724 getSubId()725 public int getSubId() { 726 return mSubId; 727 } 728 729 @java.lang.Override toString()730 public String toString() { 731 return "CoexCellChannel{" 732 + "rat=" + mRat 733 + ", band=" + mBand 734 + ", dlFreqKhz=" + mDownlinkFreqKhz 735 + ", dlBandwidthKhz=" + mDownlinkBandwidthKhz 736 + ", ulFreqKhz=" + mUplinkFreqKhz 737 + ", ulBandwidthKhz=" + mUplinkBandwidthKhz 738 + ", subId=" + mSubId 739 + '}'; 740 } 741 742 @java.lang.Override equals(Object o)743 public boolean equals(Object o) { 744 if (this == o) return true; 745 if (!(o instanceof CoexCellChannel)) return false; 746 CoexCellChannel that = (CoexCellChannel) o; 747 return getRat() == that.getRat() && getBand() == that.getBand() 748 && getDownlinkFreqKhz() == that.getDownlinkFreqKhz() 749 && getDownlinkBandwidthKhz() == that.getDownlinkBandwidthKhz() 750 && getUplinkFreqKhz() == that.getUplinkFreqKhz() 751 && getUplinkBandwidthKhz() == that.getUplinkBandwidthKhz() 752 && getSubId() == that.getSubId(); 753 } 754 755 @java.lang.Override hashCode()756 public int hashCode() { 757 return Objects.hash(getRat(), getBand(), getDownlinkFreqKhz(), 758 getDownlinkBandwidthKhz(), 759 getUplinkFreqKhz(), getUplinkBandwidthKhz(), getSubId()); 760 } 761 } 762 } 763