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

Commit 84ab1dd6 authored by Charlie Boutier's avatar Charlie Boutier Committed by Automerger Merge Worker
Browse files

Merge "[PTS-Bot] Added 9 GATT test cases." am: e9816584

parents 0ac37497 e9816584
Loading
Loading
Loading
Loading
+164 −62
Original line number Diff line number Diff line
@@ -14,14 +14,16 @@

import re
import sys
import time
from threading import Thread

from mmi2grpc._helpers import assert_description
from mmi2grpc._helpers import assert_description, match_description
from mmi2grpc._proxy import ProfileProxy

from pandora_experimental.gatt_grpc import GATT
from pandora.host_grpc import Host
from pandora.host_pb2 import PUBLIC, RANDOM
from pandora.security_grpc import SecurityStorage
from pandora_experimental.gatt_pb2 import (
    INVALID_HANDLE,
    READ_NOT_PERMITTED,
@@ -37,6 +39,8 @@ from pandora_experimental.gatt_pb2 import (
    PERMISSION_READ,
    PERMISSION_WRITE,
    PERMISSION_READ_ENCRYPTED,
    ENABLE_NOTIFICATION_VALUE,
    ENABLE_INDICATION_VALUE,
)
from pandora_experimental.gatt_pb2 import GattServiceParams
from pandora_experimental.gatt_pb2 import GattCharacteristicParams
@@ -67,12 +71,15 @@ class GATTProxy(ProfileProxy):
        super().__init__(channel)
        self.gatt = GATT(channel)
        self.host = Host(channel)
        self.security_storage = SecurityStorage(channel)
        self.connection = None
        self.services = None
        self.characteristics = None
        self.descriptors = None
        self.read_response = None
        self.write_response = None
        self.characteristic_notification_received = False
        self.handles = []
        self.written_over_length = False
        self.last_added_service = None

@@ -109,6 +116,8 @@ class GATTProxy(ProfileProxy):
        self.descriptors = None
        self.read_response = None
        self.write_response = None
        self.characteristic_notification_received = False
        self.handles = []
        self.written_over_length = False
        self.last_added_service = None
        return "OK"
@@ -456,10 +465,12 @@ class GATTProxy(ProfileProxy):

        assert self.connection is not None
        handle = stringHandleToInt(re.findall("'([a0-Z9]*)'O", description)[0])

        def read():
            nonlocal handle
            self.read_response = self.gatt.ReadCharacteristicFromHandle(\
                    connection=self.connection, handle=handle)

        worker = Thread(target=read)
        worker.start()
        worker.join(timeout=30)
@@ -747,11 +758,13 @@ class GATTProxy(ProfileProxy):
        matches = re.findall("'([a0-Z9]*)'O with <= '([a0-Z9]*)'", description)
        handle = stringHandleToInt(matches[0][0])
        data = bytes([1]) * int(matches[0][1])

        def write():
            nonlocal handle
            nonlocal data
            self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\
                handle=handle, value=data)

        worker = Thread(target=write)
        worker.start()
        worker.join(timeout=30)
@@ -966,8 +979,8 @@ class GATTProxy(ProfileProxy):
            connectable=True,
            own_address_type=PUBLIC,
        )
        self.gatt.RegisterService(
            service=GattServiceParams(
        time.sleep(1)
        self.gatt.RegisterService(service=GattServiceParams(
            uuid=BASE_READ_WRITE_SERVICE_UUID,
            characteristics=[
                GattCharacteristicParams(
@@ -1051,8 +1064,7 @@ class GATTProxy(ProfileProxy):
        that the Implementation Under Test (IUT) can respond Read Not Permitted.
        """

        self.last_added_service = self.gatt.RegisterService(
            service=GattServiceParams(
        self.last_added_service = self.gatt.RegisterService(service=GattServiceParams(
            uuid=CUSTOM_SERVICE_UUID,
            characteristics=[
                GattCharacteristicParams(
@@ -1095,8 +1107,7 @@ class GATTProxy(ProfileProxy):
        Authentication.
        """

        self.last_added_service = self.gatt.RegisterService(
            service=GattServiceParams(
        self.last_added_service = self.gatt.RegisterService(service=GattServiceParams(
            uuid=CUSTOM_SERVICE_UUID,
            characteristics=[
                GattCharacteristicParams(
@@ -1128,8 +1139,7 @@ class GATTProxy(ProfileProxy):
        Implementation Under Test (IUT) can issue a Read Not Permitted Response.
        """

        self.last_added_service = self.gatt.RegisterService(
            service=GattServiceParams(
        self.last_added_service = self.gatt.RegisterService(service=GattServiceParams(
            uuid=CUSTOM_SERVICE_UUID,
            characteristics=[
                GattCharacteristicParams(
@@ -1161,8 +1171,7 @@ class GATTProxy(ProfileProxy):
        Permitted.
        """

        self.last_added_service = self.gatt.RegisterService(
            service=GattServiceParams(
        self.last_added_service = self.gatt.RegisterService(service=GattServiceParams(
            uuid=CUSTOM_SERVICE_UUID,
            characteristics=[
                GattCharacteristicParams(
@@ -1174,6 +1183,99 @@ class GATTProxy(ProfileProxy):
        ))
        return "{:04x}".format(self.last_added_service.service.characteristics[0].handle)

    def MMI_IUT_SEND_INDICATION(self, description: str, **kwargs):
        """
        Please write to client characteristic configuration handle = 'XXXX'O to
        enable indication to the PTS. Discover all characteristics if needed.
        Description: Verify that the Implementation Under Test (IUT) can receive
        indication sent from PTS.
        """
        assert self.connection is not None
        handle = stringHandleToInt(re.findall("'([a0-Z9]*)'O", description)[0])
        self.handles.append(handle)
        self.gatt.SetCharacteristicNotificationFromHandle(connection=self.connection,\
                handle=handle, enable_value=ENABLE_INDICATION_VALUE)

        return "OK"

    @assert_description
    def MMI_IUT_RECEIVE_INDICATION(self, **kwargs):
        """
        Please confirm IUT received indication from PTS. Click YES if received,
        otherwise NO.

        Description: Verify that the Implementation Under Test
        (IUT) can receive indication send from PTS.
        """
        for handle in self.handles:
            self.characteristic_notification_received = self.gatt.WaitCharacteristicNotification(
                connection=self.connection, handle=handle).characteristic_notification_received
            assert self.characteristic_notification_received

        return "OK"

    def MMI_IUT_SEND_NOTIFICATION(self, description: str, **kwargs):
        """
        Please write to client characteristic configuration handle = 'XXXX'O to
        enable notification to the PTS. Discover all characteristics if needed.
        Description: Verify that the Implementation Under Test (IUT) can receive
        notification sent from PTS.
        """
        assert self.connection is not None
        handle = stringHandleToInt(re.findall("'([a0-Z9]*)'O", description)[0])
        self.handles.append(handle)
        self.gatt.SetCharacteristicNotificationFromHandle(connection=self.connection,\
                handle=handle, enable_value=ENABLE_NOTIFICATION_VALUE)

        return "OK"

    @assert_description
    def MMI_IUT_RECEIVE_NOTIFICATION(self, **kwargs):
        """
        Please confirm IUT received notification from PTS. Click YES if
        received, otherwise NO.

        Description: Verify that the Implementation
        Under Test (IUT) can receive notification send from PTS.
        """
        for handle in self.handles:
            self.characteristic_notification_received = self.gatt.WaitCharacteristicNotification(
                connection=self.connection, handle=handle).characteristic_notification_received
            assert self.characteristic_notification_received

        return "OK"

    @assert_description
    def MMI_IUT_DELETE_SECUIRTY_KEY(self, pts_addr: bytes, **kwargs):
        """
        Please delete security key before connecting to PTS if IUT was bonded
        previously.
        """
        self.security_storage.DeleteBond(public=pts_addr)

        return "OK"

    def MMI_IUT_WRITE_SUPPORT_FEATURE(self, description: str, **kwargs):
        """
        Please write to client support feature handle = 'XXXX'O to enable Robust
        Caching. Discover all characteristics if needed.
        """
        assert self.connection is not None
        handle = stringHandleToInt(re.findall("'([a0-Z9]*)'O", description)[0])
        data = bytes([1])
        self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\
                handle=handle, value=data)

        return "OK"

    @match_description
    def _mmi_2004(self, passkey: str, **kwargs):
        """
        Please confirm that 6 digit number is matched with (?P<passkey>[0-9]+).
        """

        return "OK"


common_uuid = "0000XXXX-0000-1000-8000-00805f9b34fb"

+12 −12
Original line number Diff line number Diff line
@@ -217,6 +217,8 @@
    "GATT/CL/GAD/BV-04-C",
    "GATT/CL/GAD/BV-05-C",
    "GATT/CL/GAD/BV-06-C",
    "GATT/CL/GAI/BV-01-C",
    "GATT/CL/GAN/BV-01-C",
    "GATT/CL/GAR/BI-01-C",
    "GATT/CL/GAR/BI-02-C",
    "GATT/CL/GAR/BI-06-C",
@@ -229,6 +231,11 @@
    "GATT/CL/GAR/BV-04-C",
    "GATT/CL/GAR/BV-06-C",
    "GATT/CL/GAR/BV-07-C",
    "GATT/CL/GAS/BV-01-C",
    "GATT/CL/GAS/BV-03-C",
    "GATT/CL/GAS/BV-05-C",
    "GATT/CL/GAT/BV-01-C",
    "GATT/CL/GAT/BV-02-C",
    "GATT/CL/GAW/BI-02-C",
    "GATT/CL/GAW/BI-03-C",
    "GATT/CL/GAW/BI-07-C",
@@ -252,6 +259,7 @@
    "GATT/SR/GAR/BI-01-C",
    "GATT/SR/GAR/BI-02-C",
    "GATT/SR/GAR/BI-04-C",
    "GATT/SR/GAR/BI-05-C",
    "GATT/SR/GAR/BI-06-C",
    "GATT/SR/GAR/BI-07-C",
    "GATT/SR/GAR/BI-08-C",
@@ -259,9 +267,11 @@
    "GATT/SR/GAR/BI-12-C",
    "GATT/SR/GAR/BI-14-C",
    "GATT/SR/GAR/BI-16-C",
    "GATT/SR/GAR/BI-17-C",
    "GATT/SR/GAR/BI-18-C",
    "GATT/SR/GAR/BI-19-C",
    "GATT/SR/GAR/BI-21-C",
    "GATT/SR/GAR/BI-22-C",
    "GATT/SR/GAR/BI-36-C",
    "GATT/SR/GAR/BI-38-C",
    "GATT/SR/GAR/BI-42-C",
@@ -270,15 +280,14 @@
    "GATT/SR/GAR/BV-04-C",
    "GATT/SR/GAR/BV-05-C",
    "GATT/SR/GAR/BV-09-C",
    "GATT/CL/GAS/BV-05-C",
    "GATT/CL/GAT/BV-01-C",
    "GATT/CL/GAT/BV-02-C",
    "GATT/SR/GAW/BI-02-C",
    "GATT/SR/GAW/BI-03-C",
    "GATT/SR/GAW/BI-05-C",
    "GATT/SR/GAW/BI-06-C",
    "GATT/SR/GAW/BI-07-C",
    "GATT/SR/GAW/BI-08-C",
    "GATT/SR/GAW/BI-12-C",
    "GATT/SR/GAW/BI-13-C",
    "GATT/SR/UNS/BI-01-C",
    "GATT/SR/UNS/BI-02-C",
    "HFP/AG/ACC/BV-08-I",
@@ -791,8 +800,6 @@
    "GAP/SEC/SEM/BV-44-C",
    "GATT/CL/GAD/BV-07-C",
    "GATT/CL/GAD/BV-08-C",
    "GATT/CL/GAI/BV-01-C",
    "GATT/CL/GAN/BV-01-C",
    "GATT/CL/GAN/BV-02-C",
    "GATT/CL/GAR/BI-04-C",
    "GATT/CL/GAR/BI-05-C",
@@ -801,8 +808,6 @@
    "GATT/CL/GAR/BI-16-C",
    "GATT/CL/GAR/BI-17-C",
    "GATT/CL/GAR/BV-03-C",
    "GATT/CL/GAS/BV-01-C",
    "GATT/CL/GAS/BV-03-C",
    "GATT/CL/GAT/BV-03-C",
    "GATT/CL/GAW/BI-05-C",
    "GATT/CL/GAW/BI-06-C",
@@ -815,20 +820,15 @@
    "GATT/SR/GAN/BV-01-C",
    "GATT/SR/GAN/BV-02-C",
    "GATT/SR/GAN/BV-02-C_LT2",
    "GATT/SR/GAR/BI-05-C",
    "GATT/SR/GAR/BI-11-C",
    "GATT/SR/GAR/BI-13-C",
    "GATT/SR/GAR/BI-17-C",
    "GATT/SR/GAR/BI-22-C",
    "GATT/SR/GAR/BI-44-C",
    "GATT/SR/GAR/BV-06-C",
    "GATT/SR/GAR/BV-07-C",
    "GATT/SR/GAR/BV-08-C",
    "GATT/SR/GAS/BV-01-C",
    "GATT/SR/GAT/BV-01-C",
    "GATT/SR/GAW/BI-06-C",
    "GATT/SR/GAW/BI-09-C",
    "GATT/SR/GAW/BI-13-C",
    "GATT/SR/GAW/BI-32-C",
    "GATT/SR/GAW/BI-33-C",
    "GATT/SR/GAW/BV-01-C",
+51 −0
Original line number Diff line number Diff line
@@ -271,6 +271,57 @@ class Gatt(private val context: Context) : GATTImplBase(), Closeable {
    }
  }

  override fun setCharacteristicNotificationFromHandle(
    request: SetCharacteristicNotificationFromHandleRequest,
    responseObserver: StreamObserver<SetCharacteristicNotificationFromHandleResponse>
  ) {
    grpcUnary<SetCharacteristicNotificationFromHandleResponse>(mScope, responseObserver) {
      Log.i(TAG, "SetCharcteristicNotificationFromHandle")
      val gattInstance = GattInstance.get(request.connection.address)
      val descriptor: BluetoothGattDescriptor? =
        getDescriptorWithHandle(request.handle, gattInstance)
      checkNotNull(descriptor) {
        "Found no descriptor with handle ${request.handle}"
      }
      var characteristic = descriptor.getCharacteristic()
      gattInstance.mGatt.setCharacteristicNotification(characteristic, true)
      if (request.enableValue == EnableValue.ENABLE_INDICATION_VALUE){
        val valueWrote =
          gattInstance.writeDescriptorBlocking(descriptor, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
        SetCharacteristicNotificationFromHandleResponse.newBuilder()
          .setHandle(valueWrote.handle)
          .setStatus(valueWrote.status)
          .build()
      } else {
        val valueWrote =
          gattInstance.writeDescriptorBlocking(descriptor, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
        SetCharacteristicNotificationFromHandleResponse.newBuilder()
          .setHandle(valueWrote.handle)
          .setStatus(valueWrote.status)
          .build()
      }
    }
  }

  override fun waitCharacteristicNotification(
    request: WaitCharacteristicNotificationRequest,
    responseObserver: StreamObserver<WaitCharacteristicNotificationResponse>
  ) {
    grpcUnary<WaitCharacteristicNotificationResponse>(mScope, responseObserver) {
      val gattInstance = GattInstance.get(request.connection.address)
      val descriptor: BluetoothGattDescriptor? =
        getDescriptorWithHandle(request.handle, gattInstance)
      checkNotNull(descriptor) {
        "Found no descriptor with handle ${request.handle}"
      }
      var characteristic = descriptor.getCharacteristic()
      val characteristicNotificationReceived = gattInstance.waitForOnCharacteristicChanged(characteristic)
      WaitCharacteristicNotificationResponse.newBuilder()
        .setCharacteristicNotificationReceived(characteristicNotificationReceived)
        .build()
    }
  }

  /**
   * Discovers services, then returns characteristic with given handle. BluetoothGatt API is
   * package-private so we have to redefine it here.
+21 −0
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@ class GattInstance(val mDevice: BluetoothDevice, val mTransport: Int, val mConte
  private var mConnectionState = MutableStateFlow(BluetoothProfile.STATE_DISCONNECTED)
  private var mValuesRead = MutableStateFlow(0)
  private var mValueWrote = MutableStateFlow(false)
  private var mOnCharacteristicChanged = MutableStateFlow(false)
  private var mCharacteristicChangedMap : MutableMap<BluetoothGattCharacteristic, Boolean> = mutableMapOf()

  /**
   * Wrapper for characteristic and descriptor reading. Uuid, startHandle and endHandle are used to
@@ -151,6 +153,16 @@ class GattInstance(val mDevice: BluetoothDevice, val mTransport: Int, val mConte
        mGattInstanceValueWrote.status = AttStatusCode.forNumber(status)
        mValueWrote.value = true
      }

      override fun onCharacteristicChanged(
        bluetoothGatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        value: ByteArray
      ) {
        Log.i(TAG, "onCharacteristicChanged, characteristic: " + characteristic.getUuid().toString())
        mCharacteristicChangedMap[characteristic] = true
        mOnCharacteristicChanged.value = true
      }
    }

  init {
@@ -187,6 +199,15 @@ class GattInstance(val mDevice: BluetoothDevice, val mTransport: Int, val mConte
    return mServiceDiscovered.value
  }

  public suspend fun waitForOnCharacteristicChanged(
    characteristic: BluetoothGattCharacteristic
    ):  Boolean{
    if (mOnCharacteristicChanged.value == false) {
      mOnCharacteristicChanged.first { it  == true }
    }
    return mCharacteristicChangedMap[characteristic] == true
  }

  public suspend fun waitForState(newState: Int) {
    if (mConnectionState.value != newState) {
      mConnectionState.first { it == newState }
+35 −0
Original line number Diff line number Diff line
@@ -37,6 +37,12 @@ service GATT {

  // Register a GATT service
  rpc RegisterService(RegisterServiceRequest) returns (RegisterServiceResponse);

  // Set characteristic notification/indication with given client characteristic configuration descriptor handle
  rpc SetCharacteristicNotificationFromHandle(SetCharacteristicNotificationFromHandleRequest) returns  (SetCharacteristicNotificationFromHandleResponse);

  // Wait for characteristic notification/indication
  rpc WaitCharacteristicNotification(WaitCharacteristicNotificationRequest) returns (WaitCharacteristicNotificationResponse);
}

enum AttStatusCode {
@@ -65,6 +71,11 @@ enum AttPermissions {
  PERMISSION_READ_ENCRYPTED = 0x02;
}

enum EnableValue {
  ENABLE_NOTIFICATION_VALUE = 0;
  ENABLE_INDICATION_VALUE = 1;
}

// A message representing a GATT service.
message GattService {
  uint32 handle = 1;
@@ -118,6 +129,30 @@ message WriteResponse {
  AttStatusCode status = 2;
}

// Request for the `SetCharacteristicNotificationFromHandle` rpc.
message SetCharacteristicNotificationFromHandleRequest {
  Connection connection = 1;
  uint32 handle = 2;
  EnableValue enable_value = 3;
}

// Response for the `SetCharacteristicNotificationFromHandle` rpc.
message SetCharacteristicNotificationFromHandleResponse {
  uint32 handle = 1;
  AttStatusCode status = 2;
}

// Request for the `WaitCharacteristicNotification` rpc.
message WaitCharacteristicNotificationRequest {
  Connection connection = 1;
  uint32 handle = 2;
}

// Response for the `WaitCharacteristicNotification` rpc.
message WaitCharacteristicNotificationResponse {
  bool characteristic_notification_received = 1;
}

// Request for the `DiscoverServiceByUuid` rpc.
message DiscoverServiceByUuidRequest {
  Connection connection = 1;