From 094e9a4f79f387ac84cc04b297c2a26367ee4ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Lenart?= Date: Fri, 6 Jul 2018 13:09:13 +0200 Subject: [PATCH] Added GATT status code to BleDisconnectException (#405) (#456) * Added GATT status code to BleDisconnectException (#405) * Added suggested RestrictTo LIBRARY_GROUP annotation fo adapterDisabled utility function. --- .../BleAdapterDisabledException.java | 5 + .../exceptions/BleDisconnectedException.java | 55 +++++++++-- .../exceptions/BleGattException.java | 7 +- .../internal/SingleResponseOperation.java | 7 +- .../connection/DisconnectionRouter.java | 3 +- .../connection/RxBleConnectionImpl.java | 3 +- .../connection/RxBleGattCallback.java | 2 +- .../CharacteristicLongWriteOperation.java | 3 +- .../internal/operations/ConnectOperation.java | 2 +- .../operations/DisconnectOperation.java | 2 +- .../ConnectionOperationQueueImpl.java | 5 +- .../rxandroidble/utils/GattStatusParser.java | 96 +++++++++++++++++++ .../BleDisconnectedExceptionTest.groovy | 16 +++- .../exceptions/BleGattExceptionTest.groovy | 2 +- 14 files changed, 188 insertions(+), 20 deletions(-) create mode 100644 rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleAdapterDisabledException.java create mode 100644 rxandroidble/src/main/java/com/polidea/rxandroidble/utils/GattStatusParser.java diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleAdapterDisabledException.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleAdapterDisabledException.java new file mode 100644 index 000000000..8df3035cc --- /dev/null +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleAdapterDisabledException.java @@ -0,0 +1,5 @@ +package com.polidea.rxandroidble.exceptions; + +public class BleAdapterDisabledException extends BleException { + // Disconnection related to disabled Bluetooth adapter +} diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleDisconnectedException.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleDisconnectedException.java index abcb75e69..0b06f84b5 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleDisconnectedException.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleDisconnectedException.java @@ -2,36 +2,77 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.RestrictTo; + +import com.polidea.rxandroidble.utils.GattStatusParser; /** * Exception emitted when the BLE link has been disconnected either when the connection was already established * or was in pending connection state. This state is expected when the connection was released as a * part of expected behavior (with {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS} state). * - * @see com.polidea.rxandroidble.RxBleDevice#establishConnection(Context, boolean) + * @see com.polidea.rxandroidble.RxBleDevice#establishConnection(boolean) */ public class BleDisconnectedException extends BleException { + /** + * Set when the state is not available, for example when the adapter has been switched off. + */ + public static final int UNKNOWN_STATUS = -1; + @SuppressWarnings("WeakerAccess") @NonNull public final String bluetoothDeviceAddress; + public final int state; + + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public static BleDisconnectedException adapterDisabled(String macAddress) { + return new BleDisconnectedException(new BleAdapterDisabledException(), macAddress, UNKNOWN_STATUS); + } + /** + * @deprecated In general, there's no place in a public API that requires you to instantiate this exception directly. + * If you use it anyway, please switch to {@link #BleDisconnectedException(String, int)} + */ @Deprecated public BleDisconnectedException() { - bluetoothDeviceAddress = ""; + this("", UNKNOWN_STATUS); } + /** + * @deprecated In general, there's no place in a public API that requires you to instantiate this exception directly. + * If you use it anyway, please switch to {@link #BleDisconnectedException(Throwable, String, int)} + */ + @Deprecated public BleDisconnectedException(Throwable throwable, @NonNull String bluetoothDeviceAddress) { - super(createMessage(bluetoothDeviceAddress), throwable); - this.bluetoothDeviceAddress = bluetoothDeviceAddress; + this(throwable, bluetoothDeviceAddress, UNKNOWN_STATUS); } + /** + * @deprecated In general, there's no place in a public API that requires you to instantiate this exception directly. + * If you use it anyway, please switch to {@link #BleDisconnectedException(String, int)} or don't use it. + */ + @Deprecated public BleDisconnectedException(@NonNull String bluetoothDeviceAddress) { - super(createMessage(bluetoothDeviceAddress)); + this(bluetoothDeviceAddress, UNKNOWN_STATUS); + } + + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public BleDisconnectedException(Throwable throwable, @NonNull String bluetoothDeviceAddress, int status) { + super(createMessage(bluetoothDeviceAddress, status), throwable); + this.bluetoothDeviceAddress = bluetoothDeviceAddress; + this.state = status; + } + + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public BleDisconnectedException(@NonNull String bluetoothDeviceAddress, int status) { + super(createMessage(bluetoothDeviceAddress, status)); this.bluetoothDeviceAddress = bluetoothDeviceAddress; + this.state = status; } - private static String createMessage(@Nullable String bluetoothDeviceAddress) { - return "Disconnected from " + bluetoothDeviceAddress; + private static String createMessage(@Nullable String bluetoothDeviceAddress, int status) { + final String gattCallbackStatusDescription = GattStatusParser.getGattCallbackStatusDescription(status); + return "Disconnected from " + bluetoothDeviceAddress + " with status " + status + " (" + gattCallbackStatusDescription + ")"; } } diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleGattException.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleGattException.java index b0eae85fd..6cb245207 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleGattException.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/exceptions/BleGattException.java @@ -5,6 +5,8 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.polidea.rxandroidble.utils.GattStatusParser; + /** * Exception emitted when the BLE link has been interrupted as a result of an error. The exception contains * detailed explanation of the error source (type of operation) and the code proxied from @@ -65,9 +67,10 @@ private static String createMessage(@Nullable BluetoothGatt gatt, int status, Bl getMacAddress(gatt), bleGattOperationType); } + final String statusDescription = GattStatusParser.getGattCallbackStatusDescription(status); final String link = "https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-5.1.0_r1/stack/include/gatt_api.h"; - return String.format("GATT exception from MAC address %s, status %d, type %s. (Look up status 0x%02x here %s)", - getMacAddress(gatt), status, bleGattOperationType, status, link); + return String.format("GATT exception from MAC address %s, status %d (%s), type %s. (Look up status 0x%02x here %s)", + getMacAddress(gatt), status, statusDescription, bleGattOperationType, status, link); } } diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/SingleResponseOperation.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/SingleResponseOperation.java index acc688af5..4438ce02c 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/SingleResponseOperation.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/SingleResponseOperation.java @@ -14,7 +14,9 @@ import com.polidea.rxandroidble.internal.serialization.QueueReleaseInterface; import com.polidea.rxandroidble.internal.util.QueueReleasingEmitterWrapper; + import java.util.concurrent.TimeUnit; + import rx.Emitter; import rx.Observable; import rx.Scheduler; @@ -22,6 +24,7 @@ /** * A convenience class intended to use with {@link BluetoothGatt} functions that fire one-time actions. + * * @param The type of emitted result. */ public abstract class SingleResponseOperation extends QueueOperation { @@ -74,6 +77,7 @@ final protected void protectedRun(final Emitter emitter, final QueueReleaseIn /** * A function that should call the passed {@link BluetoothGatt} and return `true` if the call has succeeded. + * * @param bluetoothGatt the {@link BluetoothGatt} to use * @return `true` if success, `false` otherwise */ @@ -89,6 +93,7 @@ protected Observable timeoutFallbackProcedure( @Override protected BleException provideException(DeadObjectException deadObjectException) { - return new BleDisconnectedException(deadObjectException, bluetoothGatt.getDevice().getAddress()); + return new BleDisconnectedException(deadObjectException, bluetoothGatt.getDevice().getAddress(), + BleDisconnectedException.UNKNOWN_STATUS); } } diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/DisconnectionRouter.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/DisconnectionRouter.java index 8ac2efbf8..20192915f 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/DisconnectionRouter.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/DisconnectionRouter.java @@ -8,6 +8,7 @@ import com.polidea.rxandroidble.exceptions.BleGattException; import com.polidea.rxandroidble.internal.DeviceModule; import com.polidea.rxandroidble.internal.util.RxBleAdapterWrapper; + import bleshadow.javax.inject.Inject; import bleshadow.javax.inject.Named; import rx.Observable; @@ -45,7 +46,7 @@ public Boolean call(Boolean isAdapterUsable) { .map(new Func1() { @Override public BleException call(Boolean isAdapterUsable) { - return new BleDisconnectedException(macAddress); // TODO: Introduce BleDisabledException? + return BleDisconnectedException.adapterDisabled(macAddress); } }); diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/RxBleConnectionImpl.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/RxBleConnectionImpl.java index 40046bb35..1acadea25 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/RxBleConnectionImpl.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/RxBleConnectionImpl.java @@ -354,7 +354,8 @@ public void call() { @Override protected BleException provideException(DeadObjectException deadObjectException) { - return new BleDisconnectedException(deadObjectException, bluetoothGatt.getDevice().getAddress()); + return new BleDisconnectedException(deadObjectException, bluetoothGatt.getDevice().getAddress(), + BleDisconnectedException.UNKNOWN_STATUS); } }); } diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/RxBleGattCallback.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/RxBleGattCallback.java index 4bdf45954..acb28f876 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/RxBleGattCallback.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/connection/RxBleGattCallback.java @@ -73,7 +73,7 @@ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState bluetoothGattProvider.updateBluetoothGatt(gatt); if (isDisconnectedOrDisconnecting(newState)) { - disconnectionRouter.onDisconnectedException(new BleDisconnectedException(gatt.getDevice().getAddress())); + disconnectionRouter.onDisconnectedException(new BleDisconnectedException(gatt.getDevice().getAddress(), status)); } else if (status != BluetoothGatt.GATT_SUCCESS) { disconnectionRouter.onGattConnectionStateException( new BleGattException(gatt, status, BleGattOperationType.CONNECTION_STATE) diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/CharacteristicLongWriteOperation.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/CharacteristicLongWriteOperation.java index 9a181ffd8..d4b90fd88 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/CharacteristicLongWriteOperation.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/CharacteristicLongWriteOperation.java @@ -116,7 +116,8 @@ public void call(Throwable throwable) { @Override protected BleException provideException(DeadObjectException deadObjectException) { - return new BleDisconnectedException(deadObjectException, bluetoothGatt.getDevice().getAddress()); + return new BleDisconnectedException(deadObjectException, bluetoothGatt.getDevice().getAddress(), + BleDisconnectedException.UNKNOWN_STATUS); } @NonNull diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/ConnectOperation.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/ConnectOperation.java index 14ac5f231..2aac3c817 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/ConnectOperation.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/ConnectOperation.java @@ -177,6 +177,6 @@ public void cancel() throws Exception { @Override protected BleException provideException(DeadObjectException deadObjectException) { - return new BleDisconnectedException(deadObjectException, bluetoothDevice.getAddress()); + return new BleDisconnectedException(deadObjectException, bluetoothDevice.getAddress(), BleDisconnectedException.UNKNOWN_STATUS); } } diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/DisconnectOperation.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/DisconnectOperation.java index 36415f94a..fbcfa9a7c 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/DisconnectOperation.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/operations/DisconnectOperation.java @@ -160,6 +160,6 @@ public void call() { @Override protected BleException provideException(DeadObjectException deadObjectException) { - return new BleDisconnectedException(deadObjectException, macAddress); + return new BleDisconnectedException(deadObjectException, macAddress, BleDisconnectedException.UNKNOWN_STATUS); } } diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/serialization/ConnectionOperationQueueImpl.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/serialization/ConnectionOperationQueueImpl.java index 1184f03cd..0f38bd8db 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/serialization/ConnectionOperationQueueImpl.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/internal/serialization/ConnectionOperationQueueImpl.java @@ -1,6 +1,7 @@ package com.polidea.rxandroidble.internal.serialization; import android.support.annotation.RestrictTo; + import com.polidea.rxandroidble.ClientComponent; import com.polidea.rxandroidble.exceptions.BleDisconnectedException; import com.polidea.rxandroidble.exceptions.BleException; @@ -10,8 +11,10 @@ import com.polidea.rxandroidble.internal.connection.ConnectionSubscriptionWatcher; import com.polidea.rxandroidble.internal.connection.DisconnectionRouterOutput; import com.polidea.rxandroidble.internal.operations.Operation; + import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; + import bleshadow.javax.inject.Inject; import bleshadow.javax.inject.Named; import rx.Emitter; @@ -144,6 +147,6 @@ public void call(BleException bleException) { public void onConnectionUnsubscribed() { disconnectionThrowableSubscription.unsubscribe(); disconnectionThrowableSubscription = null; - terminate(new BleDisconnectedException(deviceMacAddress)); + terminate(new BleDisconnectedException(deviceMacAddress, BleDisconnectedException.UNKNOWN_STATUS)); } } diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble/utils/GattStatusParser.java b/rxandroidble/src/main/java/com/polidea/rxandroidble/utils/GattStatusParser.java new file mode 100644 index 000000000..09a17277c --- /dev/null +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble/utils/GattStatusParser.java @@ -0,0 +1,96 @@ +/** + * Parsing thanks to + * https://github.com/adafruit/Bluefruit_LE_Connect_Android/blob/ + * master/app/src/main/java/com/adafruit/bluefruit/le/connect/ble/StandardUUIDs.java + * Bluefruit LE Connect for Android + *

+ *

+ * The MIT License (MIT) + *

+ * Copyright (c) 2015 Adafruit Industries + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.polidea.rxandroidble.utils; + +import android.annotation.SuppressLint; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class GattStatusParser { + + private static final Map GATT_STATUS; + + static { + @SuppressLint("UseSparseArrays") Map aMap = new HashMap<>(); + aMap.put(0x00, "GATT_SUCCESS"); + aMap.put(0x01, "GATT_INVALID_HANDLE"); + aMap.put(0x02, "GATT_READ_NOT_PERMIT"); + aMap.put(0x03, "GATT_WRITE_NOT_PERMIT"); + aMap.put(0x04, "GATT_INVALID_PDU"); + aMap.put(0x05, "GATT_INSUF_AUTHENTICATION"); + aMap.put(0x06, "GATT_REQ_NOT_SUPPORTED"); + aMap.put(0x07, "GATT_INVALID_OFFSET"); + aMap.put(0x08, "GATT_INSUF_AUTHORIZATION"); + aMap.put(0x09, "GATT_PREPARE_Q_FULL"); + aMap.put(0x0a, "GATT_NOT_FOUND"); + aMap.put(0x0b, "GATT_NOT_LONG"); + aMap.put(0x0c, "GATT_INSUF_KEY_SIZE"); + aMap.put(0x0d, "GATT_INVALID_ATTR_LEN"); + aMap.put(0x0e, "GATT_ERR_UNLIKELY"); + aMap.put(0x0f, "GATT_INSUF_ENCRYPTION"); + aMap.put(0x10, "GATT_UNSUPPORT_GRP_TYPE"); + aMap.put(0x11, "GATT_INSUF_RESOURCE"); + + aMap.put(0x87, "GATT_ILLEGAL_PARAMETER"); + aMap.put(0x80, "GATT_NO_RESOURCES"); + aMap.put(0x81, "GATT_INTERNAL_ERROR"); + aMap.put(0x82, "GATT_WRONG_STATE"); + aMap.put(0x83, "GATT_DB_FULL"); + aMap.put(0x84, "GATT_BUSY"); + aMap.put(0x85, "GATT_ERROR"); + aMap.put(0x86, "GATT_CMD_STARTED"); + aMap.put(0x88, "GATT_PENDING"); + aMap.put(0x89, "GATT_AUTH_FAIL"); + aMap.put(0x8a, "GATT_MORE"); + aMap.put(0x8b, "GATT_INVALID_CFG"); + aMap.put(0x8c, "GATT_SERVICE_STARTED"); + aMap.put(0x00, "GATT_SUCCESS"); + aMap.put(0x8d, "GATT_ENCRYPED_NO_MITM"); + aMap.put(0x8e, "GATT_NOT_ENCRYPTED"); + aMap.put(0x8f, "GATT_CONGESTED"); + + aMap.put(0xfd, "GATT_CCC_CFG_ERR"); + aMap.put(0xfe, "GATT_PRC_IN_PROGRESS"); + aMap.put(0xff, "GATT_OUT_OF_RANGE"); + GATT_STATUS = Collections.unmodifiableMap(aMap); + } + + private GattStatusParser() { + // utility class + } + + public static String getGattCallbackStatusDescription(int status) { + final String description = GATT_STATUS.get(status); + return description == null ? "UNKNOWN" : description; + } +} diff --git a/rxandroidble/src/test/groovy/com/polidea/rxandroidble/exceptions/BleDisconnectedExceptionTest.groovy b/rxandroidble/src/test/groovy/com/polidea/rxandroidble/exceptions/BleDisconnectedExceptionTest.groovy index 29e1275c5..4ece8c3de 100644 --- a/rxandroidble/src/test/groovy/com/polidea/rxandroidble/exceptions/BleDisconnectedExceptionTest.groovy +++ b/rxandroidble/src/test/groovy/com/polidea/rxandroidble/exceptions/BleDisconnectedExceptionTest.groovy @@ -9,13 +9,25 @@ class BleDisconnectedExceptionTest extends Specification { BleDisconnectedException objectUnderTest - def "toString should include message"() { + def "toString should include message with unknown status"() { when: objectUnderTest = new BleDisconnectedException("myBluetoothAddress") then: assert objectUnderTest.toString() == - "com.polidea.rxandroidble.exceptions.BleDisconnectedException: Disconnected from myBluetoothAddress" + "com.polidea.rxandroidble.exceptions.BleDisconnectedException: Disconnected from myBluetoothAddress with status -1 (UNKNOWN)" + } + + def "toString should include message with status"() { + given: + def expectedStatus = 0x81 + + when: + objectUnderTest = new BleDisconnectedException("myBluetoothAddress", expectedStatus) + + then: + assert objectUnderTest.toString() == + "com.polidea.rxandroidble.exceptions.BleDisconnectedException: Disconnected from myBluetoothAddress with status $expectedStatus (GATT_INTERNAL_ERROR)" } } diff --git a/rxandroidble/src/test/groovy/com/polidea/rxandroidble/exceptions/BleGattExceptionTest.groovy b/rxandroidble/src/test/groovy/com/polidea/rxandroidble/exceptions/BleGattExceptionTest.groovy index ab0e62452..ad6a7e080 100644 --- a/rxandroidble/src/test/groovy/com/polidea/rxandroidble/exceptions/BleGattExceptionTest.groovy +++ b/rxandroidble/src/test/groovy/com/polidea/rxandroidble/exceptions/BleGattExceptionTest.groovy @@ -16,7 +16,7 @@ class BleGattExceptionTest extends Specification { then: assert objectUnderTest.toString() == - "com.polidea.rxandroidble.exceptions.BleGattException: GATT exception from MAC address null, status 10, " + + "com.polidea.rxandroidble.exceptions.BleGattException: GATT exception from MAC address null, status 10 (GATT_NOT_FOUND), " + "type BleGattOperation{description='CONNECTION_STATE'}. " + "(Look up status 0x0a here " + "https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-5.1.0_r1/stack/include/gatt_api.h)"