MTE Relay Client for Android
Introduction
This library provides the Eclypses MteRelay Mobile Client for Android. It enables secure, encrypted HTTP(S) communication between your Android app and your backend via an MteRelay server. You must have licensed access to an MteRelay server instance. More Info
Purpose of MteRelay:
- Securely relay HTTP requests to your server
- Protect sensitive headers and data with MTE encryption
- Stream large files efficiently
- Support for both Volley and OkHttp networking libraries
This guide provides a quick-start for experienced developers. For detailed examples (both Kotlin and Java), troubleshooting, and in-depth explanations, see the complete documentation on GitHub.
Prerequisites
- Android API 26 (Android 8.0) or later
- Kotlin 1.9+ or Java 11+
- Access to a licensed MteRelay server instance
Installation
Gradle (Recommended)
Add the following to your module's build.gradle.kts or build.gradle:
dependencies {
implementation("com.eclypses:eclypses-aws-mte-relay-client-android-release:4.2.6")
// Required logging dependencies
implementation("org.slf4j:slf4j-api:2.0.9")
implementation("com.github.tony19:logback-android:3.0.0")
// Networking library (choose one based on your needs)
implementation("com.android.volley:volley:1.2.1") // For Volley
// OR
implementation("com.squareup.okhttp3:okhttp:4.12.0") // For OkHttp
}
The SLF4J and Logback dependencies are required. Without them, the library will crash with NoClassDefFoundError.
Permissions
Add to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Setup
- Set up and configure your MteRelay server to receive and decode requests from your application.
- Import the MteRelay classes:
import com.eclypses.mte.relay.Relay
import com.eclypses.mte.relay.callback.RelayResponseListener
- Create a
Relayinstance with a pairing callback:
val relay = Relay.getInstance(context, object : RelayResponseListener {
override fun onCompletion(success: Boolean, message: String) {
if (success) {
Log.d("Relay", "Pairing successful: $message")
} else {
Log.e("Relay", "Pairing failed: $message")
}
}
})
Usage
Simple Volley Request (Kotlin)
val url = "https://your-relay-server.com/api/users"
val request = JsonArrayRequest(Request.Method.GET, url, null, null, null)
val headersToEncrypt = arrayOf("content-type")
relay.addToMteRequestQueue(request, headersToEncrypt, object : RelayVolleyRequestListener {
override fun onError(statusCode: Int?, message: String, responseHeaders: Map<String, List<String>>?) {
Log.e("Relay", "Error: $message")
}
override fun onResponse(response: ByteArray, responseHeaders: Map<String, List<String>>) {
Log.d("Relay", "Response: ${String(response)}")
}
override fun onResponse(response: JSONObject, responseHeaders: Map<String, List<String>>) {
Log.d("Relay", "JSON Response: $response")
}
override fun onResponse(response: JSONArray, responseHeaders: Map<String, List<String>>) {
Log.d("Relay", "JSON Array Response: $response")
}
})
Streamed File Upload
Create request properties and upload file:
val file = File(context.filesDir, "upload.txt")
val serverUrl = "https://your-relay-server.com"
val route = "/api/files/upload"
// Prepare headers (including Content-Type and content-length)
val headers = mapOf(
"Content-Type" to "application/octet-stream",
"content-length" to file.length().toString()
)
val headersToEncrypt = arrayOf("Content-Type", "content-length")
// Create request properties with stream callback
val reqProperties = RelayFileRequestProperties(
serverUrl,
headers,
headersToEncrypt,
object : RelayStreamCallback {
override fun getRequestBodyStream(outputStream: PipedOutputStream) {
// Write file bytes to output stream
file.inputStream().use { input ->
input.copyTo(outputStream)
}
outputStream.close()
}
}
)
// Upload the file
relay.uploadFile(
reqProperties,
route,
object : RelayStreamResponseListener {
override fun relayStreamResponse(
statusCode: Int,
success: Boolean,
responseStr: String?,
errorMessage: String?,
responseHeaders: Map<String, List<String>>?
) {
if (success) {
Log.d("Upload", "Success: $responseStr")
} else {
Log.e("Upload", "Failed: $errorMessage")
}
}
},
object : RelayStreamCompletionCallback {
override fun onProgressUpdate(bytesCompleted: Int, totalBytes: Int) {
val percentage = (bytesCompleted.toDouble() / totalBytes) * 100
Log.d("Upload", "Progress: ${percentage.toInt()}%")
}
}
)
For multipart/form-data uploads, see the complete example on GitHub which includes the FileUploadHelper utility.
Streamed File Download
Important: Ensure the download directory exists:
val downloadFile = File(context.filesDir, "downloads/file.txt")
downloadFile.parentFile?.mkdirs()
Create request properties and download:
val serverUrl = "https://your-relay-server.com"
val route = "/api/files/download/file.txt"
val headers = mapOf(
"content-type" to "application/octet-stream"
)
val headersToEncrypt = arrayOf("content-type")
// Create request properties for download
val reqProperties = RelayFileRequestProperties(
serverUrl,
route,
downloadFile.absolutePath,
headers,
headersToEncrypt
)
// Download the file
relay.downloadFile(
reqProperties,
object : RelayStreamResponseListener {
override fun relayStreamResponse(
statusCode: Int,
success: Boolean,
responseStr: String?,
errorMessage: String?,
responseHeaders: Map<String, List<String>>?
) {
if (success) {
Log.d("Download", "Saved to: ${downloadFile.absolutePath}")
} else {
Log.e("Download", "Failed: $errorMessage")
}
}
}
)
Re-pair with Server
relay.rePairWithRelayServer(relayUrl, object : RelayResponseListener {
override fun onError(message: String, responseHeaders: Map<String, List<String>>?) {
Log.e("Relay", "Re-pairing failed: $message")
}
override fun onResponse(responseBytes: ByteArray, responseHeaders: Map<String, List<String>>) {
Log.d("Relay", "Re-paired successfully")
}
override fun onResponse(responseJson: JSONObject, responseHeaders: Map<String, List<String>>) {
Log.d("Relay", "Re-paired successfully")
}
})
Adjust Relay Settings
val serverUrl = "https://your-relay-server.com"
val streamChunkSize = 1048576 // 1MB chunks (0 = no change)
val pairPoolSize = 3 // Number of pairs (0 = no change)
val persistPairs = false // Persist pairs to storage
val message = relay.adjustRelaySettings(
serverUrl,
streamChunkSize,
pairPoolSize,
persistPairs
)
Log.d("Relay", "Settings adjusted: $message")
// If settings changed, relay will automatically re-pair with server
// Response delivered to RelayResponseListener.onCompletion() callback
Logging
val serverUrl = "https://your-relay-server.com"
// Enable file logging
Relay.enableFileLogging(serverUrl, true)
// Read log contents
val logs = Relay.readLogFile(serverUrl)
Log.d("Relay", "Logs: $logs")
// Clear log file
Relay.clearLogFile(serverUrl)
API Reference
See the GitHub repository for full API details. Key classes:
Relay: Main entry point for secure requests and file streamingRelayResponseListener: Interface for receiving responses and errorsProgressCallback: Interface for tracking upload/download progress
Support
Email: info@eclypses.com
Web: www.eclypses.com
Additional Resources
- GitHub Repository – Complete examples in Kotlin and Java
- Maven Central – Latest releases
All trademarks of Eclypses Inc. may not be used without Eclypses Inc.'s prior written consent.