1 /* 2 * Copyright (C) 2010 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; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.content.IntentFilter; 22 import android.net.MacAddress; 23 import android.net.wifi.SoftApConfiguration; 24 import android.net.wifi.SoftApConfiguration.BandType; 25 import android.os.Handler; 26 import android.os.Process; 27 import android.text.TextUtils; 28 import android.util.Log; 29 import android.util.SparseIntArray; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.modules.utils.build.SdkLevel; 33 import com.android.net.module.util.MacAddressUtils; 34 import com.android.server.wifi.util.ApConfigUtil; 35 import com.android.wifi.resources.R; 36 37 import java.nio.charset.CharsetEncoder; 38 import java.nio.charset.StandardCharsets; 39 import java.security.SecureRandom; 40 import java.util.ArrayList; 41 import java.util.Objects; 42 import java.util.Random; 43 44 import javax.annotation.Nullable; 45 46 /** 47 * Provides API for reading/writing soft access point configuration. 48 */ 49 public class WifiApConfigStore { 50 51 // Intent when user has interacted with the softap settings change notification 52 public static final String ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT = 53 "com.android.server.wifi.WifiApConfigStoreUtil.HOTSPOT_CONFIG_USER_TAPPED_CONTENT"; 54 55 private static final String TAG = "WifiApConfigStore"; 56 57 private static final int RAND_SSID_INT_MIN = 1000; 58 private static final int RAND_SSID_INT_MAX = 9999; 59 60 @VisibleForTesting 61 static final int SSID_MIN_LEN = 1; 62 @VisibleForTesting 63 static final int SSID_MAX_LEN = 32; 64 @VisibleForTesting 65 static final int PSK_MIN_LEN = 8; 66 @VisibleForTesting 67 static final int PSK_MAX_LEN = 63; 68 69 private SoftApConfiguration mPersistentWifiApConfig = null; 70 71 private final Context mContext; 72 private final Handler mHandler; 73 private final WifiMetrics mWifiMetrics; 74 private final BackupManagerProxy mBackupManagerProxy; 75 private final MacAddressUtil mMacAddressUtil; 76 private final WifiConfigManager mWifiConfigManager; 77 private final ActiveModeWarden mActiveModeWarden; 78 private boolean mHasNewDataToSerialize = false; 79 private boolean mForceApChannel = false; 80 private int mForcedApBand; 81 private int mForcedApChannel; 82 83 /** 84 * Module to interact with the wifi config store. 85 */ 86 private class SoftApStoreDataSource implements SoftApStoreData.DataSource { 87 toSerialize()88 public SoftApConfiguration toSerialize() { 89 mHasNewDataToSerialize = false; 90 return mPersistentWifiApConfig; 91 } 92 fromDeserialized(SoftApConfiguration config)93 public void fromDeserialized(SoftApConfiguration config) { 94 mPersistentWifiApConfig = new SoftApConfiguration.Builder(config).build(); 95 } 96 reset()97 public void reset() { 98 mPersistentWifiApConfig = null; 99 } 100 hasNewDataToSerialize()101 public boolean hasNewDataToSerialize() { 102 return mHasNewDataToSerialize; 103 } 104 } 105 WifiApConfigStore(Context context, WifiInjector wifiInjector, Handler handler, BackupManagerProxy backupManagerProxy, WifiConfigStore wifiConfigStore, WifiConfigManager wifiConfigManager, ActiveModeWarden activeModeWarden, WifiMetrics wifiMetrics)106 WifiApConfigStore(Context context, 107 WifiInjector wifiInjector, 108 Handler handler, 109 BackupManagerProxy backupManagerProxy, 110 WifiConfigStore wifiConfigStore, 111 WifiConfigManager wifiConfigManager, 112 ActiveModeWarden activeModeWarden, 113 WifiMetrics wifiMetrics) { 114 mContext = context; 115 mHandler = handler; 116 mBackupManagerProxy = backupManagerProxy; 117 mWifiConfigManager = wifiConfigManager; 118 mActiveModeWarden = activeModeWarden; 119 mWifiMetrics = wifiMetrics; 120 121 // Register store data listener 122 wifiConfigStore.registerStoreData( 123 wifiInjector.makeSoftApStoreData(new SoftApStoreDataSource())); 124 125 IntentFilter filter = new IntentFilter(); 126 filter.addAction(ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT); 127 mMacAddressUtil = wifiInjector.getMacAddressUtil(); 128 } 129 130 /** 131 * Return the current soft access point configuration. 132 */ getApConfiguration()133 public synchronized SoftApConfiguration getApConfiguration() { 134 if (mPersistentWifiApConfig == null) { 135 /* Use default configuration. */ 136 Log.d(TAG, "Fallback to use default AP configuration"); 137 persistConfigAndTriggerBackupManagerProxy(getDefaultApConfiguration()); 138 } 139 SoftApConfiguration sanitizedPersistentconfig = 140 sanitizePersistentApConfig(mPersistentWifiApConfig); 141 if (!Objects.equals(mPersistentWifiApConfig, sanitizedPersistentconfig)) { 142 Log.d(TAG, "persisted config was converted, need to resave it"); 143 persistConfigAndTriggerBackupManagerProxy(sanitizedPersistentconfig); 144 } 145 if (mForceApChannel) { 146 Log.d(TAG, "getApConfiguration: Band force to " + mForcedApBand 147 + ", and channel force to " + mForcedApChannel); 148 return mForcedApChannel == 0 149 ? new SoftApConfiguration.Builder(mPersistentWifiApConfig) 150 .setBand(mForcedApBand).build() 151 : new SoftApConfiguration.Builder(mPersistentWifiApConfig) 152 .setChannel(mForcedApChannel, mForcedApBand).build(); 153 } 154 return mPersistentWifiApConfig; 155 } 156 157 /** 158 * Update the current soft access point configuration. 159 * Restore to default AP configuration if null is provided. 160 * This can be invoked under context of binder threads (WifiManager.setWifiApConfiguration) 161 * and the main Wifi thread (CMD_START_AP). 162 */ setApConfiguration(SoftApConfiguration config)163 public synchronized void setApConfiguration(SoftApConfiguration config) { 164 if (config == null) { 165 config = getDefaultApConfiguration(); 166 } else { 167 config = sanitizePersistentApConfig(config); 168 } 169 persistConfigAndTriggerBackupManagerProxy( 170 new SoftApConfiguration.Builder(config).setUserConfiguration(true).build()); 171 } 172 173 /** 174 * Returns SoftApConfiguration in which some parameters might be upgrade to supported default 175 * configuration. 176 */ upgradeSoftApConfiguration(@onNull SoftApConfiguration config)177 public SoftApConfiguration upgradeSoftApConfiguration(@NonNull SoftApConfiguration config) { 178 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(config); 179 if (SdkLevel.isAtLeastS() && ApConfigUtil.isBridgedModeSupported(mContext) 180 && config.getBands().length == 1) { 181 int[] dual_bands = new int[] { 182 SoftApConfiguration.BAND_2GHZ, 183 SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ}; 184 if (SdkLevel.isAtLeastS()) { 185 configBuilder.setBands(dual_bands); 186 } 187 Log.i(TAG, "Device support bridged AP, upgrade band setting to bridged configuration"); 188 } 189 return configBuilder.build(); 190 } 191 192 /** 193 * Returns SoftApConfiguration in which some parameters might be reset to supported default 194 * config since it depends on UI or HW. 195 * 196 * MaxNumberOfClients and isClientControlByUserEnabled will need HAL support client force 197 * disconnect, and Band setting (5g/6g) need HW support. 198 * 199 * HiddenSsid, Channel, ShutdownTimeoutMillis and AutoShutdownEnabled are features 200 * which need UI(Setting) support. 201 * 202 * SAE/SAE-Transition need hardware support, reset to secured WPA2 security type when device 203 * doesn't support it. 204 * 205 * Check band(s) setting to make sure all of the band(s) are supported. 206 * - If previous bands configuration is bridged mode. Reset to 2.4G when device doesn't support 207 * it. 208 */ resetToDefaultForUnsupportedConfig( @onNull SoftApConfiguration config)209 public SoftApConfiguration resetToDefaultForUnsupportedConfig( 210 @NonNull SoftApConfiguration config) { 211 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(config); 212 if ((!ApConfigUtil.isClientForceDisconnectSupported(mContext) 213 || mContext.getResources().getBoolean( 214 R.bool.config_wifiSoftapResetUserControlConfig)) 215 && (config.isClientControlByUserEnabled() 216 || config.getBlockedClientList().size() != 0)) { 217 configBuilder.setClientControlByUserEnabled(false); 218 configBuilder.setBlockedClientList(new ArrayList<>()); 219 Log.i(TAG, "Reset ClientControlByUser to false due to device doesn't support"); 220 } 221 222 if ((!ApConfigUtil.isClientForceDisconnectSupported(mContext) 223 || mContext.getResources().getBoolean( 224 R.bool.config_wifiSoftapResetMaxClientSettingConfig)) 225 && config.getMaxNumberOfClients() != 0) { 226 configBuilder.setMaxNumberOfClients(0); 227 Log.i(TAG, "Reset MaxNumberOfClients to 0 due to device doesn't support"); 228 } 229 230 if (!ApConfigUtil.isWpa3SaeSupported(mContext) && (config.getSecurityType() 231 == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE 232 || config.getSecurityType() 233 == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)) { 234 configBuilder.setPassphrase(generatePassword(), 235 SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); 236 Log.i(TAG, "Device doesn't support WPA3-SAE, reset config to WPA2"); 237 } 238 239 if (mContext.getResources().getBoolean(R.bool.config_wifiSoftapResetChannelConfig) 240 && config.getChannel() != 0) { 241 // The device might not support customize channel or forced channel might not 242 // work in some countries. Need to reset it. 243 configBuilder.setBand(ApConfigUtil.append24GToBandIf24GSupported( 244 config.getBand(), mContext)); 245 Log.i(TAG, "Reset SAP channel configuration"); 246 } 247 248 if (SdkLevel.isAtLeastS() && config.getBands().length > 1) { 249 if (!ApConfigUtil.isBridgedModeSupported(mContext) 250 || !isBandsSupported(config.getBands(), mContext)) { 251 int newSingleApBand = 0; 252 for (int targetBand : config.getBands()) { 253 int availableBand = ApConfigUtil.removeUnsupportedBands( 254 mContext, targetBand); 255 newSingleApBand |= availableBand; 256 } 257 newSingleApBand = ApConfigUtil.append24GToBandIf24GSupported( 258 newSingleApBand, mContext); 259 configBuilder.setBand(newSingleApBand); 260 Log.i(TAG, "An unsupported band setting for the bridged mode, force to " 261 + newSingleApBand); 262 } 263 } else { 264 // Single band case, check and remove unsupported band. 265 int newBand = ApConfigUtil.removeUnsupportedBands(mContext, config.getBand()); 266 if (newBand != config.getBand()) { 267 newBand = ApConfigUtil.append24GToBandIf24GSupported(newBand, mContext); 268 Log.i(TAG, "Reset band from " + config.getBand() + " to " 269 + newBand); 270 configBuilder.setBand(newBand); 271 } 272 } 273 274 if (mContext.getResources().getBoolean(R.bool.config_wifiSoftapResetHiddenConfig) 275 && config.isHiddenSsid()) { 276 configBuilder.setHiddenSsid(false); 277 Log.i(TAG, "Reset SAP Hidden Network configuration"); 278 } 279 280 if (mContext.getResources().getBoolean( 281 R.bool.config_wifiSoftapResetAutoShutdownTimerConfig) 282 && config.getShutdownTimeoutMillis() != 0) { 283 configBuilder.setShutdownTimeoutMillis(0); 284 Log.i(TAG, "Reset SAP auto shutdown configuration"); 285 } 286 287 if (!ApConfigUtil.isApMacRandomizationSupported(mContext)) { 288 if (SdkLevel.isAtLeastS()) { 289 configBuilder.setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE); 290 Log.i(TAG, "Force set SAP MAC randomization to NONE when not supported"); 291 } 292 } 293 294 mWifiMetrics.noteSoftApConfigReset(config, configBuilder.build()); 295 return configBuilder.build(); 296 } 297 sanitizePersistentApConfig(SoftApConfiguration config)298 private SoftApConfiguration sanitizePersistentApConfig(SoftApConfiguration config) { 299 SoftApConfiguration.Builder convertedConfigBuilder = 300 new SoftApConfiguration.Builder(config); 301 int[] bands = config.getBands(); 302 // The bands length should always 1 in R. Adding SdkLevel.isAtLeastS for lint check only. 303 if (bands.length > 1 && SdkLevel.isAtLeastS()) { 304 // Consider 2.4G instance may be shutdown, i.e. only left 5G instance. If the 5G 305 // configuration is 5G band only, it will cause that driver can't switch channel from 306 // 5G to 2.4G when coexistence happene. Always append 2.4G into band configuration to 307 // allow driver handle coexistence case after 2.4G instance shutdown. 308 SparseIntArray newChannels = new SparseIntArray(); 309 for (int i = 0; i < bands.length; i++) { 310 int channel = config.getChannels().valueAt(i); 311 if (channel == 0 && (bands[i] & SoftApConfiguration.BAND_2GHZ) == 0 312 && ApConfigUtil.isBandSupported(bands[i], mContext)) { 313 newChannels.put(ApConfigUtil.append24GToBandIf24GSupported(bands[i], mContext), 314 0); 315 } else { 316 newChannels.put(bands[i], channel); 317 } 318 } 319 convertedConfigBuilder.setChannels(newChannels); 320 } else if (config.getChannel() == 0 && (bands[0] & SoftApConfiguration.BAND_2GHZ) == 0) { 321 // some countries are unable to support 5GHz only operation, always allow for 2GHz when 322 // config doesn't force channel 323 if (ApConfigUtil.isBandSupported(bands[0], mContext)) { 324 Log.i(TAG, "Supplied ap config band without 2.4G, add allowing for 2.4GHz"); 325 convertedConfigBuilder.setBand( 326 ApConfigUtil.append24GToBandIf24GSupported(bands[0], mContext)); 327 } 328 } 329 return convertedConfigBuilder.build(); 330 } 331 persistConfigAndTriggerBackupManagerProxy(SoftApConfiguration config)332 private void persistConfigAndTriggerBackupManagerProxy(SoftApConfiguration config) { 333 mPersistentWifiApConfig = config; 334 mHasNewDataToSerialize = true; 335 mWifiConfigManager.saveToStore(true); 336 mBackupManagerProxy.notifyDataChanged(); 337 } 338 339 /** 340 * Generate a default WPA3 SAE transition (if supported) or WPA2 based 341 * configuration with a random password. 342 * We are changing the Wifi Ap configuration storage from secure settings to a 343 * flat file accessible only by the system. A WPA2 based default configuration 344 * will keep the device secure after the update. 345 */ getDefaultApConfiguration()346 private SoftApConfiguration getDefaultApConfiguration() { 347 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); 348 configBuilder.setBand(generateDefaultBand(mContext)); 349 configBuilder.setSsid(mContext.getResources().getString( 350 R.string.wifi_tether_configure_ssid_default) + "_" + getRandomIntForDefaultSsid()); 351 if (ApConfigUtil.isWpa3SaeSupported(mContext)) { 352 configBuilder.setPassphrase(generatePassword(), 353 SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION); 354 } else { 355 configBuilder.setPassphrase(generatePassword(), 356 SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); 357 } 358 359 // It is new overlay configuration, it should always false in R. Add SdkLevel.isAtLeastS for 360 // lint check 361 if (ApConfigUtil.isBridgedModeSupported(mContext)) { 362 if (SdkLevel.isAtLeastS()) { 363 int[] dual_bands = new int[] { 364 SoftApConfiguration.BAND_2GHZ, 365 SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ}; 366 configBuilder.setBands(dual_bands); 367 } 368 } 369 370 // Update default MAC randomization setting to NONE when feature doesn't support it. 371 if (!ApConfigUtil.isApMacRandomizationSupported(mContext)) { 372 if (SdkLevel.isAtLeastS()) { 373 configBuilder.setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE); 374 } 375 } 376 377 configBuilder.setUserConfiguration(false); 378 return configBuilder.build(); 379 } 380 getRandomIntForDefaultSsid()381 private static int getRandomIntForDefaultSsid() { 382 Random random = new Random(); 383 return random.nextInt((RAND_SSID_INT_MAX - RAND_SSID_INT_MIN) + 1) + RAND_SSID_INT_MIN; 384 } 385 generateLohsSsid(Context context)386 private static String generateLohsSsid(Context context) { 387 return context.getResources().getString( 388 R.string.wifi_localhotspot_configure_ssid_default) + "_" 389 + getRandomIntForDefaultSsid(); 390 } 391 392 /** 393 * Generate a temporary WPA2 based configuration for use by the local only hotspot. 394 * This config is not persisted and will not be stored by the WifiApConfigStore. 395 */ generateLocalOnlyHotspotConfig(Context context, int apBand, @Nullable SoftApConfiguration customConfig)396 public SoftApConfiguration generateLocalOnlyHotspotConfig(Context context, int apBand, 397 @Nullable SoftApConfiguration customConfig) { 398 SoftApConfiguration.Builder configBuilder; 399 if (customConfig != null) { 400 configBuilder = new SoftApConfiguration.Builder(customConfig); 401 } else { 402 configBuilder = new SoftApConfiguration.Builder(); 403 // Default to disable the auto shutdown 404 configBuilder.setAutoShutdownEnabled(false); 405 } 406 407 configBuilder.setBand(apBand); 408 409 if (customConfig == null || customConfig.getSsid() == null) { 410 configBuilder.setSsid(generateLohsSsid(context)); 411 } 412 if (customConfig == null) { 413 if (ApConfigUtil.isWpa3SaeSupported(context)) { 414 configBuilder.setPassphrase(generatePassword(), 415 SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION); 416 } else { 417 configBuilder.setPassphrase(generatePassword(), 418 SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); 419 } 420 } 421 422 // Update default MAC randomization setting to NONE when feature doesn't support it or 423 // It was disabled in tethered mode. 424 if (!ApConfigUtil.isApMacRandomizationSupported(context) || (mPersistentWifiApConfig != null 425 && mPersistentWifiApConfig.getMacRandomizationSettingInternal() 426 == SoftApConfiguration.RANDOMIZATION_NONE)) { 427 if (SdkLevel.isAtLeastS()) { 428 configBuilder.setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE); 429 } 430 } 431 432 return configBuilder.build(); 433 } 434 435 /** 436 * @return a copy of the given SoftApConfig with the BSSID randomized, unless a custom BSSID is 437 * already set. 438 */ randomizeBssidIfUnset(Context context, SoftApConfiguration config)439 SoftApConfiguration randomizeBssidIfUnset(Context context, SoftApConfiguration config) { 440 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(config); 441 if (config.getBssid() == null && ApConfigUtil.isApMacRandomizationSupported(mContext)) { 442 if (config.getMacRandomizationSettingInternal() 443 == SoftApConfiguration.RANDOMIZATION_NONE) { 444 return configBuilder.build(); 445 } 446 447 MacAddress macAddress = mMacAddressUtil.calculatePersistentMac(config.getSsid(), 448 mMacAddressUtil.obtainMacRandHashFunctionForSap(Process.WIFI_UID)); 449 if (macAddress == null) { 450 Log.e(TAG, "Failed to calculate MAC from SSID. " 451 + "Generating new random MAC instead."); 452 macAddress = MacAddressUtils.createRandomUnicastAddress(); 453 } 454 configBuilder.setBssid(macAddress); 455 } 456 return configBuilder.build(); 457 } 458 459 /** 460 * Verify provided SSID for existence, length and conversion to bytes 461 * 462 * @param ssid String ssid name 463 * @return boolean indicating ssid met requirements 464 */ validateApConfigSsid(String ssid)465 private static boolean validateApConfigSsid(String ssid) { 466 if (TextUtils.isEmpty(ssid)) { 467 Log.d(TAG, "SSID for softap configuration must be set."); 468 return false; 469 } 470 471 try { 472 byte[] ssid_bytes = ssid.getBytes(StandardCharsets.UTF_8); 473 474 if (ssid_bytes.length < SSID_MIN_LEN || ssid_bytes.length > SSID_MAX_LEN) { 475 Log.d(TAG, "softap SSID is defined as UTF-8 and it must be at least " 476 + SSID_MIN_LEN + " byte and not more than " + SSID_MAX_LEN + " bytes"); 477 return false; 478 } 479 } catch (IllegalArgumentException e) { 480 Log.e(TAG, "softap config SSID verification failed: malformed string " + ssid); 481 return false; 482 } 483 return true; 484 } 485 486 /** 487 * Verify provided preSharedKey in ap config for WPA2_PSK network meets requirements. 488 */ validateApConfigPreSharedKey(String preSharedKey)489 private static boolean validateApConfigPreSharedKey(String preSharedKey) { 490 if (preSharedKey.length() < PSK_MIN_LEN || preSharedKey.length() > PSK_MAX_LEN) { 491 Log.d(TAG, "softap network password string size must be at least " + PSK_MIN_LEN 492 + " and no more than " + PSK_MAX_LEN); 493 return false; 494 } 495 496 try { 497 preSharedKey.getBytes(StandardCharsets.UTF_8); 498 } catch (IllegalArgumentException e) { 499 Log.e(TAG, "softap network password verification failed: malformed string"); 500 return false; 501 } 502 return true; 503 } 504 505 /** 506 * Validate a SoftApConfiguration is properly configured for use by SoftApManager. 507 * 508 * This method checks the length of the SSID and for consistency between security settings (if 509 * it requires a password, was one provided?). 510 * 511 * @param apConfig {@link SoftApConfiguration} to use for softap mode 512 * @param isPrivileged indicate the caller can pass some fields check or not 513 * @return boolean true if the provided config meets the minimum set of details, false 514 * otherwise. 515 */ validateApWifiConfiguration(@onNull SoftApConfiguration apConfig, boolean isPrivileged, Context context)516 static boolean validateApWifiConfiguration(@NonNull SoftApConfiguration apConfig, 517 boolean isPrivileged, Context context) { 518 // first check the SSID 519 if (!validateApConfigSsid(apConfig.getSsid())) { 520 // failed SSID verificiation checks 521 return false; 522 } 523 524 // BSSID can be set if caller own permission:android.Manifest.permission.NETWORK_SETTINGS. 525 if (apConfig.getBssid() != null && !isPrivileged) { 526 Log.e(TAG, "Config BSSID needs NETWORK_SETTINGS permission"); 527 return false; 528 } 529 530 String preSharedKey = apConfig.getPassphrase(); 531 boolean hasPreSharedKey = !TextUtils.isEmpty(preSharedKey); 532 int authType; 533 534 try { 535 authType = apConfig.getSecurityType(); 536 } catch (IllegalStateException e) { 537 Log.d(TAG, "Unable to get AuthType for softap config: " + e.getMessage()); 538 return false; 539 } 540 541 if (authType == SoftApConfiguration.SECURITY_TYPE_OPEN) { 542 // open networks should not have a password 543 if (hasPreSharedKey) { 544 Log.d(TAG, "open softap network should not have a password"); 545 return false; 546 } 547 } else if (authType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK 548 || authType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION 549 || authType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) { 550 // this is a config that should have a password - check that first 551 if (!hasPreSharedKey) { 552 Log.d(TAG, "softap network password must be set"); 553 return false; 554 } 555 556 if (context.getResources().getBoolean( 557 R.bool.config_wifiSoftapPassphraseAsciiEncodableCheck)) { 558 final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); 559 if (!asciiEncoder.canEncode(preSharedKey)) { 560 Log.d(TAG, "passphrase not ASCII encodable"); 561 return false; 562 } 563 } 564 565 if (authType != SoftApConfiguration.SECURITY_TYPE_WPA3_SAE 566 && !validateApConfigPreSharedKey(preSharedKey)) { 567 // failed preSharedKey checks for WPA2 and WPA3 SAE Transition mode. 568 return false; 569 } 570 } else { 571 // this is not a supported security type 572 Log.d(TAG, "softap configs must either be open or WPA2 PSK networks"); 573 return false; 574 } 575 576 if (SdkLevel.isAtLeastS()) { 577 if (!isBandsSupported(apConfig.getBands(), context)) { 578 return false; 579 } 580 } else { 581 if (!ApConfigUtil.isBandSupported(apConfig.getBand(), context)) { 582 return false; 583 } 584 } 585 586 return true; 587 } 588 generatePassword()589 private static String generatePassword() { 590 // Characters that will be used for password generation. Some characters commonly known to 591 // be confusing like 0 and O excluded from this list. 592 final String allowed = "23456789abcdefghijkmnpqrstuvwxyz"; 593 final int passLength = 15; 594 595 StringBuilder sb = new StringBuilder(passLength); 596 SecureRandom random = new SecureRandom(); 597 for (int i = 0; i < passLength; i++) { 598 sb.append(allowed.charAt(random.nextInt(allowed.length()))); 599 } 600 return sb.toString(); 601 } 602 603 /** 604 * Generate default band base on supported band configuration. 605 * 606 * @param context The caller context used to get value from resource file. 607 * @return A band which will be used for a default band in default configuration. 608 */ generateDefaultBand(Context context)609 public static @BandType int generateDefaultBand(Context context) { 610 for (int band : SoftApConfiguration.BAND_TYPES) { 611 if (ApConfigUtil.isBandSupported(band, context)) { 612 return band; 613 } 614 } 615 Log.e(TAG, "Invalid overlay configuration! No any band supported on SoftAp"); 616 return SoftApConfiguration.BAND_2GHZ; 617 } 618 isBandsSupported(@onNull int[] apBands, Context context)619 private static boolean isBandsSupported(@NonNull int[] apBands, Context context) { 620 for (int band : apBands) { 621 if (!ApConfigUtil.isBandSupported(band, context)) { 622 return false; 623 } 624 } 625 return true; 626 } 627 628 /** 629 * Enable force-soft-AP-channel mode which takes effect when soft AP starts next time 630 * 631 * @param forcedApBand The forced band. 632 * @param forcedApChannel The forced IEEE channel number or 0 when forced AP band only. 633 */ enableForceSoftApBandOrChannel(@andType int forcedApBand, int forcedApChannel)634 public void enableForceSoftApBandOrChannel(@BandType int forcedApBand, int forcedApChannel) { 635 mForceApChannel = true; 636 mForcedApChannel = forcedApChannel; 637 mForcedApBand = forcedApBand; 638 } 639 640 /** 641 * Disable force-soft-AP-channel mode which take effect when soft AP starts next time 642 */ disableForceSoftApBandOrChannel()643 public void disableForceSoftApBandOrChannel() { 644 mForceApChannel = false; 645 } 646 } 647