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