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.

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.