-
Notifications
You must be signed in to change notification settings - Fork 10
feat: android interop #1646
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: android interop #1646
Changes from all commits
b0559ee
297805f
d2fb2b5
07b6ee2
5a8f730
04ad6e1
80e2772
7a539e1
ce87c04
e220b38
41f5d32
6fbd19b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,4 +7,4 @@ pluginManagement { | |
| } | ||
| } | ||
|
|
||
| include("jvm", "android") | ||
| include(":jvm", ":android") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| /build | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import org.jetbrains.kotlin.gradle.dsl.JvmTarget | ||
|
|
||
| plugins { | ||
| alias(libs.plugins.android.application) | ||
| alias(libs.plugins.kotlin.android) | ||
| alias(libs.plugins.kotlin.serialisation) | ||
| } | ||
|
|
||
| android { | ||
| namespace = "com.wire.androidinterop" | ||
| compileSdk = 36 | ||
|
|
||
| defaultConfig { | ||
| applicationId = "com.wire.androidinterop" | ||
| minSdk = 30 | ||
| targetSdk = 36 | ||
| versionCode = 1 | ||
| versionName = "1.0" | ||
| } | ||
|
|
||
| buildTypes { | ||
| release { | ||
| isMinifyEnabled = false | ||
| signingConfig = signingConfigs.getByName("debug") | ||
| } | ||
| } | ||
| compileOptions { | ||
| sourceCompatibility = JavaVersion.VERSION_11 | ||
| targetCompatibility = JavaVersion.VERSION_11 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These should be set to |
||
| } | ||
| kotlin { | ||
| compilerOptions { | ||
| jvmTarget.set(JvmTarget.JVM_11) | ||
| } | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was part of the default project. Are you saying it's unnecessary specify which jvm we are targeting?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, we can just omit the whole
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Getting errors about conflicting JVM versions if I remove it.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Strange, it works for me. Did you make sure to sync the diff --git a/interop/src/clients/android-interop/build.gradle.kts b/interop/src/clients/android-interop/build.gradle.kts
index 45518ebfc..0aca03fdd 100644
--- a/interop/src/clients/android-interop/build.gradle.kts
+++ b/interop/src/clients/android-interop/build.gradle.kts
@@ -25,13 +25,8 @@ android {
}
}
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
- }
- kotlin {
- compilerOptions {
- jvmTarget.set(JvmTarget.JVM_11)
- }
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
}
} |
||
| } | ||
|
|
||
| dependencies { | ||
| implementation("core-crypto-kotlin:android") | ||
| implementation(libs.ktxSerialization) | ||
| implementation(libs.androidx.activity) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
|
|
||
| <application | ||
| android:allowBackup="false" | ||
| android:supportsRtl="true"> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: we don't need to set these two attributes. |
||
| <activity | ||
| android:name=".MainActivity" | ||
| android:launchMode="singleTop" | ||
| android:exported="true" | ||
| android:label="AndroidInterop"> | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.MAIN"/> | ||
| <category android:name="android.intent.category.LAUNCHER"/> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like we don't actually need
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need it because you can't launch the application without this intent.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is it possible then that the tests pass without it?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK maybe you right the interop doesn't need it, but for debugging purposes it's very convenient to be able to launch the interop client in the debugger.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, you're using a graphical debugger? I guess it makes sense to keep it then, if it makes debugging easier. 👍 |
||
| <action android:name="android.intent.action.RUN"/> | ||
| <category android:name="android.intent.category.DEFAULT"/> | ||
| </intent-filter> | ||
| </activity> | ||
| </application> | ||
|
|
||
| </manifest> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| package com.wire.androidinterop | ||
|
|
||
| import android.content.Intent | ||
| import kotlinx.serialization.Serializable | ||
| import kotlin.io.encoding.Base64 | ||
| import kotlin.io.encoding.ExperimentalEncodingApi | ||
|
|
||
| @Serializable | ||
| sealed class InteropAction { | ||
| sealed class MLS : InteropAction() { | ||
| class InitMLS(val clientId: ByteArray, val ciphersuite: Int) : MLS() | ||
|
|
||
| class GetKeyPackage(val ciphersuite: Int) : MLS() | ||
|
|
||
| class AddClient(val conversationId: ByteArray, val keyPackage: ByteArray) : MLS() | ||
|
|
||
| class RemoveClient(val conversationId: ByteArray, val clientId: ByteArray) : MLS() | ||
|
|
||
| class ProcessWelcome(val welcome: ByteArray) : MLS() | ||
|
|
||
| class EncryptMessage(val conversationId: ByteArray, val message: ByteArray) : MLS() | ||
|
|
||
| class DecryptMessage(val conversationId: ByteArray, val message: ByteArray) : MLS() | ||
| } | ||
|
|
||
| sealed class Proteus : InteropAction() { | ||
| class InitProteus : Proteus() | ||
|
|
||
| class GetPrekey(val id: UShort) : Proteus() | ||
|
|
||
| class SessionFromPrekey(val sessionId: String, val prekey: ByteArray) : Proteus() | ||
|
|
||
| class SessionFromMessage(val sessionId: String, val message: ByteArray) : Proteus() | ||
|
|
||
| class EncryptProteusMessage(val sessionId: String, val message: ByteArray) : Proteus() | ||
|
|
||
| class DecryptProteusMessage(val sessionId: String, val message: ByteArray) : Proteus() | ||
|
|
||
| class GetProteusFingerprint() : Proteus() | ||
| } | ||
|
|
||
| companion object { | ||
| @OptIn(ExperimentalEncodingApi::class) | ||
| fun fromIntent(intent: Intent): InteropAction { | ||
| return when (intent.getStringExtra("action")) { | ||
| "init-mls" -> { | ||
| val clientId = intent.getStringExtra("client_id") ?: throw IllegalArgumentException("client_id is missing") | ||
| val ciphersuite = intent.getIntExtra("ciphersuite", 0) | ||
|
|
||
| MLS.InitMLS(clientId = Base64.Default.decode(clientId), ciphersuite = ciphersuite) | ||
| } | ||
|
|
||
| "get-key-package" -> { | ||
| val ciphersuite = intent.getIntExtra("ciphersuite", 0) | ||
| MLS.GetKeyPackage(ciphersuite = ciphersuite) | ||
| } | ||
|
|
||
| "add-client" -> { | ||
| val conversationId = intent.getStringExtra("cid") ?: throw IllegalArgumentException("conversation_id is missing") | ||
| val keyPackage = intent.getStringExtra("kp") ?: throw IllegalArgumentException("key_package is missing") | ||
|
|
||
| MLS.AddClient(conversationId = Base64.Default.decode(conversationId), keyPackage = Base64.Default.decode(keyPackage)) | ||
| } | ||
|
|
||
| "remove-client" -> { | ||
| val conversationId = intent.getStringExtra("cid") ?: throw IllegalArgumentException("conversation_id is missing") | ||
| val clientId = intent.getStringExtra("client_id") ?: throw IllegalArgumentException("client_id is missing") | ||
|
|
||
| MLS.RemoveClient(conversationId = Base64.Default.decode(conversationId), clientId = Base64.Default.decode(clientId)) | ||
| } | ||
|
|
||
| "process-welcome" -> { | ||
| val welcome = intent.getStringExtra("welcome") ?: throw IllegalArgumentException("welcome is missing") | ||
|
|
||
| MLS.ProcessWelcome(Base64.Default.decode(welcome)) | ||
| } | ||
|
|
||
| "encrypt-message" -> { | ||
| val conversationId = intent.getStringExtra("cid") ?: throw IllegalArgumentException("conversation_id is missing") | ||
| val message = intent.getStringExtra("message") ?: throw IllegalArgumentException("message is missing") | ||
|
|
||
| MLS.EncryptMessage(Base64.Default.decode(conversationId), Base64.Default.decode(message)) | ||
| } | ||
|
|
||
| "decrypt-message" -> { | ||
| val conversationId = intent.getStringExtra("cid") ?: throw IllegalArgumentException("conversation_id is missing") | ||
| val message = intent.getStringExtra("message") ?: throw IllegalArgumentException("message is missing") | ||
|
|
||
| MLS.DecryptMessage(Base64.Default.decode(conversationId), Base64.Default.decode(message)) | ||
| } | ||
|
|
||
| "init-proteus" -> { | ||
| Proteus.InitProteus() | ||
| } | ||
|
|
||
| "get-prekey" -> { | ||
| val id = intent.getIntExtra("id", 0).toUShort() | ||
| Proteus.GetPrekey(id) | ||
| } | ||
|
|
||
| "session-from-prekey" -> { | ||
| val sessionId = intent.getStringExtra("session_id") ?: throw IllegalArgumentException("session_id is missing") | ||
| val prekey = intent.getStringExtra("prekey") ?: throw IllegalArgumentException("prekey is missing") | ||
|
|
||
| Proteus.SessionFromPrekey(sessionId = sessionId, prekey = Base64.Default.decode(prekey)) | ||
| } | ||
|
|
||
| "session-from-message" -> { | ||
| val sessionId = intent.getStringExtra("session_id") ?: throw IllegalArgumentException("session_id is missing") | ||
| val message = intent.getStringExtra("message") ?: throw IllegalArgumentException("message is missing") | ||
|
|
||
| Proteus.SessionFromMessage(sessionId = sessionId, message = Base64.Default.decode(message)) | ||
| } | ||
|
|
||
| "encrypt-proteus" -> { | ||
| val sessionId = intent.getStringExtra("session_id") ?: throw IllegalArgumentException("session_id is missing") | ||
| val message = intent.getStringExtra("message") ?: throw IllegalArgumentException("message is missing") | ||
|
|
||
| Proteus.EncryptProteusMessage(sessionId = sessionId, message = Base64.Default.decode(message)) | ||
| } | ||
|
|
||
| "decrypt-proteus" -> { | ||
| val sessionId = intent.getStringExtra("session_id") ?: throw IllegalArgumentException("session_id is missing") | ||
| val message = intent.getStringExtra("message") ?: throw IllegalArgumentException("message is missing") | ||
|
|
||
| Proteus.DecryptProteusMessage(sessionId = sessionId, message = Base64.Default.decode(message)) | ||
| } | ||
|
|
||
| "get-fingerprint" -> { | ||
| Proteus.GetProteusFingerprint() | ||
| } | ||
|
|
||
| else -> { | ||
| throw IllegalArgumentException("Unknown action: ${intent.getStringExtra("action")}") | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
istankovic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Uh oh!
There was an error while loading. Please reload this page.