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

Commit 0cfbfaa8 authored by Mohammad Sabri's avatar Mohammad Sabri
Browse files

Floss: Pandora GATT: Implement GATT server

-Handle characteristic/descriptor read requests.
-Handle characteristic/descriptor write requests.
-Define required services, characteristics and descriptors for PTS.

Bug: 300954033
Test: mma packages/modules/Bluetooth && pts-bot GATT
Tag: #floss
Flag: EXEMPT floss only changes
Change-Id: I3f67b3a5b50090eb7704a74157fa8f3697820033
parent f28adda7
Loading
Loading
Loading
Loading
+181 −0
Original line number Original line Diff line number Diff line
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Class to hold the GATT service/characteristic/descriptor object."""

import uuid


class Service:
    """Class represents Bluetooth GATT service."""

    def __init__(self,
                 instance_id=None,
                 service_type=None,
                 uuid=None,
                 characteristics=None,
                 included_services=None,
                 value=None):
        self.instance_id = instance_id
        self.service_type = service_type
        self.uuid = uuid
        self.characteristics = characteristics
        self.included_services = included_services
        self.value = value

    def to_dict(self):
        """Converts service object to dictionary.

        Returns:
            GATT service as dict.
        """
        return {
            'instance_id': self.instance_id,
            'service_type': self.service_type,
            'uuid': self.uuid,
            'included_services': [service.to_dict() for service in self.included_services],
            'characteristics': [characteristic.to_dict() for characteristic in self.characteristics],
            'value': self.value
        }


class Characteristic:
    """Class represents Bluetooth GATT characteristic."""

    def __init__(self,
                 instance_id=None,
                 permissions=None,
                 write_type=None,
                 descriptors=None,
                 uuid=None,
                 key_size=None,
                 properties=None,
                 value=None):
        self.instance_id = instance_id
        self.permissions = permissions
        self.write_type = write_type
        self.descriptors = descriptors
        self.uuid = uuid
        self.key_size = key_size
        self.properties = properties
        self.value = value

    def to_dict(self):
        """Converts characteristic object to dictionary.

        Returns:
            GATT characteristic as dict.
        """
        return {
            'properties': self.properties,
            'permissions': self.permissions,
            'uuid': self.uuid,
            'instance_id': self.instance_id,
            'descriptors': [descriptor.to_dict() for descriptor in self.descriptors],
            'key_size': self.key_size,
            'write_type': self.write_type,
            'value': self.value
        }


class Descriptor:
    """Class represents Bluetooth GATT descriptor."""

    def __init__(self, permissions=None, uuid=None, instance_id=None, value=None):
        self.permissions = permissions
        self.uuid = uuid
        self.instance_id = instance_id
        self.value = value

    def to_dict(self):
        """Converts descriptor object to dictionary.

        Returns:
            GATT descriptor as dict.
        """
        return {
            'instance_id': self.instance_id,
            'permissions': self.permissions,
            'uuid': self.uuid,
            'value': self.value
        }


def create_gatt_service(service):
    """Creates GATT service from a dictionary.

    Args:
        service: Bluetooth GATT service as a dictionary.

    Returns:
        Bluetooth GATT service object.
    """
    return Service(
        instance_id=service['instance_id'],
        service_type=service['service_type'],
        uuid=str(uuid.UUID(bytes=bytes(service['uuid']))).upper(),
        included_services=[create_gatt_service(included_service) for included_service in service['included_services']],
        characteristics=[create_gatt_characteristic(characteristic) for characteristic in service['characteristics']],
        value=service.get('value'))


def create_gatt_characteristic(characteristic):
    """Creates GATT characteristic from a dictionary.

    Args:
        characteristic: Bluetooth GATT characteristic as a dictionary.

    Returns:
        Bluetooth GATT characteristic object.
    """
    return Characteristic(
        properties=characteristic['properties'],
        permissions=characteristic['permissions'],
        uuid=str(uuid.UUID(bytes=bytes(characteristic['uuid']))).upper(),
        instance_id=characteristic['instance_id'],
        descriptors=[create_gatt_characteristic_descriptor(descriptor) for descriptor in characteristic['descriptors']],
        key_size=characteristic['key_size'],
        write_type=characteristic['write_type'],
        value=characteristic.get('value'))


def create_gatt_characteristic_descriptor(descriptor):
    """Creates GATT descriptor from a dictionary.

    Args:
        descriptor: Bluetooth GATT descriptor as a dictionary.

    Returns:
        Bluetooth GATT descriptor object.
    """
    return Descriptor(instance_id=descriptor['instance_id'],
                      permissions=descriptor['permissions'],
                      uuid=str(uuid.UUID(bytes=bytes(descriptor['uuid']))).upper(),
                      value=descriptor.get('value'))


def convert_object_to_dict(obj):
    """Coverts object to dictionary.

    Args:
        obj: Service/Characteristic/Descriptor object.

    Returns:
        A dictionary represents the object.
    """
    if isinstance(obj, (Descriptor, Characteristic, Service)):
        return obj.to_dict()
    elif isinstance(obj, list):
        return [convert_object_to_dict(item) for item in obj]
    else:
        return obj
+25 −0
Original line number Original line Diff line number Diff line
@@ -47,6 +47,31 @@ class LePhy(enum.IntEnum):
    PHY_CODED = 3
    PHY_CODED = 3




class GattPermission(enum.IntEnum):
    """Bluetooth GATT permissions."""
    PERM_READ = (1 << 0)
    PERM_READ_ENCRYPTED = (1 << 1)
    PERM_READ_ENC_MITM = (1 << 2)
    PERM_WRITE = (1 << 4)
    PERM_WRITE_ENCRYPTED = (1 << 5)
    PERM_WRITE_ENC_MITM = (1 << 6)
    PERM_WRITE_SIGNED = (1 << 7)
    PERM_WRITE_SIGNED_MITM = (1 << 8)
    PERM_READ_IF_ENCRYPTED_OR_DISCOVERABLE = (1 << 9)


class GattCharacteristicProprieties(enum.IntEnum):
    """Bluetooth GATT characteristic proprieties."""
    CHAR_PROP_BIT_BROADCAST = (1 << 0)
    CHAR_PROP_BIT_READ = (1 << 1)
    CHAR_PROP_BIT_WRITE_NR = (1 << 2)
    CHAR_PROP_BIT_WRITE = (1 << 3)
    CHAR_PROP_BIT_NOTIFY = (1 << 4)
    CHAR_PROP_BIT_INDICATE = (1 << 5)
    CHAR_PROP_BIT_AUTH = (1 << 6)
    CHAR_PROP_BIT_EXT_PROP = (1 << 7)


class GattStatus(enum.IntEnum):
class GattStatus(enum.IntEnum):
    """Bluetooth GATT return status."""
    """Bluetooth GATT return status."""
    SUCCESS = 0x00
    SUCCESS = 0x00
+617 −22

File changed.

Preview size limit exceeded, changes collapsed.

+13 −0
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@ import functools
import logging
import logging
import threading
import threading
import time
import time
import uuid
from typing import List, Optional
from typing import List, Optional
import traceback
import traceback


@@ -448,6 +449,18 @@ def uuid32_to_uuid128(uuid32: str):
    return f'{uuid32}-0000-1000-8000-00805f9b34fb'
    return f'{uuid32}-0000-1000-8000-00805f9b34fb'




def get_uuid_as_list(str_uuid):
    """Converts string uuid to a list of bytes.

    Args:
        str_uuid: String UUID.

    Returns:
        UUID string as list of bytes.
    """
    return list(uuid.UUID(str_uuid).bytes)


def advertise_data_from(request_data: host_pb2.DataTypes):
def advertise_data_from(request_data: host_pb2.DataTypes):
    """Mapping DataTypes to a dict.
    """Mapping DataTypes to a dict.


+3 −3
Original line number Original line Diff line number Diff line
@@ -530,7 +530,7 @@ class GATTService(gatt_grpc_aio.GATTServicer):
        def convert_req_to_dictionary(request):
        def convert_req_to_dictionary(request):
            service_dict = {
            service_dict = {
                'service_type': self.SERVICE_TYPE_PRIMARY,
                'service_type': self.SERVICE_TYPE_PRIMARY,
                'uuid': list(UUID(request.uuid).bytes),
                'uuid': request.uuid,
                'instance_id': self.DEFAULT_INSTANCE_ID,
                'instance_id': self.DEFAULT_INSTANCE_ID,
                'included_services': [],
                'included_services': [],
                'characteristics': [],
                'characteristics': [],
@@ -539,7 +539,7 @@ class GATTService(gatt_grpc_aio.GATTServicer):
            # Iterate through the characteristics in the request.
            # Iterate through the characteristics in the request.
            for char in request.characteristics:
            for char in request.characteristics:
                char_dict = {
                char_dict = {
                    'uuid': list(UUID(char.uuid).bytes),
                    'uuid': char.uuid,
                    'instance_id': self.DEFAULT_INSTANCE_ID,
                    'instance_id': self.DEFAULT_INSTANCE_ID,
                    'properties': char.properties,
                    'properties': char.properties,
                    'permissions': char.permissions,
                    'permissions': char.permissions,
@@ -551,7 +551,7 @@ class GATTService(gatt_grpc_aio.GATTServicer):
                # Iterate through the descriptors in the characteristic.
                # Iterate through the descriptors in the characteristic.
                for desc in char.descriptors:
                for desc in char.descriptors:
                    desc_dict = {
                    desc_dict = {
                        'uuid': list(UUID(desc.uuid).bytes),
                        'uuid': desc.uuid,
                        'instance_id': self.DEFAULT_INSTANCE_ID,
                        'instance_id': self.DEFAULT_INSTANCE_ID,
                        'permissions': desc.permissions,
                        'permissions': desc.permissions,
                    }
                    }