davidliu
Committed by GitHub

Forward leave reason of disconnected events (#191)

... ... @@ -6,6 +6,7 @@ import io.livekit.android.room.track.LocalTrackPublication
import io.livekit.android.room.track.RemoteTrackPublication
import io.livekit.android.room.track.Track
import io.livekit.android.room.track.TrackPublication
import livekit.LivekitModels
sealed class RoomEvent(val room: Room) : Event() {
/**
... ... @@ -22,7 +23,7 @@ sealed class RoomEvent(val room: Room) : Event() {
/**
* Disconnected from room
*/
class Disconnected(room: Room, val error: Exception?) : RoomEvent(room)
class Disconnected(room: Room, val error: Exception?, val reason: DisconnectReason) : RoomEvent(room)
/**
* When a [RemoteParticipant] joins after the local participant. It will not emit events
... ... @@ -168,4 +169,30 @@ sealed class RoomEvent(val room: Room) : Event() {
val newPermissions: ParticipantPermission?,
val oldPermissions: ParticipantPermission?,
) : RoomEvent(room)
}
enum class DisconnectReason {
UNKNOWN_REASON,
CLIENT_INITIATED,
DUPLICATE_IDENTITY,
SERVER_SHUTDOWN,
PARTICIPANT_REMOVED,
ROOM_DELETED,
STATE_MISMATCH,
JOIN_FAILURE;
}
fun LivekitModels.DisconnectReason?.convert(): DisconnectReason {
return when (this) {
LivekitModels.DisconnectReason.CLIENT_INITIATED -> DisconnectReason.CLIENT_INITIATED
LivekitModels.DisconnectReason.DUPLICATE_IDENTITY -> DisconnectReason.DUPLICATE_IDENTITY
LivekitModels.DisconnectReason.SERVER_SHUTDOWN -> DisconnectReason.SERVER_SHUTDOWN
LivekitModels.DisconnectReason.PARTICIPANT_REMOVED -> DisconnectReason.PARTICIPANT_REMOVED
LivekitModels.DisconnectReason.ROOM_DELETED -> DisconnectReason.ROOM_DELETED
LivekitModels.DisconnectReason.STATE_MISMATCH -> DisconnectReason.STATE_MISMATCH
LivekitModels.DisconnectReason.JOIN_FAILURE -> DisconnectReason.JOIN_FAILURE
LivekitModels.DisconnectReason.UNKNOWN_REASON,
LivekitModels.DisconnectReason.UNRECOGNIZED,
null -> DisconnectReason.UNKNOWN_REASON
}
}
\ No newline at end of file
... ...
... ... @@ -5,6 +5,8 @@ import com.google.protobuf.ByteString
import io.livekit.android.ConnectOptions
import io.livekit.android.RoomOptions
import io.livekit.android.dagger.InjectionNames
import io.livekit.android.events.DisconnectReason
import io.livekit.android.events.convert
import io.livekit.android.room.participant.ParticipantTrackPermission
import io.livekit.android.room.track.TrackException
import io.livekit.android.room.util.MediaConstraintKeys
... ... @@ -456,7 +458,7 @@ internal constructor(
}
close("Failed reconnecting")
listener?.onEngineDisconnected("failed reconnecting.")
listener?.onEngineDisconnected(DisconnectReason.UNKNOWN_REASON)
}
reconnectingJob = job
... ... @@ -566,7 +568,7 @@ internal constructor(
fun onEngineConnected()
fun onEngineReconnected()
fun onEngineReconnecting()
fun onEngineDisconnected(reason: String)
fun onEngineDisconnected(reason: DisconnectReason)
fun onFailToConnect(error: Throwable)
fun onJoinResponse(response: LivekitRtc.JoinResponse)
fun onAddTrack(track: MediaStreamTrack, streams: Array<out MediaStream>)
... ... @@ -722,7 +724,8 @@ internal constructor(
fullReconnectOnNext = true
} else {
close()
listener?.onEngineDisconnected("server leave")
val disconnectReason = leave.reason.convert()
listener?.onEngineDisconnected(disconnectReason)
}
}
... ...
... ... @@ -16,10 +16,7 @@ import io.livekit.android.RoomOptions
import io.livekit.android.Version
import io.livekit.android.audio.AudioHandler
import io.livekit.android.dagger.InjectionNames
import io.livekit.android.events.BroadcastEventBus
import io.livekit.android.events.ParticipantEvent
import io.livekit.android.events.RoomEvent
import io.livekit.android.events.collect
import io.livekit.android.events.*
import io.livekit.android.memory.CloseableManager
import io.livekit.android.renderer.TextureViewRenderer
import io.livekit.android.room.participant.*
... ... @@ -250,7 +247,7 @@ constructor(
*/
fun disconnect() {
engine.client.sendLeave()
handleDisconnect()
handleDisconnect(DisconnectReason.CLIENT_INITIATED)
}
/**
... ... @@ -463,7 +460,7 @@ constructor(
.forEach { sid -> handleParticipantDisconnect(sid) }
}
private fun handleDisconnect() {
private fun handleDisconnect(reason: DisconnectReason) {
if (state == State.DISCONNECTED) {
return
}
... ... @@ -485,7 +482,7 @@ constructor(
// Ensure all observers see the disconnected before closing scope.
runBlocking {
eventBus.postEvent(RoomEvent.Disconnected(this@Room, null), coroutineScope).join()
eventBus.postEvent(RoomEvent.Disconnected(this@Room, null, reason), coroutineScope).join()
}
coroutineScope.cancel()
}
... ... @@ -737,9 +734,9 @@ constructor(
/**
* @suppress
*/
override fun onEngineDisconnected(reason: String) {
override fun onEngineDisconnected(reason: DisconnectReason) {
LKLog.v { "engine did disconnect: $reason" }
handleDisconnect()
handleDisconnect(reason)
}
/**
... ...
... ... @@ -5,9 +5,7 @@ import android.net.ConnectivityManager
import android.net.Network
import androidx.test.platform.app.InstrumentationRegistry
import io.livekit.android.MockE2ETest
import io.livekit.android.events.EventCollector
import io.livekit.android.events.FlowCollector
import io.livekit.android.events.RoomEvent
import io.livekit.android.events.*
import io.livekit.android.mock.MockAudioStreamTrack
import io.livekit.android.mock.MockMediaStream
import io.livekit.android.mock.TestData
... ... @@ -315,9 +313,33 @@ class RoomMockE2ETest : MockE2ETest() {
room.disconnect()
val events = eventCollector.stopCollecting()
Assert.assertEquals(2, events.size)
Assert.assertEquals(true, events[0] is RoomEvent.TrackUnpublished)
Assert.assertEquals(true, events[1] is RoomEvent.Disconnected)
assertEquals(2, events.size)
assertEquals(true, events[0] is RoomEvent.TrackUnpublished)
assertEquals(true, events[1] is RoomEvent.Disconnected)
}
@Test
fun serverDisconnectReason() = runTest {
connect()
val eventCollector = EventCollector(room.events, coroutineRule.scope)
wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.LEAVE.toOkioByteString())
val events = eventCollector.stopCollecting()
assertEquals(1, events.size)
assertEquals(true, events[0] is RoomEvent.Disconnected)
assertEquals(SignalClientTest.LEAVE.leave.reason.convert(), (events[0] as RoomEvent.Disconnected).reason)
}
@Test
fun clientDisconnectReason() = runTest {
connect()
val eventCollector = EventCollector(room.events, coroutineRule.scope)
room.disconnect()
val events = eventCollector.stopCollecting()
assertEquals(1, events.size)
assertEquals(true, events[0] is RoomEvent.Disconnected)
assertEquals(DisconnectReason.CLIENT_INITIATED, (events[0] as RoomEvent.Disconnected).reason)
}
@Test
... ...
... ... @@ -7,10 +7,7 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry
import io.livekit.android.audio.NoAudioHandler
import io.livekit.android.coroutines.TestCoroutineRule
import io.livekit.android.events.EventCollector
import io.livekit.android.events.EventListenable
import io.livekit.android.events.ParticipantEvent
import io.livekit.android.events.RoomEvent
import io.livekit.android.events.*
import io.livekit.android.memory.CloseableManager
import io.livekit.android.mock.*
import io.livekit.android.room.participant.LocalParticipant
... ... @@ -135,11 +132,12 @@ class RoomTest {
connect()
val eventCollector = EventCollector(room.events, coroutineRule.scope)
room.onEngineDisconnected("")
room.onEngineDisconnected(DisconnectReason.SERVER_SHUTDOWN)
val events = eventCollector.stopCollecting()
Assert.assertEquals(1, events.size)
Assert.assertEquals(true, events[0] is RoomEvent.Disconnected)
assertEquals(1, events.size)
assertEquals(true, events[0] is RoomEvent.Disconnected)
assertEquals(DisconnectReason.SERVER_SHUTDOWN, (events[0] as RoomEvent.Disconnected).reason)
}
@Test
... ... @@ -160,14 +158,14 @@ class RoomTest {
)
val eventCollector = EventCollector(room.events, coroutineRule.scope)
room.onEngineDisconnected("")
room.onEngineDisconnected(DisconnectReason.CLIENT_INITIATED)
val events = eventCollector.stopCollecting()
Assert.assertEquals(4, events.size)
Assert.assertEquals(true, events[0] is RoomEvent.TrackUnsubscribed)
Assert.assertEquals(true, events[1] is RoomEvent.TrackUnpublished)
Assert.assertEquals(true, events[2] is RoomEvent.ParticipantDisconnected)
Assert.assertEquals(true, events[3] is RoomEvent.Disconnected)
assertEquals(4, events.size)
assertEquals(true, events[0] is RoomEvent.TrackUnsubscribed)
assertEquals(true, events[1] is RoomEvent.TrackUnpublished)
assertEquals(true, events[2] is RoomEvent.ParticipantDisconnected)
assertEquals(true, events[3] is RoomEvent.Disconnected)
Assert.assertTrue(room.remoteParticipants.isEmpty())
}
}
\ No newline at end of file
... ...
... ... @@ -442,6 +442,7 @@ class SignalClientTest : BaseTest() {
val LEAVE = with(LivekitRtc.SignalResponse.newBuilder()) {
leave = with(LivekitRtc.LeaveRequest.newBuilder()) {
reason = LivekitModels.DisconnectReason.SERVER_SHUTDOWN
build()
}
build()
... ...