davidliu
Committed by GitHub

Add version to rpc requests (#605)

* Add version to rpc requests

* spotless
  1 +---
  2 +"client-sdk-android": patch
  3 +---
  4 +
  5 +Add version number to rpc requests
@@ -35,6 +35,7 @@ import io.livekit.android.room.RTCEngine @@ -35,6 +35,7 @@ import io.livekit.android.room.RTCEngine
35 import io.livekit.android.room.Room 35 import io.livekit.android.room.Room
36 import io.livekit.android.room.TrackBitrateInfo 36 import io.livekit.android.room.TrackBitrateInfo
37 import io.livekit.android.room.isSVCCodec 37 import io.livekit.android.room.isSVCCodec
  38 +import io.livekit.android.room.rpc.RpcManager
38 import io.livekit.android.room.track.DataPublishReliability 39 import io.livekit.android.room.track.DataPublishReliability
39 import io.livekit.android.room.track.LocalAudioTrack 40 import io.livekit.android.room.track.LocalAudioTrack
40 import io.livekit.android.room.track.LocalAudioTrackOptions 41 import io.livekit.android.room.track.LocalAudioTrackOptions
@@ -986,6 +987,7 @@ internal constructor( @@ -986,6 +987,7 @@ internal constructor(
986 this.method = method 987 this.method = method
987 this.payload = payload 988 this.payload = payload
988 this.responseTimeoutMs = responseTimeout.inWholeMilliseconds.toUInt().toInt() 989 this.responseTimeoutMs = responseTimeout.inWholeMilliseconds.toUInt().toInt()
  990 + this.version = RpcManager.RPC_VERSION
989 build() 991 build()
990 } 992 }
991 build() 993 build()
@@ -1071,7 +1073,7 @@ internal constructor( @@ -1071,7 +1073,7 @@ internal constructor(
1071 ) { 1073 ) {
1072 publishRpcAck(callerIdentity, requestId) 1074 publishRpcAck(callerIdentity, requestId)
1073 1075
1074 - if (version != 1) { 1076 + if (version != RpcManager.RPC_VERSION) {
1075 publishRpcResponse( 1077 publishRpcResponse(
1076 destinationIdentity = callerIdentity, 1078 destinationIdentity = callerIdentity,
1077 requestId = requestId, 1079 requestId = requestId,
  1 +/*
  2 + * Copyright 2025 LiveKit, Inc.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package io.livekit.android.room.rpc
  18 +
  19 +class RpcManager {
  20 + companion object {
  21 + const val RPC_VERSION = 1
  22 + }
  23 +}
@@ -18,6 +18,7 @@ package io.livekit.android.room.participant @@ -18,6 +18,7 @@ package io.livekit.android.room.participant
18 18
19 import com.google.protobuf.ByteString 19 import com.google.protobuf.ByteString
20 import io.livekit.android.room.RTCEngine 20 import io.livekit.android.room.RTCEngine
  21 +import io.livekit.android.room.rpc.RpcManager
21 import io.livekit.android.rpc.RpcError 22 import io.livekit.android.rpc.RpcError
22 import io.livekit.android.test.MockE2ETest 23 import io.livekit.android.test.MockE2ETest
23 import io.livekit.android.test.mock.MockDataChannel 24 import io.livekit.android.test.mock.MockDataChannel
@@ -31,6 +32,7 @@ import kotlinx.coroutines.launch @@ -31,6 +32,7 @@ import kotlinx.coroutines.launch
31 import livekit.LivekitModels 32 import livekit.LivekitModels
32 import livekit.LivekitRtc 33 import livekit.LivekitRtc
33 import org.junit.Assert.assertEquals 34 import org.junit.Assert.assertEquals
  35 +import org.junit.Assert.assertFalse
34 import org.junit.Assert.assertTrue 36 import org.junit.Assert.assertTrue
35 import org.junit.Test 37 import org.junit.Test
36 import org.junit.runner.RunWith 38 import org.junit.runner.RunWith
@@ -147,6 +149,43 @@ class RpcMockE2ETest : MockE2ETest() { @@ -147,6 +149,43 @@ class RpcMockE2ETest : MockE2ETest() {
147 } 149 }
148 150
149 @Test 151 @Test
  152 + fun handleRpcRequestWithNoVersion() = runTest {
  153 + connect()
  154 +
  155 + var methodCalled = false
  156 + room.localParticipant.registerRpcMethod("hello") {
  157 + methodCalled = true
  158 + return@registerRpcMethod "hello back"
  159 + }
  160 +
  161 + val noVersionRequest = with(TestData.DATA_PACKET_RPC_REQUEST.toBuilder()) {
  162 + rpcRequest = with(rpcRequest.toBuilder()) {
  163 + clearVersion()
  164 + build()
  165 + }
  166 + build()
  167 + }
  168 + subDataChannel.simulateBufferReceived(noVersionRequest.toDataChannelBuffer())
  169 +
  170 + coroutineRule.dispatcher.scheduler.advanceUntilIdle()
  171 +
  172 + assertFalse(methodCalled)
  173 + // Check that ack and response were sent
  174 + val buffers = pubDataChannel.sentBuffers
  175 + assertEquals(2, buffers.size)
  176 +
  177 + val ackBuffer = LivekitModels.DataPacket.parseFrom(ByteString.copyFrom(buffers[0].data))
  178 + val responseBuffer = LivekitModels.DataPacket.parseFrom(ByteString.copyFrom(buffers[1].data))
  179 +
  180 + assertTrue(ackBuffer.hasRpcAck())
  181 + assertEquals(TestData.DATA_PACKET_RPC_REQUEST.rpcRequest.id, ackBuffer.rpcAck.requestId)
  182 +
  183 + assertTrue(responseBuffer.hasRpcResponse())
  184 + assertEquals(TestData.DATA_PACKET_RPC_REQUEST.rpcRequest.id, responseBuffer.rpcResponse.requestId)
  185 + assertEquals(RpcError.BuiltinRpcError.UNSUPPORTED_VERSION.create(), RpcError.fromProto(responseBuffer.rpcResponse.error))
  186 + }
  187 +
  188 + @Test
150 fun handleRpcRequestWithNoHandler() = runTest { 189 fun handleRpcRequestWithNoHandler() = runTest {
151 connect() 190 connect()
152 191
@@ -190,6 +229,7 @@ class RpcMockE2ETest : MockE2ETest() { @@ -190,6 +229,7 @@ class RpcMockE2ETest : MockE2ETest() {
190 assertTrue(requestBuffer.hasRpcRequest()) 229 assertTrue(requestBuffer.hasRpcRequest())
191 assertEquals("hello", requestBuffer.rpcRequest.method) 230 assertEquals("hello", requestBuffer.rpcRequest.method)
192 assertEquals("hello world", requestBuffer.rpcRequest.payload) 231 assertEquals("hello world", requestBuffer.rpcRequest.payload)
  232 + assertEquals(RpcManager.RPC_VERSION, requestBuffer.rpcRequest.version)
193 233
194 val requestId = requestBuffer.rpcRequest.id 234 val requestId = requestBuffer.rpcRequest.id
195 235