Java Code Examples for android.bluetooth.BluetoothGattDescriptor#getValue()

The following examples show how to use android.bluetooth.BluetoothGattDescriptor#getValue() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: DfuBaseService.java    From microbit with Apache License 2.0 6 votes vote down vote up
@Override
public void onDescriptorRead(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        if (CLIENT_CHARACTERISTIC_CONFIG.equals(descriptor.getUuid())) {
            if (SERVICE_CHANGED_UUID.equals(descriptor.getCharacteristic().getUuid())) {
                // We have enabled indications for the Service Changed characteristic
                mServiceChangedIndicationsEnabled = descriptor.getValue()[0] == 2;
                mRequestCompleted = true;
            }
        }
    } else {
        loge("Descriptor read error: " + status);
        mError = ERROR_CONNECTION_MASK | status;
    }
    // Notify waiting thread
    synchronized (mLock) {
        mLock.notifyAll();
    }
}
 
Example 2
Source File: InfoActivity.java    From Bluefruit_LE_Connect_Android with MIT License 6 votes vote down vote up
@Override
public void onDataAvailable(BluetoothGattDescriptor descriptor) {
    BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
    BluetoothGattService service = characteristic.getService();
    final String key = new ElementPath(service.getUuid().toString(), service.getInstanceId(), characteristic.getUuid().toString(), descriptor.getUuid().toString(), null, null).getKey();
    final byte[] value = descriptor.getValue();
    mValuesMap.put(key, value);

    // Update UI
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            updateUI();
        }
    });
}
 
Example 3
Source File: LoggerUtil.java    From RxAndroidBle with Apache License 2.0 5 votes vote down vote up
public static void logCallback(String callbackName, BluetoothGatt gatt, int status, BluetoothGattDescriptor descriptor,
                               boolean valueMatters) {
    if (!RxBleLog.isAtLeast(LogConstants.INFO)) {
        return;
    }
    AttributeLogWrapper value = new AttributeLogWrapper(descriptor.getUuid(), descriptor.getValue(), valueMatters);
    RxBleLog.i(commonMacMessage(gatt) + commonCallbackMessage() + commonStatusMessage() + commonValueMessage(),
            callbackName, status, value);
}
 
Example 4
Source File: BluetoothDebug.java    From openScale with GNU General Public License v3.0 5 votes vote down vote up
private void logService(BluetoothGattService service, boolean included) {
    Timber.d("Service %s%s", BluetoothGattUuid.prettyPrint(service.getUuid()),
            included ? " (included)" : "");

    for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
        Timber.d("|- characteristic %s (#%d): %s%s",
                BluetoothGattUuid.prettyPrint(characteristic.getUuid()),
                characteristic.getInstanceId(),
                propertiesToString(characteristic.getProperties(), characteristic.getWriteType()),
                permissionsToString(characteristic.getPermissions()));
        byte[] value = characteristic.getValue();
        if (value != null && value.length > 0) {
            Timber.d("|--> value: %s (%s)", byteInHex(value), byteToString(value));
        }

        for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
            Timber.d("|--- descriptor %s%s",
                    BluetoothGattUuid.prettyPrint(descriptor.getUuid()),
                    permissionsToString(descriptor.getPermissions()));

            value = descriptor.getValue();
            if (value != null && value.length > 0) {
                Timber.d("|-----> value: %s (%s)", byteInHex(value), byteToString(value));
            }
        }
    }

    for (BluetoothGattService includedService : service.getIncludedServices()) {
        logService(includedService, true);
    }
}
 
Example 5
Source File: BleManagerHandler.java    From Android-BLE-Library with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
private boolean internalSendNotification(@Nullable final BluetoothGattCharacteristic serverCharacteristic,
										 final boolean confirm) {
	if (serverManager == null || serverManager.getServer() == null || serverCharacteristic == null)
		return false;
	final int requiredProperty = confirm ? BluetoothGattCharacteristic.PROPERTY_INDICATE : BluetoothGattCharacteristic.PROPERTY_NOTIFY;
	if ((serverCharacteristic.getProperties() & requiredProperty) == 0)
		return false;
	final BluetoothGattDescriptor cccd = serverCharacteristic.getDescriptor(BleManager.CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
	if (cccd == null)
		return false;
	// If notifications/indications were enabled, send the notification/indication.
	final byte[] value = descriptorValues.containsKey(cccd) ? descriptorValues.get(cccd) : cccd.getValue();
	if (value != null && value.length == 2 && value[0] != 0) {
		log(Log.VERBOSE, "[Server] Sending " + (confirm ? "indication" : "notification") + " to " + serverCharacteristic.getUuid());
		log(Log.DEBUG, "server.notifyCharacteristicChanged(device, " + serverCharacteristic.getUuid() + ", " + confirm + ")");
		final boolean result = serverManager.getServer().notifyCharacteristicChanged(bluetoothDevice, serverCharacteristic, confirm);
		if (result && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
			post(() -> {
				notifyNotificationSent(bluetoothDevice);
				nextRequest(true);
			});
		}
		return result;
	}
	// Otherwise, assume the data was sent. The remote side has not registered for them.
	nextRequest(true);
	return true;
}
 
Example 6
Source File: P_BleDevice_Listeners.java    From SweetBlue with GNU General Public License v3.0 5 votes vote down vote up
@Override
public final void onDescriptorRead(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int gattStatus)
{
    final byte[] data = descriptor.getValue();

    m_device.getManager().getPostManager().runOrPostToUpdateThread(new Runnable()
    {
        @Override
        public void run()
        {
            onDescriptorRead_updateThread(gatt, descriptor, data, gattStatus);
        }
    });
}
 
Example 7
Source File: BLETransport.java    From esp-idf-provisioning-android with Apache License 2.0 5 votes vote down vote up
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {

    Log.d(TAG, "DescriptorRead, : Status " + status + " Data : " + new String(descriptor.getValue(), StandardCharsets.UTF_8));

    if (status != BluetoothGatt.GATT_SUCCESS) {
        Log.e(TAG, "Failed to read descriptor");
        EventBus.getDefault().post(new DeviceConnectionEvent(ESPConstants.EVENT_DEVICE_CONNECTION_FAILED));
        return;
    }

    byte[] data = descriptor.getValue();

    String value = new String(data, StandardCharsets.UTF_8);
    uuidMap.put(value, descriptor.getCharacteristic().getUuid().toString());
    Log.d(TAG, "Value : " + value + " for UUID : " + descriptor.getCharacteristic().getUuid().toString());

    if (isReadingDescriptors) {

        readNextDescriptor();

    } else {

        BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(uuidMap.get(ESPConstants.HANDLER_PROTO_VER)));

        if (characteristic != null) {
            // Write anything. It doesn't matter. We need to read characteristic and for that we need to write something.
            characteristic.setValue("ESP");
            bluetoothGatt.writeCharacteristic(characteristic);
        }
    }
}
 
Example 8
Source File: BlePeripheral.java    From Bluefruit_LE_Connect_Android_V2 with MIT License 5 votes vote down vote up
public static boolean isCharacteristicNotifyingForCachedClientConfigDescriptor(@NonNull BluetoothGattCharacteristic characteristic) {
    // Note: client characteristic descriptor should have been read previously
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(kClientCharacteristicConfigUUID);
    if (descriptor != null) {
        byte[] configValue = descriptor.getValue();
        return Arrays.equals(configValue, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    } else {
        return false;
    }
}
 
Example 9
Source File: ConnectionImpl.java    From easyble-x with Apache License 2.0 5 votes vote down vote up
private boolean enableNotificationOrIndicationFail(boolean enable, boolean notification, BluetoothGattCharacteristic characteristic) {
    if (!bluetoothAdapter.isEnabled() || bluetoothGatt == null || !bluetoothGatt
            .setCharacteristicNotification(characteristic, enable)) {
        return true;
    }
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(clientCharacteristicConfig);
    if (descriptor == null) {
        return true;
    }
    byte[] originValue = descriptor.getValue();
    if (currentRequest != null) {
        if (currentRequest.type == RequestType.SET_NOTIFICATION || currentRequest.type == RequestType.SET_INDICATION) {
            currentRequest.descriptorTemp = originValue;
        }
    }
    if (enable) {
        if (notification) {
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        } else {
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
        }
    } else {
        descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
    }
    // There was a bug in Android up to 6.0 where the descriptor was written using parent
    // characteristic's write type, instead of always Write With Response, as the spec says.
    int writeType = characteristic.getWriteType();
    characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
    boolean result = bluetoothGatt.writeDescriptor(descriptor);
    if (!enable) {
        //还原原始值
        descriptor.setValue(originValue);
    }
    characteristic.setWriteType(writeType);
    return !result;
}
 
Example 10
Source File: P_BleDevice_Listeners.java    From AsteroidOSSync with GNU General Public License v3.0 5 votes vote down vote up
@Override
public final void onDescriptorRead(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int gattStatus)
{
    final byte[] data = descriptor.getValue();

    m_device.getManager().getPostManager().runOrPostToUpdateThread(new Runnable()
    {
        @Override
        public void run()
        {
            onDescriptorRead_updateThread(gatt, descriptor, data, gattStatus);
        }
    });
}
 
Example 11
Source File: P_BleDevice_Listeners.java    From SweetBlue with GNU General Public License v3.0 5 votes vote down vote up
@Override
public final void onDescriptorWrite(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int gattStatus)
{
    final byte[] data = descriptor.getValue();

    m_device.getManager().getPostManager().runOrPostToUpdateThread(new Runnable()
    {
        @Override
        public void run()
        {
            onDescriptorWrite_updateThread(gatt, descriptor, data, gattStatus);
        }
    });
}
 
Example 12
Source File: ReadGattServerCharacteristicDescriptorValueTransaction.java    From bitgatt with Mozilla Public License 2.0 5 votes vote down vote up
@Override
protected void transaction(GattTransactionCallback callback) {
    super.transaction(callback);
    getGattServer().setState(GattState.READING_DESCRIPTOR);
    BluetoothGattCharacteristic localCharacteristic = service.getCharacteristic(this.characteristic.getUuid());
    if(localCharacteristic == null) {
        respondWithError(null, null, callback);
        return;
    }
    BluetoothGattDescriptor localDescriptor = localCharacteristic.getDescriptor(this.descriptor.getUuid());
    if(localDescriptor == null) {
        respondWithError(null, null, callback);
        return;
    }
    TransactionResult.Builder builder = new TransactionResult.Builder().transactionName(getName());
    byte[] value = localDescriptor.getValue();
    if(value != null) {
        // success
        builder.responseStatus(GattStatus.GATT_SUCCESS.ordinal());
            getGattServer().setState(GattState.READ_DESCRIPTOR_SUCCESS);
            builder.data(localCharacteristic.getValue())
                    .gattState(getGattServer().getGattState())
                    .resultStatus(TransactionResult.TransactionResultStatus.SUCCESS)
                    .serviceUuid(service.getUuid())
                    .characteristicUuid(localCharacteristic.getUuid())
                    .data(value)
                    .descriptorUuid(localDescriptor.getUuid());
        callCallbackWithTransactionResultAndRelease(callback, builder.build());
        getGattServer().setState(GattState.IDLE);
    } else {
        // failure
        respondWithError(localCharacteristic, localDescriptor, callback);
    }
}
 
Example 13
Source File: BleGattDescriptor.java    From Android-BluetoothKit with Apache License 2.0 4 votes vote down vote up
public BleGattDescriptor(BluetoothGattDescriptor descriptor) {
    this.mUuid = new ParcelUuid(descriptor.getUuid());
    this.mPermissions = descriptor.getPermissions();
    this.mValue = descriptor.getValue();
}
 
Example 14
Source File: Data.java    From Android-BLE-Library with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
public static Data from(@NonNull final BluetoothGattDescriptor descriptor) {
	return new Data(descriptor.getValue());
}
 
Example 15
Source File: BleManagerHandler.java    From Android-BLE-Library with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
@Override
public void onDescriptorWrite(final BluetoothGatt gatt,
							  final BluetoothGattDescriptor descriptor,
							  final int status) {
	final byte[] data = descriptor.getValue();

	if (status == BluetoothGatt.GATT_SUCCESS) {
		log(Log.INFO, "Data written to descr. " + descriptor.getUuid() +
				", value: " + ParserUtils.parse(data));

		if (isServiceChangedCCCD(descriptor)) {
			log(Log.INFO, "Service Changed notifications enabled");
		} else if (isCCCD(descriptor)) {
			if (data != null && data.length == 2 && data[1] == 0x00) {
				switch (data[0]) {
					case 0x00:
						log(Log.INFO, "Notifications and indications disabled");
						break;
					case 0x01:
						log(Log.INFO, "Notifications enabled");
						break;
					case 0x02:
						log(Log.INFO, "Indications enabled");
						break;
				}
				BleManagerHandler.this.onDescriptorWrite(gatt, descriptor);
			}
		} else {
			BleManagerHandler.this.onDescriptorWrite(gatt, descriptor);
		}
		if (request instanceof WriteRequest) {
			final WriteRequest wr = (WriteRequest) request;
			final boolean valid = wr.notifyPacketSent(gatt.getDevice(), data);
			if (!valid && requestQueue instanceof ReliableWriteRequest) {
				wr.notifyFail(gatt.getDevice(), FailCallback.REASON_VALIDATION);
				requestQueue.cancelQueue();
			} else if (wr.hasMore()) {
				enqueueFirst(wr);
			} else {
				wr.notifySuccess(gatt.getDevice());
			}
		}
	} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION
			|| status == 8 /* GATT INSUF AUTHORIZATION */
			|| status == 137 /* GATT AUTH FAIL */) {
		log(Log.WARN, "Authentication required (" + status + ")");
		if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) {
			// This should never happen but it used to: http://stackoverflow.com/a/20093695/2115352
			Log.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED);
			postCallback(c -> c.onError(gatt.getDevice(), ERROR_AUTH_ERROR_WHILE_BONDED, status));
		}
		// The request will be repeated when the bond state changes to BONDED.
		return;
	} else {
		Log.e(TAG, "onDescriptorWrite error " + status);
		if (request instanceof WriteRequest) {
			request.notifyFail(gatt.getDevice(), status);
			// Automatically abort Reliable Write when write error happen
			if (requestQueue instanceof ReliableWriteRequest)
				requestQueue.cancelQueue();
		}
		awaitingRequest = null;
		onError(gatt.getDevice(), ERROR_WRITE_DESCRIPTOR, status);
	}
	checkCondition();
	nextRequest(true);
}
 
Example 16
Source File: BleManagerHandler.java    From Android-BLE-Library with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
@Override
public void onCharacteristicChanged(final BluetoothGatt gatt,
									final BluetoothGattCharacteristic characteristic) {
	final byte[] data = characteristic.getValue();

	if (isServiceChangedCharacteristic(characteristic)) {
		// TODO this should be tested. Should services be invalidated?
		// Forbid enqueuing more operations.
		operationInProgress = true;
		// Clear queues, services are no longer valid.
		taskQueue.clear();
		initQueue = null;
		log(Log.INFO, "Service Changed indication received");
		log(Log.VERBOSE, "Discovering Services...");
		log(Log.DEBUG, "gatt.discoverServices()");
		gatt.discoverServices();
	} else {
		final BluetoothGattDescriptor cccd =
				characteristic.getDescriptor(BleManager.CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
		final boolean notifications = cccd == null || cccd.getValue() == null ||
				cccd.getValue().length != 2 || cccd.getValue()[0] == 0x01;

		final String dataString = ParserUtils.parse(data);
		if (notifications) {
			log(Log.INFO, "Notification received from " +
					characteristic.getUuid() + ", value: " + dataString);
			onCharacteristicNotified(gatt, characteristic);
		} else { // indications
			log(Log.INFO, "Indication received from " +
					characteristic.getUuid() + ", value: " + dataString);
			onCharacteristicIndicated(gatt, characteristic);
		}
		if (batteryLevelNotificationCallback != null && isBatteryLevelCharacteristic(characteristic)) {
			batteryLevelNotificationCallback.notifyValueChanged(gatt.getDevice(), data);
		}
		// Notify the notification registered listener, if set
		final ValueChangedCallback request = valueChangedCallbacks.get(characteristic);
		if (request != null && request.matches(data)) {
			request.notifyValueChanged(gatt.getDevice(), data);
		}
		// If there is a value change request,
		if (awaitingRequest instanceof WaitForValueChangedRequest
				// registered for this characteristic
				&& awaitingRequest.characteristic == characteristic
				// and didn't have a trigger, or the trigger was started
				// (not necessarily completed)
				&& !awaitingRequest.isTriggerPending()) {
			final WaitForValueChangedRequest valueChangedRequest = (WaitForValueChangedRequest) awaitingRequest;
			if (valueChangedRequest.matches(data)) {
				// notify that new data was received.
				valueChangedRequest.notifyValueChanged(gatt.getDevice(), data);

				// If no more data are expected
				if (!valueChangedRequest.hasMore()) {
					// notify success,
					valueChangedRequest.notifySuccess(gatt.getDevice());
					// and proceed to the next request only if the trigger has completed.
					// Otherwise, the next request will be started when the request's callback
					// will be received.
					awaitingRequest = null;
					if (valueChangedRequest.isTriggerCompleteOrNull()) {
						nextRequest(true);
					}
				}
			}
		}
		if (checkCondition()) {
			nextRequest(true);
		}
	}
}
 
Example 17
Source File: BleManagerHandler.java    From Android-BLE-Library with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
final void onDescriptorReadRequest(@NonNull final BluetoothGattServer server,
								   @NonNull final BluetoothDevice device, final int requestId, final int offset,
								   @NonNull final BluetoothGattDescriptor descriptor) {
	log(Log.DEBUG, "[Server callback] Read request for descriptor " + descriptor.getUuid() + " (requestId=" + requestId + ", offset: " + offset + ")");
	if (offset == 0)
		log(Log.INFO, "[Server] READ request for descriptor " + descriptor.getUuid() + " received");

	byte[] data = descriptorValues == null || !descriptorValues.containsKey(descriptor)
			? descriptor.getValue() : descriptorValues.get(descriptor);

	WaitForReadRequest waitForReadRequest = null;
	// First, try to get the data from the WaitForReadRequest if the request awaits,
	if (awaitingRequest instanceof WaitForReadRequest
			// is registered for this descriptor
			&& awaitingRequest.descriptor == descriptor
			// and didn't have a trigger, or the trigger was started
			// (not necessarily completed)
			&& !awaitingRequest.isTriggerPending()) {
		waitForReadRequest = (WaitForReadRequest) awaitingRequest;
		waitForReadRequest.setDataIfNull(data);
		data = waitForReadRequest.getData(mtu);
	}
	// If data are longer than MTU - 1, cut the array. Only ATT_MTU - 1 bytes can be sent in Long Read.
	if (data != null && data.length > mtu - 1) {
		data = Bytes.copy(data, offset, mtu - 1);
	}

	sendResponse(server, device, BluetoothGatt.GATT_SUCCESS, requestId, offset, data);

	if (waitForReadRequest != null) {
		waitForReadRequest.notifyPacketRead(device, data);

		// If the request is complete, start next one.
		if (!waitForReadRequest.hasMore() && (data == null || data.length < mtu - 1)) {
			waitForReadRequest.notifySuccess(device);
			awaitingRequest = null;
			nextRequest(true);
		}
	} else if (checkCondition()) {
		nextRequest(true);
	}
}
 
Example 18
Source File: LoggerUtil.java    From RxAndroidBle with Apache License 2.0 4 votes vote down vote up
public static AttributeLogWrapper wrap(BluetoothGattDescriptor descriptor, boolean valueMatters) {
    return new AttributeLogWrapper(descriptor.getUuid(), descriptor.getValue(), valueMatters);
}
 
Example 19
Source File: Peripheral.java    From react-native-ble-manager with Apache License 2.0 4 votes vote down vote up
public WritableMap asWritableMap(BluetoothGatt gatt) {

		WritableMap map = asWritableMap();

		WritableArray servicesArray = Arguments.createArray();
		WritableArray characteristicsArray = Arguments.createArray();

		if (connected && gatt != null) {
			for (Iterator<BluetoothGattService> it = gatt.getServices().iterator(); it.hasNext();) {
				BluetoothGattService service = it.next();
				WritableMap serviceMap = Arguments.createMap();
				serviceMap.putString("uuid", UUIDHelper.uuidToString(service.getUuid()));

				for (Iterator<BluetoothGattCharacteristic> itCharacteristic = service.getCharacteristics()
						.iterator(); itCharacteristic.hasNext();) {
					BluetoothGattCharacteristic characteristic = itCharacteristic.next();
					WritableMap characteristicsMap = Arguments.createMap();

					characteristicsMap.putString("service", UUIDHelper.uuidToString(service.getUuid()));
					characteristicsMap.putString("characteristic", UUIDHelper.uuidToString(characteristic.getUuid()));

					characteristicsMap.putMap("properties", Helper.decodeProperties(characteristic));

					if (characteristic.getPermissions() > 0) {
						characteristicsMap.putMap("permissions", Helper.decodePermissions(characteristic));
					}

					WritableArray descriptorsArray = Arguments.createArray();

					for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
						WritableMap descriptorMap = Arguments.createMap();
						descriptorMap.putString("uuid", UUIDHelper.uuidToString(descriptor.getUuid()));
						if (descriptor.getValue() != null) {
							descriptorMap.putString("value",
									Base64.encodeToString(descriptor.getValue(), Base64.NO_WRAP));
						} else {
							descriptorMap.putString("value", null);
						}

						if (descriptor.getPermissions() > 0) {
							descriptorMap.putMap("permissions", Helper.decodePermissions(descriptor));
						}
						descriptorsArray.pushMap(descriptorMap);
					}
					if (descriptorsArray.size() > 0) {
						characteristicsMap.putArray("descriptors", descriptorsArray);
					}
					characteristicsArray.pushMap(characteristicsMap);
				}
				servicesArray.pushMap(serviceMap);
			}
			map.putArray("services", servicesArray);
			map.putArray("characteristics", characteristicsArray);
		}

		return map;
	}
 
Example 20
Source File: BluetoothPeripheral.java    From blessed-android with MIT License 4 votes vote down vote up
@Override
public void onDescriptorWrite(BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {
    final BluetoothGattCharacteristic parentCharacteristic = descriptor.getCharacteristic();
    if (status != GATT_SUCCESS) {
        Timber.e("failed to write <%s> to descriptor of characteristic: <%s> for device: '%s', ", bytes2String(currentWriteBytes), parentCharacteristic.getUuid(), getAddress());
    }

    // Check if this was the Client Configuration Descriptor
    if (descriptor.getUuid().equals(UUID.fromString(CCC_DESCRIPTOR_UUID))) {
        if (status == GATT_SUCCESS) {
            byte[] value = descriptor.getValue();
            if (value != null) {
                if (Arrays.equals(value, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) ||
                    Arrays.equals(value, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)){
                    // Notify set to on, add it to the set of notifying characteristics
                    notifyingCharacteristics.add(parentCharacteristic.getUuid());
                    if (notifyingCharacteristics.size() > MAX_NOTIFYING_CHARACTERISTICS) {
                        Timber.e("too many (%d) notifying characteristics. The maximum Android can handle is %d", notifyingCharacteristics.size(), MAX_NOTIFYING_CHARACTERISTICS);
                    }
                } else if (Arrays.equals(value, BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)){
                    // Notify was turned off, so remove it from the set of notifying characteristics
                    notifyingCharacteristics.remove(parentCharacteristic.getUuid());
                } else {
                    Timber.e("unexpected CCC descriptor value");
                }
            }
        }

        callbackHandler.post(new Runnable() {
            @Override
            public void run() {
                peripheralCallback.onNotificationStateUpdate(BluetoothPeripheral.this, parentCharacteristic, status);
            }
        });
    } else {
        callbackHandler.post(new Runnable() {
            @Override
            public void run() {
                peripheralCallback.onDescriptorWrite(BluetoothPeripheral.this, currentWriteBytes, descriptor, status);
            }
        });
    }
    completedCommand();
}