• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.util;
18 
19 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD;
20 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_BAND_24G_SUPPORTED;
21 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_BAND_5G_SUPPORTED;
22 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_BAND_60G_SUPPORTED;
23 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_BAND_6G_SUPPORTED;
24 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT;
25 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_IEEE80211_AX;
26 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_IEEE80211_BE;
27 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION;
28 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_WPA3_OWE;
29 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_WPA3_OWE_TRANSITION;
30 import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_WPA3_SAE;
31 import static android.net.wifi.SoftApConfiguration.BAND_2GHZ;
32 import static android.net.wifi.SoftApConfiguration.BAND_5GHZ;
33 
34 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE;
35 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_STA;
36 
37 import android.annotation.NonNull;
38 import android.annotation.Nullable;
39 import android.content.Context;
40 import android.content.res.Resources;
41 import android.net.wifi.CoexUnsafeChannel;
42 import android.net.wifi.ScanResult;
43 import android.net.wifi.SoftApCapability;
44 import android.net.wifi.SoftApConfiguration;
45 import android.net.wifi.SoftApConfiguration.BandType;
46 import android.net.wifi.SoftApInfo;
47 import android.net.wifi.WifiAvailableChannel;
48 import android.net.wifi.WifiClient;
49 import android.net.wifi.WifiConfiguration;
50 import android.net.wifi.WifiContext;
51 import android.net.wifi.WifiManager;
52 import android.net.wifi.WifiScanner;
53 import android.net.wifi.nl80211.DeviceWiphyCapabilities;
54 import android.net.wifi.util.WifiResourceCache;
55 import android.text.TextUtils;
56 import android.util.Log;
57 import android.util.SparseArray;
58 import android.util.SparseIntArray;
59 
60 import androidx.annotation.Keep;
61 
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.modules.utils.build.SdkLevel;
64 import com.android.server.wifi.SoftApManager;
65 import com.android.server.wifi.WifiInjector;
66 import com.android.server.wifi.WifiNative;
67 import com.android.server.wifi.WifiSettingsConfigStore;
68 import com.android.server.wifi.coex.CoexManager;
69 import com.android.wifi.flags.Flags;
70 import com.android.wifi.resources.R;
71 
72 import java.util.ArrayList;
73 import java.util.Arrays;
74 import java.util.Collections;
75 import java.util.HashMap;
76 import java.util.HashSet;
77 import java.util.List;
78 import java.util.Map;
79 import java.util.Objects;
80 import java.util.Random;
81 import java.util.Set;
82 import java.util.StringJoiner;
83 import java.util.stream.Collectors;
84 import java.util.stream.IntStream;
85 
86 /**
87  * Provide utility functions for updating soft AP related configuration.
88  */
89 public class ApConfigUtil {
90     private static final String TAG = "ApConfigUtil";
91 
92     public static final int INVALID_VALUE_FOR_BAND_OR_CHANNEL = -1;
93     public static final int DEFAULT_AP_BAND = SoftApConfiguration.BAND_2GHZ;
94     public static final int DEFAULT_AP_CHANNEL = 6;
95     public static final int HIGHEST_2G_AP_CHANNEL = 14;
96 
97     /* Random number generator used for AP channel selection. */
98     private static final Random sRandom = new Random();
99     private static boolean sVerboseLoggingEnabled = false;
100 
101     /**
102      * Enable or disable verbose logging
103      * @param verboseEnabled true if verbose logging is enabled
104      */
enableVerboseLogging(boolean verboseEnabled)105     public static void enableVerboseLogging(boolean verboseEnabled) {
106         sVerboseLoggingEnabled = verboseEnabled;
107     }
108 
109     /**
110      * Valid Global Operating classes in each wifi band
111      * Reference: Table E-4 in IEEE Std 802.11-2016.
112      */
113     private static final SparseArray<int[]> sBandToOperatingClass = new SparseArray<>();
114     static {
sBandToOperatingClass.append(SoftApConfiguration.BAND_2GHZ, new int[]{81, 82, 83, 84})115         sBandToOperatingClass.append(SoftApConfiguration.BAND_2GHZ, new int[]{81, 82, 83, 84});
sBandToOperatingClass.append(SoftApConfiguration.BAND_5GHZ, new int[]{115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130})116         sBandToOperatingClass.append(SoftApConfiguration.BAND_5GHZ, new int[]{115, 116, 117, 118,
117                 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130});
sBandToOperatingClass.append(SoftApConfiguration.BAND_6GHZ, new int[]{131, 132, 133, 134, 135, 136})118         sBandToOperatingClass.append(SoftApConfiguration.BAND_6GHZ, new int[]{131, 132, 133, 134,
119                 135, 136});
120     }
121 
122     /**
123      * Converts a SoftApConfiguration.BAND_* constant to a meaningful String
124      */
bandToString(int band)125     public static String bandToString(int band) {
126         StringJoiner sj = new StringJoiner(" & ");
127         sj.setEmptyValue("unspecified");
128         if ((band & SoftApConfiguration.BAND_2GHZ) != 0) {
129             sj.add("2Ghz");
130         }
131         band &= ~SoftApConfiguration.BAND_2GHZ;
132 
133         if ((band & SoftApConfiguration.BAND_5GHZ) != 0) {
134             sj.add("5Ghz");
135         }
136         band &= ~SoftApConfiguration.BAND_5GHZ;
137 
138         if ((band & SoftApConfiguration.BAND_6GHZ) != 0) {
139             sj.add("6Ghz");
140         }
141         band &= ~SoftApConfiguration.BAND_6GHZ;
142 
143         if ((band & SoftApConfiguration.BAND_60GHZ) != 0) {
144             sj.add("60Ghz");
145         }
146         band &= ~SoftApConfiguration.BAND_60GHZ;
147         if (band != 0) {
148             return "Invalid band";
149         }
150         return sj.toString();
151     }
152 
153     /**
154      * Helper function to get the band corresponding to the operating class.
155      *
156      * @param operatingClass Global operating class.
157      * @return band, -1 if no match.
158      *
159      */
getBandFromOperatingClass(int operatingClass)160     public static int getBandFromOperatingClass(int operatingClass) {
161         for (int i = 0; i < sBandToOperatingClass.size(); i++) {
162             int band = sBandToOperatingClass.keyAt(i);
163             int[] operatingClasses = sBandToOperatingClass.get(band);
164 
165             for (int j = 0; j < operatingClasses.length; j++) {
166                 if (operatingClasses[j] == operatingClass) {
167                     return band;
168                 }
169             }
170         }
171         return -1;
172     }
173 
174     /**
175      * Convert band from SoftApConfiguration.BandType to WifiScanner.WifiBand
176      * @param band in SoftApConfiguration.BandType
177      * @return band in WifiScanner.WifiBand
178      */
apConfig2wifiScannerBand(@andType int band)179     public static @WifiScanner.WifiBand int apConfig2wifiScannerBand(@BandType int band) {
180         switch(band) {
181             case SoftApConfiguration.BAND_2GHZ:
182                 return WifiScanner.WIFI_BAND_24_GHZ;
183             case SoftApConfiguration.BAND_5GHZ:
184                 return WifiScanner.WIFI_BAND_5_GHZ;
185             case SoftApConfiguration.BAND_6GHZ:
186                 return WifiScanner.WIFI_BAND_6_GHZ;
187             case SoftApConfiguration.BAND_60GHZ:
188                 return WifiScanner.WIFI_BAND_60_GHZ;
189             default:
190                 return WifiScanner.WIFI_BAND_UNSPECIFIED;
191         }
192     }
193 
194     /**
195      * Convert channel/band to frequency.
196      * Note: the utility does not perform any regulatory domain compliance.
197      * @param channel number to convert
198      * @param band of channel to convert
199      * @return center frequency in Mhz of the channel, -1 if no match
200      */
convertChannelToFrequency(int channel, @BandType int band)201     public static int convertChannelToFrequency(int channel, @BandType int band) {
202         return ScanResult.convertChannelToFrequencyMhzIfSupported(channel,
203                 apConfig2wifiScannerBand(band));
204     }
205 
206     /**
207      * Convert frequency to band.
208      * Note: the utility does not perform any regulatory domain compliance.
209      * @param frequency frequency to convert
210      * @return band, -1 if no match
211      */
convertFrequencyToBand(int frequency)212     public static int convertFrequencyToBand(int frequency) {
213         if (ScanResult.is24GHz(frequency)) {
214             return SoftApConfiguration.BAND_2GHZ;
215         } else if (ScanResult.is5GHz(frequency)) {
216             return SoftApConfiguration.BAND_5GHZ;
217         } else if (ScanResult.is6GHz(frequency)) {
218             return SoftApConfiguration.BAND_6GHZ;
219         } else if (ScanResult.is60GHz(frequency)) {
220             return SoftApConfiguration.BAND_60GHZ;
221         }
222 
223         return -1;
224     }
225 
226     /**
227      * Convert band from WifiConfiguration into SoftApConfiguration
228      *
229      * @param wifiConfigBand band encoded as WifiConfiguration.AP_BAND_xxxx
230      * @return band as encoded as SoftApConfiguration.BAND_xxx
231      */
convertWifiConfigBandToSoftApConfigBand(int wifiConfigBand)232     public static int convertWifiConfigBandToSoftApConfigBand(int wifiConfigBand) {
233         switch (wifiConfigBand) {
234             case WifiConfiguration.AP_BAND_2GHZ:
235                 return SoftApConfiguration.BAND_2GHZ;
236             case WifiConfiguration.AP_BAND_5GHZ:
237                 return SoftApConfiguration.BAND_5GHZ;
238             case WifiConfiguration.AP_BAND_ANY:
239                 return SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ;
240             default:
241                 return SoftApConfiguration.BAND_2GHZ;
242         }
243     }
244 
245     /**
246      * Add 2.4Ghz to target band when 2.4Ghz SoftAp supported.
247      *
248      * @param targetBand The band is needed to add 2.4G.
249      * @return The band includes 2.4Ghz when 2.4G SoftAp supported.
250      */
append24GToBandIf24GSupported(@andType int targetBand, WifiContext context)251     public static @BandType int append24GToBandIf24GSupported(@BandType int targetBand,
252             WifiContext context) {
253         if (isBandSupported(SoftApConfiguration.BAND_2GHZ, context)) {
254             return targetBand | SoftApConfiguration.BAND_2GHZ;
255         }
256         return targetBand;
257     }
258 
259     /**
260      * Add 5Ghz to target band when 5Ghz SoftAp supported.
261      *
262      * @param targetBand The band is needed to add 5GHz band.
263      * @return The band includes 5Ghz when 5G SoftAp supported.
264      */
append5GToBandIf5GSupported(@andType int targetBand, WifiContext context)265     public static @BandType int append5GToBandIf5GSupported(@BandType int targetBand,
266             WifiContext context) {
267         if (isBandSupported(SoftApConfiguration.BAND_5GHZ, context)) {
268             return targetBand | SoftApConfiguration.BAND_5GHZ;
269         }
270         return targetBand;
271     }
272 
273     /**
274      * Checks if band is a valid combination of {link  SoftApConfiguration#BandType} values
275      */
isBandValid(@andType int band)276     public static boolean isBandValid(@BandType int band) {
277         int bandAny = SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ
278                 | SoftApConfiguration.BAND_6GHZ | SoftApConfiguration.BAND_60GHZ;
279         return ((band != 0) && ((band & ~bandAny) == 0));
280     }
281 
282     /**
283      * Check if the band contains a certain sub-band
284      *
285      * @param band The combination of bands to validate
286      * @param testBand the test band to validate on
287      * @return true if band contains testBand, false otherwise
288      */
containsBand(@andType int band, @BandType int testBand)289     public static boolean containsBand(@BandType int band, @BandType int testBand) {
290         return ((band & testBand) != 0);
291     }
292 
293     /**
294      * Checks if band contains multiple sub-bands
295      * @param band a combination of sub-bands
296      * @return true if band has multiple sub-bands, false otherwise
297      */
isMultiband(@andType int band)298     public static boolean isMultiband(@BandType int band) {
299         return ((band & (band - 1)) != 0);
300     }
301 
302 
303     /**
304      * Checks whether or not band configuration is supported.
305      * @param apBand a combination of the bands
306      * @param context the caller context used to get value from resource file.
307      * @return true if band is supported, false otherwise
308      */
isBandSupported(@andType int apBand, WifiContext context)309     public static boolean isBandSupported(@BandType int apBand, WifiContext context) {
310         if (!isBandValid(apBand)) {
311             Log.e(TAG, "Invalid SoftAp band " + apBand);
312             return false;
313         }
314 
315         for (int b : SoftApConfiguration.BAND_TYPES) {
316             if (containsBand(apBand, b) && !isSoftApBandSupported(context, b)) {
317                 Log.e(TAG, "Can not start softAp with band " + bandToString(b)
318                         + " not supported.");
319                 return false;
320             }
321         }
322 
323         return true;
324     }
325 
326     /**
327      * Convert string to channel list
328      * Format of the list is a comma separated channel numbers, or range of channel numbers
329      * Example, "34-48, 149".
330      * @param channelString for a comma separated channel numbers, or range of channel numbers
331      *        such as "34-48, 149"
332      * @return list of channel numbers
333      */
convertStringToChannelList(String channelString)334     public static List<Integer> convertStringToChannelList(String channelString) {
335         if (channelString == null) {
336             return null;
337         }
338 
339         List<Integer> channelList = new ArrayList<Integer>();
340 
341         for (String channelRange : channelString.split(",")) {
342             try {
343                 if (channelRange.contains("-")) {
344                     String[] channels = channelRange.split("-");
345                     if (channels.length != 2) {
346                         Log.e(TAG, "Unrecognized channel range, Length is " + channels.length);
347                         continue;
348                     }
349                     int start = Integer.parseInt(channels[0].trim());
350                     int end = Integer.parseInt(channels[1].trim());
351                     if (start > end) {
352                         Log.e(TAG, "Invalid channel range, from " + start + " to " + end);
353                         continue;
354                     }
355 
356                     for (int channel = start; channel <= end; channel++) {
357                         channelList.add(channel);
358                     }
359                 } else {
360                     channelList.add(Integer.parseInt(channelRange.trim()));
361                 }
362             } catch (NumberFormatException e) {
363                 // Ignore malformed string
364                 Log.e(TAG, "Malformed channel value detected: " + e);
365                 continue;
366             }
367         }
368         return channelList;
369     }
370 
371     /**
372      * Returns the unsafe channels frequency from coex module.
373      *
374      * @param coexManager reference used to get unsafe channels to avoid for coex.
375      */
376     @NonNull
getUnsafeChannelFreqsFromCoex(@onNull CoexManager coexManager)377     public static Set<Integer> getUnsafeChannelFreqsFromCoex(@NonNull CoexManager coexManager) {
378         Set<Integer> unsafeFreqs = new HashSet<>();
379         if (SdkLevel.isAtLeastS()) {
380             for (CoexUnsafeChannel unsafeChannel : coexManager.getCoexUnsafeChannels()) {
381                 unsafeFreqs.add(ScanResult.convertChannelToFrequencyMhzIfSupported(
382                         unsafeChannel.getChannel(), unsafeChannel.getBand()));
383             }
384         }
385         return unsafeFreqs;
386     }
387 
getConfiguredChannelList(WifiResourceCache resources, @BandType int band)388     private static List<Integer> getConfiguredChannelList(WifiResourceCache resources,
389             @BandType int band) {
390         switch (band) {
391             case SoftApConfiguration.BAND_2GHZ:
392                 return convertStringToChannelList(resources.getString(
393                         R.string.config_wifiSoftap2gChannelList));
394             case SoftApConfiguration.BAND_5GHZ:
395                 return convertStringToChannelList(resources.getString(
396                         R.string.config_wifiSoftap5gChannelList));
397             case SoftApConfiguration.BAND_6GHZ:
398                 return convertStringToChannelList(resources.getString(
399                         R.string.config_wifiSoftap6gChannelList));
400             case SoftApConfiguration.BAND_60GHZ:
401                 return convertStringToChannelList(resources.getString(
402                         R.string.config_wifiSoftap60gChannelList));
403             default:
404                 return null;
405         }
406     }
407 
addDfsChannelsIfNeeded(List<Integer> regulatoryList, @WifiScanner.WifiBand int scannerBand, WifiNative wifiNative, WifiResourceCache resources, boolean inFrequencyMHz)408     private static List<Integer> addDfsChannelsIfNeeded(List<Integer> regulatoryList,
409             @WifiScanner.WifiBand int scannerBand, WifiNative wifiNative,
410             WifiResourceCache resources, boolean inFrequencyMHz) {
411         // Add DFS channels to the supported channel list if the device supports SoftAp
412         // operation in the DFS channel.
413         if (resources.getBoolean(R.bool.config_wifiSoftapAcsIncludeDfs)
414                 && scannerBand == WifiScanner.WIFI_BAND_5_GHZ) {
415             int[] dfs5gBand = wifiNative.getChannelsForBand(
416                     WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY);
417             for (int freq : dfs5gBand) {
418                 final int freqOrChan = inFrequencyMHz
419                         ? freq : ScanResult.convertFrequencyMhzToChannelIfSupported(freq);
420                 if (!regulatoryList.contains(freqOrChan)) {
421                     regulatoryList.add(freqOrChan);
422                 }
423             }
424         }
425         return regulatoryList;
426     }
427 
getWifiCondAvailableChannelsForBand( @ifiScanner.WifiBand int scannerBand, WifiNative wifiNative, WifiResourceCache resources, boolean inFrequencyMHz)428     private static List<Integer> getWifiCondAvailableChannelsForBand(
429             @WifiScanner.WifiBand int scannerBand, WifiNative wifiNative,
430             WifiResourceCache resources, boolean inFrequencyMHz) {
431         List<Integer> regulatoryList = new ArrayList<Integer>();
432         // Get the allowed list of channel frequencies in MHz from wificond
433         int[] regulatoryArray = wifiNative.getChannelsForBand(scannerBand);
434         for (int freq : regulatoryArray) {
435             regulatoryList.add(inFrequencyMHz
436                     ? freq : ScanResult.convertFrequencyMhzToChannelIfSupported(freq));
437         }
438         return addDfsChannelsIfNeeded(regulatoryList, scannerBand, wifiNative, resources,
439                 inFrequencyMHz);
440     }
441 
getHalAvailableChannelsForBand( @ifiScanner.WifiBand int scannerBand, WifiNative wifiNative, WifiResourceCache resources, boolean inFrequencyMHz)442     private static List<Integer> getHalAvailableChannelsForBand(
443             @WifiScanner.WifiBand int scannerBand, WifiNative wifiNative,
444             WifiResourceCache resources,
445             boolean inFrequencyMHz) {
446         // Try vendor HAL API to get the usable channel list.
447         List<WifiAvailableChannel> usableChannelList = wifiNative.getUsableChannels(
448                 scannerBand,
449                 WifiAvailableChannel.OP_MODE_SAP,
450                 WifiAvailableChannel.FILTER_REGULATORY);
451         if (usableChannelList == null) {
452             // If HAL doesn't support getUsableChannels then return null
453             return null;
454         }
455         List<Integer> regulatoryList = new ArrayList<>();
456         if (inFrequencyMHz) {
457             usableChannelList.forEach(a -> regulatoryList.add(a.getFrequencyMhz()));
458         } else {
459             usableChannelList.forEach(a -> regulatoryList.add(ScanResult
460                     .convertFrequencyMhzToChannelIfSupported(a.getFrequencyMhz())));
461 
462         }
463         return addDfsChannelsIfNeeded(regulatoryList, scannerBand, wifiNative, resources,
464                 inFrequencyMHz);
465     }
466 
467     /**
468      * Get channels or frequencies for band that are allowed by both regulatory
469      * and OEM configuration.
470      *
471      * @param band to get channels for
472      * @param wifiNative reference used to get regulatory restrictions.
473      * @param resources used to get OEM restrictions.
474      * @param inFrequencyMHz true to convert channel to frequency.
475      * @return A list of frequencies that are allowed, null on error.
476      * TODO(b/380087289): Resources will be removed in the future together with the @keep annotation
477      */
478     @Keep
getAvailableChannelFreqsForBand( @andType int band, WifiNative wifiNative, Resources resources, boolean inFrequencyMHz)479     public static List<Integer> getAvailableChannelFreqsForBand(
480             @BandType int band, WifiNative wifiNative, Resources resources,
481             boolean inFrequencyMHz) {
482         if (!isBandValid(band) || isMultiband(band)) {
483             return null;
484         }
485         WifiResourceCache resourceCache = WifiInjector.getInstance().getContext()
486                 .getResourceCache();
487 
488         int scannerBand = apConfig2wifiScannerBand(band);
489         List<Integer> regulatoryList = null;
490         boolean useWifiCond = false;
491         // Check if vendor HAL API for getting usable channels is available. If HAL doesn't support
492         // the API it returns null list, in that case we retrieve the list from wificond.
493         if (!wifiNative.isHalSupported()) {
494             // HAL is not supported, fallback to wificond
495             useWifiCond = true;
496         } else {
497             if (!wifiNative.isHalStarted()) {
498                 // HAL is not started, return null
499                 return null;
500             }
501             regulatoryList = getHalAvailableChannelsForBand(scannerBand, wifiNative, resourceCache,
502                     inFrequencyMHz);
503             if (regulatoryList == null) {
504                 // HAL API not supported by HAL, fallback to wificond
505                 useWifiCond = true;
506             }
507         }
508         if (useWifiCond) {
509             regulatoryList = getWifiCondAvailableChannelsForBand(scannerBand, wifiNative,
510                     resourceCache, inFrequencyMHz);
511         }
512         List<Integer> configuredList = getConfiguredChannelList(resourceCache, band);
513         if (configuredList == null || configuredList.isEmpty() || regulatoryList == null) {
514             return regulatoryList;
515         }
516         List<Integer> filteredList = new ArrayList<Integer>();
517         // Otherwise, filter the configured list
518         for (int channel : configuredList) {
519             if (inFrequencyMHz) {
520                 int channelFreq = convertChannelToFrequency(channel, band);
521                 if (regulatoryList.contains(channelFreq)) {
522                     filteredList.add(channelFreq);
523                 }
524             } else if (regulatoryList.contains(channel)) {
525                 filteredList.add(channel);
526             }
527         }
528         if (sVerboseLoggingEnabled) {
529             Log.d(TAG, "Filtered channel list for band " + bandToString(band) + " : "
530                     + filteredList.stream().map(Object::toString).collect(Collectors.joining(",")));
531         }
532         return filteredList;
533     }
534 
535     /**
536      * Return a channel frequency for AP setup based on the frequency band.
537      * @param apBand one or combination of the values of SoftApConfiguration.BAND_*.
538      * @param coexManager reference used to get unsafe channels to avoid for coex.
539      * @param resources the resources to use to get configured allowed channels.
540      * @param capability soft AP capability
541      * @return a valid channel frequency on success, -1 on failure.
542      */
chooseApChannel(int apBand, @NonNull CoexManager coexManager, @NonNull WifiResourceCache resources, SoftApCapability capability)543     public static int chooseApChannel(int apBand, @NonNull CoexManager coexManager,
544             @NonNull WifiResourceCache resources, SoftApCapability capability) {
545         if (!isBandValid(apBand)) {
546             Log.e(TAG, "Invalid band: " + apBand);
547             return -1;
548         }
549 
550         Set<Integer> unsafeFreqs = new HashSet<>();
551         if (SdkLevel.isAtLeastS()) {
552             unsafeFreqs = getUnsafeChannelFreqsFromCoex(coexManager);
553         }
554         final int[] bandPreferences = new int[]{
555                 SoftApConfiguration.BAND_60GHZ,
556                 SoftApConfiguration.BAND_6GHZ,
557                 SoftApConfiguration.BAND_5GHZ,
558                 SoftApConfiguration.BAND_2GHZ};
559         int selectedUnsafeFreq = 0;
560         for (int band : bandPreferences) {
561             if ((apBand & band) == 0) {
562                 continue;
563             }
564             int[] availableChannels = capability.getSupportedChannelList(band);
565             if (availableChannels == null || availableChannels.length == 0) {
566                 continue;
567             }
568             final List<Integer> availableFreqs =
569                     Arrays.stream(availableChannels).boxed()
570                             .map(ch -> convertChannelToFrequency(ch, band))
571                             .collect(Collectors.toList());
572             // Separate the available freqs by safe and unsafe.
573             List<Integer> availableSafeFreqs = new ArrayList<>();
574             List<Integer> availableUnsafeFreqs = new ArrayList<>();
575             for (int freq : availableFreqs) {
576                 if (unsafeFreqs.contains(freq)) {
577                     availableUnsafeFreqs.add(freq);
578                 } else {
579                     availableSafeFreqs.add(freq);
580                 }
581             }
582             // If there are safe freqs available for this band, randomly select one.
583             if (!availableSafeFreqs.isEmpty()) {
584                 return availableSafeFreqs.get(sRandom.nextInt(availableSafeFreqs.size()));
585             } else if (!availableUnsafeFreqs.isEmpty() && selectedUnsafeFreq == 0) {
586                 // Save an unsafe freq from the first preferred band to fall back on later.
587                 selectedUnsafeFreq = availableUnsafeFreqs.get(
588                         sRandom.nextInt(availableUnsafeFreqs.size()));
589             }
590         }
591         // If all available channels are soft unsafe, select a random one of the highest band.
592         boolean isHardUnsafe = false;
593         if (SdkLevel.isAtLeastS()) {
594             isHardUnsafe =
595                     (coexManager.getCoexRestrictions() & WifiManager.COEX_RESTRICTION_SOFTAP) != 0;
596         }
597         if (!isHardUnsafe && selectedUnsafeFreq != 0) {
598             return selectedUnsafeFreq;
599         }
600 
601         // If all available channels are hard unsafe, select the default AP channel.
602         if (containsBand(apBand, DEFAULT_AP_BAND)) {
603             final int defaultChannelFreq = convertChannelToFrequency(DEFAULT_AP_CHANNEL,
604                     DEFAULT_AP_BAND);
605             Log.e(TAG, "Allowed channel list not specified, selecting default channel");
606             if (isHardUnsafe && unsafeFreqs.contains(defaultChannelFreq)) {
607                 Log.e(TAG, "Default channel is hard restricted due to coex");
608             }
609             return defaultChannelFreq;
610         }
611         Log.e(TAG, "No available channels");
612         return -1;
613     }
614 
615     /**
616      * Remove unavailable bands from the input band and return the resulting
617      * (remaining) available bands. Unavailable bands are those which don't have channels available.
618      *
619      * @param capability SoftApCapability which indicates supported channel list.
620      * @param targetBand The target band which plan to enable
621      * @param coexManager reference to CoexManager
622      *
623      * @return the available band which removed the unsupported band.
624      *         0 when all of the band is not supported.
625      */
removeUnavailableBands(SoftApCapability capability, @NonNull int targetBand, CoexManager coexManager)626     public static @BandType int removeUnavailableBands(SoftApCapability capability,
627             @NonNull int targetBand, CoexManager coexManager) {
628         int availableBand = targetBand;
629         for (int band : SoftApConfiguration.BAND_TYPES) {
630             Set<Integer> availableChannelFreqsList = new HashSet<>();
631             if ((targetBand & band) != 0) {
632                 for (int channel : capability.getSupportedChannelList(band)) {
633                     availableChannelFreqsList.add(convertChannelToFrequency(channel, band));
634                 }
635                 // Only remove hard unsafe channels
636                 if (SdkLevel.isAtLeastS()
637                         && (coexManager.getCoexRestrictions() & WifiManager.COEX_RESTRICTION_SOFTAP)
638                         != 0) {
639                     availableChannelFreqsList.removeAll(getUnsafeChannelFreqsFromCoex(coexManager));
640                 }
641                 if (availableChannelFreqsList.size() == 0) {
642                     availableBand &= ~band;
643                 }
644             }
645         }
646         return availableBand;
647     }
648 
649     /**
650      * Remove unavailable bands from the softAp configuration and return the updated configuration.
651      * Unavailable bands are those which don't have channels available.
652      *
653      * @param config The current {@link SoftApConfiguration}.
654      * @param capability SoftApCapability which indicates supported channel list.
655      * @param coexManager reference to CoexManager
656      * @param context the caller context used to get value from resource file.
657      *
658      * @return the updated SoftApConfiguration.
659      */
removeUnavailableBandsFromConfig( SoftApConfiguration config, SoftApCapability capability, CoexManager coexManager, @NonNull WifiContext context)660     public static SoftApConfiguration removeUnavailableBandsFromConfig(
661             SoftApConfiguration config, SoftApCapability capability, CoexManager coexManager,
662             @NonNull WifiContext context) {
663         SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(config);
664 
665         try {
666             if (config.getBands().length == 1) {
667                 int configuredBand = config.getBand();
668                 int availableBand = ApConfigUtil.removeUnavailableBands(
669                         capability,
670                         configuredBand, coexManager);
671                 if (availableBand != configuredBand) {
672                     availableBand = ApConfigUtil.append24GToBandIf24GSupported(availableBand,
673                             context);
674                     Log.i(TAG, "Reset band from " + configuredBand + " to "
675                             + availableBand + " in single AP configuration");
676                     builder.setBand(availableBand);
677                 }
678             } else if (SdkLevel.isAtLeastS()) {
679                 SparseIntArray channels = config.getChannels();
680                 SparseIntArray newChannels = new SparseIntArray(channels.size());
681                 for (int i = 0; i < channels.size(); i++) {
682                     int configuredBand = channels.keyAt(i);
683                     int availableBand = ApConfigUtil.removeUnavailableBands(
684                             capability,
685                             configuredBand, coexManager);
686                     if (availableBand != configuredBand) {
687                         Log.i(TAG, "Reset band in index " + i + " from " + configuredBand
688                                 + " to " + availableBand + " in dual AP configuration");
689                     }
690                     if (isBandValid(availableBand)) {
691                         newChannels.put(availableBand, channels.valueAt(i));
692                     }
693                 }
694                 if (newChannels.size() != 0) {
695                     builder.setChannels(newChannels);
696                 } else {
697                     builder.setBand(
698                             ApConfigUtil.append24GToBandIf24GSupported(0, context));
699                 }
700             }
701         } catch (Exception e) {
702             Log.e(TAG, "Failed to update config by removing unavailable bands"
703                     + e);
704             return null;
705         }
706 
707         return builder.build();
708     }
709 
710     /**
711      * Upgrades a single band config to 2 + 5 GHz dual band if the overlay is configured and
712      * there are no non-2GHz/5GHz bands that are configured and available with the current
713      * capabilities.
714      * </p>
715      * This is intended for configurations that were previously set with single band in a different
716      * country code that didn't support 2 + 5 GHz dual band, but the current country code does
717      * support 2 + 5 GHz dual band.
718      */
upgradeTo2g5gBridgedIfAvailableBandsAreSubset( SoftApConfiguration config, SoftApCapability capability, @NonNull WifiContext context)719     public static SoftApConfiguration upgradeTo2g5gBridgedIfAvailableBandsAreSubset(
720             SoftApConfiguration config, SoftApCapability capability, @NonNull WifiContext context) {
721         // DBS requires SdkLevel S or above.
722         if (!SdkLevel.isAtLeastS()) {
723             return config;
724         }
725 
726         // Skip if overlay isn't set.
727         if (!context.getResourceCache().getBoolean(
728                 R.bool.config_wifiSoftapUpgradeTetheredTo2g5gBridgedIfBandsAreSubset)) {
729             return config;
730         }
731 
732         // Skip if config is already multi-band.
733         if (config.getBands().length != 1) {
734             return config;
735         }
736 
737         // Skip if 2 or 5 GHz aren't supported.
738         if (capability.getSupportedChannelList(BAND_2GHZ).length == 0
739                 || capability.getSupportedChannelList(BAND_5GHZ).length == 0) {
740             return config;
741         }
742 
743         // Skip if any non-2GHz/5GHz band is specified and supported.
744         int configuredBand = config.getBand();
745         for (int band : SoftApConfiguration.BAND_TYPES) {
746             if (band == BAND_2GHZ || band == BAND_5GHZ) {
747                 continue;
748             }
749             if ((configuredBand & band) != 0
750                     && capability.getSupportedChannelList(band).length > 0) {
751                 return config;
752             }
753         }
754 
755         Log.i(TAG, "Temporarily upgrading config with band " + config.getBands()[0]
756                 + " to 2 + 5GHz bridged.");
757         return new SoftApConfiguration.Builder(config)
758                 .setBands(new int[]{BAND_2GHZ, BAND_2GHZ | BAND_5GHZ})
759                 .build();
760     }
761 
762     /**
763      * Remove all unsupported bands from the input band and return the resulting
764      * (remaining) support bands. Unsupported bands are those which don't have channels available.
765      *
766      * @param context The caller context used to get value from resource file.
767      * @param band The target band which plan to enable
768      *
769      * @return the available band which removed the unsupported band.
770      *         0 when all of the band is not supported.
771      */
removeUnsupportedBands(WifiContext context, @NonNull int band)772     public static @BandType int removeUnsupportedBands(WifiContext context,
773             @NonNull int band) {
774         int availableBand = band;
775         for (int b : SoftApConfiguration.BAND_TYPES) {
776             if (((band & b) != 0) && !isSoftApBandSupported(context, b)) {
777                 availableBand &= ~b;
778             }
779         }
780         return availableBand;
781     }
782 
783     /**
784      * Check if security type is restricted for operation in 6GHz band
785      * As per WFA specification for 6GHz operation, the following security types are not allowed to
786      * be used in 6GHz band:
787      *   - OPEN
788      *   - WPA2-Personal
789      *   - WPA3-SAE-Transition
790      *   - WPA3-OWE-Transition
791      *
792      * @param type security type to check on
793      *
794      * @return true if security type is restricted for operation in 6GHz band, false otherwise
795      */
isSecurityTypeRestrictedFor6gBand( @oftApConfiguration.SecurityType int type)796     public static boolean isSecurityTypeRestrictedFor6gBand(
797             @SoftApConfiguration.SecurityType int type) {
798         switch(type) {
799             case SoftApConfiguration.SECURITY_TYPE_OPEN:
800             case SoftApConfiguration.SECURITY_TYPE_WPA2_PSK:
801             case SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION:
802             case SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION:
803                 return true;
804         }
805         return false;
806     }
807 
808     /**
809      * Checks whether HAL support converting the restricted security type to an allowed one in 6GHz
810      * band configuration.
811      * @param resources the resources to get the OEM configuration for HAL support.
812      * @param type security type.
813      * @return true if HAL support to map WPA3 transition mode to WPA3 in 6GHz band,
814      * false otherwise.
815      */
canHALConvertRestrictedSecurityTypeFor6GHz( @onNull WifiResourceCache resources, @SoftApConfiguration.SecurityType int type)816     public static boolean canHALConvertRestrictedSecurityTypeFor6GHz(
817             @NonNull WifiResourceCache resources, @SoftApConfiguration.SecurityType int type) {
818         return type == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION
819                 && resources.getBoolean(R.bool
820                         .config_wifiSofapHalMapWpa3TransitionModeToWpa3OnlyIn6GHzBand);
821     }
822 
823     /**
824      * Remove {@link SoftApConfiguration#BAND_6GHZ} if multiple bands are configured
825      * as a mask when security type is restricted to operate in this band.
826      *
827      * @param resources the resources to get the OEM configuration for HAL support.
828      * @param config The current {@link SoftApConfiguration}.
829      * @param isBridgedMode true if bridged mode is enabled, false otherwise.
830      *
831      * @return the updated SoftApConfiguration.
832      */
remove6gBandForUnsupportedSecurity( @onNull WifiResourceCache resources, SoftApConfiguration config, boolean isBridgedMode)833     public static SoftApConfiguration remove6gBandForUnsupportedSecurity(
834             @NonNull WifiResourceCache resources,
835             SoftApConfiguration config, boolean isBridgedMode) {
836         SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(config);
837 
838         try {
839             int securityType = config.getSecurityType();
840             if (config.getBands().length == 1) {
841                 int configuredBand = config.getBand();
842                 if ((configuredBand & SoftApConfiguration.BAND_6GHZ) != 0
843                         && isSecurityTypeRestrictedFor6gBand(config.getSecurityType())) {
844                     Log.i(TAG, "remove BAND_6G if multiple bands are configured "
845                             + "as a mask when security type is restricted");
846                     builder.setBand(configuredBand & ~SoftApConfiguration.BAND_6GHZ);
847                 }
848             } else if (SdkLevel.isAtLeastS()) {
849                 SparseIntArray channels = config.getChannels();
850                 SparseIntArray newChannels = new SparseIntArray(channels.size());
851                 if (isSecurityTypeRestrictedFor6gBand(securityType)) {
852                     for (int i = 0; i < channels.size(); i++) {
853                         int band = channels.keyAt(i);
854                         if ((band & SoftApConfiguration.BAND_6GHZ) != 0
855                                 && canHALConvertRestrictedSecurityTypeFor6GHz(resources,
856                                 securityType) && isBridgedMode) {
857                             Log.i(TAG, "Do not remove BAND_6G in bridged mode for"
858                                     + " security type: " + securityType
859                                     + " as HAL can convert the security type");
860                         } else {
861                             Log.i(TAG, "remove BAND_6G if multiple bands are configured "
862                                     + "as a mask when security type is restricted");
863                             band &= ~SoftApConfiguration.BAND_6GHZ;
864                         }
865                         newChannels.put(band, channels.valueAt(i));
866                     }
867                     builder.setChannels(newChannels);
868                 }
869             }
870         } catch (Exception e) {
871             Log.e(TAG, "Failed to update config by removing 6G band for unsupported security type:"
872                     + e);
873             return null;
874         }
875 
876         return builder.build();
877     }
878 
879     /**
880      * As per IEEE specification, 11BE mode should be disabled for the following
881      * security types.
882      *   - OPEN
883      *   - WPA2-Personal
884      * Also, disable 11BE in OWE-Transition as SoftAp run in bridged mode with one instance in open
885      * mode.
886      */
887     @VisibleForTesting
is11beDisabledForSecurityType( @oftApConfiguration.SecurityType int type)888     static boolean is11beDisabledForSecurityType(
889             @SoftApConfiguration.SecurityType int type) {
890         switch(type) {
891             case SoftApConfiguration.SECURITY_TYPE_OPEN:
892             case SoftApConfiguration.SECURITY_TYPE_WPA2_PSK:
893             case SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION:
894                 return true;
895         }
896         return false;
897     }
898 
899     /**
900      * Check if IEEE80211BE is allowed for the given softAp configuration.
901      *
902      * @param capabilities capabilities of the device to check support for IEEE80211BE support.
903      * @param context The caller context used to get the OEM configuration for support for
904      *                IEEE80211BE & single link MLO in bridged mode from the resource file.
905      * @param config The current {@link SoftApConfiguration}.
906      * @param isBridgedMode true if bridged mode is enabled, false otherwise.
907      * @param maximumSupportedMLD maximum number of supported MLD on SoftAp.
908      * @param currentExistingMLD number of existing 11BE SoftApManager.
909      * @param isMLDApSupportMLO true if the chip reports the support multiple links
910      *                                    on a single MLD AP.
911      *
912      * @return true if IEEE80211BE is allowed for the given configuration, false otherwise.
913      */
is11beAllowedForThisConfiguration(DeviceWiphyCapabilities capabilities, @NonNull WifiContext context, SoftApConfiguration config, boolean isBridgedMode, int maximumSupportedMLD, int currentExistingMLD, boolean isMLDApSupportMLO)914     public static boolean is11beAllowedForThisConfiguration(DeviceWiphyCapabilities capabilities,
915             @NonNull WifiContext context,
916             SoftApConfiguration config,
917             boolean isBridgedMode, int maximumSupportedMLD, int currentExistingMLD,
918             boolean isMLDApSupportMLO) {
919         if (!ApConfigUtil.isIeee80211beSupported(context)) {
920             return false;
921         }
922         if (!isMLDApSupportMLO) {
923             // For non-MLO case, check capabilities
924             if (capabilities == null || !capabilities.isWifiStandardSupported(
925                     ScanResult.WIFI_STANDARD_11BE)) {
926                 return false;
927             }
928         }
929         if (Flags.mloSap()) {
930             if (!hasAvailableMLD(context, isBridgedMode, maximumSupportedMLD,
931                     currentExistingMLD, isMLDApSupportMLO)) {
932                 Log.i(TAG, "No available MLD, hence downgrading from 11be. currentExistingMLD = "
933                         + currentExistingMLD + ", isMLDApSupportMLO = " + isMLDApSupportMLO);
934                 return false;
935             }
936         } else {
937             if (isBridgedMode
938                     && !context.getResourceCache().getBoolean(
939                             R.bool.config_wifiSoftApSingleLinkMloInBridgedModeSupported)) {
940                 return false;
941             }
942         }
943         if (is11beDisabledForSecurityType(config.getSecurityType())) {
944             return false;
945         }
946         return true;
947     }
948 
hasAvailableMLD(@onNull WifiContext context, boolean isBridgedMode, int maximumSupportedMLD, int currentExistingMLD, boolean isMLDApSupportMLO)949     private static boolean hasAvailableMLD(@NonNull WifiContext context,
950             boolean isBridgedMode, int maximumSupportedMLD, int currentExistingMLD,
951             boolean isMLDApSupportMLO) {
952         int numberOfMLDStillAllowed =
953                 maximumSupportedMLD - currentExistingMLD;
954         if (numberOfMLDStillAllowed < 1) {
955             return false;
956         }
957         if (isBridgedMode && !isMLDApSupportMLO && numberOfMLDStillAllowed < 2) {
958             // For non multilink MLO bridged mode, it requires two 11be instances.
959             return false;
960         }
961         return true;
962     }
963 
964     /**
965      * Returns maximum number of supported MLD on SoftAp.
966      *
967      * @param context The caller context used to get the OEM configuration from resource file.
968      * @param chipSupportsMultipleMld whether Chip supports multiple mld on SoftAp.
969      */
getMaximumSupportedMLD(@onNull WifiContext context, boolean chipSupportsMultipleMld)970     public static int getMaximumSupportedMLD(@NonNull WifiContext context,
971             boolean chipSupportsMultipleMld) {
972         int numberOfMLDSupported = context.getResourceCache()
973                 .getInteger(R.integer.config_wifiSoftApMaxNumberMLDSupported);
974         if (numberOfMLDSupported > 0) {
975             if (Flags.multipleMldOnSapSupported() && !chipSupportsMultipleMld) {
976                 // Chip doesn't support multiple mld on SoftAp
977                 return 1;
978             }
979             return numberOfMLDSupported;
980         }
981         if (context.getResourceCache().getBoolean(
982                         R.bool.config_wifiSoftApSingleLinkMloInBridgedModeSupported)) {
983             return 2;
984         }
985         return 1;
986     }
987 
988     /**
989      * Update AP band and channel based on the provided country code and band.
990      * This will also set
991      * @param wifiNative reference to WifiNative
992      * @param coexManager reference to CoexManager
993      * @param resources the resources to use to get configured allowed channels.
994      * @param countryCode country code
995      * @param config configuration to update
996      * @param capability soft ap capability
997      * @return the corresponding {@link SoftApManager.StartResult} result code.
998      */
updateApChannelConfig(WifiNative wifiNative, @NonNull CoexManager coexManager, WifiResourceCache resources, String countryCode, SoftApConfiguration.Builder configBuilder, SoftApConfiguration config, SoftApCapability capability)999     public static @SoftApManager.StartResult int updateApChannelConfig(WifiNative wifiNative,
1000             @NonNull CoexManager coexManager,
1001             WifiResourceCache resources,
1002             String countryCode,
1003             SoftApConfiguration.Builder configBuilder,
1004             SoftApConfiguration config,
1005             SoftApCapability capability) {
1006         /* Use default band and channel for device without HAL. */
1007         if (!wifiNative.isHalStarted()) {
1008             configBuilder.setChannel(DEFAULT_AP_CHANNEL, DEFAULT_AP_BAND);
1009             return SoftApManager.START_RESULT_SUCCESS;
1010         }
1011 
1012         /* Country code is mandatory for 5GHz band. */
1013         if (config.getBand() == SoftApConfiguration.BAND_5GHZ
1014                 && countryCode == null) {
1015             Log.e(TAG, "5GHz band is not allowed without country code");
1016             return SoftApManager.START_RESULT_FAILURE_GENERAL;
1017         }
1018         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_ACS_OFFLOAD)) {
1019             /* Select a channel if it is not specified and ACS is not enabled */
1020             if (config.getChannel() == 0) {
1021                 int freq = chooseApChannel(config.getBand(), coexManager, resources,
1022                         capability);
1023                 if (freq == -1) {
1024                     /* We're not able to get channel from wificond. */
1025                     Log.e(TAG, "Failed to get available channel.");
1026                     return SoftApManager.START_RESULT_FAILURE_NO_CHANNEL;
1027                 }
1028                 configBuilder.setChannel(
1029                         ScanResult.convertFrequencyMhzToChannelIfSupported(freq),
1030                         convertFrequencyToBand(freq));
1031             }
1032 
1033             if (SdkLevel.isAtLeastT()) {
1034                 /* remove list of allowed channels since they only apply to ACS */
1035                 if (sVerboseLoggingEnabled) {
1036                     Log.i(TAG, "Ignoring Allowed ACS channels since ACS is not supported.");
1037                 }
1038                 configBuilder.setAllowedAcsChannels(SoftApConfiguration.BAND_2GHZ,
1039                         new int[] {});
1040                 configBuilder.setAllowedAcsChannels(SoftApConfiguration.BAND_5GHZ,
1041                         new int[] {});
1042                 configBuilder.setAllowedAcsChannels(SoftApConfiguration.BAND_6GHZ,
1043                         new int[] {});
1044             }
1045         }
1046 
1047         return SoftApManager.START_RESULT_SUCCESS;
1048     }
1049 
1050     /**
1051      * Helper function for converting WifiConfiguration to SoftApConfiguration.
1052      *
1053      * Only Support None and WPA2 configuration conversion.
1054      * Note that WifiConfiguration only Supports 2GHz, 5GHz, 2GHz+5GHz bands,
1055      * so conversion is limited to these bands.
1056      *
1057      * @param wifiConfig the WifiConfiguration which need to convert.
1058      * @return the SoftApConfiguration if wifiConfig is valid, null otherwise.
1059      */
1060     @Nullable
fromWifiConfiguration( @onNull WifiConfiguration wifiConfig)1061     public static SoftApConfiguration fromWifiConfiguration(
1062             @NonNull WifiConfiguration wifiConfig) {
1063         SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
1064         try {
1065             // WifiConfiguration#SSID uses a formatted string with double quotes for UTF-8 and no
1066             // quotes for hexadecimal. But to support legacy behavior, we need to continue
1067             // setting the entire string with quotes as the UTF-8 SSID.
1068             configBuilder.setSsid(wifiConfig.SSID);
1069             if (wifiConfig.getAuthType() == WifiConfiguration.KeyMgmt.WPA2_PSK) {
1070                 configBuilder.setPassphrase(wifiConfig.preSharedKey,
1071                         SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
1072             }
1073             configBuilder.setHiddenSsid(wifiConfig.hiddenSSID);
1074 
1075             int band;
1076             switch (wifiConfig.apBand) {
1077                 case WifiConfiguration.AP_BAND_2GHZ:
1078                     band = SoftApConfiguration.BAND_2GHZ;
1079                     break;
1080                 case WifiConfiguration.AP_BAND_5GHZ:
1081                     band = SoftApConfiguration.BAND_5GHZ;
1082                     break;
1083                 case WifiConfiguration.AP_BAND_60GHZ:
1084                     band = SoftApConfiguration.BAND_60GHZ;
1085                     break;
1086                 default:
1087                     // WifiConfiguration.AP_BAND_ANY means only 2GHz and 5GHz bands
1088                     band = SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ;
1089                     break;
1090             }
1091             if (wifiConfig.apChannel == 0) {
1092                 configBuilder.setBand(band);
1093             } else {
1094                 configBuilder.setChannel(wifiConfig.apChannel, band);
1095             }
1096         } catch (IllegalArgumentException iae) {
1097             Log.e(TAG, "Invalid WifiConfiguration" + iae);
1098             return null;
1099         } catch (IllegalStateException ise) {
1100             Log.e(TAG, "Invalid WifiConfiguration" + ise);
1101             return null;
1102         }
1103         return configBuilder.build();
1104     }
1105 
1106     /**
1107      * Helper function to creating SoftApCapability instance with initial field from resource file.
1108      *
1109      * @param context the caller context used to get value from resource file.
1110      * @return SoftApCapability which updated the feature support or not from resource.
1111      */
1112     @NonNull
1113     @Keep
updateCapabilityFromResource(@onNull Context contextIn)1114     public static SoftApCapability updateCapabilityFromResource(@NonNull Context contextIn) {
1115         WifiContext context;
1116         if (contextIn instanceof WifiContext) {
1117             context = (WifiContext) contextIn;
1118         } else {
1119             context = new WifiContext(contextIn);
1120         }
1121         long features = 0;
1122         if (isAcsSupported(context)) {
1123             Log.d(TAG, "Update Softap capability, add acs feature support");
1124             features |= SOFTAP_FEATURE_ACS_OFFLOAD;
1125         }
1126 
1127         if (isClientForceDisconnectSupported(context)) {
1128             Log.d(TAG, "Update Softap capability, add client control feature support");
1129             features |= SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT;
1130         }
1131 
1132         if (isWpa3SaeSupported(context)) {
1133             Log.d(TAG, "Update Softap capability, add SAE feature support");
1134             features |= SOFTAP_FEATURE_WPA3_SAE;
1135         }
1136 
1137         if (isMacCustomizationSupported(context)) {
1138             Log.d(TAG, "Update Softap capability, add MAC customization support");
1139             features |= SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION;
1140         }
1141 
1142         if (isSoftApBandSupported(context, SoftApConfiguration.BAND_2GHZ)) {
1143             Log.d(TAG, "Update Softap capability, add 2.4G support");
1144             features |= SOFTAP_FEATURE_BAND_24G_SUPPORTED;
1145         }
1146 
1147         if (isSoftApBandSupported(context, SoftApConfiguration.BAND_5GHZ)) {
1148             Log.d(TAG, "Update Softap capability, add 5G support");
1149             features |= SOFTAP_FEATURE_BAND_5G_SUPPORTED;
1150         }
1151 
1152         if (isSoftApBandSupported(context, SoftApConfiguration.BAND_6GHZ)) {
1153             Log.d(TAG, "Update Softap capability, add 6G support");
1154             features |= SOFTAP_FEATURE_BAND_6G_SUPPORTED;
1155         }
1156 
1157         if (isSoftApBandSupported(context, SoftApConfiguration.BAND_60GHZ)) {
1158             Log.d(TAG, "Update Softap capability, add 60G support");
1159             features |= SOFTAP_FEATURE_BAND_60G_SUPPORTED;
1160         }
1161 
1162         if (isIeee80211axSupported(context)) {
1163             Log.d(TAG, "Update Softap capability, add ax support");
1164             features |= SOFTAP_FEATURE_IEEE80211_AX;
1165         }
1166 
1167         if (isIeee80211beSupported(context)) {
1168             Log.d(TAG, "Update Softap capability, add be support");
1169             features |= SOFTAP_FEATURE_IEEE80211_BE;
1170         }
1171 
1172         if (isOweTransitionSupported(context)) {
1173             Log.d(TAG, "Update Softap capability, add OWE Transition feature support");
1174             features |= SOFTAP_FEATURE_WPA3_OWE_TRANSITION;
1175         }
1176 
1177         if (isOweSupported(context)) {
1178             Log.d(TAG, "Update Softap capability, add OWE feature support");
1179             features |= SOFTAP_FEATURE_WPA3_OWE;
1180         }
1181 
1182         SoftApCapability capability = new SoftApCapability(features);
1183         int hardwareSupportedMaxClient = context.getResourceCache().getInteger(
1184                 R.integer.config_wifiHardwareSoftapMaxClientCount);
1185         if (hardwareSupportedMaxClient > 0) {
1186             Log.d(TAG, "Update Softap capability, max client = " + hardwareSupportedMaxClient);
1187             capability.setMaxSupportedClients(hardwareSupportedMaxClient);
1188         }
1189 
1190         return capability;
1191     }
1192 
1193     /**
1194      * Helper function to update SoftApCapability instance based on config store.
1195      *
1196      * @param capability the original softApCapability
1197      * @param configStore where we stored the Capability after first time fetch from driver.
1198      * @return SoftApCapability which updated from the config store.
1199      */
1200     @NonNull
updateCapabilityFromConfigStore( SoftApCapability capability, WifiSettingsConfigStore configStore)1201     public static SoftApCapability updateCapabilityFromConfigStore(
1202             SoftApCapability capability,
1203             WifiSettingsConfigStore configStore) {
1204         if (capability == null) {
1205             return null;
1206         }
1207         if (capability.areFeaturesSupported(SOFTAP_FEATURE_IEEE80211_BE)) {
1208             capability.setSupportedFeatures(isIeee80211beEnabledInConfig(configStore),
1209                     SOFTAP_FEATURE_IEEE80211_BE);
1210         }
1211         return capability;
1212     }
1213 
1214     /**
1215      * Helper function to get device support 802.11 AX on Soft AP or not
1216      *
1217      * @param context the caller context used to get value from resource file.
1218      * @return true if supported, false otherwise.
1219      */
isIeee80211axSupported(@onNull WifiContext context)1220     public static boolean isIeee80211axSupported(@NonNull WifiContext context) {
1221         return context.getResourceCache().getBoolean(
1222                     R.bool.config_wifiSoftapIeee80211axSupported);
1223     }
1224 
1225     /**
1226      * Helper function to get device support 802.11 BE on Soft AP or not
1227      *
1228      * @param context the caller context used to get value from resource file.
1229      * @return true if supported, false otherwise.
1230      */
isIeee80211beSupported(@onNull WifiContext context)1231     public static boolean isIeee80211beSupported(@NonNull WifiContext context) {
1232         return context.getResourceCache().getBoolean(
1233                     R.bool.config_wifiSoftapIeee80211beSupported);
1234     }
1235 
1236     /**
1237      * Helper function to check Config supports 802.11 BE on Soft AP or not
1238      *
1239      * @param configStore to check the support from WifiSettingsConfigStore
1240      * @return true if supported, false otherwise.
1241      */
isIeee80211beEnabledInConfig( WifiSettingsConfigStore configStore)1242     public static boolean isIeee80211beEnabledInConfig(
1243             WifiSettingsConfigStore configStore) {
1244         return configStore.get(
1245                     WifiSettingsConfigStore.WIFI_WIPHY_11BE_SUPPORTED);
1246     }
1247 
1248     /**
1249      * Helper function to get device support AP MAC randomization or not.
1250      *
1251      * @param context the caller context used to get value from resource file.
1252      * @return true if supported, false otherwise.
1253      */
isApMacRandomizationSupported(@onNull WifiContext context)1254     public static boolean isApMacRandomizationSupported(@NonNull WifiContext context) {
1255         return context.getResourceCache().getBoolean(
1256                     R.bool.config_wifi_ap_mac_randomization_supported);
1257     }
1258 
1259     /**
1260      * Helper function to get HAL support bridged AP or not.
1261      *
1262      * @param context the caller context used to get value from resource file.
1263      * @param wifiNative to get the Iface combination from device.
1264      * @return true if supported, false otherwise.
1265      */
isBridgedModeSupported( @onNull WifiContext context, @NonNull WifiNative wifiNative)1266     public static boolean isBridgedModeSupported(
1267             @NonNull WifiContext context, @NonNull WifiNative wifiNative) {
1268         return SdkLevel.isAtLeastS() && context.getResourceCache().getBoolean(
1269                     R.bool.config_wifiBridgedSoftApSupported)
1270                     && wifiNative.canDeviceSupportCreateTypeCombo(new SparseArray<Integer>() {{
1271                             put(HDM_CREATE_IFACE_AP_BRIDGE, 1);
1272                         }});
1273     }
1274 
1275    /**
1276      * Helper function to get whether or not device claim support bridged AP.
1277      * (i.e. In resource file)
1278      *
1279      * @param context the caller context used to get value from resource file.
1280      * @return true if supported, false otherwise.
1281      */
1282     public static boolean isBridgedModeSupportedInConfig(@NonNull WifiContext context) {
1283         return SdkLevel.isAtLeastS() && context.getResourceCache().getBoolean(
1284                     R.bool.config_wifiBridgedSoftApSupported);
1285     }
1286 
1287 
1288     /**
1289      * Helper function to get HAL support STA + bridged AP or not.
1290      *
1291      * @param context the caller context used to get value from resource file.
1292      * @param wifiNative to get the Iface combination from device.
1293      * @return true if supported, false otherwise.
1294      */
1295     public static boolean isStaWithBridgedModeSupported(
1296             @NonNull WifiContext context, @NonNull WifiNative wifiNative) {
1297         return SdkLevel.isAtLeastS() && context.getResourceCache().getBoolean(
1298                     R.bool.config_wifiStaWithBridgedSoftApConcurrencySupported)
1299                     && wifiNative.canDeviceSupportCreateTypeCombo(new SparseArray<Integer>() {{
1300                             put(HDM_CREATE_IFACE_AP_BRIDGE, 1);
1301                             put(HDM_CREATE_IFACE_STA, 1);
1302                         }});
1303     }
1304 
1305     /**
1306      * Helper function to get HAL support client force disconnect or not.
1307      *
1308      * @param context the caller context used to get value from resource file.
1309      * @return true if supported, false otherwise.
1310      */
1311     public static boolean isClientForceDisconnectSupported(@NonNull WifiContext context) {
1312         return context.getResourceCache().getBoolean(
1313                 R.bool.config_wifiSofapClientForceDisconnectSupported);
1314     }
1315 
1316     /**
1317      * Helper function to get SAE support or not.
1318      *
1319      * @param context the caller context used to get value from resource file.
1320      * @return true if supported, false otherwise.
1321      */
1322     @Keep
1323     public static boolean isWpa3SaeSupported(@NonNull Context contextIn) {
1324         WifiContext context;
1325         if (contextIn instanceof WifiContext) {
1326             context = (WifiContext) contextIn;
1327         } else {
1328             context = new WifiContext(contextIn);
1329         }
1330         return context.getResourceCache().getBoolean(
1331                 R.bool.config_wifi_softap_sae_supported);
1332     }
1333 
1334     /**
1335      * Helper function to get ACS support or not.
1336      *
1337      * @param context the caller context used to get value from resource file.
1338      * @return true if supported, false otherwise.
1339      */
1340     public static boolean isAcsSupported(@NonNull WifiContext context) {
1341         return context.getResourceCache().getBoolean(
1342                 R.bool.config_wifi_softap_acs_supported);
1343     }
1344 
1345     /**
1346      * Helper function to get MAC Address customization or not.
1347      *
1348      * @param context the caller context used to get value from resource file.
1349      * @return true if supported, false otherwise.
1350      */
1351     public static boolean isMacCustomizationSupported(@NonNull WifiContext context) {
1352         return context.getResourceCache().getBoolean(
1353                 R.bool.config_wifiSoftapMacAddressCustomizationSupported);
1354     }
1355 
1356     /**
1357      * Helper function to get whether or not Soft AP support on particular band.
1358      *
1359      * @param context the caller context used to get value from resource file.
1360      * @param band the band soft AP to operate on.
1361      * @return true if supported, false otherwise.
1362      */
1363     @Keep
1364     public static boolean isSoftApBandSupported(@NonNull Context contextIn,
1365             @BandType int band) {
1366         WifiContext context;
1367         if (contextIn instanceof WifiContext) {
1368             context = (WifiContext) contextIn;
1369         } else {
1370             context = new WifiContext(contextIn);
1371         }
1372         switch (band) {
1373             case SoftApConfiguration.BAND_2GHZ:
1374                 return context.getResourceCache().getBoolean(R.bool.config_wifi24ghzSupport)
1375                         && context.getResourceCache().getBoolean(
1376                         R.bool.config_wifiSoftap24ghzSupported);
1377             case SoftApConfiguration.BAND_5GHZ:
1378                 return context.getResourceCache().getBoolean(R.bool.config_wifi5ghzSupport)
1379                         && context.getResourceCache().getBoolean(
1380                         R.bool.config_wifiSoftap5ghzSupported);
1381             case SoftApConfiguration.BAND_6GHZ:
1382                 return context.getResourceCache().getBoolean(R.bool.config_wifi6ghzSupport)
1383                         && context.getResourceCache().getBoolean(
1384                         R.bool.config_wifiSoftap6ghzSupported);
1385             case SoftApConfiguration.BAND_60GHZ:
1386                 return context.getResourceCache().getBoolean(R.bool.config_wifi60ghzSupport)
1387                         && context.getResourceCache().getBoolean(
1388                         R.bool.config_wifiSoftap60ghzSupported);
1389             default:
1390                 return false;
1391         }
1392     }
1393 
1394     /**
1395      * Helper function to get whether or not dynamic country code update is supported when Soft AP
1396      * enabled.
1397      *
1398      * @param context the caller context used to get value from resource file.
1399      * @return true if supported, false otherwise.
1400      */
1401     public static boolean isSoftApDynamicCountryCodeSupported(@NonNull WifiContext context) {
1402         return context.getResourceCache().getBoolean(
1403                 R.bool.config_wifiSoftApDynamicCountryCodeUpdateSupported);
1404     }
1405 
1406 
1407     /**
1408      * Helper function to get whether or not restart Soft AP required when country code changed.
1409      *
1410      * @param context the caller context used to get value from resource file.
1411      * @return true if supported, false otherwise.
1412      */
1413     public static boolean isSoftApRestartRequiredWhenCountryCodeChanged(
1414             @NonNull WifiContext context) {
1415         return context.getResourceCache().getBoolean(
1416                 R.bool.config_wifiForcedSoftApRestartWhenCountryCodeChanged);
1417     }
1418 
1419     /**
1420      * Helper function to get OWE-Transition is support or not.
1421      *
1422      * @param context the caller context used to get value from resource file.
1423      * @return true if supported, false otherwise.
1424      */
1425     public static boolean isOweTransitionSupported(@NonNull WifiContext context) {
1426         return context.getResourceCache().getBoolean(
1427                 R.bool.config_wifiSoftapOweTransitionSupported);
1428     }
1429 
1430     /**
1431      * Helper function to get OWE is support or not.
1432      *
1433      * @param context the caller context used to get value from resource file.
1434      * @return true if supported, false otherwise.
1435      */
1436     public static boolean isOweSupported(@NonNull WifiContext context) {
1437         return context.getResourceCache().getBoolean(
1438                 R.bool.config_wifiSoftapOweSupported);
1439     }
1440 
1441     /**
1442      * Helper function for comparing two SoftApConfiguration.
1443      *
1444      * @param currentConfig the original configuration.
1445      * @param newConfig the new configuration which plan to apply.
1446      * @return true if the difference between the two configurations requires a restart to apply,
1447      *         false otherwise.
1448      */
1449     public static boolean checkConfigurationChangeNeedToRestart(
1450             SoftApConfiguration currentConfig, SoftApConfiguration newConfig) {
1451         return !Objects.equals(currentConfig.getWifiSsid(), newConfig.getWifiSsid())
1452                 || !Objects.equals(currentConfig.getBssid(), newConfig.getBssid())
1453                 || currentConfig.getSecurityType() != newConfig.getSecurityType()
1454                 || !Objects.equals(currentConfig.getPassphrase(), newConfig.getPassphrase())
1455                 || currentConfig.isHiddenSsid() != newConfig.isHiddenSsid()
1456                 || currentConfig.getBand() != newConfig.getBand()
1457                 || currentConfig.getChannel() != newConfig.getChannel()
1458                 || (SdkLevel.isAtLeastS() && !currentConfig.getChannels().toString()
1459                         .equals(newConfig.getChannels().toString()));
1460     }
1461 
1462 
1463     /**
1464      * Helper function for checking all of the configuration are supported or not.
1465      *
1466      * @param config target configuration want to check.
1467      * @param capability the capability which indicate feature support or not.
1468      * @return true if supported, false otherwise.
1469      */
1470     public static boolean checkSupportAllConfiguration(SoftApConfiguration config,
1471             SoftApCapability capability) {
1472         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)
1473                 && (config.getMaxNumberOfClients() != 0 || config.isClientControlByUserEnabled()
1474                 || config.getBlockedClientList().size() != 0)) {
1475             Log.d(TAG, "Error, Client control requires HAL support");
1476             return false;
1477         }
1478         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_WPA3_SAE)) {
1479             if (config.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION
1480                     || config.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) {
1481                 Log.d(TAG, "Error, SAE requires HAL support");
1482                 return false;
1483             }
1484         }
1485         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION)) {
1486             if (config.getBssid() != null) {
1487                 Log.d(TAG, "Error, MAC address customization requires HAL support");
1488                 return false;
1489             }
1490             if (SdkLevel.isAtLeastS()
1491                     && (config.getMacRandomizationSetting()
1492                     == SoftApConfiguration.RANDOMIZATION_PERSISTENT
1493                     || config.getMacRandomizationSetting()
1494                     == SoftApConfiguration.RANDOMIZATION_NON_PERSISTENT)) {
1495                 Log.d(TAG, "Error, MAC randomization requires HAL support");
1496                 return false;
1497             }
1498         }
1499         int requestedBands = 0;
1500         for (int band : config.getBands()) {
1501             requestedBands |= band;
1502         }
1503         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_BAND_24G_SUPPORTED)) {
1504             if ((requestedBands & SoftApConfiguration.BAND_2GHZ) != 0) {
1505                 Log.d(TAG, "Error, 2.4Ghz band requires HAL support");
1506                 return false;
1507             }
1508         }
1509         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_BAND_5G_SUPPORTED)) {
1510             if ((requestedBands & SoftApConfiguration.BAND_5GHZ) != 0) {
1511                 Log.d(TAG, "Error, 5Ghz band requires HAL support");
1512                 return false;
1513             }
1514         }
1515         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_BAND_6G_SUPPORTED)) {
1516             if ((requestedBands & SoftApConfiguration.BAND_6GHZ) != 0) {
1517                 Log.d(TAG, "Error, 6Ghz band requires HAL support");
1518                 return false;
1519             }
1520         }
1521         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_BAND_60G_SUPPORTED)) {
1522             if ((requestedBands & SoftApConfiguration.BAND_60GHZ) != 0) {
1523                 Log.d(TAG, "Error, 60Ghz band requires HAL support");
1524                 return false;
1525             }
1526         }
1527         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_WPA3_OWE_TRANSITION)) {
1528             if (config.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION) {
1529                 Log.d(TAG, "Error, OWE transition requires HAL support");
1530                 return false;
1531             }
1532         }
1533         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_WPA3_OWE)) {
1534             if (config.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA3_OWE) {
1535                 Log.d(TAG, "Error, OWE requires HAL support");
1536                 return false;
1537             }
1538         }
1539 
1540         // Checks for Dual AP
1541         if (SdkLevel.isAtLeastS() && config.getBands().length > 1) {
1542             int[] bands = config.getBands();
1543             if ((bands[0] & SoftApConfiguration.BAND_60GHZ) != 0
1544                     || (bands[1] & SoftApConfiguration.BAND_60GHZ) != 0) {
1545                 Log.d(TAG, "Error, dual APs doesn't support on 60GHz");
1546                 return false;
1547             }
1548             if (!capability.areFeaturesSupported(SOFTAP_FEATURE_ACS_OFFLOAD)
1549                     && (config.getChannels().valueAt(0) == 0
1550                     || config.getChannels().valueAt(1) == 0)) {
1551                 Log.d(TAG, "Error, dual APs requires HAL ACS support when channel isn't specified");
1552                 return false;
1553             }
1554         }
1555         return true;
1556     }
1557 
1558 
1559     /**
1560      * Check if need to provide freq range for ACS.
1561      *
1562      * @param band in SoftApConfiguration.BandType
1563      * @param context the caller context used to get values from resource file
1564      * @param config the used SoftApConfiguration
1565      *
1566      * @return true when freq ranges is needed, otherwise false.
1567      */
1568     public static boolean isSendFreqRangesNeeded(@BandType int band, WifiContext context,
1569             SoftApConfiguration config) {
1570         // Fist we check if one of the selected bands has restrictions in the overlay file or in the
1571         // provided SoftApConfiguration.
1572         // Note,
1573         //   - We store the config string here for future use, hence we need to check all bands.
1574         //   - If there is no restrictions on channels, we store the full band
1575         for (int b : SoftApConfiguration.BAND_TYPES) {
1576             if ((band & b) != 0) {
1577                 List<Integer> configuredList = getConfiguredChannelList(
1578                         context.getResourceCache(), b);
1579                 if (configuredList != null && !configuredList.isEmpty()) {
1580                     // If any of the selected band has restriction in the overlay file return true.
1581                     return true;
1582                 }
1583                 if (SdkLevel.isAtLeastT() && config.getAllowedAcsChannels(b).length != 0) {
1584                     return true;
1585                 }
1586             }
1587         }
1588 
1589         // Next, if only one of 5G or 6G is selected, then we need freqList to separate them
1590         // Since there is no other way.
1591         if (((band & SoftApConfiguration.BAND_5GHZ) != 0)
1592                 && ((band & SoftApConfiguration.BAND_6GHZ) == 0)) {
1593             return true;
1594         }
1595         if (((band & SoftApConfiguration.BAND_5GHZ) == 0)
1596                 && ((band & SoftApConfiguration.BAND_6GHZ) != 0)) {
1597             return true;
1598         }
1599 
1600         // In all other cases, we don't need to set the freqList
1601         return false;
1602     }
1603 
1604     /**
1605      * Collect a List of allowed channels for ACS operations on a selected band
1606      *
1607      * @param band on which channel list are required
1608      * @param oemConfigString Configuration string from OEM resource file.
1609      *        An empty string means all channels on this band are allowed
1610      * @param callerConfig allowed chnannels as required by the caller
1611      *
1612      * @return List of channel numbers that meet both criteria
1613      */
1614     public static List<Integer> collectAllowedAcsChannels(@BandType int band,
1615             String oemConfigString, int[] callerConfig) {
1616 
1617         // Convert the OEM config string into a set of channel numbers
1618         Set<Integer> allowedChannelSet = getOemAllowedChannels(band, oemConfigString);
1619 
1620         // Update the allowed channels with user configuration
1621         allowedChannelSet.retainAll(getCallerAllowedChannels(band, callerConfig));
1622 
1623         return new ArrayList<Integer>(allowedChannelSet);
1624     }
1625 
1626     private static Set<Integer> getSetForAllChannelsInBand(@BandType int band) {
1627         switch(band) {
1628             case SoftApConfiguration.BAND_2GHZ:
1629                 return IntStream.rangeClosed(
1630                         ScanResult.BAND_24_GHZ_FIRST_CH_NUM,
1631                         ScanResult.BAND_24_GHZ_LAST_CH_NUM)
1632                         .boxed()
1633                         .collect(Collectors.toSet());
1634 
1635             case SoftApConfiguration.BAND_5GHZ:
1636                 return IntStream.rangeClosed(
1637                         ScanResult.BAND_5_GHZ_FIRST_CH_NUM,
1638                         ScanResult.BAND_5_GHZ_LAST_CH_NUM)
1639                         .boxed()
1640                         .collect(Collectors.toSet());
1641 
1642             case SoftApConfiguration.BAND_6GHZ:
1643                 return IntStream.rangeClosed(
1644                         ScanResult.BAND_6_GHZ_FIRST_CH_NUM,
1645                         ScanResult.BAND_6_GHZ_LAST_CH_NUM)
1646                         .boxed()
1647                         .collect(Collectors.toSet());
1648             default:
1649                 Log.e(TAG, "Invalid band: " + bandToString(band));
1650                 return Collections.emptySet();
1651         }
1652     }
1653 
1654     private static Set<Integer> getOemAllowedChannels(@BandType int band, String oemConfigString) {
1655         if (TextUtils.isEmpty(oemConfigString)) {
1656             // Empty string means all channels are allowed in this band
1657             return getSetForAllChannelsInBand(band);
1658         }
1659 
1660         // String is not empty, parsing it
1661         Set<Integer> allowedChannelsOem = new HashSet<>();
1662 
1663         for (String channelRange : oemConfigString.split(",")) {
1664             try {
1665                 if (channelRange.contains("-")) {
1666                     String[] channels  = channelRange.split("-");
1667                     if (channels.length != 2) {
1668                         Log.e(TAG, "Unrecognized channel range, length is " + channels.length);
1669                         continue;
1670                     }
1671                     int start = Integer.parseInt(channels[0].trim());
1672                     int end = Integer.parseInt(channels[1].trim());
1673                     if (start > end) {
1674                         Log.e(TAG, "Invalid channel range, from " + start + " to " + end);
1675                         continue;
1676                     }
1677 
1678                     allowedChannelsOem.addAll(IntStream.rangeClosed(start, end)
1679                             .boxed().collect(Collectors.toSet()));
1680                 } else if (!TextUtils.isEmpty(channelRange)) {
1681                     int channel = Integer.parseInt(channelRange.trim());
1682                     allowedChannelsOem.add(channel);
1683                 }
1684             } catch (NumberFormatException e) {
1685                 // Ignore malformed value
1686                 Log.e(TAG, "Malformed channel value detected: " + e);
1687                 continue;
1688             }
1689         }
1690 
1691         return allowedChannelsOem;
1692     }
1693 
1694     private static Set<Integer> getCallerAllowedChannels(@BandType int band, int[] callerConfig) {
1695         if (callerConfig.length == 0) {
1696             // Empty set means all channels are allowed in this band
1697             return getSetForAllChannelsInBand(band);
1698         }
1699 
1700         // Otherwise return the caller set as is
1701         return IntStream.of(callerConfig).boxed()
1702                 .collect(Collectors.toCollection(HashSet::new));
1703     }
1704 
1705     /**
1706      * Deep copy for object Map<String, SoftApInfo>
1707      */
1708     public static Map<String, SoftApInfo> deepCopyForSoftApInfoMap(
1709             Map<String, SoftApInfo> originalMap) {
1710         if (originalMap == null) {
1711             return null;
1712         }
1713         Map<String, SoftApInfo> deepCopyMap = new HashMap<String, SoftApInfo>();
1714         for (Map.Entry<String, SoftApInfo> entry: originalMap.entrySet()) {
1715             deepCopyMap.put(entry.getKey(), new SoftApInfo(entry.getValue()));
1716         }
1717         return deepCopyMap;
1718     }
1719 
1720     /**
1721      * Deep copy for object Map<String, List<WifiClient>>
1722      */
1723     public static Map<String, List<WifiClient>> deepCopyForWifiClientListMap(
1724             Map<String, List<WifiClient>> originalMap) {
1725         if (originalMap == null) {
1726             return null;
1727         }
1728         Map<String, List<WifiClient>> deepCopyMap = new HashMap<String, List<WifiClient>>();
1729         for (Map.Entry<String, List<WifiClient>> entry: originalMap.entrySet()) {
1730             List<WifiClient> clients = new ArrayList<>();
1731             for (WifiClient client : entry.getValue()) {
1732                 clients.add(new WifiClient(client.getMacAddress(),
1733                         client.getApInstanceIdentifier()));
1734             }
1735             deepCopyMap.put(entry.getKey(), clients);
1736         }
1737         return deepCopyMap;
1738     }
1739 
1740     /**
1741      * Observer the available channel from native layer (vendor HAL if getUsableChannels is
1742      * supported, or wificond if not supported) and update the SoftApCapability
1743      *
1744      * @param softApCapability the current softap capability
1745      * @param context the caller context used to get value from resource file
1746      * @param wifiNative reference used to collect regulatory restrictions.
1747      * @param channelMap the channel for each band
1748      * @return updated soft AP capability
1749      */
1750     public static SoftApCapability updateSoftApCapabilityWithAvailableChannelList(
1751             @NonNull SoftApCapability softApCapability, @NonNull WifiContext context,
1752             @NonNull WifiNative wifiNative, @NonNull SparseArray<int[]> channelMap) {
1753         SoftApCapability newSoftApCapability = new SoftApCapability(softApCapability);
1754         if (channelMap != null) {
1755             for (int band : SoftApConfiguration.BAND_TYPES) {
1756                 if (isSoftApBandSupported(context, band)) {
1757                     int[] supportedChannelList = channelMap.get(band);
1758                     if (supportedChannelList != null) {
1759                         newSoftApCapability.setSupportedChannelList(band, supportedChannelList);
1760                     }
1761                 }
1762             }
1763             return newSoftApCapability;
1764         }
1765         List<Integer> supportedChannelList = null;
1766 
1767         for (int band : SoftApConfiguration.BAND_TYPES) {
1768             if (isSoftApBandSupported(context, band)) {
1769                 supportedChannelList = getAvailableChannelFreqsForBand(
1770                         band, wifiNative, null, false);
1771                 if (supportedChannelList != null) {
1772                     newSoftApCapability.setSupportedChannelList(
1773                             band,
1774                             supportedChannelList.stream().mapToInt(Integer::intValue).toArray());
1775                 }
1776             }
1777         }
1778         return newSoftApCapability;
1779     }
1780 
1781     /**
1782      * Helper function to check if security type can ignore password.
1783      *
1784      * @param security type for SoftApConfiguration.
1785      * @return true for Open/Owe-Transition SoftAp AKM.
1786      */
1787     public static boolean isNonPasswordAP(int security) {
1788         return (security == SoftApConfiguration.SECURITY_TYPE_OPEN
1789                 || security == SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION
1790                 || security == SoftApConfiguration.SECURITY_TYPE_WPA3_OWE);
1791     }
1792 }
1793