/// <summary> /// Fetch the characteristic referred to in the endpointInfo object and perform access verification. /// </summary> /// <param name="errorContext"> /// A string to include in error reporting, if an error is encountered /// </param> /// <param name="endpointInfo"> /// A JSON object which may contain a 'serviceId' property and a 'characteristicId' property /// </param> /// <param name="checkFlag"> /// Check if this flag is set for this service or characteristic in the block list. If so, throw. /// </param> /// <returns> /// The specified GATT service characteristic, if it can be resolved and all checks pass. /// Otherwise, a JSON-RPC exception is thrown indicating what went wrong. /// </returns> private async Task <GattCharacteristic> GetEndpoint(string errorContext, JObject endpointInfo, GattHelpers.BlockListStatus checkFlag) { GattDeviceService service; Guid?serviceId; if (_peripheral.ConnectionStatus != BluetoothConnectionStatus.Connected) { throw JsonRpcException.ApplicationError($"Peripheral is not connected for {errorContext}"); } if (endpointInfo.TryGetValue("serviceId", out var serviceToken)) { serviceId = GattHelpers.GetServiceUuid(serviceToken); service = _services?.FirstOrDefault(s => s.Uuid == serviceId); } else { service = _services?.FirstOrDefault(); // could in theory be null serviceId = service?.Uuid; } if (!serviceId.HasValue) { throw JsonRpcException.InvalidParams($"Could not determine service UUID for {errorContext}"); } //if (_allowedServices?.Contains(serviceId.Value) != true) //{ // throw JsonRpcException.InvalidParams($"attempt to access unexpected service: {serviceId}"); //} var blockStatus = GattHelpers.GetBlockListStatus(serviceId.Value); if (blockStatus.HasFlag(checkFlag)) { throw JsonRpcException.InvalidParams($"service is block-listed with {blockStatus}: {serviceId}"); } if (service == null) { throw JsonRpcException.InvalidParams($"could not find service {serviceId}"); } GattCharacteristic characteristic; Guid?characteristicId; if (endpointInfo.TryGetValue("characteristicId", out var characteristicToken)) { characteristic = null; // we will attempt to collect this below characteristicId = GattHelpers.GetCharacteristicUuid(characteristicToken); } else { if (!_cachedServiceCharacteristics.TryGetValue(service.Uuid, out var characteristics)) { var characteristicsResult = await service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (characteristicsResult.Status != GattCommunicationStatus.Success) { throw JsonRpcException.ApplicationError( $"failed to collect characteristics from service: {characteristicsResult.Status}"); } characteristics = characteristicsResult.Characteristics; _cachedServiceCharacteristics.Add(service.Uuid, characteristics); } characteristic = characteristics.FirstOrDefault(); // could in theory be null characteristicId = characteristic?.Uuid; } if (!characteristicId.HasValue) { throw JsonRpcException.InvalidParams($"Could not determine characteristic UUID for {errorContext}"); } blockStatus = GattHelpers.GetBlockListStatus(characteristicId.Value); if (blockStatus.HasFlag(checkFlag)) { throw JsonRpcException.InvalidParams( $"characteristic is block-listed with {blockStatus}: {characteristicId}"); } // collect the characteristic if we didn't do so above if (characteristic == null && !_cachedCharacteristics.TryGetValue(characteristicId.Value, out characteristic)) { var characteristicsResult = await service.GetCharacteristicsForUuidAsync(characteristicId.Value, BluetoothCacheMode.Uncached); if (characteristicsResult.Status != GattCommunicationStatus.Success) { throw JsonRpcException.ApplicationError( $"failed to collect characteristics from service: {characteristicsResult.Status}"); } if (characteristicsResult.Characteristics.Count < 1) { throw JsonRpcException.InvalidParams( $"could not find characteristic {characteristicId} on service {serviceId}"); } // TODO: why is this a list? characteristic = characteristicsResult.Characteristics[0]; _cachedCharacteristics.Add(characteristicId.Value, characteristic); } try { // Unfortunately there's no direct way to test if the peripheral object has been disposed. The // `connectionState` property still indicates that the peripheral is connected in some cases, for // example when Bluetooth is turned off in Bluetooth settings / Control Panel. However, trying to // access the `Service` property of the `Characteristic` will throw an `ObjectDisposedException` in // this case, so that's the hack being used here to check for a disposed peripheral. var tempDisposalProbe = characteristic.Service; } catch (ObjectDisposedException e) { // This could mean that Bluetooth was turned off or the computer resumed from sleep throw JsonRpcException.ApplicationError($"Peripheral is disposed for {errorContext}"); } return(characteristic); }
/// <summary> /// Fetch the characteristic referred to in the endpointInfo object and perform access verification. /// </summary> /// <param name="errorContext"> /// A string to include in error reporting, if an error is encountered /// </param> /// <param name="endpointInfo"> /// A JSON object which may contain a 'serviceId' property and a 'characteristicId' property /// </param> /// <param name="checkFlag"> /// Check if this flag is set for this service or characteristic in the block list. If so, throw. /// </param> /// <returns> /// The specified GATT service characteristic, if it can be resolved and all checks pass. /// Otherwise, a JSON-RPC exception is thrown indicating what went wrong. /// </returns> private async Task <GattCharacteristic> GetEndpoint(string errorContext, JObject endpointInfo, GattHelpers.BlockListStatus checkFlag) { GattDeviceService service; Guid?serviceId; if (endpointInfo.TryGetValue("serviceId", out var serviceToken)) { serviceId = GattHelpers.GetServiceUuid(serviceToken); service = _services?.FirstOrDefault(s => s.Uuid == serviceId); } else { service = _services?.FirstOrDefault(); // could in theory be null serviceId = service?.Uuid; } if (!serviceId.HasValue) { throw JsonRpcException.InvalidParams($"Could not determine service UUID for {errorContext}"); } if (_allowedServices?.Contains(serviceId.Value) != true) { throw JsonRpcException.InvalidParams($"attempt to access unexpected service: {serviceId}"); } var blockStatus = GattHelpers.GetBlockListStatus(serviceId.Value); if (blockStatus.HasFlag(checkFlag)) { throw JsonRpcException.InvalidParams($"service is block-listed with {blockStatus}: {serviceId}"); } if (service == null) { throw JsonRpcException.InvalidParams($"could not find service {serviceId}"); } GattCharacteristic characteristic; Guid?characteristicId; if (endpointInfo.TryGetValue("characteristicId", out var characteristicToken)) { characteristic = null; // we will attempt to collect this below characteristicId = GattHelpers.GetCharacteristicUuid(characteristicToken); } else { if (!_cachedServiceCharacteristics.TryGetValue(service.Uuid, out var characteristics)) { var characteristicsResult = await service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (characteristicsResult.Status != GattCommunicationStatus.Success) { throw JsonRpcException.ApplicationError( $"failed to collect characteristics from service: {characteristicsResult.Status}"); } characteristics = characteristicsResult.Characteristics; _cachedServiceCharacteristics.Add(service.Uuid, characteristics); } characteristic = characteristics.FirstOrDefault(); // could in theory be null characteristicId = characteristic?.Uuid; } if (!characteristicId.HasValue) { throw JsonRpcException.InvalidParams($"Could not determine characteristic UUID for {errorContext}"); } blockStatus = GattHelpers.GetBlockListStatus(characteristicId.Value); if (blockStatus.HasFlag(checkFlag)) { throw JsonRpcException.InvalidParams( $"characteristic is block-listed with {blockStatus}: {characteristicId}"); } // collect the characteristic if we didn't do so above if (characteristic == null && !_cachedCharacteristics.TryGetValue(characteristicId.Value, out characteristic)) { var characteristicsResult = await service.GetCharacteristicsForUuidAsync(characteristicId.Value, BluetoothCacheMode.Uncached); if (characteristicsResult.Status != GattCommunicationStatus.Success) { throw JsonRpcException.ApplicationError( $"failed to collect characteristics from service: {characteristicsResult.Status}"); } if (characteristicsResult.Characteristics.Count < 1) { throw JsonRpcException.InvalidParams( $"could not find characteristic {characteristicId} on service {serviceId}"); } // TODO: why is this a list? characteristic = characteristicsResult.Characteristics[0]; _cachedCharacteristics.Add(characteristicId.Value, characteristic); } return(characteristic); }