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