SocketX Client for Android
Introduction
This Android library provides the Eclypses SocketX Mobile Client for Android. It enables secure, persistent WebSocket communication between your Android app and your backend services via a SocketX server. You must have licensed access to a SocketX server instance. More Info
Purpose of SocketX:
- Establish persistent, encrypted WebSocket tunnels to your server
- Protect real-time data with MTE encryption
- Support bidirectional messaging with automatic encryption/decryption
- Enable secure pub/sub and room-based communication patterns
Documentation
📚 Complete README with Java Examples - Comprehensive documentation with detailed Java and Kotlin examples, troubleshooting, and API reference
💡 This quick-start guide provides Kotlin examples for experienced Android developers. For Java examples, detailed troubleshooting, and comprehensive documentation, see the GitHub README.
Prerequisites
- Android 8.0 (API 26) or later - Required for modern WebSocket support and cryptographic capabilities
- Kotlin 1.9+ / Java 8+ - Library is Kotlin-based but fully Java-compatible
- OkHttp 4.12.0 or later - Library wraps OkHttp WebSocket implementation
- Access to a licensed SocketX server instance - Server URL and proper licensing credentials required
How SocketX Works
Traditional WebSocket (Unencrypted):
[Your Android App] ←→ WebSocket ←→ [Your Backend Server]
⚠️ Data transmitted in plaintext (even with WSS/TLS, data is visible at endpoints)
With SocketX (MTE-Encrypted):
[Your Android App] ←→ Encrypted WebSocket Tunnel ←→ [SocketX Server] ←→ [Your Backend Service]
↑ ↑
Encrypts here Decrypts & forwards here
Data Flow:
- Pairing: Automatic MTE pairing handshake using post-quantum Kyber-512 key exchange
- Encode: Data encrypted using MTE before transmission
- Tunnel: Encrypted payload travels over WebSocket to SocketX server
- Decode: Server decrypts payload using paired MTE decoder
- Forward: Server forwards original data to designated backend service or room
- Response: Response follows same path in reverse
Key Benefits:
- End-to-end encryption that's quantum-resistant (Kyber-512)
- Zero-trust architecture - data encrypted before leaving your app
- Persistent connections for real-time, low-latency messaging
- Automatic reconnection and pairing management
Installation
Gradle (Recommended)
Kotlin DSL (build.gradle.kts):
dependencies {
implementation("com.eclypses:socketx-client-android:1.0.8")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
}
Groovy DSL (build.gradle):
dependencies {
implementation 'com.eclypses:socketx-client-android:1.0.8'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}
Quick Start
1. Add Internet Permission
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
2. Basic Implementation
import okhttp3.*
import okio.ByteString.Companion.toByteString
import com.eclypses.socketx_client_android.SocketXClient
import com.eclypses.socketx_client_android.SocketXError
class ChatManager(private val serverUrl: String, private val roomPath: String) {
private var okHttpClient: OkHttpClient? = null
private var socketXClient: SocketXClient? = null
private var webSocket: WebSocket? = null
var isConnected = false
fun connect() {
// 1. Configure OkHttp client with custom settings
okHttpClient = OkHttpClient.Builder()
.pingInterval(20, TimeUnit.SECONDS)
.addInterceptor { chain ->
val request = chain.request().newBuilder()
.header("Authorization", "Bearer your-token")
.build()
chain.proceed(request)
}
.build()
// 2. Create SocketX factory (wraps OkHttp client)
socketXClient = SocketXClient(okHttpClient!!)
// 3. Build WebSocket request
val request = Request.Builder()
.url("$serverUrl$roomPath")
.build()
// 4. Create WebSocket listener
val listener = object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
// Called after MTE handshake completes
isConnected = true
println("✅ Connected and paired")
}
override fun onMessage(webSocket: WebSocket, text: String) {
// Automatically decrypted
println("📨 Received: $text")
}
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
// Automatically decrypted
println("📦 Received: ${bytes.size} bytes")
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
isConnected = false
when (t) {
is SocketXError.HandshakeError -> println("❌ Handshake failed")
is SocketXError.CodecError -> println("❌ Encryption error")
is SocketXError.NetworkError -> println("❌ Network error")
}
}
}
// 5. Create secure WebSocket (automatically encrypts/decrypts)
webSocket = socketXClient!!.newWebSocket(request, listener)
}
fun sendMessage(text: String) {
webSocket?.send(text) // Automatically encrypted
}
fun sendBinary(data: ByteArray) {
webSocket?.send(data.toByteString()) // Automatically encrypted
}
fun disconnect() {
webSocket?.close(1000, "Goodbye")
isConnected = false
}
}
Java Developers: See the GitHub README for complete Java examples.
3. Usage in Activity
class ChatActivity : AppCompatActivity() {
private lateinit var chatManager: ChatManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
chatManager = ChatManager("wss://your-server.com", "/chat-room-1")
chatManager.connect()
sendButton.setOnClickListener {
chatManager.sendMessage(messageEditText.text.toString())
}
}
override fun onDestroy() {
super.onDestroy()
chatManager.disconnect()
}
}
Usage Examples
Sending Messages
Text Messages:
// Simple text
webSocket?.send("Hello, World!")
// JSON
val json = JSONObject().apply {
put("type", "chat")
put("message", "Hello")
}.toString()
webSocket?.send(json)
Binary Data:
// Image
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.photo)
val stream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream)
webSocket?.send(stream.toByteArray().toByteString())
// File
val fileData = File("/path/to/file.pdf").readBytes()
webSocket?.send(fileData.toByteString())
Receiving Messages
Text Messages:
override fun onMessage(webSocket: WebSocket, text: String) {
// Parse JSON
val json = JSONObject(text)
val messageType = json.getString("type")
val content = json.getString("message")
runOnUiThread {
// Update UI
}
}
Binary Data:
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
val data = bytes.toByteArray()
// Display image
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
runOnUiThread {
imageView.setImageBitmap(bitmap)
}
}
Room-Based Communication
// Connect to different rooms
val chatManager = ChatManager("wss://server.com", "/chat/general")
val notificationManager = ChatManager("wss://server.com", "/notifications/user123")
chatManager.connect()
notificationManager.connect()
// Messages sent to each room are isolated
chatManager.sendMessage("Hello to chat room")
notificationManager.sendMessage("Status update")
URL Path Examples:
/chat/general- General chat room/notifications/user123- User-specific notifications/game/match-456- Game-specific room/stream/video-1- Streaming channel
Error Handling
Error Types
sealed class SocketXError : Throwable() {
class HandshakeError(message: String) : SocketXError() // MTE pairing failed
class CodecError(message: String) : SocketXError() // Encryption/decryption error
class NetworkError(message: String) : SocketXError() // Connection error
class InternalError(message: String) : SocketXError() // License/config error
}
Comprehensive Error Handling
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
when (t) {
is SocketXError.HandshakeError -> {
// MTE pairing failed - check server configuration
Log.e(TAG, "Handshake failed: ${t.message}")
}
is SocketXError.CodecError -> {
// Encryption error - reconnect to re-pair
Log.e(TAG, "Codec error: ${t.message}")
scheduleReconnect()
}
is SocketXError.NetworkError -> {
// Network issue - check connectivity
Log.e(TAG, "Network error: ${t.message}")
if (isNetworkAvailable()) {
scheduleReconnect()
}
}
is SocketXError.InternalError -> {
// Configuration issue - check MTE license
Log.e(TAG, "Internal error: ${t.message}")
}
}
}
Advanced Configuration
Custom OkHttp Configuration
val okHttpClient = OkHttpClient.Builder()
// Timeouts
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
// Keep-alive
.pingInterval(20, TimeUnit.SECONDS)
// Custom headers
.addInterceptor { chain ->
val request = chain.request().newBuilder()
.header("Authorization", "Bearer $token")
.header("X-API-Key", apiKey)
.build()
chain.proceed(request)
}
// Certificate pinning
.certificatePinner(
CertificatePinner.Builder()
.add("your-domain.com", "sha256/AAAA...")
.build()
)
// Retry on failure
.retryOnConnectionFailure(true)
.build()
val socketXClient = SocketXClient(okHttpClient)
Connection Lifecycle Management
class ChatManager {
private val handler = Handler(Looper.getMainLooper())
private var reconnectAttempts = 0
private val maxReconnectAttempts = 5
private fun scheduleReconnect() {
if (reconnectAttempts >= maxReconnectAttempts) {
Log.e(TAG, "Max reconnect attempts reached")
return
}
val delay = (2.0.pow(reconnectAttempts) * 1000).toLong() // Exponential backoff
reconnectAttempts++
handler.postDelayed({
connect()
}, delay)
}
override fun onOpen(webSocket: WebSocket, response: Response) {
reconnectAttempts = 0 // Reset on success
isConnected = true
}
}
Background Connection Management
class ChatService : Service() {
private lateinit var chatManager: ChatManager
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// Create foreground notification
val notification = createNotification()
startForeground(NOTIFICATION_ID, notification)
// Connect
chatManager.connect()
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
chatManager.disconnect()
}
}
Troubleshooting
Connection Issues
Problem: Connection fails immediately
✅ Verify URL format: wss://your-server.com/room (not https://)
✅ Check network permissions in AndroidManifest.xml
✅ Ensure server is running and accessible
✅ Test with wscat -c wss://your-server.com/test
Problem: Handshake error during connection
✅ Verify MTE license is valid
✅ Check server MTE configuration
✅ Review server logs for rejection reasons
Problem: Works in emulator but not on device
✅ Check device network connectivity
✅ Verify server URL is accessible (not localhost)
✅ Review certificate pinning if configured
Message Issues
Problem: Messages not received by server
✅ Verify connection before sending (isConnected)
✅ Check server logs
✅ Verify correct room path
Problem: Cannot receive messages
✅ Set up callbacks before calling connect()
✅ Ensure messages are sent to correct room
Problem: Codec errors
✅ Disconnect and reconnect to re-establish pairing
✅ Verify compatible MTE versions
✅ Check for data corruption
Performance Issues
Problem: High memory usage
✅ Don't accumulate messages indefinitely (limit collection size)
✅ Process binary data immediately
✅ Disconnect when not needed
Problem: Connection drops in background
✅ Normal Android behavior - apps suspended in background
✅ Use foreground service for persistent connections
✅ Reconnect in onResume()
API Reference
SocketXClient
Constructor:
SocketXClient(client: OkHttpClient)
Creates a factory for secure WebSocket connections. Validates MTE licensing.
Throws: SocketXError.InternalError if MTE license validation fails
Method:
fun newWebSocket(request: Request, listener: WebSocketListener): WebSocket
Creates a secure WebSocket wrapper that:
- Uses provided OkHttpClient for underlying connection
- Automatically performs MTE pairing handshake
- Transparently encrypts/decrypts all messages
- Calls
onOpenAFTER handshake completes
Returns: WebSocket instance with MTE encryption
WebSocket (Standard OkHttp)
fun send(text: String): Boolean // Send text (auto-encrypted)
fun send(bytes: ByteString): Boolean // Send binary (auto-encrypted)
fun close(code: Int, reason: String?) // Close connection
fun cancel() // Immediately release resources
WebSocketListener Callbacks
onOpen(webSocket: WebSocket, response: Response)
// Called after MTE handshake completes
onMessage(webSocket: WebSocket, text: String)
// Received text message (auto-decrypted)
onMessage(webSocket: WebSocket, bytes: ByteString)
// Received binary data (auto-decrypted)
onClosing(webSocket: WebSocket, code: Int, reason: String)
// Remote peer initiated shutdown
onClosed(webSocket: WebSocket, code: Int, reason: String)
// Connection fully closed
onFailure(webSocket: WebSocket, t: Throwable, response: Response?)
// Error occurred (check SocketXError types)
Contact Eclypses
Email: info@eclypses.com
Web: www.eclypses.com
All trademarks of Eclypses Inc. may not be used without Eclypses Inc.'s prior written consent.