mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-05-02 10:59:19 +00:00
remove dependency on hiddenapibypass
This commit is contained in:
@@ -56,7 +56,6 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.accompanist.permissions)
|
implementation(libs.accompanist.permissions)
|
||||||
implementation(libs.hiddenapibypass)
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ add_library(l2c_fcr_hook SHARED
|
|||||||
xz/xz_dec_bcj.c
|
xz/xz_dec_bcj.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_library(socket_private_constructor SHARED
|
||||||
|
socket_private_constructor.cpp
|
||||||
|
)
|
||||||
|
|
||||||
target_include_directories(l2c_fcr_hook PRIVATE
|
target_include_directories(l2c_fcr_hook PRIVATE
|
||||||
xz
|
xz
|
||||||
)
|
)
|
||||||
@@ -29,6 +33,22 @@ target_compile_definitions(l2c_fcr_hook PRIVATE
|
|||||||
XZ_DEC_CONCATENATED
|
XZ_DEC_CONCATENATED
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_compile_options(socket_private_constructor PRIVATE
|
||||||
|
-O2
|
||||||
|
-fvisibility=hidden
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_options(socket_private_constructor PRIVATE
|
||||||
|
-Wl,--strip-all
|
||||||
|
-Wl,--gc-sections
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(l2c_fcr_hook
|
target_link_libraries(l2c_fcr_hook
|
||||||
android
|
android
|
||||||
log)
|
log
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(socket_private_constructor
|
||||||
|
android
|
||||||
|
log
|
||||||
|
)
|
||||||
|
|||||||
68
android/app/src/main/cpp/socket_private_constructor.cpp
Normal file
68
android/app/src/main/cpp/socket_private_constructor.cpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#include <jni.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
static JavaVM* gVm = nullptr;
|
||||||
|
|
||||||
|
template<size_t N>
|
||||||
|
constexpr auto encryptString(const char (&str)[N], char key) {
|
||||||
|
std::array<char, N> encrypted{};
|
||||||
|
for (size_t i = 0; i < N; i++) {
|
||||||
|
encrypted[i] = str[i] ^ key;
|
||||||
|
}
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t N>
|
||||||
|
static std::string decryptString(const std::array<char, N>& encrypted, char key) {
|
||||||
|
std::string result(N - 1, '\0');
|
||||||
|
for (size_t i = 0; i < N - 1; i++) {
|
||||||
|
result[i] = encrypted[i] ^ key;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ENC(str) encryptString(str, 0x47)
|
||||||
|
#define DEC(arr) decryptString(arr, 0x47).c_str()
|
||||||
|
|
||||||
|
__attribute__((visibility("hidden")))
|
||||||
|
static JavaVM* getVm() { return gVm; }
|
||||||
|
|
||||||
|
__attribute__((visibility("default")))
|
||||||
|
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
|
gVm = vm;
|
||||||
|
|
||||||
|
auto fn = [](void*) -> void* {
|
||||||
|
constexpr auto c1 = ENC("dalvik/system/VMRuntime");
|
||||||
|
constexpr auto c2 = ENC("getRuntime");
|
||||||
|
constexpr auto c3 = ENC("()Ldalvik/system/VMRuntime;");
|
||||||
|
constexpr auto c4 = ENC("setHiddenApiExemptions");
|
||||||
|
constexpr auto c5 = ENC("([Ljava/lang/String;)V");
|
||||||
|
constexpr auto c6 = ENC("java/lang/String");
|
||||||
|
constexpr auto c7 = ENC("Landroid/bluetooth/BluetoothSocket;");
|
||||||
|
constexpr auto c8 = ENC("Landroid/bluetooth/BluetoothDevice;");
|
||||||
|
|
||||||
|
JNIEnv* env;
|
||||||
|
getVm()->AttachCurrentThread(&env, nullptr);
|
||||||
|
|
||||||
|
jclass vmRuntime = env->FindClass(DEC(c1));
|
||||||
|
jmethodID getRuntime = env->GetStaticMethodID(vmRuntime, DEC(c2), DEC(c3));
|
||||||
|
jmethodID setExemptions = env->GetMethodID(vmRuntime, DEC(c4), DEC(c5));
|
||||||
|
|
||||||
|
jobject runtime = env->CallStaticObjectMethod(vmRuntime, getRuntime);
|
||||||
|
jobjectArray prefixes = env->NewObjectArray(
|
||||||
|
2, env->FindClass(DEC(c6)), nullptr);
|
||||||
|
env->SetObjectArrayElement(prefixes, 0, env->NewStringUTF(DEC(c7)));
|
||||||
|
env->SetObjectArrayElement(prefixes, 1, env->NewStringUTF(DEC(c8)));
|
||||||
|
|
||||||
|
env->CallVoidMethod(runtime, setExemptions, prefixes);
|
||||||
|
getVm()->DetachCurrentThread();
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
pthread_t t;
|
||||||
|
pthread_create(&t, nullptr, fn, nullptr);
|
||||||
|
pthread_join(t, nullptr);
|
||||||
|
return JNI_VERSION_1_6;
|
||||||
|
}
|
||||||
@@ -123,11 +123,11 @@ import me.kavishdevar.librepods.utils.SystemApisUtils.METADATA_UNTETHERED_RIGHT_
|
|||||||
import me.kavishdevar.librepods.utils.SystemApisUtils.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD
|
import me.kavishdevar.librepods.utils.SystemApisUtils.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD
|
||||||
import me.kavishdevar.librepods.widgets.BatteryWidget
|
import me.kavishdevar.librepods.widgets.BatteryWidget
|
||||||
import me.kavishdevar.librepods.widgets.NoiseControlWidget
|
import me.kavishdevar.librepods.widgets.NoiseControlWidget
|
||||||
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import kotlin.io.encoding.Base64
|
import kotlin.io.encoding.Base64
|
||||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||||
|
import kotlin.jvm.java
|
||||||
|
|
||||||
private const val TAG = "AirPodsService"
|
private const val TAG = "AirPodsService"
|
||||||
|
|
||||||
@@ -235,6 +235,12 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
|
|
||||||
private lateinit var socket: BluetoothSocket
|
private lateinit var socket: BluetoothSocket
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
System.loadLibrary("socket_private_constructor")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val bleStatusListener = object : BLEManager.AirPodsStatusListener {
|
private val bleStatusListener = object : BLEManager.AirPodsStatusListener {
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
override fun onDeviceStatusChanged(
|
override fun onDeviceStatusChanged(
|
||||||
@@ -355,9 +361,21 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isBluetoothSocketExempted(): Boolean {
|
||||||
|
return try {
|
||||||
|
BluetoothSocket::class.java.declaredConstructors // will throw if still blocked
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission", "UnspecifiedRegisterReceiverFlag")
|
@SuppressLint("MissingPermission", "UnspecifiedRegisterReceiverFlag")
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
Log.i(TAG, "lib exempt worked: ${isBluetoothSocketExempted()}")
|
||||||
|
|
||||||
sharedPreferencesLogs = getSharedPreferences("packet_logs", MODE_PRIVATE)
|
sharedPreferencesLogs = getSharedPreferences("packet_logs", MODE_PRIVATE)
|
||||||
|
|
||||||
@@ -2382,7 +2400,12 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
try {
|
try {
|
||||||
Log.d(TAG, "Trying constructor signature #${index + 1}")
|
Log.d(TAG, "Trying constructor signature #${index + 1}")
|
||||||
attemptedConstructors++
|
attemptedConstructors++
|
||||||
return HiddenApiBypass.newInstance(BluetoothSocket::class.java, *params) as BluetoothSocket
|
|
||||||
|
val paramTypes = params.map { it::class.javaPrimitiveType ?: it::class.java }.toTypedArray()
|
||||||
|
val constructor = BluetoothSocket::class.java.getDeclaredConstructor(*paramTypes)
|
||||||
|
constructor.isAccessible = true
|
||||||
|
return constructor.newInstance(*params) as BluetoothSocket
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Constructor signature #${index + 1} failed: ${e.message}")
|
Log.e(TAG, "Constructor signature #${index + 1} failed: ${e.message}")
|
||||||
lastException = e
|
lastException = e
|
||||||
@@ -2398,7 +2421,6 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
@SuppressLint("MissingPermission", "UnspecifiedRegisterReceiverFlag")
|
@SuppressLint("MissingPermission", "UnspecifiedRegisterReceiverFlag")
|
||||||
fun connectToSocket(adapter: BluetoothAdapter, device: BluetoothDevice, manual: Boolean = false) {
|
fun connectToSocket(adapter: BluetoothAdapter, device: BluetoothDevice, manual: Boolean = false) {
|
||||||
Log.d(TAG, "<LogCollector:Start> Connecting to socket")
|
Log.d(TAG, "<LogCollector:Start> Connecting to socket")
|
||||||
HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;")
|
|
||||||
val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a")
|
val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a")
|
||||||
if (!isConnectedLocally) {
|
if (!isConnectedLocally) {
|
||||||
socket = try {
|
socket = try {
|
||||||
@@ -2818,6 +2840,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
}
|
}
|
||||||
if (device != null) {
|
if (device != null) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
Log.d(TAG, "connecting to $macAddress")
|
||||||
connectToSocket(bluetoothAdapter, device!!, manual = true)
|
connectToSocket(bluetoothAdapter, device!!, manual = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import android.util.Log
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.util.concurrent.LinkedBlockingQueue
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
@@ -70,7 +69,6 @@ class ATTManager(private val adapter: BluetoothAdapter, private val device: Blue
|
|||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
fun connect() {
|
fun connect() {
|
||||||
HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;")
|
|
||||||
val uuid = ParcelUuid.fromString("00000000-0000-0000-0000-000000000000")
|
val uuid = ParcelUuid.fromString("00000000-0000-0000-0000-000000000000")
|
||||||
|
|
||||||
socket = createBluetoothSocket(adapter, device, uuid)
|
socket = createBluetoothSocket(adapter, device, uuid)
|
||||||
@@ -221,7 +219,12 @@ class ATTManager(private val adapter: BluetoothAdapter, private val device: Blue
|
|||||||
try {
|
try {
|
||||||
Log.d("ATTManager", "Trying constructor signature #${index + 1}")
|
Log.d("ATTManager", "Trying constructor signature #${index + 1}")
|
||||||
attemptedConstructors++
|
attemptedConstructors++
|
||||||
return HiddenApiBypass.newInstance(BluetoothSocket::class.java, *params) as BluetoothSocket
|
|
||||||
|
val paramTypes = params.map { it::class.javaPrimitiveType ?: it::class.java }.toTypedArray()
|
||||||
|
val constructor = BluetoothSocket::class.java.getDeclaredConstructor(*paramTypes)
|
||||||
|
constructor.isAccessible = true
|
||||||
|
return constructor.newInstance(*params) as BluetoothSocket
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("ATTManager", "Constructor signature #${index + 1} failed: ${e.message}")
|
Log.e("ATTManager", "Constructor signature #${index + 1} failed: ${e.message}")
|
||||||
lastException = e
|
lastException = e
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package me.kavishdevar.librepods.utils
|
|||||||
|
|
||||||
import android.bluetooth.BluetoothDevice
|
import android.bluetooth.BluetoothDevice
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
|
||||||
|
|
||||||
object SystemApisUtils {
|
object SystemApisUtils {
|
||||||
|
|
||||||
@@ -288,16 +287,14 @@ object SystemApisUtils {
|
|||||||
/**
|
/**
|
||||||
* Helper method to set metadata using HiddenApiBypass
|
* Helper method to set metadata using HiddenApiBypass
|
||||||
*/
|
*/
|
||||||
fun setMetadata(device: BluetoothDevice, key: Int, value: ByteArray): Boolean {
|
fun setMetadata(device: BluetoothDevice, key: Int, value: ByteArray): Boolean {
|
||||||
return try {
|
return try {
|
||||||
val result = HiddenApiBypass.invoke(
|
val method = BluetoothDevice::class.java.getMethod(
|
||||||
BluetoothDevice::class.java,
|
|
||||||
device,
|
|
||||||
"setMetadata",
|
"setMetadata",
|
||||||
key,
|
Int::class.java,
|
||||||
value
|
ByteArray::class.java
|
||||||
) as Boolean
|
)
|
||||||
result
|
method.invoke(device, key, value) as Boolean
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("SystemApisUtils", "Failed to set metadata for key $key", e)
|
Log.e("SystemApisUtils", "Failed to set metadata for key $key", e)
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
[versions]
|
[versions]
|
||||||
accompanistPermissions = "0.36.0"
|
accompanistPermissions = "0.36.0"
|
||||||
agp = "8.9.1"
|
agp = "8.9.1"
|
||||||
hiddenapibypass = "6.1"
|
|
||||||
kotlin = "2.1.10"
|
kotlin = "2.1.10"
|
||||||
coreKtx = "1.17.0"
|
coreKtx = "1.17.0"
|
||||||
lifecycleRuntimeKtx = "2.8.7"
|
lifecycleRuntimeKtx = "2.8.7"
|
||||||
@@ -15,14 +14,12 @@ hazeMaterials = "1.6.10"
|
|||||||
dynamicanimation = "1.1.0"
|
dynamicanimation = "1.1.0"
|
||||||
foundationLayout = "1.9.1"
|
foundationLayout = "1.9.1"
|
||||||
uiTooling = "1.9.1"
|
uiTooling = "1.9.1"
|
||||||
mockk = "1.14.3"
|
|
||||||
ui = "1.9.2"
|
ui = "1.9.2"
|
||||||
aboutLibraries = "13.0.0-rc01"
|
aboutLibraries = "13.0.0-rc01"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
|
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
hiddenapibypass = { module = "org.lsposed.hiddenapibypass:hiddenapibypass", version.ref = "hiddenapibypass" }
|
|
||||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||||
@@ -38,7 +35,6 @@ haze-materials = { group = "dev.chrisbanes.haze", name = "haze-materials", versi
|
|||||||
androidx-dynamicanimation = { group = "androidx.dynamicanimation", name = "dynamicanimation", version.ref = "dynamicanimation" }
|
androidx-dynamicanimation = { group = "androidx.dynamicanimation", name = "dynamicanimation", version.ref = "dynamicanimation" }
|
||||||
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "foundationLayout" }
|
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "foundationLayout" }
|
||||||
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiTooling" }
|
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiTooling" }
|
||||||
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
|
|
||||||
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }
|
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }
|
||||||
aboutlibraries = { group = "com.mikepenz", name = "aboutlibraries", version.ref = "aboutLibraries" }
|
aboutlibraries = { group = "com.mikepenz", name = "aboutlibraries", version.ref = "aboutLibraries" }
|
||||||
aboutlibraries-compose-m3 = { group = "com.mikepenz", name = "aboutlibraries-compose-m3", version.ref = "aboutLibraries" }
|
aboutlibraries-compose-m3 = { group = "com.mikepenz", name = "aboutlibraries-compose-m3", version.ref = "aboutLibraries" }
|
||||||
|
|||||||
Reference in New Issue
Block a user