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.databinding.ActivityMainBinding
|
||||||
import com.android.grape.job.MonitorService
|
import com.android.grape.job.MonitorService
|
||||||
import com.android.grape.util.ClashUtil
|
import com.android.grape.util.ClashUtil
|
||||||
|
import com.android.grape.util.MockTools
|
||||||
import com.android.grape.util.NotificationPermissionHandler
|
import com.android.grape.util.NotificationPermissionHandler
|
||||||
import com.android.grape.util.ScriptUtil
|
import com.android.grape.util.ScriptUtil
|
||||||
|
import com.android.grape.util.ShellUtils
|
||||||
import com.android.grape.util.StoragePermissionHelper
|
import com.android.grape.util.StoragePermissionHelper
|
||||||
|
import com.android.grape.util.Util
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
private val viewModel by viewModels<MainViewModel>()
|
private val viewModel by viewModels<MainViewModel>()
|
||||||
|
@ -25,6 +28,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
|
init()
|
||||||
viewBinding = ActivityMainBinding.inflate(layoutInflater)
|
viewBinding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(viewBinding.root)
|
setContentView(viewBinding.root)
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||||
|
@ -36,28 +40,19 @@ class MainActivity : AppCompatActivity() {
|
||||||
ScriptUtil.registerScriptResultReceiver()
|
ScriptUtil.registerScriptResultReceiver()
|
||||||
viewBinding.start.setOnClickListener {
|
viewBinding.start.setOnClickListener {
|
||||||
MonitorService.onEvent(MainApplication.instance)
|
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 {
|
viewBinding.stop.setOnClickListener {
|
||||||
ClashUtil.stopProxy(this)
|
val isRoot = ShellUtils.hasRootAccess()
|
||||||
// AutoJsUtil.stopAutojsScript(this)
|
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() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
ClashUtil.unregisterReceiver(this)
|
ClashUtil.unregisterReceiver(this)
|
||||||
|
@ -65,7 +60,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkPermission() {
|
private fun checkPermission() {
|
||||||
Log.d("TAG", "checkPermission: ")
|
|
||||||
permissionHandler = NotificationPermissionHandler(this) { result ->
|
permissionHandler = NotificationPermissionHandler(this) { result ->
|
||||||
handlePermissionResult(result)
|
handlePermissionResult(result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ class StartVpnPortJobService : JobIntentService() {
|
||||||
do {
|
do {
|
||||||
val url =
|
val url =
|
||||||
"http://39.103.73.250/tt/test/testProxy.jsp?port=$port&country=" + Util.proxyCountry
|
"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)
|
val result: String = MyGet.get(url)
|
||||||
Log.d(TAG, "request url == $url result$result")
|
Log.d(TAG, "request url == $url result$result")
|
||||||
if (result.contains("ok")) {
|
if (result.contains("ok")) {
|
||||||
|
|
|
@ -39,6 +39,10 @@ object ChangeDeviceInfoUtil {
|
||||||
put("propertiesName", "MCCMNC")
|
put("propertiesName", "MCCMNC")
|
||||||
put("propertiesValue", "${device.mcc},${device.mnc}")
|
put("propertiesValue", "${device.mcc},${device.mnc}")
|
||||||
})
|
})
|
||||||
|
put(JSONObject().apply {
|
||||||
|
put("propertiesName", "OpName")
|
||||||
|
put("propertiesValue", device.operator)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
put("systemPropertiesList", JSONArray().apply {
|
put("systemPropertiesList", JSONArray().apply {
|
||||||
put(JSONObject().apply {
|
put(JSONObject().apply {
|
||||||
|
@ -187,7 +191,7 @@ object ChangeDeviceInfoUtil {
|
||||||
vcloudsettingsPut("$currentPkgName.vendor", device.expand.glVendor, context)
|
vcloudsettingsPut("$currentPkgName.vendor", device.expand.glVendor, context)
|
||||||
// callVCloudSettings_put("$currentPkgName.battery_scale", batteryScale.toString(), context)
|
// callVCloudSettings_put("$currentPkgName.battery_scale", batteryScale.toString(), context)
|
||||||
vcloudsettingsPut("$currentPkgName.os_lang", device.lang, 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.net", device.network, context)
|
||||||
vcloudsettingsPut("$currentPkgName.dpi", device.expand.dPI.toString(), context)
|
vcloudsettingsPut("$currentPkgName.dpi", device.expand.dPI.toString(), context)
|
||||||
// callVCloudSettings_put(
|
// 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()) {
|
if (key.isEmpty()) {
|
||||||
LogUtils.e(Log.ERROR, "ChangeDeviceInfoUtil", "Key cannot be null or empty", null)
|
LogUtils.e(Log.ERROR, "ChangeDeviceInfoUtil", "Key cannot be null or empty", null)
|
||||||
throw IllegalArgumentException("Key cannot be null or empty")
|
throw IllegalArgumentException("Key cannot be null or empty")
|
||||||
|
|
|
@ -107,7 +107,6 @@ class NotificationPermissionHandler(
|
||||||
fun requestNotificationPermission() {
|
fun requestNotificationPermission() {
|
||||||
// 保存当前权限状态,用于比较设置后是否发生变化
|
// 保存当前权限状态,用于比较设置后是否发生变化
|
||||||
lastPermissionState = areNotificationsEnabled()
|
lastPermissionState = areNotificationsEnabled()
|
||||||
Log.d(TAG, "requestNotificationPermission: ")
|
|
||||||
when {
|
when {
|
||||||
// Android 13+ - 使用运行时权限请求
|
// Android 13+ - 使用运行时权限请求
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
|
||||||
|
@ -199,7 +198,6 @@ class NotificationPermissionHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
val activity = context as? AppCompatActivity ?: return
|
val activity = context as? AppCompatActivity ?: return
|
||||||
Log.d(TAG, "requestPermissionApi33: ")
|
|
||||||
when {
|
when {
|
||||||
// 已经拥有权限
|
// 已经拥有权限
|
||||||
areNotificationsEnabled() -> {
|
areNotificationsEnabled() -> {
|
||||||
|
@ -215,7 +213,6 @@ class NotificationPermissionHandler(
|
||||||
// 请求权限
|
// 请求权限
|
||||||
else -> {
|
else -> {
|
||||||
permissionRequestPending = true
|
permissionRequestPending = true
|
||||||
Log.d(TAG, "requestPermissionApi33: launch")
|
|
||||||
permissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS)
|
permissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,30 +281,23 @@ class NotificationPermissionHandler(
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun checkPermissionState(context: Context): PermissionResult {
|
fun checkPermissionState(context: Context): PermissionResult {
|
||||||
Log.d(TAG, "checkPermissionState: --------->")
|
|
||||||
return when {
|
return when {
|
||||||
// Android 13+ 需要运行时权限
|
// Android 13+ 需要运行时权限
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
|
||||||
Log.d(TAG, "checkPermissionState: Android 13+")
|
|
||||||
if (ContextCompat.checkSelfPermission(
|
if (ContextCompat.checkSelfPermission(
|
||||||
context,
|
context,
|
||||||
Manifest.permission.POST_NOTIFICATIONS
|
Manifest.permission.POST_NOTIFICATIONS
|
||||||
) == PackageManager.PERMISSION_GRANTED) {
|
) == PackageManager.PERMISSION_GRANTED) {
|
||||||
Log.d(TAG, "checkPermissionState: Granted")
|
|
||||||
PermissionResult.GRANTED
|
PermissionResult.GRANTED
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "checkPermissionState: Denied")
|
|
||||||
PermissionResult.DENIED
|
PermissionResult.DENIED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Android 8.0+ 检查通知设置
|
// Android 8.0+ 检查通知设置
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
|
||||||
Log.d(TAG, "checkPermissionState: Android 8.0+")
|
|
||||||
if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
|
if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
|
||||||
Log.d(TAG, "checkPermissionState: Granted")
|
|
||||||
PermissionResult.GRANTED
|
PermissionResult.GRANTED
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "checkPermissionState: Denied")
|
|
||||||
PermissionResult.NEEDS_SETTINGS
|
PermissionResult.NEEDS_SETTINGS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,32 +102,23 @@ public class ShellUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String execRootCmdAndGetResult(String cmd) {
|
public static String execRootCmdAndGetResult(String cmd) {
|
||||||
// Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: " + cmd);
|
Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: " + cmd);
|
||||||
if (cmd == null || cmd.trim().isEmpty()) {
|
if (!isCommandSafe(cmd)) { // 检查命令的合法性
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Unsafe or empty command. Aborting execution.", null);
|
Log.e("ShellUtils", "Detected unsafe command. Aborting execution.");
|
||||||
throw new IllegalArgumentException("Unsafe or empty command.");
|
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;
|
Process process = null;
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(2);
|
ExecutorService executor = Executors.newFixedThreadPool(2);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Log.d("ShellUtils", "Determining appropriate shell for execution...");
|
|
||||||
if (hasBin("su")) {
|
if (hasBin("su")) {
|
||||||
// Log.d("ShellUtils", "'su' binary found, using 'su' shell.");
|
|
||||||
process = Runtime.getRuntime().exec("su");
|
process = Runtime.getRuntime().exec("su");
|
||||||
} else if (hasBin("xu")) {
|
} else if (hasBin("xu")) {
|
||||||
// Log.d("ShellUtils", "'xu' binary found, using 'xu' shell.");
|
|
||||||
process = Runtime.getRuntime().exec("xu");
|
process = Runtime.getRuntime().exec("xu");
|
||||||
} else if (hasBin("vu")) {
|
} else if (hasBin("vu")) {
|
||||||
// Log.d("ShellUtils", "'vu' binary found, using 'vu' shell.");
|
|
||||||
process = Runtime.getRuntime().exec("vu");
|
process = Runtime.getRuntime().exec("vu");
|
||||||
} else {
|
} else {
|
||||||
// Log.d("ShellUtils", "No specific binary found, using 'sh' shell.");
|
|
||||||
process = Runtime.getRuntime().exec("sh");
|
process = Runtime.getRuntime().exec("sh");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +128,6 @@ public class ShellUtils {
|
||||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
|
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
|
||||||
BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream, 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(() -> {
|
executor.submit(() -> {
|
||||||
String line;
|
String line;
|
||||||
try {
|
try {
|
||||||
|
@ -149,28 +139,21 @@ public class ShellUtils {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Log.d("ShellUtils", "Writing the command to the shell...");
|
|
||||||
os.write((cmd + "\n").getBytes());
|
os.write((cmd + "\n").getBytes());
|
||||||
os.write("exit\n".getBytes());
|
os.write("exit\n".getBytes());
|
||||||
os.flush();
|
os.flush();
|
||||||
// Log.d("ShellUtils", "Command written to shell. Waiting for process to complete.");
|
|
||||||
|
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
String line;
|
String line;
|
||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
// Log.d("ShellUtils", "Shell Output: " + line);
|
|
||||||
output.append(line).append("\n");
|
output.append(line).append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log.d("ShellUtils", "Awaiting process termination...");
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
if (!process.waitFor(10, TimeUnit.SECONDS)) {
|
if (!process.waitFor(10, TimeUnit.SECONDS)) {
|
||||||
// LogUtils.e(Log.ERROR, "ShellUtils", "Process execution timed out. Destroying process.", null);
|
|
||||||
process.destroyForcibly();
|
process.destroyForcibly();
|
||||||
throw new RuntimeException("Shell command execution timeout.");
|
throw new RuntimeException("Shell command execution timeout.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Log.d("ShellUtils", "Using manual time tracking method for process termination (API < 26).");
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
|
@ -178,7 +161,6 @@ public class ShellUtils {
|
||||||
break;
|
break;
|
||||||
} catch (IllegalThreadStateException e) {
|
} catch (IllegalThreadStateException e) {
|
||||||
if (System.currentTimeMillis() - startTime > 10000) { // 10 seconds
|
if (System.currentTimeMillis() - startTime > 10000) { // 10 seconds
|
||||||
// LogUtils.e(Log.ERROR, "ShellUtils", "Process execution timed out (manual tracking). Destroying process.", null);
|
|
||||||
process.destroy();
|
process.destroy();
|
||||||
throw new RuntimeException("Shell command execution timeout.");
|
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();
|
return output.toString().trim();
|
||||||
}
|
}
|
||||||
} catch (IOException | InterruptedException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
|
@ -196,7 +176,6 @@ public class ShellUtils {
|
||||||
return "Error: " + e.getMessage();
|
return "Error: " + e.getMessage();
|
||||||
} finally {
|
} finally {
|
||||||
if (process != null) {
|
if (process != null) {
|
||||||
Log.d("ShellUtils", "Finalizing process. Attempting to destroy it.");
|
|
||||||
process.destroy();
|
process.destroy();
|
||||||
}
|
}
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
|
@ -207,7 +186,6 @@ public class ShellUtils {
|
||||||
public static void execRootCmd(String cmd) {
|
public static void execRootCmd(String cmd) {
|
||||||
// 校验命令是否安全
|
// 校验命令是否安全
|
||||||
if (!isCommandSafe(cmd)) {
|
if (!isCommandSafe(cmd)) {
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Unsafe command, aborting.", null);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<String> cmds = new ArrayList<>();
|
List<String> cmds = new ArrayList<>();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.android.grape.util
|
package com.android.grape.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.android.grape.util.TimeZoneUtils.formatTimeZoneOffset
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
@ -66,4 +67,9 @@ object TimeZoneUtils {
|
||||||
val diff = targetOffsetMillis - currentOffset
|
val diff = targetOffsetMillis - currentOffset
|
||||||
return Date(time.time + diff)
|
return Date(time.time + diff)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val offset = formatTimeZoneOffset(19800000)
|
||||||
|
println("当前时区偏移:$offset")
|
||||||
}
|
}
|
Loading…
Reference in New Issue