Skip to content
Snippets Groups Projects
Commit ef739316 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Implemented proper offline functionality handling

parent 1e220874
No related branches found
No related tags found
No related merge requests found
Showing
with 130 additions and 108 deletions
......@@ -7,7 +7,8 @@ import android.os.Handler
import android.os.HandlerThread
import de.kuschku.libquassel.protocol.*
import de.kuschku.libquassel.session.Backend
import de.kuschku.libquassel.session.Session
import de.kuschku.libquassel.session.ISession
import de.kuschku.libquassel.session.SessionManager
import de.kuschku.libquassel.session.SocketAddress
import de.kuschku.quasseldroid_ng.BuildConfig
import de.kuschku.quasseldroid_ng.R
......@@ -18,20 +19,31 @@ import java.security.cert.X509Certificate
import javax.net.ssl.X509TrustManager
class QuasselService : LifecycleService() {
private lateinit var session: Session
private lateinit var sessionManager: SessionManager
private lateinit var clientData: ClientData
private val trustManager = object : X509TrustManager {
override fun checkClientTrusted(p0: Array<out X509Certificate>?, p1: String?) {
}
override fun checkServerTrusted(p0: Array<out X509Certificate>?, p1: String?) {
}
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
}
private val backendImplementation = object : Backend {
override fun session() = session
override fun sessionManager() = sessionManager
override fun connect(address: SocketAddress, user: String, pass: String) {
disconnect()
val handlerService = AndroidHandlerService()
session.connect(address, handlerService)
session.userData = user to pass
sessionManager.connect(clientData, trustManager, address, handlerService, user to pass)
}
override fun disconnect() {
session.cleanUp()
sessionManager.disconnect()
}
}
......@@ -56,7 +68,7 @@ class QuasselService : LifecycleService() {
}
}
override fun session() = backendImplementation.session()
override fun sessionManager() = backendImplementation.sessionManager()
}
private lateinit var database: QuasselDatabase
......@@ -64,7 +76,7 @@ class QuasselService : LifecycleService() {
override fun onCreate() {
super.onCreate()
database = QuasselDatabase.Creator.init(application)
session = Session(
sessionManager = SessionManager(ISession.NULL)
clientData = ClientData(
identifier = "${resources.getString(R.string.app_name)} ${BuildConfig.VERSION_NAME}",
buildDate = Instant.ofEpochSecond(BuildConfig.GIT_COMMIT_DATE),
......@@ -74,16 +86,6 @@ class QuasselService : LifecycleService() {
Protocol_Feature.TLS
),
supportedProtocols = byteArrayOf(0x02)
),
trustManager = object : X509TrustManager {
override fun checkClientTrusted(p0: Array<out X509Certificate>?, p1: String?) {
}
override fun checkServerTrusted(p0: Array<out X509Certificate>?, p1: String?) {
}
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
}
)
}
......
......@@ -19,7 +19,6 @@ import de.kuschku.libquassel.util.compatibility.LoggingHandler
import de.kuschku.quasseldroid_ng.R
import de.kuschku.quasseldroid_ng.util.helper.stickyMapNotNull
import de.kuschku.quasseldroid_ng.util.helper.stickySwitchMapNotNull
import de.kuschku.quasseldroid_ng.util.helper.switchMapNullable
import org.threeten.bp.ZoneOffset
import org.threeten.bp.ZonedDateTime
import org.threeten.bp.format.DateTimeFormatter
......@@ -49,12 +48,9 @@ class MainActivity : ServiceBoundActivity() {
@BindView(R.id.errorList)
lateinit var errorList: TextView
private val state = backend.stickyMapNotNull(null, Backend::session)
.switchMapNullable(null) { session ->
LiveDataReactiveStreams.fromPublisher(session.connectionPublisher)
}
.stickySwitchMapNotNull(ConnectionState.DISCONNECTED) { connection ->
LiveDataReactiveStreams.fromPublisher(connection.state)
private val state = backend.stickyMapNotNull(null, Backend::sessionManager)
.stickySwitchMapNotNull(ConnectionState.DISCONNECTED) { session ->
LiveDataReactiveStreams.fromPublisher(session.state)
}
private var snackbar: Snackbar? = null
......
......@@ -3,5 +3,5 @@ package de.kuschku.libquassel.session
interface Backend {
fun connect(address: SocketAddress, user: String, pass: String)
fun disconnect()
fun session(): Session
fun sessionManager(): SessionManager
}
......@@ -21,6 +21,7 @@ import io.reactivex.BackpressureStrategy
import io.reactivex.subjects.BehaviorSubject
import org.threeten.bp.ZoneOffset
import org.threeten.bp.format.DateTimeFormatter
import java.io.Closeable
import java.lang.Thread.UncaughtExceptionHandler
import java.net.Socket
import java.net.SocketException
......@@ -30,7 +31,7 @@ class CoreConnection(
private val session: Session,
private val address: SocketAddress,
private val handlerService: HandlerService
) : Thread(), ICoreConnection {
) : Thread(), Closeable {
companion object {
private const val TAG = "CoreConnection"
}
......@@ -43,7 +44,7 @@ class CoreConnection(
private val chainedBuffer = ChainedByteBuffer(direct = true)
private val internalState = BehaviorSubject.createDefault(ConnectionState.DISCONNECTED)
override val state = internalState.toFlowable(BackpressureStrategy.LATEST)
val state = internalState.toFlowable(BackpressureStrategy.LATEST)
private var channel: WrappedChannel? = null
......@@ -57,7 +58,7 @@ class CoreConnection(
channel = WrappedChannel.ofSocket(socket)
}
override fun setState(value: ConnectionState) {
fun setState(value: ConnectionState) {
log(INFO, "CoreConnection", value.name)
internalState.onNext(value)
}
......@@ -122,7 +123,7 @@ class CoreConnection(
}
}
override fun dispatch(message: HandshakeMessage) {
fun dispatch(message: HandshakeMessage) {
handlerService.parse {
try {
val data = HandshakeMessage.serialize(message)
......@@ -136,7 +137,7 @@ class CoreConnection(
}
}
override fun dispatch(message: SignalProxyMessage) {
fun dispatch(message: SignalProxyMessage) {
handlerService.parse {
try {
val data = SignalProxyMessage.serialize(message)
......
package de.kuschku.libquassel.session
import de.kuschku.libquassel.protocol.message.HandshakeMessage
import de.kuschku.libquassel.protocol.message.SignalProxyMessage
import io.reactivex.BackpressureStrategy
import io.reactivex.subjects.BehaviorSubject
import org.reactivestreams.Publisher
interface ICoreConnection {
val state: Publisher<ConnectionState>
fun close()
fun dispatch(message: HandshakeMessage)
fun dispatch(message: SignalProxyMessage)
fun start()
fun join()
fun setState(value: ConnectionState)
companion object {
val NULL = object : ICoreConnection {
override fun setState(value: ConnectionState) = Unit
override fun start() = Unit
override fun join() = Unit
override fun close() = Unit
override fun dispatch(message: HandshakeMessage) = Unit
override fun dispatch(message: SignalProxyMessage) = Unit
override val state: Publisher<ConnectionState>
get() = BehaviorSubject.createDefault(ConnectionState.DISCONNECTED)
.toFlowable(BackpressureStrategy.LATEST)
}
}
}
package de.kuschku.libquassel.session
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.subjects.BehaviorSubject
import java.io.Closeable
interface ISession : Closeable {
val state: Flowable<ConnectionState>
companion object {
val NULL = object : ISession {
override fun close() = Unit
override val state: Flowable<ConnectionState>
= BehaviorSubject.createDefault(ConnectionState.DISCONNECTED)
.toFlowable(BackpressureStrategy.LATEST)
}
}
}
......@@ -11,8 +11,9 @@ import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.DEBUG
import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.WARN
import de.kuschku.libquassel.util.compatibility.log
import org.threeten.bp.Instant
import java.io.Closeable
abstract class ProtocolHandler : SignalProxy, AuthHandler {
abstract class ProtocolHandler : SignalProxy, AuthHandler, Closeable {
private val objectStorage: ObjectStorage = ObjectStorage(this)
private val rpcHandler: RpcHandler = RpcHandler(this)
......@@ -170,7 +171,7 @@ abstract class ProtocolHandler : SignalProxy, AuthHandler {
toInit.remove(syncableObject)
}
open fun cleanUp() {
override fun close() {
objectStorage.clear()
toInit.clear()
}
......
......@@ -5,25 +5,26 @@ import de.kuschku.libquassel.protocol.message.HandshakeMessage
import de.kuschku.libquassel.protocol.message.SignalProxyMessage
import de.kuschku.libquassel.quassel.QuasselFeature
import de.kuschku.libquassel.quassel.syncables.*
import de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invokers
import de.kuschku.libquassel.util.compatibility.HandlerService
import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.DEBUG
import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.INFO
import de.kuschku.libquassel.util.compatibility.log
import de.kuschku.libquassel.util.hasFlag
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.subjects.BehaviorSubject
import org.threeten.bp.Instant
import javax.net.ssl.X509TrustManager
class Session(
val clientData: ClientData,
val trustManager: X509TrustManager
) : ProtocolHandler() {
val trustManager: X509TrustManager,
address: SocketAddress,
handlerService: HandlerService,
private val userData: Pair<String, String>
) : ProtocolHandler(), ISession {
var coreFeatures: Quassel_Features = Quassel_Feature.NONE
var userData: Pair<String, String>? = null
private val coreConnection = CoreConnection(this, address, handlerService)
override val state: Flowable<ConnectionState> = coreConnection.state
private var aliasManager: AliasManager? = null
private var backlogManager: BacklogManager? = null
......@@ -38,34 +39,21 @@ class Session(
private var networks = mutableMapOf<NetworkId, Network>()
private var networkConfig: NetworkConfig? = null
private val connection = BehaviorSubject.createDefault(ICoreConnection.NULL)
val connectionPublisher: Flowable<ICoreConnection> = connection.toFlowable(
BackpressureStrategy.LATEST)
init {
log(INFO, "Session", "Session created")
// This should preload them
Invokers
}
fun connect(address: SocketAddress, handlerService: HandlerService) {
val coreConnection = CoreConnection(this, address, handlerService)
connection.onNext(coreConnection)
coreConnection.start()
}
override fun handle(f: HandshakeMessage.ClientInitAck): Boolean {
coreFeatures = f.coreFeatures ?: Quassel_Feature.NONE
dispatch(HandshakeMessage.ClientLogin(
user = userData?.first,
password = userData?.second
user = userData.first,
password = userData.second
))
return true
}
override fun handle(f: HandshakeMessage.SessionInit): Boolean {
connection.value.setState(ConnectionState.INIT)
coreConnection.setState(ConnectionState.INIT)
f.networkIds?.forEach {
val network = Network(it.value(-1), this)
......@@ -117,7 +105,7 @@ class Session(
}
override fun onInitDone() {
connection.value.setState(ConnectionState.CONNECTED)
coreConnection.setState(ConnectionState.CONNECTED)
log(INFO, "Session", "Initialization finished")
}
......@@ -130,20 +118,19 @@ class Session(
override fun dispatch(message: SignalProxyMessage) {
log(DEBUG, "Session", "> $message")
connection.value.dispatch(message)
coreConnection.dispatch(message)
}
override fun dispatch(message: HandshakeMessage) {
log(DEBUG, "Session", "> $message")
connection.value.dispatch(message)
coreConnection.dispatch(message)
}
override fun network(id: NetworkId): Network? = networks[id]
override fun identity(id: IdentityId): Identity? = identities[id]
override fun cleanUp() {
connection.value.close()
connection.onNext(ICoreConnection.NULL)
override fun close() {
coreConnection.close()
aliasManager = null
backlogManager = null
......@@ -159,9 +146,10 @@ class Session(
identities.clear()
networks.clear()
super.cleanUp()
super.close()
}
fun connection(): ICoreConnection?
= connection.value
fun join() {
coreConnection.join()
}
}
package de.kuschku.libquassel.session
import de.kuschku.libquassel.protocol.ClientData
import de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invokers
import de.kuschku.libquassel.util.compatibility.HandlerService
import de.kuschku.libquassel.util.compatibility.LoggingHandler
import de.kuschku.libquassel.util.compatibility.log
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.subjects.BehaviorSubject
import javax.net.ssl.X509TrustManager
class SessionManager(
private val offlineSession: ISession
) {
init {
log(LoggingHandler.LogLevel.INFO, "Session", "Session created")
// This should preload them
Invokers
}
fun connect(
clientData: ClientData,
trustManager: X509TrustManager,
address: SocketAddress,
handlerService: HandlerService,
userData: Pair<String, String>
) {
inProgressSession.value.close()
inProgressSession.onNext(Session(clientData, trustManager, address, handlerService, userData))
}
fun disconnect() {
inProgressSession.value.close()
inProgressSession.onNext(offlineSession)
}
private var inProgressSession = BehaviorSubject.createDefault(offlineSession)
private val inProgressSessionPublisher: Flowable<ISession>
= inProgressSession.toFlowable(BackpressureStrategy.LATEST)
val state = inProgressSessionPublisher.switchMap { it.state }
val session = state.map { connectionState ->
if (connectionState == ConnectionState.CONNECTED)
inProgressSession.value
else
offlineSession
}
}
......@@ -46,10 +46,7 @@ class ConnectionUnitTest {
}
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
})
session.userData = user to pass
session.connect(SocketAddress(host, port), JavaHandlerService())
session.connection()?.join()
}, SocketAddress(host, port), JavaHandlerService(), user to pass)
session.join()
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment