refactor(app): 重构应用安装和管理相关功能
- 移除 AdvertisingIdClient 类 - 新增 AfClient 类,实现信息设置和文件下载功能 - 新增 AppState 类,用于存储应用状态信息 - 新增 AppUtils 类,提供应用安装、卸载、检查等工具方法
This commit is contained in:
parent
6e51067b07
commit
66ee32d0e2
|
@ -12,13 +12,13 @@ import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import com.android.grape.databinding.ActivityMainBinding
|
import com.android.grape.databinding.ActivityMainBinding
|
||||||
import com.android.grape.job.MonitorService
|
import com.android.grape.job.MonitorService
|
||||||
import com.android.grape.service.SocketServer
|
import com.android.grape.util.BackupUtils.killRecordProcess
|
||||||
import com.android.grape.util.ClashUtil
|
import com.android.grape.util.ClashUtil
|
||||||
import com.android.grape.util.MockTools
|
import com.android.grape.util.MockTools
|
||||||
import com.android.grape.util.NotificationPermissionHandler
|
import com.android.grape.util.NotificationPermissionHandler
|
||||||
import com.android.grape.util.ScriptUtil
|
import com.android.grape.util.ScriptUtils.registerScriptResultReceiver
|
||||||
|
import com.android.grape.util.ScriptUtils.unregisterScriptResultReceiver
|
||||||
import com.android.grape.util.StoragePermissionHelper
|
import com.android.grape.util.StoragePermissionHelper
|
||||||
import com.android.grape.util.Util.killRecordProcess
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* public class MainActivity extends AppCompatActivity
|
* public class MainActivity extends AppCompatActivity
|
||||||
|
@ -68,7 +68,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
insets
|
insets
|
||||||
}
|
}
|
||||||
checkPermission()
|
checkPermission()
|
||||||
ScriptUtil.registerScriptResultReceiver()
|
registerScriptResultReceiver()
|
||||||
viewBinding.start.setOnClickListener {
|
viewBinding.start.setOnClickListener {
|
||||||
MonitorService.onEvent(MainApplication.instance)
|
MonitorService.onEvent(MainApplication.instance)
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
ClashUtil.unregisterReceiver(this)
|
ClashUtil.unregisterReceiver(this)
|
||||||
ScriptUtil.unregisterScriptResultReceiver()
|
unregisterScriptResultReceiver()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkPermission() {
|
private fun checkPermission() {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,8 +5,9 @@ import android.content.Intent
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import androidx.core.app.JobIntentService
|
import androidx.core.app.JobIntentService
|
||||||
import com.android.grape.util.ScriptUtil
|
import com.android.grape.data.AppState.recordPackageName
|
||||||
import com.android.grape.util.Util
|
import com.android.grape.util.ScriptUtils.execScript
|
||||||
|
import com.android.grape.util.TaskUtils
|
||||||
|
|
||||||
class AutoJobService : JobIntentService() {
|
class AutoJobService : JobIntentService() {
|
||||||
override fun onHandleWork(intent: Intent) {
|
override fun onHandleWork(intent: Intent) {
|
||||||
|
@ -14,9 +15,9 @@ class AutoJobService : JobIntentService() {
|
||||||
|
|
||||||
if (openZzzj) {
|
if (openZzzj) {
|
||||||
Handler(Looper.getMainLooper()).postDelayed({
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
ScriptUtil.execScript(
|
execScript(
|
||||||
this@AutoJobService,
|
this@AutoJobService,
|
||||||
"/sdcard/script/${Util.recordPackageName}/main.js"
|
"/sdcard/script/${recordPackageName}/main.js"
|
||||||
)
|
)
|
||||||
}, 2000L)
|
}, 2000L)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,18 +4,23 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.JobIntentService
|
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.net.MyGet
|
||||||
import com.android.grape.util.Util
|
import com.android.grape.util.TaskUtils
|
||||||
|
|
||||||
class CheckIpJobService : JobIntentService() {
|
class CheckIpJobService : JobIntentService() {
|
||||||
override fun onHandleWork(intent: Intent) {
|
override fun onHandleWork(intent: Intent) {
|
||||||
if (checkIp()) {
|
if (checkIp()) {
|
||||||
InstallService.onEvent(this)
|
InstallService.onEvent(this)
|
||||||
} else {
|
} else {
|
||||||
Util.isClickRet = false
|
isClickRet = false
|
||||||
Util.setInstallRet(false)
|
TaskUtils.setInstallRet(false)
|
||||||
Util.clickErrReason = "networkErr"
|
clickErrReason = "networkErr"
|
||||||
Util.setFinish(this)
|
TaskUtils.setFinish(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,9 +30,9 @@ class CheckIpJobService : JobIntentService() {
|
||||||
do {
|
do {
|
||||||
val url = "http://8.218.80.200/myipp"
|
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")
|
Log.i(TAG, "checkIp : $resp")
|
||||||
|
|
||||||
|
@ -35,9 +40,9 @@ class CheckIpJobService : JobIntentService() {
|
||||||
try {
|
try {
|
||||||
val conn =
|
val conn =
|
||||||
resp.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
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]
|
val ip = conn[0]
|
||||||
Util.delegateIp = ip
|
delegateIp = ip
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -67,4 +72,4 @@ class CheckIpJobService : JobIntentService() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@ package com.android.grape.job
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
|
||||||
import androidx.core.app.JobIntentService
|
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
|
import com.blankj.utilcode.util.LogUtils
|
||||||
|
|
||||||
class DownloadAppJobService : JobIntentService() {
|
class DownloadAppJobService : JobIntentService() {
|
||||||
|
@ -14,7 +15,7 @@ class DownloadAppJobService : JobIntentService() {
|
||||||
var succ: Boolean = false
|
var succ: Boolean = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
succ = Util.execDownload(this)
|
succ = TaskUtils.execDownload(this)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
@ -26,10 +27,10 @@ class DownloadAppJobService : JobIntentService() {
|
||||||
// InstallService.onEvent(this)
|
// InstallService.onEvent(this)
|
||||||
StartVpnPortJobService.onEvent(this)
|
StartVpnPortJobService.onEvent(this)
|
||||||
} else {
|
} else {
|
||||||
Util.isClickRet = false
|
isClickRet = false
|
||||||
Util.setInstallRet(false)
|
TaskUtils.setInstallRet(false)
|
||||||
Util.clickErrReason = "downloadErr"
|
clickErrReason = "downloadErr"
|
||||||
Util.setFinish(this)
|
TaskUtils.setFinish(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,4 +49,4 @@ class DownloadAppJobService : JobIntentService() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,19 +6,22 @@ import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.JobIntentService
|
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() {
|
class InstallService : JobIntentService() {
|
||||||
override fun onHandleWork(intent: Intent) {
|
override fun onHandleWork(intent: Intent) {
|
||||||
Log.i(TAG, "onHandleWork ...")
|
Log.i(TAG, "onHandleWork ...")
|
||||||
|
|
||||||
if (Util.installRecord(this)) {
|
if (installRecord(this)) {
|
||||||
Log.i(TAG, "installRecord succ")
|
Log.i(TAG, "installRecord succ")
|
||||||
tryNum = 0
|
tryNum = 0
|
||||||
Util.setInstallRet(true)
|
TaskUtils.setInstallRet(true)
|
||||||
|
|
||||||
println("IOSTQ:isNeedRestored == " + Util.isNeedRestored)
|
println("IOSTQ:isNeedRestored == " + isNeedRestored)
|
||||||
if (Util.isNeedRestored) {
|
if (isNeedRestored) {
|
||||||
Handler(Looper.getMainLooper()).postDelayed({
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
RecoverJobService.onEvent(
|
RecoverJobService.onEvent(
|
||||||
this@InstallService
|
this@InstallService
|
||||||
|
@ -40,9 +43,9 @@ class InstallService : JobIntentService() {
|
||||||
)
|
)
|
||||||
}, 5000)
|
}, 5000)
|
||||||
} else { //超过5次安装失败直接取消后续
|
} else { //超过5次安装失败直接取消后续
|
||||||
Log.i(TAG, "install error : " + Util.recordPackageName + " ; " + tryNum)
|
Log.i(TAG, "install error : " + recordPackageName + " ; " + tryNum)
|
||||||
|
|
||||||
Util.setFinish(this)
|
TaskUtils.setFinish(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,4 +67,4 @@ class InstallService : JobIntentService() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,10 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.JobIntentService
|
import androidx.core.app.JobIntentService
|
||||||
|
import com.android.grape.data.AppState.clickTime
|
||||||
import com.android.grape.util.ClashUtil
|
import com.android.grape.util.ClashUtil
|
||||||
import com.android.grape.util.MockTools
|
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")
|
ClashUtil.switchProxyGroup("PROXY", "DIRECT", "http://127.0.0.1:6170")
|
||||||
Thread.sleep(3000)
|
Thread.sleep(3000)
|
||||||
|
|
||||||
if (Util.clickTime > 0L) {
|
if (clickTime > 0L) {
|
||||||
Util.clickTime = 0L
|
clickTime = 0L
|
||||||
SendCallbackJobService.onEvent(this)
|
SendCallbackJobService.onEvent(this)
|
||||||
} else {
|
} else {
|
||||||
Util.execTask(applicationContext)
|
TaskUtils.execTask(applicationContext)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
|
|
@ -2,25 +2,29 @@ package com.android.grape.job
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.text.TextUtils
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.core.app.JobIntentService
|
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.net.ChangeCallBack
|
||||||
import com.android.grape.provider.DeviceDataAccessor
|
import com.android.grape.provider.DeviceDataAccessor
|
||||||
import com.android.grape.provider.DeviceInfoHelper
|
import com.android.grape.provider.DeviceInfoHelper
|
||||||
import com.android.grape.util.ChangeDeviceInfoUtil
|
import com.android.grape.util.ChangeDeviceInfoUtil
|
||||||
import com.android.grape.util.FileUtils
|
import com.android.grape.util.FileUtils
|
||||||
import com.android.grape.util.MockTools
|
import com.android.grape.util.MockTools
|
||||||
|
import com.android.grape.util.ScriptUtils.doScript
|
||||||
import com.android.grape.util.ServiceUtils
|
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
|
import java.io.IOException
|
||||||
|
|
||||||
|
|
||||||
class OpenAppService : JobIntentService() {
|
class OpenAppService : JobIntentService() {
|
||||||
override fun onHandleWork(intent: Intent) {
|
override fun onHandleWork(intent: Intent) {
|
||||||
println("IOSTQ:isCanAuto() == " + Util.isCanAuto)
|
println("IOSTQ:isCanAuto() == " + isCanAuto)
|
||||||
println("IOSTQ:getCanAutoLc() == " + Util.canAutoLc)
|
println("IOSTQ:getCanAutoLc() == " + canAutoLc)
|
||||||
Util.recordPackageName?.let {
|
recordPackageName?.let {
|
||||||
ServiceUtils.setEnableApp(it, true)
|
ServiceUtils.setEnableApp(it, true)
|
||||||
}
|
}
|
||||||
ServiceUtils.setEnableApp("org.mozilla.firefox", true)
|
ServiceUtils.setEnableApp("org.mozilla.firefox", true)
|
||||||
|
@ -31,32 +35,32 @@ class OpenAppService : JobIntentService() {
|
||||||
ChangeDeviceInfoUtil.changeDevice(callBack = object : ChangeCallBack {
|
ChangeDeviceInfoUtil.changeDevice(callBack = object : ChangeCallBack {
|
||||||
override fun changeSuccess() {
|
override fun changeSuccess() {
|
||||||
runCatching {
|
runCatching {
|
||||||
if (Util.isCanAuto && Util.canAutoLc.isNotEmpty()) {
|
if (isCanAuto && canAutoLc.isNotEmpty()) {
|
||||||
val deviceInfo = DeviceDataAccessor.getDeviceInfo(applicationContext, DeviceInfoHelper.getDeviceId())
|
val deviceInfo = DeviceDataAccessor.getDeviceInfo(applicationContext, DeviceInfoHelper.getDeviceId())
|
||||||
FileUtils.writePackageName(Util.recordPackageName?:"")
|
FileUtils.writePackageName(recordPackageName?:"")
|
||||||
FileUtils.writeDevice(Util.recordPackageName?:"", deviceInfo?: "")
|
FileUtils.writeDevice(recordPackageName?:"", deviceInfo?: "")
|
||||||
FileUtils.runPlugin(Util.recordPackageName?:"")
|
FileUtils.runPlugin(recordPackageName?:"")
|
||||||
MockTools.exec("pm grant ${Util.AUTO_JSPACKAGENAME} android.permission.READ_EXTERNAL_STORAGE") //sdcard权限
|
MockTools.exec("pm grant ${AUTO_JSPACKAGENAME} android.permission.READ_EXTERNAL_STORAGE") //sdcard权限
|
||||||
MockTools.exec("pm grant ${Util.AUTO_JSPACKAGENAME} android.permission.SYSTEM_ALERT_WINDOW") //悬浮窗权限
|
MockTools.exec("pm grant ${AUTO_JSPACKAGENAME} android.permission.SYSTEM_ALERT_WINDOW") //悬浮窗权限
|
||||||
MockTools.exec("settings put secure enabled_accessibility_services ${Util.AUTO_JSPACKAGENAME}/${Util.AUTO_JSPACKAGENAME}.core.accessibility.AccessibilityService")
|
MockTools.exec("settings put secure enabled_accessibility_services ${AUTO_JSPACKAGENAME}/${AUTO_JSPACKAGENAME}.core.accessibility.AccessibilityService")
|
||||||
Util.doScript(this@OpenAppService, Util.AUTO_JSPACKAGENAME) //autojs
|
doScript(this@OpenAppService, AUTO_JSPACKAGENAME) //autojs
|
||||||
}else {
|
}else {
|
||||||
Util.setFinish(this@OpenAppService)
|
setFinish(this@OpenAppService)
|
||||||
}
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
it.printStackTrace()
|
it.printStackTrace()
|
||||||
Util.setFinish(this@OpenAppService)
|
setFinish(this@OpenAppService)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun changeFailed() {
|
override fun changeFailed() {
|
||||||
Util.setFinish(this@OpenAppService)
|
setFinish(this@OpenAppService)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
Util.setFinish(this@OpenAppService)
|
setFinish(this@OpenAppService)
|
||||||
}
|
}
|
||||||
// Util.hookOpenApp(this);
|
// Util.hookOpenApp(this);
|
||||||
}
|
}
|
||||||
|
@ -76,4 +80,4 @@ class OpenAppService : JobIntentService() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.JobIntentService
|
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() {
|
class RecoverJobService : JobIntentService() {
|
||||||
override fun onHandleWork(intent: Intent) {
|
override fun onHandleWork(intent: Intent) {
|
||||||
|
@ -15,7 +16,7 @@ class RecoverJobService : JobIntentService() {
|
||||||
var succ = false
|
var succ = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
succ = Util.recoverRecordData(this)
|
succ = recoverRecordData(this)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
@ -49,4 +50,4 @@ class RecoverJobService : JobIntentService() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,37 @@ import android.content.Intent
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import androidx.core.app.JobIntentService
|
import androidx.core.app.JobIntentService
|
||||||
import com.android.grape.net.MyPost
|
import com.android.grape.data.AppState.afApp
|
||||||
import com.android.grape.util.Util
|
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 com.blankj.utilcode.util.LogUtils
|
||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
|
@ -31,16 +60,16 @@ class SendCallbackJobService : JobIntentService() {
|
||||||
override fun onHandleWork(intent: Intent) {
|
override fun onHandleWork(intent: Intent) {
|
||||||
LogUtils.i(TAG, "onHandleWork")
|
LogUtils.i(TAG, "onHandleWork")
|
||||||
|
|
||||||
if (Util.isInstallRet()) {
|
if (isInstallRet()) {
|
||||||
SendBackup()
|
SendBackup()
|
||||||
|
|
||||||
if (Util.isNeedRestored) {
|
if (isNeedRestored) {
|
||||||
sendReloginEvent()
|
sendReloginEvent()
|
||||||
} else {
|
} else {
|
||||||
sendLogEvent()
|
sendLogEvent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Util.setFinish(this@SendCallbackJobService)
|
setFinish(this@SendCallbackJobService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,39 +80,39 @@ class SendCallbackJobService : JobIntentService() {
|
||||||
val paramsJo = JSONObject()
|
val paramsJo = JSONObject()
|
||||||
val datajson = JSONObject()
|
val datajson = JSONObject()
|
||||||
|
|
||||||
if (Util.isNeedBackup) {
|
if (isNeedBackup) {
|
||||||
val cachejson = JSONObject()
|
val cachejson = JSONObject()
|
||||||
cachejson.put("firstInstallTime", Util.installTime)
|
cachejson.put("firstInstallTime", installTime)
|
||||||
cachejson.put("lastUpdateTime", Util.lastUpdateTime)
|
cachejson.put("lastUpdateTime", lastUpdateTime)
|
||||||
cachejson.put("installServerTimeFromGP", Util.installServerTimeFromGP)
|
cachejson.put("installServerTimeFromGP", installServerTimeFromGP)
|
||||||
cachejson.put("clickServerTimeToGP", Util.clickServerTimeFromGP)
|
cachejson.put("clickServerTimeToGP", clickServerTimeFromGP)
|
||||||
cachejson.put("installTimeFromGP", Util.instalTimeFromGp)
|
cachejson.put("installTimeFromGP", instalTimeFromGp)
|
||||||
datajson.put("cacheJson", cachejson)
|
datajson.put("cacheJson", cachejson)
|
||||||
}
|
}
|
||||||
|
|
||||||
paramsJo.put("recordId", Util.recordId)
|
paramsJo.put("recordId", recordId)
|
||||||
paramsJo.put("preClickRecordId", Util.preClickRecordId)
|
paramsJo.put("preClickRecordId", preClickRecordId)
|
||||||
paramsJo.put("dataJson", datajson)
|
paramsJo.put("dataJson", datajson)
|
||||||
paramsJo.put("reloginRecordId", Util.reloginRecordId)
|
paramsJo.put("reloginRecordId", reloginRecordId)
|
||||||
val clickInfoJo = JSONObject()
|
val clickInfoJo = JSONObject()
|
||||||
clickInfoJo.put("clickRet", Util.isClickRet)
|
clickInfoJo.put("clickRet", isClickRet)
|
||||||
clickInfoJo.put("clickIp", Util.delegateIp)
|
clickInfoJo.put("clickIp", delegateIp)
|
||||||
clickInfoJo.put("clickTime", Util.clickTime)
|
clickInfoJo.put("clickTime", clickTime)
|
||||||
clickInfoJo.put("clickErrReason", Util.clickErrReason)
|
clickInfoJo.put("clickErrReason", clickErrReason)
|
||||||
paramsJo.put("clickInfo", clickInfoJo)
|
paramsJo.put("clickInfo", clickInfoJo)
|
||||||
|
|
||||||
val installInfoJo = JSONObject()
|
val installInfoJo = JSONObject()
|
||||||
installInfoJo.put("installRet", Util.isInstallRet())
|
installInfoJo.put("installRet", isInstallRet())
|
||||||
installInfoJo.put("installTime", Util.installTime)
|
installInfoJo.put("installTime", installTime)
|
||||||
paramsJo.put("installInfo", installInfoJo)
|
paramsJo.put("installInfo", installInfoJo)
|
||||||
|
|
||||||
|
|
||||||
if (null != Util.backupResult) {
|
if (null != backupResult) {
|
||||||
paramsJo.put("backUpFiles", Util.backupResult)
|
paramsJo.put("backUpFiles", backupResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
val logInfoJo = JSONObject()
|
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("setConfLog", Util.getParamsJson());
|
||||||
paramsJo.put("logInfo", logInfoJo)
|
paramsJo.put("logInfo", logInfoJo)
|
||||||
var params: String? = null
|
var params: String? = null
|
||||||
|
@ -91,7 +120,7 @@ class SendCallbackJobService : JobIntentService() {
|
||||||
|
|
||||||
var nRetryCount = 0
|
var nRetryCount = 0
|
||||||
do {
|
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")
|
LogUtils.i(TAG, "ret:$ret")
|
||||||
val jo = ret?.let { JSONObject(it) }
|
val jo = ret?.let { JSONObject(it) }
|
||||||
if (jo?.getInt("code") == 1) {
|
if (jo?.getInt("code") == 1) {
|
||||||
|
@ -113,48 +142,48 @@ class SendCallbackJobService : JobIntentService() {
|
||||||
val paramsJo = JSONObject()
|
val paramsJo = JSONObject()
|
||||||
val datajson = JSONObject()
|
val datajson = JSONObject()
|
||||||
|
|
||||||
if (Util.isNeedBackup) {
|
if (isNeedBackup) {
|
||||||
val cachejson = JSONObject()
|
val cachejson = JSONObject()
|
||||||
cachejson.put("firstInstallTime", Util.installTime)
|
cachejson.put("firstInstallTime", installTime)
|
||||||
cachejson.put("lastUpdateTime", Util.lastUpdateTime)
|
cachejson.put("lastUpdateTime", lastUpdateTime)
|
||||||
cachejson.put("installServerTimeFromGP", Util.installServerTimeFromGP)
|
cachejson.put("installServerTimeFromGP", installServerTimeFromGP)
|
||||||
cachejson.put("clickServerTimeToGP", Util.clickServerTimeFromGP)
|
cachejson.put("clickServerTimeToGP", clickServerTimeFromGP)
|
||||||
cachejson.put("installTimeFromGP", Util.instalTimeFromGp)
|
cachejson.put("installTimeFromGP", instalTimeFromGp)
|
||||||
datajson.put("cacheJson", cachejson)
|
datajson.put("cacheJson", cachejson)
|
||||||
}
|
}
|
||||||
|
|
||||||
paramsJo.put("recordId", Util.recordId)
|
paramsJo.put("recordId", recordId)
|
||||||
paramsJo.put("preClickRecordId", Util.preClickRecordId)
|
paramsJo.put("preClickRecordId", preClickRecordId)
|
||||||
paramsJo.put("dataJson", datajson)
|
paramsJo.put("dataJson", datajson)
|
||||||
val clickInfoJo = JSONObject()
|
val clickInfoJo = JSONObject()
|
||||||
clickInfoJo.put("clickRet", Util.isClickRet)
|
clickInfoJo.put("clickRet", isClickRet)
|
||||||
clickInfoJo.put("clickIp", Util.delegateIp)
|
clickInfoJo.put("clickIp", delegateIp)
|
||||||
clickInfoJo.put("clickTime", Util.clickTime)
|
clickInfoJo.put("clickTime", clickTime)
|
||||||
clickInfoJo.put("clickErrReason", Util.clickErrReason)
|
clickInfoJo.put("clickErrReason", clickErrReason)
|
||||||
paramsJo.put("clickInfo", clickInfoJo)
|
paramsJo.put("clickInfo", clickInfoJo)
|
||||||
|
|
||||||
val installInfoJo = JSONObject()
|
val installInfoJo = JSONObject()
|
||||||
installInfoJo.put("installRet", Util.isInstallRet())
|
installInfoJo.put("installRet", isInstallRet())
|
||||||
installInfoJo.put("installTime", Util.installTime)
|
installInfoJo.put("installTime", installTime)
|
||||||
paramsJo.put("installInfo", installInfoJo)
|
paramsJo.put("installInfo", installInfoJo)
|
||||||
|
|
||||||
val logInfoJo = JSONObject()
|
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("setConfLog", Util.getParamsJson());
|
||||||
logInfoJo.put("hookVer", Util.HkVer())
|
logInfoJo.put("hookVer", HkVer())
|
||||||
logInfoJo.put("afVer", Util.getAppAfVer())
|
logInfoJo.put("afVer", getAppAfVer())
|
||||||
logInfoJo.put("afApp", Util.afApp)
|
logInfoJo.put("afApp", afApp)
|
||||||
paramsJo.put("logInfo", logInfoJo)
|
paramsJo.put("logInfo", logInfoJo)
|
||||||
|
|
||||||
if (null != Util.backupResult) {
|
if (null != backupResult) {
|
||||||
paramsJo.put("backUpFiles", Util.backupResult)
|
paramsJo.put("backUpFiles", backupResult)
|
||||||
}
|
}
|
||||||
var params: String? = null
|
var params: String? = null
|
||||||
params = paramsJo?.toString() ?: "error"
|
params = paramsJo?.toString() ?: "error"
|
||||||
|
|
||||||
var nRetryCount = 0
|
var nRetryCount = 0
|
||||||
do {
|
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")
|
LogUtils.i(TAG, "ret:$ret")
|
||||||
val jo = JSONObject(ret)
|
val jo = JSONObject(ret)
|
||||||
if (jo.getInt("code") == 1) {
|
if (jo.getInt("code") == 1) {
|
||||||
|
@ -175,9 +204,9 @@ class SendCallbackJobService : JobIntentService() {
|
||||||
LogUtils.i(TAG, "onHandleWork")
|
LogUtils.i(TAG, "onHandleWork")
|
||||||
var succ = false
|
var succ = false
|
||||||
try {
|
try {
|
||||||
val fileName: String? = Util.backFileName
|
val fileName: String? = backFileName
|
||||||
val fileName1: String = Util.backFileName1?:""
|
val fileName1: String = backFileName1 ?: ""
|
||||||
val fileName2: String = Util.backFileName2?:""
|
val fileName2: String = backFileName2 ?: ""
|
||||||
|
|
||||||
LogUtils.i(
|
LogUtils.i(
|
||||||
TAG,
|
TAG,
|
||||||
|
@ -191,9 +220,9 @@ class SendCallbackJobService : JobIntentService() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (succ) {
|
if (succ) {
|
||||||
Util.delFileSh(fileName)
|
delFileSh(fileName)
|
||||||
Util.delFileSh(fileName1)
|
delFileSh(fileName1)
|
||||||
Util.delFileSh(fileName2)
|
delFileSh(fileName2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -209,7 +238,7 @@ class SendCallbackJobService : JobIntentService() {
|
||||||
file1 = File(fileName1)
|
file1 = File(fileName1)
|
||||||
}
|
}
|
||||||
|
|
||||||
Util.chownSh(fileName, Util.getMainUserAndGroup(this))
|
chownSh(fileName, getMainUserAndGroup(this))
|
||||||
var url = "http://39.103.73.250/tt/ddj/backup.do"
|
var url = "http://39.103.73.250/tt/ddj/backup.do"
|
||||||
// if (Util.backUpServerIp != "") {
|
// if (Util.backUpServerIp != "") {
|
||||||
// url = "http://" + Util.backUpServerIp + "/tt/ddj/backup.do"
|
// url = "http://" + Util.backUpServerIp + "/tt/ddj/backup.do"
|
||||||
|
@ -231,8 +260,8 @@ class SendCallbackJobService : JobIntentService() {
|
||||||
builder.setType(MultipartBody.FORM)
|
builder.setType(MultipartBody.FORM)
|
||||||
|
|
||||||
//追加参数
|
//追加参数
|
||||||
builder.addFormDataPart("recordId", "${Util.recordId}")
|
builder.addFormDataPart("recordId", "${recordId}")
|
||||||
builder.addFormDataPart("afVer", Util.getAppAfVer())
|
builder.addFormDataPart("afVer", getAppAfVer())
|
||||||
|
|
||||||
if (file.exists() && file.length() > 0) {
|
if (file.exists() && file.length() > 0) {
|
||||||
builder.addFormDataPart(
|
builder.addFormDataPart(
|
||||||
|
@ -253,7 +282,7 @@ class SendCallbackJobService : JobIntentService() {
|
||||||
//创建RequestBody
|
//创建RequestBody
|
||||||
val body: RequestBody = builder.build()
|
val body: RequestBody = builder.build()
|
||||||
LogUtils.d(TAG, "sendBackupEvent-> url = $url")
|
LogUtils.d(TAG, "sendBackupEvent-> url = $url")
|
||||||
LogUtils.d(TAG, "sendBackupEvent-> recordId= " + Util.recordId)
|
LogUtils.d(TAG, "sendBackupEvent-> recordId= " + recordId)
|
||||||
//创建Request
|
//创建Request
|
||||||
val request = Request.Builder().url(url).post(body).build()
|
val request = Request.Builder().url(url).post(body).build()
|
||||||
//单独设置参数 比如读取超时时间
|
//单独设置参数 比如读取超时时间
|
||||||
|
@ -267,7 +296,7 @@ class SendCallbackJobService : JobIntentService() {
|
||||||
val retJo = JSONObject(ret)
|
val retJo = JSONObject(ret)
|
||||||
if (retJo.getInt("code") == 1) {
|
if (retJo.getInt("code") == 1) {
|
||||||
if (retJo.has("backUpFiles")) {
|
if (retJo.has("backUpFiles")) {
|
||||||
Util.backupResult = retJo.getJSONObject("backUpFiles")
|
backupResult = retJo.getJSONObject("backUpFiles")
|
||||||
}
|
}
|
||||||
result = true
|
result = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,14 @@ import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.JobIntentService
|
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.net.MyGet
|
||||||
import com.android.grape.util.ClashUtil
|
import com.android.grape.util.ClashUtil
|
||||||
import com.android.grape.util.ClashUtil.getProxyPort
|
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
|
import java.util.Locale
|
||||||
|
|
||||||
class StartVpnPortJobService : JobIntentService() {
|
class StartVpnPortJobService : JobIntentService() {
|
||||||
|
@ -23,9 +27,9 @@ class StartVpnPortJobService : JobIntentService() {
|
||||||
)
|
)
|
||||||
}, 1000L)
|
}, 1000L)
|
||||||
} else {
|
} else {
|
||||||
Util.setInstallRet(false)
|
setInstallRet(false)
|
||||||
Util.isClickRet = false
|
isClickRet = false
|
||||||
Util.setFinish(this)
|
setFinish(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +39,7 @@ class StartVpnPortJobService : JobIntentService() {
|
||||||
var nRetryCount = 0
|
var nRetryCount = 0
|
||||||
do {
|
do {
|
||||||
val url =
|
val url =
|
||||||
"http://39.103.73.250/tt/test/testProxy.jsp?port=$port&country=" + Util.proxyCountry
|
"http://39.103.73.250/tt/test/testProxy.jsp?port=$port&country=" + proxyCountry
|
||||||
?.uppercase(Locale.getDefault())
|
?.uppercase(Locale.getDefault())
|
||||||
val result: String = MyGet.get(url)
|
val result: String = MyGet.get(url)
|
||||||
Log.d(TAG, "request url == $url result$result")
|
Log.d(TAG, "request url == $url result$result")
|
||||||
|
@ -68,4 +72,4 @@ class StartVpnPortJobService : JobIntentService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,7 @@ import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.JobIntentService
|
import androidx.core.app.JobIntentService
|
||||||
import com.android.grape.MainApplication
|
|
||||||
import com.android.grape.util.ClashUtil
|
import com.android.grape.util.ClashUtil
|
||||||
import com.android.grape.util.CountryCode
|
|
||||||
import com.android.grape.util.Util
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 本地vpn功能
|
* 本地vpn功能
|
||||||
|
@ -51,4 +48,4 @@ class StartVpnServerJobService : JobIntentService() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
package com.android.grape.job
|
package com.android.grape.job
|
||||||
|
|
||||||
import android.app.ActivityManager
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.core.app.JobIntentService
|
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.MockTools
|
||||||
import com.android.grape.util.Util
|
import com.android.grape.util.TaskUtils
|
||||||
import com.blankj.utilcode.util.LogUtils
|
import com.android.grape.util.TaskUtils.setFinish
|
||||||
|
import com.android.grape.util.TaskUtils.setInstallRet
|
||||||
|
|
||||||
|
|
||||||
class UnInstallService : JobIntentService() {
|
class UnInstallService : JobIntentService() {
|
||||||
override fun onHandleWork(intent: Intent) {
|
override fun onHandleWork(intent: Intent) {
|
||||||
Util.backUp(this)
|
backUp(this)
|
||||||
Util.setInstallRet(true)
|
setInstallRet(true)
|
||||||
MockTools.exec("pm clear " + Util.recordPackageName)
|
MockTools.exec("pm clear " + recordPackageName)
|
||||||
Util.delFiles(this@UnInstallService)
|
delFiles(this@UnInstallService)
|
||||||
Util.setFinish(this@UnInstallService)
|
setFinish(this@UnInstallService)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -33,4 +36,4 @@ class UnInstallService : JobIntentService() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,9 +5,10 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import com.android.grape.data.AppState.script_status
|
||||||
import com.android.grape.job.UnInstallService
|
import com.android.grape.job.UnInstallService
|
||||||
import com.android.grape.util.ScriptUtil.SCRIPT_RESULT_KEY
|
import com.android.grape.util.ScriptUtils.SCRIPT_RESULT_KEY
|
||||||
import com.android.grape.util.Util
|
import com.android.grape.util.TaskUtils
|
||||||
import com.blankj.utilcode.util.LogUtils
|
import com.blankj.utilcode.util.LogUtils
|
||||||
|
|
||||||
class ScriptReceiver : BroadcastReceiver() {
|
class ScriptReceiver : BroadcastReceiver() {
|
||||||
|
@ -15,7 +16,7 @@ class ScriptReceiver : BroadcastReceiver() {
|
||||||
val action = intent.action
|
val action = intent.action
|
||||||
LogUtils.i("TAG", context.packageName + " Receive action:" + action)
|
LogUtils.i("TAG", context.packageName + " Receive action:" + action)
|
||||||
val scriptResult = intent.getStringExtra(SCRIPT_RESULT_KEY)
|
val scriptResult = intent.getStringExtra(SCRIPT_RESULT_KEY)
|
||||||
Util.script_status = 0
|
script_status = 0
|
||||||
LogUtils.i(
|
LogUtils.i(
|
||||||
"IOSTQ:脚本结束",
|
"IOSTQ:脚本结束",
|
||||||
"result----> $scriptResult"
|
"result----> $scriptResult"
|
||||||
|
@ -25,4 +26,4 @@ class ScriptReceiver : BroadcastReceiver() {
|
||||||
3000
|
3000
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.android.grape.sai
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.android.grape.util.ShellUtils.catSh
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
@ -13,6 +14,7 @@ import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
import java.io.UnsupportedEncodingException
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.security.DigestInputStream
|
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)
|
@Throws(IOException::class)
|
||||||
fun copyFileFromAssets(context: Context, assetFileName: String, destination: File?) {
|
fun copyFileFromAssets(context: Context, assetFileName: String, destination: File?) {
|
||||||
context.assets.open(assetFileName).use { inputStream ->
|
context.assets.open(assetFileName).use { inputStream ->
|
||||||
|
@ -176,4 +211,4 @@ object IOUtils {
|
||||||
return readStream(`in`)
|
return readStream(`in`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import android.util.Log
|
||||||
import android.util.Pair
|
import android.util.Pair
|
||||||
import com.android.grape.MainApplication
|
import com.android.grape.MainApplication
|
||||||
import com.android.grape.R
|
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.inter.ApkSource
|
||||||
import com.android.grape.sai.param.SaiPiSessionParams
|
import com.android.grape.sai.param.SaiPiSessionParams
|
||||||
import com.android.grape.sai.param.SaiPiSessionState
|
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.rootless.AndroidPackageInstallerError
|
||||||
import com.android.grape.sai.shell.MiuiUtils
|
import com.android.grape.sai.shell.MiuiUtils
|
||||||
import com.android.grape.sai.shell.Shell
|
import com.android.grape.sai.shell.Shell
|
||||||
import com.android.grape.util.Util
|
import com.android.grape.util.TaskUtils
|
||||||
|
import com.android.grape.util.TaskUtils.setInstallRet
|
||||||
import com.blankj.utilcode.util.LogUtils
|
import com.blankj.utilcode.util.LogUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Arrays
|
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.Semaphore
|
import java.util.concurrent.Semaphore
|
||||||
|
@ -76,7 +78,7 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
|
||||||
}
|
}
|
||||||
unlockInstallation()
|
unlockInstallation()
|
||||||
// Toast.makeText(context,"Installation succeed",Toast.LENGTH_SHORT).show();
|
// 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()
|
).build()
|
||||||
)
|
)
|
||||||
unlockInstallation()
|
unlockInstallation()
|
||||||
Util.setInstallRet(false)
|
setInstallRet(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
androidSessionId = createSession()
|
androidSessionId = createSession()
|
||||||
//todo params.apkSource().apkLocalPath?
|
//todo params.apkSource().apkLocalPath?
|
||||||
val path = "/sdcard/apks/${Util.recordPackageName}"
|
val path = "/sdcard/apks/${recordPackageName}"
|
||||||
val file = File(path)
|
val file = File(path)
|
||||||
|
|
||||||
val files = file.listFiles()
|
val files = file.listFiles()
|
||||||
|
@ -145,7 +147,7 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
|
||||||
if (f.length() <= 0) {
|
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())
|
setSessionState(sessionId, SaiPiSessionState.Builder(sessionId, SaiPiSessionStatus.INSTALLATION_FAILED).appTempName(appTempName).error(MainApplication.instance.getString(R.string.installer_error_unknown_apk_size), null).build())
|
||||||
unlockInstallation()
|
unlockInstallation()
|
||||||
Util.setInstallRet(false)
|
setInstallRet(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ensureCommandSucceeded(shell.exec(Shell.Command("pm", "install-write", f.length().toString(), androidSessionId.toString(), String.format("%d.apk", currentApkFile++), f.path)))
|
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())
|
""".trimIndent()).build())
|
||||||
unlockInstallation()
|
unlockInstallation()
|
||||||
|
|
||||||
Util.setInstallRet(false)
|
setInstallRet(false)
|
||||||
} else {
|
} else {
|
||||||
Util.isClickRet = true
|
isClickRet = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -207,7 +209,7 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
|
||||||
|
|
||||||
unlockInstallation()
|
unlockInstallation()
|
||||||
|
|
||||||
Util.setInstallRet(false)
|
setInstallRet(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,4 +357,4 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
|
||||||
companion object {
|
companion object {
|
||||||
private val mSharedSemaphore = Semaphore(1)
|
private val mSharedSemaphore = Semaphore(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,11 +4,12 @@ import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.android.grape.MainApplication
|
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.data.Device
|
||||||
import com.android.grape.net.Api
|
import com.android.grape.net.Api
|
||||||
import com.android.grape.net.ChangeCallBack
|
import com.android.grape.net.ChangeCallBack
|
||||||
import com.android.grape.provider.DeviceDataAccessor
|
import com.android.grape.provider.DeviceDataAccessor
|
||||||
import com.android.grape.util.Util.taskJson
|
|
||||||
import com.blankj.utilcode.util.LogUtils
|
import com.blankj.utilcode.util.LogUtils
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -116,7 +117,7 @@ object ChangeDeviceInfoUtil {
|
||||||
})
|
})
|
||||||
put("settingPropertiesList", JSONArray().apply {
|
put("settingPropertiesList", JSONArray().apply {
|
||||||
put(JSONObject().apply {
|
put(JSONObject().apply {
|
||||||
put("propertiesName", "ssaid/${Util.recordPackageName}")
|
put("propertiesName", "ssaid/${recordPackageName}")
|
||||||
put("propertiesValue", device.androidId)
|
put("propertiesValue", device.androidId)
|
||||||
})
|
})
|
||||||
put(JSONObject().apply {
|
put(JSONObject().apply {
|
||||||
|
@ -399,4 +400,4 @@ object ChangeDeviceInfoUtil {
|
||||||
// ShellUtils.execRootCmd("setprop ro.build.fingerprint \"Vortex/HD65_Select/HD65_Select:13/TP1A.220624.014/20240306:user/release-keys\"")
|
// ShellUtils.execRootCmd("setprop ro.build.fingerprint \"Vortex/HD65_Select/HD65_Select:13/TP1A.220624.014/20240306:user/release-keys\"")
|
||||||
// ShellUtils.execRootCmd("setprop ro.board.platform sm8150p")
|
// ShellUtils.execRootCmd("setprop ro.board.platform sm8150p")
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,16 @@
|
||||||
package com.android.grape.util
|
package com.android.grape.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.android.grape.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 com.blankj.utilcode.util.LogUtils
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
|
@ -31,8 +39,83 @@ import java.util.zip.ZipFile
|
||||||
* @date :2021/9/28 15:44
|
* @date :2021/9/28 15:44
|
||||||
*/
|
*/
|
||||||
object FileUtils {
|
object FileUtils {
|
||||||
private const val TAG = "IOSTQ:FileUtils"
|
public const val BUFFER_SIZE = 1024 * 1024 //1M Byte
|
||||||
private 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() // 删除目录本身
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LogUtils.d("TAG", "writePackageName: $packageName", null)
|
LogUtils.d("FileUtils", "writePackageName: $packageName", null)
|
||||||
try {
|
try {
|
||||||
BufferedOutputStream(
|
BufferedOutputStream(
|
||||||
FileOutputStream(file)
|
FileOutputStream(file)
|
||||||
|
@ -420,7 +557,7 @@ object FileUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeDevice(packageName: String, device: String) {
|
fun writeDevice(packageName: String, device: String) {
|
||||||
LogUtils.d("TAG", "writeDevice: $device")
|
LogUtils.d("FileUtils", "writeDevice: $device")
|
||||||
val base64Content: String =
|
val base64Content: String =
|
||||||
Base64.encodeToString(device.toByteArray(StandardCharsets.UTF_8), Base64.NO_WRAP)
|
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 cmd = "sh -c 'printf %s '\''" + base64Content + "'\'' > " + filePath + "'"
|
||||||
val result = ShellUtils.execRootCmdAndGetResult(cmd)
|
val result = ShellUtils.execRootCmdAndGetResult(cmd)
|
||||||
ShellUtils.execRootCmdAndGetResult("chmod 777 $filePath")
|
ShellUtils.execRootCmdAndGetResult("chmod 777 $filePath")
|
||||||
LogUtils.d("TAG", "writeDevice: $result")
|
LogUtils.d("FileUtils", "writeDevice: $result")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runPlugin(packageName: String){
|
fun runPlugin(packageName: String) {
|
||||||
val cmd = "apmt patch add -n ArmCloudAF -p $packageName -f /sdcard/Download/ArmCloudAF_lo.apk"
|
val cmd =
|
||||||
|
"apmt patch add -n ArmCloudAF -p $packageName -f /sdcard/Download/ArmCloudAF_lo.apk"
|
||||||
val result = ShellUtils.execRootCmdAndGetResult(cmd)
|
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 cmd = "apmt patch del -n ArmCloudAF"
|
||||||
val result = ShellUtils.execRootCmdAndGetResult(cmd)
|
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 ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,16 @@
|
||||||
package com.android.grape.util
|
package com.android.grape.util
|
||||||
|
|
||||||
import android.app.IMikRom
|
import android.app.IMikRom
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Environment
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.util.Log
|
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 {
|
object ServiceUtils {
|
||||||
private var iMikRom: IMikRom? = null
|
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("文件写入", "成功")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
* 使用Shell命令给提供的用户和组。
|
||||||
import java.io.IOException;
|
*
|
||||||
import java.io.InputStream;
|
* @param上下文调用该方法的上下文
|
||||||
import java.io.InputStreamReader;
|
* @param dir是将更改所有权的目录的路径
|
||||||
import java.io.InterruptedIOException;
|
* @param groupanduser组和用户以“组:用户”的形式
|
||||||
import java.io.OutputStream;
|
* @return true如果操作成功,则否则为否则
|
||||||
import java.lang.reflect.Field;
|
*/
|
||||||
import java.nio.charset.StandardCharsets;
|
fun chownSh(dir: String?, groupAndUser: String?): Boolean {
|
||||||
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) {
|
|
||||||
try {
|
try {
|
||||||
PackageManager pm = context.getPackageManager();
|
val cmd = "chown -R $groupAndUser $dir"
|
||||||
ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
|
Log.i("ShellUtils", "chownSh cmd:$cmd")
|
||||||
return appInfo.sourceDir; // 返回 APK 的完整路径
|
MockTools.exec(cmd)
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
return true
|
||||||
e.printStackTrace();
|
} catch (e: Exception) {
|
||||||
return "";
|
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 {
|
try {
|
||||||
LogUtils.e(Log.INFO, "ShellUtils", "Executing command: " + cmd, null);
|
val cmd = "cp -r -f $oldPath $newPath"
|
||||||
Process process = Runtime.getRuntime().exec(cmd);
|
Log.i("ShellUtils", "copyFolderSh cmd:$cmd")
|
||||||
process.waitFor();
|
MockTools.exec(cmd)
|
||||||
} catch (Exception e) {
|
} catch (e: Exception) {
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing command: " + e.getMessage(), e);
|
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) {
|
fun getPid(p: Process): Int {
|
||||||
int pid = -1;
|
var pid = -1
|
||||||
try {
|
return try {
|
||||||
Field f = p.getClass().getDeclaredField("pid");
|
val f = p.javaClass.getDeclaredField("pid")
|
||||||
f.setAccessible(true);
|
f.isAccessible = true
|
||||||
pid = f.getInt(p);
|
pid = f.getInt(p)
|
||||||
f.setAccessible(false);
|
f.isAccessible = false
|
||||||
} catch (Throwable e) {
|
pid
|
||||||
pid = -1;
|
} catch (e: Throwable) {
|
||||||
|
pid
|
||||||
}
|
}
|
||||||
return pid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasBin(String binName) {
|
fun hasBin(binName: String): Boolean {
|
||||||
// 验证 binName 是否符合规则
|
if (binName.isEmpty()) {
|
||||||
if (binName == null || binName.isEmpty()) {
|
LogUtils.e(Log.ERROR, "ShellUtils", "Invalid bin name", null)
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Invalid bin name",null);
|
throw IllegalArgumentException("Bin name cannot be null or empty")
|
||||||
throw new 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 != '-') {
|
if (!Character.isLetterOrDigit(c) && c != '.' && c != '_' && c != '-') {
|
||||||
throw new IllegalArgumentException("Invalid bin name");
|
throw IllegalArgumentException("Invalid bin name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 PATH 环境变量
|
val pathEnv = System.getenv("PATH") ?: return false
|
||||||
String pathEnv = System.getenv("PATH");
|
val paths = pathEnv.split(File.pathSeparatorChar)
|
||||||
if (pathEnv == null) {
|
for (path in paths) {
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "PATH environment variable is not available", null);
|
val file = File(path, binName)
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用适合当前系统的路径分隔符分割路径
|
|
||||||
String[] paths = pathEnv.split(File.pathSeparator);
|
|
||||||
for (String path : paths) {
|
|
||||||
File file = new File(path, binName); // 使用 File 构造完整路径
|
|
||||||
try {
|
try {
|
||||||
// 检查文件是否可执行
|
|
||||||
if (file.canExecute()) {
|
if (file.canExecute()) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
} catch (SecurityException e) {
|
} catch (e: SecurityException) {
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Security exception occurred while checking: " + file.getAbsolutePath(), e);
|
LogUtils.e(
|
||||||
|
Log.ERROR,
|
||||||
|
"ShellUtils",
|
||||||
|
"Security exception occurred while checking: ${file.absolutePath}",
|
||||||
|
e
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
// 如果未找到可执行文件,返回 false
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String execRootCmdAndGetResult(String cmd) {
|
fun execRootCmdAndGetResult(cmd: String): String {
|
||||||
Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: " + 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.");
|
|
||||||
// }
|
|
||||||
|
|
||||||
Process process = null;
|
val process: Process? = when {
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(2);
|
hasBin("su") -> Runtime.getRuntime().exec("su")
|
||||||
|
hasBin("xu") -> Runtime.getRuntime().exec("xu")
|
||||||
|
hasBin("vu") -> Runtime.getRuntime().exec("vu")
|
||||||
|
else -> Runtime.getRuntime().exec("sh")
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
val executor = Executors.newFixedThreadPool(2)
|
||||||
if (hasBin("su")) {
|
return try {
|
||||||
process = Runtime.getRuntime().exec("su");
|
process?.let {
|
||||||
} else if (hasBin("xu")) {
|
val os = BufferedOutputStream(it.outputStream)
|
||||||
process = Runtime.getRuntime().exec("xu");
|
val isInputStream = it.inputStream
|
||||||
} else if (hasBin("vu")) {
|
val errorStream = it.errorStream
|
||||||
process = Runtime.getRuntime().exec("vu");
|
val br =
|
||||||
} else {
|
BufferedReader(InputStreamReader(isInputStream, StandardCharsets.UTF_8))
|
||||||
process = Runtime.getRuntime().exec("sh");
|
val errorReader =
|
||||||
}
|
BufferedReader(InputStreamReader(errorStream, StandardCharsets.UTF_8))
|
||||||
|
|
||||||
try (OutputStream os = new BufferedOutputStream(process.getOutputStream());
|
executor.submit {
|
||||||
InputStream is = process.getInputStream();
|
|
||||||
InputStream errorStream = process.getErrorStream();
|
|
||||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
|
|
||||||
BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream, StandardCharsets.UTF_8))) {
|
|
||||||
|
|
||||||
executor.submit(() -> {
|
|
||||||
String line;
|
|
||||||
try {
|
try {
|
||||||
while ((line = errorReader.readLine()) != null) {
|
var line: String?
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Shell Error: " + line, null);
|
while (true) {
|
||||||
|
line = errorReader.readLine()
|
||||||
|
if (line == null) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
LogUtils.e(Log.ERROR, "ShellUtils", "Shell Error: $line", null)
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (e: IOException) {
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error while reading process error stream: " + e.getMessage(), e);
|
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) {
|
os.write("$cmd\n".toByteArray())
|
||||||
if (!process.waitFor(10, TimeUnit.SECONDS)) {
|
os.write("exit\n".toByteArray())
|
||||||
process.destroyForcibly();
|
os.flush()
|
||||||
throw new RuntimeException("Shell command execution timeout.");
|
|
||||||
|
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 {
|
} else {
|
||||||
long startTime = System.currentTimeMillis();
|
val startTime = System.currentTimeMillis()
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
process.exitValue();
|
it.exitValue()
|
||||||
break;
|
break
|
||||||
} catch (IllegalThreadStateException e) {
|
} catch (e: IllegalThreadStateException) {
|
||||||
if (System.currentTimeMillis() - startTime > 10000) { // 10 seconds
|
if (System.currentTimeMillis() - startTime > 10000) {
|
||||||
process.destroy();
|
it.destroy()
|
||||||
throw new RuntimeException("Shell command execution timeout.");
|
throw RuntimeException("Shell command execution timeout.")
|
||||||
}
|
}
|
||||||
Thread.sleep(100); // Sleep briefly before re-checking
|
Thread.sleep(100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
return output.toString().trim();
|
|
||||||
}
|
output.toString().trim()
|
||||||
} catch (IOException | InterruptedException e) {
|
} ?: ""
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: " + e.getMessage(), e);
|
} catch (e: IOException) {
|
||||||
Thread.currentThread().interrupt();
|
LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: ${e.message}", e)
|
||||||
return "Error: " + e.getMessage();
|
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 {
|
} finally {
|
||||||
if (process != null) {
|
process?.destroy()
|
||||||
process.destroy();
|
executor.shutdown()
|
||||||
}
|
Log.d("ShellUtils", "Executor service shut down.")
|
||||||
executor.shutdown();
|
|
||||||
Log.d("ShellUtils", "Executor service shut down.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void execRootCmd(String cmd) {
|
fun execRootCmd(cmd: String) {
|
||||||
// 校验命令是否安全
|
val cmds = mutableListOf(cmd)
|
||||||
// if (!isCommandSafe(cmd)) {
|
synchronized(ShellUtils::class.java) {
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
List<String> cmds = new ArrayList<>();
|
|
||||||
cmds.add(cmd);
|
|
||||||
|
|
||||||
// 使用同步锁保护线程安全
|
|
||||||
synchronized (ShellUtils.class) {
|
|
||||||
try {
|
try {
|
||||||
List<String> results = execRootCmds(cmds);
|
val results = execRootCmds(cmds)
|
||||||
// 判断是否需要打印输出,仅用于开发调试阶段
|
for (result in results) {
|
||||||
for (String result : results) {
|
Log.d("ShellUtils", "Command Result: $result")
|
||||||
Log.d("ShellUtils", "Command Result: " + result);
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (e: Exception) {
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Unexpected error: " + e.getMessage(), e);
|
LogUtils.e(Log.ERROR, "ShellUtils", "Unexpected error: ${e.message}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isCommandSafe(cmd: String): Boolean {
|
||||||
private static boolean isCommandSafe(String cmd) {
|
if (cmd.isBlank()) {
|
||||||
// 检查空值和空字符串
|
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command: empty or null value.", null)
|
||||||
if (cmd == null || cmd.trim().isEmpty()) {
|
return false
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command: empty or null value.", null);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查非法字符
|
if (!cmd.matches(Regex("^[a-zA-Z0-9._/:\\\\\\-~`'\" *|]+\$"))) {
|
||||||
if (!cmd.matches("^[a-zA-Z0-9._/:\\-~`'\" *|]+$")) {
|
LogUtils.e(
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command due to illegal characters: " + cmd, null);
|
Log.ERROR,
|
||||||
return false;
|
"ShellUtils",
|
||||||
|
"Rejected command due to illegal characters: $cmd",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查多命令(逻辑运算符限制)
|
|
||||||
if (cmd.contains("&&") || cmd.contains("||")) {
|
if (cmd.contains("&&") || cmd.contains("||")) {
|
||||||
Log.d("ShellUtils", "Command contains logical operators.");
|
|
||||||
if (!isExpectedMultiCommand(cmd)) {
|
if (!isExpectedMultiCommand(cmd)) {
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command due to prohibited structure: " + cmd, null);
|
LogUtils.e(
|
||||||
return false;
|
Log.ERROR,
|
||||||
|
"ShellUtils",
|
||||||
|
"Rejected command due to prohibited structure: $cmd",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 路径遍历保护
|
if (cmd.contains("../") || cmd.contains("..\\\\")) {
|
||||||
if (cmd.contains("../") || cmd.contains("..\\")) {
|
LogUtils.e(
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command due to path traversal attempt: " + cmd, null);
|
Log.ERROR,
|
||||||
return false;
|
"ShellUtils",
|
||||||
|
"Rejected command due to path traversal attempt: $cmd",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 命令长度限制
|
if (cmd.startsWith("tar") && cmd.length > 800) {
|
||||||
if (cmd.startsWith("tar") && cmd.length() > 800) { // 特定命令支持更长长度
|
LogUtils.e(
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Command rejected due to excessive length.", null);
|
Log.ERROR,
|
||||||
return false;
|
"ShellUtils",
|
||||||
} else if (cmd.length() > 500) {
|
"Command rejected due to excessive length.",
|
||||||
LogUtils.e("ShellUtils", "Command rejected due to excessive length.", null);
|
null
|
||||||
return false;
|
)
|
||||||
|
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);
|
Log.d("ShellUtils", "Command passed safety checks: $cmd")
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 附加方法:检查多命令是否符合预期
|
private fun isExpectedMultiCommand(cmd: String): Boolean {
|
||||||
private static boolean isExpectedMultiCommand(String cmd) {
|
return cmd.matches(Regex("^cd .+ && (tar|zip|cp).+"))
|
||||||
// 判断是否为允许的命令组合,比如 `cd` 或 `tar` 组合命令
|
|
||||||
return cmd.matches("^cd .+ && (tar|zip|cp).+");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> execRootCmds(List<String> cmds) {
|
fun execRootCmds(cmds: List<String>): List<String> {
|
||||||
List<String> results = new ArrayList<>();
|
val results = mutableListOf<String>()
|
||||||
Process process = null;
|
var process: Process? = null
|
||||||
try {
|
try {
|
||||||
// 初始化 Shell 环境
|
process = if (hasBin("su")) {
|
||||||
process = hasBin("su") ? Runtime.getRuntime().exec("su") : Runtime.getRuntime().exec("sh");
|
Runtime.getRuntime().exec("su")
|
||||||
|
} else {
|
||||||
// 启动读取线程
|
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
try {
|
||||||
// 执行命令、等待解决
|
process.waitFor()
|
||||||
process.waitFor();
|
} catch (e: InterruptedException) {
|
||||||
} catch (InterruptedException e) {
|
Thread.currentThread().interrupt()
|
||||||
Thread.currentThread().interrupt(); // 恢复中断
|
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e)
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待子线程完成
|
stdThread.join()
|
||||||
stdThread.join();
|
errThread.join()
|
||||||
errThread.join();
|
|
||||||
|
|
||||||
} catch (InterruptedIOException e) {
|
} catch (e: InterruptedIOException) {
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout: Interrupted", e);
|
LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout: Interrupted", e)
|
||||||
Thread.currentThread().interrupt(); // 恢复线程的中断状态
|
Thread.currentThread().interrupt()
|
||||||
} catch (Exception e) {
|
} catch (e: Exception) {
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e);
|
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e)
|
||||||
} finally {
|
} finally {
|
||||||
if (process != null) {
|
process?.destroy()
|
||||||
process.destroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return results;
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasRootAccess() {
|
/**
|
||||||
// 记录是否出现安全异常
|
* 使用Shell命令删除给定文件名指定的文件或目录。
|
||||||
boolean hasSecurityError = false;
|
* 此方法执行“ 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 检查二进制文件
|
fun delFolderSh(dir: String, fileName: String) {
|
||||||
String[] binariesToCheck = {"su", "xu", "vu"};
|
Log.i("ShellUtils", "start delFolderSh :$fileName")
|
||||||
for (String bin : binariesToCheck) {
|
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 {
|
try {
|
||||||
if (hasBin(bin)) {
|
if (hasBin(bin)) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
} catch (SecurityException e) {
|
} catch (e: SecurityException) {
|
||||||
hasSecurityError = true;
|
hasSecurityError = true
|
||||||
LogUtils.e(Log.ERROR, "ShellUtils", "Security exception while checking: " + bin, e);
|
LogUtils.e(
|
||||||
|
Log.ERROR,
|
||||||
|
"ShellUtils",
|
||||||
|
"Security exception while checking: $bin",
|
||||||
|
e
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断如果发生安全异常则反馈问题
|
|
||||||
if (hasSecurityError) {
|
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 result
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
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 ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化并构建请求参数:获取设备唯一 ID(ANDROID_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
Loading…
Reference in New Issue