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 static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA2_PSK; 20 import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION; 21 import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE; 22 import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION; 23 24 import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_STATIC_CHIP_INFO; 25 26 import android.annotation.NonNull; 27 import android.app.compat.CompatChanges; 28 import android.content.Context; 29 import android.content.IntentFilter; 30 import android.content.pm.PackageManager; 31 import android.net.MacAddress; 32 import android.net.wifi.SoftApCapability; 33 import android.net.wifi.SoftApConfiguration; 34 import android.net.wifi.SoftApConfiguration.BandType; 35 import android.net.wifi.WifiSsid; 36 import android.os.Handler; 37 import android.os.Process; 38 import android.text.TextUtils; 39 import android.util.Log; 40 import android.util.SparseIntArray; 41 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.modules.utils.build.SdkLevel; 44 import com.android.net.module.util.MacAddressUtils; 45 import com.android.server.wifi.util.ApConfigUtil; 46 import com.android.wifi.resources.R; 47 48 import java.nio.charset.CharsetEncoder; 49 import java.nio.charset.StandardCharsets; 50 import java.security.SecureRandom; 51 import java.util.ArrayList; 52 import java.util.Objects; 53 import java.util.Random; 54 55 import javax.annotation.Nullable; 56 57 /** 58 * Provides API for reading/writing soft access point configuration. 59 */ 60 public class WifiApConfigStore { 61 62 // Intent when user has interacted with the softap settings change notification 63 public static final String ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT = 64 "com.android.server.wifi.WifiApConfigStoreUtil.HOTSPOT_CONFIG_USER_TAPPED_CONTENT"; 65 66 private static final String TAG = "WifiApConfigStore"; 67 68 private static final int RAND_SSID_INT_MIN = 1000; 69 private static final int RAND_SSID_INT_MAX = 9999; 70 71 @VisibleForTesting 72 static final int SAE_ASCII_MIN_LEN = 1; 73 @VisibleForTesting 74 static final int PSK_ASCII_MIN_LEN = 8; 75 @VisibleForTesting 76 static final int PSK_SAE_ASCII_MAX_LEN = 63; 77 78 // Should only be accessed via synchronized methods. 79 private SoftApConfiguration mPersistentWifiApConfig = null; 80 private String mLastConfiguredPassphrase = null; 81 82 private final Context mContext; 83 private final Handler mHandler; 84 private final WifiMetrics mWifiMetrics; 85 private final BackupManagerProxy mBackupManagerProxy; 86 private final MacAddressUtil mMacAddressUtil; 87 private final WifiConfigManager mWifiConfigManager; 88 private final ActiveModeWarden mActiveModeWarden; 89 private final WifiNative mWifiNative; 90 private final HalDeviceManager mHalDeviceManager; 91 private final WifiSettingsConfigStore mWifiSettingsConfigStore; 92 private boolean mHasNewDataToSerialize = false; 93 private boolean mForceApChannel = false; 94 private int mForcedApBand; 95 private int mForcedApChannel; 96 private int mForcedApMaximumChannelBandWidth; 97 private final boolean mIsAutoAppendLowerBandEnabled; 98 99 /** 100 * Module to interact with the wifi config store. 101 */ 102 private class SoftApStoreDataSource implements SoftApStoreData.DataSource { 103 toSerialize()104 public SoftApConfiguration toSerialize() { 105 mHasNewDataToSerialize = false; 106 return mPersistentWifiApConfig; 107 } 108 fromDeserialized(SoftApConfiguration config)109 public void fromDeserialized(SoftApConfiguration config) { 110 if (config.getPersistentRandomizedMacAddress() == null) { 111 config = updatePersistentRandomizedMacAddress(config); 112 } 113 mPersistentWifiApConfig = new SoftApConfiguration.Builder(config).build(); 114 if (!TextUtils.isEmpty(mPersistentWifiApConfig.getPassphrase())) { 115 mLastConfiguredPassphrase = mPersistentWifiApConfig.getPassphrase(); 116 } 117 } 118 reset()119 public void reset() { 120 mPersistentWifiApConfig = null; 121 } 122 hasNewDataToSerialize()123 public boolean hasNewDataToSerialize() { 124 return mHasNewDataToSerialize; 125 } 126 } 127 WifiApConfigStore(Context context, WifiInjector wifiInjector, Handler handler, BackupManagerProxy backupManagerProxy, WifiConfigStore wifiConfigStore, WifiConfigManager wifiConfigManager, ActiveModeWarden activeModeWarden, WifiMetrics wifiMetrics)128 WifiApConfigStore(Context context, 129 WifiInjector wifiInjector, 130 Handler handler, 131 BackupManagerProxy backupManagerProxy, 132 WifiConfigStore wifiConfigStore, 133 WifiConfigManager wifiConfigManager, 134 ActiveModeWarden activeModeWarden, 135 WifiMetrics wifiMetrics) { 136 mContext = context; 137 mHandler = handler; 138 mBackupManagerProxy = backupManagerProxy; 139 mWifiConfigManager = wifiConfigManager; 140 mActiveModeWarden = activeModeWarden; 141 mWifiMetrics = wifiMetrics; 142 mWifiNative = wifiInjector.getWifiNative(); 143 // Register store data listener 144 wifiConfigStore.registerStoreData( 145 wifiInjector.makeSoftApStoreData(new SoftApStoreDataSource())); 146 147 IntentFilter filter = new IntentFilter(); 148 filter.addAction(ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT); 149 mMacAddressUtil = wifiInjector.getMacAddressUtil(); 150 mIsAutoAppendLowerBandEnabled = mContext.getResources().getBoolean( 151 R.bool.config_wifiSoftapAutoAppendLowerBandsToBandConfigurationEnabled); 152 mHalDeviceManager = wifiInjector.getHalDeviceManager(); 153 mWifiSettingsConfigStore = wifiInjector.getSettingsConfigStore(); 154 mWifiSettingsConfigStore.registerChangeListener(WIFI_STATIC_CHIP_INFO, 155 (key, value) -> { 156 if (mPersistentWifiApConfig != null 157 && mHalDeviceManager.isConcurrencyComboLoadedFromDriver()) { 158 Log.i(TAG, "Chip capability is updated, check config"); 159 SoftApConfiguration.Builder configBuilder = 160 new SoftApConfiguration.Builder(mPersistentWifiApConfig); 161 if (SdkLevel.isAtLeastS() 162 && mPersistentWifiApConfig.getBands().length > 1) { 163 // Current band setting is dual band, check if device supports it. 164 if (!ApConfigUtil.isBridgedModeSupported(mContext, mWifiNative)) { 165 Log.i(TAG, "Chip doesn't support bridgedAp, reset to default band"); 166 configBuilder.setBand(generateDefaultBand(mContext)); 167 persistConfigAndTriggerBackupManagerProxy(configBuilder.build()); 168 } 169 } 170 } 171 }, mHandler); 172 } 173 174 /** 175 * Return the current soft access point configuration. 176 */ getApConfiguration()177 public synchronized SoftApConfiguration getApConfiguration() { 178 if (mPersistentWifiApConfig == null) { 179 /* Use default configuration. */ 180 Log.d(TAG, "Fallback to use default AP configuration"); 181 persistConfigAndTriggerBackupManagerProxy( 182 updatePersistentRandomizedMacAddress(getDefaultApConfiguration())); 183 } 184 SoftApConfiguration sanitizedPersistentconfig = 185 sanitizePersistentApConfig(mPersistentWifiApConfig); 186 if (!Objects.equals(mPersistentWifiApConfig, sanitizedPersistentconfig)) { 187 Log.d(TAG, "persisted config was converted, need to resave it"); 188 persistConfigAndTriggerBackupManagerProxy(sanitizedPersistentconfig); 189 } 190 191 if (mForceApChannel) { 192 Log.d(TAG, "getApConfiguration: Band force to " 193 + mForcedApBand 194 + ", and channel force to " 195 + mForcedApChannel 196 + ", and maximum channel width limited to " 197 + mForcedApMaximumChannelBandWidth); 198 if (SdkLevel.isAtLeastT()) { 199 return mForcedApChannel == 0 200 ? new SoftApConfiguration.Builder(mPersistentWifiApConfig) 201 .setBand(mForcedApBand) 202 .setMaxChannelBandwidth(mForcedApMaximumChannelBandWidth) 203 .build() 204 : new SoftApConfiguration.Builder(mPersistentWifiApConfig) 205 .setChannel(mForcedApChannel, mForcedApBand) 206 .setMaxChannelBandwidth(mForcedApMaximumChannelBandWidth) 207 .build(); 208 } else { 209 return mForcedApChannel == 0 210 ? new SoftApConfiguration.Builder(mPersistentWifiApConfig) 211 .setBand(mForcedApBand) 212 .build() 213 : new SoftApConfiguration.Builder(mPersistentWifiApConfig) 214 .setChannel(mForcedApChannel, mForcedApBand) 215 .build(); 216 } 217 } 218 return mPersistentWifiApConfig; 219 } 220 221 /** 222 * Update the current soft access point configuration. 223 * Restore to default AP configuration if null is provided. 224 * This can be invoked under context of binder threads (WifiManager.setWifiApConfiguration) 225 * and the main Wifi thread (CMD_START_AP). 226 */ setApConfiguration(SoftApConfiguration config)227 public synchronized void setApConfiguration(SoftApConfiguration config) { 228 SoftApConfiguration newConfig = config == null ? getDefaultApConfiguration() 229 : new SoftApConfiguration.Builder(sanitizePersistentApConfig(config)) 230 .setUserConfiguration(true).build(); 231 persistConfigAndTriggerBackupManagerProxy( 232 updatePersistentRandomizedMacAddress(newConfig)); 233 } 234 235 /** 236 * Returns SoftApConfiguration in which some parameters might be upgrade to supported default 237 * configuration. 238 */ upgradeSoftApConfiguration( @onNull SoftApConfiguration config)239 public synchronized SoftApConfiguration upgradeSoftApConfiguration( 240 @NonNull SoftApConfiguration config) { 241 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(config); 242 if (SdkLevel.isAtLeastS() && ApConfigUtil.isBridgedModeSupported(mContext, mWifiNative) 243 && config.getBands().length == 1 && mContext.getResources().getBoolean( 244 R.bool.config_wifiSoftapAutoUpgradeToBridgedConfigWhenSupported)) { 245 int[] dual_bands = new int[] { 246 SoftApConfiguration.BAND_2GHZ, 247 SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ}; 248 if (SdkLevel.isAtLeastS()) { 249 configBuilder.setBands(dual_bands); 250 } 251 Log.i(TAG, "Device support bridged AP, upgrade band setting to bridged configuration"); 252 } 253 return configBuilder.build(); 254 } 255 256 /** 257 * Returns SoftApConfiguration in which some parameters might be reset to supported default 258 * config since it depends on UI or HW. 259 * 260 * MaxNumberOfClients and isClientControlByUserEnabled will need HAL support client force 261 * disconnect, and Band setting (5g/6g) need HW support. 262 * 263 * HiddenSsid, Channel, ShutdownTimeoutMillis and AutoShutdownEnabled are features 264 * which need UI(Setting) support. 265 * 266 * SAE/SAE-Transition need hardware support, reset to secured WPA2 security type when device 267 * doesn't support it. 268 * 269 * Check band(s) setting to make sure all of the band(s) are supported. 270 * - If previous bands configuration is bridged mode. Reset to 2.4G when device doesn't support 271 * it. 272 */ resetToDefaultForUnsupportedConfig( @onNull SoftApConfiguration config)273 public synchronized SoftApConfiguration resetToDefaultForUnsupportedConfig( 274 @NonNull SoftApConfiguration config) { 275 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(config); 276 if ((!ApConfigUtil.isClientForceDisconnectSupported(mContext) 277 || mContext.getResources().getBoolean( 278 R.bool.config_wifiSoftapResetUserControlConfig)) 279 && (config.isClientControlByUserEnabled() 280 || config.getBlockedClientList().size() != 0)) { 281 configBuilder.setClientControlByUserEnabled(false); 282 configBuilder.setBlockedClientList(new ArrayList<>()); 283 Log.i(TAG, "Reset ClientControlByUser to false due to device doesn't support"); 284 } 285 286 if ((!ApConfigUtil.isClientForceDisconnectSupported(mContext) 287 || mContext.getResources().getBoolean( 288 R.bool.config_wifiSoftapResetMaxClientSettingConfig)) 289 && config.getMaxNumberOfClients() != 0) { 290 configBuilder.setMaxNumberOfClients(0); 291 Log.i(TAG, "Reset MaxNumberOfClients to 0 due to device doesn't support"); 292 } 293 294 if (!ApConfigUtil.isWpa3SaeSupported(mContext) && (config.getSecurityType() 295 == SECURITY_TYPE_WPA3_SAE 296 || config.getSecurityType() 297 == SECURITY_TYPE_WPA3_SAE_TRANSITION)) { 298 try { 299 configBuilder.setPassphrase(generatePassword(), 300 SECURITY_TYPE_WPA2_PSK); 301 } catch (IllegalArgumentException e) { 302 Log.wtf(TAG, "Generated password was invalid: " + e); 303 } 304 Log.i(TAG, "Device doesn't support WPA3-SAE, reset config to WPA2"); 305 } 306 307 if (mContext.getResources().getBoolean(R.bool.config_wifiSoftapResetChannelConfig) 308 && config.getChannel() != 0) { 309 // The device might not support customize channel or forced channel might not 310 // work in some countries. Need to reset it. 311 configBuilder.setBand(ApConfigUtil.append24GToBandIf24GSupported( 312 config.getBand(), mContext)); 313 Log.i(TAG, "Reset SAP channel configuration"); 314 } 315 316 if (SdkLevel.isAtLeastS() && config.getBands().length > 1) { 317 if (!ApConfigUtil.isBridgedModeSupported(mContext, mWifiNative) 318 || !isBandsSupported(config.getBands(), mContext)) { 319 int newSingleApBand = 0; 320 for (int targetBand : config.getBands()) { 321 int availableBand = ApConfigUtil.removeUnsupportedBands( 322 mContext, targetBand); 323 newSingleApBand |= availableBand; 324 } 325 newSingleApBand = ApConfigUtil.append24GToBandIf24GSupported( 326 newSingleApBand, mContext); 327 configBuilder.setBand(newSingleApBand); 328 Log.i(TAG, "An unsupported band setting for the bridged mode, force to " 329 + newSingleApBand); 330 } 331 } else { 332 // Single band case, check and remove unsupported band. 333 int newBand = ApConfigUtil.removeUnsupportedBands(mContext, config.getBand()); 334 if (newBand != config.getBand()) { 335 newBand = ApConfigUtil.append24GToBandIf24GSupported(newBand, mContext); 336 Log.i(TAG, "Reset band from " + config.getBand() + " to " 337 + newBand); 338 configBuilder.setBand(newBand); 339 } 340 } 341 342 if (mContext.getResources().getBoolean(R.bool.config_wifiSoftapResetHiddenConfig) 343 && config.isHiddenSsid()) { 344 configBuilder.setHiddenSsid(false); 345 Log.i(TAG, "Reset SAP Hidden Network configuration"); 346 } 347 348 if (mContext.getResources().getBoolean( 349 R.bool.config_wifiSoftapResetAutoShutdownTimerConfig) 350 && config.getShutdownTimeoutMillis() > 0) { 351 if (CompatChanges.isChangeEnabled( 352 SoftApConfiguration.REMOVE_ZERO_FOR_TIMEOUT_SETTING)) { 353 configBuilder.setShutdownTimeoutMillis(SoftApConfiguration.DEFAULT_TIMEOUT); 354 } else { 355 configBuilder.setShutdownTimeoutMillis(0); 356 } 357 Log.i(TAG, "Reset SAP auto shutdown configuration"); 358 } 359 360 if (!ApConfigUtil.isApMacRandomizationSupported(mContext)) { 361 if (SdkLevel.isAtLeastS()) { 362 configBuilder.setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE); 363 Log.i(TAG, "Force set SAP MAC randomization to NONE when not supported"); 364 } 365 } 366 367 mWifiMetrics.noteSoftApConfigReset(config, configBuilder.build()); 368 return configBuilder.build(); 369 } 370 sanitizePersistentApConfig(SoftApConfiguration config)371 private SoftApConfiguration sanitizePersistentApConfig(SoftApConfiguration config) { 372 SoftApConfiguration.Builder convertedConfigBuilder = 373 new SoftApConfiguration.Builder(config); 374 int[] bands = config.getBands(); 375 SparseIntArray newChannels = new SparseIntArray(); 376 // The bands length should always 1 in R. Adding SdkLevel.isAtLeastS for lint check only. 377 for (int i = 0; i < bands.length; i++) { 378 int channel = SdkLevel.isAtLeastS() 379 ? config.getChannels().valueAt(i) : config.getChannel(); 380 int newBand = bands[i]; 381 if (channel == 0 && mIsAutoAppendLowerBandEnabled 382 && ApConfigUtil.isBandSupported(newBand, mContext)) { 383 // some countries are unable to support 5GHz only operation, always allow for 2GHz 384 // when config doesn't force channel 385 if ((newBand & SoftApConfiguration.BAND_2GHZ) == 0) { 386 newBand = ApConfigUtil.append24GToBandIf24GSupported(newBand, mContext); 387 } 388 // If the 6G configuration doesn't includes 5G band (2.4G have appended because 389 // countries reason), it will cause that driver can't switch channel from 6G to 390 // 5G/2.4G when coexistence happened (For instance: wifi connected to 2.4G or 5G 391 // channel). Always append 5G into band configuration when configured band includes 392 // 6G. 393 if ((newBand & SoftApConfiguration.BAND_6GHZ) != 0 394 && (newBand & SoftApConfiguration.BAND_5GHZ) == 0) { 395 newBand = ApConfigUtil.append5GToBandIf5GSupported(newBand, mContext); 396 } 397 } 398 newChannels.put(newBand, channel); 399 } 400 if (SdkLevel.isAtLeastS()) { 401 convertedConfigBuilder.setChannels(newChannels); 402 } else if (bands.length > 0 && newChannels.valueAt(0) == 0) { 403 convertedConfigBuilder.setBand(newChannels.keyAt(0)); 404 } 405 return convertedConfigBuilder.build(); 406 } 407 persistConfigAndTriggerBackupManagerProxy( SoftApConfiguration config)408 private synchronized void persistConfigAndTriggerBackupManagerProxy( 409 SoftApConfiguration config) { 410 mPersistentWifiApConfig = config; 411 if (!TextUtils.isEmpty(config.getPassphrase())) { 412 mLastConfiguredPassphrase = config.getPassphrase(); 413 } 414 mHasNewDataToSerialize = true; 415 mHandler.post(() -> mWifiConfigManager.saveToStore()); 416 mBackupManagerProxy.notifyDataChanged(); 417 } 418 419 /** 420 * Generate a default WPA3 SAE transition (if supported) or WPA2 based 421 * configuration with a random password. 422 * We are changing the Wifi Ap configuration storage from secure settings to a 423 * flat file accessible only by the system. A WPA2 based default configuration 424 * will keep the device secure after the update. 425 */ getDefaultApConfiguration()426 private SoftApConfiguration getDefaultApConfiguration() { 427 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); 428 configBuilder.setBand(generateDefaultBand(mContext)); 429 configBuilder.setSsid(mContext.getResources().getString( 430 R.string.wifi_tether_configure_ssid_default) + "_" + getRandomIntForDefaultSsid()); 431 try { 432 if (ApConfigUtil.isWpa3SaeSupported(mContext)) { 433 configBuilder.setPassphrase(generatePassword(), 434 SECURITY_TYPE_WPA3_SAE_TRANSITION); 435 } else { 436 configBuilder.setPassphrase(generatePassword(), 437 SECURITY_TYPE_WPA2_PSK); 438 } 439 } catch (IllegalArgumentException e) { 440 Log.wtf(TAG, "Generated password was invalid: " + e); 441 } 442 443 // It is new overlay configuration, it should always false in R. Add SdkLevel.isAtLeastS for 444 // lint check 445 if (SdkLevel.isAtLeastS()) { 446 boolean isBridgedModeSupported = mHalDeviceManager.isConcurrencyComboLoadedFromDriver() 447 ? ApConfigUtil.isBridgedModeSupported(mContext, mWifiNative) 448 : ApConfigUtil.isBridgedModeSupportedInConfig(mContext); 449 if (isBridgedModeSupported) { 450 int[] dual_bands = new int[] { 451 SoftApConfiguration.BAND_2GHZ, 452 SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ}; 453 configBuilder.setBands(dual_bands); 454 } 455 } 456 457 // Update default MAC randomization setting to NONE when feature doesn't support it. 458 if (!ApConfigUtil.isApMacRandomizationSupported(mContext)) { 459 if (SdkLevel.isAtLeastS()) { 460 configBuilder.setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE); 461 } 462 } 463 464 configBuilder.setUserConfiguration(false); 465 return configBuilder.build(); 466 } 467 getRandomIntForDefaultSsid()468 private static int getRandomIntForDefaultSsid() { 469 Random random = new Random(); 470 return random.nextInt((RAND_SSID_INT_MAX - RAND_SSID_INT_MIN) + 1) + RAND_SSID_INT_MIN; 471 } 472 generateLohsSsid(Context context)473 private static String generateLohsSsid(Context context) { 474 return context.getResources().getString( 475 R.string.wifi_localhotspot_configure_ssid_default) + "_" 476 + getRandomIntForDefaultSsid(); 477 } 478 hasAutomotiveFeature(Context context)479 private static boolean hasAutomotiveFeature(Context context) { 480 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); 481 } 482 483 /** 484 * Generate a temporary WPA2 based configuration for use by the local only hotspot. 485 * This config is not persisted and will not be stored by the WifiApConfigStore. 486 */ generateLocalOnlyHotspotConfig(@onNull Context context, @Nullable SoftApConfiguration customConfig, @NonNull SoftApCapability capability)487 public SoftApConfiguration generateLocalOnlyHotspotConfig(@NonNull Context context, 488 @Nullable SoftApConfiguration customConfig, @NonNull SoftApCapability capability) { 489 SoftApConfiguration.Builder configBuilder; 490 if (customConfig != null) { 491 configBuilder = new SoftApConfiguration.Builder(customConfig); 492 // Make sure that we use available band on old build. 493 if (!SdkLevel.isAtLeastT() 494 && !isBandsSupported(customConfig.getBands(), context)) { 495 configBuilder.setBand(generateDefaultBand(context)); 496 } 497 } else { 498 configBuilder = new SoftApConfiguration.Builder(); 499 // Make sure the default band configuration is supported. 500 configBuilder.setBand(generateDefaultBand(context)); 501 // Default to disable the auto shutdown 502 configBuilder.setAutoShutdownEnabled(false); 503 try { 504 if (ApConfigUtil.isWpa3SaeSupported(context)) { 505 configBuilder.setPassphrase(generatePassword(), 506 SECURITY_TYPE_WPA3_SAE_TRANSITION); 507 } else { 508 configBuilder.setPassphrase(generatePassword(), 509 SECURITY_TYPE_WPA2_PSK); 510 } 511 } catch (IllegalArgumentException e) { 512 Log.wtf(TAG, "Generated password was invalid: " + e); 513 } 514 synchronized (this) { 515 // Update default MAC randomization setting to NONE when feature doesn't support 516 // it, or it was disabled in tethered mode. 517 if (!ApConfigUtil.isApMacRandomizationSupported(context) 518 || (mPersistentWifiApConfig != null 519 && mPersistentWifiApConfig.getMacRandomizationSettingInternal() 520 == SoftApConfiguration.RANDOMIZATION_NONE)) { 521 if (SdkLevel.isAtLeastS()) { 522 configBuilder.setMacRandomizationSetting( 523 SoftApConfiguration.RANDOMIZATION_NONE); 524 } 525 } 526 } 527 } 528 529 // Automotive mode can force the LOHS to specific bands 530 if (hasAutomotiveFeature(context)) { 531 int desiredBand = SoftApConfiguration.BAND_2GHZ; 532 if (context.getResources().getBoolean(R.bool.config_wifiLocalOnlyHotspot6ghz) 533 && ApConfigUtil.isBandSupported(SoftApConfiguration.BAND_6GHZ, mContext)) { 534 desiredBand |= SoftApConfiguration.BAND_6GHZ; 535 } 536 if (context.getResources().getBoolean(R.bool.config_wifi_local_only_hotspot_5ghz) 537 && ApConfigUtil.isBandSupported(SoftApConfiguration.BAND_5GHZ, mContext)) { 538 desiredBand |= SoftApConfiguration.BAND_5GHZ; 539 } 540 configBuilder.setBand(desiredBand); 541 } 542 if (customConfig == null || customConfig.getSsid() == null) { 543 configBuilder.setSsid(generateLohsSsid(context)); 544 } 545 546 return updatePersistentRandomizedMacAddress(configBuilder.build()); 547 } 548 549 /** 550 * @return a copy of the given SoftApConfig with the BSSID randomized, unless a custom BSSID is 551 * already set. 552 */ randomizeBssidIfUnset(Context context, SoftApConfiguration config)553 SoftApConfiguration randomizeBssidIfUnset(Context context, SoftApConfiguration config) { 554 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(config); 555 if (config.getBssid() == null && ApConfigUtil.isApMacRandomizationSupported(mContext) 556 && config.getMacRandomizationSettingInternal() 557 != SoftApConfiguration.RANDOMIZATION_NONE) { 558 MacAddress macAddress = null; 559 if (config.getMacRandomizationSettingInternal() 560 == SoftApConfiguration.RANDOMIZATION_PERSISTENT) { 561 macAddress = config.getPersistentRandomizedMacAddress(); 562 if (macAddress == null) { 563 WifiSsid ssid = config.getWifiSsid(); 564 macAddress = mMacAddressUtil.calculatePersistentMacForSap( 565 ssid != null ? ssid.toString() : null, Process.WIFI_UID); 566 if (macAddress == null) { 567 Log.e(TAG, "Failed to calculate MAC from SSID. " 568 + "Generating new random MAC instead."); 569 } 570 } 571 } 572 if (macAddress == null) { 573 macAddress = MacAddressUtils.createRandomUnicastAddress(); 574 } 575 configBuilder.setBssid(macAddress); 576 if (macAddress != null && SdkLevel.isAtLeastS()) { 577 configBuilder.setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE); 578 } 579 } 580 return configBuilder.build(); 581 } 582 583 /** 584 * Verify provided preSharedKey in ap config for WPA2_PSK/WPA3_SAE (Transition) network 585 * meets requirements. 586 */ 587 @SuppressWarnings("ReturnValueIgnored") validateApConfigAsciiPreSharedKey( @oftApConfiguration.SecurityType int securityType, String preSharedKey)588 private static boolean validateApConfigAsciiPreSharedKey( 589 @SoftApConfiguration.SecurityType int securityType, String preSharedKey) { 590 final int sharedKeyLen = preSharedKey.length(); 591 final int keyMinLen = securityType == SECURITY_TYPE_WPA3_SAE 592 ? SAE_ASCII_MIN_LEN : PSK_ASCII_MIN_LEN; 593 if (sharedKeyLen < keyMinLen || sharedKeyLen > PSK_SAE_ASCII_MAX_LEN) { 594 Log.d(TAG, "softap network password string size must be at least " + keyMinLen 595 + " and no more than " + PSK_SAE_ASCII_MAX_LEN + " when type is " 596 + securityType); 597 return false; 598 } 599 600 try { 601 preSharedKey.getBytes(StandardCharsets.UTF_8); 602 } catch (IllegalArgumentException e) { 603 Log.e(TAG, "softap network password verification failed: malformed string"); 604 return false; 605 } 606 return true; 607 } 608 609 /** 610 * Validate a SoftApConfiguration is properly configured for use by SoftApManager. 611 * 612 * This method checks for consistency between security settings (if it requires a password, was 613 * one provided?). 614 * 615 * @param apConfig {@link SoftApConfiguration} to use for softap mode 616 * @param isPrivileged indicate the caller can pass some fields check or not 617 * @param wifiNative to use native API to get iface combinations. 618 * @return boolean true if the provided config meets the minimum set of details, false 619 * otherwise. 620 */ validateApWifiConfiguration(@onNull SoftApConfiguration apConfig, boolean isPrivileged, Context context, WifiNative wifiNative)621 static boolean validateApWifiConfiguration(@NonNull SoftApConfiguration apConfig, 622 boolean isPrivileged, Context context, WifiNative wifiNative) { 623 // first check the SSID 624 WifiSsid ssid = apConfig.getWifiSsid(); 625 if (ssid == null || ssid.getBytes().length == 0) { 626 Log.d(TAG, "SSID for softap configuration cannot be null or 0 length."); 627 return false; 628 } 629 630 // BSSID can be set if caller own permission:android.Manifest.permission.NETWORK_SETTINGS. 631 if (apConfig.getBssid() != null && !isPrivileged) { 632 Log.e(TAG, "Config BSSID needs NETWORK_SETTINGS permission"); 633 return false; 634 } 635 636 String preSharedKey = apConfig.getPassphrase(); 637 boolean hasPreSharedKey = !TextUtils.isEmpty(preSharedKey); 638 int authType; 639 640 try { 641 authType = apConfig.getSecurityType(); 642 } catch (IllegalStateException e) { 643 Log.d(TAG, "Unable to get AuthType for softap config: " + e.getMessage()); 644 return false; 645 } 646 647 if (ApConfigUtil.isNonPasswordAP(authType)) { 648 // open networks should not have a password 649 if (hasPreSharedKey) { 650 Log.d(TAG, "open softap network should not have a password"); 651 return false; 652 } 653 } else if (authType == SECURITY_TYPE_WPA2_PSK 654 || authType == SECURITY_TYPE_WPA3_SAE_TRANSITION 655 || authType == SECURITY_TYPE_WPA3_SAE) { 656 // this is a config that should have a password - check that first 657 if (!hasPreSharedKey) { 658 Log.d(TAG, "softap network password must be set"); 659 return false; 660 } 661 662 if (context.getResources().getBoolean( 663 R.bool.config_wifiSoftapPassphraseAsciiEncodableCheck)) { 664 final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); 665 if (!asciiEncoder.canEncode(preSharedKey)) { 666 Log.d(TAG, "passphrase not ASCII encodable"); 667 return false; 668 } 669 if (!validateApConfigAsciiPreSharedKey(authType, preSharedKey)) { 670 // failed preSharedKey checks for WPA2 and WPA3 SAE (Transition) mode. 671 return false; 672 } 673 } 674 } else { 675 // this is not a supported security type 676 Log.d(TAG, "softap configs must either be open or WPA2 PSK networks"); 677 return false; 678 } 679 680 if (!isBandsSupported(apConfig.getBands(), context)) { 681 return false; 682 } 683 684 if (ApConfigUtil.isSecurityTypeRestrictedFor6gBand(authType)) { 685 for (int band : apConfig.getBands()) { 686 // Only return failure if requested band is limited to 6GHz only 687 if (band == SoftApConfiguration.BAND_6GHZ 688 && !ApConfigUtil.canHALConvertRestrictedSecurityTypeFor6GHz( 689 context.getResources(), authType)) { 690 Log.d(TAG, "security type: " + authType 691 + " is not allowed for softap in 6GHz band"); 692 return false; 693 } 694 } 695 } 696 697 if (SdkLevel.isAtLeastT() 698 && authType == SECURITY_TYPE_WPA3_OWE_TRANSITION) { 699 if (!ApConfigUtil.isBridgedModeSupported(context, wifiNative)) { 700 Log.d(TAG, "softap owe transition needs bridge mode support"); 701 return false; 702 } else if (apConfig.getBands().length > 1) { 703 Log.d(TAG, "softap owe transition must use single band"); 704 return false; 705 } 706 } 707 708 return true; 709 } 710 generatePassword()711 private static String generatePassword() { 712 // Characters that will be used for password generation. Some characters commonly known to 713 // be confusing like 0 and O excluded from this list. 714 final String allowed = "23456789abcdefghijkmnpqrstuvwxyz"; 715 final int passLength = 15; 716 717 StringBuilder sb = new StringBuilder(passLength); 718 SecureRandom random = new SecureRandom(); 719 for (int i = 0; i < passLength; i++) { 720 sb.append(allowed.charAt(random.nextInt(allowed.length()))); 721 } 722 return sb.toString(); 723 } 724 725 /** 726 * Generate default band base on supported band configuration. 727 * 728 * @param context The caller context used to get value from resource file. 729 * @return A band which will be used for a default band in default configuration. 730 */ generateDefaultBand(Context context)731 public static @BandType int generateDefaultBand(Context context) { 732 for (int band : SoftApConfiguration.BAND_TYPES) { 733 if (ApConfigUtil.isBandSupported(band, context)) { 734 return band; 735 } 736 } 737 Log.e(TAG, "Invalid overlay configuration! No any band supported on SoftAp"); 738 return SoftApConfiguration.BAND_2GHZ; 739 } 740 isBandsSupported(@onNull int[] apBands, Context context)741 private static boolean isBandsSupported(@NonNull int[] apBands, Context context) { 742 for (int band : apBands) { 743 if (!ApConfigUtil.isBandSupported(band, context)) { 744 return false; 745 } 746 } 747 return true; 748 } 749 750 /** 751 * Enable force-soft-AP-channel mode which takes effect when soft AP starts next time 752 * 753 * @param forcedApBand The forced band. 754 * @param forcedApChannel The forced IEEE channel number or 0 when forced AP band only. 755 * @param forcedApMaximumChannelBandWidth The forced maximum channel bandwidth. 756 */ enableForceSoftApBandOrChannel( @andType int forcedApBand, int forcedApChannel, int forcedApMaximumChannelBandWidth)757 public synchronized void enableForceSoftApBandOrChannel( 758 @BandType int forcedApBand, int forcedApChannel, int forcedApMaximumChannelBandWidth) { 759 mForceApChannel = true; 760 mForcedApChannel = forcedApChannel; 761 mForcedApBand = forcedApBand; 762 mForcedApMaximumChannelBandWidth = forcedApMaximumChannelBandWidth; 763 } 764 765 /** 766 * Disable force-soft-AP-channel mode which take effect when soft AP starts next time 767 */ disableForceSoftApBandOrChannel()768 public synchronized void disableForceSoftApBandOrChannel() { 769 mForceApChannel = false; 770 } 771 updatePersistentRandomizedMacAddress(SoftApConfiguration config)772 private SoftApConfiguration updatePersistentRandomizedMacAddress(SoftApConfiguration config) { 773 // Update randomized MacAddress 774 WifiSsid ssid = config.getWifiSsid(); 775 MacAddress randomizedMacAddress = mMacAddressUtil.calculatePersistentMacForSap( 776 ssid != null ? ssid.toString() : null, Process.WIFI_UID); 777 if (randomizedMacAddress != null) { 778 return new SoftApConfiguration.Builder(config) 779 .setRandomizedMacAddress(randomizedMacAddress).build(); 780 } 781 782 if (config.getPersistentRandomizedMacAddress() != null) { 783 return config; 784 } 785 786 randomizedMacAddress = MacAddressUtils.createRandomUnicastAddress(); 787 return new SoftApConfiguration.Builder(config) 788 .setRandomizedMacAddress(randomizedMacAddress).build(); 789 } 790 791 /** 792 * Returns the last configured Wi-Fi tethered AP passphrase. 793 */ getLastConfiguredTetheredApPassphraseSinceBoot()794 public synchronized String getLastConfiguredTetheredApPassphraseSinceBoot() { 795 return mLastConfiguredPassphrase; 796 } 797 } 798