Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b456c17 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes I5a9793af,I93cf1a76 into main

* changes:
  Pandora: remove experiemental l2cap.proto
  PandoraServer: Implement new L2CAP interface
parents c9164e9e 2a8fc4d1
Loading
Loading
Loading
Loading
+31 −36
Original line number Original line Diff line number Diff line
@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# See the License for the specific language governing permissions and
# limitations under the License.
# limitations under the License.


import time
import sys
import sys


from mmi2grpc._helpers import assert_description
from mmi2grpc._helpers import assert_description
@@ -22,15 +21,16 @@ from mmi2grpc._rootcanal import Dongle


from pandora.host_grpc import Host
from pandora.host_grpc import Host
from pandora.host_pb2 import PUBLIC, RANDOM, Connection
from pandora.host_pb2 import PUBLIC, RANDOM, Connection
from pandora.l2cap_grpc import L2CAP
from pandora.l2cap_pb2 import CreditBasedChannelRequest
from pandora.security_pb2 import PairingEventAnswer
from pandora.security_pb2 import PairingEventAnswer
from pandora.security_grpc import Security
from pandora.security_grpc import Security
from pandora_experimental.l2cap_grpc import L2CAP


from typing import Optional
from typing import Optional, Dict




class L2CAPProxy(ProfileProxy):
class L2CAPProxy(ProfileProxy):
    test_status_map = {}  # record test status and pass them between MMI
    test_status_map: Dict[str, str] = {}  # record test status and pass them between MMI
    LE_DATA_PACKET_LARGE = "data: LE_DATA_PACKET_LARGE"
    LE_DATA_PACKET_LARGE = "data: LE_DATA_PACKET_LARGE"
    LE_DATA_PACKET1 = "data: LE_PACKET1"
    LE_DATA_PACKET1 = "data: LE_PACKET1"
    connection: Optional[Connection] = None
    connection: Optional[Connection] = None
@@ -44,6 +44,7 @@ class L2CAPProxy(ProfileProxy):


        self.connection = None
        self.connection = None
        self.pairing_events = None
        self.pairing_events = None
        self.channel = None


    def test_started(self, test: str, **kwargs):
    def test_started(self, test: str, **kwargs):
        self.rootcanal.select_pts_dongle(Dongle.CSR_RCK_PTS_DONGLE)
        self.rootcanal.select_pts_dongle(Dongle.CSR_RCK_PTS_DONGLE)
@@ -63,16 +64,12 @@ class L2CAPProxy(ProfileProxy):
        tests_target_to_fail = [
        tests_target_to_fail = [
            'L2CAP/LE/CFC/BV-01-C',
            'L2CAP/LE/CFC/BV-01-C',
            'L2CAP/LE/CFC/BV-04-C',
            'L2CAP/LE/CFC/BV-04-C',
            'L2CAP/LE/CFC/BV-10-C',
            'L2CAP/LE/CFC/BV-11-C',
            'L2CAP/LE/CFC/BV-12-C',
            'L2CAP/LE/CFC/BV-14-C',
            'L2CAP/LE/CFC/BV-14-C',
            'L2CAP/LE/CFC/BV-16-C',
            'L2CAP/LE/CFC/BV-16-C',
            'L2CAP/LE/CFC/BV-18-C',
            'L2CAP/LE/CFC/BV-18-C',
            'L2CAP/LE/CFC/BV-19-C',
            'L2CAP/LE/CFC/BV-19-C',
            "L2CAP/LE/CFC/BV-21-C",
            "L2CAP/LE/CFC/BV-21-C",
        ]
        ]
        tests_require_secure_connection = []


        # This MMI is called twice in 'L2CAP/LE/CFC/BV-04-C'
        # This MMI is called twice in 'L2CAP/LE/CFC/BV-04-C'
        # We are not sure whether the lower tester’s BluetoothServerSocket
        # We are not sure whether the lower tester’s BluetoothServerSocket
@@ -97,10 +94,13 @@ class L2CAPProxy(ProfileProxy):
        if test == 'L2CAP/LE/CFC/BV-12-C':
        if test == 'L2CAP/LE/CFC/BV-12-C':
            psm = 0xF3  # default TSPX_psm_authorization_required value
            psm = 0xF3  # default TSPX_psm_authorization_required value


        secure_connection = test in tests_require_secure_connection

        try:
        try:
            self.l2cap.CreateLECreditBasedChannel(connection=self.connection, psm=psm, secure=secure_connection)
            connect_response = self.l2cap.Connect(connection=self.connection,
                                                  le_credit_based=CreditBasedChannelRequest(spsm=psm))
            if connect_response.HasField('channel'):
                self.channel = connect_response.channel
            else:
                raise Exception(connect_response.error)
        except Exception as e:
        except Exception as e:
            if test in tests_target_to_fail:
            if test in tests_target_to_fail:
                self.test_status_map[test] = 'OK'
                self.test_status_map[test] = 'OK'
@@ -117,11 +117,13 @@ class L2CAPProxy(ProfileProxy):
        """
        """
        Place the IUT into LE connectable mode.
        Place the IUT into LE connectable mode.
        """
        """

        self.advertise = self.host.Advertise(
        self.advertise = self.host.Advertise(
            legacy=True,
            legacy=True,
            connectable=True,
            connectable=True,
            own_address_type=PUBLIC,
            own_address_type=PUBLIC,
        )
        )

        # not strictly necessary, but can save time on waiting connection
        # not strictly necessary, but can save time on waiting connection
        tests_to_open_bluetooth_server_socket = [
        tests_to_open_bluetooth_server_socket = [
            "L2CAP/COS/CFC/BV-01-C",
            "L2CAP/COS/CFC/BV-01-C",
@@ -129,31 +131,19 @@ class L2CAPProxy(ProfileProxy):
            "L2CAP/COS/CFC/BV-03-C",
            "L2CAP/COS/CFC/BV-03-C",
            "L2CAP/COS/CFC/BV-04-C",
            "L2CAP/COS/CFC/BV-04-C",
            "L2CAP/LE/CFC/BV-03-C",
            "L2CAP/LE/CFC/BV-03-C",
            "L2CAP/LE/CFC/BV-05-C",
            "L2CAP/LE/CFC/BV-06-C",
            "L2CAP/LE/CFC/BV-06-C",
            "L2CAP/LE/CFC/BV-09-C",
            "L2CAP/LE/CFC/BV-09-C",
            "L2CAP/LE/CFC/BV-13-C",
            "L2CAP/LE/CFC/BV-20-C",
            "L2CAP/LE/CFC/BV-20-C",
            "L2CAP/LE/CFC/BI-01-C",
            "L2CAP/LE/CFC/BI-01-C",
        ]
        ]
        tests_require_secure_connection = [
            "L2CAP/LE/CFC/BV-13-C",
        ]
        tests_connection_target_to_failed = [
            "L2CAP/LE/CFC/BV-05-C",
        ]


        if test in tests_to_open_bluetooth_server_socket:
        if test in tests_to_open_bluetooth_server_socket:
            secure_connection = test in tests_require_secure_connection
            wait_connection_response = self.l2cap.WaitConnection(le_credit_based=CreditBasedChannelRequest(spsm=0))
            self.l2cap.ListenL2CAPChannel(connection=self.connection, secure=secure_connection)
            if wait_connection_response.HasField('channel'):
            try:
                self.channel = wait_connection_response.channel
                self.l2cap.AcceptL2CAPChannel(connection=self.connection)
            except Exception as e:
                if test in tests_connection_target_to_failed:
                    self.test_status_map[test] = 'OK'
                    print(test, 'connection targets to fail', file=sys.stderr)
            else:
            else:
                    raise e
                raise Exception(wait_connection_response.error)

        return "OK"
        return "OK"


    @assert_description
    @assert_description
@@ -171,7 +161,8 @@ class L2CAPProxy(ProfileProxy):
        # all data frames arrived
        # all data frames arrived
        # it seemed like when the time gap between the 1st frame and 2nd frame
        # it seemed like when the time gap between the 1st frame and 2nd frame
        # larger than 100ms this problem will occur
        # larger than 100ms this problem will occur
        self.l2cap.SendData(connection=self.connection, data=bytes(self.LE_DATA_PACKET_LARGE, "utf-8"))
        assert self.channel
        self.l2cap.Send(channel=self.channel, data=bytes(self.LE_DATA_PACKET_LARGE, "utf-8"))
        return "OK"
        return "OK"


    @match_description
    @match_description
@@ -198,8 +189,9 @@ class L2CAPProxy(ProfileProxy):
        Upper Tester command IUT to send at least 4 frames of LE data packets to
        Upper Tester command IUT to send at least 4 frames of LE data packets to
        the PTS.
        the PTS.
        """
        """
        self.l2cap.SendData(
        assert self.channel
            connection=self.connection,
        self.l2cap.Send(
            channel=self.channel,
            data=b"this is a large data package with at least 4 frames: MMI_UPPER_TESTER_SEND_LE_DATA_PACKET_LARGE")
            data=b"this is a large data package with at least 4 frames: MMI_UPPER_TESTER_SEND_LE_DATA_PACKET_LARGE")
        return "OK"
        return "OK"


@@ -208,8 +200,9 @@ class L2CAPProxy(ProfileProxy):
        """
        """
        IUT continue to send LE data packet(s) to the PTS.
        IUT continue to send LE data packet(s) to the PTS.
        """
        """
        self.l2cap.SendData(
        assert self.channel
            connection=self.connection,
        self.l2cap.Send(
            channel=self.channel,
            data=b"this is a large data package with at least 4 frames: MMI_UPPER_TESTER_SEND_LE_DATA_PACKET_LARGE")
            data=b"this is a large data package with at least 4 frames: MMI_UPPER_TESTER_SEND_LE_DATA_PACKET_LARGE")
        return "OK"
        return "OK"


@@ -232,7 +225,8 @@ class L2CAPProxy(ProfileProxy):
        """
        """
        Please confirm the Upper Tester receive data
        Please confirm the Upper Tester receive data
        """
        """
        data = self.l2cap.ReceiveData(connection=self.connection)
        assert self.channel
        data = next(self.l2cap.Receive(channel=self.channel)).data
        assert data, "data received should not be empty"
        assert data, "data received should not be empty"
        return "OK"
        return "OK"


@@ -504,7 +498,8 @@ class L2CAPProxy(ProfileProxy):
         Description : The Implementation Under Test(IUT)
         Description : The Implementation Under Test(IUT)
        should send none segmantation LE frame of LE data to the PTS.
        should send none segmantation LE frame of LE data to the PTS.
        """
        """
        self.l2cap.SendData(connection=self.connection, data=bytes(self.LE_DATA_PACKET1, "utf-8"))
        assert self.channel
        self.l2cap.Send(channel=self.channel, data=bytes(self.LE_DATA_PACKET1, "utf-8"))
        return "OK"
        return "OK"


    @assert_description
    @assert_description
+1 −0
Original line number Original line Diff line number Diff line
@@ -1048,6 +1048,7 @@
    "L2CAP/CMC/BV-13-C",
    "L2CAP/CMC/BV-13-C",
    "L2CAP/CMC/BV-14-C",
    "L2CAP/CMC/BV-14-C",
    "L2CAP/CMC/BV-15-C",
    "L2CAP/CMC/BV-15-C",
    "L2CAP/COS/CED/BI-02-C",
    "L2CAP/COS/CED/BV-10-C",
    "L2CAP/COS/CED/BV-10-C",
    "L2CAP/COS/CED/BV-12-C",
    "L2CAP/COS/CED/BV-12-C",
    "L2CAP/COS/CED/BV-13-C",
    "L2CAP/COS/CED/BV-13-C",
+95 −116
Original line number Original line Diff line number Diff line
@@ -16,46 +16,35 @@


package com.android.pandora
package com.android.pandora


import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothServerSocket
import android.bluetooth.BluetoothSocket
import android.content.Context
import android.content.Context
import android.util.Log
import android.util.Log
import com.google.protobuf.Any
import com.google.protobuf.ByteString
import com.google.protobuf.ByteString
import io.grpc.stub.StreamObserver
import io.grpc.stub.StreamObserver
import java.io.Closeable
import java.io.Closeable
import java.io.IOException
import java.util.concurrent.atomic.AtomicLong
import java.io.InputStream
import java.io.OutputStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancel
import kotlinx.coroutines.withContext
import kotlinx.coroutines.flow.flow
import pandora.HostProto.Connection
import pandora.l2cap.L2CAPGrpc.L2CAPImplBase
import pandora.L2CAPGrpc.L2CAPImplBase
import pandora.l2cap.L2CAPProto.*
import pandora.L2capProto.AcceptL2CAPChannelRequest
import pandora.L2capProto.AcceptL2CAPChannelResponse
import pandora.L2capProto.CreateLECreditBasedChannelRequest
import pandora.L2capProto.CreateLECreditBasedChannelResponse
import pandora.L2capProto.ListenL2CAPChannelRequest
import pandora.L2capProto.ListenL2CAPChannelResponse
import pandora.L2capProto.ReceiveDataRequest
import pandora.L2capProto.ReceiveDataResponse
import pandora.L2capProto.SendDataRequest
import pandora.L2capProto.SendDataResponse


@kotlinx.coroutines.ExperimentalCoroutinesApi
@kotlinx.coroutines.ExperimentalCoroutinesApi
class L2cap(val context: Context) : L2CAPImplBase(), Closeable {
class L2cap(val context: Context) : L2CAPImplBase(), Closeable {
    private val TAG = "PandoraL2cap"
    private val TAG = "PandoraL2cap"
    private val scope: CoroutineScope
    private val scope: CoroutineScope
    private val BLUETOOTH_SERVER_SOCKET_TIMEOUT: Int = 10000
    private val BLUETOOTH_SERVER_SOCKET_TIMEOUT: Int = 10000
    private val channelIdCounter = AtomicLong(1)
    private val BUFFER_SIZE = 512
    private val BUFFER_SIZE = 512


    private val bluetoothManager =
    private val bluetoothManager =
        context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    private val bluetoothAdapter = bluetoothManager.adapter
    private val bluetoothAdapter = bluetoothManager.adapter
    private var connectionInStreamMap: HashMap<Connection, InputStream> = hashMapOf()
    private val channels: HashMap<Long, BluetoothSocket> = hashMapOf()
    private var connectionOutStreamMap: HashMap<Connection, OutputStream> = hashMapOf()
    private var connectionServerSocketMap: HashMap<Connection, BluetoothServerSocket> = hashMapOf()


    init {
    init {
        // Init the CoroutineScope
        // Init the CoroutineScope
@@ -67,127 +56,117 @@ class L2cap(val context: Context) : L2CAPImplBase(), Closeable {
        scope.cancel()
        scope.cancel()
    }
    }


    suspend fun receive(inStream: InputStream): ByteArray {
    override fun connect(
        return withContext(Dispatchers.IO) {
        request: ConnectRequest,
            val buf = ByteArray(BUFFER_SIZE)
        responseObserver: StreamObserver<ConnectResponse>,
            inStream.read(buf, 0, BUFFER_SIZE) // blocking
            Log.i(TAG, "receive: $buf")
            buf
        }
    }

    /** Open a BluetoothServerSocket to accept connections */
    override fun listenL2CAPChannel(
        request: ListenL2CAPChannelRequest,
        responseObserver: StreamObserver<ListenL2CAPChannelResponse>,
    ) {
    ) {
        grpcUnary(scope, responseObserver) {
        grpcUnary(scope, responseObserver) {
            Log.i(TAG, "listenL2CAPChannel: secure=${request.secure}")
            val device = request.connection.toBluetoothDevice(bluetoothAdapter)
            val connection = request.connection

            val bluetoothServerSocket =
            val psm =
                if (request.secure) {
                when {
                    bluetoothAdapter.listenUsingL2capChannel()
                    request.hasBasic() -> request.basic.psm
                } else {
                    request.hasLeCreditBased() -> request.leCreditBased.spsm
                    bluetoothAdapter.listenUsingInsecureL2capChannel()
                    request.hasEnhancedCreditBased() -> request.enhancedCreditBased.spsm
                    else -> throw RuntimeException("unreachable")
                }
                }
            connectionServerSocketMap[connection] = bluetoothServerSocket
            Log.i(TAG, "connect: $device psm: $psm")
            ListenL2CAPChannelResponse.newBuilder().build()

            val bluetoothSocket = device.createInsecureL2capChannel(psm)
            bluetoothSocket.connect()
            val channelId = getNewChannelId()
            channels.put(channelId, bluetoothSocket)

            Log.d(TAG, "connect: channelId=$channelId")
            ConnectResponse.newBuilder().setChannel(craftChannel(channelId)).build()
        }
        }
    }
    }


    override fun acceptL2CAPChannel(
    override fun waitConnection(
        request: AcceptL2CAPChannelRequest,
        request: WaitConnectionRequest,
        responseObserver: StreamObserver<AcceptL2CAPChannelResponse>,
        responseObserver: StreamObserver<WaitConnectionResponse>,
    ) {
    ) {
        grpcUnary(scope, responseObserver) {
        grpcUnary(scope, responseObserver) {
            Log.i(TAG, "acceptL2CAPChannel")
            val device: BluetoothDevice? =

            val connection = request.connection
            val bluetoothServerSocket = connectionServerSocketMap[connection]
                try {
                try {
                val bluetoothSocket =
                    request.connection.toBluetoothDevice(bluetoothAdapter)
                    bluetoothServerSocket!!.accept(BLUETOOTH_SERVER_SOCKET_TIMEOUT)
                } catch (e: Exception) {
                connectionInStreamMap[connection] = bluetoothSocket.getInputStream()!!
                    Log.w(TAG, e)
                connectionOutStreamMap[connection] = bluetoothSocket.getOutputStream()!!
                    null
            } catch (e: IOException) {
                Log.e(TAG, "bluetoothServerSocket not accepted", e)
                throw e
                }
                }


            AcceptL2CAPChannelResponse.newBuilder().build()
            Log.i(TAG, "waitConnection: $device")
        }

            val psm =
                when {
                    request.hasBasic() -> request.basic.psm
                    request.hasLeCreditBased() -> request.leCreditBased.spsm
                    request.hasEnhancedCreditBased() -> request.enhancedCreditBased.spsm
                    else -> throw RuntimeException("unreachable")
                }
                }


    /** Set device to send LE based connection request */
            var bluetoothSocket: BluetoothSocket?
    override fun createLECreditBasedChannel(
        request: CreateLECreditBasedChannelRequest,
        responseObserver: StreamObserver<CreateLECreditBasedChannelResponse>,
    ) {
        // Creates a gRPC coroutine in a given coroutine scope which executes a given suspended
        // function
        // returning a gRPC response and sends it on a given gRPC stream observer.
        grpcUnary(scope, responseObserver) {
            Log.i(TAG, "createLECreditBasedChannel: secure=${request.secure}, psm=${request.psm}")
            val connection = request.connection
            val device = request.connection.toBluetoothDevice(bluetoothAdapter)
            val psm = request.psm


            try {
            while (true) {
                val bluetoothSocket =
                val bluetoothServerSocket =
                    if (request.secure) {
                    if (psm == 0) {
                        device.createL2capChannel(psm)
                        bluetoothAdapter.listenUsingInsecureL2capChannel()
                    } else {
                    } else {
                        device.createInsecureL2capChannel(psm)
                        bluetoothAdapter.listenUsingInsecureL2capOn(psm)
                    }
                    }
                bluetoothSocket.connect()
                bluetoothSocket = bluetoothServerSocket.accept()
                connectionInStreamMap[connection] = bluetoothSocket.getInputStream()!!
                bluetoothServerSocket.close()
                connectionOutStreamMap[connection] = bluetoothSocket.getOutputStream()!!
                if (device != null && !bluetoothSocket.getRemoteDevice().equals(device)) continue
            } catch (e: IOException) {
                break
                Log.d(TAG, "bluetoothSocket not connected: $e")
                throw e
            }
            }


            // Response sent to client
            val channelId = getNewChannelId()
            CreateLECreditBasedChannelResponse.newBuilder().build()
            channels.put(channelId, bluetoothSocket!!)

            Log.d(TAG, "waitConnection: channelId=$channelId")
            WaitConnectionResponse.newBuilder().setChannel(craftChannel(channelId)).build()
        }
        }
    }
    }


    /** send data packet */
    override fun send(request: SendRequest, responseObserver: StreamObserver<SendResponse>) {
    override fun sendData(
        request: SendDataRequest,
        responseObserver: StreamObserver<SendDataResponse>,
    ) {
        grpcUnary(scope, responseObserver) {
        grpcUnary(scope, responseObserver) {
            Log.i(TAG, "sendDataPacket: data=${request.data}")
            Log.i(TAG, "send")
            val buffer = request.data!!.toByteArray()
            val bluetoothSocket = request.channel.toBluetoothSocket(channels)
            val connection = request.connection
            val outputStream = bluetoothSocket.outputStream
            val outputStream = connectionOutStreamMap[connection]!!


            withContext(Dispatchers.IO) {
            outputStream.write(request.data.toByteArray())
                try {
                    outputStream.write(buffer)
            outputStream.flush()
            outputStream.flush()
                } catch (e: IOException) {

                    Log.e(TAG, "Exception during writing to sendDataPacket output stream", e)
            SendResponse.newBuilder().build()
        }
        }
    }
    }


            // Response sent to client
    override fun receive(
            SendDataResponse.newBuilder().build()
        request: ReceiveRequest,
        responseObserver: StreamObserver<ReceiveResponse>,
    ) {
        Log.i(TAG, "receive")
        val bluetoothSocket = request.channel.toBluetoothSocket(channels)
        val inputStream = bluetoothSocket.inputStream
        grpcServerStream(scope, responseObserver) {
            flow {
                val buffer = ByteArray(BUFFER_SIZE)
                inputStream.read(buffer, 0, BUFFER_SIZE)
                val data = ByteString.copyFrom(buffer)
                val response = ReceiveResponse.newBuilder().setData(data).build()
                emit(response)
            }
        }
        }
    }
    }


    override fun receiveData(
    fun getNewChannelId(): Long = channelIdCounter.getAndIncrement()
        request: ReceiveDataRequest,
        responseObserver: StreamObserver<ReceiveDataResponse>,
    ) {
        grpcUnary(scope, responseObserver) {
            Log.i(TAG, "receiveData")
            val connection = request.connection
            val inputStream = connectionInStreamMap[connection]!!
            val buf = receive(inputStream)


            ReceiveDataResponse.newBuilder().setData(ByteString.copyFrom(buf)).build()
    fun craftChannel(id: Long): Channel {
        }
        val cookie = Any.newBuilder().setValue(ByteString.copyFromUtf8(id.toString())).build()
        val channel = Channel.newBuilder().setCookie(cookie).build()
        return channel
    }
    }

    fun Channel.toBluetoothSocket(channels: HashMap<Long, BluetoothSocket>): BluetoothSocket =
        channels.get(this.cookie.value.toStringUtf8().toLong())!!
}
}
+0 −62
Original line number Original line Diff line number Diff line
syntax = "proto3";

package pandora;

option java_outer_classname = "L2capProto";

import "google/protobuf/empty.proto";
import "pandora/host.proto";

service L2CAP {
  // Create a L2CAP connection to a peer.
  rpc CreateLECreditBasedChannel(CreateLECreditBasedChannelRequest) returns (CreateLECreditBasedChannelResponse);
  // Send some data
  rpc SendData(SendDataRequest) returns (SendDataResponse);
  // Receive data
  rpc ReceiveData(ReceiveDataRequest) returns (ReceiveDataResponse);
  // Listen L2CAP channel for connection
  rpc ListenL2CAPChannel(ListenL2CAPChannelRequest) returns (ListenL2CAPChannelResponse);
  // Accept L2CAP connection
  rpc AcceptL2CAPChannel(AcceptL2CAPChannelRequest) returns (AcceptL2CAPChannelResponse);
}

// Request for the `OpenSource` method.
message CreateLECreditBasedChannelRequest {
  // The connection that will open the stream.
  Connection connection = 1;
  int32 psm = 2;
  bool secure = 3;
}

// Request for the `OpenSource` method.
message CreateLECreditBasedChannelResponse {}

message SendDataRequest {
  // The connection that will open the stream.
  Connection connection = 1;
  bytes data = 2;
}

message SendDataResponse {}

message ReceiveDataRequest {
  // The connection that will open the stream.
  Connection connection = 1;
}

message ReceiveDataResponse {
  bytes data = 1;
}

message ListenL2CAPChannelRequest{
  Connection connection = 1;
  bool secure = 2;
}

message ListenL2CAPChannelResponse {}

message AcceptL2CAPChannelRequest{
  Connection connection = 1;
}

message AcceptL2CAPChannelResponse {}
 No newline at end of file
+0 −5
Original line number Original line Diff line number Diff line
@@ -54,10 +54,6 @@ genrule {
        "pandora_experimental/hid_grpc_aio.py",
        "pandora_experimental/hid_grpc_aio.py",
        "pandora_experimental/hid_pb2.py",
        "pandora_experimental/hid_pb2.py",
        "pandora_experimental/hid_pb2.pyi",
        "pandora_experimental/hid_pb2.pyi",
        "pandora_experimental/l2cap_grpc.py",
        "pandora_experimental/l2cap_grpc_aio.py",
        "pandora_experimental/l2cap_pb2.py",
        "pandora_experimental/l2cap_pb2.pyi",
        "pandora_experimental/le_audio_grpc.py",
        "pandora_experimental/le_audio_grpc.py",
        "pandora_experimental/le_audio_grpc_aio.py",
        "pandora_experimental/le_audio_grpc_aio.py",
        "pandora_experimental/le_audio_pb2.py",
        "pandora_experimental/le_audio_pb2.py",
@@ -118,7 +114,6 @@ filegroup {
        ":pandora_experimental-python-gen-src{pandora_experimental/hap_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/hap_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/hfp_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/hfp_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/hid_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/hid_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/l2cap_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/le_audio_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/le_audio_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/map_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/map_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/mediaplayer_pb2.pyi}",
        ":pandora_experimental-python-gen-src{pandora_experimental/mediaplayer_pb2.pyi}",