• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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