Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
adba200
Bluetooth core foundations: uuid, errors, adapter state, device, facade
shai-almog Jul 17, 2026
d367efe
Bluetooth LE central: scan multiplexer, GATT client, op queue, L2CAP
shai-almog Jul 17, 2026
2562504
Bluetooth peripheral mode, classic RFCOMM, package docs
shai-almog Jul 17, 2026
20d3112
Auto-detect Bluetooth API usage in Android/iOS builders
shai-almog Jul 17, 2026
ebaac58
Android Bluetooth port: full stack over android.bluetooth
shai-almog Jul 17, 2026
70356b4
Bluetooth core API contract tests (layer 1)
shai-almog Jul 17, 2026
8821368
JavaSE Bluetooth: scriptable virtual stack + simulator port (layer 2)
shai-almog Jul 17, 2026
1acd0a1
iOS Bluetooth port: CoreBluetooth natives + Java bridge
shai-almog Jul 17, 2026
a9d2752
Bluetooth Simulation window + simulator hooks
shai-almog Jul 17, 2026
db45655
JavaSE native BLE backend: Rust btleplug helper + subprocess bridge
shai-almog Jul 17, 2026
4cabcf0
JavaScript port: Web Bluetooth over the worker host bridge
shai-almog Jul 17, 2026
d36f4c0
Developer guide: Bluetooth chapter + compilable snippets
shai-almog Jul 17, 2026
f9b3619
Developer guide: Bluetooth Simulation window screenshots
shai-almog Jul 17, 2026
d7d649e
Bluetooth record & replay: capture real traffic, scramble, replay
shai-almog Jul 17, 2026
1b44b84
Fix latent iOS bugs: WebAuthn trampoline names, IOSNfc static-init order
shai-almog Jul 17, 2026
a2fb36f
Don't ship the cn1-binaries ble README into the javase jar
shai-almog Jul 17, 2026
cecd4fe
Add missing copyright headers; make the header gate CRLF-tolerant
shai-almog Jul 17, 2026
82afea8
Resolve the BLE helper per CPU architecture, not just per OS
shai-almog Jul 17, 2026
d55fa3f
Fix device-API and SpotBugs violations Maven missed
shai-almog Jul 17, 2026
f7268d7
iOS port: replace Character.forDigit (absent from ParparVM JavaAPI)
shai-almog Jul 17, 2026
dc947db
Android port: clear 9 SpotBugs SIC_INNER_SHOULD_BE_STATIC_ANON findings
shai-almog Jul 17, 2026
af2196c
Merge remote-tracking branch 'origin/master' into first-class-bluetooth
shai-almog Jul 17, 2026
a7ae861
Fix forbidden PMD violations in the Bluetooth core
shai-almog Jul 17, 2026
f6cffdb
Extract shared native-ble helper client module; refactor JavaSE onto it
shai-almog Jul 17, 2026
280ad3e
Native subprocess bridge for the Win32 and Linux ports
shai-almog Jul 17, 2026
85e52ad
Real BLE central on the native Win32 and Linux ports
shai-almog Jul 17, 2026
de410a9
Move BLE helper client into core so the Ant build can see it
shai-almog Jul 17, 2026
2ccba2d
Add package-info for the relocated bluetooth.helper package
shai-almog Jul 17, 2026
eeea333
Convert bluetooth.helper doc comments to /// markdown style
shai-almog Jul 17, 2026
2dc6e93
Fix forbidden PMD and a test race exposed by moving the helper into core
shai-almog Jul 17, 2026
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
57 changes: 57 additions & 0 deletions CodenameOne/src/com/codename1/bluetooth/AdapterState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.bluetooth;

/// Lifecycle states of the local Bluetooth adapter, a union of the Android
/// `BluetoothAdapter` states and the iOS `CBManagerState` values. Query via
/// [Bluetooth#getAdapterState()] and observe changes with
/// [Bluetooth#addAdapterStateListener(AdapterStateListener)].
public enum AdapterState {
/// The state has not been determined yet -- e.g. iOS before the first
/// CoreBluetooth manager callback fired.
UNKNOWN,

/// The device has no Bluetooth hardware, or the port does not implement
/// Bluetooth at all. The fallback base [Bluetooth] class always reports
/// this state.
UNSUPPORTED,

/// The app is not authorized to use Bluetooth (iOS privacy authorization
/// denied / restricted, or missing runtime permissions on Android).
UNAUTHORIZED,

/// The adapter is off. Ask the user to enable it, optionally via
/// [Bluetooth#requestEnable()].
POWERED_OFF,

/// The adapter is transitioning to [#POWERED_ON] (Android only; iOS
/// reports the transition as a direct state change).
TURNING_ON,

/// The adapter is on and ready for scanning, connections and
/// advertising.
POWERED_ON,

/// The adapter is transitioning to [#POWERED_OFF] (Android only).
TURNING_OFF
}
33 changes: 33 additions & 0 deletions CodenameOne/src/com/codename1/bluetooth/AdapterStateListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.bluetooth;

/// Observes local adapter state changes registered via
/// [Bluetooth#addAdapterStateListener(AdapterStateListener)]. Single
/// abstract method so application code can pass a lambda. Always invoked on
/// the EDT.
public interface AdapterStateListener {
/// Fired on the EDT whenever the adapter transitions to a new
/// [AdapterState].
void adapterStateChanged(AdapterState newState);
}
265 changes: 265 additions & 0 deletions CodenameOne/src/com/codename1/bluetooth/Bluetooth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.bluetooth;

import com.codename1.ui.Display;
import com.codename1.util.AsyncResource;

import java.util.ArrayList;

/// Entry point for the Codename One Bluetooth API -- adapter state, runtime
/// permissions and the capability queries that let cross-platform code
/// branch cleanly. Obtain the platform implementation via [#getInstance()];
/// the returned subclass is owned by the active port and never `null`.
///
/// The API is split by role:
/// - `com.codename1.bluetooth.le` -- BLE central: scanning, connections and
/// the GATT client, plus L2CAP channels.
/// - `com.codename1.bluetooth.le.server` -- BLE peripheral: advertising and
/// the local GATT server.
/// - `com.codename1.bluetooth.classic` -- classic Bluetooth: discovery and
/// RFCOMM stream connections (Android and the simulator only).
///
/// #### Quick start: check support and permissions
///
/// ```java
/// Bluetooth bt = Bluetooth.getInstance();
/// if (!bt.isLeSupported()) {
/// // hide the feature
/// return;
/// }
/// bt.requestPermissions(BluetoothPermission.SCAN, BluetoothPermission.CONNECT)
/// .onResult((granted, err) -> {
/// if (err == null && granted) {
/// // start scanning...
/// }
/// });
/// ```
///
/// #### Threading
///
/// Every callback of the Bluetooth API -- `AsyncResource` results, scan
/// results, connection events, notifications, adapter state changes -- is
/// delivered on the EDT. The only exceptions are the blocking
/// `InputStream`/`OutputStream` pairs of RFCOMM and L2CAP connections,
/// which must be consumed off the EDT.
///
/// #### Platform support
///
/// - **Android** -- full stack: BLE central + peripheral, L2CAP (API 29+),
/// classic RFCOMM. Permissions and manifest entries are auto-injected at
/// build time when these packages are referenced.
/// - **iOS / Mac Catalyst** -- CoreBluetooth: BLE central + peripheral and
/// L2CAP. Classic RFCOMM is not exposed by iOS. The `NSBluetooth*` plist
/// entries and CoreBluetooth framework are auto-injected at build time.
/// - **JavaSE simulator** -- a scriptable virtual Bluetooth stack with a
/// full feature matrix (Simulate -> Bluetooth Simulation), plus an
/// optional native backend that talks to the host machine's real
/// Bluetooth hardware (BLE central only).
/// - **JavaScript** -- Web Bluetooth where the browser supports it: central
/// role via the browser's device chooser, GATT client and notifications.
/// - **All other ports** -- this base class is returned as-is and reports
/// Bluetooth as unsupported; every operation fails fast with
/// [BluetoothError#NOT_SUPPORTED].
public class Bluetooth {

private ArrayList<AdapterStateListener> adapterListeners;

/// Ports construct subclasses. Application code obtains the active
/// instance via [#getInstance()].
protected Bluetooth() {
}

/// Returns the platform-specific singleton owned by the current port.
/// On ports that do not implement Bluetooth this returns a base
/// [Bluetooth] instance that reports the feature as unsupported;
/// calling code never needs a `null` check or a platform-specific
/// `if`.
public static Bluetooth getInstance() {
Bluetooth b = Display.getInstance().getBluetooth();
return b != null ? b : DEFAULT;
}

private static final Bluetooth DEFAULT = new Bluetooth();

/// `true` when this port/device exposes any Bluetooth functionality at
/// all. Returns `false` on the fallback base class.
public boolean isSupported() {
return false;
}

/// `true` when the BLE central role (scanning, connections, GATT
/// client) is available. Defaults to `false`.
public boolean isLeSupported() {
return false;
}

/// `true` when classic Bluetooth (discovery, RFCOMM) is available.
/// Always `false` on iOS, which does not expose classic Bluetooth to
/// apps. Defaults to `false`.
public boolean isClassicSupported() {
return false;
}

/// `true` when the BLE peripheral role (advertising and a local GATT
/// server) is available. `false` on tvOS/watchOS and on the JavaScript
/// port. Defaults to `false`.
public boolean isPeripheralModeSupported() {
return false;
}

/// `true` when L2CAP connection-oriented channels are available
/// (Android 10+, iOS 11+). Defaults to `false`.
public boolean isL2capSupported() {
return false;
}

/// The current state of the local adapter. The fallback base class
/// reports [AdapterState#UNSUPPORTED].
public AdapterState getAdapterState() {
return AdapterState.UNSUPPORTED;
}

/// Convenience for `getAdapterState() == AdapterState.POWERED_ON`.
public boolean isEnabled() {
return getAdapterState() == AdapterState.POWERED_ON;
}

/// Registers a listener notified on the EDT whenever the adapter state
/// changes. Listeners stay registered until removed via
/// [#removeAdapterStateListener(AdapterStateListener)].
public void addAdapterStateListener(AdapterStateListener l) {
if (l == null) {
return;
}
synchronized (this) {
if (adapterListeners == null) {
adapterListeners = new ArrayList<AdapterStateListener>();
}
if (!adapterListeners.contains(l)) {
adapterListeners.add(l);
}
}
}

/// Removes a listener previously added via
/// [#addAdapterStateListener(AdapterStateListener)].
public void removeAdapterStateListener(AdapterStateListener l) {
synchronized (this) {
if (adapterListeners != null) {
adapterListeners.remove(l);
}
}
}

/// Called by ports (from any thread) when the adapter transitions to a
/// new state; dispatches to the registered listeners on the EDT.
protected final void fireAdapterStateChanged(AdapterState newState) {
Object[] snapshot;
synchronized (this) {
if (adapterListeners == null || adapterListeners.isEmpty()) {
return;
}
snapshot = adapterListeners.toArray();
}
dispatchAdapterState(snapshot, newState);
}

// The dispatch lives in a static method so the Runnable doesn't carry a
// synthetic outer-Bluetooth reference (SpotBugs
// SIC_INNER_SHOULD_BE_STATIC_ANON).
private static void dispatchAdapterState(final Object[] snapshot,
final AdapterState newState) {
Display.getInstance().callSerially(new Runnable() {
@Override
public void run() {
for (Object listener : snapshot) {
((AdapterStateListener) listener)
.adapterStateChanged(newState);
}
}
});
}

/// Asks the user to enable the Bluetooth adapter where the platform
/// supports it (the Android system dialog). Resolves `true` when the
/// adapter was enabled, `false` when the user declined or the platform
/// offers no programmatic enable flow (iOS, and this fallback base
/// class). Never fails just because the feature is unsupported.
public AsyncResource<Boolean> requestEnable() {
AsyncResource<Boolean> r = new AsyncResource<Boolean>();
r.complete(Boolean.FALSE);
return r;
}

/// The BLE central role entry point -- scanning, connections, GATT
/// client. Never `null`: on ports without BLE a no-op instance is
/// returned whose operations fail fast with
/// [BluetoothError#NOT_SUPPORTED]; branch via [#isLeSupported()].
public com.codename1.bluetooth.le.BluetoothLE getLE() {
return DefaultLE.INSTANCE;
}

/// Lazy holder so the fallback LE instance is only created when a
/// port without BLE support is actually asked for it.
private static final class DefaultLE {
static final com.codename1.bluetooth.le.BluetoothLE INSTANCE =
new com.codename1.bluetooth.le.BluetoothLE() {
};
}

/// The classic Bluetooth role entry point -- discovery, bonding and
/// RFCOMM streams. Never `null`: on ports without classic Bluetooth
/// (iOS among them) a no-op instance is returned whose operations fail
/// fast with [BluetoothError#NOT_SUPPORTED]; branch via
/// [#isClassicSupported()].
public com.codename1.bluetooth.classic.BluetoothClassic getClassic() {
return DefaultClassic.INSTANCE;
}

/// Lazy holder so the fallback classic instance is only created when
/// a port without classic support is actually asked for it.
private static final class DefaultClassic {
static final com.codename1.bluetooth.classic.BluetoothClassic
INSTANCE = new com.codename1.bluetooth.classic.BluetoothClassic() {
};
}

/// `true` when the given runtime permission is currently granted. See
/// [BluetoothPermission] for the per-platform mapping. Defaults to
/// `false`.
public boolean hasPermission(BluetoothPermission permission) {
return false;
}

/// Requests the given runtime permissions, prompting the user where
/// needed. Resolves `true` when every requested permission is granted.
/// Resolves `false` (rather than failing) when denied or when the
/// platform has no Bluetooth support at all.
public AsyncResource<Boolean> requestPermissions(
BluetoothPermission... permissions) {
AsyncResource<Boolean> r = new AsyncResource<Boolean>();
r.complete(Boolean.FALSE);
return r;
}
}
Loading
Loading