diff --git a/packages/wifi_iot/README.md b/packages/wifi_iot/README.md index 9b3da102..293b813b 100644 --- a/packages/wifi_iot/README.md +++ b/packages/wifi_iot/README.md @@ -48,6 +48,26 @@ Becareful, some commands as no effect on iOS because Apple don't let us to do wh Additional Wifi protocols on Android side like - Wifi Direct, Wifi Aware, etc are in active discussion at [#140](https://github.com/flutternetwork/WiFiFlutter/issues/140). Encourage you to engage if you want this features. +## WPA2 and WPA3-Personal + +Use `NetworkSecurity.WPA2` for password-protected WPA2 networks or +`NetworkSecurity.WPA3` for WPA3-Personal (SAE) networks. The existing +`NetworkSecurity.WPA` value keeps its WPA2 behavior. WPA3-Enterprise is not +supported by these password-based connection methods. + +```dart +final connected = await WiFiForIoTPlugin.connect( + 'My network', + password: 'My password', + security: NetworkSecurity.WPA3, +); +``` + +On Android, WPA3-Personal requires Android 10 or later and SAE support on the +device. `findAndConnect` falls back to WPA2 for WPA2/WPA3 transition networks +when the device does not support SAE. On iOS, WPA2 and WPA3 use the system's +password-based hotspot configuration. + ## Access Point | Description | Android | iOS | | :------------------------------------------------------------------------------------ | :--------------------: | :------------------: | diff --git a/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java b/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java index 455839e1..020e6e7f 100644 --- a/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java +++ b/packages/wifi_iot/android/src/main/java/com/alternadom/wifiiot/WifiIotPlugin.java @@ -925,6 +925,12 @@ private void registerWifiNetwork(final MethodCall poCall, final Result poResult) String security = poCall.argument("security"); Boolean isHidden = poCall.argument("is_hidden"); + if ("WPA3".equalsIgnoreCase(security) + && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || !moWiFi.isWpa3SaeSupported())) { + poResult.error("Error", "WPA3-Personal is not supported on this device", ""); + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { final WifiNetworkSuggestion.Builder suggestedNet = new WifiNetworkSuggestion.Builder(); suggestedNet.setSsid(ssid); @@ -938,8 +944,11 @@ private void registerWifiNetwork(final MethodCall poCall, final Result poResult) suggestedNet.setBssid(macAddress); } - if (security != null && security.toUpperCase().equals("WPA")) { + if (security != null + && (security.toUpperCase().equals("WPA") || security.toUpperCase().equals("WPA2"))) { suggestedNet.setWpa2Passphrase(password); + } else if (security != null && security.toUpperCase().equals("WPA3")) { + suggestedNet.setWpa3Passphrase(password); } else if (security != null && security.toUpperCase().equals("WEP")) { // WEP is not supported poResult.error( @@ -1038,10 +1047,17 @@ public void run() { }.start(); } - private static String getSecurityType(ScanResult scanResult) { + private String getSecurityType(ScanResult scanResult) { String capabilities = scanResult.capabilities; - if (capabilities.contains("WPA") + if (capabilities.contains("SAE")) { + // Transition networks also advertise PSK; use it if SAE is unavailable. + if (capabilities.contains("PSK") + && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || !moWiFi.isWpa3SaeSupported())) { + return "WPA"; + } + return "WPA3"; + } else if (capabilities.contains("WPA") || capabilities.contains("WPA2") || capabilities.contains("WPA/WPA2 PSK")) { return "WPA"; @@ -1267,6 +1283,11 @@ private void connectTo( final Boolean isHidden, final Integer timeoutInSeconds) { final Handler handler = new Handler(Looper.getMainLooper()); + if ("WPA3".equalsIgnoreCase(security) + && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || !moWiFi.isWpa3SaeSupported())) { + handler.post(() -> poResult.success(false)); + return; + } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { final boolean connected = connectToDeprecated(ssid, bssid, password, security, joinOnce, isHidden); @@ -1313,8 +1334,11 @@ public void run() { } // set password - if (security != null && security.toUpperCase().equals("WPA")) { + if (security != null + && (security.toUpperCase().equals("WPA") || security.toUpperCase().equals("WPA2"))) { builder.setWpa2Passphrase(password); + } else if (security != null && security.toUpperCase().equals("WPA3")) { + builder.setWpa3Passphrase(password); } // remove suggestions if already existing @@ -1363,8 +1387,11 @@ public void run() { } // set security - if (security != null && security.toUpperCase().equals("WPA")) { + if (security != null + && (security.toUpperCase().equals("WPA") || security.toUpperCase().equals("WPA2"))) { builder.setWpa2Passphrase(password); + } else if (security != null && security.toUpperCase().equals("WPA3")) { + builder.setWpa3Passphrase(password); } final NetworkRequest networkRequest = @@ -1467,8 +1494,7 @@ private android.net.wifi.WifiConfiguration generateConfiguration( if (security != null) security = security.toUpperCase(); else security = "NONE"; - if (security.toUpperCase().equals("WPA")) { - + if (security.equals("WPA") || security.equals("WPA2")) { /// appropriate ciper is need to set according to security type used, /// ifcase of not added it will not be able to connect conf.preSharedKey = "\"" + password + "\""; @@ -1489,6 +1515,13 @@ private android.net.wifi.WifiConfiguration generateConfiguration( conf.allowedProtocols.set(android.net.wifi.WifiConfiguration.Protocol.RSN); conf.allowedProtocols.set(android.net.wifi.WifiConfiguration.Protocol.WPA); + } else if (security.equals("WPA3")) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + conf.preSharedKey = "\"" + password + "\""; + conf.allowedKeyManagement.set(android.net.wifi.WifiConfiguration.KeyMgmt.SAE); + conf.allowedProtocols.set(android.net.wifi.WifiConfiguration.Protocol.RSN); + conf.status = android.net.wifi.WifiConfiguration.Status.ENABLED; + } } else if (security.equals("WEP")) { conf.wepKeys[0] = "\"" + password + "\""; conf.wepTxKeyIndex = 0; diff --git a/packages/wifi_iot/ios/wifi_iot/Sources/wifi_iot/WifiIotPlugin.swift b/packages/wifi_iot/ios/wifi_iot/Sources/wifi_iot/WifiIotPlugin.swift index 65073906..0db16dab 100755 --- a/packages/wifi_iot/ios/wifi_iot/Sources/wifi_iot/WifiIotPlugin.swift +++ b/packages/wifi_iot/ios/wifi_iot/Sources/wifi_iot/WifiIotPlugin.swift @@ -171,7 +171,7 @@ public class WifiIotPlugin: NSObject, FlutterPlugin { -> NEHotspotConfiguration { switch security?.uppercased() { - case "WPA": + case "WPA", "WPA2", "WPA3": return NEHotspotConfiguration.init(ssid: ssid, passphrase: passphrase!, isWEP: false) case "WEP": return NEHotspotConfiguration.init(ssid: ssid, passphrase: passphrase!, isWEP: true) diff --git a/packages/wifi_iot/lib/wifi_iot.dart b/packages/wifi_iot/lib/wifi_iot.dart index 0125e4a8..bc817a87 100755 --- a/packages/wifi_iot/lib/wifi_iot.dart +++ b/packages/wifi_iot/lib/wifi_iot.dart @@ -13,12 +13,14 @@ enum WIFI_AP_STATE { WIFI_AP_STATE_FAILED, } -enum NetworkSecurity { WPA, WEP, NONE } +enum NetworkSecurity { WPA, WEP, NONE, WPA2, WPA3 } const serializeNetworkSecurityMap = { NetworkSecurity.WPA: "WPA", NetworkSecurity.WEP: "WEP", NetworkSecurity.NONE: "NONE", + NetworkSecurity.WPA2: "WPA2", + NetworkSecurity.WPA3: "WPA3", }; const MethodChannel _channel = const MethodChannel('wifi_iot'); @@ -320,7 +322,8 @@ class WiFiForIoTPlugin { /// @param [security] The security type of the network. [NetworkSecurity.NONE] /// means no password is required. /// On Android, from version 10 (Q) onward, [NetworkSecurity.WEP] is no - /// longer supported. + /// longer supported. WPA3-Personal requires Android 10 (Q) or later and + /// a device that supports SAE. WPA remains an alias for WPA2. /// /// @param [joinOnce] If true, the network will be removed on exit. /// @@ -397,7 +400,8 @@ class WiFiForIoTPlugin { /// @param [security] The security type of the network. [NetworkSecurity.NONE] /// means no password is required. /// On Android, from version 10 (Q) onward, [NetworkSecurity.WEP] is no - /// longer supported. + /// longer supported. WPA3-Personal requires Android 10 (Q) or later and + /// a device that supports SAE. WPA remains an alias for WPA2. /// /// @param [isHidden] Whether the SSID is hidden (not broadcasted by the AP). ///