This commit is contained in:
Administrator 2025-07-10 16:19:47 +08:00
parent 5bcecde83d
commit 0e252b3751
6 changed files with 30 additions and 58 deletions

View File

@ -14,9 +14,12 @@ import androidx.core.view.WindowInsetsCompat
import com.android.grape.databinding.ActivityMainBinding
import com.android.grape.job.MonitorService
import com.android.grape.util.ClashUtil
import com.android.grape.util.MockTools
import com.android.grape.util.NotificationPermissionHandler
import com.android.grape.util.ScriptUtil
import com.android.grape.util.ShellUtils
import com.android.grape.util.StoragePermissionHelper
import com.android.grape.util.Util
class MainActivity : AppCompatActivity() {
private val viewModel by viewModels<MainViewModel>()
@ -25,6 +28,7 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
init()
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
@ -36,28 +40,19 @@ class MainActivity : AppCompatActivity() {
ScriptUtil.registerScriptResultReceiver()
viewBinding.start.setOnClickListener {
MonitorService.onEvent(MainApplication.instance)
// try {
// ClashUtil.startProxy(this)
// ClashUtil.switchProxyGroup(
// "GLOBAL",
// ClashUtil.getRandomLocale(),
// "http://127.0.0.1:6170"
// )
// } catch (e: Exception) {
// LogUtils.e("startProxyVpn: Failed to start VPN", e)
// Toast.makeText(
// this,
// "Failed to start VPN: " + (if (e.message != null) e.message else "Unknown error"),
// Toast.LENGTH_SHORT
// ).show()
// }
}
viewBinding.stop.setOnClickListener {
ClashUtil.stopProxy(this)
// AutoJsUtil.stopAutojsScript(this)
val isRoot = ShellUtils.hasRootAccess()
Log.d("TAG", "onCreate: isRoot $isRoot")
}
}
fun init() {
MockTools.exec("pm grant com.android.grape android.permission.INTERACT_ACROSS_USERS")
MockTools.exec("pm grant com.android.grape android.permission.WRITE_SECURE_SETTINGS")
MockTools.exec("pm setenforce 0")
}
override fun onDestroy() {
super.onDestroy()
ClashUtil.unregisterReceiver(this)
@ -65,7 +60,6 @@ class MainActivity : AppCompatActivity() {
}
private fun checkPermission() {
Log.d("TAG", "checkPermission: ")
permissionHandler = NotificationPermissionHandler(this) { result ->
handlePermissionResult(result)
}

View File

@ -36,7 +36,7 @@ class StartVpnPortJobService : JobIntentService() {
do {
val url =
"http://39.103.73.250/tt/test/testProxy.jsp?port=$port&country=" + Util.proxyCountry
?.lowercase(Locale.getDefault())
?.uppercase(Locale.getDefault())
val result: String = MyGet.get(url)
Log.d(TAG, "request url == $url result$result")
if (result.contains("ok")) {

View File

@ -39,6 +39,10 @@ object ChangeDeviceInfoUtil {
put("propertiesName", "MCCMNC")
put("propertiesValue", "${device.mcc},${device.mnc}")
})
put(JSONObject().apply {
put("propertiesName", "OpName")
put("propertiesValue", device.operator)
})
})
put("systemPropertiesList", JSONArray().apply {
put(JSONObject().apply {
@ -187,7 +191,7 @@ object ChangeDeviceInfoUtil {
vcloudsettingsPut("$currentPkgName.vendor", device.expand.glVendor, context)
// callVCloudSettings_put("$currentPkgName.battery_scale", batteryScale.toString(), context)
vcloudsettingsPut("$currentPkgName.os_lang", device.lang, context)
vcloudsettingsPut("$currentPkgName.model", device.model, context);
vcloudsettingsPut("$currentPkgName.model", device.model, context)
vcloudsettingsPut("$currentPkgName.net", device.network, context)
vcloudsettingsPut("$currentPkgName.dpi", device.expand.dPI.toString(), context)
// callVCloudSettings_put(
@ -295,7 +299,7 @@ object ChangeDeviceInfoUtil {
}
private fun vcloudsettingsPut(key: String, value: String, context: Context) {
fun vcloudsettingsPut(key: String, value: String, context: Context) {
if (key.isEmpty()) {
LogUtils.e(Log.ERROR, "ChangeDeviceInfoUtil", "Key cannot be null or empty", null)
throw IllegalArgumentException("Key cannot be null or empty")

View File

@ -107,7 +107,6 @@ class NotificationPermissionHandler(
fun requestNotificationPermission() {
// 保存当前权限状态,用于比较设置后是否发生变化
lastPermissionState = areNotificationsEnabled()
Log.d(TAG, "requestNotificationPermission: ")
when {
// Android 13+ - 使用运行时权限请求
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
@ -199,7 +198,6 @@ class NotificationPermissionHandler(
}
val activity = context as? AppCompatActivity ?: return
Log.d(TAG, "requestPermissionApi33: ")
when {
// 已经拥有权限
areNotificationsEnabled() -> {
@ -215,7 +213,6 @@ class NotificationPermissionHandler(
// 请求权限
else -> {
permissionRequestPending = true
Log.d(TAG, "requestPermissionApi33: launch")
permissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
@ -284,30 +281,23 @@ class NotificationPermissionHandler(
*/
@JvmStatic
fun checkPermissionState(context: Context): PermissionResult {
Log.d(TAG, "checkPermissionState: --------->")
return when {
// Android 13+ 需要运行时权限
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
Log.d(TAG, "checkPermissionState: Android 13+")
if (ContextCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "checkPermissionState: Granted")
PermissionResult.GRANTED
} else {
Log.d(TAG, "checkPermissionState: Denied")
PermissionResult.DENIED
}
}
// Android 8.0+ 检查通知设置
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
Log.d(TAG, "checkPermissionState: Android 8.0+")
if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
Log.d(TAG, "checkPermissionState: Granted")
PermissionResult.GRANTED
} else {
Log.d(TAG, "checkPermissionState: Denied")
PermissionResult.NEEDS_SETTINGS
}
}

View File

@ -102,32 +102,23 @@ public class ShellUtils {
}
public static String execRootCmdAndGetResult(String cmd) {
// Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: " + cmd);
if (cmd == null || cmd.trim().isEmpty()) {
LogUtils.e(Log.ERROR, "ShellUtils", "Unsafe or empty command. Aborting execution.", null);
throw new IllegalArgumentException("Unsafe or empty command.");
}
// if (!isCommandSafe(cmd)) { // 检查命令的合法性
// Log.e("ShellUtils", "Detected unsafe command. Aborting execution.");
// throw new IllegalArgumentException("Detected unsafe command.");
// }
Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: " + cmd);
if (!isCommandSafe(cmd)) { // 检查命令的合法性
Log.e("ShellUtils", "Detected unsafe command. Aborting execution.");
throw new IllegalArgumentException("Detected unsafe command.");
}
Process process = null;
ExecutorService executor = Executors.newFixedThreadPool(2);
try {
// Log.d("ShellUtils", "Determining appropriate shell for execution...");
if (hasBin("su")) {
// Log.d("ShellUtils", "'su' binary found, using 'su' shell.");
process = Runtime.getRuntime().exec("su");
} else if (hasBin("xu")) {
// Log.d("ShellUtils", "'xu' binary found, using 'xu' shell.");
process = Runtime.getRuntime().exec("xu");
} else if (hasBin("vu")) {
// Log.d("ShellUtils", "'vu' binary found, using 'vu' shell.");
process = Runtime.getRuntime().exec("vu");
} else {
// Log.d("ShellUtils", "No specific binary found, using 'sh' shell.");
process = Runtime.getRuntime().exec("sh");
}
@ -137,7 +128,6 @@ public class ShellUtils {
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream, StandardCharsets.UTF_8))) {
// Log.d("ShellUtils", "Starting separate thread to process error stream...");
executor.submit(() -> {
String line;
try {
@ -149,28 +139,21 @@ public class ShellUtils {
}
});
// Log.d("ShellUtils", "Writing the command to the shell...");
os.write((cmd + "\n").getBytes());
os.write("exit\n".getBytes());
os.flush();
// Log.d("ShellUtils", "Command written to shell. Waiting for process to complete.");
StringBuilder output = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
// Log.d("ShellUtils", "Shell Output: " + line);
output.append(line).append("\n");
}
// Log.d("ShellUtils", "Awaiting process termination...");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!process.waitFor(10, TimeUnit.SECONDS)) {
// LogUtils.e(Log.ERROR, "ShellUtils", "Process execution timed out. Destroying process.", null);
process.destroyForcibly();
throw new RuntimeException("Shell command execution timeout.");
}
} else {
// Log.d("ShellUtils", "Using manual time tracking method for process termination (API < 26).");
long startTime = System.currentTimeMillis();
while (true) {
try {
@ -178,7 +161,6 @@ public class ShellUtils {
break;
} catch (IllegalThreadStateException e) {
if (System.currentTimeMillis() - startTime > 10000) { // 10 seconds
// LogUtils.e(Log.ERROR, "ShellUtils", "Process execution timed out (manual tracking). Destroying process.", null);
process.destroy();
throw new RuntimeException("Shell command execution timeout.");
}
@ -186,8 +168,6 @@ public class ShellUtils {
}
}
}
Log.d("ShellUtils", "Process terminated successfully. Returning result.");
return output.toString().trim();
}
} catch (IOException | InterruptedException e) {
@ -196,7 +176,6 @@ public class ShellUtils {
return "Error: " + e.getMessage();
} finally {
if (process != null) {
Log.d("ShellUtils", "Finalizing process. Attempting to destroy it.");
process.destroy();
}
executor.shutdown();
@ -207,7 +186,6 @@ public class ShellUtils {
public static void execRootCmd(String cmd) {
// 校验命令是否安全
if (!isCommandSafe(cmd)) {
LogUtils.e(Log.ERROR, "ShellUtils", "Unsafe command, aborting.", null);
return;
}
List<String> cmds = new ArrayList<>();

View File

@ -1,6 +1,7 @@
package com.android.grape.util
import android.content.Context
import com.android.grape.util.TimeZoneUtils.formatTimeZoneOffset
import java.util.*
import kotlin.math.abs
@ -66,4 +67,9 @@ object TimeZoneUtils {
val diff = targetOffsetMillis - currentOffset
return Date(time.time + diff)
}
}
fun main() {
val offset = formatTimeZoneOffset(19800000)
println("当前时区偏移:$offset")
}