Skip to content

Commit 7904013

Browse files
authored
Throw exception when Android's BluetoothAdapter is not On (#137)
* Throw exception when Android's `BluetoothAdapter` is not `On` * Fix broken build; perform api dump * Extract adapter state check into function
1 parent d9ff2ce commit 7904013

4 files changed

Lines changed: 47 additions & 5 deletions

File tree

core/api/core.api

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ public final class com/juul/kable/AndroidScanner : com/juul/kable/Scanner {
3232
public fun getAdvertisements ()Lkotlinx/coroutines/flow/Flow;
3333
}
3434

35-
public class com/juul/kable/BluetoothLeException : java/lang/Exception {
35+
public final class com/juul/kable/BluetoothDisabledException : com/juul/kable/BluetoothException {
36+
public fun <init> ()V
37+
}
38+
39+
public class com/juul/kable/BluetoothException : java/lang/Exception {
3640
public fun <init> ()V
3741
}
3842

@@ -97,7 +101,7 @@ public final class com/juul/kable/DiscoveredService : com/juul/kable/Service {
97101
public fun toString ()Ljava/lang/String;
98102
}
99103

100-
public final class com/juul/kable/GattRequestRejectedException : com/juul/kable/BluetoothLeException {
104+
public final class com/juul/kable/GattRequestRejectedException : com/juul/kable/BluetoothException {
101105
public fun <init> ()V
102106
}
103107

core/src/androidMain/kotlin/Exceptions.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@file:JvmName("AndroidExceptionsKt") // Removes conflict with commonMain Exceptions.kt
2+
13
package com.juul.kable
24

35
import android.bluetooth.BluetoothDevice
@@ -18,4 +20,4 @@ public actual typealias IOException = java.io.IOException
1820
public class GattRequestRejectedException internal constructor(
1921
message: String? = null,
2022
cause: Throwable? = null,
21-
) : BluetoothLeException(message, cause)
23+
) : BluetoothException(message, cause)

core/src/androidMain/kotlin/Peripheral.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package com.juul.kable
22

3+
import android.bluetooth.BluetoothAdapter
34
import android.bluetooth.BluetoothAdapter.STATE_OFF
5+
import android.bluetooth.BluetoothAdapter.STATE_ON
6+
import android.bluetooth.BluetoothAdapter.STATE_TURNING_OFF
7+
import android.bluetooth.BluetoothAdapter.STATE_TURNING_ON
48
import android.bluetooth.BluetoothDevice
59
import android.bluetooth.BluetoothGatt
610
import android.bluetooth.BluetoothGattCharacteristic.PROPERTY_INDICATE
@@ -197,6 +201,7 @@ public class AndroidPeripheral internal constructor(
197201

198202
public override suspend fun connect() {
199203
check(job.isNotCancelled) { "Cannot connect, scope is cancelled for $this" }
204+
checkBluetoothAdapterState(expected = STATE_ON)
200205
connectJob.updateAndGet { it ?: connectAsync() }!!.await()
201206
}
202207

@@ -377,3 +382,26 @@ private val PlatformCharacteristic.supportsNotify: Boolean
377382

378383
private val PlatformCharacteristic.supportsIndicate: Boolean
379384
get() = bluetoothGattCharacteristic.properties and PROPERTY_INDICATE != 0
385+
386+
/**
387+
* Explicitly check the adapter state before connecting in order to respect system settings.
388+
* Android doesn't actually turn bluetooth off when the setting is disabled, so without this
389+
* check we're able to reconnect the device illegally.
390+
*/
391+
private fun checkBluetoothAdapterState(
392+
expected: Int,
393+
) {
394+
fun nameFor(value: Int) = when (value) {
395+
STATE_OFF -> "Off"
396+
STATE_ON -> "On"
397+
STATE_TURNING_OFF -> "TurningOff"
398+
STATE_TURNING_ON -> "TurningOn"
399+
else -> "Unknown"
400+
}
401+
val actual = BluetoothAdapter.getDefaultAdapter().state
402+
if (expected != actual) {
403+
val actualName = nameFor(actual)
404+
val expectedName = nameFor(expected)
405+
throw BluetoothDisabledException("Bluetooth adapter state is $actualName ($actual), but $expectedName ($expected) was required.")
406+
}
407+
}

core/src/commonMain/kotlin/Exceptions.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
package com.juul.kable
22

3-
/** Failure occurred with the underlying Bluetooth Low Energy system. */
4-
public open class BluetoothLeException internal constructor(
3+
/** Failure occurred with the underlying Bluetooth system. */
4+
public open class BluetoothException internal constructor(
55
message: String? = null,
66
cause: Throwable? = null,
77
) : Exception(message, cause)
88

9+
@Deprecated("Class has been renamed.", ReplaceWith("BluetoothException", "com.juul.kable.BluetoothException"))
10+
public typealias BluetoothLeException = BluetoothException
11+
12+
public class BluetoothDisabledException internal constructor(
13+
message: String? = null,
14+
cause: Throwable? = null,
15+
) : BluetoothException(message, cause)
16+
917
public expect open class IOException internal constructor(
1018
message: String? = null,
1119
cause: Throwable? = null,

0 commit comments

Comments
 (0)