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