Skip to content
This repository was archived by the owner on Aug 30, 2022. It is now read-only.

Commit c7743db

Browse files
committed
Throw GattClosed if Gatt is closed during I/O operation
Typical behavior of `Channel` is to throw a `ClosedReceiveChannelException` on invocation of `close` if a `receive` is suspended. This is unfortunately a bit counter intuitive and can produce misleading stacktraces. In order to be more explicit with our exception stacktraces related to `Channel` cancelation, using `receiveOrNull` and explicitly throwing `GattClosed` exception when receiving `null`. May need to revisit this behavior in the future after `receiveOrClosed` is implemented per Kotlin/kotlinx.coroutines#330. Also noteworthy is that `receiveOrNull` was undeprecated via Kotlin/kotlinx.coroutines#739.
1 parent ff5f4fb commit c7743db

File tree

2 files changed

+18
-10
lines changed

2 files changed

+18
-10
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ sealed class ConnectGattResult {
110110
<sup>1</sup> _Suspends until `STATE_CONNECTED` or non-`GATT_SUCCESS` is received._<br/>
111111
<sup>2</sup> _Suspends until `STATE_DISCONNECTED` or non-`GATT_SUCCESS` is received._<br/>
112112
<sup>3</sup> _Throws [`RemoteException`] if underlying [`BluetoothGatt`] call returns `false`._
113+
<sup>3</sup> _Throws [GattClosed] if [Gatt] is closed while method is executing._
113114

114115
### Details
115116

core/src/main/java/CoroutinesGatt.kt

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import kotlinx.coroutines.experimental.channels.BroadcastChannel
3131
import java.util.UUID
3232
import kotlin.coroutines.experimental.CoroutineContext
3333

34+
class GattClosed(message: String) : IllegalStateException(message)
35+
3436
class CoroutinesGatt(
3537
private val bluetoothGatt: BluetoothGatt,
3638
private val messenger: Messenger
@@ -79,6 +81,7 @@ class CoroutinesGatt(
7981

8082
/**
8183
* @throws [RemoteException] if underlying [BluetoothGatt.discoverServices] returns `false`.
84+
* @throws [GattClosed] if [Gatt] is closed while method is executing.
8285
*/
8386
override suspend fun discoverServices(): GattStatus {
8487
Able.debug { "discoverServices → send(DiscoverServices)" }
@@ -93,13 +96,14 @@ class CoroutinesGatt(
9396
}
9497

9598
Able.verbose { "discoverServices → Waiting for BluetoothGattCallback" }
96-
return messenger.callback.onServicesDiscovered.receive().also { status ->
99+
return messenger.callback.onServicesDiscovered.receiveOrNull()?.also { status ->
97100
Able.info { "discoverServices, status=${status.asGattStatusString()}" }
98-
}
101+
} ?: throw GattClosed("Gatt closed during discoverServices")
99102
}
100103

101104
/**
102105
* @throws [RemoteException] if underlying [BluetoothGatt.readCharacteristic] returns `false`.
106+
* @throws [GattClosed] if [Gatt] is closed while method is executing.
103107
*/
104108
override suspend fun readCharacteristic(
105109
characteristic: BluetoothGattCharacteristic
@@ -117,19 +121,20 @@ class CoroutinesGatt(
117121
}
118122

119123
Able.verbose { "readCharacteristic → Waiting for BluetoothGattCallback" }
120-
return messenger.callback.onCharacteristicRead.receive().also { (_, value, status) ->
124+
return messenger.callback.onCharacteristicRead.receiveOrNull()?.also { (_, value, status) ->
121125
Able.info {
122126
val bytesString = value.size.bytesString
123127
val statusString = status.asGattStatusString()
124128
"← readCharacteristic $uuid ($bytesString), status=$statusString"
125129
}
126-
}
130+
} ?: throw GattClosed("Gatt closed during readCharacteristic[uuid=$uuid]")
127131
}
128132

129133
/**
130134
* @param value applied to [characteristic] when characteristic is written.
131135
* @param writeType applied to [characteristic] when characteristic is written.
132136
* @throws [RemoteException] if underlying [BluetoothGatt.writeCharacteristic] returns `false`.
137+
* @throws [GattClosed] if [Gatt] is closed while method is executing.
133138
*/
134139
override suspend fun writeCharacteristic(
135140
characteristic: BluetoothGattCharacteristic,
@@ -149,19 +154,20 @@ class CoroutinesGatt(
149154
}
150155

151156
Able.verbose { "writeCharacteristic → Waiting for BluetoothGattCallback" }
152-
return messenger.callback.onCharacteristicWrite.receive().also { (_, status) ->
157+
return messenger.callback.onCharacteristicWrite.receiveOrNull()?.also { (_, status) ->
153158
Able.info {
154159
val bytesString = value.size.bytesString
155160
val typeString = writeType.asWriteTypeString()
156161
val statusString = status.asGattStatusString()
157162
"→ writeCharacteristic $uuid ($bytesString), type=$typeString, status=$statusString"
158163
}
159-
}
164+
} ?: throw GattClosed("Gatt closed during writeCharacteristic[uuid=$uuid]")
160165
}
161166

162167
/**
163168
* @param value applied to [descriptor] when descriptor is written.
164169
* @throws [RemoteException] if underlying [BluetoothGatt.writeDescriptor] returns `false`.
170+
* @throws [GattClosed] if [Gatt] is closed while method is executing.
165171
*/
166172
override suspend fun writeDescriptor(
167173
descriptor: BluetoothGattDescriptor, value: ByteArray
@@ -179,17 +185,18 @@ class CoroutinesGatt(
179185
}
180186

181187
Able.verbose { "writeDescriptor → Waiting for BluetoothGattCallback" }
182-
return messenger.callback.onDescriptorWrite.receive().also { (_, status) ->
188+
return messenger.callback.onDescriptorWrite.receiveOrNull()?.also { (_, status) ->
183189
Able.info {
184190
val bytesString = value.size.bytesString
185191
val statusString = status.asGattStatusString()
186192
"→ writeDescriptor $uuid ($bytesString), status=$statusString"
187193
}
188-
}
194+
} ?: throw GattClosed("Gatt closed during writeDescriptor[uuid=$uuid]")
189195
}
190196

191197
/**
192198
* @throws [RemoteException] if underlying [BluetoothGatt.requestMtu] returns `false`.
199+
* @throws [GattClosed] if [Gatt] is closed while method is executing.
193200
*/
194201
override suspend fun requestMtu(mtu: Int): OnMtuChanged {
195202
Able.debug { "requestMtu → send(RequestMtu[mtu=$mtu])" }
@@ -204,9 +211,9 @@ class CoroutinesGatt(
204211
}
205212

206213
Able.verbose { "requestMtu → Waiting for BluetoothGattCallback" }
207-
return messenger.callback.onMtuChanged.receive().also { (mtu, status) ->
214+
return messenger.callback.onMtuChanged.receiveOrNull()?.also { (mtu, status) ->
208215
Able.info { "requestMtu $mtu, status=${status.asGattStatusString()}" }
209-
}
216+
} ?: throw GattClosed("Gatt closed during requestMtu[mtu=$mtu]")
210217
}
211218

212219
override fun setCharacteristicNotification(

0 commit comments

Comments
 (0)