Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/wifi_iot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,36 @@ Permission to configure WiFi AP SSID and password:
```
--->

## Connection results

`connect()` continues to return `Future<bool>`. Use `connectWithResult()` with
the same arguments when you need a stable error code and available native
details:

```dart
final result = await WiFiForIoTPlugin.connectWithResult(
'My network',
password: 'My password',
security: NetworkSecurity.WPA,
);
if (!result.isSuccess) {
print(result.code);
print(result.nativeCode);
print(result.nativeDomain);
print(result.message);
}
```

`WiFiConnectCode.ok` (an empty string) means success. Native details are nullable:
Android suggestion results include the original `WifiManager` status, and iOS
errors include their `NSError` code, domain and localized message. Other failures
use stable plugin codes; Android `onUnavailable()` cannot reliably distinguish
user rejection, wrong credentials and other connection failures.

On Android 10 or later, `withInternet: true` uses the suggestion API. A successful
result means the suggestion was accepted, and does not guarantee that the system
has connected to the network.

## Troubleshooting

Don't hesitate and come [here](https://github.com/flutternetwork/WiFiFlutter/issues), we will be happy to help you!
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.alternadom.wifiiot;

/**
* Unified Wi-Fi connect result codes; values must match {@code lib/wifi_connect_codes.dart} and iOS
* {@code WifiConnectCodes.swift}. Empty string means success.
*/
public final class WifiConnectCodes {
private WifiConnectCodes() {}

public static final String OK = "";

public static final String INVALID_SSID = "invalid_ssid";

public static final String INVALID_BSSID = "invalid_bssid";
public static final String WEP_NOT_SUPPORTED = "wep_not_supported";
public static final String UNKNOWN = "unknown";

public static final String NETWORK_CONFIGURATION_FAILED = "network_configuration_failed";
public static final String DISCONNECT_FAILED = "disconnect_failed";
public static final String ENABLE_NETWORK_FAILED = "enable_network_failed";
public static final String CONNECTION_TIMEOUT = "connection_timeout";
public static final String CONNECTION_UNAVAILABLE = "connection_unavailable";

public static final String NETWORK_SUGGESTION_INTERNAL = "network_suggestion_internal";
public static final String NETWORK_SUGGESTION_APP_DISALLOWED =
"network_suggestion_app_disallowed";
public static final String NETWORK_SUGGESTION_DUPLICATE = "network_suggestion_duplicate";
public static final String NETWORK_SUGGESTION_EXCEEDS_MAX_PER_APP =
"network_suggestion_exceeds_max_per_app";
public static final String NETWORK_SUGGESTION_REMOVE_INVALID =
"network_suggestion_remove_invalid";
public static final String NETWORK_SUGGESTION_ADD_NOT_ALLOWED =
"network_suggestion_add_not_allowed";
public static final String NETWORK_SUGGESTION_INVALID = "network_suggestion_invalid";
public static final String NETWORK_SUGGESTION_RESTRICTED_BY_ADMIN =
"network_suggestion_restricted_by_admin";
public static final String NETWORK_SUGGESTION_FAILED = "network_suggestion_failed";
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
Expand Down Expand Up @@ -233,13 +235,16 @@ public void onMethodCall(MethodCall poCall, Result poResult) {
setEnabled(poCall, poResult);
break;
case "connect":
connect(poCall, poResult);
connect(poCall, connectResultAdapter(poResult, false));
break;
case "connectWithResult":
connect(poCall, connectResultAdapter(poResult, true));
break;
case "registerWifiNetwork":
registerWifiNetwork(poCall, poResult);
break;
case "findAndConnect":
findAndConnect(poCall, poResult);
findAndConnect(poCall, connectResultAdapter(poResult, false));
break;
case "isConnected":
isConnected(poResult);
Expand Down Expand Up @@ -878,16 +883,26 @@ public void run() {
Boolean isHidden = poCall.argument("is_hidden");
Integer timeoutInSeconds = poCall.argument("timeout_in_seconds");

connectTo(
poResult,
ssid,
bssid,
password,
security,
joinOnce,
withInternet,
isHidden,
timeoutInSeconds);
try {
connectTo(
poResult,
ssid,
bssid,
password,
security,
joinOnce,
withInternet,
isHidden,
timeoutInSeconds);
} catch (RuntimeException error) {
new Handler(Looper.getMainLooper())
.post(
() ->
poResult.error(
error.getClass().getName(),
error.getMessage(),
error.getClass().getName()));
}
}
}.start();
}
Expand Down Expand Up @@ -1255,6 +1270,103 @@ private static String longToIP(int longIp) {
return sb.toString();
}

private static String networkSuggestionStatusToCode(int status) {
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
return WifiConnectCodes.OK;
}
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL) {
return WifiConnectCodes.NETWORK_SUGGESTION_INTERNAL;
}
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED) {
return WifiConnectCodes.NETWORK_SUGGESTION_APP_DISALLOWED;
}
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE) {
return WifiConnectCodes.NETWORK_SUGGESTION_DUPLICATE;
}
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP) {
return WifiConnectCodes.NETWORK_SUGGESTION_EXCEEDS_MAX_PER_APP;
}
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID) {
return WifiConnectCodes.NETWORK_SUGGESTION_REMOVE_INVALID;
}
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED) {
return WifiConnectCodes.NETWORK_SUGGESTION_ADD_NOT_ALLOWED;
}
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID) {
return WifiConnectCodes.NETWORK_SUGGESTION_INVALID;
}
if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_RESTRICTED_BY_ADMIN) {
return WifiConnectCodes.NETWORK_SUGGESTION_RESTRICTED_BY_ADMIN;
}
return WifiConnectCodes.NETWORK_SUGGESTION_FAILED;
}

private static Map<String, Object> buildConnectResult(String code) {
return buildConnectResult(code, null, null, null);
}

private static Map<String, Object> buildConnectResult(
String code, Object nativeCode, String nativeDomain, String message) {
Map<String, Object> value = new HashMap<>();
value.put("code", code);
if (nativeCode != null) value.put("nativeCode", nativeCode);
if (nativeDomain != null) value.put("nativeDomain", nativeDomain);
if (message != null) value.put("message", message);
return value;
}

private Result connectResultAdapter(final Result result, final boolean detailed) {
return new Result() {
@Override
public void success(Object value) {
if (detailed) {
// Allow shared connection paths to report a boolean without losing
// the detailed method's map contract.
if (value instanceof Boolean) {
result.success(
buildConnectResult(
(Boolean) value ? WifiConnectCodes.OK : WifiConnectCodes.UNKNOWN));
} else {
result.success(value);
}
return;
}
if (!(value instanceof Map)) {
result.success(value);
return;
}
final Object code = ((Map<?, ?>) value).get("code");
if (WifiConnectCodes.INVALID_BSSID.equals(code)) {
result.error("Error", "Invalid BSSID representation", "");
} else if (WifiConnectCodes.WEP_NOT_SUPPORTED.equals(code)) {
result.error(
"Error", "WEP is not supported for Android SDK " + Build.VERSION.SDK_INT, "");
} else {
result.success(WifiConnectCodes.OK.equals(code));
}
}

@Override
public void error(String code, String message, Object details) {
if (detailed) {
result.success(
buildConnectResult(
"platform_error",
code,
details instanceof String ? (String) details : null,
message));
} else {
result.error(code, message, details);
}
}

@Override
public void notImplemented() {
result.notImplemented();
}
};
}

/// Method to connect to WIFI Network
private void connectTo(
final Result poResult,
Expand All @@ -1268,13 +1380,12 @@ private void connectTo(
final Integer timeoutInSeconds) {
final Handler handler = new Handler(Looper.getMainLooper());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
final boolean connected =
connectToDeprecated(ssid, bssid, password, security, joinOnce, isHidden);
final String code = connectToDeprecated(ssid, bssid, password, security, joinOnce, isHidden);
handler.post(
new Runnable() {
@Override
public void run() {
poResult.success(connected);
poResult.success(buildConnectResult(code));
}
});
} else {
Expand All @@ -1284,8 +1395,7 @@ public void run() {
new Runnable() {
@Override
public void run() {
poResult.error(
"Error", "WEP is not supported for Android SDK " + Build.VERSION.SDK_INT, "");
poResult.success(buildConnectResult(WifiConnectCodes.WEP_NOT_SUPPORTED));
}
});
return;
Expand All @@ -1304,7 +1414,7 @@ public void run() {
new Runnable() {
@Override
public void run() {
poResult.error("Error", "Invalid BSSID representation", "");
poResult.success(buildConnectResult(WifiConnectCodes.INVALID_BSSID));
}
});
return;
Expand Down Expand Up @@ -1338,7 +1448,12 @@ public void run() {
new Runnable() {
@Override
public void run() {
poResult.success(status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
poResult.success(
buildConnectResult(
networkSuggestionStatusToCode(status),
status,
"android.net.wifi.WifiManager",
null));
}
});
} else {
Expand All @@ -1354,7 +1469,7 @@ public void run() {
new Runnable() {
@Override
public void run() {
poResult.error("Error", "Invalid BSSID representation", "");
poResult.success(buildConnectResult(WifiConnectCodes.INVALID_BSSID));
}
});
return;
Expand Down Expand Up @@ -1388,7 +1503,7 @@ public void onAvailable(@NonNull Network network) {
super.onAvailable(network);
if (!resultSent) {
joinedNetwork = network;
poResult.success(true);
poResult.success(buildConnectResult(WifiConnectCodes.OK));
resultSent = true;
}
}
Expand All @@ -1400,7 +1515,7 @@ public void onUnavailable() {
connectivityManager.unregisterNetworkCallback(this);
}
if (!resultSent) {
poResult.success(false);
poResult.success(buildConnectResult(WifiConnectCodes.CONNECTION_UNAVAILABLE));
resultSent = true;
}
}
Expand Down Expand Up @@ -1502,7 +1617,7 @@ private android.net.wifi.WifiConfiguration generateConfiguration(
}

@SuppressWarnings("deprecation")
private Boolean connectToDeprecated(
private String connectToDeprecated(
String ssid,
String bssid,
String password,
Expand All @@ -1516,7 +1631,7 @@ private Boolean connectToDeprecated(
int updateNetwork = registerWifiNetworkDeprecated(conf);

if (updateNetwork == -1) {
return false;
return WifiConnectCodes.NETWORK_CONFIGURATION_FAILED;
}

if (joinOnce != null && joinOnce.booleanValue()) {
Expand All @@ -1525,13 +1640,14 @@ private Boolean connectToDeprecated(

boolean disconnect = moWiFi.disconnect();
if (!disconnect) {
return false;
return WifiConnectCodes.DISCONNECT_FAILED;
}

boolean enabled = moWiFi.enableNetwork(updateNetwork, true);
if (!enabled) return false;
if (!enabled) {
return WifiConnectCodes.ENABLE_NETWORK_FAILED;
}

boolean connected = false;
for (int i = 0; i < 20; i++) {
WifiInfo currentNet = moWiFi.getConnectionInfo();
int networkId = currentNet.getNetworkId();
Expand All @@ -1540,16 +1656,18 @@ private Boolean connectToDeprecated(
// Wait for connection to reach state completed
// to discard false positives like auth error
if (networkId != -1 && netState == SupplicantState.COMPLETED) {
connected = networkId == updateNetwork;
break;
if (networkId == updateNetwork) {
return WifiConnectCodes.OK;
}
return WifiConnectCodes.CONNECTION_TIMEOUT;
}
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
break;
return WifiConnectCodes.CONNECTION_TIMEOUT;
}
}

return connected;
return WifiConnectCodes.CONNECTION_TIMEOUT;
}
}
Loading
Loading