Compare commits
3 Commits
dadf5c7551
...
54ec5023fc
Author | SHA1 | Date |
---|---|---|
|
54ec5023fc | |
|
42da6261fd | |
|
afd85cd10b |
|
@ -4,14 +4,155 @@
|
||||||
<mappings />
|
<mappings />
|
||||||
<preferences />
|
<preferences />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="DBNavigator.Project.DataEditorManager">
|
||||||
|
<record-view-column-sorting-type value="BY_INDEX" />
|
||||||
|
<value-preview-text-wrapping value="true" />
|
||||||
|
<value-preview-pinned value="false" />
|
||||||
|
</component>
|
||||||
<component name="DBNavigator.Project.DatabaseAssistantManager">
|
<component name="DBNavigator.Project.DatabaseAssistantManager">
|
||||||
<assistants />
|
<assistants>
|
||||||
|
<assistant-state connection-id="96c06034-88db-4cab-9c16-73c5d99a2c0a" default-profile-name="" selected-profile-name="" selected-model-name="" assistant-type="GENERIC" selected-action="SHOW_SQL" availability="UNAVAILABLE" acknowledgement="NONE">
|
||||||
|
<messages />
|
||||||
|
</assistant-state>
|
||||||
|
</assistants>
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.DatabaseBrowserManager">
|
||||||
|
<autoscroll-to-editor value="false" />
|
||||||
|
<autoscroll-from-editor value="true" />
|
||||||
|
<show-object-properties value="true" />
|
||||||
|
<loaded-nodes />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.DatabaseConsoleManager">
|
||||||
|
<connection id="96c06034-88db-4cab-9c16-73c5d99a2c0a">
|
||||||
|
<console name="Connection" type="STANDARD" schema="device_info" session="Main" />
|
||||||
|
</connection>
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.DatabaseEditorStateManager">
|
||||||
|
<last-used-providers />
|
||||||
</component>
|
</component>
|
||||||
<component name="DBNavigator.Project.DatabaseFileManager">
|
<component name="DBNavigator.Project.DatabaseFileManager">
|
||||||
<open-files />
|
<open-files />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="DBNavigator.Project.DatabaseSessionManager">
|
||||||
|
<connection id="96c06034-88db-4cab-9c16-73c5d99a2c0a" />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.DatasetFilterManager">
|
||||||
|
<filter-actions connection-id="96c06034-88db-4cab-9c16-73c5d99a2c0a" dataset="device_info.device_info" active-filter-id="EMPTY_FILTER" />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.ExecutionManager">
|
||||||
|
<retain-sticky-names value="false" />
|
||||||
|
</component>
|
||||||
|
<component name="DBNavigator.Project.ObjectQuickFilterManager">
|
||||||
|
<last-used-operator value="EQUAL" />
|
||||||
|
<filters />
|
||||||
|
</component>
|
||||||
<component name="DBNavigator.Project.Settings">
|
<component name="DBNavigator.Project.Settings">
|
||||||
<connections />
|
<connections>
|
||||||
|
<connection id="96c06034-88db-4cab-9c16-73c5d99a2c0a" active="true" signed="true">
|
||||||
|
<database>
|
||||||
|
<name value="Connection" />
|
||||||
|
<description value="" />
|
||||||
|
<database-type value="SQLITE" />
|
||||||
|
<config-type value="BASIC" />
|
||||||
|
<database-version value="3.49" />
|
||||||
|
<driver-source value="BUNDLED" />
|
||||||
|
<driver-library value="" />
|
||||||
|
<driver value="" />
|
||||||
|
<url-type value="FILE" />
|
||||||
|
<host value="" />
|
||||||
|
<port value="" />
|
||||||
|
<database value="" />
|
||||||
|
<tns-folder value="" />
|
||||||
|
<tns-profile value="" />
|
||||||
|
<files>
|
||||||
|
<file path="D:\Desktop\device_info.db" schema="device_info" />
|
||||||
|
</files>
|
||||||
|
<type value="NONE" />
|
||||||
|
<user value="" />
|
||||||
|
<token-type value="" />
|
||||||
|
<token-config-file value="" />
|
||||||
|
<token-profile value="" />
|
||||||
|
<session-user value="" />
|
||||||
|
</database>
|
||||||
|
<properties>
|
||||||
|
<auto-commit value="false" />
|
||||||
|
</properties>
|
||||||
|
<ssh-settings>
|
||||||
|
<active value="false" />
|
||||||
|
<proxy-host value="" />
|
||||||
|
<proxy-port value="22" />
|
||||||
|
<proxy-user value="" />
|
||||||
|
<auth-type value="PASSWORD" />
|
||||||
|
<key-file value="" />
|
||||||
|
</ssh-settings>
|
||||||
|
<ssl-settings>
|
||||||
|
<active value="false" />
|
||||||
|
<certificate-authority-file value="" />
|
||||||
|
<client-certificate-file value="" />
|
||||||
|
<client-key-file value="" />
|
||||||
|
</ssl-settings>
|
||||||
|
<details>
|
||||||
|
<charset value="UTF-8" />
|
||||||
|
<session-management value="true" />
|
||||||
|
<ddl-file-binding value="true" />
|
||||||
|
<database-logging value="true" />
|
||||||
|
<connect-automatically value="true" />
|
||||||
|
<restore-workspace value="true" />
|
||||||
|
<restore-workspace-deep value="false" />
|
||||||
|
<environment-type value="default" />
|
||||||
|
<connectivity-timeout value="30" />
|
||||||
|
<idle-time-to-disconnect value="30" />
|
||||||
|
<idle-time-to-disconnect-pool value="5" />
|
||||||
|
<credential-expiry-time value="10" />
|
||||||
|
<max-connection-pool-size value="7" />
|
||||||
|
<alternative-statement-delimiter value="" />
|
||||||
|
</details>
|
||||||
|
<debugger>
|
||||||
|
<compile-dependencies value="true" />
|
||||||
|
<tcp-driver-tunneling value="false" />
|
||||||
|
<tcp-host-address value="" />
|
||||||
|
<tcp-port-from value="4000" />
|
||||||
|
<tcp-port-to value="4999" />
|
||||||
|
<debugger-type value="ASK" />
|
||||||
|
</debugger>
|
||||||
|
<object-filters hide-empty-schemas="false" hide-pseudo-columns="false" hide-audit-columns="false">
|
||||||
|
<object-filters />
|
||||||
|
<object-type-filter use-master-settings="true">
|
||||||
|
<object-type name="SCHEMA" enabled="true" />
|
||||||
|
<object-type name="USER" enabled="true" />
|
||||||
|
<object-type name="ROLE" enabled="true" />
|
||||||
|
<object-type name="PRIVILEGE" enabled="true" />
|
||||||
|
<object-type name="CHARSET" enabled="true" />
|
||||||
|
<object-type name="TABLE" enabled="true" />
|
||||||
|
<object-type name="VIEW" enabled="true" />
|
||||||
|
<object-type name="MATERIALIZED_VIEW" enabled="true" />
|
||||||
|
<object-type name="NESTED_TABLE" enabled="true" />
|
||||||
|
<object-type name="COLUMN" enabled="true" />
|
||||||
|
<object-type name="INDEX" enabled="true" />
|
||||||
|
<object-type name="CONSTRAINT" enabled="true" />
|
||||||
|
<object-type name="DATASET_TRIGGER" enabled="true" />
|
||||||
|
<object-type name="DATABASE_TRIGGER" enabled="true" />
|
||||||
|
<object-type name="SYNONYM" enabled="true" />
|
||||||
|
<object-type name="SEQUENCE" enabled="true" />
|
||||||
|
<object-type name="PROCEDURE" enabled="true" />
|
||||||
|
<object-type name="FUNCTION" enabled="true" />
|
||||||
|
<object-type name="PACKAGE" enabled="true" />
|
||||||
|
<object-type name="TYPE" enabled="true" />
|
||||||
|
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
|
||||||
|
<object-type name="ARGUMENT" enabled="true" />
|
||||||
|
<object-type name="JAVA_CLASS" enabled="true" />
|
||||||
|
<object-type name="JAVA_INNER_CLASS" enabled="true" />
|
||||||
|
<object-type name="JAVA_FIELD" enabled="true" />
|
||||||
|
<object-type name="JAVA_METHOD" enabled="true" />
|
||||||
|
<object-type name="DIMENSION" enabled="true" />
|
||||||
|
<object-type name="CLUSTER" enabled="true" />
|
||||||
|
<object-type name="DBLINK" enabled="true" />
|
||||||
|
<object-type name="CREDENTIAL" enabled="true" />
|
||||||
|
<object-type name="AI_PROFILE" enabled="true" />
|
||||||
|
</object-type-filter>
|
||||||
|
</object-filters>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
<browser-settings>
|
<browser-settings>
|
||||||
<general>
|
<general>
|
||||||
<display-mode value="TABBED" />
|
<display-mode value="TABBED" />
|
||||||
|
@ -426,4 +567,8 @@
|
||||||
</environment>
|
</environment>
|
||||||
</general-settings>
|
</general-settings>
|
||||||
</component>
|
</component>
|
||||||
|
<component name="DBNavigator.Project.StatementExecutionManager">
|
||||||
|
<execution-variables />
|
||||||
|
<execution-variable-types />
|
||||||
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
|
@ -7,6 +8,15 @@ android {
|
||||||
namespace = "com.android.grape"
|
namespace = "com.android.grape"
|
||||||
compileSdk = 35
|
compileSdk = 35
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
getByName("release") {
|
||||||
|
storeFile = file("key.jks")
|
||||||
|
storePassword = "androidgrape"
|
||||||
|
keyAlias = "key0"
|
||||||
|
keyPassword = "androidgrape"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.android.grape"
|
applicationId = "com.android.grape"
|
||||||
minSdk = 23
|
minSdk = 23
|
||||||
|
|
|
@ -18,6 +18,7 @@ import com.android.grape.util.MockTools
|
||||||
import com.android.grape.util.NotificationPermissionHandler
|
import com.android.grape.util.NotificationPermissionHandler
|
||||||
import com.android.grape.util.ScriptUtils.registerScriptResultReceiver
|
import com.android.grape.util.ScriptUtils.registerScriptResultReceiver
|
||||||
import com.android.grape.util.ScriptUtils.unregisterScriptResultReceiver
|
import com.android.grape.util.ScriptUtils.unregisterScriptResultReceiver
|
||||||
|
import com.android.grape.util.ShellUtil
|
||||||
import com.android.grape.util.StoragePermissionHelper
|
import com.android.grape.util.StoragePermissionHelper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -75,8 +75,8 @@ data class Device(
|
||||||
var nativeDir: Boolean = false,
|
var nativeDir: Boolean = false,
|
||||||
var network: String = "",
|
var network: String = "",
|
||||||
var noRcLatency: Boolean = false,
|
var noRcLatency: Boolean = false,
|
||||||
@SerializedName("open_referrer")
|
// @SerializedName("open_referrer")//todo string or object
|
||||||
var openReferrer: String = "",
|
// var openReferrer: String = "",
|
||||||
@SerializedName("open_referrerReset")
|
@SerializedName("open_referrerReset")
|
||||||
var openReferrerReset: Int = 0,
|
var openReferrerReset: Int = 0,
|
||||||
var opener: String = "",
|
var opener: String = "",
|
||||||
|
|
|
@ -23,8 +23,9 @@ import com.android.grape.sai.prefers.PreferencesHelper
|
||||||
import com.android.grape.sai.rootless.AndroidPackageInstallerError
|
import com.android.grape.sai.rootless.AndroidPackageInstallerError
|
||||||
import com.android.grape.sai.shell.MiuiUtils
|
import com.android.grape.sai.shell.MiuiUtils
|
||||||
import com.android.grape.sai.shell.Shell
|
import com.android.grape.sai.shell.Shell
|
||||||
import com.android.grape.util.InstallUtils.setInstallRet
|
import com.android.grape.util.FileUtils
|
||||||
import com.android.grape.util.TaskUtils
|
import com.android.grape.util.TaskUtils
|
||||||
|
import com.android.grape.util.TaskUtils.setInstallRet
|
||||||
import com.blankj.utilcode.util.LogUtils
|
import com.blankj.utilcode.util.LogUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
|
@ -137,7 +138,7 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
|
||||||
}
|
}
|
||||||
androidSessionId = createSession()
|
androidSessionId = createSession()
|
||||||
//todo params.apkSource().apkLocalPath?
|
//todo params.apkSource().apkLocalPath?
|
||||||
val path = "/sdcard/apks/${recordPackageName}"
|
val path = "${FileUtils.CACHE_PATH}${recordPackageName}"
|
||||||
val file = File(path)
|
val file = File(path)
|
||||||
|
|
||||||
val files = file.listFiles()
|
val files = file.listFiles()
|
||||||
|
|
|
@ -111,8 +111,8 @@ class RootlessSaiPackageInstaller private constructor(c: Context) :
|
||||||
val callbackIntent: Intent =
|
val callbackIntent: Intent =
|
||||||
Intent(RootlessSaiPiBroadcastReceiver.ACTION_DELIVER_PI_EVENT)
|
Intent(RootlessSaiPiBroadcastReceiver.ACTION_DELIVER_PI_EVENT)
|
||||||
val pendingIntent = PendingIntent.getBroadcast(context, 0, callbackIntent,
|
val pendingIntent = PendingIntent.getBroadcast(context, 0, callbackIntent,
|
||||||
0)
|
PendingIntent.FLAG_IMMUTABLE)
|
||||||
session!!.commit(pendingIntent.intentSender)
|
session.commit(pendingIntent.intentSender)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, e)
|
Log.w(TAG, e)
|
||||||
|
|
|
@ -609,7 +609,7 @@ object AppUtils {
|
||||||
) {
|
) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
downloadFile(url, "/sdcard/apks/$recordFileName")
|
downloadFile(url, "${FileUtils.CACHE_PATH}$recordFileName")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
|
@ -128,11 +128,11 @@ object BackupUtils {
|
||||||
*/
|
*/
|
||||||
fun killRecordProcess(context: Context?, packageName: String?) {
|
fun killRecordProcess(context: Context?, packageName: String?) {
|
||||||
Log.i("BackupUtils", "start killRecordProcess :$packageName")
|
Log.i("BackupUtils", "start killRecordProcess :$packageName")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val cmd = "am force-stop $packageName"
|
val cmd = "am force-stop $packageName"
|
||||||
Log.i("BackupUtils", "killRecordProcess-> cmd:$cmd")
|
Log.i("BackupUtils", "killRecordProcess-> cmd:$cmd")
|
||||||
MockTools.exec(cmd)
|
ShellUtil.execRootCmdAndGetResult(cmd)
|
||||||
|
// MockTools.exec(cmd)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,11 +71,56 @@ object DeviceUtils {
|
||||||
* @param context
|
* @param context
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
fun getUserAndGroupSh(context: Context, packageName: String?, string: String): String {
|
fun getUserAndGroupSh(context: Context, packageName: String?, txtFileName: String): String {
|
||||||
return getUserAndGroupSh(
|
try {
|
||||||
context,
|
// String txtFileName = getRecordTxtFileName(context);
|
||||||
recordPackageName, getRecordTxtFileName(context)
|
|
||||||
|
val cmd = "ls /data/data -l | grep $packageName"
|
||||||
|
val result = MockTools.execRead(cmd)
|
||||||
|
|
||||||
|
val txtFile = File(txtFileName)
|
||||||
|
|
||||||
|
if (result.length > 20) {
|
||||||
|
val contents = result
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"getUserAndGroupSh file->$txtFileName; contents:$contents"
|
||||||
)
|
)
|
||||||
|
if (contents.length > 0) {
|
||||||
|
val arr =
|
||||||
|
contents.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
|
var userAndGroup = ""
|
||||||
|
for (i in arr.indices) {
|
||||||
|
val line = arr[i]
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"getUserAndGroup: line=$line"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (line.endsWith(" $packageName")) {
|
||||||
|
val arr1 = line.split(" ".toRegex()).dropLastWhile { it.isEmpty() }
|
||||||
|
.toTypedArray()
|
||||||
|
for (s1 in arr1) {
|
||||||
|
if (s1.startsWith("u0_")) {
|
||||||
|
val user = s1
|
||||||
|
userAndGroup = "$user:$user"
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"getUserAndGroupSh userAndGroup:$userAndGroup"
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return userAndGroup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.android.grape.MainApplication
|
||||||
import com.android.grape.data.AppState.apkDir
|
import com.android.grape.data.AppState.apkDir
|
||||||
import com.android.grape.data.AppState.monitorDir
|
import com.android.grape.data.AppState.monitorDir
|
||||||
import com.android.grape.data.AppState.recordFileName
|
import com.android.grape.data.AppState.recordFileName
|
||||||
|
@ -43,6 +44,7 @@ object FileUtils {
|
||||||
public const val BUFFER_SIZE = 1024 * 1024 //1M Byte
|
public const val BUFFER_SIZE = 1024 * 1024 //1M Byte
|
||||||
|
|
||||||
public var name: String? = null
|
public var name: String? = null
|
||||||
|
val CACHE_PATH = "${MainApplication.instance.cacheDir.absolutePath}/"
|
||||||
|
|
||||||
fun delFiles(context: Context?) {
|
fun delFiles(context: Context?) {
|
||||||
Log.i("TaskUtils", "start to delFiles : " + startInstallTime)
|
Log.i("TaskUtils", "start to delFiles : " + startInstallTime)
|
||||||
|
@ -63,6 +65,14 @@ object FileUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun getRecordDataFileName(context: Context): String {
|
||||||
|
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getRecordSdcardApkVerFileName(context: Context): String {
|
||||||
|
return "${FileUtils.CACHE_PATH}" + recordFileName
|
||||||
|
}
|
||||||
|
|
||||||
public fun forceCreteDir(file: File) {
|
public fun forceCreteDir(file: File) {
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
val parent = file.parentFile
|
val parent = file.parentFile
|
||||||
|
@ -78,6 +88,51 @@ object FileUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun getSessionTxtFileName(context: Context): String {
|
||||||
|
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/sessionTxt.txt"
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getRecordTxtFileName(context: Context): String {
|
||||||
|
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".txt"
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getSelfRecordTxtFileName(context: Context): String {
|
||||||
|
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + context.packageName + ".txt"
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getRecordApkVerFileName(context: Context): String {
|
||||||
|
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordFileName
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public fun getRecordApkFileName(context: Context): String {
|
||||||
|
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".apk"
|
||||||
|
}
|
||||||
|
public fun makeFile(fileName: String) {
|
||||||
|
var PrintWriter: PrintWriter? = null
|
||||||
|
var process: Process? = null
|
||||||
|
try {
|
||||||
|
process = Runtime.getRuntime().exec("su")
|
||||||
|
PrintWriter = PrintWriter(process.outputStream)
|
||||||
|
//String cmd = "cd " + path+" \n";
|
||||||
|
val cmd = "touch $fileName"
|
||||||
|
Log.i("TaskUtils", "makefile-> cmd:$cmd")
|
||||||
|
PrintWriter.println(cmd)
|
||||||
|
|
||||||
|
PrintWriter.flush()
|
||||||
|
PrintWriter.close()
|
||||||
|
val value = process.waitFor()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} finally {
|
||||||
|
process?.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getRecordListTxtFileName(context: Context): String {
|
||||||
|
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".list.txt"
|
||||||
|
}
|
||||||
|
|
||||||
public fun forceMakeDir(file: File) {
|
public fun forceMakeDir(file: File) {
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
val parent = file.parentFile
|
val parent = file.parentFile
|
||||||
|
|
|
@ -12,7 +12,7 @@ object MockTools {
|
||||||
fun exec(cmd: String): String {
|
fun exec(cmd: String): String {
|
||||||
var retString = ""
|
var retString = ""
|
||||||
try {
|
try {
|
||||||
retString = ShellUtils.execRootCmdAndGetResult(cmd)
|
retString = ShellUtil.execRootCmdAndGetResult(cmd)
|
||||||
// //创建socket
|
// //创建socket
|
||||||
// val myCmd = "SU|$cmd"
|
// val myCmd = "SU|$cmd"
|
||||||
//
|
//
|
||||||
|
@ -41,7 +41,7 @@ object MockTools {
|
||||||
fun execRead(cmd: String): String {
|
fun execRead(cmd: String): String {
|
||||||
var retString = ""
|
var retString = ""
|
||||||
try {
|
try {
|
||||||
retString = ShellUtils.execRootCmdAndGetResult(cmd)
|
retString = ShellUtil.execRootCmdAndGetResult(cmd)
|
||||||
// //创建socket
|
// //创建socket
|
||||||
// val myCmd = "SU_1|$cmd"
|
// val myCmd = "SU_1|$cmd"
|
||||||
//
|
//
|
||||||
|
|
|
@ -18,6 +18,7 @@ import com.android.grape.net.AfClient.downloadFile
|
||||||
import com.android.grape.receiver.ScriptReceiver
|
import com.android.grape.receiver.ScriptReceiver
|
||||||
import com.android.grape.util.ShellUtils.delFileSh
|
import com.android.grape.util.ShellUtils.delFileSh
|
||||||
import com.android.grape.util.ShellUtils.unzipAPkSh
|
import com.android.grape.util.ShellUtils.unzipAPkSh
|
||||||
|
import com.android.grape.util.ShellUtils.unzipScriptSh
|
||||||
import com.blankj.utilcode.util.LogUtils
|
import com.blankj.utilcode.util.LogUtils
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,7 +53,7 @@ object ScriptUtils {
|
||||||
Log.i("TaskUtils", "execDownScript isDownload : $isDownload")
|
Log.i("TaskUtils", "execDownScript isDownload : $isDownload")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
unzipAPkSh(script_path, "/sdcard/script/")
|
unzipScriptSh(script_path, "/sdcard/script/")
|
||||||
delFileSh(script_path)
|
delFileSh(script_path)
|
||||||
}
|
}
|
||||||
return isDownload
|
return isDownload
|
||||||
|
|
|
@ -0,0 +1,348 @@
|
||||||
|
package com.android.grape.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.blankj.utilcode.util.LogUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class ShellUtil {
|
||||||
|
|
||||||
|
public static String getPackagePath(Context context, String packageName) {
|
||||||
|
try {
|
||||||
|
PackageManager pm = context.getPackageManager();
|
||||||
|
ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
|
||||||
|
return appInfo.sourceDir; // 返回 APK 的完整路径
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void exec(String cmd) {
|
||||||
|
try {
|
||||||
|
LogUtils.d(Log.INFO, "ShellUtils", "Executing command: " + cmd, null);
|
||||||
|
Process process = Runtime.getRuntime().exec(cmd);
|
||||||
|
process.waitFor();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Error executing command: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getPid(Process p) {
|
||||||
|
int pid = -1;
|
||||||
|
try {
|
||||||
|
Field f = p.getClass().getDeclaredField("pid");
|
||||||
|
f.setAccessible(true);
|
||||||
|
pid = f.getInt(p);
|
||||||
|
f.setAccessible(false);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
pid = -1;
|
||||||
|
}
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasBin(String binName) {
|
||||||
|
// 验证 binName 是否符合规则
|
||||||
|
if (binName == null || binName.isEmpty()) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Invalid bin name",null);
|
||||||
|
throw new IllegalArgumentException("Bin name cannot be null or empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (char c : binName.toCharArray()) {
|
||||||
|
if (!Character.isLetterOrDigit(c) && c != '.' && c != '_' && c != '-') {
|
||||||
|
throw new IllegalArgumentException("Invalid bin name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 PATH 环境变量
|
||||||
|
String pathEnv = System.getenv("PATH");
|
||||||
|
if (pathEnv == null) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "PATH environment variable is not available", null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用适合当前系统的路径分隔符分割路径
|
||||||
|
String[] paths = pathEnv.split(File.pathSeparator);
|
||||||
|
for (String path : paths) {
|
||||||
|
File file = new File(path, binName); // 使用 File 构造完整路径
|
||||||
|
try {
|
||||||
|
// 检查文件是否可执行
|
||||||
|
if (file.canExecute()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Security exception occurred while checking: " + file.getAbsolutePath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果未找到可执行文件,返回 false
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String execRootCmdAndGetResult(String cmd) {
|
||||||
|
Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: " + cmd);
|
||||||
|
|
||||||
|
Process process = null;
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(2);
|
||||||
|
|
||||||
|
try {
|
||||||
|
process = Runtime.getRuntime().exec("vu");
|
||||||
|
|
||||||
|
try (OutputStream os = new BufferedOutputStream(process.getOutputStream());
|
||||||
|
InputStream is = process.getInputStream();
|
||||||
|
InputStream errorStream = process.getErrorStream();
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
|
||||||
|
BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream, StandardCharsets.UTF_8))) {
|
||||||
|
executor.submit(() -> {
|
||||||
|
String line;
|
||||||
|
try {
|
||||||
|
while ((line = errorReader.readLine()) != null) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Shell Error: " + line, null);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Error while reading process error stream: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
os.write((cmd + "\n").getBytes());
|
||||||
|
os.write("exit\n".getBytes());
|
||||||
|
os.flush();
|
||||||
|
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
Log.d("ShellUtils", "Shell Output: " + line);
|
||||||
|
output.append(line).append("\n");
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
if (!process.waitFor(10, TimeUnit.SECONDS)) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Process execution timed out. Destroying process.", null);
|
||||||
|
process.destroyForcibly();
|
||||||
|
throw new RuntimeException("Shell command execution timeout.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
process.exitValue();
|
||||||
|
break;
|
||||||
|
} catch (IllegalThreadStateException e) {
|
||||||
|
if (System.currentTimeMillis() - startTime > 10000) { // 10 seconds
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Process execution timed out (manual tracking). Destroying process.", null);
|
||||||
|
process.destroy();
|
||||||
|
throw new RuntimeException("Shell command execution timeout.");
|
||||||
|
}
|
||||||
|
Thread.sleep(100); // Sleep briefly before re-checking
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output.toString().trim();
|
||||||
|
}
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Command execution failed: " + e.getMessage(), e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return "Error: " + e.getMessage();
|
||||||
|
} finally {
|
||||||
|
if (process != null) {
|
||||||
|
Log.d("ShellUtils", "Finalizing process. Attempting to destroy it.");
|
||||||
|
process.destroy();
|
||||||
|
}
|
||||||
|
executor.shutdown();
|
||||||
|
Log.d("ShellUtils", "Executor service shut down.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void execRootCmd(String cmd) {
|
||||||
|
// 校验命令是否安全
|
||||||
|
if (!isCommandSafe(cmd)) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Unsafe command, aborting.", null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<String> cmds = new ArrayList<>();
|
||||||
|
cmds.add(cmd);
|
||||||
|
|
||||||
|
// 使用同步锁保护线程安全
|
||||||
|
synchronized (ShellUtils.class) {
|
||||||
|
try {
|
||||||
|
List<String> results = execRootCmds(cmds);
|
||||||
|
// 判断是否需要打印输出,仅用于开发调试阶段
|
||||||
|
for (String result : results) {
|
||||||
|
Log.d("ShellUtils", "Command Result: " + result);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Unexpected error: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isCommandSafe(String cmd) {
|
||||||
|
// 检查空值和空字符串
|
||||||
|
if (cmd == null || cmd.trim().isEmpty()) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Rejected command: empty or null value.", null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查非法字符
|
||||||
|
if (!cmd.matches("^[a-zA-Z0-9._/:\\-~`'\" *|]+$")) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Rejected command due to illegal characters: " + cmd, null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查多命令(逻辑运算符限制)
|
||||||
|
if (cmd.contains("&&") || cmd.contains("||")) {
|
||||||
|
Log.d("ShellUtils", "Command contains logical operators.");
|
||||||
|
if (!isExpectedMultiCommand(cmd)) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Rejected command due to prohibited structure: " + cmd, null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 路径遍历保护
|
||||||
|
if (cmd.contains("../") || cmd.contains("..\\")) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Rejected command due to path traversal attempt: " + cmd, null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 命令长度限制
|
||||||
|
if (cmd.startsWith("tar") && cmd.length() > 800) { // 特定命令支持更长长度
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Command rejected due to excessive length.", null);
|
||||||
|
return false;
|
||||||
|
} else if (cmd.length() > 500) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Command rejected due to excessive length.", null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("ShellUtils", "Command passed safety checks: " + cmd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 附加方法:检查多命令是否符合预期
|
||||||
|
private static boolean isExpectedMultiCommand(String cmd) {
|
||||||
|
// 判断是否为允许的命令组合,比如 `cd` 或 `tar` 组合命令
|
||||||
|
return cmd.matches("^cd .+ && (tar|zip|cp).+");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> execRootCmds(List<String> cmds) {
|
||||||
|
List<String> results = new ArrayList<>();
|
||||||
|
Process process = null;
|
||||||
|
try {
|
||||||
|
// 初始化 Shell 环境
|
||||||
|
process = hasBin("su") ? Runtime.getRuntime().exec("su") : Runtime.getRuntime().exec("sh");
|
||||||
|
|
||||||
|
// 启动读取线程
|
||||||
|
Process stdProcess = process;
|
||||||
|
Thread stdThread = new Thread(() -> {
|
||||||
|
try (BufferedReader stdReader = new BufferedReader(new InputStreamReader(stdProcess.getInputStream()))) {
|
||||||
|
List<String> localResults = new ArrayList<>();
|
||||||
|
String line;
|
||||||
|
while ((line = stdReader.readLine()) != null) {
|
||||||
|
localResults.add(line);
|
||||||
|
Log.d("ShellUtils", "Stdout: " + line);
|
||||||
|
}
|
||||||
|
synchronized (results) {
|
||||||
|
results.addAll(localResults);
|
||||||
|
}
|
||||||
|
} catch (IOException ioException) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Error reading stdout", ioException);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Process finalProcess = process;
|
||||||
|
Thread errThread = new Thread(() -> {
|
||||||
|
try (BufferedReader errReader = new BufferedReader(new InputStreamReader(finalProcess.getErrorStream()))) {
|
||||||
|
String line;
|
||||||
|
while ((line = errReader.readLine()) != null) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Stderr: " + line, null);
|
||||||
|
}
|
||||||
|
} catch (IOException ioException) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Error reading stderr", ioException);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启动子线程
|
||||||
|
stdThread.start();
|
||||||
|
errThread.start();
|
||||||
|
|
||||||
|
try (OutputStream os = process.getOutputStream()) {
|
||||||
|
for (String cmd : cmds) {
|
||||||
|
// if (!isCommandSafe(cmd)) {
|
||||||
|
// Log.w("ShellUtils", "Skipping unsafe command: " + cmd);
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
os.write((cmd + "\n").getBytes());
|
||||||
|
Log.d("ShellUtils", "Executing command: " + cmd);
|
||||||
|
}
|
||||||
|
os.write("exit\n".getBytes());
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 执行命令、等待解决
|
||||||
|
process.waitFor();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt(); // 恢复中断
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Error executing commands", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待子线程完成
|
||||||
|
stdThread.join();
|
||||||
|
errThread.join();
|
||||||
|
|
||||||
|
} catch (InterruptedIOException e) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Error reading stdout: Interrupted", e);
|
||||||
|
Thread.currentThread().interrupt(); // 恢复线程的中断状态
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Error executing commands", e);
|
||||||
|
} finally {
|
||||||
|
if (process != null) {
|
||||||
|
process.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasRootAccess() {
|
||||||
|
// 记录是否出现安全异常
|
||||||
|
boolean hasSecurityError = false;
|
||||||
|
|
||||||
|
// 检查二进制文件
|
||||||
|
String[] binariesToCheck = {"su", "xu", "vu"};
|
||||||
|
for (String bin : binariesToCheck) {
|
||||||
|
try {
|
||||||
|
if (hasBin(bin)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
hasSecurityError = true;
|
||||||
|
LogUtils.d(Log.ERROR, "ShellUtils", "Security exception while checking: " + bin, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断如果发生安全异常则反馈问题
|
||||||
|
if (hasSecurityError) {
|
||||||
|
Log.w("ShellUtils", "Potential security error detected while checking root access.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有找到合法的二进制文件,则认为无root权限
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -558,7 +558,7 @@ object ShellUtils {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unzipAPkSh(zipFileName: String, dataDir: String) {
|
fun unzipScriptSh(zipFileName: String, dataDir: String) {
|
||||||
try {
|
try {
|
||||||
val file = File(dataDir)
|
val file = File(dataDir)
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
|
@ -571,6 +571,19 @@ object ShellUtils {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fun unzipAPkSh(zipFileName: String, dataDir: String) {
|
||||||
|
try {
|
||||||
|
val file = File(dataDir)
|
||||||
|
if (!file.exists()) {
|
||||||
|
FileUtils.forceCreteDir(file)
|
||||||
|
}
|
||||||
|
val cmd = "unzip -o " + FileUtils.CACHE_PATH + File(zipFileName).name + " -d " + dataDir
|
||||||
|
val unzipResult = MockTools.execRead(cmd)
|
||||||
|
Log.i("ShellUtils", "unZipFileSh-> cmd:$unzipResult")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行shell命令以检索指定文件的内容。
|
* 执行shell命令以检索指定文件的内容。
|
||||||
|
|
|
@ -48,16 +48,31 @@ import com.android.grape.manager.ConfigManager.initDefaultProxyJo
|
||||||
import com.android.grape.util.AppUtils.execTargetApp
|
import com.android.grape.util.AppUtils.execTargetApp
|
||||||
import com.android.grape.util.AppUtils.getApkPackageName
|
import com.android.grape.util.AppUtils.getApkPackageName
|
||||||
import com.android.grape.util.AppUtils.setTopApp
|
import com.android.grape.util.AppUtils.setTopApp
|
||||||
import com.android.grape.util.ContextUtils.getRecordDataFileName
|
import com.android.grape.util.ContextUtils.getBaseFilesDir
|
||||||
import com.android.grape.util.FileUtils.forceMakeDir
|
import com.android.grape.util.FileUtils.forceMakeDir
|
||||||
|
import com.android.grape.util.FileUtils.getRecordDataFileName
|
||||||
import com.android.grape.util.JsonUtils.execSetJson
|
import com.android.grape.util.JsonUtils.execSetJson
|
||||||
import com.blankj.utilcode.util.LogUtils
|
import com.blankj.utilcode.util.LogUtils
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.util.Locale
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `util`类用作封装多种方法和属性的实用程序类
|
||||||
|
* 用于管理与应用程序相关的数据并执行各种操作。此课程提供
|
||||||
|
* 诸如检索和更新应用程序元数据,管理日志,处理等功能
|
||||||
|
* 备份要求,跟踪用户和组信息以及与shell命令进行交互。
|
||||||
|
*
|
||||||
|
* 它还揭示了使用JSON数据,时间戳,代理配置,软件包的方法
|
||||||
|
* 所有权和其他特定于应用程序的属性。该课程中的许多方法都相关
|
||||||
|
* 将应用程序状态管理和设备上的特定命令执行。
|
||||||
|
*
|
||||||
|
* 注意:此类包括用于初始化和内部配置的方法
|
||||||
|
* 不直接暴露于外部使用。
|
||||||
|
*/
|
||||||
object TaskUtils {
|
object TaskUtils {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -113,6 +128,16 @@ object TaskUtils {
|
||||||
return "normal"
|
return "normal"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setInstallRet(installRetV: Boolean) {
|
||||||
|
Log.i("TaskUtils", "setInstallRet: $installRetV")
|
||||||
|
installRet = installRetV
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isInstallRet(): Boolean {
|
||||||
|
return installRet == true
|
||||||
|
}
|
||||||
|
|
||||||
fun setAfLog(afLogV: String) {
|
fun setAfLog(afLogV: String) {
|
||||||
afLog = afLogV
|
afLog = afLogV
|
||||||
}
|
}
|
||||||
|
@ -144,14 +169,13 @@ object TaskUtils {
|
||||||
return "v1060"
|
return "v1060"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 该函数execReloginTask的功能是执行重新登录任务,主要逻辑如下:
|
构建请求参数:获取设备唯一 ID,拼接请求 URL。
|
||||||
* 初始化配置并构造请求URL及参数;
|
发送 POST 请求:调用 MyPost.postData 向服务器提交请求。
|
||||||
* 发送POST网络请求到指定地址;
|
处理响应结果:
|
||||||
* 解析返回结果:
|
成功时设置权限、解析 JSON 数据并设置 clickTime。
|
||||||
* 若成功(code == 1),执行文件权限修改并设置相关数据;
|
失败或异常时调用 setFinish 结束任务。
|
||||||
* 若失败,记录错误日志并调用setFinish;
|
|
||||||
* 捕获异常并处理错误。
|
|
||||||
*/
|
*/
|
||||||
fun execReloginTask(context: Context) {
|
fun execReloginTask(context: Context) {
|
||||||
ConfigManager.init()
|
ConfigManager.init()
|
||||||
|
@ -196,13 +220,11 @@ object TaskUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 该函数execInstallTask的功能是执行安装任务,主要逻辑如下:
|
* 初始化并构建请求参数:获取设备唯一 ID(ANDROID_ID),拼接请求 URL。
|
||||||
* 初始化配置并构建请求地址与参数;
|
* 发送 POST 请求:通过 MyPost.postData 向服务器提交安装请求。
|
||||||
* 发送POST网络请求到指定URL;
|
* 处理响应结果:
|
||||||
* 解析返回结果:
|
* 成功时解析 JSON 数据、设置权限,并触发 clickTime 为 1 表示需执行点击操作。
|
||||||
* 若成功(code == 1),修改文件权限并执行设置JSON数据操作;
|
* 失败或异常时调用 setFinish 结束任务。
|
||||||
* 若失败,记录错误日志并调用setFinish;
|
|
||||||
* 捕获异常并记录错误信息。
|
|
||||||
*/
|
*/
|
||||||
fun execInstallTask(context: Context) {
|
fun execInstallTask(context: Context) {
|
||||||
ConfigManager.init()
|
ConfigManager.init()
|
||||||
|
@ -244,12 +266,10 @@ object TaskUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 该函数execTask的功能是随机执行重新登录或安装任务,具体逻辑如下:
|
* 根据随机条件执行任务。
|
||||||
* 增加随机数计数器;
|
* 在运行Relogin任务和安装任务之间交替。
|
||||||
* 删除插件文件;
|
*
|
||||||
* 根据计数器决定执行:
|
* @param上下文执行任务执行的上下文
|
||||||
* 每3次调用执行一次execReloginTask;
|
|
||||||
* 其余情况执行execInstallTask。
|
|
||||||
*/
|
*/
|
||||||
fun execTask(context: Context) {
|
fun execTask(context: Context) {
|
||||||
nRandom++
|
nRandom++
|
||||||
|
|
Loading…
Reference in New Issue