This commit is contained in:
parent
5bcecde83d
commit
0e252b3751
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")) {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
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.");
|
||||
}
|
||||
// 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<>();
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -67,3 +68,8 @@ object TimeZoneUtils {
|
|||
return Date(time.time + diff)
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val offset = formatTimeZoneOffset(19800000)
|
||||
println("当前时区偏移:$offset")
|
||||
}
|
Loading…
Reference in New Issue