Codename One ships a set of APIs under com.codename1.io.wifi, com.codename1.io.bonjour, com.codename1.io.usb and an extension of com.codename1.io.NetworkManager that lets apps inspect and manage the device’s local network beyond plain HTTP.
A quick connectivity check is NetworkManager.getInstance().isConnected() — it reads the cached platform network state and returns immediately without an HTTP probe, so it’s safe to call from the EDT before deciding whether to fire a ConnectionRequest.
These APIs share three guarantees:
Every callback runs on the EDT so you can update UI directly from the result.
The build pipeline injects the matching Android permissions, iOS entitlements, and
NSXxxUsageDescription/NSBonjourServicesInfo.plist entries automatically by scanning the classpath. Apps that never reference these classes see no manifest or entitlement changes.The simulator implements best-effort versions of each API so you can wire and test your UI without a device. The simulator also prints the list of permissions/entitlements each API will need in a real build the first time you call it.
The simulator’s "API usage report" prints to stdout on JVM shutdown so you can spot anything you pulled in via a transitive cn1lib.
WiFi information
The fastest way to know whether the user is on WiFi, what SSID they joined, and what their local IP is:
if (WiFi.isInfoSupported()) {
String ssid = WiFi.getCurrentSSID(); // e.g. "Codename One"
String bssid = WiFi.getBSSID(); // "aa:bb:cc:11:22:33"
String gw = WiFi.getGateway(); // "192.168.1.1"
String ip = WiFi.getIp(); // "192.168.1.42"
}
Any of these calls may return null if the device isn’t on WiFi, if the user denied a runtime permission, or if the platform never exposed the value to apps. The framework normalizes the wrapping that Android adds to the SSID and treats <unknown ssid> and the obfuscated BSSID 02:00:00:00:00:00 as null.
Required permissions and entitlements
The build pipeline injects the following automatically the first time it sees com.codename1.io.wifi.WiFi on the classpath:
| Platform | Injected | Notes |
|---|---|---|
Android |
| "normal" permissions; no runtime prompt |
Android API 26+ |
| Required by the OS to return a real SSID |
Android API 33+ |
| Replaces fine-location for scan flows |
iOS |
| Required since iOS 13 |
iOS |
| iOS still checks CoreLocation behind the scenes |
You can override any injected automatically value via the standard build hints:
ios.locationUsageDescription=Allow access to read your Wi-Fi network name to discover printers on your network.
android.xpermissions=<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
WiFi scan
Scanning returns an array of `WiFiNetwork`s sorted by signal strength. The callback fires once.
iOS doesn’t expose a public WiFi scan API. On iOS WiFi.scan(…) always reports an UnsupportedOperationException. Plan for a graceful fallback if your UI exposes a scan button.
Android throttles scans to 4 per 2 minutes per foreground app on API 28+; throttled scans return cached results, not an error.
WiFi connect
To associate the device with a specific SSID:
WiFi.connect("MyAccessPoint", "s3cret!", WiFiSecurity.WPA_PSK,
new WiFiConnectCallback() {
@Override
public void onConnectResult(boolean connected, Throwable error) {
if (!connected) {
Log.e(error);
return;
}
// ssid is now joined
}
});
On Android 10+ the OS shows a system confirmation dialog with the SSID before joining; this dialog can’t be bypassed.
On Android 9 and below the framework falls back to the legacy
WifiConfigurationAPI and joins without an in-app prompt.On iOS 11+ this uses
NEHotspotConfiguration. The user is shown a system prompt the first time the app tries to join the SSID; subsequent attempts reuse the cached configuration.WiFiSecuritymust match the AP’s actual security mode (OPEN,WEP,WPA_PSK,WPA3_SAE). Passing the wrong mode causes the join to fail.
WiFi.disconnect(ssid) releases the request on Android 10+ and removes the hotspot configuration on iOS.
On iOS the build pipeline also enables the NetworkExtension.framework and the HotspotConfiguration entitlement only when it sees a call to WiFi.connect(…) or WiFi.disconnect(…). Apps that only read SSID/BSSID won’t pull in the framework, which keeps App Store API-usage scans clean.
Bonjour / mDNS discovery
BonjourBrowser watches the local network for services of a given mDNS type. BonjourPublisher advertises a service.
BonjourBrowser browser = BonjourBrowser.browse("_http._tcp.",
new BonjourServiceListener() {
@Override
public void onServiceResolved(BonjourService s) {
Log.p("Found " + s.getName() + " at " + s.getHost() + ":" + s.getPort());
}
@Override
public void onServiceLost(BonjourService s) { /* null */ }
@Override
public void onBrowseError(Throwable t) { Log.e(t); }
});
// when done:
browser.stop();
To advertise an HTTP server you wrote on port 8080:
BonjourPublisher pub = BonjourPublisher.publish(
"My Server", "_http._tcp.", 8080, null);
// later:
pub.unpublish();
iOS requirements
Bonjour on iOS 14+ returns no results unless:
The Info.plist
NSLocalNetworkUsageDescriptionkey explains why the app needs local-network access.The Info.plist
NSBonjourServicesarray lists each service type the app expects to find.
The build pipeline injects both keys automatically when com.codename1.io.bonjour is on the classpath, with _http._tcp. as the seed type. To publish or browse a custom type, set the build hint:
ios.NSBonjourServices=_myapp._tcp.,_http._tcp.
ios.NSLocalNetworkUsageDescription=Discover other instances of MyApp on this Wi-Fi network.
The comma-separated values are expanded into the Info.plist array at build time.
Android requirements
The pipeline adds CHANGE_WIFI_MULTICAST_STATE automatically. NsdManager does the rest; there is no runtime permission prompt.
Simulator behaviour
If javax.jmdns.JmDNS is on the simulator classpath, browse calls dispatch through it. Otherwise the listener receives a single UnsupportedOperationException and the simulator prints a hint. Add JmDNS to your application’s executable-jar / simulator profile (typically the <profile id="executable-jar"> block in your cn1app’s common/pom.xml or your javase/pom.xml) to exercise real discovery in the simulator:
<!-- in YOUR app's pom, NOT in the framework / common pom that gets shipped
to the device. The cn1app archetype's executable-jar profile is the
right place because it only applies when launching the simulator. -->
<dependency>
<groupId>org.jmdns</groupId>
<artifactId>jmdns</artifactId>
<version>3.5.9</version>
<scope>runtime</scope>
</dependency>
Don’t add JmDNS to the cn1app common/pom.xml outside a simulator-scoped profile — it would then ship with your Android / iOS binaries even though both platforms supply native mDNS implementations.
Network type change events
Subscribe to network type transitions (WiFi <→ Cellular <→ Ethernet <→ None):
NetworkManager.getInstance().addNetworkTypeListener(new NetworkTypeListener() {
@Override
public void onNetworkTypeChanged(int oldType, int newType, boolean vpnActive) {
if (newType == NetworkManager.NETWORK_TYPE_NONE) {
// offline -- queue uploads for later
}
if (vpnActive) {
// refuse to roam to corporate-only resources
}
}
});
The current snapshot is available without subscribing:
int type = NetworkManager.getInstance().getCurrentNetworkType();
boolean vpn = NetworkManager.getInstance().isVPNActive();
The platform implementations are:
Android:
ConnectivityManager.registerNetworkCallbackon API 21+, falling back to the legacyCONNECTIVITY_ACTIONbroadcast on older devices.iOS:
SCNetworkReachabilitywith a kernel callback scheduled on the main run loop.Simulator: derives the type from
NetworkInterface.getDisplayName; transitions aren’t synthesized.
VPN detection is best-effort and intentionally heuristic. On Android it uses NetworkCapabilities.TRANSPORT_VPN plus an interface-name probe; on iOS it inspects utun*, tun*, ppp* and ipsec* interfaces. Use it as a hint, not as a security boundary.
WiFi Direct (Wi-Fi P2P)
WiFi Direct lets two devices form a peer-to-peer link without an access point. The API is Android-only — iOS uses MultipeerConnectivity for similar scenarios and is intentionally out of scope.
The build pipeline injects CHANGE_WIFI_STATE, ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE, ACCESS_FINE_LOCATION and NEARBY_WIFI_DEVICES automatically when WiFiDirect is on the classpath, plus a <uses-feature android:name="android.hardware.wifi.direct"/> declaration so devices without the radio can still install the app via Play Store filtering.
USB host
The USB API in com.codename1.io.usb lets the device act as a USB host and talk to attached peripherals — a barcode scanner, a serial converter, a microcontroller. It’s Android-only; iOS has no third-party USB host access and the simulator stubs everything out.
if (!Usb.isSupported()) { return; }
Usb.addDeviceListener(new UsbDeviceListener() {
@Override
public void onDeviceAttached(UsbDevice d) {
if (d.getVendorId() == 0x0403 && d.getProductId() == 0x6001) {
Usb.requestPermission(d);
}
}
@Override
public void onDeviceDetached(UsbDevice d) { }
@Override
public void onPermissionResult(UsbDevice d, boolean granted) {
if (granted) {
try (InputStream in = Usb.openInputStream(d, 0x81);
OutputStream out = Usb.openOutputStream(d, 0x02)) {
out.write("AT\r\n".getBytes());
byte[] buf = new byte[64];
int n = in.read(buf);
// null
} catch (IOException e) { Log.e(e); }
}
}
});
The build pipeline injects:
<uses-feature android:name="android.hardware.usb.host" android:required="false"/>.
If you want the OS to launch your app when a matching device is plugged in, ship a device_filter.xml resource and declare an <intent-filter> via the android.xintent_filter build hint:
<!-- native/android/res/xml/device_filter.xml -->
<resources>
<usb-device vendor-id="1027" product-id="24577" />
</resources>
android.xintent_filter=<intent-filter><action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /></intent-filter><meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" />
Releasing platform resources
Most of these APIs hold background resources (broadcast receivers, network callbacks, NSNetService delegates) that you must release explicitly:
BonjourBrowser— call.stop().BonjourPublisher— call.unpublish().WiFiDirect— call.stopDiscovery()and.disconnect().WiFi.connect(…)— callWiFi.disconnect(ssid)when the user navigates away.Usb.addDeviceListener(…)— callremoveDeviceListener(…).NetworkManager.addNetworkTypeListener(…)— callremoveNetworkTypeListener(…).
Each of these keeps a radio or system service awake. Bonjour browsers and WiFi scans in particular drain battery noticeably — they’re fine during a few user-facing screens but shouldn’t stay armed for the lifetime of the app. Release them as soon as the screen that needs them is no longer visible.