refactor(app): 重构应用安装和管理相关功能

- 移除 AdvertisingIdClient 类
- 新增 AfClient 类,实现信息设置和文件下载功能
- 新增 AppState 类,用于存储应用状态信息
- 新增 AppUtils 类,提供应用安装、卸载、检查等工具方法
This commit is contained in:
yjj38 2025-07-17 11:18:48 +08:00
parent 6e51067b07
commit 66ee32d0e2
36 changed files with 4449 additions and 3936 deletions

View File

@ -12,13 +12,13 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.android.grape.databinding.ActivityMainBinding
import com.android.grape.job.MonitorService
import com.android.grape.service.SocketServer
import com.android.grape.util.BackupUtils.killRecordProcess
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.ScriptUtils.registerScriptResultReceiver
import com.android.grape.util.ScriptUtils.unregisterScriptResultReceiver
import com.android.grape.util.StoragePermissionHelper
import com.android.grape.util.Util.killRecordProcess
/**
* public class MainActivity extends AppCompatActivity
@ -68,7 +68,7 @@ class MainActivity : AppCompatActivity() {
insets
}
checkPermission()
ScriptUtil.registerScriptResultReceiver()
registerScriptResultReceiver()
viewBinding.start.setOnClickListener {
MonitorService.onEvent(MainApplication.instance)
}
@ -86,7 +86,7 @@ class MainActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
ClashUtil.unregisterReceiver(this)
ScriptUtil.unregisterScriptResultReceiver()
unregisterScriptResultReceiver()
}
private fun checkPermission() {

View File

@ -0,0 +1,139 @@
package com.android.grape.data
import android.util.Log
import org.json.JSONObject
/**
* @Time: 2025-15-16 16:15
* @Creator: 初屿贤
* @File: AppState
* @Project: AndroidGrape
* @Description:
*/
// AppState.kt
object AppState {
/**
* 任务json
*/
var taskJson: JSONObject? = null
set
var paramsJson: JSONObject? = null
set
var recordPackageName: String? = null
var recordFileName: String? = null
var trackingLink: String? = null
var recordId: Long = 0L
var proxyIp: String? = null
var proxyPort: Int = 0
var proxyCountry: String? = null
var lang: String? = null
var ua: String? = null
var clickTime: Long = 0L
var installTime: Long = 0L
var installServerTimeFromGP: Long = 0L
var clickServerTimeFromGP: Long = 0L
var lastUpdateTime: Long = 0L
var instalTimeFromGp: Long = 0L
val country: String? = null
var reloginRecordId: Long = 0L
var videoProxy: String = ""
var forwardIp: String? = ""
var afApp: String = ""
var preClickRecordId: Long = 0
var backFileName: String? = null
set
var backFileName1: String? = null
set
var backFileName2: String? = null
set
var isClickRet: Boolean = false
var clickErrReason: String = ""
var delegateIp: String? = null
var isNeedReg: Boolean = false
var isNeedBackup: Boolean = false
var regEmailJson: JSONObject? = null
var isCanAuto: Boolean = false
var canAutoLc: String = ""
var canAutoAtc: String = ""
var backupResult: JSONObject? = null
var backUpServerIp: String = ""
var isCanceled: Boolean = false
var isPaused: Boolean = false
var ctit: Int = 0
set
var keepOpen: Int = 0
var script_path: String = "/sdcard/apks/script.zip"
var fuzzy_domain: String = ""
var fuzzy_proxy: String? = ""
var script_status: Int = 0
var scriptOpenApp: Int = 0
var isNeedRestored: Boolean = false
var logBuffer: StringBuffer = StringBuffer()
set
const val monitorDir = "monitor"
const val apkDir = "apks"
var recordExtraFileName: String? = null
var mainUserAndGroup: String? = null
var nRandom = 0
const val TAG = "IOSTQ:Util"
const val tag = "3"
const val sendRefer = true
const val AUTO_PACKAGENAME: String = "com.play4u.luabox"
const val AUTO_JSPACKAGENAME: String = "org.autojs.autojs6"
const val proxy_packagename: String = "com.tunnelworkshop.postern"
const val AUTO_CLASSNAME: String = "com.cyjh.elfin.activity.SplashActivity"
const val hookPackageName = "com.affsystem.androidhooker"
const val hookAppMainClass = "com.affsystem.androidhooker.MainActivity"
const val monitorFile = "monitor.txt"
const val taskFile = "task.txt"
var defaultAppJo = JSONObject()
var defaultPRoxyJo = JSONObject()
var appVer: String? = null
var appVerCode: Long? = null
val cacheJson: JSONObject? = null
var startInstallTime = 0L
var referer: String? = null
var appDataUrl = ""
var afLog = ""
var installRet: Boolean? = null
var appAfVer = ""
const val TYPE_SUCCESS: Int = 0
const val TYPE_FAILED: Int = 1
const val TYPE_PAUSED: Int = 2
const val TYPE_CANCELED: Int = 3
var tags: String? = null
const val isCollectURL = false
const val testForSetInfo = false
var oldpath: String? = null
var newpath: String? = null
const val apk_path = "/sdcard/Android/data/sperixlabs.proxyme/files/monitor/apks"
var zip_name: String? = null
var baoming: String? = null
var appVersion: String?
get() = appVer
set(appAfVerV) {
Log.i("TaskUtils", "setAppAfV: $appAfVerV")
appVer = appAfVerV
}
var appVersionCode: Long?
get() = appVerCode
set(appVerCode) {
Log.i("TaskUtils", "setAppAfVerCode: $appVerCode")
AppState.appVerCode = appVerCode
}
}

View File

@ -5,8 +5,9 @@ import android.content.Intent
import android.os.Handler
import android.os.Looper
import androidx.core.app.JobIntentService
import com.android.grape.util.ScriptUtil
import com.android.grape.util.Util
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.util.ScriptUtils.execScript
import com.android.grape.util.TaskUtils
class AutoJobService : JobIntentService() {
override fun onHandleWork(intent: Intent) {
@ -14,9 +15,9 @@ class AutoJobService : JobIntentService() {
if (openZzzj) {
Handler(Looper.getMainLooper()).postDelayed({
ScriptUtil.execScript(
execScript(
this@AutoJobService,
"/sdcard/script/${Util.recordPackageName}/main.js"
"/sdcard/script/${recordPackageName}/main.js"
)
}, 2000L)
} else {

View File

@ -4,18 +4,23 @@ import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.core.app.JobIntentService
import com.android.grape.data.AppState.clickErrReason
import com.android.grape.data.AppState.delegateIp
import com.android.grape.data.AppState.isClickRet
import com.android.grape.data.AppState.proxyCountry
import com.android.grape.data.AppState.ua
import com.android.grape.net.MyGet
import com.android.grape.util.Util
import com.android.grape.util.TaskUtils
class CheckIpJobService : JobIntentService() {
override fun onHandleWork(intent: Intent) {
if (checkIp()) {
InstallService.onEvent(this)
} else {
Util.isClickRet = false
Util.setInstallRet(false)
Util.clickErrReason = "networkErr"
Util.setFinish(this)
isClickRet = false
TaskUtils.setInstallRet(false)
clickErrReason = "networkErr"
TaskUtils.setFinish(this)
}
}
@ -25,9 +30,9 @@ class CheckIpJobService : JobIntentService() {
do {
val url = "http://8.218.80.200/myipp"
Log.i(TAG, "start to getIp : " + url + " ; " + Util.ua)
Log.i(TAG, "start to getIp : " + url + " ; " + ua)
val resp: String? = MyGet.getData(url, Util.ua)
val resp: String? = MyGet.getData(url, ua)
Log.i(TAG, "checkIp : $resp")
@ -35,9 +40,9 @@ class CheckIpJobService : JobIntentService() {
try {
val conn =
resp.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (conn.size > 1 && Util.proxyCountry.equals(conn[1])) {
if (conn.size > 1 && proxyCountry.equals(conn[1])) {
val ip = conn[0]
Util.delegateIp = ip
delegateIp = ip
return true
}
} catch (e: Exception) {

View File

@ -2,9 +2,10 @@ package com.android.grape.job
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.core.app.JobIntentService
import com.android.grape.util.Util
import com.android.grape.data.AppState.clickErrReason
import com.android.grape.data.AppState.isClickRet
import com.android.grape.util.TaskUtils
import com.blankj.utilcode.util.LogUtils
class DownloadAppJobService : JobIntentService() {
@ -14,7 +15,7 @@ class DownloadAppJobService : JobIntentService() {
var succ: Boolean = false
try {
succ = Util.execDownload(this)
succ = TaskUtils.execDownload(this)
} catch (e: Exception) {
e.printStackTrace()
}
@ -26,10 +27,10 @@ class DownloadAppJobService : JobIntentService() {
// InstallService.onEvent(this)
StartVpnPortJobService.onEvent(this)
} else {
Util.isClickRet = false
Util.setInstallRet(false)
Util.clickErrReason = "downloadErr"
Util.setFinish(this)
isClickRet = false
TaskUtils.setInstallRet(false)
clickErrReason = "downloadErr"
TaskUtils.setFinish(this)
}
}

View File

@ -6,19 +6,22 @@ import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.app.JobIntentService
import com.android.grape.util.Util
import com.android.grape.data.AppState.isNeedRestored
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.util.AppUtils.installRecord
import com.android.grape.util.TaskUtils
class InstallService : JobIntentService() {
override fun onHandleWork(intent: Intent) {
Log.i(TAG, "onHandleWork ...")
if (Util.installRecord(this)) {
if (installRecord(this)) {
Log.i(TAG, "installRecord succ")
tryNum = 0
Util.setInstallRet(true)
TaskUtils.setInstallRet(true)
println("IOSTQ:isNeedRestored == " + Util.isNeedRestored)
if (Util.isNeedRestored) {
println("IOSTQ:isNeedRestored == " + isNeedRestored)
if (isNeedRestored) {
Handler(Looper.getMainLooper()).postDelayed({
RecoverJobService.onEvent(
this@InstallService
@ -40,9 +43,9 @@ class InstallService : JobIntentService() {
)
}, 5000)
} else { //超过5次安装失败直接取消后续
Log.i(TAG, "install error : " + Util.recordPackageName + " ; " + tryNum)
Log.i(TAG, "install error : " + recordPackageName + " ; " + tryNum)
Util.setFinish(this)
TaskUtils.setFinish(this)
}
}
}

View File

@ -4,9 +4,10 @@ import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.core.app.JobIntentService
import com.android.grape.data.AppState.clickTime
import com.android.grape.util.ClashUtil
import com.android.grape.util.MockTools
import com.android.grape.util.Util
import com.android.grape.util.TaskUtils
/**
@ -46,11 +47,11 @@ class MonitorService : JobIntentService() {
ClashUtil.switchProxyGroup("PROXY", "DIRECT", "http://127.0.0.1:6170")
Thread.sleep(3000)
if (Util.clickTime > 0L) {
Util.clickTime = 0L
if (clickTime > 0L) {
clickTime = 0L
SendCallbackJobService.onEvent(this)
} else {
Util.execTask(applicationContext)
TaskUtils.execTask(applicationContext)
}
} catch (e: Exception) {
e.printStackTrace()

View File

@ -2,25 +2,29 @@ package com.android.grape.job
import android.content.Context
import android.content.Intent
import android.text.TextUtils
import android.util.Log
import androidx.core.app.JobIntentService
import com.android.grape.data.AppState.AUTO_JSPACKAGENAME
import com.android.grape.data.AppState.canAutoLc
import com.android.grape.data.AppState.isCanAuto
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.net.ChangeCallBack
import com.android.grape.provider.DeviceDataAccessor
import com.android.grape.provider.DeviceInfoHelper
import com.android.grape.util.ChangeDeviceInfoUtil
import com.android.grape.util.FileUtils
import com.android.grape.util.MockTools
import com.android.grape.util.ScriptUtils.doScript
import com.android.grape.util.ServiceUtils
import com.android.grape.util.Util
import com.android.grape.util.TaskUtils
import com.android.grape.util.TaskUtils.setFinish
import java.io.IOException
class OpenAppService : JobIntentService() {
override fun onHandleWork(intent: Intent) {
println("IOSTQ:isCanAuto() == " + Util.isCanAuto)
println("IOSTQ:getCanAutoLc() == " + Util.canAutoLc)
Util.recordPackageName?.let {
println("IOSTQ:isCanAuto() == " + isCanAuto)
println("IOSTQ:getCanAutoLc() == " + canAutoLc)
recordPackageName?.let {
ServiceUtils.setEnableApp(it, true)
}
ServiceUtils.setEnableApp("org.mozilla.firefox", true)
@ -31,32 +35,32 @@ class OpenAppService : JobIntentService() {
ChangeDeviceInfoUtil.changeDevice(callBack = object : ChangeCallBack {
override fun changeSuccess() {
runCatching {
if (Util.isCanAuto && Util.canAutoLc.isNotEmpty()) {
if (isCanAuto && canAutoLc.isNotEmpty()) {
val deviceInfo = DeviceDataAccessor.getDeviceInfo(applicationContext, DeviceInfoHelper.getDeviceId())
FileUtils.writePackageName(Util.recordPackageName?:"")
FileUtils.writeDevice(Util.recordPackageName?:"", deviceInfo?: "")
FileUtils.runPlugin(Util.recordPackageName?:"")
MockTools.exec("pm grant ${Util.AUTO_JSPACKAGENAME} android.permission.READ_EXTERNAL_STORAGE") //sdcard权限
MockTools.exec("pm grant ${Util.AUTO_JSPACKAGENAME} android.permission.SYSTEM_ALERT_WINDOW") //悬浮窗权限
MockTools.exec("settings put secure enabled_accessibility_services ${Util.AUTO_JSPACKAGENAME}/${Util.AUTO_JSPACKAGENAME}.core.accessibility.AccessibilityService")
Util.doScript(this@OpenAppService, Util.AUTO_JSPACKAGENAME) //autojs
FileUtils.writePackageName(recordPackageName?:"")
FileUtils.writeDevice(recordPackageName?:"", deviceInfo?: "")
FileUtils.runPlugin(recordPackageName?:"")
MockTools.exec("pm grant ${AUTO_JSPACKAGENAME} android.permission.READ_EXTERNAL_STORAGE") //sdcard权限
MockTools.exec("pm grant ${AUTO_JSPACKAGENAME} android.permission.SYSTEM_ALERT_WINDOW") //悬浮窗权限
MockTools.exec("settings put secure enabled_accessibility_services ${AUTO_JSPACKAGENAME}/${AUTO_JSPACKAGENAME}.core.accessibility.AccessibilityService")
doScript(this@OpenAppService, AUTO_JSPACKAGENAME) //autojs
}else {
Util.setFinish(this@OpenAppService)
setFinish(this@OpenAppService)
}
}.onFailure {
it.printStackTrace()
Util.setFinish(this@OpenAppService)
setFinish(this@OpenAppService)
}
}
override fun changeFailed() {
Util.setFinish(this@OpenAppService)
setFinish(this@OpenAppService)
}
})
} catch (e: IOException) {
e.printStackTrace()
Util.setFinish(this@OpenAppService)
setFinish(this@OpenAppService)
}
// Util.hookOpenApp(this);
}

View File

@ -6,7 +6,8 @@ import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.app.JobIntentService
import com.android.grape.util.Util
import com.android.grape.util.BackupUtils.recoverRecordData
import com.android.grape.util.TaskUtils
class RecoverJobService : JobIntentService() {
override fun onHandleWork(intent: Intent) {
@ -15,7 +16,7 @@ class RecoverJobService : JobIntentService() {
var succ = false
try {
succ = Util.recoverRecordData(this)
succ = recoverRecordData(this)
} catch (e: Exception) {
e.printStackTrace()
}

View File

@ -5,8 +5,37 @@ import android.content.Intent
import android.os.Handler
import android.os.Looper
import androidx.core.app.JobIntentService
import com.android.grape.net.MyPost
import com.android.grape.util.Util
import com.android.grape.data.AppState.afApp
import com.android.grape.data.AppState.backFileName
import com.android.grape.data.AppState.backFileName1
import com.android.grape.data.AppState.backFileName2
import com.android.grape.data.AppState.backupResult
import com.android.grape.data.AppState.clickErrReason
import com.android.grape.data.AppState.clickServerTimeFromGP
import com.android.grape.data.AppState.clickTime
import com.android.grape.data.AppState.delegateIp
import com.android.grape.data.AppState.instalTimeFromGp
import com.android.grape.data.AppState.installServerTimeFromGP
import com.android.grape.data.AppState.installTime
import com.android.grape.data.AppState.isClickRet
import com.android.grape.data.AppState.isNeedBackup
import com.android.grape.data.AppState.isNeedRestored
import com.android.grape.data.AppState.lastUpdateTime
import com.android.grape.data.AppState.logBuffer
import com.android.grape.data.AppState.preClickRecordId
import com.android.grape.data.AppState.recordId
import com.android.grape.data.AppState.reloginRecordId
import com.android.grape.net.AfClient.postData
import com.android.grape.util.AppUtils.getAppAfVer
import com.android.grape.util.DeviceUtils.getMainUserAndGroup
import com.android.grape.util.MyPost
import com.android.grape.util.ShellUtils.chownSh
import com.android.grape.util.ShellUtils.delFileSh
import com.android.grape.util.TaskUtils
import com.android.grape.util.TaskUtils.HkVer
import com.android.grape.util.TaskUtils.getAfLog
import com.android.grape.util.TaskUtils.isInstallRet
import com.android.grape.util.TaskUtils.setFinish
import com.blankj.utilcode.util.LogUtils
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
@ -31,16 +60,16 @@ class SendCallbackJobService : JobIntentService() {
override fun onHandleWork(intent: Intent) {
LogUtils.i(TAG, "onHandleWork")
if (Util.isInstallRet()) {
if (isInstallRet()) {
SendBackup()
if (Util.isNeedRestored) {
if (isNeedRestored) {
sendReloginEvent()
} else {
sendLogEvent()
}
}
Util.setFinish(this@SendCallbackJobService)
setFinish(this@SendCallbackJobService)
}
@ -51,39 +80,39 @@ class SendCallbackJobService : JobIntentService() {
val paramsJo = JSONObject()
val datajson = JSONObject()
if (Util.isNeedBackup) {
if (isNeedBackup) {
val cachejson = JSONObject()
cachejson.put("firstInstallTime", Util.installTime)
cachejson.put("lastUpdateTime", Util.lastUpdateTime)
cachejson.put("installServerTimeFromGP", Util.installServerTimeFromGP)
cachejson.put("clickServerTimeToGP", Util.clickServerTimeFromGP)
cachejson.put("installTimeFromGP", Util.instalTimeFromGp)
cachejson.put("firstInstallTime", installTime)
cachejson.put("lastUpdateTime", lastUpdateTime)
cachejson.put("installServerTimeFromGP", installServerTimeFromGP)
cachejson.put("clickServerTimeToGP", clickServerTimeFromGP)
cachejson.put("installTimeFromGP", instalTimeFromGp)
datajson.put("cacheJson", cachejson)
}
paramsJo.put("recordId", Util.recordId)
paramsJo.put("preClickRecordId", Util.preClickRecordId)
paramsJo.put("recordId", recordId)
paramsJo.put("preClickRecordId", preClickRecordId)
paramsJo.put("dataJson", datajson)
paramsJo.put("reloginRecordId", Util.reloginRecordId)
paramsJo.put("reloginRecordId", reloginRecordId)
val clickInfoJo = JSONObject()
clickInfoJo.put("clickRet", Util.isClickRet)
clickInfoJo.put("clickIp", Util.delegateIp)
clickInfoJo.put("clickTime", Util.clickTime)
clickInfoJo.put("clickErrReason", Util.clickErrReason)
clickInfoJo.put("clickRet", isClickRet)
clickInfoJo.put("clickIp", delegateIp)
clickInfoJo.put("clickTime", clickTime)
clickInfoJo.put("clickErrReason", clickErrReason)
paramsJo.put("clickInfo", clickInfoJo)
val installInfoJo = JSONObject()
installInfoJo.put("installRet", Util.isInstallRet())
installInfoJo.put("installTime", Util.installTime)
installInfoJo.put("installRet", isInstallRet())
installInfoJo.put("installTime", installTime)
paramsJo.put("installInfo", installInfoJo)
if (null != Util.backupResult) {
paramsJo.put("backUpFiles", Util.backupResult)
if (null != backupResult) {
paramsJo.put("backUpFiles", backupResult)
}
val logInfoJo = JSONObject()
logInfoJo.put("afLog", Util.getAfLog() + "\r\n" + Util.logBuffer.toString())
logInfoJo.put("afLog", getAfLog() + "\r\n" + logBuffer.toString())
// logInfoJo.put("setConfLog", Util.getParamsJson());
paramsJo.put("logInfo", logInfoJo)
var params: String? = null
@ -91,7 +120,7 @@ class SendCallbackJobService : JobIntentService() {
var nRetryCount = 0
do {
val ret: String? = MyPost.postData(params.toByteArray(charset("utf-8")), url)
val ret: String? = postData(params.toByteArray(charset("utf-8")), url)
LogUtils.i(TAG, "ret:$ret")
val jo = ret?.let { JSONObject(it) }
if (jo?.getInt("code") == 1) {
@ -113,48 +142,48 @@ class SendCallbackJobService : JobIntentService() {
val paramsJo = JSONObject()
val datajson = JSONObject()
if (Util.isNeedBackup) {
if (isNeedBackup) {
val cachejson = JSONObject()
cachejson.put("firstInstallTime", Util.installTime)
cachejson.put("lastUpdateTime", Util.lastUpdateTime)
cachejson.put("installServerTimeFromGP", Util.installServerTimeFromGP)
cachejson.put("clickServerTimeToGP", Util.clickServerTimeFromGP)
cachejson.put("installTimeFromGP", Util.instalTimeFromGp)
cachejson.put("firstInstallTime", installTime)
cachejson.put("lastUpdateTime", lastUpdateTime)
cachejson.put("installServerTimeFromGP", installServerTimeFromGP)
cachejson.put("clickServerTimeToGP", clickServerTimeFromGP)
cachejson.put("installTimeFromGP", instalTimeFromGp)
datajson.put("cacheJson", cachejson)
}
paramsJo.put("recordId", Util.recordId)
paramsJo.put("preClickRecordId", Util.preClickRecordId)
paramsJo.put("recordId", recordId)
paramsJo.put("preClickRecordId", preClickRecordId)
paramsJo.put("dataJson", datajson)
val clickInfoJo = JSONObject()
clickInfoJo.put("clickRet", Util.isClickRet)
clickInfoJo.put("clickIp", Util.delegateIp)
clickInfoJo.put("clickTime", Util.clickTime)
clickInfoJo.put("clickErrReason", Util.clickErrReason)
clickInfoJo.put("clickRet", isClickRet)
clickInfoJo.put("clickIp", delegateIp)
clickInfoJo.put("clickTime", clickTime)
clickInfoJo.put("clickErrReason", clickErrReason)
paramsJo.put("clickInfo", clickInfoJo)
val installInfoJo = JSONObject()
installInfoJo.put("installRet", Util.isInstallRet())
installInfoJo.put("installTime", Util.installTime)
installInfoJo.put("installRet", isInstallRet())
installInfoJo.put("installTime", installTime)
paramsJo.put("installInfo", installInfoJo)
val logInfoJo = JSONObject()
logInfoJo.put("afLog", Util.getAfLog() + "\r\n" + Util.logBuffer.toString())
logInfoJo.put("afLog", getAfLog() + "\r\n" + logBuffer.toString())
// logInfoJo.put("setConfLog", Util.getParamsJson());
logInfoJo.put("hookVer", Util.HkVer())
logInfoJo.put("afVer", Util.getAppAfVer())
logInfoJo.put("afApp", Util.afApp)
logInfoJo.put("hookVer", HkVer())
logInfoJo.put("afVer", getAppAfVer())
logInfoJo.put("afApp", afApp)
paramsJo.put("logInfo", logInfoJo)
if (null != Util.backupResult) {
paramsJo.put("backUpFiles", Util.backupResult)
if (null != backupResult) {
paramsJo.put("backUpFiles", backupResult)
}
var params: String? = null
params = paramsJo?.toString() ?: "error"
var nRetryCount = 0
do {
val ret: String = MyPost.postData( params.toByteArray(charset("utf-8")), url)?:""
val ret: String = postData(params.toByteArray(charset("utf-8")), url) ?: ""
LogUtils.i(TAG, "ret:$ret")
val jo = JSONObject(ret)
if (jo.getInt("code") == 1) {
@ -175,9 +204,9 @@ class SendCallbackJobService : JobIntentService() {
LogUtils.i(TAG, "onHandleWork")
var succ = false
try {
val fileName: String? = Util.backFileName
val fileName1: String = Util.backFileName1?:""
val fileName2: String = Util.backFileName2?:""
val fileName: String? = backFileName
val fileName1: String = backFileName1 ?: ""
val fileName2: String = backFileName2 ?: ""
LogUtils.i(
TAG,
@ -191,9 +220,9 @@ class SendCallbackJobService : JobIntentService() {
}
}
if (succ) {
Util.delFileSh(fileName)
Util.delFileSh(fileName1)
Util.delFileSh(fileName2)
delFileSh(fileName)
delFileSh(fileName1)
delFileSh(fileName2)
}
}
} catch (e: Exception) {
@ -209,7 +238,7 @@ class SendCallbackJobService : JobIntentService() {
file1 = File(fileName1)
}
Util.chownSh(fileName, Util.getMainUserAndGroup(this))
chownSh(fileName, getMainUserAndGroup(this))
var url = "http://39.103.73.250/tt/ddj/backup.do"
// if (Util.backUpServerIp != "") {
// url = "http://" + Util.backUpServerIp + "/tt/ddj/backup.do"
@ -231,8 +260,8 @@ class SendCallbackJobService : JobIntentService() {
builder.setType(MultipartBody.FORM)
//追加参数
builder.addFormDataPart("recordId", "${Util.recordId}")
builder.addFormDataPart("afVer", Util.getAppAfVer())
builder.addFormDataPart("recordId", "${recordId}")
builder.addFormDataPart("afVer", getAppAfVer())
if (file.exists() && file.length() > 0) {
builder.addFormDataPart(
@ -253,7 +282,7 @@ class SendCallbackJobService : JobIntentService() {
//创建RequestBody
val body: RequestBody = builder.build()
LogUtils.d(TAG, "sendBackupEvent-> url = $url")
LogUtils.d(TAG, "sendBackupEvent-> recordId= " + Util.recordId)
LogUtils.d(TAG, "sendBackupEvent-> recordId= " + recordId)
//创建Request
val request = Request.Builder().url(url).post(body).build()
//单独设置参数 比如读取超时时间
@ -267,7 +296,7 @@ class SendCallbackJobService : JobIntentService() {
val retJo = JSONObject(ret)
if (retJo.getInt("code") == 1) {
if (retJo.has("backUpFiles")) {
Util.backupResult = retJo.getJSONObject("backUpFiles")
backupResult = retJo.getJSONObject("backUpFiles")
}
result = true
}

View File

@ -6,10 +6,14 @@ import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.app.JobIntentService
import com.android.grape.data.AppState.isClickRet
import com.android.grape.data.AppState.proxyCountry
import com.android.grape.net.MyGet
import com.android.grape.util.ClashUtil
import com.android.grape.util.ClashUtil.getProxyPort
import com.android.grape.util.Util
import com.android.grape.util.TaskUtils
import com.android.grape.util.TaskUtils.setFinish
import com.android.grape.util.TaskUtils.setInstallRet
import java.util.Locale
class StartVpnPortJobService : JobIntentService() {
@ -23,9 +27,9 @@ class StartVpnPortJobService : JobIntentService() {
)
}, 1000L)
} else {
Util.setInstallRet(false)
Util.isClickRet = false
Util.setFinish(this)
setInstallRet(false)
isClickRet = false
setFinish(this)
}
}
@ -35,7 +39,7 @@ class StartVpnPortJobService : JobIntentService() {
var nRetryCount = 0
do {
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=" + proxyCountry
?.uppercase(Locale.getDefault())
val result: String = MyGet.get(url)
Log.d(TAG, "request url == $url result$result")

View File

@ -6,10 +6,7 @@ import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.app.JobIntentService
import com.android.grape.MainApplication
import com.android.grape.util.ClashUtil
import com.android.grape.util.CountryCode
import com.android.grape.util.Util
/**
* 本地vpn功能

View File

@ -1,21 +1,24 @@
package com.android.grape.job
import android.app.ActivityManager
import android.content.Context
import android.content.Intent
import androidx.core.app.JobIntentService
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.util.BackupUtils.backUp
import com.android.grape.util.FileUtils.delFiles
import com.android.grape.util.MockTools
import com.android.grape.util.Util
import com.blankj.utilcode.util.LogUtils
import com.android.grape.util.TaskUtils
import com.android.grape.util.TaskUtils.setFinish
import com.android.grape.util.TaskUtils.setInstallRet
class UnInstallService : JobIntentService() {
override fun onHandleWork(intent: Intent) {
Util.backUp(this)
Util.setInstallRet(true)
MockTools.exec("pm clear " + Util.recordPackageName)
Util.delFiles(this@UnInstallService)
Util.setFinish(this@UnInstallService)
backUp(this)
setInstallRet(true)
MockTools.exec("pm clear " + recordPackageName)
delFiles(this@UnInstallService)
setFinish(this@UnInstallService)
}
companion object {

View File

@ -0,0 +1,191 @@
package com.android.grape.manager
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.AssetManager
import com.android.grape.data.AppState.afApp
import com.android.grape.data.AppState.afLog
import com.android.grape.data.AppState.appAfVer
import com.android.grape.data.AppState.appDataUrl
import com.android.grape.data.AppState.backFileName
import com.android.grape.data.AppState.backFileName1
import com.android.grape.data.AppState.backFileName2
import com.android.grape.data.AppState.backUpServerIp
import com.android.grape.data.AppState.backupResult
import com.android.grape.data.AppState.canAutoAtc
import com.android.grape.data.AppState.canAutoLc
import com.android.grape.data.AppState.clickErrReason
import com.android.grape.data.AppState.clickTime
import com.android.grape.data.AppState.ctit
import com.android.grape.data.AppState.defaultAppJo
import com.android.grape.data.AppState.defaultPRoxyJo
import com.android.grape.data.AppState.delegateIp
import com.android.grape.data.AppState.forwardIp
import com.android.grape.data.AppState.fuzzy_domain
import com.android.grape.data.AppState.fuzzy_proxy
import com.android.grape.data.AppState.instalTimeFromGp
import com.android.grape.data.AppState.installRet
import com.android.grape.data.AppState.installTime
import com.android.grape.data.AppState.isCanAuto
import com.android.grape.data.AppState.isClickRet
import com.android.grape.data.AppState.isNeedBackup
import com.android.grape.data.AppState.isNeedReg
import com.android.grape.data.AppState.isNeedRestored
import com.android.grape.data.AppState.keepOpen
import com.android.grape.data.AppState.lang
import com.android.grape.data.AppState.logBuffer
import com.android.grape.data.AppState.paramsJson
import com.android.grape.data.AppState.proxyCountry
import com.android.grape.data.AppState.proxyIp
import com.android.grape.data.AppState.proxyPort
import com.android.grape.data.AppState.recordExtraFileName
import com.android.grape.data.AppState.recordFileName
import com.android.grape.data.AppState.recordId
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.data.AppState.referer
import com.android.grape.data.AppState.regEmailJson
import com.android.grape.data.AppState.scriptOpenApp
import com.android.grape.data.AppState.startInstallTime
import com.android.grape.data.AppState.taskJson
import com.android.grape.data.AppState.trackingLink
import com.android.grape.data.AppState.ua
import com.android.grape.data.AppState.videoProxy
import org.json.JSONObject
import java.io.IOException
import java.io.InputStream
import java.util.Properties
/**
* @Time: 2025-27-16 17:27
* @Creator: 初屿贤
* @File: ConfigManager
* @Project: AndroidGrape
* @Description:
*/
object ConfigManager {
fun initDefaultAppJo() {
try {
defaultAppJo = JSONObject(
"{\"packname\":\"luxury.best.mycamerafilter\",\"minsdk\":19,\"appname\":\"Super Fast Filter\",\"sig_sha256\":\"a2a087582d4b08db38bb50a5a4a7e588513688d81c044e5ae6aa7569dd18c454\",\"vercode\":7,\"apkmd5\":\"81d97721c197506795bd4697da916edc\",\"sig_sha1\":\"80cd0ca6eb908870d89f2ee7b9ff06d080512562\",\"sig\":\"MIIDSzCCAjOgAwIBAgIEKbAxNjANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJLUjEMMAoGA1UECBMDUHJvMQ0wCwYDVQQHEwRDaXR5MQwwCgYDVQQKEwNPcmcxCzAJBgNVBAsTAk9VMQ4wDAYDVQQDEwVZb3VuZzAgFw0yMDEyMzExNTE5MTNaGA8yMDc1MTAwNDE1MTkxM1owVTELMAkGA1UEBhMCS1IxDDAKBgNVBAgTA1BybzENMAsGA1UEBxMEQ2l0eTEMMAoGA1UEChMDT3JnMQswCQYDVQQLEwJPVTEOMAwGA1UEAxMFWW91bmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnkvDWPAkmp0RriiRp0pdKWvG3EvYP+EQ3cKUBz2IEygZRK4rH6dfHnzG0AbqZE/eTV1ISiA8sOotlCIAukq3DdBpg+2WFmO7RO//0lhxc2L+QJJsSyrweeE7jCgyTTRCTf/NUqNhCsscGaFMa2JYU/FVDQDDGNXS1rPk6Tx+D+B+XWu7nZBioH0Bcp6rEJ+FD23+tHKEwXiw7znKUojuJ+wtZjWqFNI6L0PkI5rI7Ckg/NwBqGIT9xEL8TDZATdIIOb4c3QbqhEdxA42/PFnXTmWG8W3AcYKivvdRCXmFPhz++e4NRxZHnbQGvRfbl0yKs8U2ViHPV9YMooIaVMAnAgMBAAGjITAfMB0GA1UdDgQWBBQ5Bpf8zg0LB3RTfMr7tKo+e7+RujANBgkqhkiG9w0BAQsFAAOCAQEAl/JZliWRvfKGP46L4vF3PYvvg+61Iho6giPq+zYLmFFhtw67Vc5bUrBqn5t+rAZ5EO871pqnB326SJW+Q0Iy2W/z05MK5QS302R8RYB+9DyQQHCi9c7NVsYoNoEQ3BCh/K01qtYfkE+xoKSMsiWhTtrpoNIkxh2pVjxAcos0MZcOjN0htP6xkXL24uEMSZTkmfv3Wyty1OFvdguncRJJksPVb+Xwt3gCOzsFbuPtG06NlwfN3R4PhfaeIapil5zMhbzWnx7OPoNXYYJhk+rAiVI8wPcQEo4+MGXEyCKma6OZzPFSNoRZBOdEsFAe21/D/0heRHJ2BveHwtA6jcx6zg==\",\"vername\":\"1.0.4\",\"apksize\":3609050,\"sig_md5\":\"6c6e8954878832e065f2a12bf21aa40c\",\"appid\":\"5133078\",\"slotid\":\"945726662\",\"adType\":1}"
)
} catch (e: Exception) {
e.printStackTrace()
}
}
fun initDefaultProxyJo() {
try {
defaultPRoxyJo = JSONObject("{\"proxyPort\": 0,\"proxyIp\": \"\"}")
} catch (e: Exception) {
}
}
fun init() {
scriptOpenApp = 0
fuzzy_proxy = null
backupResult = null
backUpServerIp = ""
fuzzy_domain = ""
referer = ""
isNeedBackup = false
isNeedRestored = false
taskJson = null
installRet = false
paramsJson = null
recordPackageName = null
recordFileName = null
recordExtraFileName = null
trackingLink = null
proxyIp = null
proxyPort = 0
proxyCountry = null
lang = null
delegateIp = null
ua = null
recordId = 0L
videoProxy = ""
forwardIp = null
referer = null
appDataUrl = ""
installTime = 0L
instalTimeFromGp = 0L
startInstallTime = 0L
clickTime = 0L
installTime = 0L
afLog = ""
backFileName = null
backFileName1 = null
backFileName2 = null
installRet = null
isClickRet = false
clickErrReason = ""
appAfVer = ""
afApp = ""
ctit = 0
keepOpen = 0
isNeedReg = false
isNeedBackup = false
regEmailJson = null
isCanAuto = false
canAutoAtc = ""
canAutoLc = ""
logBuffer = StringBuffer()
}
fun getPropertiesFromAssets(context: Context, fileName: String): Properties {
val am: AssetManager = context.assets
var `is`: InputStream? = null
val prop = Properties()
try {
`is` = am.open(fileName)
prop.load(`is`)
} catch (e: IOException) {
e.printStackTrace()
} finally {
if (`is` != null) {
try {
`is`.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
return prop
}
fun getMetaDataValue(context: Context, name: String): String {
var value: Any? = null
val packageManager: PackageManager = context.packageManager
val applicationInfo: ApplicationInfo
try {
applicationInfo =
packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA)
if (applicationInfo.metaData != null) {
value = applicationInfo.metaData[name]
}
} catch (e: PackageManager.NameNotFoundException) {
throw RuntimeException("Could not read the name in the manifest file.", e)
}
if (value == null) {
throw RuntimeException("The name '$name' is not defined in the manifest file's meta data.")
}
return value.toString()
}
}

View File

@ -0,0 +1,54 @@
package com.android.grape.manager
import com.android.grape.data.AppState.clickTime
import com.android.grape.data.AppState.instalTimeFromGp
import com.android.grape.data.AppState.installTime
import java.util.Random
/**
* @Time: 2025-30-16 17:30
* @Creator: 初屿贤
* @File: TrackingManager
* @Project: AndroidGrape
* @Description:
*/
object TrackingManager {
fun genInstallTimeFromGP(ct: Long, it: Long): LongArray {
var clickServerTimeToGP: Long
var installServerTimeFromGP: Long
val timeOff = it - ct
if (timeOff < 180000L) {
installTime = it - 1000 * (5 + Random().nextInt(10))
instalTimeFromGp = (installTime / 1000) - (installTime % 7) - 3
} else if (timeOff < 600000L) {
installTime = it - 1000 * (20 + Random().nextInt(40))
instalTimeFromGp = (installTime / 1000) - (installTime % 30) - 30
} else {
// firstInstallTime = it - 1000 * (40+new Random().nextInt(140));
installTime = it - 1000 * (180 + Random().nextInt((timeOff * 0.3 / 1000).toInt()))
instalTimeFromGp = (installTime / 1000) - (installTime % 120) - 180
}
clickServerTimeToGP = (instalTimeFromGp * 1000 - clickTime)
if (clickServerTimeToGP > 0) {
val randomNum =
if (clickServerTimeToGP.toInt() / 1000 >= 20) Random().nextInt(20) else Random().nextInt(
clickServerTimeToGP.toInt() / 1000
)
clickServerTimeToGP = clickTime + ((1 + Random().nextInt(1 + randomNum)) * 1000)
}
installServerTimeFromGP = (installTime - instalTimeFromGp * 1000)
if (installServerTimeFromGP > 0) {
val randomNum =
if (installServerTimeFromGP.toInt() / 1000 >= 20) Random().nextInt(20) else Random().nextInt(
installServerTimeFromGP.toInt() / 1000
)
installServerTimeFromGP = instalTimeFromGp + (1 + Random().nextInt(1 + randomNum))
}
return longArrayOf(clickServerTimeToGP, installServerTimeFromGP)
}
}

View File

@ -0,0 +1,316 @@
package com.android.grape.net
import android.content.Context
import android.util.Log
import com.android.grape.data.AppState
import com.android.grape.data.AppState.clickServerTimeFromGP
import com.android.grape.data.AppState.clickTime
import com.android.grape.data.AppState.instalTimeFromGp
import com.android.grape.data.AppState.installTime
import com.android.grape.data.AppState.lastUpdateTime
import com.android.grape.data.AppState.paramsJson
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.data.AppState.referer
import com.android.grape.data.AppState.taskJson
import com.android.grape.manager.TrackingManager.genInstallTimeFromGP
import com.android.grape.util.DeviceUtils.getGoogleAdId
import com.android.grape.util.FileUtils
import com.android.grape.util.ServiceUtils
import com.android.grape.util.TaskUtils
import com.blankj.utilcode.util.LogUtils
import java.io.BufferedInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.io.UnsupportedEncodingException
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLDecoder
/**
* @Time: 2025-07-16 16:07
* @Creator: 初屿贤
* @File: AfClient
* @Project: AndroidGrape
* @Description:
*/
object AfClient {
@Throws(IOException::class)
fun setInfo(context: Context): Boolean {
val url = "http://127.0.0.1:8090/ctl/setinfo"
//安装时间
try {
clickTime = taskJson!!.getLong("clickTime")
val deviceJo = paramsJson!!.getJSONObject("device")
// installTimeFromGP = (long) (clickTime / 1000) + (clickTime % 7) + 3;
installTime = System.currentTimeMillis()
Thread.sleep(2000)
Log.d("IOSTQ:installTime == ", installTime.toString() + "")
Log.d("IOSTQ:clickTime == ", clickTime.toString() + "")
val time = genInstallTimeFromGP(clickTime, installTime)
// installTimeFromGP = firstInstallTime / 1000;
installTime = System.currentTimeMillis()
lastUpdateTime = installTime
LogUtils.d("IOSTQ:lastUpdateTime ", lastUpdateTime.toString() + "")
deviceJo.put("firstInstallTime", installTime)
deviceJo.put("lastUpdateTime", lastUpdateTime)
deviceJo.put("installTimeFromGP", instalTimeFromGp)
val installServerTimeFromGP = time[1] // + (installTimeFromGP % 2) + 1;
deviceJo.put("installServerTimeFromGP", installServerTimeFromGP)
val clickTimeToGp = (clickTime / 1000)
deviceJo.put("clickTimeToGP", clickTimeToGp)
val clickServerTimeToGP = time[0] / 1000 // + (clickTimeToGp % 2) + 1;
deviceJo.put("clickServerTimeToGP", clickServerTimeToGP)
AppState.installServerTimeFromGP = installServerTimeFromGP
clickServerTimeFromGP = clickServerTimeToGP
if (taskJson!!.has("clickData")) {
val clickdata = taskJson!!.getJSONObject("clickData")
if (clickdata.has("referer") && clickdata.getString("referer").length > 10) {
referer = clickdata.getString("referer")
}
} else {
referer = ""
}
if (referer != null && referer!!.length > 10) {
deviceJo.put("referrerFromGP", URLDecoder.decode(referer, "UTF-8"))
} else {
deviceJo.put("referrerFromGP", "utm_source=google-play&utm_medium=organic")
deviceJo.put("clickServerTimeToGP", 0)
deviceJo.put("clickTimeToGP", 0)
deviceJo.put("installTimeFromGP", 0)
deviceJo.put("installServerTimeFromGP", 0)
}
//JSONObject proxyJo = paramsJson.getJSONObject("proxy");
//proxyJo.put("forwardIp", Util.getDelegateIp());
Log.d("IOSTQ", "firstInstalTime == " + installTime)
Log.d("IOSTQ", "paramsJson == " + paramsJson)
val origin_gaid: String = getGoogleAdId(context) ?: ""
deviceJo.put("origin_gaid", origin_gaid)
paramsJson!!.put("device", deviceJo)
val params = paramsJson.toString()
ServiceUtils.setEnableApp(recordPackageName ?: "", true)
ServiceUtils.setEnableApp("org.mozilla.firefox", true)
ServiceUtils.setEnableApp("com.google.android.webview", true)
ServiceUtils.setEnableApp("com.android.chrome", true)
ServiceUtils.writeFile("/data/system/device.txt", params)
Log.d("IOSTQ:param == ", params)
// paramsJson.put("proxy", proxyJo);
} catch (e: Exception) {
Log.d("IOSTQ", e.message!!)
e.printStackTrace()
}
// printStr("execSetJson params:"+params);
// writeFileToSDCard(params);
try {
// String ret = new MyPost().PostData(context, params.getBytes("utf-8"), url);
// Log.i("TaskUtils", "set info ret : " + ret);
} catch (e: Exception) {
e.printStackTrace()
}
return true
}
fun downloadFile(httpUrl: String, fileName: String): Boolean {
Log.i(
"AfClient",
"start to downloadFile : $httpUrl ; $fileName"
)
val file = File(fileName)
if (file.exists() && file.length() >= 1024 * 1024 * 2) { //文件已经存在就不下载 且 大小超过3M
return true
}
val create_dir = file.parentFile
if (create_dir != null && !create_dir.exists()) {
FileUtils.forceMakeDir(create_dir)
} else if (create_dir == null) {
return false
}
val fileLength = 0L
try {
var conn: HttpURLConnection? = null
var `is`: InputStream? = null
var fos: FileOutputStream? = null
val url = URL(httpUrl)
try {
conn = url.openConnection() as HttpURLConnection
`is` = conn.inputStream
fos = FileOutputStream(file)
val buf = ByteArray(256)
conn.connect()
if (conn.responseCode >= 400) {
Log.i("AfClient", "connection timeout")
} else {
//System.out.println("文件大小:"+fileLength);
while (true) {
if (`is` != null) {
val numRead = `is`.read(buf)
if (numRead <= 0) {
return true
} else {
fos.write(buf, 0, numRead)
}
} else {
break
}
}
if (file.length() >= 1024 * 100) {
return true
}
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
conn?.disconnect()
} catch (e: Exception) {
}
try {
fos?.close()
} catch (e: Exception) {
}
try {
`is`?.close()
} catch (e: Exception) {
}
}
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
fun postData(byt: ByteArray?, url: String): String? {
val bytes: ByteArray? = postDataBytes(byt, url)
if (bytes != null && bytes.isNotEmpty()) {
try {
return String(bytes, charset("UTF-8"))
} catch (e: UnsupportedEncodingException) {
e.printStackTrace()
}
}
return null
}
fun postDataBytes(byt: ByteArray?, url: String): ByteArray? {
var result: String? = null
var httpUrlConnection: HttpURLConnection? = null
var inStrm: InputStream? = null
var baos: ByteArrayOutputStream? = null
var bis: BufferedInputStream? = null
try {
httpUrlConnection = getHttpURLConnection(url)
httpUrlConnection.allowUserInteraction = true
httpUrlConnection.doOutput = true
httpUrlConnection.doInput = true
httpUrlConnection.useCaches = false
httpUrlConnection.setRequestProperty("Connection", "close") //add 20200428
httpUrlConnection.setRequestProperty(
"Content-type",
"application/x-java-serialized-object"
)
// httpUrlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
httpUrlConnection.requestMethod = "POST"
httpUrlConnection.connectTimeout = 20000
var outStrm: OutputStream? = null
outStrm = httpUrlConnection.outputStream
outStrm.write(byt)
outStrm.flush()
outStrm.close()
inStrm = httpUrlConnection.inputStream
baos = ByteArrayOutputStream()
bis = BufferedInputStream(inStrm)
val buf = ByteArray(1024)
var readSize = -1
while ((bis.read(buf).also { readSize = it }) != -1) {
baos.write(buf, 0, readSize)
}
val data = baos.toByteArray()
return data
} catch (e: Exception) {
e.printStackTrace()
result = null
} finally {
if (baos != null) {
try {
baos.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
if (bis != null) {
try {
bis.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
if (inStrm != null) {
try {
inStrm.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
if (httpUrlConnection != null) {
httpUrlConnection.disconnect()
httpUrlConnection = null
}
System.gc()
}
return null
}
const val dynamicPort0: Int = 20380
const val dynamicPort: Int = dynamicPort0
const val dynamicPort1: Int = 30379
fun getHttpURLConnection(
_url: String
): HttpURLConnection {
val url = URL(_url)
var httpUrlConnection: HttpURLConnection? = null
httpUrlConnection = url.openConnection() as HttpURLConnection
return httpUrlConnection
}
}

View File

@ -1,127 +0,0 @@
package com.android.grape.net
import java.io.BufferedInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.io.UnsupportedEncodingException
import java.net.HttpURLConnection
import java.net.URL
object MyPost {
fun postData(byt: ByteArray?, url: String): String? {
val bytes: ByteArray? = postDataBytes(byt, url)
if (bytes != null && bytes.isNotEmpty()) {
try {
return String(bytes, charset("UTF-8"))
} catch (e: UnsupportedEncodingException) {
e.printStackTrace()
}
}
return null
}
fun postDataBytes(byt: ByteArray?, url: String): ByteArray? {
var result: String? = null
var httpUrlConnection: HttpURLConnection? = null
var inStrm: InputStream? = null
var baos: ByteArrayOutputStream? = null
var bis: BufferedInputStream? = null
try {
httpUrlConnection = getHttpURLConnection(url)
httpUrlConnection.allowUserInteraction = true
httpUrlConnection.doOutput = true
httpUrlConnection.doInput = true
httpUrlConnection.useCaches = false
httpUrlConnection.setRequestProperty("Connection", "close") //add 20200428
httpUrlConnection.setRequestProperty(
"Content-type",
"application/x-java-serialized-object"
)
// httpUrlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
httpUrlConnection.requestMethod = "POST"
httpUrlConnection.connectTimeout = 20000
var outStrm: OutputStream? = null
outStrm = httpUrlConnection.outputStream
outStrm.write(byt)
outStrm.flush()
outStrm.close()
inStrm = httpUrlConnection.inputStream
baos = ByteArrayOutputStream()
bis = BufferedInputStream(inStrm)
val buf = ByteArray(1024)
var readSize = -1
while ((bis.read(buf).also { readSize = it }) != -1) {
baos.write(buf, 0, readSize)
}
val data = baos.toByteArray()
return data
} catch (e: Exception) {
e.printStackTrace()
result = null
} finally {
if (baos != null) {
try {
baos.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
if (bis != null) {
try {
bis.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
if (inStrm != null) {
try {
inStrm.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
if (httpUrlConnection != null) {
httpUrlConnection.disconnect()
httpUrlConnection = null
}
System.gc()
}
return null
}
const val dynamicPort0: Int = 20380
const val dynamicPort: Int = dynamicPort0
const val dynamicPort1: Int = 30379
fun getHttpURLConnection(
_url: String
): HttpURLConnection {
val url = URL(_url)
var httpUrlConnection: HttpURLConnection? = null
httpUrlConnection = url.openConnection() as HttpURLConnection
return httpUrlConnection
}
}

View File

@ -5,9 +5,10 @@ import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import com.android.grape.data.AppState.script_status
import com.android.grape.job.UnInstallService
import com.android.grape.util.ScriptUtil.SCRIPT_RESULT_KEY
import com.android.grape.util.Util
import com.android.grape.util.ScriptUtils.SCRIPT_RESULT_KEY
import com.android.grape.util.TaskUtils
import com.blankj.utilcode.util.LogUtils
class ScriptReceiver : BroadcastReceiver() {
@ -15,7 +16,7 @@ class ScriptReceiver : BroadcastReceiver() {
val action = intent.action
LogUtils.i("TAG", context.packageName + " Receive action:" + action)
val scriptResult = intent.getStringExtra(SCRIPT_RESULT_KEY)
Util.script_status = 0
script_status = 0
LogUtils.i(
"IOSTQ:脚本结束",
"result----> $scriptResult"

View File

@ -2,6 +2,7 @@ package com.android.grape.sai
import android.content.Context
import android.util.Log
import com.android.grape.util.ShellUtils.catSh
import java.io.BufferedReader
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@ -13,6 +14,7 @@ import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.UnsupportedEncodingException
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.security.DigestInputStream
@ -41,6 +43,39 @@ object IOUtils {
}
}
fun getStringFromFile(context: Context?, filepath: String): String {
Log.i(
TAG,
"start to getStringFromFile : $filepath"
)
val file = File(filepath)
if (file.exists()) {
return catSh(context, filepath)
}
return ""
}
fun getStringFromInputStream(inputStream: InputStream?): String {
var inputStreamReader: InputStreamReader? = null
try {
inputStreamReader = InputStreamReader(inputStream, "utf-8")
} catch (e1: UnsupportedEncodingException) {
e1.printStackTrace()
}
val reader = BufferedReader(inputStreamReader)
val sb = StringBuffer("")
var line: String?
try {
while ((reader.readLine().also { line = it }) != null) {
sb.append(line)
sb.append("\n")
}
} catch (e: IOException) {
e.printStackTrace()
}
return sb.toString()
}
@Throws(IOException::class)
fun copyFileFromAssets(context: Context, assetFileName: String, destination: File?) {
context.assets.open(assetFileName).use { inputStream ->

View File

@ -12,6 +12,8 @@ import android.util.Log
import android.util.Pair
import com.android.grape.MainApplication
import com.android.grape.R
import com.android.grape.data.AppState.isClickRet
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.sai.inter.ApkSource
import com.android.grape.sai.param.SaiPiSessionParams
import com.android.grape.sai.param.SaiPiSessionState
@ -21,10 +23,10 @@ import com.android.grape.sai.prefers.PreferencesHelper
import com.android.grape.sai.rootless.AndroidPackageInstallerError
import com.android.grape.sai.shell.MiuiUtils
import com.android.grape.sai.shell.Shell
import com.android.grape.util.Util
import com.android.grape.util.TaskUtils
import com.android.grape.util.TaskUtils.setInstallRet
import com.blankj.utilcode.util.LogUtils
import java.io.File
import java.util.Arrays
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Semaphore
@ -76,7 +78,7 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
}
unlockInstallation()
// Toast.makeText(context,"Installation succeed",Toast.LENGTH_SHORT).show();
Util.setInstallRet(true)
setInstallRet(true)
}
}
@ -130,12 +132,12 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
).build()
)
unlockInstallation()
Util.setInstallRet(false)
setInstallRet(false)
return
}
androidSessionId = createSession()
//todo params.apkSource().apkLocalPath?
val path = "/sdcard/apks/${Util.recordPackageName}"
val path = "/sdcard/apks/${recordPackageName}"
val file = File(path)
val files = file.listFiles()
@ -145,7 +147,7 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
if (f.length() <= 0) {
setSessionState(sessionId, SaiPiSessionState.Builder(sessionId, SaiPiSessionStatus.INSTALLATION_FAILED).appTempName(appTempName).error(MainApplication.instance.getString(R.string.installer_error_unknown_apk_size), null).build())
unlockInstallation()
Util.setInstallRet(false)
setInstallRet(false)
return
}
ensureCommandSucceeded(shell.exec(Shell.Command("pm", "install-write", f.length().toString(), androidSessionId.toString(), String.format("%d.apk", currentApkFile++), f.path)))
@ -170,9 +172,9 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
""".trimIndent()).build())
unlockInstallation()
Util.setInstallRet(false)
setInstallRet(false)
} else {
Util.isClickRet = true
isClickRet = true
}
}
} catch (e: Exception) {
@ -207,7 +209,7 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
unlockInstallation()
Util.setInstallRet(false)
setInstallRet(false)
}
}

View File

@ -1,119 +0,0 @@
package com.android.grape.util
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.os.IInterface
import android.os.Looper
import android.os.Parcel
import android.os.RemoteException
import android.util.Log
import java.util.concurrent.LinkedBlockingQueue
/**
* date:2020-11-13
* desc:
*/
object AdvertisingIdClient {
private const val TAG = "AdvertisingIdClient"
private fun logd(msg: String) {
Log.d(TAG, msg)
}
/**
* 这个方法是耗时的不能在主线程调用
*/
@Throws(Exception::class)
fun getGoogleAdId(context: Context): String? {
if (Looper.getMainLooper() == Looper.myLooper()) {
logd("getGoogleAdId not running in main thread#########")
return null
}
val pm = context.packageManager
pm.getPackageInfo("com.android.vending", 0)
val connection = AdvertisingConnection()
val intent = Intent(
"com.google.android.gms.ads.identifier.service.START"
)
intent.setPackage("com.google.android.gms")
if (context.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
try {
val adInterface = AdvertisingInterface(
connection.binder
)
return adInterface.getId()
} finally {
context.unbindService(connection)
}
}
return null
}
private class AdvertisingConnection : ServiceConnection {
var retrieved: Boolean = false
private val queue = LinkedBlockingQueue<IBinder>(1)
override fun onServiceConnected(name: ComponentName, service: IBinder) {
try {
queue.put(service)
} catch (localInterruptedException: InterruptedException) {
}
}
override fun onServiceDisconnected(name: ComponentName) {
}
@get:Throws(InterruptedException::class)
val binder: IBinder
get() {
check(!this.retrieved)
this.retrieved = true
return queue.take()
}
}
private class AdvertisingInterface(private val binder: IBinder) : IInterface {
override fun asBinder(): IBinder {
return binder
}
@Throws(RemoteException::class)
fun getId(): String? {
val data = Parcel.obtain()
val reply = Parcel.obtain()
var id: String?
try {
data.writeInterfaceToken("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService")
binder.transact(1, data, reply, 0)
reply.readException()
id = reply.readString()
} finally {
reply.recycle()
data.recycle()
}
return id
}
@Throws(RemoteException::class)
fun isLimitAdTrackingEnabled(paramBoolean: Boolean): Boolean {
val data = Parcel.obtain()
val reply = Parcel.obtain()
var limitAdTracking: Boolean
try {
data.writeInterfaceToken("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService")
data.writeInt(if (paramBoolean) 1 else 0)
binder.transact(2, data, reply, 0)
reply.readException()
limitAdTracking = 0 != reply.readInt()
} finally {
reply.recycle()
data.recycle()
}
return limitAdTracking
}
}
}

View File

@ -0,0 +1,651 @@
package com.android.grape.util
import android.R.attr.name
import android.app.ActivityManager
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.android.grape.MainApplication
import com.android.grape.data.AppState.apkDir
import com.android.grape.data.AppState.apk_path
import com.android.grape.data.AppState.appAfVer
import com.android.grape.data.AppState.appVersionCode
import com.android.grape.data.AppState.backUpServerIp
import com.android.grape.data.AppState.baoming
import com.android.grape.data.AppState.isNeedRestored
import com.android.grape.data.AppState.monitorDir
import com.android.grape.data.AppState.recordExtraFileName
import com.android.grape.data.AppState.recordFileName
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.data.AppState.startInstallTime
import com.android.grape.data.AppState.tags
import com.android.grape.data.AppState.taskJson
import com.android.grape.data.AppState.videoProxy
import com.android.grape.data.AppState.zip_name
import com.android.grape.manager.ConfigManager.getMetaDataValue
import com.android.grape.manager.ConfigManager.getPropertiesFromAssets
import com.android.grape.net.AfClient.downloadFile
import com.android.grape.sai.ApkSourceBuilder
import com.android.grape.sai.FlexSaiPackageInstaller
import com.android.grape.sai.IOUtils.getStringFromFile
import com.android.grape.sai.RootedSaiPackageInstaller
import com.android.grape.sai.inter.ApkSource
import com.android.grape.sai.param.SaiPiSessionParams
import com.android.grape.sai.prefers.PreferencesHelper
import com.android.grape.util.ContextUtils.getBaseFilesDir
import com.android.grape.util.ContextUtils.getRecordDataDirName
import com.android.grape.util.ContextUtils.getRecordExtraFileName
import com.android.grape.util.DeviceUtils.getUserAndGroupSh
import com.android.grape.util.FileUtils.forceMakeDir
import com.android.grape.util.FileUtils.getFileContent
import com.android.grape.util.FileUtils.getRecordDataFileName
import com.android.grape.util.FileUtils.getRecordSdcardApkVerFileName
import com.android.grape.util.InstallUtils.installApks4Tmp
import com.android.grape.util.ShellUtils.chownSh
import com.android.grape.util.ShellUtils.unzipAPkSh
import com.android.grape.util.TaskUtils.getSessionTxtFileName
import com.android.grape.util.TaskUtils.isInstallRet
import com.blankj.utilcode.util.ActivityUtils
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.PrintWriter
import java.util.Locale
/**
* @Time: 2025-49-16 15:49
* @Creator: 初屿贤
* @File: AppUtils
* @Project: AndroidGrape
* @Description:
*/
object AppUtils {
fun execTargetApp(context: Context, targetPackageName: String): Boolean {
Log.i("AppUtils", "开始启动应用 ...$targetPackageName")
val packageManager: PackageManager = context.packageManager
var intent: Intent? = null
intent = packageManager.getLaunchIntentForPackage(targetPackageName)
if (intent == null) {
return false
} else {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
return true
}
}
/**
* 集上应用
*
* @param context 上下文
*/
//当本应用位于后台时,则将它切换到最前端
fun setTopApp(context: Context) {
Log.i("AppUtils", "start to setTopApp");
if (isRunningForeground(context)) {
Log.i("AppUtils", "app isRunningForeground");
return;
}
//获取ActivityManager
val activityManager: ActivityManager =
context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
//获得当前运行的task(任务)
val taskInfoList: List<ActivityManager.RunningTaskInfo> =
activityManager.getRunningTasks(100)
for (taskInfo in taskInfoList) {
//找到本应用的 task并将它切换到前台
if (taskInfo.baseActivity?.packageName == context.packageName) {
// Log.i("AppUtils", "setTopApp exec");
activityManager.moveTaskToFront(taskInfo.id, 0)
// checkFloatPermission(context)
break
}
}
}
//判断本应用是否已经位于最前端:已经位于最前端时,返回 true否则返回 false
fun isRunningForeground(context: Context): Boolean {
val activityManager: ActivityManager =
context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val appProcessInfoList: List<ActivityManager.RunningAppProcessInfo> =
activityManager.runningAppProcesses
if (appProcessInfoList.isNotEmpty()) {
for (appProcessInfo in appProcessInfoList) {
if (appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&& appProcessInfo.processName == context.applicationInfo.processName
) {
return true
}
}
}
return false
}
//应用藏到后台
fun setBackApp(context: Context) {
Log.i("AppUtils", "start to setBackApp")
if (isRunningForeground(context)) {
Handler(Looper.getMainLooper()).post(Runnable {
Log.i("AppUtils", "setBackApp exec")
ActivityUtils.getTopActivity()?.moveTaskToBack(true)
})
} else {
Log.i("AppUtils", "app is back already")
}
}
fun execTargetApp(
context: Context,
targetPackageName: String,
targetAppMainClass: String
): Boolean {
Log.i(
"AppUtils",
"start to openTargetApp ... $targetPackageName ; $targetAppMainClass"
)
execTargetApp(context, targetPackageName)
return true
}
fun installApk(context: Context, apkFile: File, extraFile: File?): Boolean {
Log.i(
"AppUtils",
"start to install apk ... $apkFile ; $extraFile"
)
if (extraFile == null) {
return if (apkFile.name.endsWith(".apk")) {
clientInstall(apkFile)
} else {
clientInstallOther(context, apkFile)
}
} else {
val extraFileName = extraFile.name.lowercase(Locale.getDefault())
return if (extraFileName.endsWith("obb.apk")) {
clientInstallObb(context, apkFile, extraFile)
} else {
clientInstallSplit(context, apkFile, extraFile)
}
}
}
public fun recordAppInstalled(context: Context): Boolean {
return checkAppInstalled(
context,
recordPackageName
)
}
/**
* 卸载
*
* @return
*/
fun clientUninstallRecord(context: Context): Boolean {
while (recordAppInstalled(context)) {
clientUninstall(recordPackageName)
}
return true
}
/**
* 安装次留软件
*
* @param context
* @return
*/
fun installRecord(context: Context): Boolean {
var installRet = false
startInstallTime = System.currentTimeMillis()
if (CheckAppNeedUpgrade(context) || !checkAppInstalled(
context,
recordPackageName
)
) {
try {
val file = File(getRecordSdcardApkVerFileName(context))
var extraFile: File? = null
if (recordExtraFileName?.isNotEmpty() == true) {
extraFile = File(ContextUtils.getRecordExtraFileName(context))
}
installRet = installApk(context, file, extraFile) && AppUtils.checkAppInstalled(
context,
recordPackageName
)
if (installRet && !videoProxy.contains("123.56.44.45")) {
//检查是否有新版本
if (!CheckAppNeedUpgrade(context)) {
file.delete()
extraFile?.delete()
}
}
} catch (e: Exception) {
e.printStackTrace()
}
Log.i("TaskUtils", "installRet:$installRet")
} else {
installRet = true
MockTools.exec("pm clear $recordPackageName")
}
return installRet
}
fun getTag(context: Context): String {
if (tags == null) {
val prop = getPropertiesFromAssets(context, "tag.ini")
tags = prop.getProperty("tag")
if (tags == null) {
tags = getMetaDataValue(context, "tag")
}
if (tags == null) {
tags = "000"
}
if ("000" == tags) {
tags = getFileContent(context, "/sdcard/tag.ini")
}
if (tags == null) {
tags = "000"
}
}
tags = filterStr(tags)
// Log.e(TAG, "getCid:*"+tags+"*");
return tags ?: ""
}
public fun filterStr(s: String?): String {
var s = s
val sb = StringBuffer()
if (s != null && s.length > 0) {
s = s.uppercase(Locale.getDefault())
for (i in 0..<s.length) {
val c = s.substring(i, i + 1)
if ("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(c)) {
// Log.e("TaskUtils","filterStr:*"+c+"* valid");
sb.append(c)
} else {
// Log.e("TaskUtils","filterStr:*"+c+"* invalid");
}
}
}
return sb.toString()
}
fun tagForCapture(context: Context): Boolean {
val tt = getTag(context)
return "101" == tt || "100" == tt
}
public fun getApkDataDir(context: Context, packageName: String): String? {
var apkDataPath: String? = null
try {
apkDataPath = context.packageManager.getApplicationInfo(packageName, 0).dataDir
return apkDataPath
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
return null
}
/**
* 检查应用程序是否需要通过比较当前安装的版本进行升级
* 带有录制版本
*
* @param上下文应用程序访问软件包管理器以获取的上下文
* 包装信息
* @return a布尔值指示是否需要更新如果版本代码返回true
* 不匹配或找不到软件包否则为错误
*/
//判断版本是否更新
fun CheckAppNeedUpgrade(context: Context): Boolean {
val pckMan: PackageManager = context.packageManager
val items = ArrayList<HashMap<String, Any>>()
val packageInfo: List<PackageInfo> = pckMan.getInstalledPackages(0)
for (pInfo in packageInfo) {
if (pInfo.packageName == recordPackageName) {
return if (appVersionCode != pInfo.versionCode.toLong()) {
true
} else {
false
}
}
}
return true
}
fun getAppAfVer(): String {
return appAfVer
}
fun setAppAfVer(appAfVerV: String) {
Log.i("TaskUtils", "setAppAfVer: $appAfVerV")
appAfVer = appAfVerV
}
public fun clientInstallSplit(context: Context, apkFile: File, extraFile: File): Boolean {
val PrintWriter: PrintWriter? = null
val process: Process? = null
try {
val txtFileName = getSessionTxtFileName(context)
MockTools.exec("chmod 777 $apkFile")
MockTools.exec("pm install-create > $txtFileName")
//Success: created install session [1649302163]
val sessionStr = getStringFromFile(context, getSessionTxtFileName(context))
val pos0 = sessionStr.indexOf("[")
val pos1 = sessionStr.indexOf("]")
val sessionId = sessionStr.substring(pos0 + 1, pos1)
Log.i("AppUtils", "sessionId:$sessionId")
MockTools.exec("chmod 777 $apkFile")
MockTools.exec("pm install-write $sessionId baseapk $apkFile")
MockTools.exec("pm install-write $sessionId splitapk $extraFile")
MockTools.exec("pm install-commit $sessionId")
return true
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
public fun clientInstallObb(context: Context, apkFile: File, extraFile: File): Boolean {
Log.i(
"AppUtils",
"start clientInstallObb : $apkFile ; $extraFile"
)
try {
MockTools.exec("chmod 777 $apkFile")
MockTools.exec("pm install -r $apkFile")
installObb(context, extraFile)
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
public fun installObb(context: Context, extraFile: File) {
val userAndGroup = getUserAndGroupSh(
context,
context.packageName,
getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + context.packageName + ".txt"
)
val destExtraFile = File(
"/sdcard/Android/obb/" + recordPackageName + "/" + extraFile.name.replace(
".obb.apk",
".obb"
)
)
forceMakeDir(destExtraFile)
var fileInputStream: FileInputStream? = null
var fileOutputStream: FileOutputStream? = null
try {
fileInputStream = FileInputStream(extraFile)
fileOutputStream = FileOutputStream(destExtraFile)
val buffer = ByteArray(1024)
var byteRead: Int
while ((fileInputStream.read(buffer).also { byteRead = it }) != -1) {
fileOutputStream.write(buffer, 0, byteRead)
}
chownSh("/sdcard/Android/obb/" + recordPackageName + "/", userAndGroup)
} catch (e: Exception) {
if (fileInputStream != null) {
try {
fileInputStream.close()
} catch (e1: Exception) {
e1.printStackTrace()
}
}
if (fileOutputStream != null) {
try {
fileOutputStream.flush()
fileOutputStream.close()
} catch (e2: Exception) {
e2.printStackTrace()
}
}
}
}
public fun clientInstallOther(context: Context, apkFile: File): Boolean {
Log.e("AppUtils", "clientInstallOther: $apkFile")
if (apkFile.toString().contains("xapk")) {
try {
unzipAPkSh(apkFile.toString(), getRecordDataDirName(context))
installApks4Tmp(recordPackageName, context)
} catch (e: Exception) {
e.printStackTrace()
}
} else {
Log.e("clientInstallOther", "后缀是apks")
//将文件路径转化为Uri
val uri = Uri.fromFile(apkFile)
val mInstaller: FlexSaiPackageInstaller = FlexSaiPackageInstaller.getInstance()
val mPrefsHelper: PreferencesHelper = PreferencesHelper.getInstance(context)
val rootedSaiPackageInstaller: RootedSaiPackageInstaller =
RootedSaiPackageInstaller.getInstance(context)
val apkSource: ApkSource = ApkSourceBuilder(context)
.fromZipContentUri(uri)
.setZipExtractionEnabled(mPrefsHelper.shouldExtractArchives())
.setReadZipViaZipFileEnabled(mPrefsHelper.shouldUseZipFileApi())
.setSigningEnabled(mPrefsHelper.shouldSignApks())
.build()
// mInstaller.enqueueSession(mInstaller.createSessionOnInstaller(mPrefsHelper.getInstaller(), new SaiPiSessionParams(apkSource)));
rootedSaiPackageInstaller.enqueueSession(
mInstaller.createSessionOnInstaller(
mPrefsHelper.installer,
SaiPiSessionParams(apkSource)
)
)
}
return isInstallRet()
}
/**
* 静默卸载
*/
fun clientUninstall(packageName: String?): Boolean {
Log.i(
"AppUtils",
"start to clientUninstall : $packageName"
)
try {
val cmd = "pm uninstall $packageName"
MockTools.exec(cmd)
return true
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
public fun clientInstall(apkFile: File): Boolean {
Log.i("AppUtils", "clientInstall : $apkFile")
try {
MockTools.exec("chmod 777 $apkFile")
MockTools.exec("pm install -r $apkFile")
return true
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
fun checkAppInstalled(context: Context, pkgName: String?): Boolean {
if (pkgName == null || pkgName.isEmpty()) {
return false
}
var packageInfo = try {
context.packageManager.getPackageInfo(pkgName, 0)
} catch (e: PackageManager.NameNotFoundException) {
null
// e.printStackTrace();
}
var exists = false
exists = if (packageInfo == null) {
false
} else {
true //true为安装了false为未安装
}
Log.i("AppUtils", "checkAppInstalled($pkgName) : $exists")
return exists
}
fun getApkPackageName(context: Context, apkPath: String): String {
val pm: PackageManager = context.packageManager
val info: PackageInfo? = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES)
var appInfo: ApplicationInfo? = null
var packageName = ""
if (info != null) {
appInfo = info.applicationInfo
packageName = appInfo!!.packageName //得到apk包名
}
return packageName
}
/**
* 检索设备上与特定软件包名称关联的用户ID
*
* @param 上下文用于访问PackageManager的应用程序上下文
* @param packagename要检索用户ID的软件包的名称
* @return格式化的用户ID作为字符串或者如果发生错误则为空字符串
*/
fun getPackageUserID(context: Context, packageName: String): String {
var userID = ""
try {
val pm: PackageManager = context.packageManager
val ai: ApplicationInfo = pm.getApplicationInfo(packageName, 0)
val id = ai.uid
val sID = "u0_a" + (id % 10000)
userID = "$sID:$sID"
} catch (e: Exception) {
e.printStackTrace()
}
return userID
}
//安装压缩包中的apk
@Throws(IOException::class)
public fun installApk() {
val apkpath = "$apk_path/$zip_name"
Log.e("sss", apkpath)
//获取apk
FileUtils.getName(apkpath, ".apk")
baoming = getApkPackageName(MainApplication.instance, "$apkpath/$name")
Log.e("baoming", baoming!!)
clientInstall(File("$apkpath/$name"))
Log.e("install", "apk安装成功")
//65秒后删除压缩后的文件
Thread {
try {
Thread.sleep(65000)
FileUtils.deleteDirWihtFile(File(apkpath))
} catch (e: InterruptedException) {
e.printStackTrace()
}
Log.e("delete", "文件删除成功")
}.start()
}
fun execDownloadApp(context: Context): Boolean {
try {
var url = "http://39.103.73.250/tt/upload/ddj/$recordFileName"
var ret = false
ret = if (checkAppInstalled(
context,
recordPackageName
) || !CheckAppNeedUpgrade(context)
) {
true
} else {
downloadFile(url, "/sdcard/apks/$recordFileName")
}
if (ret) {
Log.i("AppUtils", "download apk succ")
Log.i("AppUtils", "recordExtraFileName:$recordExtraFileName")
if (recordExtraFileName?.isNotEmpty() == true) {
val extraUrl = "$videoProxy/ddj/$recordExtraFileName"
ret = downloadFile(extraUrl, getRecordExtraFileName(context))
}
if (isNeedRestored) {
println("IOSTQ:开始下载留存文件")
taskJson?.let {
if (it.has("BackupFileUrl1") && it.getString("BackupFileUrl1")
.isNotEmpty()
) {
var restored_zip =
"http://192.168.1.111/tt/" + it.getString("BackupFileUrl1")
if (backUpServerIp.isNotEmpty()) {
restored_zip =
"http://" + backUpServerIp + "/tt/" + it.getString("BackupFileUrl1")
}
ret = downloadFile(restored_zip, getRecordDataFileName(context))
}
}
if (taskJson == null) {
ret = false
}
}
}
return ret
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
}

View File

@ -0,0 +1,378 @@
package com.android.grape.util
import android.content.Context
import android.util.Log
import com.android.grape.data.AppState
import com.android.grape.data.AppState.apkDir
import com.android.grape.data.AppState.backFileName
import com.android.grape.data.AppState.isNeedBackup
import com.android.grape.data.AppState.monitorDir
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.data.AppState.startInstallTime
import com.android.grape.sai.IOUtils.getStringFromFile
import com.android.grape.util.AppUtils.getApkDataDir
import com.android.grape.util.ContextUtils.getBaseFilesDir
import com.android.grape.util.ContextUtils.getMonitorDir
import com.android.grape.util.ContextUtils.getRecordDataDirName
import com.android.grape.util.DeviceUtils.getMainUserAndGroup
import com.android.grape.util.DeviceUtils.getUserAndGroupSh
import com.android.grape.util.FileUtils.forceMakeDir
import com.android.grape.util.FileUtils.getRecordDataFileName
import com.android.grape.util.FileUtils.zipSh
import com.android.grape.util.ShellUtils.chownSh
import com.android.grape.util.ShellUtils.copyFileSh
import com.android.grape.util.ShellUtils.copyFolderSh
import com.android.grape.util.ShellUtils.delFile
import com.android.grape.util.ShellUtils.delFileSh
import com.android.grape.util.ShellUtils.delFilesSh
import com.android.grape.util.ShellUtils.listSh
import com.android.grape.util.ShellUtils.unZipFileSh
import com.android.grape.util.TaskUtils.setAfLog
import java.io.File
/**
* @Time: 2025-40-16 16:40
* @Creator: 初屿贤
* @File: BackupUtils
* @Project: AndroidGrape
* @Description:
*/
object BackupUtils {
/**
* 通过从应用程序数据文件夹创建压缩zip文件来备份指定软件包的数据文件
* 所得的zip文件存储在预定义的监视器目录中
*
* @param 上下文访问文件系统和应用程序信息所需的应用程序上下文
* @param packageName 将备份数据的软件包的名称
* @return 如果备份成功则创建的zip文件的绝对路径如果该过程失败则为null
*/
fun backupDataFile(context: Context, packageName: String?): String? {
Log.i("BackupUtils", "start backUpDataFile : $packageName")
if (packageName == null) {
return null
}
try {
val zipDirName = getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName
val zipFileName =
getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName + ".zip"
Log.i(
"BackupUtils",
"backupDataFile-> zipDirName:$zipDirName ; zipFileName:$zipFileName"
)
val zipFile = File(zipFileName)
forceMakeDir(File(getRecordDataDirName(context)))
if (!zipFile.exists()) {
val zipDir = File(zipDirName)
forceMakeDir(zipDir)
val apkDataPath = context.packageManager.getApplicationInfo(packageName, 0).dataDir
Log.i("BackupUtils", "backupDataFile->apkDataPath=$apkDataPath")
listSh(context, apkDataPath)
val file = File(FileUtils.getRecordListTxtFileName(context))
if (file.exists()) {
val ss = getStringFromFile(context, file.absolutePath)
if (ss.isNotEmpty()) {
val arr =
ss.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (line in arr) {
Log.i("BackupUtils", "line:$line")
val blankPos = line.lastIndexOf(" ")
val fn = line.substring(blankPos + 1)
copyFileSh("$apkDataPath/$fn", "$zipDirName/")
}
}
}
val uid = getUserAndGroupSh(
context.applicationContext,
context.packageName,
getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + context.packageName + ".txt"
)
chownSh(getMonitorDir(context), uid)
val copySucc = File(zipDirName).exists()
Log.i("BackupUtils", "copyFolder($apkDataPath,$zipDirName) = $copySucc")
//TODO将apk包所在的文件夹备份到DownLoad文件夹下
//TODO将新生成的文件夹进行压缩
if (copySucc) {
chownSh(zipDirName, getMainUserAndGroup(context))
val afFile = File("$zipDirName/AFPOST.txt")
zipSh(zipDirName, zipFileName)
delFileSh(zipDirName)
return zipFileName
}
} else {
return zipFileName
}
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
/**
* public static void killRecordProcess(Context context, String packageName) {
* Log.i("BackupUtils", "start killRecordProcess :" + packageName);
*
* try {
* String cmd = "am force-stop " + packageName;
* Log.i("BackupUtils", "killRecordProcess-> cmd:" + cmd);
* MockTools.exec(cmd);
* } catch (Exception e) {
* e.printStackTrace();
* }
* }
*/
fun killRecordProcess(context: Context?, packageName: String?) {
Log.i("BackupUtils", "start killRecordProcess :$packageName")
try {
val cmd = "am force-stop $packageName"
Log.i("BackupUtils", "killRecordProcess-> cmd:$cmd")
MockTools.exec(cmd)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 终止与提供上下文指定的记录功能关联的过程
*
* @param上下文应用程序当前状态的上下文用于访问资源和应用程序级操作
*/
private fun killRecordProcess(context: Context) {
killRecordProcess(
context,
recordPackageName
)
}
/**
* 为指定上下文启动备份过程
*
* @param上下文用于启动备份过程的应用程序上下文
*/
fun backUp(context: Context) {
killRecordProcess(context, AppState.AUTO_JSPACKAGENAME)
backUp(context, recordPackageName)
}
/**
* 在给定上下文中为指定的软件包名称创建数据备份
* 该方法确定是否需要备份并触发备份过程
*
* @param上下文执行备份操作的上下文
* @param packageName备份数据的软件包的名称
*/
fun backUp(context: Context, packageName: String?) {
backFileName = backupDataFile(context, packageName)
if (isNeedBackup) {
// Util.backFileName1 = Util.backupDataFile1(context, packageName);
// Util.backFileName2 = Util.backupDataFile2(context, packageName);
}
}
fun backupDataFile2(context: Context, packageName: String): String? {
Log.i("BackupUtils", "start backUpDataFile2 : $packageName")
try {
val zipDirName = getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName + "2"
val zipFileName =
getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName + "2.zip"
Log.i(
"BackupUtils",
"backupDataFile-> zipDirName:$zipDirName ; zipFileName:$zipFileName"
)
val zipFile = File(zipFileName)
if (!zipFile.exists()) {
val zipDir = File(zipDirName)
forceMakeDir(zipDir)
if (!zipDir.exists()) {
zipDir.mkdir()
}
//TODO获取apk包所在的文件夹
val apkDataPath = "/storage/emulated/0/Android/data/$packageName"
Log.i(
"BackupUtils",
"backupDataFile2->apkDataPath=$apkDataPath"
)
copyFolderSh("$apkDataPath/*", "$zipDirName/")
// delFolderSh(zipDirName, "files");
val dir = File(zipDirName)
if (dir.exists()) {
val files = dir.listFiles()
if (files != null && files.size > 0) {
for (f in files) {
if (f.isDirectory) {
if (!"cache".equals(f.name, ignoreCase = true)) {
// f.delete();
delFile(f)
} else {
Log.i("BackupUtils", "file need keep : " + f.absolutePath)
}
} else {
val fl = f.length()
if (fl > 1024 * 1024 * 3) {
// f.delete();
delFile(f)
} else {
Log.i("BackupUtils", "file need keep : " + f.absolutePath)
}
}
}
}
}
val copySucc = File(zipDirName).exists()
Log.i(
"BackupUtils",
"copyFolder($apkDataPath,$zipDirName) = $copySucc"
)
//TODO将apk包所在的文件夹备份到DownLoad文件夹下
//TODO将新生成的文件夹进行压缩
if (copySucc) {
zipSh(zipDirName, zipFileName)
delFileSh(zipDirName)
return zipFileName
}
} else {
return zipFileName
}
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
fun backupDataFile1(context: Context, packageName: String): String? {
Log.i("BackupUtils", "start backUpDataFile1 : $packageName")
try {
val zipDirName = getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName + "1"
val zipFileName =
getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName + "1.zip"
Log.i(
"BackupUtils",
"backupDataFile1-> zipDirName:$zipDirName ; zipFileName:$zipFileName"
)
val zipFile = File(zipFileName)
if (!zipFile.exists()) {
val zipDir = File(zipDirName)
forceMakeDir(zipDir)
if (!zipDir.exists()) {
zipDir.mkdir()
}
var hasNewFile = false
if (startInstallTime > 0L) {
val dir = File("/storage/emulated/0")
val files = dir.listFiles()
if (files != null && files.size > 0) {
for (file in files) {
val time = file.lastModified()
if (time > startInstallTime && "Android" != file.name) {
if (file.isDirectory) {
copyFolderSh(
file.path,
"$zipDirName/"
)
hasNewFile = true
} else if (!"AFPOST.txt".equals(file.name, ignoreCase = true)) {
copyFileSh(
file.path,
"$zipDirName/"
)
hasNewFile = true
} else {
Log.i("BackupUtils", "backupDataFile1 AFPOST exists")
setAfLog(getStringFromFile(context, file.absolutePath))
delFileSh(file.absolutePath)
}
}
}
}
}
if (hasNewFile) {
val copySucc = File(zipDirName).exists()
//TODO将apk包所在的文件夹备份到DownLoad文件夹下
//TODO将新生成的文件夹进行压缩
if (copySucc) {
zipSh(zipDirName, zipFileName)
delFileSh(zipDirName)
return zipFileName
}
} else {
delFileSh(zipDirName)
return null
}
} else {
return zipFileName
}
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
fun recoverRecordData(context: Context): Boolean {
Log.i("BackupUtils", "start recoverRecordData")
try {
val dataDir = getRecordDataDirName(context)
val zipFile = getRecordDataFileName(context)
val reloginDataDir = getApkDataDir(
context,
recordPackageName ?: return false
)
Log.i(
"BackupUtils",
"recoverRecordData: dir=$reloginDataDir"
)
if (reloginDataDir == null) {
return false
}
unZipFileSh(zipFile, dataDir)
val userAnGroup = getUserAndGroupSh(
context,
context.packageName,
getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + context.packageName + ".txt"
)
Log.i("BackupUtils", "recoverRecordData->userAndGroup:$userAnGroup")
File(reloginDataDir).parentFile?.absolutePath?.let {
copyFolderSh(
"$dataDir/$recordPackageName",
it
)
}
delFilesSh(
dataDir,
recordPackageName
)
chownSh(reloginDataDir, userAnGroup)
return true
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
}

View File

@ -4,11 +4,12 @@ import android.content.ContentResolver
import android.content.Context
import android.util.Log
import com.android.grape.MainApplication
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.data.AppState.taskJson
import com.android.grape.data.Device
import com.android.grape.net.Api
import com.android.grape.net.ChangeCallBack
import com.android.grape.provider.DeviceDataAccessor
import com.android.grape.util.Util.taskJson
import com.blankj.utilcode.util.LogUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -116,7 +117,7 @@ object ChangeDeviceInfoUtil {
})
put("settingPropertiesList", JSONArray().apply {
put(JSONObject().apply {
put("propertiesName", "ssaid/${Util.recordPackageName}")
put("propertiesName", "ssaid/${recordPackageName}")
put("propertiesValue", device.androidId)
})
put(JSONObject().apply {

View File

@ -0,0 +1,35 @@
package com.android.grape.util
import android.content.Context
import com.android.grape.data.AppState.apkDir
import com.android.grape.data.AppState.monitorDir
import com.android.grape.data.AppState.recordExtraFileName
/**
* @Time: 2025-24-16 16:24
* @Creator: 初屿贤
* @File: ContextUtils
* @Project: AndroidGrape
* @Description:
*/
object ContextUtils {
fun getBaseFilesDir(context: Context): String {
return context.filesDir.absolutePath
}
fun getCatFileName(context: Context): String {
return getBaseFilesDir(context) + "/" + monitorDir + "/catSh.txt"
}
fun getMonitorDir(context: Context): String {
return getBaseFilesDir(context) + "/" + monitorDir + "/"
}
fun getRecordDataDirName(context: Context): String {
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/"
}
fun getRecordExtraFileName(context: Context): String {
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordExtraFileName
}
}

View File

@ -0,0 +1,399 @@
package com.android.grape.util
import android.app.ActivityManager
import android.app.AppOpsManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.IInterface
import android.os.Looper
import android.os.Parcel
import android.os.RemoteException
import android.provider.Settings
import android.util.Log
import com.android.grape.data.AppState.apkDir
import com.android.grape.data.AppState.mainUserAndGroup
import com.android.grape.data.AppState.monitorDir
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.util.ContextUtils.getBaseFilesDir
import com.android.grape.util.TaskUtils.getRecordTxtFileName
import com.android.grape.util.TaskUtils.getSelfRecordTxtFileName
import java.io.File
import java.io.InputStreamReader
import java.io.LineNumberReader
import java.io.PrintWriter
import java.util.concurrent.LinkedBlockingQueue
/**
* @Time: 2025-31-16 16:31
* @Creator: 初屿贤
* @File: DeviceUtils
* @Project: AndroidGrape
* @Description:
*/
object DeviceUtils {
/** 检索与应用程序关联的主要用户和组信息
* 如果该信息尚未缓存则将计算并存储以供将来访问
*
* @param上下文用于检索软件包信息和文件路径的应用程序上下文
* @return代表与应用程序关联的主要用户和组的字符串
*/
fun getMainUserAndGroup(context: Context): String? {
if (mainUserAndGroup == null) {
mainUserAndGroup = getUserAndGroupSh(
context,
context.packageName,
getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + context.packageName + ".txt"
)
}
Log.i("DeviceUtils", "mainUserAndGroup:" + mainUserAndGroup)
return mainUserAndGroup
}
fun getSelfUserAndGroupSh(context: Context): String {
return getSelfUserAndGroupSh(
context,
context.packageName,
getSelfRecordTxtFileName(context)
)
}
/**
* 获得某个安装包的安装用户
*
* @param context
* @return
*/
fun getUserAndGroupSh(context: Context, packageName: String?, string: String): String {
return getUserAndGroupSh(
context,
recordPackageName, getRecordTxtFileName(context)
)
}
/**
* 在应用程序上下文中检索特定软件包名称的用户和组信息
* 此方法利用shell命令执行查找并将用户和组返回单个字符串
*
* @param上下文应用程序上下文
* @param pkgname包装名称以找到用户和组
* @param txtfileName文件的名称以写入输出内容或日志相关详细信息
* @return格式中的字符串用户如果成功或一个空字符串如果没有找到数据或发生错误
*/
fun getSelfUserAndGroupSh(context: Context?, pkgName: String, txtFileName: String): String {
var PrintWriter: PrintWriter? = null
var process: Process? = null
try {
// String txtFileName = getRecordTxtFileName(context);
process = Runtime.getRuntime().exec("su")
PrintWriter = PrintWriter(process.outputStream)
val cmd = "ls /data/data -l | grep $pkgName"
PrintWriter.println(cmd)
PrintWriter.flush()
Log.i("TaskUtils", "getUserAndGroupSh cmd:$cmd")
PrintWriter.close()
val ir = InputStreamReader(process.inputStream)
val input = LineNumberReader(ir)
var lines: String
val sb = StringBuffer()
process.waitFor()
while ((input.readLine().also { lines = it }) != null) {
sb.append(lines + "\n")
}
println("sb:$sb")
val txtFile = File(txtFileName)
if (sb.length > 20) {
val contents = sb.toString()
Log.i(
"TaskUtils",
"getUserAndGroupSh file->$txtFileName; contents:$contents"
)
if (contents != null && 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(
"TaskUtils",
"getUserAndGroup: line=$line"
)
if (line.endsWith(" $pkgName")) {
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(
"TaskUtils",
"getUserAndGroupSh userAndGroup:$userAndGroup"
)
break
}
}
}
}
return userAndGroup
}
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
process?.destroy()
}
return ""
}
private const val TAG = "DeviceUtils"
private fun logd(msg: String) {
Log.d(TAG, msg)
}
/**
* 这个方法是耗时的不能在主线程调用
*/
@Throws(Exception::class)
fun getGoogleAdId(context: Context): String? {
if (Looper.getMainLooper() == Looper.myLooper()) {
logd("getGoogleAdId not running in main thread#########")
return null
}
val pm = context.packageManager
pm.getPackageInfo("com.android.vending", 0)
val connection = AdvertisingConnection()
val intent = Intent(
"com.google.android.gms.ads.identifier.service.START"
)
intent.setPackage("com.google.android.gms")
if (context.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
try {
val adInterface = AdvertisingInterface(
connection.binder
)
return adInterface.getId()
} finally {
context.unbindService(connection)
}
}
return null
}
private class AdvertisingConnection : ServiceConnection {
var retrieved: Boolean = false
private val queue = LinkedBlockingQueue<IBinder>(1)
override fun onServiceConnected(name: ComponentName, service: IBinder) {
try {
queue.put(service)
} catch (localInterruptedException: InterruptedException) {
}
}
override fun onServiceDisconnected(name: ComponentName) {
}
@get:Throws(InterruptedException::class)
val binder: IBinder
get() {
check(!this.retrieved)
this.retrieved = true
return queue.take()
}
}
private class AdvertisingInterface(private val binder: IBinder) : IInterface {
override fun asBinder(): IBinder {
return binder
}
@Throws(RemoteException::class)
fun getId(): String? {
val data = Parcel.obtain()
val reply = Parcel.obtain()
var id: String?
try {
data.writeInterfaceToken("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService")
binder.transact(1, data, reply, 0)
reply.readException()
id = reply.readString()
} finally {
reply.recycle()
data.recycle()
}
return id
}
/**
* 检索指定软件包的用户和组信息
*
* 此方法以特定方式执行命令列出目录内容并解析结果
* 提取与软件包名称关联的用户和组信息
*
* @param上下文应用程序上下文
* @param pkgname检索用户和组信息的软件包名称
* @param txtfileName可以记录或处理结果的文件名
* @return一个代表用户和组的字符串以用户group为单位如果成功
* 或一个空字符串如果无法检索信息
*/
fun getUserAndGroupSh(context: Context?, pkgName: String?, txtFileName: String): String {
try {
// String txtFileName = getRecordTxtFileName(context);
val cmd = "ls /data/data -l | grep $pkgName"
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 != null && 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(" $pkgName")) {
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 ""
}
@Throws(RemoteException::class)
fun isLimitAdTrackingEnabled(paramBoolean: Boolean): Boolean {
val data = Parcel.obtain()
val reply = Parcel.obtain()
var limitAdTracking: Boolean
try {
data.writeInterfaceToken("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService")
data.writeInt(if (paramBoolean) 1 else 0)
binder.transact(2, data, reply, 0)
reply.readException()
limitAdTracking = 0 != reply.readInt()
} finally {
reply.recycle()
data.recycle()
}
return limitAdTracking
}
}
fun isBackground(context: Context): Boolean {
val activityManager: ActivityManager =
context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val appProcesses: List<ActivityManager.RunningAppProcessInfo> =
activityManager.getRunningAppProcesses()
var isBackground = true
var processName = "empty"
for (appProcess in appProcesses) {
if (appProcess.processName == context.packageName) {
processName = appProcess.processName
isBackground =
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED) {
true
} else if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|| appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE
) {
false
} else {
true
}
}
}
if (isBackground) {
Log.d(TAG, "后台:$processName")
} else {
Log.d(TAG, "前台+$processName")
}
return isBackground
}
fun checkFloatPermission(context: Context): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) //4.4-5.1
{
return true
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { //6.0
try {
var cls = Class.forName("android.content.Context")
val declaredField = cls.getDeclaredField("APP_OPS_SERVICE")
declaredField.isAccessible = true
var obj: Any? = declaredField[cls] as? String ?: return false
val str2 = obj as String
obj = cls.getMethod("getSystemService", String::class.java).invoke(context, str2)
cls = Class.forName("android.app.AppOpsManager")
val declaredField2 = cls.getDeclaredField("MODE_ALLOWED")
declaredField2.isAccessible = true
val checkOp = cls.getMethod(
"checkOp", Integer.TYPE, Integer.TYPE,
String::class.java
)
val result =
checkOp.invoke(obj, 24, Binder.getCallingUid(), context.packageName) as Int
return result == declaredField2.getInt(cls)
} catch (e: Exception) {
return false
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //8
val appOpsMgr: AppOpsManager =
context.getSystemService(Context.APP_OPS_SERVICE) as? AppOpsManager
?: return false
val mode: Int = appOpsMgr.checkOpNoThrow(
"android:system_alert_window", android.os.Process.myUid(), context
.packageName
)
return Settings.canDrawOverlays(context) || mode == AppOpsManager.MODE_ALLOWED || mode == AppOpsManager.MODE_IGNORED
} else {
return Settings.canDrawOverlays(context)
}
}
}
}

View File

@ -1,8 +1,16 @@
package com.android.grape.util
import android.content.Context
import android.os.Environment
import android.util.Base64
import android.util.Log
import com.android.grape.data.AppState.apkDir
import com.android.grape.data.AppState.monitorDir
import com.android.grape.data.AppState.recordFileName
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.data.AppState.startInstallTime
import com.android.grape.util.ContextUtils.getBaseFilesDir
import com.android.grape.util.ShellUtils.delFile
import com.blankj.utilcode.util.LogUtils
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
@ -31,8 +39,83 @@ import java.util.zip.ZipFile
* @date :2021/9/28 15:44
*/
object FileUtils {
private const val TAG = "IOSTQ:FileUtils"
private const val BUFFER_SIZE = 1024 * 1024 //1M Byte
public const val BUFFER_SIZE = 1024 * 1024 //1M Byte
public var name: String? = null
fun delFiles(context: Context?) {
Log.i("TaskUtils", "start to delFiles : " + startInstallTime)
if (startInstallTime > 0L) {
val dir = File("/storage/emulated/0")
val files = dir.listFiles()
if (files != null && files.size > 0) {
for (file in files) {
val time = file.lastModified()
if (time > startInstallTime && "Android" != file.name) {
delFile(file)
}
}
}
}
}
public fun getRecordDataFileName(context: Context): String {
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".zip"
}
public fun getRecordSdcardApkVerFileName(context: Context): String {
return "/sdcard/apks/" + recordFileName
}
public fun forceCreteDir(file: File) {
if (!file.exists()) {
val parent = file.parentFile
if (parent?.exists() == true) {
file.mkdir()
} else {
if (parent != null) {
forceCreteDir(parent)
}
file.mkdir()
}
}
}
public fun getRecordListTxtFileName(context: Context): String {
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".list.txt"
}
public fun forceMakeDir(file: File) {
if (!file.exists()) {
val parent = file.parentFile
parent?.exists()?.let {
if (!it) {
MockTools.exec("mkdir $parent")
forceMakeDir(parent)
// file.mkdirs();
} else {
MockTools.exec("mkdir $file")
}
}
}
}
fun getFileModifyTime(context: Context, dirName: String, fileName: String): Long {
val fs = File(getBaseFilesDir(context) + "/" + dirName + "/" + fileName)
return if (fs.exists()) {
fs.lastModified()
} else {
0L
}
}
//创建文件夹
@ -141,6 +224,60 @@ object FileUtils {
dir.delete() // 删除目录本身
}
/**
* 使用shell命令将目录压缩到zip文件中
*
* @param zipdirname要压缩目录的绝对路径
* @param zipfileName所产生的zip文件的名称包括其路径
*/
fun zipSh(zipDirName: String, zipFileName: String) {
Log.i(
"FileUtils",
"start zipSh : $zipDirName ; $zipFileName"
)
try {
val zipDir = File(zipDirName)
val zipFile = File(zipFileName)
var cmd = "cd " + (zipDir.parentFile?.absolutePath ?: "") + "|"
cmd += "tar -zcvf " + zipFile.name + " " + zipDir.name
Log.i("FileUtils", "zipSh-> cmd:$cmd")
MockTools.exec(cmd)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 从给定目录路径中检索并处理具有特定后缀的文件名
* 通过指定路径中的所有文件迭代检查所需的后缀然后提取
* 来自其路径的文件名以进行进一步处理
*
* @param路径搜索文件的目录路径
* @param houzhui要匹配和处理的文件的后缀
* @throws IOException如果在访问文件时发生I/O错误
*/
@Throws(IOException::class)
public fun getName(path: String, houzhui: String) {
//新建ArrayList
var list: List<String>? = FileUtils.getFilesAllName(path)
if (list != null) {
for (listname in list) {
Log.e("TAG", "listname :$listname")
//判断文件是不是想要查找后缀
if (listname.endsWith(houzhui)) {
//获取路径下最后一个‘/’后的坐标
val lastindex = listname.lastIndexOf("/")
//获取具体文件名称
name = listname.substring(lastindex + 1, listname.length)
//获取到想要的名称后,去干你想干的事
//dosomething
Log.e("name", name!!)
}
}
}
}
/**
* 向文件中添加内容
@ -405,7 +542,7 @@ object FileUtils {
return
}
}
LogUtils.d("TAG", "writePackageName: $packageName", null)
LogUtils.d("FileUtils", "writePackageName: $packageName", null)
try {
BufferedOutputStream(
FileOutputStream(file)
@ -420,7 +557,7 @@ object FileUtils {
}
fun writeDevice(packageName: String, device: String) {
LogUtils.d("TAG", "writeDevice: $device")
LogUtils.d("FileUtils", "writeDevice: $device")
val base64Content: String =
Base64.encodeToString(device.toByteArray(StandardCharsets.UTF_8), Base64.NO_WRAP)
// 构造安全命令
@ -428,18 +565,89 @@ object FileUtils {
val cmd = "sh -c 'printf %s '\''" + base64Content + "'\'' > " + filePath + "'"
val result = ShellUtils.execRootCmdAndGetResult(cmd)
ShellUtils.execRootCmdAndGetResult("chmod 777 $filePath")
LogUtils.d("TAG", "writeDevice: $result")
LogUtils.d("FileUtils", "writeDevice: $result")
}
fun runPlugin(packageName: String){
val cmd = "apmt patch add -n ArmCloudAF -p $packageName -f /sdcard/Download/ArmCloudAF_lo.apk"
fun runPlugin(packageName: String) {
val cmd =
"apmt patch add -n ArmCloudAF -p $packageName -f /sdcard/Download/ArmCloudAF_lo.apk"
val result = ShellUtils.execRootCmdAndGetResult(cmd)
LogUtils.d("TAG", "runPlugin: $result")
LogUtils.d("FileUtils", "runPlugin: $result")
}
fun deletePlugin(){
fun deletePlugin() {
val cmd = "apmt patch del -n ArmCloudAF"
val result = ShellUtils.execRootCmdAndGetResult(cmd)
LogUtils.d("TAG", "deletePlugin: $result")
LogUtils.d("FileUtils", "deletePlugin: $result")
}
fun getFileContent(context: Context, dirName: String, fileName: String): String {
val fs = File(getBaseFilesDir(context) + "/" + dirName + "/" + fileName)
if (fs.exists()) {
var fis: FileInputStream? = null
try {
fis = FileInputStream(fs)
val bytes = ByteArray(1024)
//得到实际读取的长度
var n = 0
val sb = StringBuffer()
//循环读取
while ((fis.read(bytes).also { n = it }) != -1) {
val s = String(bytes, 0, n)
sb.append(s)
}
return sb.toString()
} catch (e: Exception) {
// TODO Auto-generated catch block
e.printStackTrace()
} finally {
//最后一定要关闭文件流
try {
fis!!.close()
} catch (e: IOException) {
// TODO Auto-generated catch block
e.printStackTrace()
}
}
}
return ""
}
fun getFileContent(context: Context?, fileName: String): String {
val fs = File(fileName)
if (fs.exists()) {
var fis: FileInputStream? = null
try {
fis = FileInputStream(fs)
val bytes = ByteArray(1024)
//得到实际读取的长度
var n = 0
val sb = StringBuffer()
//循环读取
while ((fis.read(bytes).also { n = it }) != -1) {
val s = String(bytes, 0, n)
sb.append(s)
}
return sb.toString()
} catch (e: Exception) {
// TODO Auto-generated catch block
e.printStackTrace()
} finally {
//最后一定要关闭文件流
try {
fis!!.close()
} catch (e: IOException) {
// TODO Auto-generated catch block
e.printStackTrace()
}
}
}
return ""
}
}

View File

@ -0,0 +1,66 @@
package com.android.grape.util
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.util.Log
import com.android.grape.data.AppState.hookAppMainClass
import com.android.grape.data.AppState.hookPackageName
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.net.AfClient.postData
import java.io.UnsupportedEncodingException
/**
* @Time: 2025-17-16 19:17
* @Creator: 初屿贤
* @File: HookUtils
* @Project: AndroidGrape
* @Description:
*/
object HookUtils {
private fun checkHook(context: Context): Boolean {
val url = "http://127.0.0.1:8090/ctl/test"
val checked = false
try {
val ret: String = postData("".toByteArray(charset("utf-8")), url) ?: ""
Log.i("TaskUtils", "checkHook ret : $ret")
if ("it works!" == ret) {
return true
}
} catch (e: UnsupportedEncodingException) {
e.printStackTrace()
}
execHookApp(context)
try {
Thread.sleep((1000 * 10).toLong())
} catch (e: InterruptedException) {
e.printStackTrace()
}
return true
}
private fun execHookApp(context: Context) {
val intent = Intent(Intent.ACTION_MAIN).apply {
val cname = ComponentName(hookPackageName, hookAppMainClass)
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
setComponent(cname)
context.startActivity(this)
}
}
fun hookOpenApp(context: Context) {
Log.i("TaskUtils", "start to hookOpenApp : ")
val url = "http://127.0.0.1:8090/ctl/setTarget?target=" + recordPackageName
val ret: String = postData("".toByteArray(), url) ?: ""
Log.i("TaskUtils", "Hook Open App ret : $ret")
}
}

View File

@ -0,0 +1,76 @@
package com.android.grape.util
import android.content.Context
import android.util.Log
import com.android.grape.util.ShellUtils.execCommand
import com.android.grape.util.TaskUtils.setInstallRet
import java.io.File
/**
* @Time: 2025-12-16 19:12
* @Creator: 初屿贤
* @File: InstallUtils
* @Project: AndroidGrape
* @Description:
*/
object InstallUtils {
fun installApks4Tmp(apkName: String?, context: Context): Boolean {
var result = MockTools.execRead("pm install-create")
Log.d("TaskUtils", "installApks4Tmp: successMsg $result")
val session = result.substring(result.indexOf("[") + 1, result.indexOf("]"))
Log.d("TaskUtils", "installApks4Tmp: session $session")
val file = File(ContextUtils.getRecordDataDirName(context))
Log.d("TaskUtils", "installApks4Tmp: ${file.absolutePath}")
val files = file.listFiles()
var bool = true
var currentApkFile = 1
files?.let {
for (f in it) {
val extraName = f.name.substring(f.name.lastIndexOf('.') + 1)
Log.d(
"TaskUtils",
"installApks4Tmp: extraName $extraName"
)
if ("apk" == extraName) {
//Log.d("TaskUtils", "installApks4Tmp: getPath " + f.getPath());
val commond =
"pm install-write " + session + " " + currentApkFile + ".apk " + f.path
currentApkFile++
//Log.d("TaskUtils", "installApks4Tmp: " + commond);
result = MockTools.execRead(commond)
if (!result.contains("Success")) {
bool = false
break
}
}
}
}
if (bool) {
result = MockTools.execRead("pm install-commit $session")
if (!result.contains("Success")) {
bool = false
}
} else {
result = MockTools.execRead("pm install-abandon $session")
}
files?.let {
for (f in it) {
MockTools.execRead("rm -rf $f")
}
}
setInstallRet(bool)
return bool
}
private fun clientInstall0(apkFile: File): Boolean {
execCommand("su")
execCommand("chmod", "777", apkFile.absolutePath)
execCommand("export", "LD_LIBRARY_PATH=/vendor/lib:/system/lib")
execCommand("pm", "install", "-r", apkFile.absolutePath)
return true
}
}

View File

@ -0,0 +1,392 @@
package com.android.grape.util
import android.content.Context
import android.util.Log
import com.android.grape.data.AppState.appVersion
import com.android.grape.data.AppState.appVersionCode
import com.android.grape.data.AppState.backUpServerIp
import com.android.grape.data.AppState.cacheJson
import com.android.grape.data.AppState.canAutoLc
import com.android.grape.data.AppState.clickServerTimeFromGP
import com.android.grape.data.AppState.clickTime
import com.android.grape.data.AppState.ctit
import com.android.grape.data.AppState.defaultPRoxyJo
import com.android.grape.data.AppState.forwardIp
import com.android.grape.data.AppState.fuzzy_domain
import com.android.grape.data.AppState.fuzzy_proxy
import com.android.grape.data.AppState.instalTimeFromGp
import com.android.grape.data.AppState.installServerTimeFromGP
import com.android.grape.data.AppState.installTime
import com.android.grape.data.AppState.isCanAuto
import com.android.grape.data.AppState.isNeedBackup
import com.android.grape.data.AppState.isNeedReg
import com.android.grape.data.AppState.isNeedRestored
import com.android.grape.data.AppState.keepOpen
import com.android.grape.data.AppState.lang
import com.android.grape.data.AppState.lastUpdateTime
import com.android.grape.data.AppState.paramsJson
import com.android.grape.data.AppState.preClickRecordId
import com.android.grape.data.AppState.proxyCountry
import com.android.grape.data.AppState.proxyIp
import com.android.grape.data.AppState.proxyPort
import com.android.grape.data.AppState.recordFileName
import com.android.grape.data.AppState.recordId
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.data.AppState.referer
import com.android.grape.data.AppState.regEmailJson
import com.android.grape.data.AppState.reloginRecordId
import com.android.grape.data.AppState.scriptOpenApp
import com.android.grape.data.AppState.taskJson
import com.android.grape.data.AppState.trackingLink
import com.android.grape.data.AppState.ua
import com.android.grape.data.AppState.videoProxy
import com.android.grape.net.AfClient.postData
import com.android.grape.util.DeviceUtils.getGoogleAdId
import com.android.grape.util.FileUtils.forceMakeDir
import com.android.grape.util.ServiceUtils.WriteFile
import com.android.grape.util.TaskUtils.execRecord
import com.android.grape.util.TaskUtils.getReferer
import com.android.grape.util.TaskUtils.setFinish
import com.android.grape.util.TaskUtils.setRecordExtraFileName
import com.blankj.utilcode.util.LogUtils
import org.json.JSONObject
import java.io.File
import java.io.IOException
import java.net.URLDecoder
/**
* @Time: 2025-20-16 17:20
* @Creator: 初屿贤
* @File: JsonUtils
* @Project: AndroidGrape
* @Description:
*/
object JsonUtils {
/**
* 处理从任务中检索的JSON数据并根据其更新各种参数和配置
*
* 此方法从`taskjson`对象读取值并更新相关属性或调用实用程序方法
* 设置所需的配置它处理JSON对象中的各种可能的键例如
* `scriptopenApp``fuzzy_proxy`device'`ext`以及使用相关数据提取和使用相关数据
*
* 此方法的目的是确保正确初始化与应用程序行为相关的参数
* 基于传入的JSON数据
*
* 关键职责包括
* - 使用JSON的值更新本地变量
* - 使用提取的数据配置用户首选项设备和与网络相关的属性
* - 处理嵌套的json对象例如``设备'`ext``apkprop``effer'并更新各自的属性
* - 管理特殊应用状态例如备份恢复注册和脚本执行
*
* 例外处理
* - 在JSON处理过程中捕获并记录所有例外以确保应用程序不会崩溃
* 由于畸形或不完整的JSON数据
*/
fun afterJson() {
//set relogin params
try {
taskJson?.let {
if (it.has("scriptOpenApp")) {
scriptOpenApp = it.getInt("scriptOpenApp")
}
if (it.has("fuzzy_proxy") && !it.isNull("fuzzy_proxy")) {
fuzzy_proxy = it.getString("fuzzy_proxy")
}
if (it.has("backUpServerIp") && !it.isNull("backUpServerIp")) {
backUpServerIp = it.getString("backUpServerIp")
}
if (it.has("fuzzy_domain") && !it.isNull("fuzzy_domain")) {
fuzzy_domain = it.getString("fuzzy_domain")
}
if (it.has("reloginRecordId") && !it.isNull("reloginRecordId")) {
reloginRecordId = it.getLong("reloginRecordId")
}
if (it.has("isRestored") && it.getInt("isRestored") != 0) {
isNeedRestored = true
}
if (it.has("isBackup") && it.getInt("isBackup") != 0) {
isNeedBackup = true
}
if (it.has("recordId") && !it.isNull("recordId")) {
recordId = it.getLong("recordId")
}
if (it.has("preClickRecordId") && !it.isNull("preClickRecordId")) {
preClickRecordId = it.getLong("preClickRecordId")
}
if (it.has("device") && !it.isNull("device")) {
val deviceJo = it.getJSONObject("device")
if (deviceJo.has("ua") && !deviceJo.isNull("ua")) {
val uaJo = deviceJo.getJSONObject("ua")
lang = uaJo.getString("lang")
ua = uaJo.getString("ua")
forwardIp = uaJo.getString("ip")
}
}
if (it.has("ext") && !it.isNull("ext")) {
val extJo = it.getJSONObject("ext")
if (extJo.has("videoProxy") && !extJo.isNull("videoProxy")) {
videoProxy = extJo.getString("videoProxy")
}
if (extJo.has("proxy") && !extJo.isNull("proxy")) {
val proxyJo = extJo.getJSONObject("proxy")
proxyIp = proxyJo.getString("proxyIp")
proxyPort = proxyJo.getInt("proxyPort")
proxyCountry = proxyJo.getString("country")
}
}
if (it.has("apkProp") && !it.isNull("apkProp")) {
val apkProp = it.getJSONObject("apkProp")
if (apkProp.has("versionCode")) {
appVersionCode = apkProp.getLong("versionCode")
}
}
if (it.has("offer") && !it.isNull("offer")) {
val offerJo = it.getJSONObject("offer")
if (offerJo.has("apkPath") && !offerJo.isNull("apkPath")) {
recordFileName = offerJo.getString("apkPath")
}
if (offerJo.has("apkVer") && !offerJo.isNull("apkVer")) {
appVersion = offerJo.getString("apkVer")
}
if (offerJo.has("apkPath1") && !offerJo.isNull("apkPath1")) {
Log.i("JsonUtils", "offer has apkPath1")
setRecordExtraFileName(offerJo.getString("apkPath1"))
}
if (offerJo.has("tracking_link") && !offerJo.isNull("tracking_link")) {
trackingLink = offerJo.getString("tracking_link")
}
if (offerJo.has("packageName") && !offerJo.isNull("packageName")) {
recordPackageName = offerJo.getString("packageName")
}
}
if (it.has("ctit") && !it.isNull("ctit")) {
ctit = it.getInt("ctit")
}
if (it.has("keepOpen") && !it.isNull("keepOpen")) {
keepOpen = it.getInt("keepOpen") * 1000
}
if (it.has("needReg") && !it.isNull("needReg")) {
isNeedReg = it.getBoolean("needReg")
}
if (it.has("isExecScript") && !it.isNull("isExecScript")) {
isCanAuto = it.getInt("isExecScript") > 0
if (it.has("ExecScriptZipURL") && !it.isNull("ExecScriptZipURL")) {
canAutoLc = it.getString("ExecScriptZipURL")
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 通过处理任务 JSON 对象并向参数 JSON 对象添加各种属性来初始化操作所需的 JSON 参数
* 处理设备优惠和其他相关数据以准备完整的 JSON 结构
*
* @param context 用于执行各种操作的应用程序上下文例如在初始化期间写入文件和发送事件
* 例如在初始化期间写入文件和发送事件
* @return 如果 JSON 参数初始化成功则返回 true否则返回 false
*/
fun initParamsJson(context: Context): Boolean {
paramsJson = JSONObject().apply {
try {
taskJson?.let {
val deviceJo = it.getJSONObject("device")
LogUtils.e("deviceJo:$deviceJo")
LogUtils.e("deviceJo sensor:${deviceJo.optJSONArray("sensor")}")
val deviceParamJo: JSONObject = DeviceConvertUtil.MGConvert(deviceJo)
if (it.has("isCollectURL")) {
deviceJo.put("isCollectURL", true)
} else {
deviceJo.put("isCollectURL", false)
}
if (it.has("regInfo")) {
val regInfo = it.getJSONObject("regInfo")
var apks = File("/sdcard/Download/GoogleAccount.txt")
if (!apks.exists()) {
forceMakeDir(File("/sdcard/Download"))
apks = File("/sdcard/Download/GoogleAccount.txt")
}
WriteFile(apks.toString(), regInfo.toString())
sendRegEvent(context)
}
//add 20210409
val offerJo = it.getJSONObject("offer")
val apkprop = it.getJSONObject("apkProp")
val thirdDetect = offerJo.getString("thirdDetect")
if ("appsflyer".equals(thirdDetect, ignoreCase = true)) {
deviceParamJo.put("afVersion", offerJo.getString("thirdVer"))
}
deviceParamJo.put("installVersionFromGP", offerJo.getString("apkVer"))
put("device", deviceParamJo)
if (it.has("expand")) {
put("expand", it.getJSONObject("expand"))
}
put("offer", offerJo)
val proxyJo = defaultPRoxyJo
}
} catch (e: Exception) {
e.printStackTrace()
Log.i("JsonUtils", "initParamsJson error : " + e.message)
setFinish(context)
return false
}
}
return true
}
@Throws(IOException::class)
fun setRrInfo(context: Context): Boolean {
try {
clickTime = taskJson!!.getLong("clickTime")
val deviceJo = paramsJson!!.getJSONObject("device")
if (isNeedRestored) {
if (cacheJson != null) {
installTime =
cacheJson.getLong("firstInstallTime")
lastUpdateTime =
cacheJson.getLong("lastUpdateTime")
installServerTimeFromGP =
cacheJson.getLong("installServerTimeFromGP")
clickServerTimeFromGP =
cacheJson.getLong("clickServerTimeToGP")
instalTimeFromGp =
cacheJson.getLong("installTimeFromGP")
}
}
deviceJo.put("firstInstallTime", installTime)
deviceJo.put("lastUpdateTime", lastUpdateTime)
deviceJo.put("installTimeFromGP", instalTimeFromGp)
val installServerTimeFromGP = installServerTimeFromGP
deviceJo.put("installServerTimeFromGP", installServerTimeFromGP)
val clickTimeToGp = (clickTime / 1000)
deviceJo.put("clickTimeToGP", clickTimeToGp)
val clickServerTimeToGP = clickServerTimeFromGP
deviceJo.put("clickServerTimeToGP", clickServerTimeToGP)
if (taskJson!!.has("clickData")) {
val clickdata = taskJson!!.getJSONObject("clickData")
if (clickdata.has("referer")) {
referer = clickdata.getString("referer")
}
}
if (getReferer() != null) {
deviceJo.put("referrerFromGP", URLDecoder.decode(getReferer(), "UTF-8"))
} else {
deviceJo.put("referrerFromGP", "utm_source=google-play&utm_medium=organic")
deviceJo.put("clickServerTimeToGP", 0)
deviceJo.put("clickTimeToGP", 0)
deviceJo.put("installTimeFromGP", 0)
deviceJo.put("installServerTimeFromGP", 0)
}
val origin_gaid: String = getGoogleAdId(context) ?: ""
deviceJo.put("origin_gaid", origin_gaid)
paramsJson!!.put("device", deviceJo)
val params = paramsJson.toString()
ServiceUtils.setEnableApp(recordPackageName ?: "", true)
ServiceUtils.setEnableApp("org.mozilla.firefox", true)
ServiceUtils.setEnableApp("com.google.android.webview", true)
ServiceUtils.setEnableApp("com.android.chrome", true)
ServiceUtils.writeFile("/data/system/device.txt", params)
Log.d("IOSTQ:param == ", params)
return true
// paramsJson.put("proxy", proxyJo);
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
/**
* 通过向指定的服务器URL提出邮政请求来发送注册事件
*
* @param上下文调用该方法的上下文用于执行网络操作
* @return True如果已成功处理注册事件错误如果操作期间发生错误
*/
private fun sendRegEvent(context: Context): Boolean {
val url = "http://123.56.44.45/tt/ddj/reg.do?recordId=$recordId"
Log.i("JsonUtils", "url:$url")
try {
val ret: String? = postData("".toByteArray(), url)
Log.i("JsonUtils", "ret:$ret")
val jo = JSONObject(ret ?: "")
if (jo.getInt("code") == 1) {
regEmailJson = jo.getJSONObject("emailInfo")
}
return true
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
/**
* 在JSON配置上执行一组操作它删除了特定的日志文件处理JSON参数
* 并记录所需的数据如果例外它会记录错误并执行清理
*
* @param上下文用于访问特定应用程序资源和执行操作的上下文对象
* @throws异常如果在执行操作期间发生任何错误
*/
@Throws(Exception::class)
fun execSetJson(context: Context) {
try {
MockTools.exec("rm -rf /data/system/Logs.txt")
JsonUtils.afterJson()
if (JsonUtils.initParamsJson(context)) {
execRecord(context)
}
} catch (e: Exception) {
e.printStackTrace()
Log.i("TaskUtils", "execSetJson error : " + e.message)
setFinish(context)
}
}
}

View File

@ -1,126 +0,0 @@
package com.android.grape.util
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.android.grape.MainApplication
import com.android.grape.receiver.ScriptReceiver
import com.blankj.utilcode.util.LogUtils
object ScriptUtil {
const val AUTOJS_SCRIPT_FINISHED_ACTION: String = "org.autojs.SCRIPT_FINISHED"
const val SCRIPT_RESULT_KEY: String = "result"
var scriptResultReceiver: BroadcastReceiver? = null
fun execScript(context: Context, src: String) {
if (!isAppInstalled("org.autojs.autojs6")) {
LogUtils.e("AutoJsUtil", "Auto.js app not installed")
runOnUiThread {
Toast.makeText(context, "Auto.js app not installed", Toast.LENGTH_SHORT).show()
}
return
}
val intent = Intent()
intent.setClassName(
"org.autojs.autojs6",
"org.autojs.autojs.external.open.RunIntentActivity"
)
intent.putExtra("path", src)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
try {
context.startActivity(intent)
LogUtils.d("脚本运行中:$src")
} catch (e: Exception) {
LogUtils.e("AutoJsUtil", "运行脚本失败", e)
runOnUiThread {
Toast.makeText(context, "运行脚本失败: " + e.message, Toast.LENGTH_SHORT).show()
}
}
}
/**
* public static void registerScriptResultReceiver() {
* if (scriptResultReceiver == null) {
* scriptResultReceiver = new ScriptReceiver();
* try {
* IntentFilter filter = new IntentFilter(AUTOJS_SCRIPT_FINISHED_ACTION);
* ContextCompat.registerReceiver(
* MainApplication.getInstance(),
* scriptResultReceiver,
* filter,
* ContextCompat.RECEIVER_EXPORTED
* );
* LogUtils.d("广播接收器成功注册", null);
* } catch (Exception e) {
* LogUtils.e("Failed to register receiver", e);
* scriptResultReceiver = null;
* }
* } else {
* LogUtils.d("广播接收器已注册,无需重复注册");
* }
* }
*/
fun registerScriptResultReceiver() {
if (scriptResultReceiver == null) {
scriptResultReceiver = ScriptReceiver()
try {
val filter = IntentFilter(AUTOJS_SCRIPT_FINISHED_ACTION)
ContextCompat.registerReceiver(
MainApplication.instance,
scriptResultReceiver,
filter,
ContextCompat.RECEIVER_EXPORTED
)
LogUtils.d("广播接收器成功注册", null)
} catch (e: java.lang.Exception) {
LogUtils.e("Failed to register receiver", e)
scriptResultReceiver = null
}
} else {
LogUtils.d("广播接收器已注册,无需重复注册")
}
}
fun unregisterScriptResultReceiver() {
if (scriptResultReceiver != null) {
try {
MainApplication.instance.unregisterReceiver(scriptResultReceiver)
LogUtils.d("广播接收器成功注销", null)
} catch (e: java.lang.Exception) {
LogUtils.e("Failed to unregister receiver", e)
}
scriptResultReceiver = null
} else {
LogUtils.d("广播接收器未注册,无需注销")
}
}
private fun runOnUiThread(action: Runnable) {
Handler(Looper.getMainLooper()).post(action)
}
fun isAppInstalled(packageName: String): Boolean {
LogUtils.d("Checking if app is installed: $packageName", null)
try {
val cmd = "pm list packages | grep $packageName"
val result = ShellUtils.execRootCmdAndGetResult(cmd)
if (result != null && result.contains(packageName)) {
LogUtils.d("App is installed: $packageName", null)
return true
} else {
LogUtils.w("App not installed: $packageName", null)
return false
}
} catch (e: java.lang.Exception) {
LogUtils.e("Unexpected exception while checking app installation: $packageName", e)
return false
}
}
}

View File

@ -0,0 +1,170 @@
package com.android.grape.util
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.android.grape.MainApplication
import com.android.grape.data.AppState.canAutoLc
import com.android.grape.data.AppState.isCanAuto
import com.android.grape.data.AppState.script_path
import com.android.grape.job.AutoJobService
import com.android.grape.net.AfClient.downloadFile
import com.android.grape.receiver.ScriptReceiver
import com.android.grape.util.ShellUtils.delFileSh
import com.android.grape.util.ShellUtils.unzipAPkSh
import com.blankj.utilcode.util.LogUtils
/**
* @Time: 2025-16-16 19:16
* @Creator: 初屿贤
* @File: ScriptUtils
* @Project: AndroidGrape
* @Description:
*/
object ScriptUtils {
fun doScript(context: Context, targetPackageName: String): Boolean {
// execTargetApp(context, targetPackageName)
// try {
// Thread.sleep(3000L)
// } catch (e: InterruptedException) {
// e.printStackTrace()
// }
Handler(Looper.getMainLooper()).postDelayed(Runnable { //开始执行脚本
AutoJobService.onEvent(context)
}, 1)
return true
}
fun execDownScript(): Boolean {
var isDownload = true
if (isCanAuto) {
Log.i("TaskUtils", "start to execDownScript")
val script_url = "http://39.103.73.250/tt/" + canAutoLc
isDownload = downloadFile(script_url, script_path)
if (!isDownload) {
Log.i("TaskUtils", "execDownScript isDownload : $isDownload")
return false
}
unzipAPkSh(script_path, "/sdcard/script/")
delFileSh(script_path)
}
return isDownload
}
const val AUTOJS_SCRIPT_FINISHED_ACTION: String = "org.autojs.SCRIPT_FINISHED"
const val SCRIPT_RESULT_KEY: String = "result"
var scriptResultReceiver: BroadcastReceiver? = null
fun execScript(context: Context, src: String) {
if (!isAppInstalled("org.autojs.autojs6")) {
LogUtils.e("AutoJsUtil", "Auto.js app not installed")
runOnUiThread {
Toast.makeText(context, "Auto.js app not installed", Toast.LENGTH_SHORT).show()
}
return
}
val intent = Intent()
intent.setClassName(
"org.autojs.autojs6",
"org.autojs.autojs.external.open.RunIntentActivity"
)
intent.putExtra("path", src)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
try {
context.startActivity(intent)
LogUtils.d("脚本运行中:$src")
} catch (e: Exception) {
LogUtils.e("AutoJsUtil", "运行脚本失败", e)
runOnUiThread {
Toast.makeText(context, "运行脚本失败: " + e.message, Toast.LENGTH_SHORT).show()
}
}
}
/**
* public static void registerScriptResultReceiver() {
* if (scriptResultReceiver == null) {
* scriptResultReceiver = new ScriptReceiver();
* try {
* IntentFilter filter = new IntentFilter(AUTOJS_SCRIPT_FINISHED_ACTION);
* ContextCompat.registerReceiver(
* MainApplication.getInstance(),
* scriptResultReceiver,
* filter,
* ContextCompat.RECEIVER_EXPORTED
* );
* LogUtils.d("广播接收器成功注册", null);
* } catch (Exception e) {
* LogUtils.e("Failed to register receiver", e);
* scriptResultReceiver = null;
* }
* } else {
* LogUtils.d("广播接收器已注册,无需重复注册");
* }
* }
*/
fun registerScriptResultReceiver() {
if (scriptResultReceiver == null) {
scriptResultReceiver = ScriptReceiver()
try {
val filter = IntentFilter(AUTOJS_SCRIPT_FINISHED_ACTION)
ContextCompat.registerReceiver(
MainApplication.instance,
scriptResultReceiver,
filter,
ContextCompat.RECEIVER_EXPORTED
)
LogUtils.d("广播接收器成功注册", null)
} catch (e: java.lang.Exception) {
LogUtils.e("Failed to register receiver", e)
scriptResultReceiver = null
}
} else {
LogUtils.d("广播接收器已注册,无需重复注册")
}
}
fun unregisterScriptResultReceiver() {
if (scriptResultReceiver != null) {
try {
MainApplication.instance.unregisterReceiver(scriptResultReceiver)
LogUtils.d("广播接收器成功注销", null)
} catch (e: java.lang.Exception) {
LogUtils.e("Failed to unregister receiver", e)
}
scriptResultReceiver = null
} else {
LogUtils.d("广播接收器未注册,无需注销")
}
}
private fun runOnUiThread(action: Runnable) {
Handler(Looper.getMainLooper()).post(action)
}
fun isAppInstalled(packageName: String): Boolean {
LogUtils.d("Checking if app is installed: $packageName", null)
try {
val cmd = "pm list packages | grep $packageName"
val result = ShellUtils.execRootCmdAndGetResult(cmd)
if (result != null && result.contains(packageName)) {
LogUtils.d("App is installed: $packageName", null)
return true
} else {
LogUtils.w("App not installed: $packageName", null)
return false
}
} catch (e: java.lang.Exception) {
LogUtils.e("Unexpected exception while checking app installation: $packageName", e)
return false
}
}
}

View File

@ -1,8 +1,16 @@
package com.android.grape.util
import android.app.IMikRom
import android.content.Context
import android.os.Environment
import android.os.IBinder
import android.util.Log
import com.android.grape.util.ContextUtils.getBaseFilesDir
import com.android.grape.util.FileUtils.forceMakeDir
import java.io.File
import java.io.FileOutputStream
import java.io.FileWriter
import java.io.IOException
object ServiceUtils {
private var iMikRom: IMikRom? = null
@ -111,4 +119,73 @@ object ServiceUtils {
}
}
}
fun writeFile1(context: Context?, dirName: String, fileName: String, contents: String) {
val fs = File("$dirName/$fileName")
forceMakeDir(fs)
var outputStream: FileOutputStream? = null
try {
outputStream = FileOutputStream(fs)
outputStream.write(contents.toByteArray())
outputStream.flush()
outputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
fun writeFile(context: Context, dirName: String, fileName: String, contents: String) {
val fs = File(getBaseFilesDir(context) + "/" + dirName + "/" + fileName)
forceMakeDir(fs)
var outputStream: FileOutputStream? = null
try {
outputStream = FileOutputStream(fs)
outputStream.write(contents.toByteArray())
outputStream.flush()
outputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
fun WriteFile(fileName: String?, content: String?) {
try {
val writer = FileWriter(fileName, false)
writer.write(content)
writer.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
@Throws(IOException::class)
private fun writeFileToSDCard(message: String) {
// 比如可以将一个文件作为普通的文档存储,那么先获取系统默认的文档存放根目录
val parent_path = Environment.getExternalStorageDirectory()
// String AppPath = "/data/data/"+ Util.getRecordPackageName() + "/";
val AppPath = "/data/data/sperixlabs.proxyme/"
val dir = File(AppPath)
if (!dir.exists()) {
dir.mkdirs()
}
val file = File(AppPath + "device.txt")
if (file.exists()) {
file.delete()
}
try {
file.createNewFile()
} catch (e: IOException) {
e.printStackTrace()
}
val writer = FileWriter(AppPath + "device.txt", true)
writer.write(message)
writer.close()
Log.d("文件写入", "成功")
}
}

View File

@ -1,360 +1,593 @@
package com.android.grape.util;
package com.android.grape.util
import static java.security.AccessController.getContext;
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.util.AppUtils.getPackageUserID
import com.android.grape.util.ContextUtils.getMonitorDir
import com.android.grape.util.TaskUtils.getRecordListTxtFileName
import com.blankj.utilcode.util.LogUtils
import java.io.BufferedOutputStream
import java.io.BufferedReader
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.InterruptedIOException
import java.io.PrintWriter
import java.nio.charset.StandardCharsets
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
/**
* @Time: 2025-56-16 14:56
* @Creator: 初屿贤
* @File: ShellUtils
* @Project: AndroidGrape
* @Description:
*/
// ShellUtils.kt
object ShellUtils {
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.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 ShellUtils {
public static String getPackagePath(Context context, String packageName) {
/**
* 递归更改指定目录及其内容的所有权
* 使用Shell命令给提供的用户和组
*
* @param上下文调用该方法的上下文
* @param dir是将更改所有权的目录的路径
* @param groupanduser组和用户以用户的形式
* @return true如果操作成功则否则为否则
*/
fun chownSh(dir: String?, groupAndUser: String?): Boolean {
try {
PackageManager pm = context.getPackageManager();
ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
return appInfo.sourceDir; // 返回 APK 的完整路径
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return "";
val cmd = "chown -R $groupAndUser $dir"
Log.i("ShellUtils", "chownSh cmd:$cmd")
MockTools.exec(cmd)
return true
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
fun getPackagePath(context: Context, packageName: String): String {
return try {
val pm = context.packageManager
val appInfo = pm.getApplicationInfo(packageName, 0)
appInfo.sourceDir // 返回 APK 的完整路径
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
""
}
}
public static void exec(String cmd) {
fun copyFolderSh(oldPath: String, newPath: String): Boolean {
Log.i("ShellUtils", "start copyFolderSh : $oldPath ; $newPath")
try {
LogUtils.e(Log.INFO, "ShellUtils", "Executing command: " + cmd, null);
Process process = Runtime.getRuntime().exec(cmd);
process.waitFor();
} catch (Exception e) {
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing command: " + e.getMessage(), e);
val cmd = "cp -r -f $oldPath $newPath"
Log.i("ShellUtils", "copyFolderSh cmd:$cmd")
MockTools.exec(cmd)
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
/**
* 执行shell命令以列出目录的内容重定向输出
* 到指定的文件并将文件所有权修改给特定用户
*
* @param上下文用于检索文件路径和用户信息的应用程序上下文
* @param dir需要列出其内容的目录
* @return `true`如果操作成功而没有异常`false`否则
*/
fun listSh(context: Context, dir: String): Boolean {
Log.i("ShellUtils", "listSh->dir:$dir")
try {
val txtFileName = getRecordListTxtFileName(context)
val cmd = "ls $dir -l > $txtFileName"
MockTools.exec(cmd)
val uid = getPackageUserID(
context,
recordPackageName ?: ""
)
chownSh(getMonitorDir(context), uid)
return true
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
fun copyFileSh(oldPath: String, newPath: String): Boolean {
Log.i("ShellUtils", "start copyFileSh : $oldPath ; $newPath")
try {
val cmd = "cp -r -f $oldPath $newPath"
MockTools.exec(cmd)
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
fun exec(cmd: String) {
try {
LogUtils.e(Log.INFO, "ShellUtils", "Executing command: $cmd", null)
val process = Runtime.getRuntime().exec(cmd)
process.waitFor()
} catch (e: Exception) {
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing command: ${e.message}", 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;
fun getPid(p: Process): Int {
var pid = -1
return try {
val f = p.javaClass.getDeclaredField("pid")
f.isAccessible = true
pid = f.getInt(p)
f.isAccessible = false
pid
} catch (e: Throwable) {
pid
}
return pid;
}
public static boolean hasBin(String binName) {
// 验证 binName 是否符合规则
if (binName == null || binName.isEmpty()) {
LogUtils.e(Log.ERROR, "ShellUtils", "Invalid bin name",null);
throw new IllegalArgumentException("Bin name cannot be null or empty");
fun hasBin(binName: String): Boolean {
if (binName.isEmpty()) {
LogUtils.e(Log.ERROR, "ShellUtils", "Invalid bin name", null)
throw IllegalArgumentException("Bin name cannot be null or empty")
}
for (char c : binName.toCharArray()) {
for (c in binName.toCharArray()) {
if (!Character.isLetterOrDigit(c) && c != '.' && c != '_' && c != '-') {
throw new IllegalArgumentException("Invalid bin name");
throw IllegalArgumentException("Invalid bin name")
}
}
// 获取 PATH 环境变量
String pathEnv = System.getenv("PATH");
if (pathEnv == null) {
LogUtils.e(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 构造完整路径
val pathEnv = System.getenv("PATH") ?: return false
val paths = pathEnv.split(File.pathSeparatorChar)
for (path in paths) {
val file = File(path, binName)
try {
// 检查文件是否可执行
if (file.canExecute()) {
return true;
return true
}
} catch (SecurityException e) {
LogUtils.e(Log.ERROR, "ShellUtils", "Security exception occurred while checking: " + file.getAbsolutePath(), e);
} catch (e: SecurityException) {
LogUtils.e(
Log.ERROR,
"ShellUtils",
"Security exception occurred while checking: ${file.absolutePath}",
e
)
}
}
// 如果未找到可执行文件,返回 false
return false;
return false
}
public static String execRootCmdAndGetResult(String cmd) {
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.");
// }
fun execRootCmdAndGetResult(cmd: String): String {
Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: $cmd")
Process process = null;
ExecutorService executor = Executors.newFixedThreadPool(2);
val process: Process? = when {
hasBin("su") -> Runtime.getRuntime().exec("su")
hasBin("xu") -> Runtime.getRuntime().exec("xu")
hasBin("vu") -> Runtime.getRuntime().exec("vu")
else -> Runtime.getRuntime().exec("sh")
}
try {
if (hasBin("su")) {
process = Runtime.getRuntime().exec("su");
} else if (hasBin("xu")) {
process = Runtime.getRuntime().exec("xu");
} else if (hasBin("vu")) {
process = Runtime.getRuntime().exec("vu");
} else {
process = Runtime.getRuntime().exec("sh");
}
val executor = Executors.newFixedThreadPool(2)
return try {
process?.let {
val os = BufferedOutputStream(it.outputStream)
val isInputStream = it.inputStream
val errorStream = it.errorStream
val br =
BufferedReader(InputStreamReader(isInputStream, StandardCharsets.UTF_8))
val errorReader =
BufferedReader(InputStreamReader(errorStream, StandardCharsets.UTF_8))
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;
executor.submit {
try {
while ((line = errorReader.readLine()) != null) {
LogUtils.e(Log.ERROR, "ShellUtils", "Shell Error: " + line, null);
var line: String?
while (true) {
line = errorReader.readLine()
if (line == null) {
break
}
LogUtils.e(Log.ERROR, "ShellUtils", "Shell Error: $line", null)
}
} catch (IOException e) {
LogUtils.e(Log.ERROR, "ShellUtils", "Error while reading process error stream: " + e.getMessage(), e);
} catch (e: IOException) {
LogUtils.e(
Log.ERROR,
"ShellUtils",
"Error while reading process error stream: ${e.message}",
e
)
}
});
os.write((cmd + "\n").getBytes());
os.write("exit\n".getBytes());
os.flush();
StringBuilder output = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
output.append(line).append("\n");
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!process.waitFor(10, TimeUnit.SECONDS)) {
process.destroyForcibly();
throw new RuntimeException("Shell command execution timeout.");
os.write("$cmd\n".toByteArray())
os.write("exit\n".toByteArray())
os.flush()
val output = StringBuilder()
var line: String?
while (true) {
line = br.readLine()
if (line == null) {
break
}
output.append(line).append("\n")
}
val completed = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
it.waitFor(10, TimeUnit.SECONDS)
} else {
long startTime = System.currentTimeMillis();
val startTime = System.currentTimeMillis()
while (true) {
try {
process.exitValue();
break;
} catch (IllegalThreadStateException e) {
if (System.currentTimeMillis() - startTime > 10000) { // 10 seconds
process.destroy();
throw new RuntimeException("Shell command execution timeout.");
it.exitValue()
break
} catch (e: IllegalThreadStateException) {
if (System.currentTimeMillis() - startTime > 10000) {
it.destroy()
throw RuntimeException("Shell command execution timeout.")
}
Thread.sleep(100); // Sleep briefly before re-checking
Thread.sleep(100)
}
}
true
}
return output.toString().trim();
}
} catch (IOException | InterruptedException e) {
LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: " + e.getMessage(), e);
Thread.currentThread().interrupt();
return "Error: " + e.getMessage();
output.toString().trim()
} ?: ""
} catch (e: IOException) {
LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: ${e.message}", e)
Thread.currentThread().interrupt()
"Error: ${e.message}"
} catch (e: InterruptedException) {
LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: ${e.message}", e)
Thread.currentThread().interrupt()
"Error: ${e.message}"
} finally {
if (process != null) {
process.destroy();
}
executor.shutdown();
Log.d("ShellUtils", "Executor service shut down.");
process?.destroy()
executor.shutdown()
Log.d("ShellUtils", "Executor service shut down.")
}
}
public static void execRootCmd(String cmd) {
// 校验命令是否安全
// if (!isCommandSafe(cmd)) {
// return;
// }
List<String> cmds = new ArrayList<>();
cmds.add(cmd);
// 使用同步锁保护线程安全
synchronized (ShellUtils.class) {
fun execRootCmd(cmd: String) {
val cmds = mutableListOf(cmd)
synchronized(ShellUtils::class.java) {
try {
List<String> results = execRootCmds(cmds);
// 判断是否需要打印输出,仅用于开发调试阶段
for (String result : results) {
Log.d("ShellUtils", "Command Result: " + result);
val results = execRootCmds(cmds)
for (result in results) {
Log.d("ShellUtils", "Command Result: $result")
}
} catch (Exception e) {
LogUtils.e(Log.ERROR, "ShellUtils", "Unexpected error: " + e.getMessage(), e);
} catch (e: Exception) {
LogUtils.e(Log.ERROR, "ShellUtils", "Unexpected error: ${e.message}", e)
}
}
}
private static boolean isCommandSafe(String cmd) {
// 检查空值和空字符串
if (cmd == null || cmd.trim().isEmpty()) {
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command: empty or null value.", null);
return false;
private fun isCommandSafe(cmd: String): Boolean {
if (cmd.isBlank()) {
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command: empty or null value.", null)
return false
}
// 检查非法字符
if (!cmd.matches("^[a-zA-Z0-9._/:\\-~`'\" *|]+$")) {
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command due to illegal characters: " + cmd, null);
return false;
if (!cmd.matches(Regex("^[a-zA-Z0-9._/:\\\\\\-~`'\" *|]+\$"))) {
LogUtils.e(
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.e(Log.ERROR, "ShellUtils", "Rejected command due to prohibited structure: " + cmd, null);
return false;
LogUtils.e(
Log.ERROR,
"ShellUtils",
"Rejected command due to prohibited structure: $cmd",
null
)
return false
}
}
// 路径遍历保护
if (cmd.contains("../") || cmd.contains("..\\")) {
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command due to path traversal attempt: " + cmd, null);
return false;
if (cmd.contains("../") || cmd.contains("..\\\\")) {
LogUtils.e(
Log.ERROR,
"ShellUtils",
"Rejected command due to path traversal attempt: $cmd",
null
)
return false
}
// 命令长度限制
if (cmd.startsWith("tar") && cmd.length() > 800) { // 特定命令支持更长长度
LogUtils.e(Log.ERROR, "ShellUtils", "Command rejected due to excessive length.", null);
return false;
} else if (cmd.length() > 500) {
LogUtils.e("ShellUtils", "Command rejected due to excessive length.", null);
return false;
if (cmd.startsWith("tar") && cmd.length > 800) {
LogUtils.e(
Log.ERROR,
"ShellUtils",
"Command rejected due to excessive length.",
null
)
return false
} else if (cmd.length > 500) {
LogUtils.e("ShellUtils", "Command rejected due to excessive length.", null)
return false
}
Log.d("ShellUtils", "Command passed safety checks: " + cmd);
return true;
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).+");
private fun isExpectedMultiCommand(cmd: String): Boolean {
return cmd.matches(Regex("^cd .+ && (tar|zip|cp).+"))
}
public static List<String> execRootCmds(List<String> cmds) {
List<String> results = new ArrayList<>();
Process process = null;
fun execRootCmds(cmds: List<String>): List<String> {
val results = mutableListOf<String>()
var 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.e(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.e(Log.ERROR, "ShellUtils", "Stderr: " + line, null);
}
} catch (IOException ioException) {
LogUtils.e(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();
process = if (hasBin("su")) {
Runtime.getRuntime().exec("su")
} else {
Runtime.getRuntime().exec("sh")
}
val stdProcess = process
val stdThread = Thread {
try {
val stdReader = BufferedReader(InputStreamReader(stdProcess.inputStream))
val localResults = mutableListOf<String>()
var line: String?
while (true) {
line = stdReader.readLine()
if (line == null) {
break
}
localResults.add(line)
Log.d("ShellUtils", "Stdout: $line")
}
synchronized(results) {
results.addAll(localResults)
}
} catch (ioException: IOException) {
LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout", ioException)
}
}
val errThread = Thread {
try {
val errReader = BufferedReader(InputStreamReader(process.errorStream))
var line: String?
while (true) {
line = errReader.readLine()
if (line == null) {
break
}
LogUtils.e(Log.ERROR, "ShellUtils", "Stderr: $line", null)
}
} catch (ioException: IOException) {
LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stderr", ioException)
}
}
stdThread.start()
errThread.start()
val os = process.outputStream
for (cmd in cmds) {
os.write("$cmd\n".toByteArray())
Log.d("ShellUtils", "Executing command: $cmd")
}
os.write("exit\n".toByteArray())
os.flush()
try {
// 执行命令、等待解决
process.waitFor();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e);
process.waitFor()
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e)
}
// 等待子线程完成
stdThread.join();
errThread.join();
stdThread.join()
errThread.join()
} catch (InterruptedIOException e) {
LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout: Interrupted", e);
Thread.currentThread().interrupt(); // 恢复线程的中断状态
} catch (Exception e) {
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e);
} catch (e: InterruptedIOException) {
LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout: Interrupted", e)
Thread.currentThread().interrupt()
} catch (e: Exception) {
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e)
} finally {
if (process != null) {
process.destroy();
}
process?.destroy()
}
return results;
return results
}
public static boolean hasRootAccess() {
// 记录是否出现安全异常
boolean hasSecurityError = false;
/**
* 使用Shell命令删除给定文件名指定的文件或目录
* 此方法执行 RM -RF命令以删除文件或目录
* 记录过程步骤并处理执行过程中可能发生的任何异常
*
* @param文件名删除文件或目录的名称或路径
*/
fun delFileSh(fileName: String) {
Log.i("ShellUtils", "start delFileSh : $fileName")
try {
val cmd = "rm -rf $fileName"
Log.i("ShellUtils", "delFileSh-> cmd:$cmd")
MockTools.exec(cmd)
} catch (e: Exception) {
e.printStackTrace()
}
}
// 检查二进制文件
String[] binariesToCheck = {"su", "xu", "vu"};
for (String bin : binariesToCheck) {
fun delFolderSh(dir: String, fileName: String) {
Log.i("ShellUtils", "start delFolderSh :$fileName")
var PrintWriter: PrintWriter? = null
var process: Process? = null
try {
process = Runtime.getRuntime().exec("su")
PrintWriter = PrintWriter(process.outputStream)
var cmd = "cd $dir \n"
cmd += "rm -rf $fileName"
Log.i("ShellUtils", "delFolderSh-> cmd:$cmd")
PrintWriter.println(cmd)
PrintWriter.flush()
PrintWriter.close()
val value = process.waitFor()
} catch (e: Exception) {
e.printStackTrace()
} finally {
process?.destroy()
}
}
fun delFile(file: File) {
try {
val cmd = "rm -rf $file"
Log.i("ShellUtils", "delFile-> cmd:$cmd")
MockTools.exec(cmd)
} catch (e: Exception) {
e.printStackTrace()
}
}
fun give777(path: String): Boolean {
execCommand("su")
execCommand("chmod", "777", path)
return true
}
fun delFilesSh(dir: String, prefix: String?) {
try {
var cmd = "cd $dir|"
cmd += "rm -rf $prefix*"
Log.i("ShellUtils", "delFilesSh-> cmd:$cmd")
MockTools.exec(cmd)
} catch (e: Exception) {
e.printStackTrace()
}
}
fun hasRootAccess(): Boolean {
var hasSecurityError = false
val binariesToCheck = arrayOf("su", "xu", "vu")
for (bin in binariesToCheck) {
try {
if (hasBin(bin)) {
return true;
return true
}
} catch (SecurityException e) {
hasSecurityError = true;
LogUtils.e(Log.ERROR, "ShellUtils", "Security exception while checking: " + bin, e);
} catch (e: SecurityException) {
hasSecurityError = true
LogUtils.e(
Log.ERROR,
"ShellUtils",
"Security exception while checking: $bin",
e
)
}
}
// 判断如果发生安全异常则反馈问题
if (hasSecurityError) {
Log.w("ShellUtils", "Potential security error detected while checking root access.");
Log.w("ShellUtils", "Potential security error detected while checking root access.")
}
return false
}
fun unZipFileSh(zipFileName: String, dataDir: String) {
try {
var cmd = "cd $dataDir|"
cmd += "tar -xvf " + File(zipFileName).name
Log.i("ShellUtils", "unZipFileSh-> cmd:$cmd")
MockTools.exec(cmd)
} catch (e: Exception) {
e.printStackTrace()
}
}
/*
* m命令可以通过adb在shell中执行同样我们可以通过代码来执行
*/
fun execCommand(vararg command: String?): String? {
var process: Process? = null
var errIs: InputStream? = null
var inIs: InputStream? = null
var result: String? = ""
try {
process = ProcessBuilder().command(*command).start()
val baos = ByteArrayOutputStream()
var read = -1
errIs = process.errorStream
while ((errIs.read().also { read = it }) != -1) {
baos.write(read)
}
inIs = process.inputStream
while ((inIs.read().also { read = it }) != -1) {
baos.write(read)
}
result = String(baos.toByteArray())
inIs?.close()
errIs?.close()
process.destroy()
} catch (e: IOException) {
result = e.message
}
// 没有找到合法的二进制文件则认为无root权限
return false;
return result
}
fun unzipAPkSh(zipFileName: String, dataDir: String) {
try {
val file = File(dataDir)
if (!file.exists()) {
FileUtils.forceCreteDir(file)
}
val cmd = "unzip -o " + "/sdcard/apks/" + File(zipFileName).name + " -d " + dataDir
val unzipResult = MockTools.execRead(cmd)
Log.i("ShellUtils", "unZipFileSh-> cmd:$unzipResult")
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 执行shell命令以检索指定文件的内容
*
* @param上下文应用程序上下文
* @param fileName要读取的文件的名称
* @return文件的内容作为字符串或一个空字符串如果发生异常
*/
fun catSh(context: Context?, fileName: String): String {
Log.i("TaskUtils", "catSh->fileName:$fileName")
try {
val cmd = "cat $fileName"
val result = MockTools.execRead(cmd)
return result
} catch (e: Exception) {
e.printStackTrace()
}
return ""
}
}

View File

@ -0,0 +1,544 @@
package com.android.grape.util
import android.R.attr.name
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.util.Log
import com.android.grape.net.AfClient.downloadFile
import com.android.grape.MainApplication
import com.android.grape.data.AppState
import com.android.grape.data.AppState.afLog
import com.android.grape.data.AppState.apkDir
import com.android.grape.data.AppState.appDataUrl
import com.android.grape.data.AppState.canAutoLc
import com.android.grape.data.AppState.clickTime
import com.android.grape.data.AppState.installRet
import com.android.grape.data.AppState.isCanAuto
import com.android.grape.data.AppState.logBuffer
import com.android.grape.data.AppState.monitorDir
import com.android.grape.data.AppState.nRandom
import com.android.grape.data.AppState.paramsJson
import com.android.grape.data.AppState.recordExtraFileName
import com.android.grape.data.AppState.recordFileName
import com.android.grape.data.AppState.recordPackageName
import com.android.grape.data.AppState.referer
import com.android.grape.data.AppState.regEmailJson
import com.android.grape.data.AppState.scriptOpenApp
import com.android.grape.data.AppState.baoming
import com.android.grape.util.FileUtils.getName
import com.android.grape.data.AppState.zip_name
import com.android.grape.data.AppState.apk_path
import com.android.grape.net.AfClient.postData
import com.android.grape.util.ContextUtils.getRecordDataDirName
import com.android.grape.data.AppState.taskJson
import com.android.grape.job.DownloadAppJobService
import com.android.grape.job.MonitorService
import com.android.grape.job.UnInstallService
import com.android.grape.manager.ConfigManager
import com.android.grape.manager.ConfigManager.initDefaultAppJo
import com.android.grape.manager.ConfigManager.initDefaultProxyJo
import com.android.grape.util.AppUtils.execTargetApp
import com.android.grape.util.AppUtils.getApkPackageName
import com.android.grape.util.AppUtils.setTopApp
import com.android.grape.util.ContextUtils.getBaseFilesDir
import com.android.grape.util.FileUtils.forceMakeDir
import com.android.grape.util.FileUtils.getRecordDataFileName
import com.android.grape.util.JsonUtils.execSetJson
import com.blankj.utilcode.util.LogUtils
import org.json.JSONObject
import java.io.File
import java.io.IOException
import java.io.PrintWriter
import java.util.Locale
import kotlin.math.min
/**
* `util`类用作封装多种方法和属性的实用程序类
* 用于管理与应用程序相关的数据并执行各种操作此课程提供
* 诸如检索和更新应用程序元数据管理日志处理等功能
* 备份要求跟踪用户和组信息以及与shell命令进行交互
*
* 它还揭示了使用JSON数据时间戳代理配置软件包的方法
* 所有权和其他特定于应用程序的属性该课程中的许多方法都相关
* 将应用程序状态管理和设备上的特定命令执行
*
* 注意此类包括用于初始化和内部配置的方法
* 不直接暴露于外部使用
*/
object TaskUtils {
init {
initDefaultAppJo()
initDefaultProxyJo()
}
fun addLog(log: String?) {
logBuffer.append(log).append("\r\n")
}
val emailStr: String
/**
* 根据电子邮件类型和关联的详细信息生成格式的电子邮件字符串
* 该方法处理JSON对象 Regemailjson来确定电子邮件的类型
* 并相应地构造字符串
* 如果电子邮件类型为 gmail则其他详细信息例如国家排序订单
* 将电子邮件地址密码和帮助邮件附加到字符串上
* 如果电子邮件类型为正常则只附加了国家和排序订单
* 如果例外该方法返回默认值 normal
*
* @return一个代表电子邮件详细信息的字符串根据电子邮件类型格式化
* 或发生错误时正常
*/
get() {
try {
val emailType =
regEmailJson!!.getString("type")
val sb = StringBuffer()
sb.append(emailType)
if ("gmail".equals(emailType, ignoreCase = true)) {
sb.append("|")
.append(regEmailJson!!.getString("country"))
.append("|")
sb.append(regEmailJson!!.getInt("sort")).append("|")
sb.append(regEmailJson!!.getString("gmail"))
.append("|")
sb.append(regEmailJson!!.getString("passwd"))
.append("|")
sb.append(regEmailJson!!.getString("helpMail"))
} else if ("normal".equals(emailType, ignoreCase = true)) {
sb.append("|")
.append(regEmailJson!!.getString("country"))
.append("|")
sb.append(regEmailJson!!.getInt("sort"))
}
return sb.toString()
} catch (e: Exception) {
e.printStackTrace()
}
return "normal"
}
public fun getRecordListTxtFileName(context: Context): String {
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".list.txt"
}
fun setInstallRet(installRetV: Boolean) {
Log.i("TaskUtils", "setInstallRet: $installRetV")
installRet = installRetV
}
fun isInstallRet(): Boolean {
return installRet ?: false
}
fun setAfLog(afLogV: String) {
afLog = afLogV
}
fun getAfLog(): String {
//return Util.afLog;
return ServiceUtils.readFile("/data/system/Logs.txt")
}
fun setReferer(refererV: String) {
Log.i("TaskUtils", "setReferer: $refererV")
referer = refererV
}
fun getReferer(): String? {
return referer
}
fun setRecordExtraFileName(extraFileNameV: String?) {
recordExtraFileName = extraFileNameV
}
fun HkLog(): String {
return ""
}
fun HkVer(): String {
return "v1060"
}
/**
构建请求参数获取设备唯一 ID拼接请求 URL
发送 POST 请求调用 MyPost.postData 向服务器提交请求
处理响应结果
成功时设置权限解析 JSON 数据并设置 clickTime
失败或异常时调用 setFinish 结束任务
*/
fun execReloginTask(context: Context) {
ConfigManager.init()
// adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context);
val url = "http://39.103.73.250/tt/ddj/preRequest!requestRelogin.do"
// String params = "platform=Android&"TaskUtils"="+get"TaskUtils"(context)+"&uuid="+PrefUtil.getUUID(context);
val ANDROID_ID =
Settings.System.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
val params = "platform=Android&tag=119&uuid=$ANDROID_ID"
val valid = false
println("IOSTQ:execReloginTask->url:$url?$params")
try {
val result: String? = postData(
" ".toByteArray(charset = Charsets.UTF_8),
"$url?$params"
)
LogUtils.e("IOSTQ:execReloginTask->result:$result")
if (result != null && result.isNotEmpty()) {
taskJson = JSONObject(result).apply {
val code = getInt("code")
if (code == 1) {
MockTools.exec("chmod 777 /data/data/com.android.grape/files/monitor")
MockTools.exec("chmod 777 /data/data/com.android.grape/files/monitor/apks")
JsonUtils.execSetJson(context)
clickTime = 1
} else {
Log.i(
"TaskUtils",
"request result code invalid : $code"
)
setFinish(context)
}
}
} else {
Log.i("TaskUtils", "request result is null")
setFinish(context)
}
} catch (e: Exception) {
Log.i("TaskUtils", "execTask error:" + e.message)
setFinish(context)
}
}
/**
* 初始化并构建请求参数获取设备唯一 IDANDROID_ID拼接请求 URL
* 发送 POST 请求通过 MyPost.postData 向服务器提交安装请求
* 处理响应结果
* 成功时解析 JSON 数据设置权限并触发 clickTime 1 表示需执行点击操作
* 失败或异常时调用 setFinish 结束任务
*/
fun execInstallTask(context: Context) {
ConfigManager.init()
val url = "http://39.103.73.250/tt/ddj/preRequest!requestInstall.do"
val ANDROID_ID =
Settings.System.getString(context.contentResolver, Settings.System.ANDROID_ID)
val params = "platform=Android&tag=119&uuid=$ANDROID_ID"
printStr("IOSTQ:request result : $url?$params")
try {
val result: String? = postData(
" ".toByteArray(charset = Charsets.UTF_8),
"$url?$params"
)
LogUtils.e("request result : $result")
if (result != null && result.isNotEmpty()) {
taskJson = JSONObject(result)
val code = taskJson?.getInt("code")
if (code == 1) {
MockTools.exec("chmod 777 /data/data/com.android.grape/files/monitor")
MockTools.exec("chmod 777 /data/data/com.android.grape/files/monitor/apks")
execSetJson(context)
clickTime = 1
} else {
LogUtils.i("TaskUtils", "request result code invalid : $code");
setFinish(context)
}
} else {
LogUtils.i("TaskUtils", "request result is null");
setFinish(context)
}
} catch (e: Exception) {
LogUtils.i("TaskUtils", "execTask error:" + e.message);
setFinish(context)
}
}
/**
* 根据随机条件执行任务
* 在运行Relogin任务和安装任务之间交替
*
* @param上下文执行任务执行的上下文
*/
fun execTask(context: Context) {
nRandom++
FileUtils.deletePlugin()
if (nRandom % 3 == 0) {
execReloginTask(context)
} else {
execInstallTask(context)
}
}
fun printStr(ss: String?) {
if (ss != null && ss.length > 0) {
val length = ss.length
val loop = (length - 1) / 2000 + 1
for (i in 0..<loop) {
Log.i(
"TaskUtils",
"print" + i + ": " + ss.substring(
i * 2000,
min(((i + 1) * 2000).toDouble(), length.toDouble()).toInt()
)
)
}
} else {
Log.i("TaskUtils", "print: ss is null")
}
}
public fun execRecord(context: Context) {
Log.i("TaskUtils", "start to execRecord")
val apks = File(getRecordDataDirName(context))
if (!apks.exists()) {
forceMakeDir(apks)
}
DownloadAppJobService.onEvent(context)
}
fun execDownload(context: Context): Boolean {
ScriptUtils
return AppUtils.execDownloadApp(context) && ScriptUtils.execDownScript()
}
fun execDownloadData(context: Context): Boolean {
try {
return downloadFile(appDataUrl, getRecordDataFileName(context))
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
val paramString: String
get() = paramsJson.toString()
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 getRecordApkFileName(context: Context): String {
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".apk"
}
public fun getRecordApkVerFileName(context: Context): String {
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordFileName
}
public fun notcl(context: Context) {
val url = "http://127.0.0.1:8090/ctl/notcl"
try {
val ret: String = postData("".toByteArray(charset("utf-8")), url) ?: ""
Log.i("TaskUtils", "notcl ret : $ret")
} catch (e: Exception) {
e.printStackTrace()
}
}
fun openRecordApp(context: Context) {
if (scriptOpenApp == 0) {
recordPackageName?.let {
execTargetApp(
context,
it
)
}
}
if (!isCanAuto || canAutoLc.length <= 10) {
Handler(Looper.getMainLooper()).postDelayed(Runnable {
setTopApp(context)
//开始卸载
UnInstallService.onEvent(context)
}, 120000)
}
}
public fun getSessionTxtFileName(context: Context): String {
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/sessionTxt.txt"
}
public fun returnResult(value: Int): Boolean {
// 代表成功
return if (value == 0) {
true
} else if (value == 1) { // 失败
false
} else { // 未知情况
false
}
}
fun setFinish(context: Context) {
Log.i("TaskUtils", "setFinish")
MonitorService.setRunning(false)
BackupUtils.killRecordProcess(context, AppState.AUTO_JSPACKAGENAME)
Handler(Looper.getMainLooper()).postDelayed(Runnable {
MonitorService.onEvent(
context
)
}, 5000)
}
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()
}
}
//将xapk格式变成zip格式并进行解压
// public fun changeZip() {
// val split = name!!.split(".xapk".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
// //分割后选择保留那段
// AppState.zip_name = split[0]
// Log.e("压缩包名", zip_name!!)
//
//
// oldpath = apk_path + "/" + name
// Log.e("oldpath", oldpath!!)
// newpath = apk_path + "/" + zip_name + ".zip"
// Log.e("newpath", newpath!!)
//
// //将xapk换成zip格式
// FileUtils.renameFile(oldpath ?: "", newpath ?: "")
//
// //进行解压
// Thread {
// try {
// Thread.sleep(3000)
//
// try {
// FileUtils.upZipFile(
// File(newpath ?: ""),
// "$apk_path/$zip_name"
// )
// } catch (e: IOException) {
// e.printStackTrace()
// }
// } catch (e: InterruptedException) {
// e.printStackTrace()
// }
// Log.e("zip", "文件解压成功")
// }.start()
// FileUtils.renameFile(newpath ?: "", oldpath ?: "")
// }
@get:Throws(IOException::class)
public val zipApk: Unit
//1分钟后获取压缩包内的apk
get() {
Thread {
try {
Thread.sleep(65000)
val apkpath =
apk_path + "/" + zip_name
Log.e("sss", apkpath)
//获取apk
getName(apkpath, ".apk")
AppState.baoming =
getApkPackageName(
MainApplication.instance,
"$apkpath/$name"
)
Log.e(
"baoming",
baoming + "获取成功"
)
} catch (e: InterruptedException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}.start()
}
//将压缩包中的obb文件复制到/sdcard/Android/obb/ 文件下
@Throws(IOException::class)
public fun obb_name() {
//首先先获取obb文件所在的位置
val obb_path = "$apk_path/$zip_name/Android/obb/$baoming"
Log.e("Tag", obb_path)
getName(obb_path, ".obb")
//创建文件夹
FileUtils.createDir("/sdcard/Android/obb/$baoming")
Thread {
try {
Thread.sleep(3000) //休眠3秒
//复制到/sdcard/Android/obb/ 文件下
try {
val sourceFile = "$obb_path/$name"
Log.e("sourceFile", sourceFile)
val targetFile =
"/sdcard/Android/obb/$baoming/$name"
Log.e("targetFile", targetFile)
FileUtils.copyFile(
File(sourceFile),
File(targetFile)
)
} catch (e: IOException) {
e.printStackTrace()
}
} catch (e: InterruptedException) {
e.printStackTrace()
}
Log.e("obb", "文件复制成功")
}.start()
}
}

File diff suppressed because it is too large Load Diff