Bluetooth: Add config to select Rfcomm services

We don't need Dev-kit's all Rfcomm services to be running every time.
Add config option for tests to select Echo, MAP or PBAP Rfcomm services.
Default to Rfcomm Echo service, if not specified.

BUG=b:424465503
TEST=Run BeTo test_concurrent_connections test

Change-Id: I5700d270b048276091daf3fae10e1df9bd53776a
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/chameleon/+/7115464
Commit-Queue: Manish Mandlik <mmandlik@chromium.org>
Reviewed-by: John Lai <johnlai@google.com>
Tested-by: Manish Mandlik <mmandlik@chromium.org>
diff --git a/chameleond/bluetooth_grpc/blueship/interface/manager.proto b/chameleond/bluetooth_grpc/blueship/interface/manager.proto
index badef77..c4390cc 100644
--- a/chameleond/bluetooth_grpc/blueship/interface/manager.proto
+++ b/chameleond/bluetooth_grpc/blueship/interface/manager.proto
@@ -109,6 +109,19 @@
 
 message RepeatedPlayerCommands { repeated PlayerCmd player_cmd_list = 1; }
 
+message DevKitConfig {
+  ConfigVariant rfcomm_services = 1; // RepeatedRfcommService
+}
+
+enum RfcommService {
+  UNKNOWN_SERVICE = 0;
+  ECHO_SERVICE = 1;
+  MAP_SERVICE = 2;
+  PBAP_SERVICE = 3;
+}
+
+message RepeatedRfcommService { repeated RfcommService service_list = 1; }
+
 message ConfigVariant {
   bool writable = 1;
 
@@ -127,6 +140,7 @@
     RepeatedString string_list = 201;
     RepeatedCodec codec_list = 202;
     RepeatedPlayerCommands player_cmd_list = 203;
+    RepeatedRfcommService service_list = 204;
   }
 }
 
@@ -139,6 +153,7 @@
   ControllerConfig controller = 3;
   SecurityConfig security = 4;
   AudioConfig audio = 5;
+  DevKitConfig devkit = 6;
 }
 
 message RestartServerRequest { Configuration next_config = 1; }
diff --git a/chameleond/bluetooth_grpc/server/device_config.py b/chameleond/bluetooth_grpc/server/device_config.py
index ee1033a..d315c9a 100644
--- a/chameleond/bluetooth_grpc/server/device_config.py
+++ b/chameleond/bluetooth_grpc/server/device_config.py
@@ -74,4 +74,12 @@
             ),
         ),
     ),
+    devkit=manager_pb2.DevKitConfig(
+        rfcomm_services=manager_pb2.ConfigVariant(
+            writable=True,
+            service_list=manager_pb2.RepeatedRfcommService(
+                service_list=[manager_pb2.ECHO_SERVICE]
+            ),
+        ),
+    ),
 )
diff --git a/chameleond/bluetooth_grpc/server/server.py b/chameleond/bluetooth_grpc/server/server.py
index f4f880e..7619044 100644
--- a/chameleond/bluetooth_grpc/server/server.py
+++ b/chameleond/bluetooth_grpc/server/server.py
@@ -214,7 +214,11 @@
             if not self.btd.ExportMediaPlayer():
                 logging.info("Failed to export media player")
 
-        self.btd.SpecifyDeviceType(self.device_type)
+        self.btd.SpecifyDeviceType(
+            self.device_type,
+            self.config.devkit.rfcomm_services.service_list.service_list,
+        )
+
         # Delay 1 second to wait for power on.
         time.sleep(1)
 
diff --git a/chameleond/utils/bluetooth_raspi.py b/chameleond/utils/bluetooth_raspi.py
index a1049f2..6e99b26 100644
--- a/chameleond/utils/bluetooth_raspi.py
+++ b/chameleond/utils/bluetooth_raspi.py
@@ -502,7 +502,7 @@
 
         return device_type
 
-    def SpecifyDeviceType(self, device_type):
+    def SpecifyDeviceType(self, device_type, rfcomm_services=None):
         """Instantiates one of our supported devices
 
         The raspi stack is designed to emulate a single device at a time. This
@@ -511,6 +511,8 @@
 
         Args:
           device_type: String device type, e.g. "MOUSE"
+          rfcomm_services: A list of Rfcomm services to start (applicable only
+                           when the device_type is DEV_KIT)
         """
 
         # Do nothing if we were already bound to this device
@@ -546,7 +548,9 @@
             # Set device type and initiate service based on it
             if "DEV_KIT" in device_type:
                 self._bluez_service = BluezRfcommService(
-                    self.device_type, self.GetLocalBluetoothAddress()
+                    self.device_type,
+                    self.GetLocalBluetoothAddress(),
+                    rfcomm_services,
                 )
             else:
                 self._bluez_service = BluezHIDService(
diff --git a/chameleond/utils/raspi_bluez_service.py b/chameleond/utils/raspi_bluez_service.py
index 67d2d2a..c114937 100644
--- a/chameleond/utils/raspi_bluez_service.py
+++ b/chameleond/utils/raspi_bluez_service.py
@@ -17,6 +17,7 @@
 import socket
 import time
 
+from blueship import manager_pb2
 from bluetooth import BluetoothSocket
 from bluetooth import L2CAP
 import dbus
@@ -113,6 +114,7 @@
         dbus.service.Object.__init__(self, bus, path)
         self.fd = None
         self.path = path
+        logging.info("EchoProfile: init")
 
     @dbus.service.method(
         "org.bluez.Profile1", in_signature="", out_signature=""
@@ -139,6 +141,7 @@
                 if not data:
                     time.sleep(0.1)
                     continue
+                logging.debug(f"EchoProfile: Writing back {data}")
                 self.fd.write(data)  # Echo back the received data
         except OSError as e:
             logging.warn(f"EchoProfile: Connection closed or error: {e}")
@@ -171,6 +174,7 @@
         self.fd = None
         self.path = path
         self.map_handler = MapServer()
+        logging.info("MapProfile: init")
 
     @dbus.service.method(
         "org.bluez.Profile1", in_signature="", out_signature=""
@@ -242,6 +246,7 @@
         self.fd = None
         self.path = path
         self.pbap_handler = PbapServer()
+        logging.info("PbapProfile: init")
 
     @dbus.service.method(
         "org.bluez.Profile1", in_signature="", out_signature=""
@@ -639,7 +644,7 @@
 class BluezRfcommService(dbus.service.Object):
     """Bluez Service implementation for RFCOMM Service."""
 
-    def __init__(self, device_type, adapter_address):
+    def __init__(self, device_type, adapter_address, rfcomm_services):
         self._profiles = []
         self._bus = dbus.SystemBus()
         self._bus_name = dbus.service.BusName(
@@ -652,10 +657,16 @@
             self._bus_name, BLUEZ_RFCOMM_SERVICE_PATH
         )
 
+        logging.debug(f"Rfcomm services to start {rfcomm_services}")
+
         # Init profile only if one is declared for this device type
         profile_uuid = PERIPHERAL_PROFILE_UUID.get(device_type, None)
 
-        if profile_uuid and ECHO_SERVICE_UUID in profile_uuid:
+        if (
+            manager_pb2.ECHO_SERVICE in rfcomm_services
+            and profile_uuid
+            and ECHO_SERVICE_UUID in profile_uuid
+        ):
             sdp_path = SERVICE_PROFILE_SDP_PATH.get(device_type, None)
             if sdp_path and ECHO_SERVICE_UUID in sdp_path:
                 self._InitBluezProfile(
@@ -664,7 +675,11 @@
                     ECHO_SERVICE_UUID,
                 )
 
-        if profile_uuid and MAP_SERVICE_UUID in profile_uuid:
+        if (
+            manager_pb2.MAP_SERVICE in rfcomm_services
+            and profile_uuid
+            and MAP_SERVICE_UUID in profile_uuid
+        ):
             sdp_path = SERVICE_PROFILE_SDP_PATH.get(device_type, None)
             if sdp_path and MAP_SERVICE_UUID in sdp_path:
                 self._InitBluezProfile(
@@ -673,7 +688,11 @@
                     MAP_SERVICE_UUID,
                 )
 
-        if profile_uuid and PBAP_SERVICE_UUID in profile_uuid:
+        if (
+            manager_pb2.PBAP_SERVICE in rfcomm_services
+            and profile_uuid
+            and PBAP_SERVICE_UUID in profile_uuid
+        ):
             sdp_path = SERVICE_PROFILE_SDP_PATH.get(device_type, None)
             if sdp_path and PBAP_SERVICE_UUID in sdp_path:
                 self._InitBluezProfile(