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 com.android.grape.databinding.ActivityMainBinding
|
||||
import com.android.grape.job.MonitorService
|
||||
import com.android.grape.service.SocketServer
|
||||
import com.android.grape.util.BackupUtils.killRecordProcess
|
||||
import com.android.grape.util.ClashUtil
|
||||
import com.android.grape.util.MockTools
|
||||
import com.android.grape.util.NotificationPermissionHandler
|
||||
import com.android.grape.util.ScriptUtil
|
||||
import com.android.grape.util.ScriptUtils.registerScriptResultReceiver
|
||||
import com.android.grape.util.ScriptUtils.unregisterScriptResultReceiver
|
||||
import com.android.grape.util.StoragePermissionHelper
|
||||
import com.android.grape.util.Util.killRecordProcess
|
||||
|
||||
/**
|
||||
* public class MainActivity extends AppCompatActivity
|
||||
|
@ -68,7 +68,7 @@ class MainActivity : AppCompatActivity() {
|
|||
insets
|
||||
}
|
||||
checkPermission()
|
||||
ScriptUtil.registerScriptResultReceiver()
|
||||
registerScriptResultReceiver()
|
||||
viewBinding.start.setOnClickListener {
|
||||
MonitorService.onEvent(MainApplication.instance)
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ class MainActivity : AppCompatActivity() {
|
|||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
ClashUtil.unregisterReceiver(this)
|
||||
ScriptUtil.unregisterScriptResultReceiver()
|
||||
unregisterScriptResultReceiver()
|
||||
}
|
||||
|
||||
private fun checkPermission() {
|
||||
|
|
|
@ -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.Looper
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.util.ScriptUtil
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.data.AppState.recordPackageName
|
||||
import com.android.grape.util.ScriptUtils.execScript
|
||||
import com.android.grape.util.TaskUtils
|
||||
|
||||
class AutoJobService : JobIntentService() {
|
||||
override fun onHandleWork(intent: Intent) {
|
||||
|
@ -14,9 +15,9 @@ class AutoJobService : JobIntentService() {
|
|||
|
||||
if (openZzzj) {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
ScriptUtil.execScript(
|
||||
execScript(
|
||||
this@AutoJobService,
|
||||
"/sdcard/script/${Util.recordPackageName}/main.js"
|
||||
"/sdcard/script/${recordPackageName}/main.js"
|
||||
)
|
||||
}, 2000L)
|
||||
} else {
|
||||
|
|
|
@ -4,18 +4,23 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.data.AppState.clickErrReason
|
||||
import com.android.grape.data.AppState.delegateIp
|
||||
import com.android.grape.data.AppState.isClickRet
|
||||
import com.android.grape.data.AppState.proxyCountry
|
||||
import com.android.grape.data.AppState.ua
|
||||
import com.android.grape.net.MyGet
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.util.TaskUtils
|
||||
|
||||
class CheckIpJobService : JobIntentService() {
|
||||
override fun onHandleWork(intent: Intent) {
|
||||
if (checkIp()) {
|
||||
InstallService.onEvent(this)
|
||||
} else {
|
||||
Util.isClickRet = false
|
||||
Util.setInstallRet(false)
|
||||
Util.clickErrReason = "networkErr"
|
||||
Util.setFinish(this)
|
||||
isClickRet = false
|
||||
TaskUtils.setInstallRet(false)
|
||||
clickErrReason = "networkErr"
|
||||
TaskUtils.setFinish(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,9 +30,9 @@ class CheckIpJobService : JobIntentService() {
|
|||
do {
|
||||
val url = "http://8.218.80.200/myipp"
|
||||
|
||||
Log.i(TAG, "start to getIp : " + url + " ; " + Util.ua)
|
||||
Log.i(TAG, "start to getIp : " + url + " ; " + ua)
|
||||
|
||||
val resp: String? = MyGet.getData(url, Util.ua)
|
||||
val resp: String? = MyGet.getData(url, ua)
|
||||
|
||||
Log.i(TAG, "checkIp : $resp")
|
||||
|
||||
|
@ -35,9 +40,9 @@ class CheckIpJobService : JobIntentService() {
|
|||
try {
|
||||
val conn =
|
||||
resp.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
if (conn.size > 1 && Util.proxyCountry.equals(conn[1])) {
|
||||
if (conn.size > 1 && proxyCountry.equals(conn[1])) {
|
||||
val ip = conn[0]
|
||||
Util.delegateIp = ip
|
||||
delegateIp = ip
|
||||
return true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
|
|
@ -2,9 +2,10 @@ package com.android.grape.job
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.data.AppState.clickErrReason
|
||||
import com.android.grape.data.AppState.isClickRet
|
||||
import com.android.grape.util.TaskUtils
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
|
||||
class DownloadAppJobService : JobIntentService() {
|
||||
|
@ -14,7 +15,7 @@ class DownloadAppJobService : JobIntentService() {
|
|||
var succ: Boolean = false
|
||||
|
||||
try {
|
||||
succ = Util.execDownload(this)
|
||||
succ = TaskUtils.execDownload(this)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
@ -26,10 +27,10 @@ class DownloadAppJobService : JobIntentService() {
|
|||
// InstallService.onEvent(this)
|
||||
StartVpnPortJobService.onEvent(this)
|
||||
} else {
|
||||
Util.isClickRet = false
|
||||
Util.setInstallRet(false)
|
||||
Util.clickErrReason = "downloadErr"
|
||||
Util.setFinish(this)
|
||||
isClickRet = false
|
||||
TaskUtils.setInstallRet(false)
|
||||
clickErrReason = "downloadErr"
|
||||
TaskUtils.setFinish(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,19 +6,22 @@ import android.os.Handler
|
|||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.data.AppState.isNeedRestored
|
||||
import com.android.grape.data.AppState.recordPackageName
|
||||
import com.android.grape.util.AppUtils.installRecord
|
||||
import com.android.grape.util.TaskUtils
|
||||
|
||||
class InstallService : JobIntentService() {
|
||||
override fun onHandleWork(intent: Intent) {
|
||||
Log.i(TAG, "onHandleWork ...")
|
||||
|
||||
if (Util.installRecord(this)) {
|
||||
if (installRecord(this)) {
|
||||
Log.i(TAG, "installRecord succ")
|
||||
tryNum = 0
|
||||
Util.setInstallRet(true)
|
||||
TaskUtils.setInstallRet(true)
|
||||
|
||||
println("IOSTQ:isNeedRestored == " + Util.isNeedRestored)
|
||||
if (Util.isNeedRestored) {
|
||||
println("IOSTQ:isNeedRestored == " + isNeedRestored)
|
||||
if (isNeedRestored) {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
RecoverJobService.onEvent(
|
||||
this@InstallService
|
||||
|
@ -40,9 +43,9 @@ class InstallService : JobIntentService() {
|
|||
)
|
||||
}, 5000)
|
||||
} else { //超过5次安装失败直接取消后续
|
||||
Log.i(TAG, "install error : " + Util.recordPackageName + " ; " + tryNum)
|
||||
Log.i(TAG, "install error : " + recordPackageName + " ; " + tryNum)
|
||||
|
||||
Util.setFinish(this)
|
||||
TaskUtils.setFinish(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,10 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.data.AppState.clickTime
|
||||
import com.android.grape.util.ClashUtil
|
||||
import com.android.grape.util.MockTools
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.util.TaskUtils
|
||||
|
||||
|
||||
/**
|
||||
|
@ -46,11 +47,11 @@ class MonitorService : JobIntentService() {
|
|||
ClashUtil.switchProxyGroup("PROXY", "DIRECT", "http://127.0.0.1:6170")
|
||||
Thread.sleep(3000)
|
||||
|
||||
if (Util.clickTime > 0L) {
|
||||
Util.clickTime = 0L
|
||||
if (clickTime > 0L) {
|
||||
clickTime = 0L
|
||||
SendCallbackJobService.onEvent(this)
|
||||
} else {
|
||||
Util.execTask(applicationContext)
|
||||
TaskUtils.execTask(applicationContext)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
|
|
|
@ -2,25 +2,29 @@ package com.android.grape.job
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.data.AppState.AUTO_JSPACKAGENAME
|
||||
import com.android.grape.data.AppState.canAutoLc
|
||||
import com.android.grape.data.AppState.isCanAuto
|
||||
import com.android.grape.data.AppState.recordPackageName
|
||||
import com.android.grape.net.ChangeCallBack
|
||||
import com.android.grape.provider.DeviceDataAccessor
|
||||
import com.android.grape.provider.DeviceInfoHelper
|
||||
import com.android.grape.util.ChangeDeviceInfoUtil
|
||||
import com.android.grape.util.FileUtils
|
||||
import com.android.grape.util.MockTools
|
||||
import com.android.grape.util.ScriptUtils.doScript
|
||||
import com.android.grape.util.ServiceUtils
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.util.TaskUtils
|
||||
import com.android.grape.util.TaskUtils.setFinish
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class OpenAppService : JobIntentService() {
|
||||
override fun onHandleWork(intent: Intent) {
|
||||
println("IOSTQ:isCanAuto() == " + Util.isCanAuto)
|
||||
println("IOSTQ:getCanAutoLc() == " + Util.canAutoLc)
|
||||
Util.recordPackageName?.let {
|
||||
println("IOSTQ:isCanAuto() == " + isCanAuto)
|
||||
println("IOSTQ:getCanAutoLc() == " + canAutoLc)
|
||||
recordPackageName?.let {
|
||||
ServiceUtils.setEnableApp(it, true)
|
||||
}
|
||||
ServiceUtils.setEnableApp("org.mozilla.firefox", true)
|
||||
|
@ -31,32 +35,32 @@ class OpenAppService : JobIntentService() {
|
|||
ChangeDeviceInfoUtil.changeDevice(callBack = object : ChangeCallBack {
|
||||
override fun changeSuccess() {
|
||||
runCatching {
|
||||
if (Util.isCanAuto && Util.canAutoLc.isNotEmpty()) {
|
||||
if (isCanAuto && canAutoLc.isNotEmpty()) {
|
||||
val deviceInfo = DeviceDataAccessor.getDeviceInfo(applicationContext, DeviceInfoHelper.getDeviceId())
|
||||
FileUtils.writePackageName(Util.recordPackageName?:"")
|
||||
FileUtils.writeDevice(Util.recordPackageName?:"", deviceInfo?: "")
|
||||
FileUtils.runPlugin(Util.recordPackageName?:"")
|
||||
MockTools.exec("pm grant ${Util.AUTO_JSPACKAGENAME} android.permission.READ_EXTERNAL_STORAGE") //sdcard权限
|
||||
MockTools.exec("pm grant ${Util.AUTO_JSPACKAGENAME} android.permission.SYSTEM_ALERT_WINDOW") //悬浮窗权限
|
||||
MockTools.exec("settings put secure enabled_accessibility_services ${Util.AUTO_JSPACKAGENAME}/${Util.AUTO_JSPACKAGENAME}.core.accessibility.AccessibilityService")
|
||||
Util.doScript(this@OpenAppService, Util.AUTO_JSPACKAGENAME) //autojs
|
||||
FileUtils.writePackageName(recordPackageName?:"")
|
||||
FileUtils.writeDevice(recordPackageName?:"", deviceInfo?: "")
|
||||
FileUtils.runPlugin(recordPackageName?:"")
|
||||
MockTools.exec("pm grant ${AUTO_JSPACKAGENAME} android.permission.READ_EXTERNAL_STORAGE") //sdcard权限
|
||||
MockTools.exec("pm grant ${AUTO_JSPACKAGENAME} android.permission.SYSTEM_ALERT_WINDOW") //悬浮窗权限
|
||||
MockTools.exec("settings put secure enabled_accessibility_services ${AUTO_JSPACKAGENAME}/${AUTO_JSPACKAGENAME}.core.accessibility.AccessibilityService")
|
||||
doScript(this@OpenAppService, AUTO_JSPACKAGENAME) //autojs
|
||||
}else {
|
||||
Util.setFinish(this@OpenAppService)
|
||||
setFinish(this@OpenAppService)
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
Util.setFinish(this@OpenAppService)
|
||||
setFinish(this@OpenAppService)
|
||||
}
|
||||
}
|
||||
|
||||
override fun changeFailed() {
|
||||
Util.setFinish(this@OpenAppService)
|
||||
setFinish(this@OpenAppService)
|
||||
}
|
||||
|
||||
})
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
Util.setFinish(this@OpenAppService)
|
||||
setFinish(this@OpenAppService)
|
||||
}
|
||||
// Util.hookOpenApp(this);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ import android.os.Handler
|
|||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.util.BackupUtils.recoverRecordData
|
||||
import com.android.grape.util.TaskUtils
|
||||
|
||||
class RecoverJobService : JobIntentService() {
|
||||
override fun onHandleWork(intent: Intent) {
|
||||
|
@ -15,7 +16,7 @@ class RecoverJobService : JobIntentService() {
|
|||
var succ = false
|
||||
|
||||
try {
|
||||
succ = Util.recoverRecordData(this)
|
||||
succ = recoverRecordData(this)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
|
|
@ -5,8 +5,37 @@ import android.content.Intent
|
|||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.net.MyPost
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.data.AppState.afApp
|
||||
import com.android.grape.data.AppState.backFileName
|
||||
import com.android.grape.data.AppState.backFileName1
|
||||
import com.android.grape.data.AppState.backFileName2
|
||||
import com.android.grape.data.AppState.backupResult
|
||||
import com.android.grape.data.AppState.clickErrReason
|
||||
import com.android.grape.data.AppState.clickServerTimeFromGP
|
||||
import com.android.grape.data.AppState.clickTime
|
||||
import com.android.grape.data.AppState.delegateIp
|
||||
import com.android.grape.data.AppState.instalTimeFromGp
|
||||
import com.android.grape.data.AppState.installServerTimeFromGP
|
||||
import com.android.grape.data.AppState.installTime
|
||||
import com.android.grape.data.AppState.isClickRet
|
||||
import com.android.grape.data.AppState.isNeedBackup
|
||||
import com.android.grape.data.AppState.isNeedRestored
|
||||
import com.android.grape.data.AppState.lastUpdateTime
|
||||
import com.android.grape.data.AppState.logBuffer
|
||||
import com.android.grape.data.AppState.preClickRecordId
|
||||
import com.android.grape.data.AppState.recordId
|
||||
import com.android.grape.data.AppState.reloginRecordId
|
||||
import com.android.grape.net.AfClient.postData
|
||||
import com.android.grape.util.AppUtils.getAppAfVer
|
||||
import com.android.grape.util.DeviceUtils.getMainUserAndGroup
|
||||
import com.android.grape.util.MyPost
|
||||
import com.android.grape.util.ShellUtils.chownSh
|
||||
import com.android.grape.util.ShellUtils.delFileSh
|
||||
import com.android.grape.util.TaskUtils
|
||||
import com.android.grape.util.TaskUtils.HkVer
|
||||
import com.android.grape.util.TaskUtils.getAfLog
|
||||
import com.android.grape.util.TaskUtils.isInstallRet
|
||||
import com.android.grape.util.TaskUtils.setFinish
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
|
@ -31,16 +60,16 @@ class SendCallbackJobService : JobIntentService() {
|
|||
override fun onHandleWork(intent: Intent) {
|
||||
LogUtils.i(TAG, "onHandleWork")
|
||||
|
||||
if (Util.isInstallRet()) {
|
||||
if (isInstallRet()) {
|
||||
SendBackup()
|
||||
|
||||
if (Util.isNeedRestored) {
|
||||
if (isNeedRestored) {
|
||||
sendReloginEvent()
|
||||
} else {
|
||||
sendLogEvent()
|
||||
}
|
||||
}
|
||||
Util.setFinish(this@SendCallbackJobService)
|
||||
setFinish(this@SendCallbackJobService)
|
||||
}
|
||||
|
||||
|
||||
|
@ -51,39 +80,39 @@ class SendCallbackJobService : JobIntentService() {
|
|||
val paramsJo = JSONObject()
|
||||
val datajson = JSONObject()
|
||||
|
||||
if (Util.isNeedBackup) {
|
||||
if (isNeedBackup) {
|
||||
val cachejson = JSONObject()
|
||||
cachejson.put("firstInstallTime", Util.installTime)
|
||||
cachejson.put("lastUpdateTime", Util.lastUpdateTime)
|
||||
cachejson.put("installServerTimeFromGP", Util.installServerTimeFromGP)
|
||||
cachejson.put("clickServerTimeToGP", Util.clickServerTimeFromGP)
|
||||
cachejson.put("installTimeFromGP", Util.instalTimeFromGp)
|
||||
cachejson.put("firstInstallTime", installTime)
|
||||
cachejson.put("lastUpdateTime", lastUpdateTime)
|
||||
cachejson.put("installServerTimeFromGP", installServerTimeFromGP)
|
||||
cachejson.put("clickServerTimeToGP", clickServerTimeFromGP)
|
||||
cachejson.put("installTimeFromGP", instalTimeFromGp)
|
||||
datajson.put("cacheJson", cachejson)
|
||||
}
|
||||
|
||||
paramsJo.put("recordId", Util.recordId)
|
||||
paramsJo.put("preClickRecordId", Util.preClickRecordId)
|
||||
paramsJo.put("recordId", recordId)
|
||||
paramsJo.put("preClickRecordId", preClickRecordId)
|
||||
paramsJo.put("dataJson", datajson)
|
||||
paramsJo.put("reloginRecordId", Util.reloginRecordId)
|
||||
paramsJo.put("reloginRecordId", reloginRecordId)
|
||||
val clickInfoJo = JSONObject()
|
||||
clickInfoJo.put("clickRet", Util.isClickRet)
|
||||
clickInfoJo.put("clickIp", Util.delegateIp)
|
||||
clickInfoJo.put("clickTime", Util.clickTime)
|
||||
clickInfoJo.put("clickErrReason", Util.clickErrReason)
|
||||
clickInfoJo.put("clickRet", isClickRet)
|
||||
clickInfoJo.put("clickIp", delegateIp)
|
||||
clickInfoJo.put("clickTime", clickTime)
|
||||
clickInfoJo.put("clickErrReason", clickErrReason)
|
||||
paramsJo.put("clickInfo", clickInfoJo)
|
||||
|
||||
val installInfoJo = JSONObject()
|
||||
installInfoJo.put("installRet", Util.isInstallRet())
|
||||
installInfoJo.put("installTime", Util.installTime)
|
||||
installInfoJo.put("installRet", isInstallRet())
|
||||
installInfoJo.put("installTime", installTime)
|
||||
paramsJo.put("installInfo", installInfoJo)
|
||||
|
||||
|
||||
if (null != Util.backupResult) {
|
||||
paramsJo.put("backUpFiles", Util.backupResult)
|
||||
if (null != backupResult) {
|
||||
paramsJo.put("backUpFiles", backupResult)
|
||||
}
|
||||
|
||||
val logInfoJo = JSONObject()
|
||||
logInfoJo.put("afLog", Util.getAfLog() + "\r\n" + Util.logBuffer.toString())
|
||||
logInfoJo.put("afLog", getAfLog() + "\r\n" + logBuffer.toString())
|
||||
// logInfoJo.put("setConfLog", Util.getParamsJson());
|
||||
paramsJo.put("logInfo", logInfoJo)
|
||||
var params: String? = null
|
||||
|
@ -91,7 +120,7 @@ class SendCallbackJobService : JobIntentService() {
|
|||
|
||||
var nRetryCount = 0
|
||||
do {
|
||||
val ret: String? = MyPost.postData(params.toByteArray(charset("utf-8")), url)
|
||||
val ret: String? = postData(params.toByteArray(charset("utf-8")), url)
|
||||
LogUtils.i(TAG, "ret:$ret")
|
||||
val jo = ret?.let { JSONObject(it) }
|
||||
if (jo?.getInt("code") == 1) {
|
||||
|
@ -113,48 +142,48 @@ class SendCallbackJobService : JobIntentService() {
|
|||
val paramsJo = JSONObject()
|
||||
val datajson = JSONObject()
|
||||
|
||||
if (Util.isNeedBackup) {
|
||||
if (isNeedBackup) {
|
||||
val cachejson = JSONObject()
|
||||
cachejson.put("firstInstallTime", Util.installTime)
|
||||
cachejson.put("lastUpdateTime", Util.lastUpdateTime)
|
||||
cachejson.put("installServerTimeFromGP", Util.installServerTimeFromGP)
|
||||
cachejson.put("clickServerTimeToGP", Util.clickServerTimeFromGP)
|
||||
cachejson.put("installTimeFromGP", Util.instalTimeFromGp)
|
||||
cachejson.put("firstInstallTime", installTime)
|
||||
cachejson.put("lastUpdateTime", lastUpdateTime)
|
||||
cachejson.put("installServerTimeFromGP", installServerTimeFromGP)
|
||||
cachejson.put("clickServerTimeToGP", clickServerTimeFromGP)
|
||||
cachejson.put("installTimeFromGP", instalTimeFromGp)
|
||||
datajson.put("cacheJson", cachejson)
|
||||
}
|
||||
|
||||
paramsJo.put("recordId", Util.recordId)
|
||||
paramsJo.put("preClickRecordId", Util.preClickRecordId)
|
||||
paramsJo.put("recordId", recordId)
|
||||
paramsJo.put("preClickRecordId", preClickRecordId)
|
||||
paramsJo.put("dataJson", datajson)
|
||||
val clickInfoJo = JSONObject()
|
||||
clickInfoJo.put("clickRet", Util.isClickRet)
|
||||
clickInfoJo.put("clickIp", Util.delegateIp)
|
||||
clickInfoJo.put("clickTime", Util.clickTime)
|
||||
clickInfoJo.put("clickErrReason", Util.clickErrReason)
|
||||
clickInfoJo.put("clickRet", isClickRet)
|
||||
clickInfoJo.put("clickIp", delegateIp)
|
||||
clickInfoJo.put("clickTime", clickTime)
|
||||
clickInfoJo.put("clickErrReason", clickErrReason)
|
||||
paramsJo.put("clickInfo", clickInfoJo)
|
||||
|
||||
val installInfoJo = JSONObject()
|
||||
installInfoJo.put("installRet", Util.isInstallRet())
|
||||
installInfoJo.put("installTime", Util.installTime)
|
||||
installInfoJo.put("installRet", isInstallRet())
|
||||
installInfoJo.put("installTime", installTime)
|
||||
paramsJo.put("installInfo", installInfoJo)
|
||||
|
||||
val logInfoJo = JSONObject()
|
||||
logInfoJo.put("afLog", Util.getAfLog() + "\r\n" + Util.logBuffer.toString())
|
||||
logInfoJo.put("afLog", getAfLog() + "\r\n" + logBuffer.toString())
|
||||
// logInfoJo.put("setConfLog", Util.getParamsJson());
|
||||
logInfoJo.put("hookVer", Util.HkVer())
|
||||
logInfoJo.put("afVer", Util.getAppAfVer())
|
||||
logInfoJo.put("afApp", Util.afApp)
|
||||
logInfoJo.put("hookVer", HkVer())
|
||||
logInfoJo.put("afVer", getAppAfVer())
|
||||
logInfoJo.put("afApp", afApp)
|
||||
paramsJo.put("logInfo", logInfoJo)
|
||||
|
||||
if (null != Util.backupResult) {
|
||||
paramsJo.put("backUpFiles", Util.backupResult)
|
||||
if (null != backupResult) {
|
||||
paramsJo.put("backUpFiles", backupResult)
|
||||
}
|
||||
var params: String? = null
|
||||
params = paramsJo?.toString() ?: "error"
|
||||
|
||||
var nRetryCount = 0
|
||||
do {
|
||||
val ret: String = MyPost.postData( params.toByteArray(charset("utf-8")), url)?:""
|
||||
val ret: String = postData(params.toByteArray(charset("utf-8")), url) ?: ""
|
||||
LogUtils.i(TAG, "ret:$ret")
|
||||
val jo = JSONObject(ret)
|
||||
if (jo.getInt("code") == 1) {
|
||||
|
@ -175,9 +204,9 @@ class SendCallbackJobService : JobIntentService() {
|
|||
LogUtils.i(TAG, "onHandleWork")
|
||||
var succ = false
|
||||
try {
|
||||
val fileName: String? = Util.backFileName
|
||||
val fileName1: String = Util.backFileName1?:""
|
||||
val fileName2: String = Util.backFileName2?:""
|
||||
val fileName: String? = backFileName
|
||||
val fileName1: String = backFileName1 ?: ""
|
||||
val fileName2: String = backFileName2 ?: ""
|
||||
|
||||
LogUtils.i(
|
||||
TAG,
|
||||
|
@ -191,9 +220,9 @@ class SendCallbackJobService : JobIntentService() {
|
|||
}
|
||||
}
|
||||
if (succ) {
|
||||
Util.delFileSh(fileName)
|
||||
Util.delFileSh(fileName1)
|
||||
Util.delFileSh(fileName2)
|
||||
delFileSh(fileName)
|
||||
delFileSh(fileName1)
|
||||
delFileSh(fileName2)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
@ -209,7 +238,7 @@ class SendCallbackJobService : JobIntentService() {
|
|||
file1 = File(fileName1)
|
||||
}
|
||||
|
||||
Util.chownSh(fileName, Util.getMainUserAndGroup(this))
|
||||
chownSh(fileName, getMainUserAndGroup(this))
|
||||
var url = "http://39.103.73.250/tt/ddj/backup.do"
|
||||
// if (Util.backUpServerIp != "") {
|
||||
// url = "http://" + Util.backUpServerIp + "/tt/ddj/backup.do"
|
||||
|
@ -231,8 +260,8 @@ class SendCallbackJobService : JobIntentService() {
|
|||
builder.setType(MultipartBody.FORM)
|
||||
|
||||
//追加参数
|
||||
builder.addFormDataPart("recordId", "${Util.recordId}")
|
||||
builder.addFormDataPart("afVer", Util.getAppAfVer())
|
||||
builder.addFormDataPart("recordId", "${recordId}")
|
||||
builder.addFormDataPart("afVer", getAppAfVer())
|
||||
|
||||
if (file.exists() && file.length() > 0) {
|
||||
builder.addFormDataPart(
|
||||
|
@ -253,7 +282,7 @@ class SendCallbackJobService : JobIntentService() {
|
|||
//创建RequestBody
|
||||
val body: RequestBody = builder.build()
|
||||
LogUtils.d(TAG, "sendBackupEvent-> url = $url")
|
||||
LogUtils.d(TAG, "sendBackupEvent-> recordId= " + Util.recordId)
|
||||
LogUtils.d(TAG, "sendBackupEvent-> recordId= " + recordId)
|
||||
//创建Request
|
||||
val request = Request.Builder().url(url).post(body).build()
|
||||
//单独设置参数 比如读取超时时间
|
||||
|
@ -267,7 +296,7 @@ class SendCallbackJobService : JobIntentService() {
|
|||
val retJo = JSONObject(ret)
|
||||
if (retJo.getInt("code") == 1) {
|
||||
if (retJo.has("backUpFiles")) {
|
||||
Util.backupResult = retJo.getJSONObject("backUpFiles")
|
||||
backupResult = retJo.getJSONObject("backUpFiles")
|
||||
}
|
||||
result = true
|
||||
}
|
||||
|
|
|
@ -6,10 +6,14 @@ import android.os.Handler
|
|||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.data.AppState.isClickRet
|
||||
import com.android.grape.data.AppState.proxyCountry
|
||||
import com.android.grape.net.MyGet
|
||||
import com.android.grape.util.ClashUtil
|
||||
import com.android.grape.util.ClashUtil.getProxyPort
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.util.TaskUtils
|
||||
import com.android.grape.util.TaskUtils.setFinish
|
||||
import com.android.grape.util.TaskUtils.setInstallRet
|
||||
import java.util.Locale
|
||||
|
||||
class StartVpnPortJobService : JobIntentService() {
|
||||
|
@ -23,9 +27,9 @@ class StartVpnPortJobService : JobIntentService() {
|
|||
)
|
||||
}, 1000L)
|
||||
} else {
|
||||
Util.setInstallRet(false)
|
||||
Util.isClickRet = false
|
||||
Util.setFinish(this)
|
||||
setInstallRet(false)
|
||||
isClickRet = false
|
||||
setFinish(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +39,7 @@ class StartVpnPortJobService : JobIntentService() {
|
|||
var nRetryCount = 0
|
||||
do {
|
||||
val url =
|
||||
"http://39.103.73.250/tt/test/testProxy.jsp?port=$port&country=" + Util.proxyCountry
|
||||
"http://39.103.73.250/tt/test/testProxy.jsp?port=$port&country=" + proxyCountry
|
||||
?.uppercase(Locale.getDefault())
|
||||
val result: String = MyGet.get(url)
|
||||
Log.d(TAG, "request url == $url result$result")
|
||||
|
|
|
@ -6,10 +6,7 @@ import android.os.Handler
|
|||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.MainApplication
|
||||
import com.android.grape.util.ClashUtil
|
||||
import com.android.grape.util.CountryCode
|
||||
import com.android.grape.util.Util
|
||||
|
||||
/**
|
||||
* 本地vpn功能
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
package com.android.grape.job
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.android.grape.data.AppState.recordPackageName
|
||||
import com.android.grape.util.BackupUtils.backUp
|
||||
import com.android.grape.util.FileUtils.delFiles
|
||||
import com.android.grape.util.MockTools
|
||||
import com.android.grape.util.Util
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
import com.android.grape.util.TaskUtils
|
||||
import com.android.grape.util.TaskUtils.setFinish
|
||||
import com.android.grape.util.TaskUtils.setInstallRet
|
||||
|
||||
|
||||
class UnInstallService : JobIntentService() {
|
||||
override fun onHandleWork(intent: Intent) {
|
||||
Util.backUp(this)
|
||||
Util.setInstallRet(true)
|
||||
MockTools.exec("pm clear " + Util.recordPackageName)
|
||||
Util.delFiles(this@UnInstallService)
|
||||
Util.setFinish(this@UnInstallService)
|
||||
backUp(this)
|
||||
setInstallRet(true)
|
||||
MockTools.exec("pm clear " + recordPackageName)
|
||||
delFiles(this@UnInstallService)
|
||||
setFinish(this@UnInstallService)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -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.os.Handler
|
||||
import android.os.Looper
|
||||
import com.android.grape.data.AppState.script_status
|
||||
import com.android.grape.job.UnInstallService
|
||||
import com.android.grape.util.ScriptUtil.SCRIPT_RESULT_KEY
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.util.ScriptUtils.SCRIPT_RESULT_KEY
|
||||
import com.android.grape.util.TaskUtils
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
|
||||
class ScriptReceiver : BroadcastReceiver() {
|
||||
|
@ -15,7 +16,7 @@ class ScriptReceiver : BroadcastReceiver() {
|
|||
val action = intent.action
|
||||
LogUtils.i("TAG", context.packageName + " Receive action:" + action)
|
||||
val scriptResult = intent.getStringExtra(SCRIPT_RESULT_KEY)
|
||||
Util.script_status = 0
|
||||
script_status = 0
|
||||
LogUtils.i(
|
||||
"IOSTQ:脚本结束",
|
||||
"result----> $scriptResult"
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.android.grape.sai
|
|||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.android.grape.util.ShellUtils.catSh
|
||||
import java.io.BufferedReader
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
@ -13,6 +14,7 @@ import java.io.IOException
|
|||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.io.OutputStream
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.DigestInputStream
|
||||
|
@ -41,6 +43,39 @@ object IOUtils {
|
|||
}
|
||||
}
|
||||
|
||||
fun getStringFromFile(context: Context?, filepath: String): String {
|
||||
Log.i(
|
||||
TAG,
|
||||
"start to getStringFromFile : $filepath"
|
||||
)
|
||||
val file = File(filepath)
|
||||
if (file.exists()) {
|
||||
return catSh(context, filepath)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
fun getStringFromInputStream(inputStream: InputStream?): String {
|
||||
var inputStreamReader: InputStreamReader? = null
|
||||
try {
|
||||
inputStreamReader = InputStreamReader(inputStream, "utf-8")
|
||||
} catch (e1: UnsupportedEncodingException) {
|
||||
e1.printStackTrace()
|
||||
}
|
||||
val reader = BufferedReader(inputStreamReader)
|
||||
val sb = StringBuffer("")
|
||||
var line: String?
|
||||
try {
|
||||
while ((reader.readLine().also { line = it }) != null) {
|
||||
sb.append(line)
|
||||
sb.append("\n")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun copyFileFromAssets(context: Context, assetFileName: String, destination: File?) {
|
||||
context.assets.open(assetFileName).use { inputStream ->
|
||||
|
|
|
@ -12,6 +12,8 @@ import android.util.Log
|
|||
import android.util.Pair
|
||||
import com.android.grape.MainApplication
|
||||
import com.android.grape.R
|
||||
import com.android.grape.data.AppState.isClickRet
|
||||
import com.android.grape.data.AppState.recordPackageName
|
||||
import com.android.grape.sai.inter.ApkSource
|
||||
import com.android.grape.sai.param.SaiPiSessionParams
|
||||
import com.android.grape.sai.param.SaiPiSessionState
|
||||
|
@ -21,10 +23,10 @@ import com.android.grape.sai.prefers.PreferencesHelper
|
|||
import com.android.grape.sai.rootless.AndroidPackageInstallerError
|
||||
import com.android.grape.sai.shell.MiuiUtils
|
||||
import com.android.grape.sai.shell.Shell
|
||||
import com.android.grape.util.Util
|
||||
import com.android.grape.util.TaskUtils
|
||||
import com.android.grape.util.TaskUtils.setInstallRet
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
import java.io.File
|
||||
import java.util.Arrays
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.Semaphore
|
||||
|
@ -76,7 +78,7 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
|
|||
}
|
||||
unlockInstallation()
|
||||
// Toast.makeText(context,"Installation succeed",Toast.LENGTH_SHORT).show();
|
||||
Util.setInstallRet(true)
|
||||
setInstallRet(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,12 +132,12 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
|
|||
).build()
|
||||
)
|
||||
unlockInstallation()
|
||||
Util.setInstallRet(false)
|
||||
setInstallRet(false)
|
||||
return
|
||||
}
|
||||
androidSessionId = createSession()
|
||||
//todo params.apkSource().apkLocalPath?
|
||||
val path = "/sdcard/apks/${Util.recordPackageName}"
|
||||
val path = "/sdcard/apks/${recordPackageName}"
|
||||
val file = File(path)
|
||||
|
||||
val files = file.listFiles()
|
||||
|
@ -145,7 +147,7 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
|
|||
if (f.length() <= 0) {
|
||||
setSessionState(sessionId, SaiPiSessionState.Builder(sessionId, SaiPiSessionStatus.INSTALLATION_FAILED).appTempName(appTempName).error(MainApplication.instance.getString(R.string.installer_error_unknown_apk_size), null).build())
|
||||
unlockInstallation()
|
||||
Util.setInstallRet(false)
|
||||
setInstallRet(false)
|
||||
return
|
||||
}
|
||||
ensureCommandSucceeded(shell.exec(Shell.Command("pm", "install-write", f.length().toString(), androidSessionId.toString(), String.format("%d.apk", currentApkFile++), f.path)))
|
||||
|
@ -170,9 +172,9 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
|
|||
""".trimIndent()).build())
|
||||
unlockInstallation()
|
||||
|
||||
Util.setInstallRet(false)
|
||||
setInstallRet(false)
|
||||
} else {
|
||||
Util.isClickRet = true
|
||||
isClickRet = true
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
@ -207,7 +209,7 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) :
|
|||
|
||||
unlockInstallation()
|
||||
|
||||
Util.setInstallRet(false)
|
||||
setInstallRet(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.util.Log
|
||||
import com.android.grape.MainApplication
|
||||
import com.android.grape.data.AppState.recordPackageName
|
||||
import com.android.grape.data.AppState.taskJson
|
||||
import com.android.grape.data.Device
|
||||
import com.android.grape.net.Api
|
||||
import com.android.grape.net.ChangeCallBack
|
||||
import com.android.grape.provider.DeviceDataAccessor
|
||||
import com.android.grape.util.Util.taskJson
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -116,7 +117,7 @@ object ChangeDeviceInfoUtil {
|
|||
})
|
||||
put("settingPropertiesList", JSONArray().apply {
|
||||
put(JSONObject().apply {
|
||||
put("propertiesName", "ssaid/${Util.recordPackageName}")
|
||||
put("propertiesName", "ssaid/${recordPackageName}")
|
||||
put("propertiesValue", device.androidId)
|
||||
})
|
||||
put(JSONObject().apply {
|
||||
|
|
|
@ -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
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.android.grape.data.AppState.apkDir
|
||||
import com.android.grape.data.AppState.monitorDir
|
||||
import com.android.grape.data.AppState.recordFileName
|
||||
import com.android.grape.data.AppState.recordPackageName
|
||||
import com.android.grape.data.AppState.startInstallTime
|
||||
import com.android.grape.util.ContextUtils.getBaseFilesDir
|
||||
import com.android.grape.util.ShellUtils.delFile
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
|
@ -31,8 +39,83 @@ import java.util.zip.ZipFile
|
|||
* @date :2021/9/28 15:44
|
||||
*/
|
||||
object FileUtils {
|
||||
private const val TAG = "IOSTQ:FileUtils"
|
||||
private const val BUFFER_SIZE = 1024 * 1024 //1M Byte
|
||||
public const val BUFFER_SIZE = 1024 * 1024 //1M Byte
|
||||
|
||||
public var name: String? = null
|
||||
|
||||
fun delFiles(context: Context?) {
|
||||
Log.i("TaskUtils", "start to delFiles : " + startInstallTime)
|
||||
if (startInstallTime > 0L) {
|
||||
val dir = File("/storage/emulated/0")
|
||||
|
||||
val files = dir.listFiles()
|
||||
|
||||
if (files != null && files.size > 0) {
|
||||
for (file in files) {
|
||||
val time = file.lastModified()
|
||||
|
||||
if (time > startInstallTime && "Android" != file.name) {
|
||||
delFile(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun getRecordDataFileName(context: Context): String {
|
||||
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".zip"
|
||||
}
|
||||
|
||||
public fun getRecordSdcardApkVerFileName(context: Context): String {
|
||||
return "/sdcard/apks/" + recordFileName
|
||||
}
|
||||
|
||||
public fun forceCreteDir(file: File) {
|
||||
if (!file.exists()) {
|
||||
val parent = file.parentFile
|
||||
|
||||
if (parent?.exists() == true) {
|
||||
file.mkdir()
|
||||
} else {
|
||||
if (parent != null) {
|
||||
forceCreteDir(parent)
|
||||
}
|
||||
file.mkdir()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public fun getRecordListTxtFileName(context: Context): String {
|
||||
return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".list.txt"
|
||||
}
|
||||
|
||||
public fun forceMakeDir(file: File) {
|
||||
if (!file.exists()) {
|
||||
val parent = file.parentFile
|
||||
|
||||
parent?.exists()?.let {
|
||||
if (!it) {
|
||||
MockTools.exec("mkdir $parent")
|
||||
forceMakeDir(parent)
|
||||
// file.mkdirs();
|
||||
} else {
|
||||
MockTools.exec("mkdir $file")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFileModifyTime(context: Context, dirName: String, fileName: String): Long {
|
||||
val fs = File(getBaseFilesDir(context) + "/" + dirName + "/" + fileName)
|
||||
|
||||
return if (fs.exists()) {
|
||||
fs.lastModified()
|
||||
} else {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//创建文件夹
|
||||
|
@ -141,6 +224,60 @@ object FileUtils {
|
|||
dir.delete() // 删除目录本身
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用shell命令将目录压缩到zip文件中。
|
||||
*
|
||||
* @param zipdirname要压缩目录的绝对路径
|
||||
* @param zipfileName所产生的zip文件的名称,包括其路径
|
||||
*/
|
||||
fun zipSh(zipDirName: String, zipFileName: String) {
|
||||
Log.i(
|
||||
"FileUtils",
|
||||
"start zipSh : $zipDirName ; $zipFileName"
|
||||
)
|
||||
try {
|
||||
val zipDir = File(zipDirName)
|
||||
val zipFile = File(zipFileName)
|
||||
var cmd = "cd " + (zipDir.parentFile?.absolutePath ?: "") + "|"
|
||||
cmd += "tar -zcvf " + zipFile.name + " " + zipDir.name
|
||||
Log.i("FileUtils", "zipSh-> cmd:$cmd")
|
||||
MockTools.exec(cmd)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从给定目录路径中检索并处理具有特定后缀的文件名。
|
||||
* 通过指定路径中的所有文件迭代,检查所需的后缀,然后提取
|
||||
* 来自其路径的文件名以进行进一步处理。
|
||||
*
|
||||
* @param路径搜索文件的目录路径
|
||||
* @param houzhui要匹配和处理的文件的后缀
|
||||
* @throws IOException如果在访问文件时发生I/O错误
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
public fun getName(path: String, houzhui: String) {
|
||||
//新建ArrayList
|
||||
|
||||
var list: List<String>? = FileUtils.getFilesAllName(path)
|
||||
if (list != null) {
|
||||
for (listname in list) {
|
||||
Log.e("TAG", "listname :$listname")
|
||||
//判断文件是不是想要查找后缀
|
||||
if (listname.endsWith(houzhui)) {
|
||||
//获取路径下最后一个‘/’后的坐标
|
||||
val lastindex = listname.lastIndexOf("/")
|
||||
//获取具体文件名称
|
||||
name = listname.substring(lastindex + 1, listname.length)
|
||||
//获取到想要的名称后,去干你想干的事
|
||||
//dosomething
|
||||
Log.e("name", name!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 向文件中添加内容
|
||||
|
@ -405,7 +542,7 @@ object FileUtils {
|
|||
return
|
||||
}
|
||||
}
|
||||
LogUtils.d("TAG", "writePackageName: $packageName", null)
|
||||
LogUtils.d("FileUtils", "writePackageName: $packageName", null)
|
||||
try {
|
||||
BufferedOutputStream(
|
||||
FileOutputStream(file)
|
||||
|
@ -420,7 +557,7 @@ object FileUtils {
|
|||
}
|
||||
|
||||
fun writeDevice(packageName: String, device: String) {
|
||||
LogUtils.d("TAG", "writeDevice: $device")
|
||||
LogUtils.d("FileUtils", "writeDevice: $device")
|
||||
val base64Content: String =
|
||||
Base64.encodeToString(device.toByteArray(StandardCharsets.UTF_8), Base64.NO_WRAP)
|
||||
// 构造安全命令
|
||||
|
@ -428,18 +565,89 @@ object FileUtils {
|
|||
val cmd = "sh -c 'printf %s '\''" + base64Content + "'\'' > " + filePath + "'"
|
||||
val result = ShellUtils.execRootCmdAndGetResult(cmd)
|
||||
ShellUtils.execRootCmdAndGetResult("chmod 777 $filePath")
|
||||
LogUtils.d("TAG", "writeDevice: $result")
|
||||
LogUtils.d("FileUtils", "writeDevice: $result")
|
||||
}
|
||||
|
||||
fun runPlugin(packageName: String) {
|
||||
val cmd = "apmt patch add -n ArmCloudAF -p $packageName -f /sdcard/Download/ArmCloudAF_lo.apk"
|
||||
val cmd =
|
||||
"apmt patch add -n ArmCloudAF -p $packageName -f /sdcard/Download/ArmCloudAF_lo.apk"
|
||||
val result = ShellUtils.execRootCmdAndGetResult(cmd)
|
||||
LogUtils.d("TAG", "runPlugin: $result")
|
||||
LogUtils.d("FileUtils", "runPlugin: $result")
|
||||
}
|
||||
|
||||
fun deletePlugin() {
|
||||
val cmd = "apmt patch del -n ArmCloudAF"
|
||||
val result = ShellUtils.execRootCmdAndGetResult(cmd)
|
||||
LogUtils.d("TAG", "deletePlugin: $result")
|
||||
LogUtils.d("FileUtils", "deletePlugin: $result")
|
||||
}
|
||||
|
||||
fun getFileContent(context: Context, dirName: String, fileName: String): String {
|
||||
val fs = File(getBaseFilesDir(context) + "/" + dirName + "/" + fileName)
|
||||
|
||||
if (fs.exists()) {
|
||||
var fis: FileInputStream? = null
|
||||
try {
|
||||
fis = FileInputStream(fs)
|
||||
val bytes = ByteArray(1024)
|
||||
//得到实际读取的长度
|
||||
var n = 0
|
||||
val sb = StringBuffer()
|
||||
//循环读取
|
||||
while ((fis.read(bytes).also { n = it }) != -1) {
|
||||
val s = String(bytes, 0, n)
|
||||
sb.append(s)
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
} catch (e: Exception) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
//最后一定要关闭文件流
|
||||
try {
|
||||
fis!!.close()
|
||||
} catch (e: IOException) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
fun getFileContent(context: Context?, fileName: String): String {
|
||||
val fs = File(fileName)
|
||||
|
||||
if (fs.exists()) {
|
||||
var fis: FileInputStream? = null
|
||||
try {
|
||||
fis = FileInputStream(fs)
|
||||
val bytes = ByteArray(1024)
|
||||
//得到实际读取的长度
|
||||
var n = 0
|
||||
val sb = StringBuffer()
|
||||
//循环读取
|
||||
while ((fis.read(bytes).also { n = it }) != -1) {
|
||||
val s = String(bytes, 0, n)
|
||||
sb.append(s)
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
} catch (e: Exception) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
//最后一定要关闭文件流
|
||||
try {
|
||||
fis!!.close()
|
||||
} catch (e: IOException) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
import android.app.IMikRom
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import com.android.grape.util.ContextUtils.getBaseFilesDir
|
||||
import com.android.grape.util.FileUtils.forceMakeDir
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.FileWriter
|
||||
import java.io.IOException
|
||||
|
||||
object ServiceUtils {
|
||||
private var iMikRom: IMikRom? = null
|
||||
|
@ -111,4 +119,73 @@ object ServiceUtils {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun writeFile1(context: Context?, dirName: String, fileName: String, contents: String) {
|
||||
val fs = File("$dirName/$fileName")
|
||||
forceMakeDir(fs)
|
||||
var outputStream: FileOutputStream? = null
|
||||
try {
|
||||
outputStream = FileOutputStream(fs)
|
||||
|
||||
outputStream.write(contents.toByteArray())
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun writeFile(context: Context, dirName: String, fileName: String, contents: String) {
|
||||
val fs = File(getBaseFilesDir(context) + "/" + dirName + "/" + fileName)
|
||||
forceMakeDir(fs)
|
||||
var outputStream: FileOutputStream? = null
|
||||
try {
|
||||
outputStream = FileOutputStream(fs)
|
||||
|
||||
outputStream.write(contents.toByteArray())
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun WriteFile(fileName: String?, content: String?) {
|
||||
try {
|
||||
val writer = FileWriter(fileName, false)
|
||||
writer.write(content)
|
||||
writer.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun writeFileToSDCard(message: String) {
|
||||
// 比如可以将一个文件作为普通的文档存储,那么先获取系统默认的文档存放根目录
|
||||
val parent_path = Environment.getExternalStorageDirectory()
|
||||
|
||||
// String AppPath = "/data/data/"+ Util.getRecordPackageName() + "/";
|
||||
val AppPath = "/data/data/sperixlabs.proxyme/"
|
||||
val dir = File(AppPath)
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs()
|
||||
}
|
||||
val file = File(AppPath + "device.txt")
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
}
|
||||
|
||||
try {
|
||||
file.createNewFile()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
val writer = FileWriter(AppPath + "device.txt", true)
|
||||
|
||||
writer.write(message)
|
||||
|
||||
writer.close()
|
||||
Log.d("文件写入", "成功")
|
||||
}
|
||||
}
|
|
@ -1,360 +1,593 @@
|
|||
package com.android.grape.util;
|
||||
package com.android.grape.util
|
||||
|
||||
import static java.security.AccessController.getContext;
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.android.grape.data.AppState.recordPackageName
|
||||
import com.android.grape.util.AppUtils.getPackageUserID
|
||||
import com.android.grape.util.ContextUtils.getMonitorDir
|
||||
import com.android.grape.util.TaskUtils.getRecordListTxtFileName
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.BufferedReader
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.io.InterruptedIOException
|
||||
import java.io.PrintWriter
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
/**
|
||||
* @Time: 2025-56-16 14:56
|
||||
* @Creator: 初屿贤
|
||||
* @File: ShellUtils
|
||||
* @Project: AndroidGrape
|
||||
* @Description:
|
||||
*/
|
||||
// ShellUtils.kt
|
||||
object ShellUtils {
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.blankj.utilcode.util.LogUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ShellUtils {
|
||||
|
||||
public static String getPackagePath(Context context, String packageName) {
|
||||
/**
|
||||
* 递归更改指定目录及其内容的所有权
|
||||
* 使用Shell命令给提供的用户和组。
|
||||
*
|
||||
* @param上下文调用该方法的上下文
|
||||
* @param dir是将更改所有权的目录的路径
|
||||
* @param groupanduser组和用户以“组:用户”的形式
|
||||
* @return true如果操作成功,则否则为否则
|
||||
*/
|
||||
fun chownSh(dir: String?, groupAndUser: String?): Boolean {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
|
||||
return appInfo.sourceDir; // 返回 APK 的完整路径
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
val cmd = "chown -R $groupAndUser $dir"
|
||||
Log.i("ShellUtils", "chownSh cmd:$cmd")
|
||||
MockTools.exec(cmd)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun getPackagePath(context: Context, packageName: String): String {
|
||||
return try {
|
||||
val pm = context.packageManager
|
||||
val appInfo = pm.getApplicationInfo(packageName, 0)
|
||||
appInfo.sourceDir // 返回 APK 的完整路径
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
e.printStackTrace()
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
public static void exec(String cmd) {
|
||||
fun copyFolderSh(oldPath: String, newPath: String): Boolean {
|
||||
Log.i("ShellUtils", "start copyFolderSh : $oldPath ; $newPath")
|
||||
|
||||
try {
|
||||
LogUtils.e(Log.INFO, "ShellUtils", "Executing command: " + cmd, null);
|
||||
Process process = Runtime.getRuntime().exec(cmd);
|
||||
process.waitFor();
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing command: " + e.getMessage(), e);
|
||||
}
|
||||
val cmd = "cp -r -f $oldPath $newPath"
|
||||
Log.i("ShellUtils", "copyFolderSh cmd:$cmd")
|
||||
MockTools.exec(cmd)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
public static int getPid(Process p) {
|
||||
int pid = -1;
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行shell命令以列出目录的内容,重定向输出
|
||||
* 到指定的文件,并将文件所有权修改给特定用户。
|
||||
*
|
||||
* @param上下文用于检索文件路径和用户信息的应用程序上下文。
|
||||
* @param dir需要列出其内容的目录。
|
||||
* @return `true`如果操作成功而没有异常,`false`否则。
|
||||
*/
|
||||
fun listSh(context: Context, dir: String): Boolean {
|
||||
Log.i("ShellUtils", "listSh->dir:$dir")
|
||||
try {
|
||||
Field f = p.getClass().getDeclaredField("pid");
|
||||
f.setAccessible(true);
|
||||
pid = f.getInt(p);
|
||||
f.setAccessible(false);
|
||||
} catch (Throwable e) {
|
||||
pid = -1;
|
||||
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 pid;
|
||||
return false
|
||||
}
|
||||
|
||||
public static boolean hasBin(String binName) {
|
||||
// 验证 binName 是否符合规则
|
||||
if (binName == null || binName.isEmpty()) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Invalid bin name",null);
|
||||
throw new IllegalArgumentException("Bin name cannot be null or empty");
|
||||
|
||||
fun 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()
|
||||
}
|
||||
|
||||
for (char c : binName.toCharArray()) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
fun getPid(p: Process): Int {
|
||||
var pid = -1
|
||||
return try {
|
||||
val f = p.javaClass.getDeclaredField("pid")
|
||||
f.isAccessible = true
|
||||
pid = f.getInt(p)
|
||||
f.isAccessible = false
|
||||
pid
|
||||
} catch (e: Throwable) {
|
||||
pid
|
||||
}
|
||||
}
|
||||
|
||||
fun hasBin(binName: String): Boolean {
|
||||
if (binName.isEmpty()) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Invalid bin name", null)
|
||||
throw IllegalArgumentException("Bin name cannot be null or empty")
|
||||
}
|
||||
|
||||
for (c in binName.toCharArray()) {
|
||||
if (!Character.isLetterOrDigit(c) && c != '.' && c != '_' && c != '-') {
|
||||
throw new IllegalArgumentException("Invalid bin name");
|
||||
throw IllegalArgumentException("Invalid bin name")
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 PATH 环境变量
|
||||
String pathEnv = System.getenv("PATH");
|
||||
if (pathEnv == null) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "PATH environment variable is not available", null);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 使用适合当前系统的路径分隔符分割路径
|
||||
String[] paths = pathEnv.split(File.pathSeparator);
|
||||
for (String path : paths) {
|
||||
File file = new File(path, binName); // 使用 File 构造完整路径
|
||||
val pathEnv = System.getenv("PATH") ?: return false
|
||||
val paths = pathEnv.split(File.pathSeparatorChar)
|
||||
for (path in paths) {
|
||||
val file = File(path, binName)
|
||||
try {
|
||||
// 检查文件是否可执行
|
||||
if (file.canExecute()) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Security exception occurred while checking: " + file.getAbsolutePath(), e);
|
||||
} catch (e: SecurityException) {
|
||||
LogUtils.e(
|
||||
Log.ERROR,
|
||||
"ShellUtils",
|
||||
"Security exception occurred while checking: ${file.absolutePath}",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果未找到可执行文件,返回 false
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
public static String execRootCmdAndGetResult(String cmd) {
|
||||
Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: " + cmd);
|
||||
// if (!isCommandSafe(cmd)) { // 检查命令的合法性
|
||||
// Log.e("ShellUtils", "Detected unsafe command. Aborting execution.");
|
||||
// throw new IllegalArgumentException("Detected unsafe command.");
|
||||
// }
|
||||
fun execRootCmdAndGetResult(cmd: String): String {
|
||||
Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: $cmd")
|
||||
|
||||
Process process = null;
|
||||
ExecutorService executor = Executors.newFixedThreadPool(2);
|
||||
val process: Process? = when {
|
||||
hasBin("su") -> Runtime.getRuntime().exec("su")
|
||||
hasBin("xu") -> Runtime.getRuntime().exec("xu")
|
||||
hasBin("vu") -> Runtime.getRuntime().exec("vu")
|
||||
else -> Runtime.getRuntime().exec("sh")
|
||||
}
|
||||
|
||||
val executor = Executors.newFixedThreadPool(2)
|
||||
return try {
|
||||
process?.let {
|
||||
val os = BufferedOutputStream(it.outputStream)
|
||||
val isInputStream = it.inputStream
|
||||
val errorStream = it.errorStream
|
||||
val br =
|
||||
BufferedReader(InputStreamReader(isInputStream, StandardCharsets.UTF_8))
|
||||
val errorReader =
|
||||
BufferedReader(InputStreamReader(errorStream, StandardCharsets.UTF_8))
|
||||
|
||||
executor.submit {
|
||||
try {
|
||||
if (hasBin("su")) {
|
||||
process = Runtime.getRuntime().exec("su");
|
||||
} else if (hasBin("xu")) {
|
||||
process = Runtime.getRuntime().exec("xu");
|
||||
} else if (hasBin("vu")) {
|
||||
process = Runtime.getRuntime().exec("vu");
|
||||
var line: String?
|
||||
while (true) {
|
||||
line = errorReader.readLine()
|
||||
if (line == null) {
|
||||
break
|
||||
}
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Shell Error: $line", null)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
LogUtils.e(
|
||||
Log.ERROR,
|
||||
"ShellUtils",
|
||||
"Error while reading process error stream: ${e.message}",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
os.write("$cmd\n".toByteArray())
|
||||
os.write("exit\n".toByteArray())
|
||||
os.flush()
|
||||
|
||||
val output = StringBuilder()
|
||||
var line: String?
|
||||
while (true) {
|
||||
line = br.readLine()
|
||||
if (line == null) {
|
||||
break
|
||||
}
|
||||
output.append(line).append("\n")
|
||||
}
|
||||
|
||||
|
||||
val completed = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
it.waitFor(10, TimeUnit.SECONDS)
|
||||
} else {
|
||||
process = Runtime.getRuntime().exec("sh");
|
||||
}
|
||||
|
||||
try (OutputStream os = new BufferedOutputStream(process.getOutputStream());
|
||||
InputStream is = process.getInputStream();
|
||||
InputStream errorStream = process.getErrorStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
|
||||
BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream, StandardCharsets.UTF_8))) {
|
||||
|
||||
executor.submit(() -> {
|
||||
String line;
|
||||
try {
|
||||
while ((line = errorReader.readLine()) != null) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Shell Error: " + line, null);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error while reading process error stream: " + e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
|
||||
os.write((cmd + "\n").getBytes());
|
||||
os.write("exit\n".getBytes());
|
||||
os.flush();
|
||||
StringBuilder output = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
output.append(line).append("\n");
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (!process.waitFor(10, TimeUnit.SECONDS)) {
|
||||
process.destroyForcibly();
|
||||
throw new RuntimeException("Shell command execution timeout.");
|
||||
}
|
||||
} else {
|
||||
long startTime = System.currentTimeMillis();
|
||||
val startTime = System.currentTimeMillis()
|
||||
while (true) {
|
||||
try {
|
||||
process.exitValue();
|
||||
break;
|
||||
} catch (IllegalThreadStateException e) {
|
||||
if (System.currentTimeMillis() - startTime > 10000) { // 10 seconds
|
||||
process.destroy();
|
||||
throw new RuntimeException("Shell command execution timeout.");
|
||||
it.exitValue()
|
||||
break
|
||||
} catch (e: IllegalThreadStateException) {
|
||||
if (System.currentTimeMillis() - startTime > 10000) {
|
||||
it.destroy()
|
||||
throw RuntimeException("Shell command execution timeout.")
|
||||
}
|
||||
Thread.sleep(100); // Sleep briefly before re-checking
|
||||
Thread.sleep(100)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
return output.toString().trim();
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: " + e.getMessage(), e);
|
||||
Thread.currentThread().interrupt();
|
||||
return "Error: " + e.getMessage();
|
||||
|
||||
output.toString().trim()
|
||||
} ?: ""
|
||||
} catch (e: IOException) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: ${e.message}", e)
|
||||
Thread.currentThread().interrupt()
|
||||
"Error: ${e.message}"
|
||||
} catch (e: InterruptedException) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: ${e.message}", e)
|
||||
Thread.currentThread().interrupt()
|
||||
"Error: ${e.message}"
|
||||
} finally {
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
executor.shutdown();
|
||||
Log.d("ShellUtils", "Executor service shut down.");
|
||||
process?.destroy()
|
||||
executor.shutdown()
|
||||
Log.d("ShellUtils", "Executor service shut down.")
|
||||
}
|
||||
}
|
||||
|
||||
public static void execRootCmd(String cmd) {
|
||||
// 校验命令是否安全
|
||||
// if (!isCommandSafe(cmd)) {
|
||||
// return;
|
||||
// }
|
||||
List<String> cmds = new ArrayList<>();
|
||||
cmds.add(cmd);
|
||||
|
||||
// 使用同步锁保护线程安全
|
||||
synchronized (ShellUtils.class) {
|
||||
fun execRootCmd(cmd: String) {
|
||||
val cmds = mutableListOf(cmd)
|
||||
synchronized(ShellUtils::class.java) {
|
||||
try {
|
||||
List<String> results = execRootCmds(cmds);
|
||||
// 判断是否需要打印输出,仅用于开发调试阶段
|
||||
for (String result : results) {
|
||||
Log.d("ShellUtils", "Command Result: " + result);
|
||||
val results = execRootCmds(cmds)
|
||||
for (result in results) {
|
||||
Log.d("ShellUtils", "Command Result: $result")
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Unexpected error: " + e.getMessage(), e);
|
||||
} catch (e: Exception) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Unexpected error: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean isCommandSafe(String cmd) {
|
||||
// 检查空值和空字符串
|
||||
if (cmd == null || cmd.trim().isEmpty()) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command: empty or null value.", null);
|
||||
return false;
|
||||
private fun isCommandSafe(cmd: String): Boolean {
|
||||
if (cmd.isBlank()) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command: empty or null value.", null)
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查非法字符
|
||||
if (!cmd.matches("^[a-zA-Z0-9._/:\\-~`'\" *|]+$")) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command due to illegal characters: " + cmd, null);
|
||||
return false;
|
||||
if (!cmd.matches(Regex("^[a-zA-Z0-9._/:\\\\\\-~`'\" *|]+\$"))) {
|
||||
LogUtils.e(
|
||||
Log.ERROR,
|
||||
"ShellUtils",
|
||||
"Rejected command due to illegal characters: $cmd",
|
||||
null
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查多命令(逻辑运算符限制)
|
||||
if (cmd.contains("&&") || cmd.contains("||")) {
|
||||
Log.d("ShellUtils", "Command contains logical operators.");
|
||||
if (!isExpectedMultiCommand(cmd)) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command due to prohibited structure: " + cmd, null);
|
||||
return false;
|
||||
LogUtils.e(
|
||||
Log.ERROR,
|
||||
"ShellUtils",
|
||||
"Rejected command due to prohibited structure: $cmd",
|
||||
null
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 路径遍历保护
|
||||
if (cmd.contains("../") || cmd.contains("..\\")) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Rejected command due to path traversal attempt: " + cmd, null);
|
||||
return false;
|
||||
if (cmd.contains("../") || cmd.contains("..\\\\")) {
|
||||
LogUtils.e(
|
||||
Log.ERROR,
|
||||
"ShellUtils",
|
||||
"Rejected command due to path traversal attempt: $cmd",
|
||||
null
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
// 命令长度限制
|
||||
if (cmd.startsWith("tar") && cmd.length() > 800) { // 特定命令支持更长长度
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Command rejected due to excessive length.", null);
|
||||
return false;
|
||||
} else if (cmd.length() > 500) {
|
||||
LogUtils.e("ShellUtils", "Command rejected due to excessive length.", null);
|
||||
return false;
|
||||
if (cmd.startsWith("tar") && cmd.length > 800) {
|
||||
LogUtils.e(
|
||||
Log.ERROR,
|
||||
"ShellUtils",
|
||||
"Command rejected due to excessive length.",
|
||||
null
|
||||
)
|
||||
return false
|
||||
} else if (cmd.length > 500) {
|
||||
LogUtils.e("ShellUtils", "Command rejected due to excessive length.", null)
|
||||
return false
|
||||
}
|
||||
|
||||
Log.d("ShellUtils", "Command passed safety checks: " + cmd);
|
||||
return true;
|
||||
Log.d("ShellUtils", "Command passed safety checks: $cmd")
|
||||
return true
|
||||
}
|
||||
|
||||
// 附加方法:检查多命令是否符合预期
|
||||
private static boolean isExpectedMultiCommand(String cmd) {
|
||||
// 判断是否为允许的命令组合,比如 `cd` 或 `tar` 组合命令
|
||||
return cmd.matches("^cd .+ && (tar|zip|cp).+");
|
||||
private fun isExpectedMultiCommand(cmd: String): Boolean {
|
||||
return cmd.matches(Regex("^cd .+ && (tar|zip|cp).+"))
|
||||
}
|
||||
|
||||
public static List<String> execRootCmds(List<String> cmds) {
|
||||
List<String> results = new ArrayList<>();
|
||||
Process process = null;
|
||||
fun execRootCmds(cmds: List<String>): List<String> {
|
||||
val results = mutableListOf<String>()
|
||||
var process: Process? = null
|
||||
try {
|
||||
// 初始化 Shell 环境
|
||||
process = hasBin("su") ? Runtime.getRuntime().exec("su") : Runtime.getRuntime().exec("sh");
|
||||
process = if (hasBin("su")) {
|
||||
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);
|
||||
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);
|
||||
results.addAll(localResults)
|
||||
}
|
||||
} catch (ioException: IOException) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout", ioException)
|
||||
}
|
||||
} 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);
|
||||
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)
|
||||
}
|
||||
} catch (IOException ioException) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stderr", ioException);
|
||||
}
|
||||
});
|
||||
|
||||
// 启动子线程
|
||||
stdThread.start();
|
||||
errThread.start();
|
||||
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 os = process.outputStream
|
||||
for (cmd in cmds) {
|
||||
os.write("$cmd\n".toByteArray())
|
||||
Log.d("ShellUtils", "Executing command: $cmd")
|
||||
}
|
||||
os.write("exit\n".toByteArray())
|
||||
os.flush()
|
||||
|
||||
try {
|
||||
// 执行命令、等待解决
|
||||
process.waitFor();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt(); // 恢复中断
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e);
|
||||
process.waitFor()
|
||||
} catch (e: InterruptedException) {
|
||||
Thread.currentThread().interrupt()
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e)
|
||||
}
|
||||
|
||||
// 等待子线程完成
|
||||
stdThread.join();
|
||||
errThread.join();
|
||||
stdThread.join()
|
||||
errThread.join()
|
||||
|
||||
} catch (InterruptedIOException e) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout: Interrupted", e);
|
||||
Thread.currentThread().interrupt(); // 恢复线程的中断状态
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e);
|
||||
} catch (e: InterruptedIOException) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout: Interrupted", e)
|
||||
Thread.currentThread().interrupt()
|
||||
} catch (e: Exception) {
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e)
|
||||
} finally {
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
process?.destroy()
|
||||
}
|
||||
}
|
||||
return results;
|
||||
return results
|
||||
}
|
||||
|
||||
public static boolean hasRootAccess() {
|
||||
// 记录是否出现安全异常
|
||||
boolean hasSecurityError = false;
|
||||
/**
|
||||
* 使用Shell命令删除给定文件名指定的文件或目录。
|
||||
* 此方法执行“ RM -RF”命令以删除文件或目录。
|
||||
* 记录过程步骤并处理执行过程中可能发生的任何异常。
|
||||
*
|
||||
* @param文件名删除文件或目录的名称或路径
|
||||
*/
|
||||
fun delFileSh(fileName: String) {
|
||||
Log.i("ShellUtils", "start delFileSh : $fileName")
|
||||
try {
|
||||
val cmd = "rm -rf $fileName"
|
||||
Log.i("ShellUtils", "delFileSh-> cmd:$cmd")
|
||||
MockTools.exec(cmd)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
// 检查二进制文件
|
||||
String[] binariesToCheck = {"su", "xu", "vu"};
|
||||
for (String bin : binariesToCheck) {
|
||||
fun delFolderSh(dir: String, fileName: String) {
|
||||
Log.i("ShellUtils", "start delFolderSh :$fileName")
|
||||
var PrintWriter: PrintWriter? = null
|
||||
var process: Process? = null
|
||||
try {
|
||||
process = Runtime.getRuntime().exec("su")
|
||||
PrintWriter = PrintWriter(process.outputStream)
|
||||
|
||||
var cmd = "cd $dir \n"
|
||||
cmd += "rm -rf $fileName"
|
||||
Log.i("ShellUtils", "delFolderSh-> cmd:$cmd")
|
||||
PrintWriter.println(cmd)
|
||||
|
||||
PrintWriter.flush()
|
||||
PrintWriter.close()
|
||||
val value = process.waitFor()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
process?.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
fun delFile(file: File) {
|
||||
try {
|
||||
val cmd = "rm -rf $file"
|
||||
Log.i("ShellUtils", "delFile-> cmd:$cmd")
|
||||
MockTools.exec(cmd)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun give777(path: String): Boolean {
|
||||
execCommand("su")
|
||||
execCommand("chmod", "777", path)
|
||||
return true
|
||||
}
|
||||
|
||||
fun delFilesSh(dir: String, prefix: String?) {
|
||||
try {
|
||||
var cmd = "cd $dir|"
|
||||
cmd += "rm -rf $prefix*"
|
||||
Log.i("ShellUtils", "delFilesSh-> cmd:$cmd")
|
||||
MockTools.exec(cmd)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun hasRootAccess(): Boolean {
|
||||
var hasSecurityError = false
|
||||
val binariesToCheck = arrayOf("su", "xu", "vu")
|
||||
for (bin in binariesToCheck) {
|
||||
try {
|
||||
if (hasBin(bin)) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
hasSecurityError = true;
|
||||
LogUtils.e(Log.ERROR, "ShellUtils", "Security exception while checking: " + bin, e);
|
||||
} catch (e: SecurityException) {
|
||||
hasSecurityError = true
|
||||
LogUtils.e(
|
||||
Log.ERROR,
|
||||
"ShellUtils",
|
||||
"Security exception while checking: $bin",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 判断如果发生安全异常则反馈问题
|
||||
if (hasSecurityError) {
|
||||
Log.w("ShellUtils", "Potential security error detected while checking root access.");
|
||||
Log.w("ShellUtils", "Potential security error detected while checking root access.")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 没有找到合法的二进制文件,则认为无root权限
|
||||
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
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun unzipAPkSh(zipFileName: String, dataDir: String) {
|
||||
try {
|
||||
val file = File(dataDir)
|
||||
if (!file.exists()) {
|
||||
FileUtils.forceCreteDir(file)
|
||||
}
|
||||
val cmd = "unzip -o " + "/sdcard/apks/" + File(zipFileName).name + " -d " + dataDir
|
||||
val unzipResult = MockTools.execRead(cmd)
|
||||
Log.i("ShellUtils", "unZipFileSh-> cmd:$unzipResult")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行shell命令以检索指定文件的内容。
|
||||
*
|
||||
* @param上下文应用程序上下文
|
||||
* @param fileName要读取的文件的名称
|
||||
* @return文件的内容作为字符串或一个空字符串,如果发生异常
|
||||
*/
|
||||
fun catSh(context: Context?, fileName: String): String {
|
||||
Log.i("TaskUtils", "catSh->fileName:$fileName")
|
||||
try {
|
||||
val cmd = "cat $fileName"
|
||||
val result = MockTools.execRead(cmd)
|
||||
return result
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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