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 / NSBonjourServices Info.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:

PlatformInjectedNotes

Android

ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE

"normal" permissions; no runtime prompt

Android API 26+

ACCESS_FINE_LOCATION (maxSdkVersion=32)

Required by the OS to return a real SSID

Android API 33+

NEARBY_WIFI_DEVICES with neverForLocation

Replaces fine-location for scan flows

iOS

com.apple.developer.networking.wifi-info entitlement

Required since iOS 13

iOS

NSLocationWhenInUseUsageDescription

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 WifiConfiguration API 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.

  • WiFiSecurity must 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 NSLocalNetworkUsageDescription key explains why the app needs local-network access.

  • The Info.plist NSBonjourServices array 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.

Linux and embedded devices

The Linux desktop and embedded targets answer and browse on their own UDP socket and need no Avahi or other system service. A headless device running the backend uses com.codename1.backend.Discovery, which speaks the same protocol, so a phone application finds it with BonjourBrowser.

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.registerNetworkCallback on API 21+, falling back to the legacy CONNECTIVITY_ACTION broadcast on older devices.

  • iOS: SCNetworkReachability with 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(…​) — call WiFi.disconnect(ssid) when the user navigates away.

  • Usb.addDeviceListener(…​) — call removeDeviceListener(…​).

  • NetworkManager.addNetworkTypeListener(…​) — call removeNetworkTypeListener(…​).

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.