mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-06-01 13:59:17 +00:00
android: listen to UUID broadcasts
This commit is contained in:
@@ -52,13 +52,13 @@ enum class ATTCCCDHandles(val value: Int) {
|
|||||||
|
|
||||||
class ATTManager(private val adapter: BluetoothAdapter, private val device: BluetoothDevice) {
|
class ATTManager(private val adapter: BluetoothAdapter, private val device: BluetoothDevice) {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "ATTManager"
|
|
||||||
|
|
||||||
private const val OPCODE_READ_REQUEST: Byte = 0x0A
|
private const val OPCODE_READ_REQUEST: Byte = 0x0A
|
||||||
private const val OPCODE_WRITE_REQUEST: Byte = 0x12
|
private const val OPCODE_WRITE_REQUEST: Byte = 0x12
|
||||||
private const val OPCODE_HANDLE_VALUE_NTF: Byte = 0x1B
|
private const val OPCODE_HANDLE_VALUE_NTF: Byte = 0x1B
|
||||||
}
|
}
|
||||||
|
private val id = System.identityHashCode(this)
|
||||||
|
@Suppress("PrivatePropertyName")
|
||||||
|
private val TAG = "ATTManager[$id]"
|
||||||
var socket: BluetoothSocket? = null
|
var socket: BluetoothSocket? = null
|
||||||
private var input: InputStream? = null
|
private var input: InputStream? = null
|
||||||
private var output: OutputStream? = null
|
private var output: OutputStream? = null
|
||||||
@@ -72,16 +72,25 @@ class ATTManager(private val adapter: BluetoothAdapter, private val device: Blue
|
|||||||
fun connect() {
|
fun connect() {
|
||||||
val uuid = ParcelUuid.fromString("00000000-0000-0000-0000-000000000000")
|
val uuid = ParcelUuid.fromString("00000000-0000-0000-0000-000000000000")
|
||||||
|
|
||||||
try {
|
if (socket == null) {
|
||||||
socket = createBluetoothSocket(adapter, device, uuid)
|
Log.d(TAG, "Socket doesn't exist, creating")
|
||||||
} catch (e: Exception) {
|
try {
|
||||||
Log.w(TAG, "Failed to create socket")
|
socket = createBluetoothSocket(adapter, device, uuid)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Failed to create socket")
|
||||||
|
e.printStackTrace()
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
if (socket?.isConnected != true) {
|
||||||
socket!!.connect()
|
Log.d(TAG, "Connection to socket")
|
||||||
} catch (e: Exception) {
|
try {
|
||||||
Log.w(TAG, "ATT socket failed to connect")
|
socket!!.connect()
|
||||||
return
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "ATT socket failed to connect")
|
||||||
|
e.printStackTrace()
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
input = socket!!.inputStream
|
input = socket!!.inputStream
|
||||||
output = socket!!.outputStream
|
output = socket!!.outputStream
|
||||||
@@ -113,6 +122,10 @@ class ATTManager(private val adapter: BluetoothAdapter, private val device: Blue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (output != null) {
|
||||||
|
Log.d(TAG, "sending read req for hearing aid declaration")
|
||||||
|
output?.write(byteArrayOf(0x0A, 0x29, 0x00))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disconnect() {
|
fun disconnect() {
|
||||||
@@ -216,11 +229,11 @@ class ATTManager(private val adapter: BluetoothAdapter, private val device: Blue
|
|||||||
)
|
)
|
||||||
|
|
||||||
val constructors = BluetoothSocket::class.java.declaredConstructors
|
val constructors = BluetoothSocket::class.java.declaredConstructors
|
||||||
Log.d("ATTManager", "BluetoothSocket has ${constructors.size} constructors:")
|
Log.d(TAG, "BluetoothSocket has ${constructors.size} constructors:")
|
||||||
|
|
||||||
constructors.forEachIndexed { index, constructor ->
|
constructors.forEachIndexed { index, constructor ->
|
||||||
val params = constructor.parameterTypes.joinToString(", ") { it.simpleName }
|
val params = constructor.parameterTypes.joinToString(", ") { it.simpleName }
|
||||||
Log.d("ATTManager", "Constructor $index: ($params)")
|
Log.d(TAG, "Constructor $index: ($params)")
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastException: Exception? = null
|
var lastException: Exception? = null
|
||||||
@@ -228,7 +241,7 @@ class ATTManager(private val adapter: BluetoothAdapter, private val device: Blue
|
|||||||
|
|
||||||
for ((index, params) in constructorSpecs.withIndex()) {
|
for ((index, params) in constructorSpecs.withIndex()) {
|
||||||
try {
|
try {
|
||||||
Log.d("ATTManager", "Trying constructor signature #${index + 1}")
|
Log.d(TAG, "Trying constructor signature #${index + 1}")
|
||||||
attemptedConstructors++
|
attemptedConstructors++
|
||||||
|
|
||||||
val paramTypes = params.map { it::class.javaPrimitiveType ?: it::class.java }.toTypedArray()
|
val paramTypes = params.map { it::class.javaPrimitiveType ?: it::class.java }.toTypedArray()
|
||||||
@@ -237,13 +250,13 @@ class ATTManager(private val adapter: BluetoothAdapter, private val device: Blue
|
|||||||
return constructor.newInstance(*params) as BluetoothSocket
|
return constructor.newInstance(*params) as BluetoothSocket
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("ATTManager", "Constructor signature #${index + 1} failed: ${e.message}")
|
Log.e(TAG, "Constructor signature #${index + 1} failed: ${e.message}")
|
||||||
lastException = e
|
lastException = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val errorMessage = "Failed to create BluetoothSocket after trying $attemptedConstructors constructor signatures"
|
val errorMessage = "Failed to create BluetoothSocket after trying $attemptedConstructors constructor signatures"
|
||||||
Log.e("ATTManager", errorMessage)
|
Log.e(TAG, errorMessage)
|
||||||
throw lastException ?: IllegalStateException(errorMessage)
|
throw lastException ?: IllegalStateException(errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -530,7 +530,9 @@ class AirPodsViewModel(
|
|||||||
}
|
}
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
service.attManager?.connect()
|
if (service.attManager?.socket?.isConnected != true) {
|
||||||
|
service.attManager?.connect()
|
||||||
|
}
|
||||||
while (service.attManager?.socket?.isConnected != true) {
|
while (service.attManager?.socket?.isConnected != true) {
|
||||||
delay(250)
|
delay(250)
|
||||||
}
|
}
|
||||||
@@ -545,21 +547,28 @@ class AirPodsViewModel(
|
|||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val loudSoundReduction =
|
val loudSoundReduction =
|
||||||
runCatching { service.attManager?.read(ATTHandles.LOUD_SOUND_REDUCTION) }.getOrNull()
|
runCatching { service.attManager?.read(ATTHandles.LOUD_SOUND_REDUCTION) }.getOrNull()
|
||||||
|
val loudSoundReductionEnabled = loudSoundReduction?.size?.let {
|
||||||
|
if (it > 0) {
|
||||||
|
loudSoundReduction[0].toInt() == 1
|
||||||
|
} else false
|
||||||
|
}
|
||||||
val transparencyData =
|
val transparencyData =
|
||||||
runCatching { service.attManager?.read(ATTHandles.TRANSPARENCY) }.getOrNull()?: byteArrayOf()
|
runCatching { service.attManager?.read(ATTHandles.TRANSPARENCY) }.getOrNull()?: byteArrayOf()
|
||||||
val hearingAid =
|
val hearingAidData =
|
||||||
runCatching { service.attManager?.read(ATTHandles.HEARING_AID) }.getOrNull()?: byteArrayOf()
|
runCatching { service.attManager?.read(ATTHandles.HEARING_AID) }.getOrNull()?: byteArrayOf()
|
||||||
_uiState.value = _uiState.value.copy(
|
_uiState.value = _uiState.value.copy(
|
||||||
loudSoundReductionEnabled = loudSoundReduction?.get(0)?.toInt() == 0x01,
|
loudSoundReductionEnabled = loudSoundReductionEnabled == true,
|
||||||
transparencyData = transparencyData,
|
transparencyData = transparencyData,
|
||||||
hearingAidData = hearingAid
|
hearingAidData = hearingAidData
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observeATT() {
|
fun observeATT() {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
service.attManager?.connect()
|
if (service.attManager?.socket?.isConnected != true) {
|
||||||
|
service.attManager?.connect()
|
||||||
|
}
|
||||||
while (service.attManager?.socket?.isConnected != true) {
|
while (service.attManager?.socket?.isConnected != true) {
|
||||||
delay(1000)
|
delay(1000)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1019,7 +1019,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
)
|
)
|
||||||
// Store in SharedPreferences
|
// Store in SharedPreferences
|
||||||
sharedPreferences.edit {
|
sharedPreferences.edit {
|
||||||
putString("airpods_name", deviceInformation.name)
|
putString("name", deviceInformation.name)
|
||||||
putString("airpods_model_number", deviceInformation.modelNumber)
|
putString("airpods_model_number", deviceInformation.modelNumber)
|
||||||
putString("airpods_manufacturer", deviceInformation.manufacturer)
|
putString("airpods_manufacturer", deviceInformation.manufacturer)
|
||||||
putString("airpods_serial_number", deviceInformation.serialNumber)
|
putString("airpods_serial_number", deviceInformation.serialNumber)
|
||||||
@@ -2388,16 +2388,27 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
?.getString("name", bluetoothDevice?.name)
|
?.getString("name", bluetoothDevice?.name)
|
||||||
if (bluetoothDevice != null && !action.isNullOrEmpty()) {
|
if (bluetoothDevice != null && !action.isNullOrEmpty()) {
|
||||||
Log.d(TAG, "Received bluetooth connection broadcast: action=$action")
|
Log.d(TAG, "Received bluetooth connection broadcast: action=$action")
|
||||||
|
val uuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a")
|
||||||
|
|
||||||
if (BluetoothDevice.ACTION_ACL_CONNECTED == action) {
|
if (BluetoothDevice.ACTION_ACL_CONNECTED == action) {
|
||||||
val uuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a")
|
if (bluetoothDevice.uuids?.contains(uuid) == true) {
|
||||||
bluetoothDevice.fetchUuidsWithSdp()
|
val intent = Intent(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED)
|
||||||
if (bluetoothDevice.uuids != null) {
|
intent.putExtra("name", name)
|
||||||
if (bluetoothDevice.uuids.contains(uuid)) {
|
intent.putExtra("device", bluetoothDevice)
|
||||||
val intent = Intent(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED)
|
context?.sendBroadcast(intent)
|
||||||
intent.putExtra("name", name)
|
} else {
|
||||||
intent.putExtra("device", bluetoothDevice)
|
bluetoothDevice.fetchUuidsWithSdp()
|
||||||
context?.sendBroadcast(intent)
|
}
|
||||||
}
|
} 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) {
|
||||||
|
val intent = Intent(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED)
|
||||||
|
intent.putExtra("name", name)
|
||||||
|
intent.putExtra("device", bluetoothDevice)
|
||||||
|
context?.sendBroadcast(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2421,6 +2432,32 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
takeOver("music", manualTakeOverAfterReversed = true)
|
takeOver("music", manualTakeOverAfterReversed = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val bluetoothManager = getSystemService(BluetoothManager::class.java)
|
||||||
|
val bluetoothAdapter = bluetoothManager.adapter
|
||||||
|
|
||||||
|
bluetoothAdapter?.bondedDevices?.forEach { device ->
|
||||||
|
device.fetchUuidsWithSdp()
|
||||||
|
|
||||||
|
if (device.uuids != null) {
|
||||||
|
// Check for the AirPods service UUID
|
||||||
|
val uuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a")
|
||||||
|
|
||||||
|
if (device.uuids.contains(uuid)) {
|
||||||
|
Log.d(TAG, "Found AirPods device: ${device.name} (${device.address})")
|
||||||
|
|
||||||
|
// Connect or do whatever you need
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
connectToSocket(bluetoothAdapter, device)
|
||||||
|
}
|
||||||
|
setMetadatas(device)
|
||||||
|
macAddress = device.address
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putString("mac_address", macAddress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2682,8 +2719,10 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
BluetoothConnectionManager.setCurrentConnection(socket, device)
|
BluetoothConnectionManager.setCurrentConnection(socket, device)
|
||||||
val xposedRemotePref = XposedRemotePrefProvider.create()
|
val xposedRemotePref = XposedRemotePrefProvider.create()
|
||||||
if (xposedRemotePref.getBoolean("vendor_id_hook", false)) {
|
if (xposedRemotePref.getBoolean("vendor_id_hook", false)) {
|
||||||
attManager = ATTManager(adapter, device)
|
if (attManager == null) {
|
||||||
attManager!!.connect()
|
attManager = ATTManager(adapter, device)
|
||||||
|
attManager!!.connect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create AirPodsInstance from stored config if available
|
// Create AirPodsInstance from stored config if available
|
||||||
|
|||||||
Reference in New Issue
Block a user