comboctl-android: Check that the adapter is available and enabled

Signed-off-by: Carlos Rafael Giani <crg7475@mailbox.org>
This commit is contained in:
Carlos Rafael Giani 2023-03-11 11:34:14 +01:00
parent 13666fd549
commit c1058ad113

View file

@ -7,8 +7,10 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import info.nightscout.comboctl.base.BluetoothAddress import info.nightscout.comboctl.base.BluetoothAddress
import info.nightscout.comboctl.base.BluetoothDevice import info.nightscout.comboctl.base.BluetoothDevice
import info.nightscout.comboctl.base.BluetoothNotEnabledException
import info.nightscout.comboctl.base.BluetoothException import info.nightscout.comboctl.base.BluetoothException
import info.nightscout.comboctl.base.BluetoothInterface import info.nightscout.comboctl.base.BluetoothInterface
import info.nightscout.comboctl.base.BluetoothNotAvailableException
import info.nightscout.comboctl.base.LogLevel import info.nightscout.comboctl.base.LogLevel
import info.nightscout.comboctl.base.Logger import info.nightscout.comboctl.base.Logger
import info.nightscout.comboctl.base.toBluetoothAddress import info.nightscout.comboctl.base.toBluetoothAddress
@ -35,7 +37,10 @@ private val logger = Logger.get("AndroidBluetoothInterface")
* instance is an ideal choice. * instance is an ideal choice.
*/ */
class AndroidBluetoothInterface(private val androidContext: Context) : BluetoothInterface { class AndroidBluetoothInterface(private val androidContext: Context) : BluetoothInterface {
private var bluetoothAdapter: SystemBluetoothAdapter? = null private var _bluetoothAdapter: SystemBluetoothAdapter? = null
private val bluetoothAdapter: SystemBluetoothAdapter
get() = _bluetoothAdapter ?: throw BluetoothNotAvailableException()
private var rfcommServerSocket: SystemBluetoothServerSocket? = null private var rfcommServerSocket: SystemBluetoothServerSocket? = null
private var discoveryStarted = false private var discoveryStarted = false
private var discoveryBroadcastReceiver: BroadcastReceiver? = null private var discoveryBroadcastReceiver: BroadcastReceiver? = null
@ -96,11 +101,16 @@ class AndroidBluetoothInterface(private val androidContext: Context) : Bluetooth
else @Suppress("DEPRECATION") getParcelableExtra(name) else @Suppress("DEPRECATION") getParcelableExtra(name)
fun setup() { fun setup() {
val bluetoothManager = androidContext.getSystemService(Context.BLUETOOTH_SERVICE) as SystemBluetoothManager val bluetoothManager = androidContext.getSystemService(Context.BLUETOOTH_SERVICE) as? SystemBluetoothManager
bluetoothAdapter = bluetoothManager.adapter _bluetoothAdapter = bluetoothManager?.adapter
checkIfBluetoothEnabledAndAvailable()
val bondedDevices = checkForConnectPermission(androidContext) { val bondedDevices = checkForConnectPermission(androidContext) {
bluetoothAdapter!!.bondedDevices // The "not enabled" check above is important, because in the disabled
// state, the adapter returns an empty list here. This would mislead
// the logic below into thinking that there are no bonded devices.
bluetoothAdapter.bondedDevices
} }
logger(LogLevel.DEBUG) { "Found ${bondedDevices.size} bonded Bluetooth device(s)" } logger(LogLevel.DEBUG) { "Found ${bondedDevices.size} bonded Bluetooth device(s)" }
@ -180,7 +190,7 @@ class AndroidBluetoothInterface(private val androidContext: Context) : Bluetooth
// necessary for correct function, just a detail for sake of completeness.) // necessary for correct function, just a detail for sake of completeness.)
logger(LogLevel.DEBUG) { "Setting up RFCOMM listener socket" } logger(LogLevel.DEBUG) { "Setting up RFCOMM listener socket" }
rfcommServerSocket = checkForConnectPermission(androidContext) { rfcommServerSocket = checkForConnectPermission(androidContext) {
bluetoothAdapter!!.listenUsingInsecureRfcommWithServiceRecord( bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(
sdpServiceName, sdpServiceName,
Constants.sdpSerialPortUUID Constants.sdpSerialPortUUID
) )
@ -203,7 +213,7 @@ class AndroidBluetoothInterface(private val androidContext: Context) : Bluetooth
logger(LogLevel.DEBUG) { "Closing accepted incoming RFCOMM socket" } logger(LogLevel.DEBUG) { "Closing accepted incoming RFCOMM socket" }
try { try {
socket.close() socket.close()
} catch (e: IOException) { } catch (_: IOException) {
} }
} }
} }
@ -274,20 +284,24 @@ class AndroidBluetoothInterface(private val androidContext: Context) : Bluetooth
stopDiscoveryInternal() stopDiscoveryInternal()
} }
override fun getDevice(deviceAddress: BluetoothAddress): BluetoothDevice = override fun getDevice(deviceAddress: BluetoothAddress): BluetoothDevice {
AndroidBluetoothDevice(androidContext, bluetoothAdapter!!, deviceAddress) checkIfBluetoothEnabledAndAvailable()
return AndroidBluetoothDevice(androidContext, bluetoothAdapter, deviceAddress)
}
override fun getAdapterFriendlyName() = override fun getAdapterFriendlyName() =
checkForConnectPermission(androidContext) { bluetoothAdapter!!.name } checkForConnectPermission(androidContext) { bluetoothAdapter.name }
?: throw BluetoothException("Could not get Bluetooth adapter friendly name") ?: throw BluetoothException("Could not get Bluetooth adapter friendly name")
override fun getPairedDeviceAddresses(): Set<BluetoothAddress> = override fun getPairedDeviceAddresses(): Set<BluetoothAddress> {
try { checkIfBluetoothEnabledAndAvailable()
return try {
deviceAddressLock.lock() deviceAddressLock.lock()
pairedDeviceAddresses.filter { pairedDeviceAddress -> deviceFilterCallback(pairedDeviceAddress) }.toSet() pairedDeviceAddresses.filter { pairedDeviceAddress -> deviceFilterCallback(pairedDeviceAddress) }.toSet()
} finally { } finally {
deviceAddressLock.unlock() deviceAddressLock.unlock()
} }
}
private fun stopDiscoveryInternal() { private fun stopDiscoveryInternal() {
// Close the server socket. This frees RFCOMM resources and ends // Close the server socket. This frees RFCOMM resources and ends
@ -332,6 +346,16 @@ class AndroidBluetoothInterface(private val androidContext: Context) : Bluetooth
} }
} }
private fun checkIfBluetoothEnabledAndAvailable() {
// Trying to access bluetoothAdapter here if it is currently null will
// automatically cause BluetoothNotAvailableException to be thrown,
// so that case is also covered implicitly by this code.
if (!bluetoothAdapter.isEnabled || (bluetoothAdapter.state != SystemBluetoothAdapter.STATE_ON)) {
logger(LogLevel.ERROR) { "Bluetooth is not enabled" }
throw BluetoothNotEnabledException()
}
}
private fun onAclConnected(intent: Intent, foundNewPairedDevice: (deviceAddress: BluetoothAddress) -> Unit) { private fun onAclConnected(intent: Intent, foundNewPairedDevice: (deviceAddress: BluetoothAddress) -> Unit) {
// Sanity check in case we get this notification for the // Sanity check in case we get this notification for the
// device already and need to avoid duplicate processing. // device already and need to avoid duplicate processing.