Android Carrier SDK
JNI wrapper for Carrier on Android: identity, friends, messages with receipts, Express offline delivery, files and sessions. The public coordinate is the 2022 upstream build; the 16 KB-aligned builds Beagle ships are not published yet.
implementation 'org.elastos:carrier:6.0.4'Integration verified on 2026-11-06: a minimal Android app depending on
org.elastos:carrier:6.0.4from Maven Central was built with./gradlew assembleDebug(JDK 17.0.15, Gradle 8.7, Android Gradle Plugin 8.5.1, compileSdk 34, minSdk 21), then run on an Android 14 arm64 emulator: the native libraries loaded, an identity was created, and the node reachedonReadyandConnectedthrough the public bootstrap nodes. Friend, message, file and session samples below compile against the same artifact; they were not exercised against a second peer.
The Android Carrier SDK is a Java wrapper (JNI) around the native Carrier C library. It gives an Android app a self-generated Carrier identity, a friend list, end-to-end encrypted friend messages with delivery receipts, offline delivery through Express nodes, pull-based file transfer, and sessions/streams for raw peer-to-peer data. All classes live under org.elastos.carrier. The WebRTC calling SDK (/docs/android-webrtc) is built on top of it.
When to use it
Use it when you are writing a native Android app (Java or Kotlin) that must be a full Carrier peer on the device: it holds its own keys, talks to friends directly, and keeps working without any server you operate. Pick /docs/android-webrtc in addition when you need audio/video calls. If your code runs in Node.js or a browser, use the npm packages instead (see /docs/peer).
What you get today is the Elastos Carrier 6.0.4 binary on Maven Central. The public fork documented here (0xli/Elastos.NET.Carrier.Android.SDK, branch agent/permissionless-realtime-network) has the same Java API as that binary, plus one method, createIdentityAuthProof, which is a stub (see the API reference). No build of the fork itself is published.
Add it to your project
What you can resolve today is not what Beagle ships.
org.elastos:carrier:6.0.4on Maven Central is the original upstream Elastos build from 2022-11-01 (Maven metadatalastUpdated20221101); it was not built from the public fork documented on this page. It lacks 16 KB page-size alignment — in the 6.0.4 AAR,arm64-v8a/libcarrierjni.soand all fivex86_64libraries have 4 KB ELF segment alignment — so Google Play may reject an app built with it, and it lacks the fixes Decent Network has made since. The 16 KB-aligned Carrier builds that Beagle Chat for Android ships with are not published yet. Use 6.0.4 to develop and evaluate; contact developer support before shipping to Play.
The only consumable distribution is Maven Central (org.elastos:carrier:6.0.4, published 2022-11-01). It is not on JitPack (JitPack has no builds of the fork), and the 5.3.2 coordinate in the repository README does not exist on Maven Central. The old Elastos Nexus repository (nexus.elastos.org) no longer resolves in DNS.
settings.gradle:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral() // org.elastos:carrier:6.0.4 is published here
}
}app/build.gradle:
android {
compileSdk 34
defaultConfig {
minSdk 21
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'org.elastos:carrier:6.0.4'
}The AAR's manifest sets android:label on <application>, so the manifest merge fails unless you override it. It already declares INTERNET.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<!-- The Carrier AAR's manifest also sets android:label; override it. -->
<application
android:label="Carrier sample"
tools:replace="android:label">
...
</application>
</manifest>What the AAR contains:
| Item | Detail |
|---|---|
| Java classes | org.elastos.carrier, .session, .filetransfer, .exceptions |
| Native libraries | libcarrierjni.so, libcarrier.so, libcarriersession.so, libcarrierfiletrans.so, libcrystal.so |
| ABIs | armeabi-v7a, arm64-v8a, x86, x86_64 |
| Min SDK | 21 |
| License | GPLv3 (per the POM and the repository LICENSE) |
In the 6.0.4 AAR, arm64-v8a/libcarrierjni.so is built with 4 KB ELF segment alignment (the other four arm64-v8a libraries use 64 KB), and all five x86_64 libraries use 4 KB. It therefore does not meet Google Play's 16 KB page-size requirement for apps targeting Android 15 and later. Plan for a rebuilt AAR if you ship on Play.
Building the fork from source is not practical today. Its Gradle files use Android Gradle Plugin 3.3.2 and jcenter(), and the prebuilt native libraries it expects in app/native-dist/lib/ are not in git. The download script .script/download_libs.sh points at a release page that returns 404. You would need to cross-compile the native Carrier SDK for Android yourself and modernise the Gradle build. The native SDK's dependency downloads work again as of release v6.1.0 (its c-toxcore source now comes from a mirror release), but an Android cross-compile of v6.1.0 has not been verified for this page.
Initialize
Create a Carrier.Options with a persistent directory and at least one bootstrap node. Then call Carrier.createInstance(options, handler), followed by start(). The SDK defines no default bootstrap nodes. The two below are the public nodes also listed in the @decentnetwork/peer documentation.
import org.elastos.carrier.AbstractCarrierHandler;
import org.elastos.carrier.Carrier;
import org.elastos.carrier.ConnectionStatus;
import org.elastos.carrier.FriendInfo;
import org.elastos.carrier.UserInfo;
import org.elastos.carrier.exceptions.CarrierException;
public class MainActivity extends Activity {
private static final String TAG = "CarrierSample";
private final Handler main = new Handler(Looper.getMainLooper());
private Carrier carrier;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "Carrier native version: " + Carrier.getVersion());
File dir = new File(getFilesDir(), "carrier");
dir.mkdirs();
Carrier.Options options = new Carrier.Options();
options.setPersistentLocation(dir.getAbsolutePath()); // keys + friend list live here
options.setUdpEnabled(true);
List<Carrier.Options.BootstrapNode> bootstraps = new ArrayList<>();
bootstraps.add(new Carrier.Options.BootstrapNode()
.setIpv4("47.100.103.201").setPort("33445")
.setPublicKey("CX1XH419p4xJ5SV4KvDxBeKYSRdMJW9QpdWJY8owUxHd"));
bootstraps.add(new Carrier.Options.BootstrapNode()
.setIpv4("154.64.235.176").setPort("33445")
.setPublicKey("GdNtV2N74fZnLjhH7NhQ18nGdxb1k8jRM9dQaK7WnxmL"));
options.setBootstrapNodes(bootstraps);
try {
carrier = Carrier.createInstance(options, new AbstractCarrierHandler() {
@Override
public void onConnection(Carrier c, ConnectionStatus status) {
Log.i(TAG, "connection: " + status);
}
@Override
public void onReady(Carrier c) {
// Carrier thread: hop to the main thread before touching UI.
main.post(() -> Log.i(TAG, "ready"));
}
@Override
public void onFriendRequest(Carrier c, String userId, UserInfo info, String hello) {
try {
c.acceptFriend(userId);
} catch (CarrierException e) {
Log.e(TAG, "acceptFriend", e);
}
}
@Override
public void onFriendAdded(Carrier c, FriendInfo info) {
Log.i(TAG, "friend added: " + info.getUserId());
}
@Override
public void onFriendMessage(Carrier c, String from, byte[] message,
Date timestamp, boolean isOffline) {
Log.i(TAG, from + (isOffline ? " (offline): " : ": ")
+ new String(message, StandardCharsets.UTF_8));
}
});
Log.i(TAG, "address: " + carrier.getAddress()); // share this to be added as a friend
Log.i(TAG, "userId: " + carrier.getUserId());
Log.i(TAG, "nodeId: " + carrier.getNodeId());
carrier.start(0); // spawns the Carrier thread (the SDK tests also pass 0)
} catch (CarrierException e) {
Log.e(TAG, "createInstance failed", e);
}
}
@Override
protected void onDestroy() {
if (carrier != null) carrier.kill();
super.onDestroy();
}
}Verified output (org.elastos:carrier:6.0.4, Android 14 arm64 emulator, 2026-11-06), from adb logcat -s CarrierSample CarrierCore:
I CarrierSample: Carrier native version: 60bd828f
I CarrierCore: Carrier node instance created
I CarrierSample: address: 8KVejycnD6Kv42HiPH9YLKcDfgB7dg35vwQjLG2pzKYD5942N6Ub
I CarrierSample: userId: 4L8hDmhCKHuRECoZwPHPbK2rQHwtT4tKwsrorYhG9Piy
I CarrierSample: nodeId: 4L8hDmhCKHuRECoZwPHPbK2rQHwtT4tKwsrorYhG9Piy
I CarrierCore: Native carrier node started
I CarrierSample: ready
I CarrierSample: connection: Connected[0]Carrier.Options setters:
| Setter | Notes |
|---|---|
setPersistentLocation(String) | Required. Holds the identity keys and the friend list. Reuse the same directory to keep the same identity. |
setSecretKey(byte[]) | Optional, exactly 32 bytes. Seeds a new identity. Ignored when persistent data already exists. |
setUdpEnabled(boolean) | false forces TCP only (slower). |
setBootstrapNodes(List<BootstrapNode>) | Required in practice; the SDK has no defaults. BootstrapNode has setIpv4, setIpv6, setPort (a String), setPublicKey. |
setExpressNodes(List<ExpressNode>) | Optional offline-message relays. ExpressNode has setIpv4, setPort, setPublicKey. |
Identity
String address = carrier.getAddress(); // share this to be added as a friend
String userId = carrier.getUserId(); // stable public key; used to address friends
String nodeId = carrier.getNodeId();
boolean ok = Carrier.isValidAddress(address);
String idFromAddr = Carrier.getIdFromAddress(address); // == userId
UserInfo me = carrier.getSelfInfo();
me.setName("Alice");
me.setDescription("Android demo");
carrier.setSelfInfo(me);
carrier.setPresence(PresenceStatus.Away);
// Rotating nospam changes the address (old address stops accepting
// new friend requests); userId stays the same.
carrier.setNospam(carrier.getNospam() + 1);- address: Base58, 38 bytes decoded (public key plus nospam and checksum). Use it only for
addFriend. - userId: Base58, 32 bytes decoded. It identifies a peer in every other call.
- nodeId: on this build it equals the
userId(see the verified output above).
Friends
carrier.addFriend(friendAddress, "hi, it's Alice"); // request; peer gets onFriendRequest
// receiving side, inside CarrierHandler.onFriendRequest(...):
// carrier.acceptFriend(userId);
if (carrier.isFriend(friendUserId)) {
FriendInfo f = carrier.getFriend(friendUserId);
Log.i(TAG, f.getName() + " " + f.getConnectionStatus() + " " + f.getPresence());
carrier.labelFriend(friendUserId, "work");
}
List<FriendInfo> all = carrier.getFriends();
carrier.removeFriend(friendUserId);These handler callbacks report friend events: onFriendRequest, onFriendAdded, onFriendRemoved, onFriendConnection (ConnectionStatus.Connected / Disconnected), onFriendPresence (PresenceStatus.None / Away / Busy) and onFriendInfoChanged. The SDK also reports the whole list once through onFriends(carrier, List<FriendInfo>).
Messages
long msgId = carrier.sendFriendMessage(friendUserId, "hello",
(messageId, state) -> {
if (state == ReceiptState.ReceiptByFriend) {
Log.i(TAG, messageId + " delivered");
} else if (state == ReceiptState.DeliveredAsOffline) {
Log.i(TAG, messageId + " stored for offline delivery");
} else {
Log.w(TAG, messageId + " failed");
}
});
// App-level request/response ("invite") carrying a string payload:
carrier.inviteFriend(friendUserId, "{\"op\":\"ping\"}",
(from, status, reason, data) -> Log.i(TAG, "invite reply " + status + " " + data));
// receiving side, inside CarrierHandler.onFriendInviteRequest(c, from, data):
// c.replyFriendInvite(from, 0, null, "{\"op\":\"pong\"}");sendFriendMessagehas two overloads:Stringandbyte[]. Both require a non-nullFriendMessageReceiptHandlerand return a message id. The String form rejects messages ofCarrier.CARRIER_MAX_USER_NAME_LEN(5 MiB) or longer. Split larger payloads yourself.- Incoming messages arrive in
onFriendMessage(carrier, from, byte[] message, Date timestamp, boolean isOffline).
Files
File transfer is pull-driven: the receiver asks for data from an offset, and the sender writes it. Create one filetransfer.Manager per Carrier instance and keep it.
Sender:
org.elastos.carrier.filetransfer.Manager ftm =
org.elastos.carrier.filetransfer.Manager.createInstance(carrier);
final FileTransferInfo info = new FileTransferInfo("photo.jpg", null, bytes.length);
FileTransfer ft = ftm.newFileTransfer(friendUserId, info, new FileTransferHandler() {
@Override public void onStateChanged(FileTransfer t, FileTransferState s) {
Log.i(TAG, "ft state " + s);
}
@Override public void onFileRequest(FileTransfer t, String fileId, String name, long size) { }
@Override public void onPullRequest(FileTransfer t, String fileId, long offset) {
try { // receiver asked for data starting at offset: push it, then finish
t.writeData(fileId, bytes, (int) offset, bytes.length - (int) offset);
t.sendFinish(fileId);
} catch (CarrierException e) {
Log.e(TAG, "writeData", e);
}
}
@Override public boolean onData(FileTransfer t, String fileId, byte[] data) { return true; }
@Override public void onDataFinished(FileTransfer t, String fileId) { }
@Override public void onPending(FileTransfer t, String fileId) { }
@Override public void onResume(FileTransfer t, String fileId) { }
@Override public void onCancel(FileTransfer t, String fileId, int status, String reason) { }
});
ft.connect(); // peer gets ManagerHandler.onConnectRequest(carrier, from, info)Receiver:
final org.elastos.carrier.filetransfer.Manager[] ftm =
new org.elastos.carrier.filetransfer.Manager[1];
ftm[0] = org.elastos.carrier.filetransfer.Manager.createInstance(carrier,
(c, from, info) -> {
try {
FileTransfer ft = ftm[0].newFileTransfer(from, info, new FileTransferHandler() {
@Override public void onStateChanged(FileTransfer t, FileTransferState s) {
if (s == FileTransferState.Connected) {
try { t.pullData(info.getFileId(), 0); }
catch (CarrierException e) { Log.e(TAG, "pull", e); }
}
}
@Override public void onFileRequest(FileTransfer t, String id, String n, long sz) { }
@Override public void onPullRequest(FileTransfer t, String id, long off) { }
@Override public boolean onData(FileTransfer t, String id, byte[] data) {
// append data to your output file; return true to keep receiving
return true;
}
@Override public void onDataFinished(FileTransfer t, String id) { t.close(); }
@Override public void onPending(FileTransfer t, String id) { }
@Override public void onResume(FileTransfer t, String id) { }
@Override public void onCancel(FileTransfer t, String id, int st, String r) { }
});
ft.acceptConnect();
} catch (CarrierException e) {
Log.e(TAG, "accept file", e);
}
});Other FileTransfer controls: addFile(FileTransferInfo), pendTransfer(fileId) / resumeTransfer(fileId) for flow control, cancelTransfer(fileId, status, reason), and getOutputStream(fileId). You can resume from a break point by calling pullData with a non-zero offset. There is no AbstractFileTransferHandler, so you implement all eight callbacks.
Sessions and calls
A session carries raw streams (reliable or unreliable, optionally multiplexed into channels, or port-forwarding) between two friends over ICE. Create one session.Manager per Carrier instance. For audio/video calls, use /docs/android-webrtc; it does its own signaling over Carrier and does not use this session API.
Initiator: add a stream, send the request once the stream is Initialized, then start() with the peer's SDP:
org.elastos.carrier.session.Manager sm =
org.elastos.carrier.session.Manager.createInstance(carrier);
final Session session = sm.newSession(friendUserId);
session.addStream(StreamType.Application, Stream.PROPERTY_RELIABLE,
new AbstractStreamHandler() {
@Override
public void onStateChanged(Stream stream, StreamState state) {
if (state == StreamState.Initialized) {
try {
session.request((s, status, reason, sdp) -> {
if (status == 0) {
try { s.start(sdp); }
catch (CarrierException e) { Log.e(TAG, "start", e); }
}
});
} catch (CarrierException e) {
Log.e(TAG, "request", e);
}
} else if (state == StreamState.Connected) {
try { stream.writeData("hello over a stream".getBytes()); }
catch (CarrierException e) { Log.e(TAG, "write", e); }
}
}
@Override
public void onStreamData(Stream stream, byte[] data) {
Log.i(TAG, "stream data " + data.length);
}
});Responder: the ManagerHandler receives onSessionRequest(carrier, from, sdp). Create a session back to from, add a matching stream, and on TransportReady reply and start:
final org.elastos.carrier.session.Manager[] sm =
new org.elastos.carrier.session.Manager[1];
sm[0] = org.elastos.carrier.session.Manager.createInstance(carrier, (c, from, sdp) -> {
try {
final Session session = sm[0].newSession(from);
session.addStream(StreamType.Application, Stream.PROPERTY_RELIABLE,
new AbstractStreamHandler() {
@Override
public void onStateChanged(Stream stream, StreamState state) {
if (state == StreamState.TransportReady) {
try {
session.replyRequest(0, null);
session.start(sdp);
} catch (CarrierException e) {
Log.e(TAG, "reply/start", e);
}
}
}
});
} catch (CarrierException e) {
Log.e(TAG, "session", e);
}
});Stream states go Initialized → TransportReady → Connecting → Connected, then Deactivated / Closed / Error. Stream.PROPERTY_* flags are COMPRESS, PLAIN, RELIABLE, MULTIPLEXING and PORT_FORWARDING. With MULTIPLEXING, use openChannel / writeData(channel, …) / closeChannel. With PORT_FORWARDING, the responder calls session.addService(name, PortForwardingProtocol.TCP, host, port) and the initiator calls stream.openPortForwarding(name, PortForwardingProtocol.TCP, host, port).
Offline messages (Express)
If you configure Express nodes in Options, a message to an offline friend is stored on an Express node. The sender then gets ReceiptState.DeliveredAsOffline in its receipt handler. The receiver gets the message later through onFriendMessage(…, isOffline = true). Without Express nodes there is no offline store, so a friend has to be online to receive a message.
List<Carrier.Options.ExpressNode> express = new ArrayList<>();
express.add(new Carrier.Options.ExpressNode()
.setIpv4("EXPRESS_HOST")
.setPort("443")
.setPublicKey("EXPRESS_PUBLIC_KEY"));
options.setExpressNodes(express);setIpv4 takes a host name or an IP address. Get the host, port and public key from the Express operator you use. Offline delivery was not exercised in this verification.
Threading and lifecycle
Carrier.createInstanceis synchronous and loadslibcarrierjnion first use of the class.getAddress()/getUserId()work immediately, beforestart().start(int iterateInterval)spawns one Java thread that runs the native event loop. EveryCarrierHandlercallback runs on that thread. In the verified run,onConnectionarrived on the Carrier thread, not the main thread. Post to a main-threadHandlerbefore you touch views.- Don't block inside callbacks.
onIdlefires on every loop iteration. File-transfer and stream callbacks also come from native threads. onReadyfires once the node has joined the network.onConnection(Connected/Disconnected)tracks the link to the network.kill()stops the loop, joins the thread and invalidates the instance; create a new one to reconnect. Callcleanup()on session and file-transfer managers beforekill(). Run oneCarrierper persistent directory.- Errors throw
CarrierException(getErrorCode(),getFacility(),getCode()), with subclassesGeneralException,SystemException,ICEException,DHTExceptionandUnknownException.
API reference
Carrier
| Member | Signature |
|---|---|
| create | static Carrier createInstance(Options options, CarrierHandler handler) throws CarrierException |
| run / stop | void start(int iterateInterval), synchronized void kill(), boolean isReady() |
| static helpers | static String getVersion(), static boolean isValidId(String), static boolean isValidAddress(String), static String getIdFromAddress(String) |
| identity | String getAddress(), String getUserId(), String getNodeId(), void setNospam(int), int getNospam() |
| profile | UserInfo getSelfInfo(), void setSelfInfo(UserInfo), void setPresence(PresenceStatus), PresenceStatus getPresence() |
| friends | void addFriend(String address, String hello), void acceptFriend(String userId), void removeFriend(String userId), boolean isFriend(String userId), FriendInfo getFriend(String userId), List<FriendInfo> getFriends(), void labelFriend(String userId, String label) |
| messages | long sendFriendMessage(String to, String message, FriendMessageReceiptHandler handler), long sendFriendMessage(String to, byte[] message, FriendMessageReceiptHandler handler) |
| invites | void inviteFriend(String to, String data, FriendInviteResponseHandler handler), void replyFriendInvite(String to, int status, String reason, String data) |
| groups | Group newGroup(), Group groupJoin(String friendId, byte[] cookie), void groupLeave(Group), Collection<Group> getGroups() |
| fork only | byte[] createIdentityAuthProof(byte[] serverPublicKey, byte[] transcript): present in the fork's Java source but not in 6.0.4, and its JNI side is a "not implemented" stub that is not even registered. Don't use it. |
CarrierHandler (extend AbstractCarrierHandler to override only what you need)
onIdle, onConnection(Carrier, ConnectionStatus), onReady, onSelfInfoChanged(Carrier, UserInfo), onFriends(Carrier, List<FriendInfo>), onFriendConnection(Carrier, String, ConnectionStatus), onFriendInfoChanged(Carrier, String, FriendInfo), onFriendPresence(Carrier, String, PresenceStatus), onFriendRequest(Carrier, String userId, UserInfo, String hello), onFriendAdded(Carrier, FriendInfo), onFriendRemoved(Carrier, String), onFriendMessage(Carrier, String from, byte[], Date, boolean isOffline), onFriendInviteRequest(Carrier, String from, String data), onGroupInvite(Carrier, String from, byte[] cookie), plus the GroupHandler methods onGroupConnected, onGroupMessage, onGroupTitle, onPeerName, onPeerListChanged.
Value types
| Type | Values / members |
|---|---|
ConnectionStatus | Connected, Disconnected |
PresenceStatus | None, Away, Busy |
ReceiptState | ReceiptByFriend, DeliveredAsOffline, Error |
UserInfo | getUserId, get/setName, get/setDescription, hasAvatar/setHasAvatar, get/setGender, get/setPhone, get/setEmail, get/setRegion |
FriendInfo extends UserInfo | get/setLabel, getConnectionStatus, getPresence |
Group | getId, invite(String friendId), sendMessage(byte[]) (max Group.MAX_APP_MESSAGE_LEN = 1024), getTitle / setTitle, getPeers(), getPeer(String) |
org.elastos.carrier.filetransfer
| Class | Members |
|---|---|
Manager | static Manager createInstance(Carrier), static Manager createInstance(Carrier, ManagerHandler), FileTransfer newFileTransfer(String to, FileTransferInfo fileinfo, FileTransferHandler handler), cleanup() |
ManagerHandler | void onConnectRequest(Carrier carrier, String from, FileTransferInfo info) |
FileTransfer | static String generateFileId(), connect(), acceptConnect(), addFile(FileTransferInfo), pullData(String fileId, long offset), writeData(String fileId, byte[] data[, int offset, int len]), getOutputStream(String fileId), sendFinish(String fileId), cancelTransfer(String fileId, int status, String reason), pendTransfer(String), resumeTransfer(String), getFileId(String filename), getFileName(String fileId), close() |
FileTransferInfo | FileTransferInfo(String filename, String fileId /* null = generate */, long size), getFileName(), getFileId(), getSize() |
FileTransferHandler | onStateChanged, onFileRequest, onPullRequest, boolean onData, onDataFinished, onPending, onResume, onCancel |
FileTransferState | Initialized, Connecting, Connected, Closed, Failed |
org.elastos.carrier.session
| Class | Members |
|---|---|
Manager | static Manager createInstance(Carrier), static Manager createInstance(Carrier, ManagerHandler), Session newSession(String to), cleanup() |
ManagerHandler | void onSessionRequest(Carrier carrier, String from, String sdp) |
Session | getPeer(), request(SessionRequestCompleteHandler), replyRequest(int status, String reason), start(String sdp), Stream addStream(StreamType type, int options, StreamHandler handler), removeStream(Stream), addService(String service, PortForwardingProtocol protocol, String host, String port), removeService(String), close() |
SessionRequestCompleteHandler | void onCompletion(Session session, int status, String reason, String sdp) |
Stream | getStreamId(), getType(), getTransportInfo(), writeData(byte[] …), getOutputStream(), openChannel(String cookie), closeChannel(int), writeData(int channel, byte[] …), pendChannel(int), resumeChannel(int), openPortForwarding(String service, PortForwardingProtocol protocol, String host, String port), closePortForwarding(int) |
StreamHandler / AbstractStreamHandler | onStateChanged, onStreamData, boolean onChannelOpen, onChannelOpened, onChannelClose, boolean onChannelData, onChannelPending, onChannelResume |
| enums | StreamType (Audio, Video, Text, Application, Message), StreamState, CloseReason, PortForwardingProtocol (TCP) |
CarrierExtension
An abstract class meant for the WebRTC SDK (its own docs advise ordinary apps not to use it). It provides a second friend-invite channel (inviteFriend, replyFriendInvite, abstract onFriendInvite) and getTurnServerInfo(), which returns the TURN server advertised by the Carrier network. All of its members are protected.
Source
- Repository: https://github.com/0xli/Elastos.NET.Carrier.Android.SDK (public, GPLv3)
- Branch documented:
agent/permissionless-realtime-network, commit38d5a77239e102edae3f613b7d0637fe69aee259(2026-08-02) - Binary verified:
org.elastos:carrier:6.0.4on Maven Central, https://repo1.maven.org/maven2/org/elastos/carrier/6.0.4/ (published 2022-11-01; its-sources.jarmatches the branch's Java sources exceptcreateIdentityAuthProof) - Native C library: https://github.com/0xli/Elastos.NET.Carrier.Native.SDK