mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-02-01 15:49:10 +00:00
android: add toggle for DID hook
This commit is contained in:
@@ -20,6 +20,7 @@ package me.kavishdevar.librepods.screens
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
@@ -99,6 +100,9 @@ import me.kavishdevar.librepods.utils.RadareOffsetFinder
|
||||
import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
import kotlin.math.roundToInt
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class, ExperimentalEncodingApi::class)
|
||||
@Composable
|
||||
@@ -107,6 +111,7 @@ fun AppSettingsScreen(navController: NavController) {
|
||||
val name = remember { mutableStateOf(sharedPreferences.getString("name", "") ?: "") }
|
||||
val isDarkTheme = isSystemInDarkTheme()
|
||||
val context = LocalContext.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val scrollState = rememberScrollState()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val hazeState = remember { HazeState() }
|
||||
@@ -200,6 +205,11 @@ fun AppSettingsScreen(navController: NavController) {
|
||||
return hexPattern.matches(input)
|
||||
}
|
||||
|
||||
var isProcessingSdp by remember { mutableStateOf(false) }
|
||||
var actAsAppleDevice by remember { mutableStateOf(false) }
|
||||
|
||||
BackHandler(enabled = isProcessingSdp) {}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
@@ -233,8 +243,11 @@ fun AppSettingsScreen(navController: NavController) {
|
||||
navigationIcon = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
navController.popBackStack()
|
||||
if (!isProcessingSdp) {
|
||||
navController.popBackStack()
|
||||
}
|
||||
},
|
||||
enabled = !isProcessingSdp,
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
modifier = Modifier.width(180.dp)
|
||||
) {
|
||||
@@ -1189,6 +1202,91 @@ fun AppSettingsScreen(navController: NavController) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
actAsAppleDevice = RadareOffsetFinder.isSdpOffsetAvailable()
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(
|
||||
enabled = !isProcessingSdp,
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
if (!isProcessingSdp) {
|
||||
val newValue = !actAsAppleDevice
|
||||
actAsAppleDevice = newValue
|
||||
isProcessingSdp = true
|
||||
coroutineScope.launch {
|
||||
if (newValue) {
|
||||
val radareOffsetFinder = RadareOffsetFinder(context)
|
||||
val success = radareOffsetFinder.findSdpOffset() ?: false
|
||||
if (success) {
|
||||
Toast.makeText(context, "Found offset please restart the Bluetooth process", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
} else {
|
||||
RadareOffsetFinder.clearSdpOffset()
|
||||
}
|
||||
isProcessingSdp = false
|
||||
}
|
||||
}
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = 8.dp)
|
||||
.padding(end = 4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Act as an Apple device",
|
||||
fontSize = 16.sp,
|
||||
color = textColor
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = "Enables multi-device connectivity and Accessibility features like customizing transparency mode (amplification, tone, ambient noise reduction, conversation boost, and EQ)",
|
||||
fontSize = 14.sp,
|
||||
color = textColor.copy(0.6f),
|
||||
lineHeight = 16.sp,
|
||||
)
|
||||
if (actAsAppleDevice) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = "Might be unstable!! A maximum of two devices can be connected to your AirPods. If you are using with an Apple device like an iPad or Mac, then please connect that device first and then your Android.",
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
lineHeight = 14.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
StyledSwitch(
|
||||
checked = actAsAppleDevice,
|
||||
onCheckedChange = {
|
||||
if (!isProcessingSdp) {
|
||||
actAsAppleDevice = it
|
||||
isProcessingSdp = true
|
||||
coroutineScope.launch {
|
||||
if (it) {
|
||||
val radareOffsetFinder = RadareOffsetFinder(context)
|
||||
val success = radareOffsetFinder.findSdpOffset() ?: false
|
||||
if (success) {
|
||||
Toast.makeText(context, "Found offset please restart the Bluetooth process", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
} else {
|
||||
RadareOffsetFinder.clearSdpOffset()
|
||||
}
|
||||
isProcessingSdp = false
|
||||
}
|
||||
}
|
||||
},
|
||||
enabled = !isProcessingSdp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
@@ -78,7 +78,7 @@ class RadareOffsetFinder(context: Context) {
|
||||
"setprop $HOOK_OFFSET_PROP '' && " +
|
||||
"setprop $CFG_REQ_OFFSET_PROP '' && " +
|
||||
"setprop $CSM_CONFIG_OFFSET_PROP '' && " +
|
||||
"setprop $PEER_INFO_REQ_OFFSET_PROP ''" +
|
||||
"setprop $PEER_INFO_REQ_OFFSET_PROP '' &&" +
|
||||
"setprop $SDP_OFFSET_PROP ''"
|
||||
))
|
||||
val exitCode = process.waitFor()
|
||||
@@ -94,6 +94,44 @@ class RadareOffsetFinder(context: Context) {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun clearSdpOffset(): Boolean {
|
||||
try {
|
||||
val process = Runtime.getRuntime().exec(arrayOf(
|
||||
"su", "-c", "setprop $SDP_OFFSET_PROP ''"
|
||||
))
|
||||
val exitCode = process.waitFor()
|
||||
|
||||
if (exitCode == 0) {
|
||||
Log.d(TAG, "Successfully cleared SDP offset property")
|
||||
return true
|
||||
} else {
|
||||
Log.e(TAG, "Failed to clear SDP offset property, exit code: $exitCode")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error clearing SDP offset property", e)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun isSdpOffsetAvailable(): Boolean {
|
||||
try {
|
||||
val process = Runtime.getRuntime().exec(arrayOf("getprop", SDP_OFFSET_PROP))
|
||||
val reader = BufferedReader(InputStreamReader(process.inputStream))
|
||||
val propValue = reader.readLine()
|
||||
process.waitFor()
|
||||
|
||||
if (propValue != null && propValue.isNotEmpty()) {
|
||||
Log.d(TAG, "SDP offset property exists: $propValue")
|
||||
return true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error checking if SDP offset property exists", e)
|
||||
}
|
||||
|
||||
Log.d(TAG, "No SDP offset available")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private val radare2TarballFile = File(context.cacheDir, "radare2.tar.gz")
|
||||
@@ -661,4 +699,57 @@ class RadareOffsetFinder(context: Context) {
|
||||
Log.e(TAG, "Failed to cleanup extracted files", e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun findSdpOffset(): Boolean = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
_progressState.value = ProgressState.Downloading
|
||||
if (!downloadRadare2TarballIfNeeded()) {
|
||||
_progressState.value = ProgressState.Error("Failed to download radare2 tarball")
|
||||
Log.e(TAG, "Failed to download radare2 tarball")
|
||||
return@withContext false
|
||||
}
|
||||
|
||||
_progressState.value = ProgressState.Extracting
|
||||
if (!extractRadare2Tarball()) {
|
||||
_progressState.value = ProgressState.Error("Failed to extract radare2 tarball")
|
||||
Log.e(TAG, "Failed to extract radare2 tarball")
|
||||
return@withContext false
|
||||
}
|
||||
|
||||
_progressState.value = ProgressState.MakingExecutable
|
||||
if (!makeExecutable()) {
|
||||
_progressState.value = ProgressState.Error("Failed to make binaries executable")
|
||||
Log.e(TAG, "Failed to make binaries executable")
|
||||
return@withContext false
|
||||
}
|
||||
|
||||
_progressState.value = ProgressState.FindingOffset
|
||||
val libraryPath = findBluetoothLibraryPath()
|
||||
if (libraryPath == null) {
|
||||
_progressState.value = ProgressState.Error("Failed to find Bluetooth library")
|
||||
Log.e(TAG, "Failed to find Bluetooth library")
|
||||
return@withContext false
|
||||
}
|
||||
|
||||
@Suppress("LocalVariableName") val currentLD_LIBRARY_PATH = ProcessBuilder().command("su", "-c", "printenv LD_LIBRARY_PATH").start().inputStream.bufferedReader().readText().trim()
|
||||
val currentPATH = ProcessBuilder().command("su", "-c", "printenv PATH").start().inputStream.bufferedReader().readText().trim()
|
||||
val envSetup = """
|
||||
export LD_LIBRARY_PATH="$RADARE2_LIB_PATH:$currentLD_LIBRARY_PATH"
|
||||
export PATH="$BUSYBOX_PATH:$RADARE2_BIN_PATH:$currentPATH"
|
||||
""".trimIndent()
|
||||
|
||||
findAndSaveSdpOffset(libraryPath, envSetup)
|
||||
|
||||
_progressState.value = ProgressState.Cleaning
|
||||
cleanupExtractedFiles()
|
||||
|
||||
_progressState.value = ProgressState.Success(0L)
|
||||
return@withContext true
|
||||
|
||||
} catch (e: Exception) {
|
||||
_progressState.value = ProgressState.Error("Error: ${e.message}")
|
||||
Log.e(TAG, "Error in findSdpOffset", e)
|
||||
return@withContext false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user