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

Commit

Permalink
Organize exceptions thrown (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
twyatt authored Sep 15, 2020
1 parent 3f7780c commit 3a8fa68
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 62 deletions.
11 changes: 6 additions & 5 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,6 @@ public abstract interface class com/juul/able/gatt/GattConnection {
public abstract fun getOnConnectionStateChange ()Lkotlinx/coroutines/flow/Flow;
}

public final class com/juul/able/gatt/GattErrorStatusException : java/io/IOException {
public final fun getEvent ()Lcom/juul/able/gatt/OnConnectionStateChange;
}

public abstract interface class com/juul/able/gatt/GattIo {
public abstract fun discoverServices (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getOnCharacteristicChanged ()Lkotlinx/coroutines/flow/Flow;
Expand All @@ -130,6 +126,11 @@ public final class com/juul/able/gatt/GattIoKt {
public static final fun writeCharacteristic (Lcom/juul/able/gatt/GattIo;Landroid/bluetooth/BluetoothGattCharacteristic;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/juul/able/gatt/GattStatusException : java/io/IOException {
public fun <init> (ILjava/lang/String;)V
public fun <init> (Ljava/lang/String;)V
}

public abstract interface class com/juul/able/gatt/HasGattStatus {
public abstract fun getStatus ()I
}
Expand Down Expand Up @@ -242,7 +243,7 @@ public final class com/juul/able/gatt/OnReadRemoteRssi : com/juul/able/gatt/HasG
public fun toString ()Ljava/lang/String;
}

public final class com/juul/able/gatt/OutOfOrderGattCallbackException : java/io/IOException {
public final class com/juul/able/gatt/OutOfOrderGattCallbackException : java/lang/IllegalStateException {
}

public final class com/juul/able/logger/AndroidLogger : com/juul/able/logger/Logger {
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/device/CoroutinesDevice.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import com.juul.able.gatt.ConnectionLostException
import com.juul.able.gatt.CoroutinesGatt
import com.juul.able.gatt.GattCallback
import com.juul.able.gatt.GattConnection
import com.juul.able.gatt.GattErrorStatusException
import com.juul.able.gatt.GattStatusException
import com.juul.able.gatt.asGattConnectionStateString
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.first
Expand Down Expand Up @@ -60,7 +60,7 @@ internal class CoroutinesDevice(
onConnectionStateChange
.onEach { event ->
Able.verbose { "← Device $device received $event while waiting for connection" }
if (event.status != GATT_SUCCESS) throw GattErrorStatusException(event)
if (event.status != GATT_SUCCESS) throw GattStatusException(event.toString())
if (event.newState == STATE_DISCONNECTED) throw ConnectionLostException()
}
.first { (_, newState) -> newState == STATE_CONNECTED }
Expand Down
7 changes: 4 additions & 3 deletions core/src/main/java/gatt/CoroutinesGatt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothProfile.STATE_DISCONNECTED
import android.os.RemoteException
import com.juul.able.Able
import java.io.IOException
import java.util.UUID
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.ExecutorCoroutineDispatcher
Expand All @@ -24,7 +23,9 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext

class OutOfOrderGattCallbackException internal constructor(message: String) : IOException(message)
class OutOfOrderGattCallbackException internal constructor(
message: String
) : IllegalStateException(message)

class CoroutinesGatt internal constructor(
private val bluetoothGatt: BluetoothGatt,
Expand Down Expand Up @@ -143,7 +144,7 @@ class CoroutinesGatt internal constructor(
}
val (status, newState) = event
if (status != GATT_SUCCESS && newState != STATE_DISCONNECTED)
throw GattErrorStatusException(event)
throw GattStatusException(event.toString())
}
.first { (_, newState) -> newState == STATE_DISCONNECTED }
.also { (_, newState) ->
Expand Down
9 changes: 6 additions & 3 deletions core/src/main/java/gatt/GattConnection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ interface GattConnection {
suspend fun disconnect(): Unit
}

class GattErrorStatusException internal constructor(
val event: OnConnectionStateChange
) : IOException("Received $event")
class GattStatusException(message: String?) : IOException(message) {
constructor(
status: GattStatus,
prefix: String
) : this("$prefix failed with status ${status.asGattStatusString()}")
}

class ConnectionLostException internal constructor(
message: String? = null,
Expand Down
6 changes: 3 additions & 3 deletions core/src/test/java/device/CoroutinesDeviceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import com.juul.able.device.CoroutinesDevice
import com.juul.able.gatt.ConnectionLostException
import com.juul.able.gatt.GATT_CONN_CANCEL
import com.juul.able.gatt.GattCallback
import com.juul.able.gatt.GattErrorStatusException
import com.juul.able.gatt.GattStatusException
import com.juul.able.gatt.OnConnectionStateChange
import com.juul.able.test.logger.ConsoleLoggerTestRule
import io.mockk.every
Expand Down Expand Up @@ -64,8 +64,8 @@ class CoroutinesDeviceTest {
val failure = device.connectGatt(mockk()) as Failure.Connection

assertEquals(
expected = OnConnectionStateChange(GATT_CONN_CANCEL, STATE_CONNECTED),
actual = (failure.cause as GattErrorStatusException).event
expected = OnConnectionStateChange(GATT_CONN_CANCEL, STATE_CONNECTED).toString(),
actual = (failure.cause as GattStatusException).message
)
verify(exactly = 1) { bluetoothGatt.close() }
}
Expand Down
2 changes: 1 addition & 1 deletion keep-alive/api/keep-alive.api
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public final class com/juul/able/keepalive/KeepAliveGattKt {
public static final fun keepAliveGatt (Lkotlinx/coroutines/CoroutineScope;Landroid/content/Context;Landroid/bluetooth/BluetoothDevice;J)Lcom/juul/able/keepalive/KeepAliveGatt;
}

public final class com/juul/able/keepalive/NotReadyException : java/lang/IllegalStateException {
public final class com/juul/able/keepalive/NotReadyException : java/io/IOException {
}

public abstract class com/juul/able/keepalive/State {
Expand Down
5 changes: 4 additions & 1 deletion keep-alive/src/main/java/KeepAliveGatt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.juul.able.gatt.OnDescriptorWrite
import com.juul.able.gatt.OnMtuChanged
import com.juul.able.gatt.OnReadRemoteRssi
import com.juul.able.gatt.WriteType
import java.io.IOException
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.coroutines.CoroutineContext
Expand Down Expand Up @@ -53,7 +54,9 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull

class NotReadyException internal constructor(message: String) : IllegalStateException(message)
class NotReadyException internal constructor(
message: String
) : IOException(message)

fun CoroutineScope.keepAliveGatt(
androidContext: Context,
Expand Down
63 changes: 35 additions & 28 deletions throw/src/main/java/GattOrThrow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,64 @@ package com.juul.able.throwable
import android.bluetooth.BluetoothGatt.GATT_SUCCESS
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.os.RemoteException
import com.juul.able.gatt.GattIo
import com.juul.able.gatt.GattStatus
import com.juul.able.gatt.GattStatusException
import com.juul.able.gatt.WriteType

/**
* @throws [IllegalStateException] if [GattIo.discoverServices] call does not return [GATT_SUCCESS].
* @throws [GattStatusException] if [GattIo.discoverServices] call does not return [GATT_SUCCESS].
*/
suspend fun GattIo.discoverServicesOrThrow() {
discoverServices()
.also { status ->
check(status == GATT_SUCCESS) {
"Service discovery failed with gatt status $status."
}
}
discoverServices().also { status ->
check(status) { "Discover services" }
}
}

/**
* @throws [IllegalStateException] if [GattIo.readRemoteRssi] call does not return [GATT_SUCCESS].
* @throws [GattStatusException] if [GattIo.readRemoteRssi] call does not return [GATT_SUCCESS].
*/
suspend fun GattIo.readRemoteRssiOrThrow(): Int {
return readRemoteRssi()
.also { (_, status) ->
check(status == GATT_SUCCESS) {
"Service discovery failed with gatt status $status."
}
}.rssi
check(status) { "Read remote RSSI" }
}
.rssi
}

/**
* @throws [IllegalStateException] if [GattIo.readCharacteristic] call does not return [GATT_SUCCESS].
* @throws [GattStatusException] if [GattIo.readCharacteristic] call does not return [GATT_SUCCESS].
*/
suspend fun GattIo.readCharacteristicOrThrow(
characteristic: BluetoothGattCharacteristic
): ByteArray {
return readCharacteristic(characteristic)
.also { (_, _, status) ->
check(status == GATT_SUCCESS) {
"Reading characteristic ${characteristic.uuid} failed with status $status."
}
}.value
check(status) { "Read characteristic ${characteristic.uuid}" }
}
.value
}

/**
* @throws [IllegalStateException] if [GattIo.setCharacteristicNotification] call returns `false`.
* @throws [RemoteException] if [GattIo.setCharacteristicNotification] call returns `false`.
*/
fun GattIo.setCharacteristicNotificationOrThrow(
characteristic: BluetoothGattCharacteristic,
enable: Boolean
) {
setCharacteristicNotification(characteristic, enable)
.also {
check(it) { "Setting characteristic notifications to $enable failed." }
.also { successful ->
if (!successful) {
val uuid = characteristic.uuid
val message = "Setting characteristic $uuid notifications to $enable failed"
throw RemoteException(message)
}
}
}

/**
* @throws [IllegalStateException] if [GattIo.writeCharacteristic] call does not return [GATT_SUCCESS].
* @throws [GattStatusException] if [GattIo.writeCharacteristic] call does not return [GATT_SUCCESS].
*/
suspend fun GattIo.writeCharacteristicOrThrow(
characteristic: BluetoothGattCharacteristic,
Expand All @@ -71,33 +73,38 @@ suspend fun GattIo.writeCharacteristicOrThrow(
) {
writeCharacteristic(characteristic, value, writeType)
.also { (_, status) ->
check(status == GATT_SUCCESS) {
"Writing characteristic ${characteristic.uuid} failed with status $status."
}
check(status) { "Write characteristic ${characteristic.uuid}" }
}
}

/**
* @throws [IllegalStateException] if [GattIo.writeDescriptor] call does not return [GATT_SUCCESS].
* @throws [GattStatusException] if [GattIo.writeDescriptor] call does not return [GATT_SUCCESS].
*/
suspend fun GattIo.writeDescriptorOrThrow(
descriptor: BluetoothGattDescriptor,
value: ByteArray
) {
writeDescriptor(descriptor, value)
.also { (_, status) ->
check(status == GATT_SUCCESS) { "Descriptor write failed with status $status." }
check(status) { "Write descriptor ${descriptor.uuid}" }
}
}

/**
* @throws [IllegalStateException] if [GattIo.requestMtu] call does not return [GATT_SUCCESS].
* @throws [GattStatusException] if [GattIo.requestMtu] call does not return [GATT_SUCCESS].
*/
suspend fun GattIo.requestMtuOrThrow(
mtu: Int
): Int {
return requestMtu(mtu)
.also { (_, status) ->
check(status == GATT_SUCCESS) { "Request MTU of $mtu failed with status $status." }
check(status) { "Request MTU of $mtu" }
}.mtu
}

private fun check(status: GattStatus, lazyPrefix: () -> Any) {
if (status != GATT_SUCCESS) {
val prefix = lazyPrefix.invoke()
throw GattStatusException(status, prefix.toString())
}
}
4 changes: 2 additions & 2 deletions throw/src/main/java/android/BluetoothDeviceOrThrow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import com.juul.able.device.ConnectGattResult.Failure
import com.juul.able.device.ConnectGattResult.Success
import com.juul.able.gatt.ConnectionLostException
import com.juul.able.gatt.Gatt
import com.juul.able.gatt.GattErrorStatusException
import com.juul.able.gatt.GattStatusException

/**
* @throws RemoteException if underlying [BluetoothDevice.connectGatt] returns `null`.
* @throws GattErrorStatusException if non-[GATT_SUCCESS] status is received during connection process.
* @throws GattStatusException if non-[GATT_SUCCESS] status is received during connection process.
* @throws ConnectionLostException if [STATE_DISCONNECTED] is received during connection process.
*/
suspend fun BluetoothDevice.connectGattOrThrow(
Expand Down
Loading

0 comments on commit 3a8fa68

Please sign in to comment.