From dc852b97baac032f12c15cf0b5fe45d57693a4ab Mon Sep 17 00:00:00 2001 From: andrekir Date: Mon, 31 Jan 2022 21:19:54 -0300 Subject: [PATCH] add bluetooth_connect permission checks --- .../java/com/geeksville/mesh/MainActivity.kt | 33 ++++++++----------- .../mesh/android/ContextServices.kt | 18 ++++++++++ .../geeksville/mesh/ui/SettingsFragment.kt | 21 +++++++----- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index 02677e20..21aa626f 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -43,11 +43,7 @@ import com.geeksville.android.GeeksvilleApplication import com.geeksville.android.Logging import com.geeksville.android.ServiceClient import com.geeksville.concurrent.handledLaunch -import com.geeksville.mesh.android.getLocationPermissions -import com.geeksville.mesh.android.getBackgroundPermissions -import com.geeksville.mesh.android.getCameraPermissions -import com.geeksville.mesh.android.getMissingPermissions -import com.geeksville.mesh.android.getScanPermissions +import com.geeksville.mesh.android.* import com.geeksville.mesh.database.entity.Packet import com.geeksville.mesh.databinding.ActivityMainBinding import com.geeksville.mesh.model.ChannelSet @@ -249,15 +245,8 @@ class MainActivity : AppCompatActivity(), Logging, */ private fun updateBluetoothEnabled() { var enabled = false // assume failure - val requiredPerms: MutableList = mutableListOf() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - requiredPerms.add(Manifest.permission.BLUETOOTH_CONNECT) - } else { - requiredPerms.add(Manifest.permission.BLUETOOTH) - } - - if (getMissingPermissions(requiredPerms).isEmpty()) { + if (hasConnectPermission()) { /// ask the adapter if we have access bluetoothAdapter?.apply { enabled = isEnabled @@ -309,6 +298,7 @@ class MainActivity : AppCompatActivity(), Logging, /** * @return a localized string warning user about missing permissions. Or null if everything is find */ + @SuppressLint("InlinedApi") fun getMissingMessage( missingPerms: List = getMinimumPermissions() ): String? { @@ -338,7 +328,7 @@ class MainActivity : AppCompatActivity(), Logging, } /** Possibly prompt user to grant permissions - * @param shouldShowDialog usually true, but in cases where we've already shown a dialog elsewhere we skip it. + * @param shouldShowDialog usually false in cases where we've already shown a dialog elsewhere we skip it. * * @return true if we already have the needed permissions */ @@ -640,7 +630,7 @@ class MainActivity : AppCompatActivity(), Logging, /** * Dispatch incoming result to the correct fragment. */ - @SuppressLint("InlinedApi") + @SuppressLint("InlinedApi", "MissingPermission") override fun onActivityResult( requestCode: Int, resultCode: Int, @@ -1075,18 +1065,21 @@ class MainActivity : AppCompatActivity(), Logging, super.onStop() } + @SuppressLint("MissingPermission") override fun onStart() { super.onStart() // Ask to start bluetooth if no USB devices are visible val hasUSB = SerialInterface.findDrivers(this).isNotEmpty() if (!isInTestLab && !hasUSB) { - bluetoothAdapter?.let { - if (!it.isEnabled) { - val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) - startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT) + if (hasConnectPermission()) { + bluetoothAdapter?.let { + if (!it.isEnabled) { + val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) + startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT) + } } - } + } else requestPermission() } try { diff --git a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt index 71e22b0b..c3926e77 100644 --- a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt +++ b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt @@ -29,6 +29,24 @@ fun Context.getMissingPermissions(perms: List) = perms.filter { ) != PackageManager.PERMISSION_GRANTED } +/** + * Bluetooth connect permissions (or empty if we already have what we need) + */ +fun Context.getConnectPermissions(): List { + val perms = mutableListOf() + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + perms.add(Manifest.permission.BLUETOOTH_CONNECT) + } else { + perms.add(Manifest.permission.BLUETOOTH) + } + + return getMissingPermissions(perms) +} + +/** @return true if the user already has Bluetooth connect permission */ +fun Context.hasConnectPermission() = getConnectPermissions().isEmpty() + /** * Bluetooth scan/discovery permissions (or empty if we already have what we need) */ diff --git a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt index 9a858122..b6f6a565 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -33,11 +33,7 @@ import com.geeksville.android.isGooglePlayAvailable import com.geeksville.mesh.MainActivity import com.geeksville.mesh.R import com.geeksville.mesh.RadioConfigProtos -import com.geeksville.mesh.android.bluetoothManager -import com.geeksville.mesh.android.hasScanPermission -import com.geeksville.mesh.android.hasLocationPermission -import com.geeksville.mesh.android.hasBackgroundPermission -import com.geeksville.mesh.android.usbManager +import com.geeksville.mesh.android.* import com.geeksville.mesh.databinding.SettingsFragmentBinding import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.service.* @@ -68,6 +64,7 @@ fun changeDeviceSelection(context: MainActivity, newAddr: String?) { } /// Show the UI asking the user to bond with a device, call changeSelection() if/when bonding completes +@SuppressLint("MissingPermission") private fun requestBonding( activity: MainActivity, device: BluetoothDevice, @@ -102,7 +99,11 @@ private fun requestBonding( activity.registerReceiver(bondChangedReceiver, filter) // We ignore missing BT adapters, because it lets us run on the emulator - device.createBond() + try { + device.createBond() + } catch (ex: Throwable) { + SLogging.warn("Failed creating Bluetooth bond: ${ex.message}") + } } class BTScanModel(app: Application) : AndroidViewModel(app), Logging { @@ -180,13 +181,14 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging { // For each device that appears in our scan, ask for its GATT, when the gatt arrives, // check if it is an eligable device and store it in our list of candidates // if that device later disconnects remove it as a candidate + @SuppressLint("MissingPermission") override fun onScanResult(callbackType: Int, result: ScanResult) { if ((result.device.name?.startsWith("Mesh") == true)) { val addr = result.device.address val fullAddr = "x$addr" // full address with the bluetooth prefix added // prevent logspam because weill get get lots of redundant scan results - val isBonded = result.device.bondState == BluetoothDevice.BOND_BONDED + val isBonded = result.device.bondState == BOND_BONDED val oldDevs = devices.value!! val oldEntry = oldDevs[fullAddr] if (oldEntry == null || oldEntry.bonded != isBonded) { // Don't spam the GUI with endless updates for non changing nodes @@ -222,6 +224,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging { devices.value = oldDevs // trigger gui updates } + @SuppressLint("MissingPermission") fun stopScan() { if (scanner != null) { debug("stopping scan") @@ -296,6 +299,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging { } } + @SuppressLint("MissingPermission") fun startScan() { /// The following call might return null if the user doesn't have bluetooth access permissions val bluetoothLeScanner: BluetoothLeScanner? = bluetoothAdapter?.bluetoothLeScanner @@ -745,6 +749,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { } } + @SuppressLint("MissingPermission") private fun updateDevicesButtons(devices: MutableMap?) { // Remove the old radio buttons and repopulate binding.deviceRadioGroup.removeAllViews() @@ -767,7 +772,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { // and before use val bleAddr = scanModel.selectedBluetooth - if (bleAddr != null && adapter != null) { + if (bleAddr != null && adapter != null && myActivity.hasConnectPermission()) { val bDevice = adapter.getRemoteDevice(bleAddr) if (bDevice.name != null) { // ignore nodes that node have a name, that means we've lost them since they appeared