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