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