public override async Task <IReadOnlyCollection <Characteristic> > DiscoverCharacteristicsAsync(IList <byte[]> characteristicUUIDs = null, int timeoutMs = 10000) { try { ThrowIfDisposed(); ThrowIfNotConnected(); if (State != ServiceState.Idle) { throw new Exception("Already busy discovering services"); } State = ServiceState.DiscoveringCharacteristics; ClearCharacteristics(); _bgApi.ATTClientAttributeValue += BGApi_ATTClientAttributeValue; _bgApi.ATTClientProcedureCompleted += BGApi_ATTClientProcedureCompleted; _clientConfigurationAttributeHandles.Clear(); // Discover characteristic declaration attributes await ReadAttributeByType( GATTAttributeType.CharacteristicUUID, timeoutMs / 2 ).ConfigureAwait(false); if (!Characteristics.Any()) { return(Characteristics); } // Discover client characteristic configuration attributes await ReadAttributeByType( GATTAttributeType.CharacteristicClientConfigurationUUID, timeoutMs / 2 ).ConfigureAwait(false); // now determine which characteristic each client characteristic configuration handle belongs to if ((_clientConfigurationAttributeHandles.Count == 1) && (Characteristics.Count == 1)) { Characteristics.First().CharacteristicClientConfigurationHandle = _clientConfigurationAttributeHandles.First(); } else if ((_clientConfigurationAttributeHandles.Count > 0) && (Characteristics.Count > 0)) { // sort the characteristic list by handle (ascending) var charsAscending = Characteristics.OrderBy( c => c.CharacteristicDeclarationHandle).ToArray(); // for each config handle, find the closest characteristic handle going up in values foreach (var cfgHnd in _clientConfigurationAttributeHandles) { Characteristic charWhereCurrConfigHandleBelongs = null; for (int i = 1; i < charsAscending.Length; i++) { var currCharHnd = charsAscending[i].CharacteristicDeclarationHandle; var lastCharHnd = charsAscending[i - 1].CharacteristicDeclarationHandle; if ((cfgHnd > lastCharHnd) && (cfgHnd < currCharHnd)) { charWhereCurrConfigHandleBelongs = charsAscending[i - 1]; break; } } if (charWhereCurrConfigHandleBelongs == null) { charWhereCurrConfigHandleBelongs = charsAscending.Last(); } charWhereCurrConfigHandleBelongs.CharacteristicClientConfigurationHandle = cfgHnd; } } } finally { _bgApi.ATTClientAttributeValue -= BGApi_ATTClientAttributeValue; _bgApi.ATTClientProcedureCompleted -= BGApi_ATTClientProcedureCompleted; _exceptionToRethrow = null; _uuidOfAttributeBeingRead = 0; _clientConfigurationAttributeHandles.Clear(); } State = ServiceState.Idle; var results = FilterCharacteristicsByUUIDs(Characteristics, characteristicUUIDs); return(results); }