Skip to content
This repository has been archived by the owner on Jan 10, 2025. It is now read-only.

Commit

Permalink
Add MollySocketDevice
Browse files Browse the repository at this point in the history
  • Loading branch information
p1gp1g authored and valldrac committed Oct 24, 2024
1 parent 04a6ef7 commit 783a34b
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 0 deletions.
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ android {
getByName("androidTest") {
java.srcDir("$projectDir/src/testShared")
}

getByName("main") {
java.srcDir("$projectDir/src/unifiedpush/java")
}
}

compileOptions {
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,15 @@
android:enabled="@bool/enable_alarm_manager"
android:exported="false"/>

<receiver android:exported="true" android:enabled="true" android:name="im.molly.unifiedpush.receiver.UnifiedPushReceiver">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.MESSAGE"/>
<action android:name="org.unifiedpush.android.connector.UNREGISTERED"/>
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT"/>
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED"/>
</intent-filter>
</receiver>

<service
android:name=".service.webrtc.ActiveCallManager$ActiveCallForegroundService"
android:exported="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;

import im.molly.unifiedpush.helper.UnifiedPushHelper;
import java.io.InterruptedIOException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
Expand Down Expand Up @@ -205,6 +206,7 @@ private void onCreateUnlock() {
.addBlocking("network-settings", this::initializeNetworkSettings)
.addBlocking("first-launch", this::initializeFirstEverAppLaunch)
.addBlocking("gcm-check", this::initializeFcmCheck)
.addBlocking("unifiedpush", UnifiedPushHelper::initializeUnifiedPush)
.addBlocking("app-migrations", this::initializeApplicationMigrations)
.addBlocking("lifecycle-observer", () -> AppForegroundObserver.addListener(this))
.addBlocking("message-retriever", this::initializeMessageRetrieval)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package im.molly.unifiedpush.device

import org.signal.core.util.logging.Log
import org.signal.libsignal.protocol.util.KeyHelper
import org.thoughtcrime.securesms.AppCapabilities
import org.thoughtcrime.securesms.crypto.PreKeyUtil
import org.thoughtcrime.securesms.database.loaders.DeviceListLoader
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.devicelist.Device
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.push.AccountManagerFactory
import org.thoughtcrime.securesms.registration.secondary.DeviceNameCipher
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.api.account.PreKeyUpload
import org.whispersystems.signalservice.api.messages.multidevice.VerifyDeviceResponse
import org.whispersystems.signalservice.api.push.ServiceIdType
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import im.molly.unifiedpush.store.MollySocketStore
import java.io.IOException
import java.nio.charset.Charset

class MollySocketDevice {
private val TAG = MollySocketDevice::class.java.simpleName
private val DEVICE_NAME = "MollySocket"
private val context = AppDependencies.application
private val store = MollySocketStore()

var socketUri: String? = null

init {
if(!isMollySocketDevicePresent()) {
Log.d(TAG, "MollySocketDevice is not present")
store.removeUri()
}
socketUri = store.getUri()
?: run {
newDevice()
store.getUri()
}
}

private fun isMollySocketDevicePresent(): Boolean {
var devices : List<Device>? = emptyList()
Thread {
try {
devices = DeviceListLoader(context, AppDependencies.signalServiceAccountManager).loadInBackground()
} catch (e: IOException) {
Log.e(TAG, "Encountered an IOException", e)
}
}.apply {
start()
join()
}
devices?.forEach { device ->
if (device.id.toInt() == store.getDeviceId() && device.name == DEVICE_NAME) {
return true
}
}
return false
}

private fun newDevice() {
Log.d(TAG, "Creating a device for MollySocket")

Thread {
try {
val number = SignalStore.account.e164 ?: return@Thread
val password = Util.getSecret(18)

val verifyDeviceResponse = verifyNewDevice(number, password)
TextSecurePreferences.setMultiDevice(context, true)

generateAndRegisterPreKeys(number, verifyDeviceResponse.deviceId, password)
store.saveDeviceId(verifyDeviceResponse.deviceId)
store.saveUri(verifyDeviceResponse.uuid, verifyDeviceResponse.deviceId, password)
} catch (e: IOException) {
Log.e(TAG, "Encountered an IOException", e)
}
}.apply {
start()
join()
}
}

@Throws(IOException::class)
private fun verifyNewDevice(number: String, password: String): VerifyDeviceResponse {
val verificationCode = AppDependencies.signalServiceAccountManager
.newDeviceVerificationCode
val registrationId = KeyHelper.generateRegistrationId(false)
val encryptedDeviceName = DeviceNameCipher.encryptDeviceName(
DEVICE_NAME.toByteArray(Charset.forName("UTF-8")),
SignalStore.account.aciIdentityKey
)

return AccountManagerFactory.getInstance().createUnauthenticated(context, number, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
.verifySecondaryDevice(
verificationCode,
registrationId,
true,
"".toByteArray(),
true,
AppCapabilities.getCapabilities(true),
false,
encryptedDeviceName,
SignalStore.account.pniRegistrationId
)
}

@Throws(IOException::class)
private fun generateAndRegisterPreKeys(number: String, deviceId: Int, password: String): Boolean? {
val protocolStore = AppDependencies.protocolStore.aci()
val accountManager = AccountManagerFactory.getInstance().createAuthenticated(
context,
SignalStore.account.aci ?: return null,
SignalStore.account.pni ?: return null,
number,
deviceId,
password
)
val metadataStore = SignalStore.account.aciPreKeys
val signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore)
val oneTimePreKeys = PreKeyUtil.generateAndStoreOneTimeEcPreKeys(protocolStore, metadataStore)
accountManager.setPreKeys(
PreKeyUpload(
serviceIdType = ServiceIdType.ACI,
signedPreKey = signedPreKey,
oneTimeEcPreKeys = oneTimePreKeys,
lastResortKyberPreKey = null,
oneTimeKyberPreKeys = null
)
)
metadataStore.activeSignedPreKeyId = signedPreKey.id
metadataStore.isSignedPreKeyRegistered = true
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package im.molly.unifiedpush.helper

import org.signal.core.util.logging.Log
import org.unifiedpush.android.connector.UnifiedPush.registerAppWithDialog
import im.molly.unifiedpush.device.MollySocketDevice
import org.thoughtcrime.securesms.dependencies.AppDependencies

object UnifiedPushHelper{
private val TAG = Log.tag(UnifiedPushHelper::class.java)

@JvmStatic
fun initializeUnifiedPush() {
Log.d(TAG, "##unifiedpush")
val socketUri = MollySocketDevice().socketUri
Log.d(TAG, socketUri)
registerAppWithDialog(AppDependencies.application)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package im.molly.unifiedpush.receiver

import android.content.Context
import android.os.Build
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.gcm.FcmFetchManager.enqueue
import org.unifiedpush.android.connector.MessagingReceiver

class UnifiedPushReceiver: MessagingReceiver() {
private val TAG = Log.tag(UnifiedPushReceiver::class.java)

override fun onNewEndpoint(context: Context, endpoint: String, instance: String) {
Log.d(TAG, "New endpoint: $endpoint")
}

override fun onRegistrationFailed(context: Context, instance: String) {
// called when the registration is not possible, eg. no network
}

override fun onUnregistered(context: Context, instance: String) {
// called when this application is unregistered from receiving push messages
// TODO : start inapp webSocket
}

override fun onMessage(context: Context, message: ByteArray, instance: String) {
Log.d(TAG, "New message")
if (Build.VERSION.SDK_INT >= 31) {
enqueue(context, true)
} else {
enqueue(context, false)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package im.molly.unifiedpush.store

import android.content.Context
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import java.util.UUID

class MollySocketStore {
private val PREF_MASTER = "unifiedpush"
private val PREF_MASTER_SOCKET_URI = "unifiedpush.socketUri"
private val PREF_MASTER_DEVICE_ID = "unifiedpush.deviceId"

private val context = AppDependencies.application
private val prefs = context.getSharedPreferences(PREF_MASTER, Context.MODE_PRIVATE)

fun getUri(): String? {
return prefs.getString(PREF_MASTER_SOCKET_URI, null)
}

private fun saveUri(uri: String) {
prefs.edit()?.putString(PREF_MASTER_SOCKET_URI, uri)?.apply()
}

fun saveUri(uuid: UUID, deviceId: Int, password: String) {
val wsUriFormat = AppDependencies.signalServiceNetworkAccess.getConfiguration()
.signalServiceUrls[0].url
.replace("https://", "wss://")
.replace("http://", "ws://") + "/v1/websocket/?login=%s.%s&password=%s"

saveUri(
String.format(wsUriFormat, uuid, deviceId, password)
)
}

fun removeUri() {
prefs.edit().remove(PREF_MASTER_SOCKET_URI).apply()
}

fun getDeviceId(): Int {
return prefs.getInt(PREF_MASTER_DEVICE_ID, SignalServiceAddress.DEFAULT_DEVICE_ID)
}

fun saveDeviceId(deviceId: Int) {
prefs.edit().putInt(PREF_MASTER_DEVICE_ID, deviceId).apply()
}

fun removeDeviceId() {
prefs.edit().remove(PREF_MASTER_DEVICE_ID).apply()
}
}
3 changes: 3 additions & 0 deletions dependencies.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ dependencyResolutionManagement {
library("nanohttpd-websocket", "org.nanohttpd", "nanohttpd-websocket").versionRef("nanohttpd")
library("kotlinx-collections-immutable", "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5")

// UnifiedPush
library("android-connector", "com.github.UnifiedPush:android-connector:2.4.0")

// Can"t use the newest version because it hits some weird NoClassDefFoundException
library("jknack-handlebars", "com.github.jknack:handlebars:4.0.7")

Expand Down
11 changes: 11 additions & 0 deletions gradle/verification-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5180,13 +5180,24 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="b7bc6c98f2d2e1739d9e43a0959addce98e8b2ec48bd34926891854c9df24897" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.github.UnifiedPush" name="android-connector" version="2.1.1">
<artifact name="android-connector-2.1.1.aar">
<sha256 value="21983ecb673f826d622cb3aba168e3b84eaa522a8f056985aa00946d0692d6f7" origin="Generated by Gradle"/>
</artifact>
<artifact name="android-connector-2.1.1.module">
<sha256 value="2758d4994b8339668a67857211981d93b903596fd06fd73e8b35abb31534f2ec" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.github.bumptech.glide" name="annotations" version="4.15.1">
<artifact name="annotations-4.15.1.jar">
<sha256 value="c331e74f28d573319d6863bfbef3691cd3775580ff4a33b95ed3f227cc339b0d" origin="Generated by Gradle"/>
</artifact>
<artifact name="annotations-4.15.1.module">
<sha256 value="162bc02c4673421ea0128927881c3fea7aa7a10d5738ac37d2bda91f48e8ebad" origin="Generated by Gradle"/>
</artifact>
<artifact name="annotations-4.14.2.module">
<sha256 value="6ec1255219b1532ad3c3eb508227cbc5a1a946900ab7bf87d0961de83d105323" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.github.bumptech.glide" name="disklrucache" version="4.15.1">
<artifact name="disklrucache-4.15.1.jar">
Expand Down
6 changes: 6 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ dependencyResolutionManagement {
includeGroup("org.signal")
}
}
maven {
url = uri("https://jitpack.io")
content {
includeModule("com.github.UnifiedPush", "android-connector")
}
}
maven {
url = uri("https://raw.githubusercontent.com/mollyim/maven/master/argon2/releases/")
content {
Expand Down

0 comments on commit 783a34b

Please sign in to comment.