diff --git a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt index 464eddde..a32d203f 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt @@ -650,6 +650,11 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList connectionReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == AirPodsNotifications.AIRPODS_CONNECTION_DETECTED) { + if (::socket.isInitialized && socket.isConnected) { + Log.d(TAG, "Connection broadcast received but socket already connected, ignoring") + return + } + device = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { intent.getParcelableExtra("device", BluetoothDevice::class.java)!! } else { @@ -2360,6 +2365,13 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList @Suppress("ClassName") private object bluetoothReceiver : BroadcastReceiver() { + private fun sendDetected(context: Context?, name: String?, device: BluetoothDevice) { + val intent = Intent(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED) + intent.putExtra("name", name) + intent.putExtra("device", device) + context?.sendBroadcast(intent) + } + @SuppressLint("MissingPermission") override fun onReceive(context: Context?, intent: Intent) { val bluetoothDevice = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -2375,16 +2387,20 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList ?.getString("name", bluetoothDevice?.name) if (bluetoothDevice != null && !action.isNullOrEmpty()) { Log.d(TAG, "Received bluetooth connection broadcast: action=$action") + val uuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") if (BluetoothDevice.ACTION_ACL_CONNECTED == action) { - val uuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") - bluetoothDevice.fetchUuidsWithSdp() - if (bluetoothDevice.uuids != null) { - if (bluetoothDevice.uuids.contains(uuid)) { - val intent = Intent(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED) - intent.putExtra("name", name) - intent.putExtra("device", bluetoothDevice) - context?.sendBroadcast(intent) - } + if (bluetoothDevice.uuids?.contains(uuid) == true) { + sendDetected(context, name, bluetoothDevice) + } else { + bluetoothDevice.fetchUuidsWithSdp() + } + } else if ("android.bluetooth.device.action.UUID" == action) { + val savedMac = context?.getSharedPreferences("settings", MODE_PRIVATE) + ?.getString("mac_address", "") ?: "" + val matchedByMac = savedMac.isNotEmpty() && bluetoothDevice.address == savedMac + val matchedByUuid = bluetoothDevice.uuids?.contains(uuid) == true + if (matchedByUuid || matchedByMac) { + sendDetected(context, name, bluetoothDevice) } } } @@ -2642,7 +2658,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList @SuppressLint("MissingPermission", "UnspecifiedRegisterReceiverFlag") fun connectToSocket( - adapter: BluetoothAdapter, device: BluetoothDevice, manual: Boolean = false + adapter: BluetoothAdapter, device: BluetoothDevice, manual: Boolean = false, retriesLeft: Int = if (manual) 0 else 3 ) { Log.d(TAG, " Connecting to socket") val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") @@ -2701,6 +2717,15 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList Log.d( TAG, " Socket not connected, ${e.message}" ) + if (retriesLeft > 0) { + Log.d(TAG, "Retrying socket connect, $retriesLeft attempts left") + try { socket.close() } catch (_: Exception) {} + CoroutineScope(Dispatchers.IO).launch { + delay(500L) + connectToSocket(adapter, device, manual, retriesLeft - 1) + } + return@withTimeout + } if (manual) { sendToast( "Couldn't connect to socket: ${e.localizedMessage}" @@ -2715,6 +2740,15 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } if (!socket.isConnected) { Log.d(TAG, " Socket not connected") + if (retriesLeft > 0) { + Log.d(TAG, "Retrying socket connect after timeout, $retriesLeft attempts left") + try { socket.close() } catch (_: Exception) {} + CoroutineScope(Dispatchers.IO).launch { + delay(500L) + connectToSocket(adapter, device, manual, retriesLeft - 1) + } + return + } if (manual) { sendToast( "Couldn't connect to socket: timeout."